diff --git a/pom.xml b/pom.xml
index 0816680b..e20e83e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -282,6 +282,12 @@
itextpdf
5.5.13.2
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
diff --git a/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java b/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
index 0f61d591..bc6071c8 100644
--- a/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
+++ b/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
@@ -1,133 +1,134 @@
-package com.ai.da.common.security.filter;
-
-import cn.hutool.core.util.StrUtil;
-import com.ai.da.common.context.UserContext;
-import com.ai.da.common.security.config.SecurityProperties;
-import com.ai.da.common.security.jwt.JWTTokenHelper;
-import com.ai.da.common.utils.LocalCacheUtils;
-import com.ai.da.common.utils.MultiReadHttpServletRequest;
-import com.ai.da.common.utils.MultiReadHttpServletResponse;
-import com.ai.da.common.utils.RequestInfoUtil;
-import com.ai.da.model.vo.AuthPrincipalVo;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.lang.NonNull;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.util.StopWatch;
-import org.springframework.util.StringUtils;
-import org.springframework.web.filter.OncePerRequestFilter;
-
-import javax.annotation.Resource;
-import javax.security.sasl.AuthenticationException;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * @author: dangweijian
- * @description: 认证拦截器
- * @create: 2020-07-10 16:50
- **/
-@Slf4j
-@Configuration
-public class AuthenticationFilter extends OncePerRequestFilter {
-
- @Resource
- private JWTTokenHelper jwtTokenHelper;
- @Resource
- private SecurityProperties properties;
-
- private static final List FILTER_URL =
- Arrays.asList("/favicon.ico", "/doc.html", "api/account/login", "api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
- "/webjars/", "/swagger-resources", "/v2/api-docs", "api/account/resetPwd",
- "/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
- "/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
- "/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew",
- "/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
- "/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
- "/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
- "/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify"
- );
-
- @Override
- protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
- String requestURI = httpServletRequest.getRequestURI();
-
- if (calculateUrl(requestURI) || hasAuthorizationToken(httpServletRequest)) {
- StopWatch stopWatch = new StopWatch();
- HttpServletRequest wrappedRequest = httpServletRequest;
- HttpServletResponse wrappedResponse = httpServletResponse;
- try {
- stopWatch.start();
- if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
- extracted(wrappedRequest);
- filterChain.doFilter(wrappedRequest, wrappedResponse);
- } else {
- wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
- wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
- extracted(wrappedRequest);
- filterChain.doFilter(wrappedRequest, wrappedResponse);
- }
- } catch (Exception e) {
- SecurityContextHolder.clearContext();
- throw e;
- } finally {
- stopWatch.stop();
- }
- } else {
- filterChain.doFilter(httpServletRequest, httpServletResponse);
- }
- }
-
- private Boolean calculateUrl(String requestURI) {
- String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
- return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
- }
-
- private boolean hasAuthorizationToken(HttpServletRequest request) {
- String authorizationHeader = request.getHeader("Authorization");
- return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
- }
-
- private void extracted(HttpServletRequest request) throws AuthenticationException {
- String jwtToken = request.getHeader(properties.getJwtTokenHeader());
-// log.debug("后台检查令牌:{}", jwtToken);
-
- if (StrUtil.isBlank(jwtToken)) {
- String ipAddress = RequestInfoUtil.getIpAddress(request);
- log.info("本次请求的ip为 : " + ipAddress);
- throw new RuntimeException("请传入token!");
- }
- if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
- //写死 暂时放行
- return;
- }
- // 检查token
- boolean validate = jwtTokenHelper.validateToken(jwtToken);
- if (validate) {
- AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
- if (principal == null) {
- throw new RuntimeException("TOKEN已过期,请重新登录!");
- }
- //先清空当前线程变量,防止上一个线程遗留
- UserContext.delete();
- //存取用户信息到缓存
- UserContext.setUserHolder(principal);
- //校验token
- String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));
-
- if(StringUtils.isEmpty(cacheToken)){
- throw new RuntimeException("TOKEN已过期,请重新登录!");
- }
- if(!cacheToken.equals(jwtToken) ){
- throw new RuntimeException("TOKEN已过期,请重新登录!");
- }
-// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
-// SecurityContextHolder.getContext().setAuthentication(authentication);
- }
- }
-}
+package com.ai.da.common.security.filter;
+
+import cn.hutool.core.util.StrUtil;
+import com.ai.da.common.context.UserContext;
+import com.ai.da.common.security.config.SecurityProperties;
+import com.ai.da.common.security.jwt.JWTTokenHelper;
+import com.ai.da.common.utils.LocalCacheUtils;
+import com.ai.da.common.utils.MultiReadHttpServletRequest;
+import com.ai.da.common.utils.MultiReadHttpServletResponse;
+import com.ai.da.common.utils.RequestInfoUtil;
+import com.ai.da.model.vo.AuthPrincipalVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.NonNull;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.StopWatch;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.annotation.Resource;
+import javax.security.sasl.AuthenticationException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author: dangweijian
+ * @description: 认证拦截器
+ * @create: 2020-07-10 16:50
+ **/
+@Slf4j
+@Configuration
+public class AuthenticationFilter extends OncePerRequestFilter {
+
+ @Resource
+ private JWTTokenHelper jwtTokenHelper;
+ @Resource
+ private SecurityProperties properties;
+
+ private static final List FILTER_URL =
+ Arrays.asList("/favicon.ico", "/doc.html", "api/account/login", "api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
+ "/webjars/", "/swagger-resources", "/v2/api-docs", "api/account/resetPwd",
+ "/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
+ "/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
+ "/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew",
+ "/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
+ "/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
+ "/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
+ "/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
+ "/notification"
+ );
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
+ String requestURI = httpServletRequest.getRequestURI();
+
+ if (calculateUrl(requestURI) || hasAuthorizationToken(httpServletRequest)) {
+ StopWatch stopWatch = new StopWatch();
+ HttpServletRequest wrappedRequest = httpServletRequest;
+ HttpServletResponse wrappedResponse = httpServletResponse;
+ try {
+ stopWatch.start();
+ if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
+ extracted(wrappedRequest);
+ filterChain.doFilter(wrappedRequest, wrappedResponse);
+ } else {
+ wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
+ wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
+ extracted(wrappedRequest);
+ filterChain.doFilter(wrappedRequest, wrappedResponse);
+ }
+ } catch (Exception e) {
+ SecurityContextHolder.clearContext();
+ throw e;
+ } finally {
+ stopWatch.stop();
+ }
+ } else {
+ filterChain.doFilter(httpServletRequest, httpServletResponse);
+ }
+ }
+
+ private Boolean calculateUrl(String requestURI) {
+ String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
+ return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ private boolean hasAuthorizationToken(HttpServletRequest request) {
+ String authorizationHeader = request.getHeader("Authorization");
+ return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
+ }
+
+ private void extracted(HttpServletRequest request) throws AuthenticationException {
+ String jwtToken = request.getHeader(properties.getJwtTokenHeader());
+// log.debug("后台检查令牌:{}", jwtToken);
+
+ if (StrUtil.isBlank(jwtToken)) {
+ String ipAddress = RequestInfoUtil.getIpAddress(request);
+ log.info("本次请求的ip为 : " + ipAddress);
+ throw new RuntimeException("请传入token!");
+ }
+ if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
+ //写死 暂时放行
+ return;
+ }
+ // 检查token
+ boolean validate = jwtTokenHelper.validateToken(jwtToken);
+ if (validate) {
+ AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
+ if (principal == null) {
+ throw new RuntimeException("TOKEN已过期,请重新登录!");
+ }
+ //先清空当前线程变量,防止上一个线程遗留
+ UserContext.delete();
+ //存取用户信息到缓存
+ UserContext.setUserHolder(principal);
+ //校验token
+ String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));
+
+ if(StringUtils.isEmpty(cacheToken)){
+ throw new RuntimeException("TOKEN已过期,请重新登录!");
+ }
+ if(!cacheToken.equals(jwtToken) ){
+ throw new RuntimeException("TOKEN已过期,请重新登录!");
+ }
+// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
+// SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ }
+}
diff --git a/src/main/java/com/ai/da/common/websocket/NotificationConnection.java b/src/main/java/com/ai/da/common/websocket/NotificationConnection.java
new file mode 100644
index 00000000..f9a9cb52
--- /dev/null
+++ b/src/main/java/com/ai/da/common/websocket/NotificationConnection.java
@@ -0,0 +1,43 @@
+package com.ai.da.common.websocket;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.websocket.*;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+@ServerEndpoint(value = "/notification")
+@Component
+@Slf4j
+public class NotificationConnection {
+
+ static Map sessionMap = new ConcurrentHashMap<>();
+ //连接建立时执行的操作
+ @OnOpen
+ public void onOpen(Session session){
+ sessionMap.put(session.getId(),session);
+ log.info("websocket is open");
+ }
+ //收到了客户端消息执行的操作
+ @OnMessage
+ public void onMessage(String text){
+ log.info("收到了一条消息:"+text);
+// return "已收到你的消息";
+ }
+ //连接关闭的时候执行的操作
+ @OnClose
+ public void onClose(Session session){
+ sessionMap.remove(session.getId());
+ log.info("websocket is close");
+ }
+
+ public void sendMsg(String message) throws IOException {
+ for(String key:sessionMap.keySet()){
+ sessionMap.get(key).getBasicRemote().sendText(message);
+ }
+ }
+}
diff --git a/src/main/java/com/ai/da/common/websocket/config/WebSocketConfig.java b/src/main/java/com/ai/da/common/websocket/config/WebSocketConfig.java
new file mode 100644
index 00000000..df5b252b
--- /dev/null
+++ b/src/main/java/com/ai/da/common/websocket/config/WebSocketConfig.java
@@ -0,0 +1,18 @@
+package com.ai.da.common.websocket.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+/**
+ * Configuration of WebSocket
+ *
+ * @author db1995
+ */
+@Configuration
+public class WebSocketConfig {
+ @Bean
+ public ServerEndpointExporter serverEndpointExporter() {
+ return new ServerEndpointExporter();
+ }
+}
diff --git a/src/main/java/com/ai/da/controller/MessageCenterController.java b/src/main/java/com/ai/da/controller/MessageCenterController.java
new file mode 100644
index 00000000..385c7557
--- /dev/null
+++ b/src/main/java/com/ai/da/controller/MessageCenterController.java
@@ -0,0 +1,56 @@
+package com.ai.da.controller;
+
+import com.ai.da.common.response.PageBaseResponse;
+import com.ai.da.common.response.Response;
+import com.ai.da.model.vo.GetNotificationVO;
+import com.ai.da.model.vo.NotificationVO;
+import com.ai.da.model.vo.PublishSysNotificationVO;
+import com.ai.da.service.MessageCenterService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+
+@Api(tags = "消息中心模块")
+@Slf4j
+@RestController
+@RequestMapping("/api/message")
+public class MessageCenterController {
+
+ @Resource
+ private MessageCenterService messageCenterService;
+
+ // 获取未读消息总数
+ @ApiOperation(value = "获取未读消息数")
+ @GetMapping("/getUnreadCount")
+ public Response