diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml
index dbd6e2f3c..bbae1a273 100644
--- a/yudao-dependencies/pom.xml
+++ b/yudao-dependencies/pom.xml
@@ -175,6 +175,12 @@
${revision}
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-websocket
+ ${revision}
+
+
com.github.xiaoymin
knife4j-openapi3-spring-boot-starter
diff --git a/yudao-framework/yudao-spring-boot-starter-websocket/pom.xml b/yudao-framework/yudao-spring-boot-starter-websocket/pom.xml
index 320e52c48..3e9cecf3c 100644
--- a/yudao-framework/yudao-spring-boot-starter-websocket/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-websocket/pom.xml
@@ -12,18 +12,22 @@
jar
${project.artifactId}
- WebSocket
+ WebSocket 框架,支持多节点的广播
https://github.com/YunaiV/ruoyi-vue-pro
-
cn.iocoder.boot
yudao-common
+
+
cn.iocoder.boot
yudao-spring-boot-starter-security
diff --git a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketHandlerConfig.java b/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketHandlerConfig.java
deleted file mode 100644
index 02c3415d5..000000000
--- a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketHandlerConfig.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package cn.iocoder.yudao.framework.websocket.config;
-
-import cn.iocoder.yudao.framework.websocket.core.UserHandshakeInterceptor;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.web.socket.server.HandshakeInterceptor;
-
-@EnableConfigurationProperties(WebSocketProperties.class)
-public class WebSocketHandlerConfig {
- @Bean
- public HandshakeInterceptor handshakeInterceptor() {
- return new UserHandshakeInterceptor();
- }
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketProperties.java b/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketProperties.java
index 0ab1b498f..7c1bd5abe 100644
--- a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketProperties.java
+++ b/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketProperties.java
@@ -15,15 +15,8 @@ import org.springframework.validation.annotation.Validated;
public class WebSocketProperties {
/**
- * 路径
+ * WebSocket 的连接路径
*/
- private String path = "";
- /**
- * 默认最多允许同时在线用户数
- */
- private int maxOnlineCount = 0;
- /**
- * 是否保存session
- */
- private boolean sessionMap = true;
+ private String path = "/ws";
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java
index f8c50ae6a..e116f3c2c 100644
--- a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java
@@ -1,11 +1,17 @@
package cn.iocoder.yudao.framework.websocket.config;
+import cn.iocoder.yudao.framework.websocket.core.handler.JsonWebSocketMessageHandler;
+import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;
+import cn.iocoder.yudao.framework.websocket.core.security.LoginUserHandshakeInterceptor;
+import cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionHandlerDecorator;
+import cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;
+import cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManagerImpl;
import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;
@@ -17,18 +23,41 @@ import java.util.List;
* @author xingyu4j
*/
@AutoConfiguration
-// 允许使用 yudao.websocket.enable=false 禁用websocket
-@ConditionalOnProperty(prefix = "yudao.websocket", value = "enable", matchIfMissing = true)
+@EnableWebSocket // 开启 websocket
+@ConditionalOnProperty(prefix = "yudao.websocket", value = "enable", matchIfMissing = true) // 允许使用 yudao.websocket.enable=false 禁用 websocket
+
@EnableConfigurationProperties(WebSocketProperties.class)
public class YudaoWebSocketAutoConfiguration {
+
@Bean
- @ConditionalOnMissingBean
- public WebSocketConfigurer webSocketConfigurer(List handshakeInterceptor,
+ public WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor[] handshakeInterceptors,
WebSocketHandler webSocketHandler,
WebSocketProperties webSocketProperties) {
-
return registry -> registry
+ // 添加 WebSocketHandler
.addHandler(webSocketHandler, webSocketProperties.getPath())
- .addInterceptors(handshakeInterceptor.toArray(new HandshakeInterceptor[0]));
+ .addInterceptors(handshakeInterceptors)
+ // 允许跨域,否则前端连接会直接断开
+ .setAllowedOriginPatterns("*");
}
-}
+
+ @Bean
+ public HandshakeInterceptor handshakeInterceptor() {
+ return new LoginUserHandshakeInterceptor();
+ }
+
+ @Bean
+ public WebSocketHandler webSocketHandler(WebSocketSessionManager sessionManager,
+ List extends WebSocketMessageListener>> messageListeners) {
+ // 1. 创建 JsonWebSocketMessageHandler 对象,处理消息
+ JsonWebSocketMessageHandler messageHandler = new JsonWebSocketMessageHandler(messageListeners);
+ // 2. 创建 WebSocketSessionHandlerDecorator 对象,处理连接
+ return new WebSocketSessionHandlerDecorator(messageHandler, sessionManager);
+ }
+
+ @Bean
+ public WebSocketSessionManager webSocketSessionManager() {
+ return new WebSocketSessionManagerImpl();
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/UserHandshakeInterceptor.java b/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/UserHandshakeInterceptor.java
deleted file mode 100644
index 3f2fa4ec3..000000000
--- a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/UserHandshakeInterceptor.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.framework.websocket.core;
-
-import cn.iocoder.yudao.framework.security.core.LoginUser;
-import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
-import org.springframework.http.server.ServerHttpRequest;
-import org.springframework.http.server.ServerHttpResponse;
-import org.springframework.web.socket.WebSocketHandler;
-import org.springframework.web.socket.server.HandshakeInterceptor;
-
-import java.util.Map;
-
-public class UserHandshakeInterceptor implements HandshakeInterceptor {
- @Override
- public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {
- LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
- attributes.put(WebSocketKeyDefine.LOGIN_USER, loginUser);
- return true;
- }
-
- @Override
- public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
-
- }
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketKeyDefine.java b/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketKeyDefine.java
deleted file mode 100644
index f75ebc41c..000000000
--- a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketKeyDefine.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package cn.iocoder.yudao.framework.websocket.core;
-
-
-import lombok.Data;
-
-@Data
-public class WebSocketKeyDefine {
- public static final String LOGIN_USER ="LOGIN_USER";
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketMessageDO.java b/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketMessageDO.java
deleted file mode 100644
index 7bb348e99..000000000
--- a/yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketMessageDO.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.framework.websocket.core;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.util.List;
-
-@Data
-@Accessors(chain = true)
-public class WebSocketMessageDO {
- /**
- * 接收消息的seesion
- */
- private List
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-websocket
+
+
cn.iocoder.boot
@@ -116,11 +121,6 @@
yudao-spring-boot-starter-file
-
-
- org.springframework.boot
- spring-boot-starter-websocket
-
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/SemaphoreUtils.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/SemaphoreUtils.java
deleted file mode 100644
index 67a87f169..000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/SemaphoreUtils.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package cn.iocoder.yudao.module.infra.websocket;
-
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.concurrent.Semaphore;
-
-/**
- * 信号量相关处理
- *
- */
-@Slf4j
-public class SemaphoreUtils {
-
- /**
- * 获取信号量
- *
- * @param semaphore
- * @return
- */
- public static boolean tryAcquire(Semaphore semaphore) {
- boolean flag = false;
-
- try {
- flag = semaphore.tryAcquire();
- } catch (Exception e) {
- log.error("获取信号量异常", e);
- }
-
- return flag;
- }
-
- /**
- * 释放信号量
- *
- * @param semaphore
- */
- public static void release(Semaphore semaphore) {
-
- try {
- semaphore.release();
- } catch (Exception e) {
- log.error("释放信号量异常", e);
- }
- }
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketConfig.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketConfig.java
deleted file mode 100644
index 380bc9317..000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketConfig.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package cn.iocoder.yudao.module.infra.websocket;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.socket.server.standard.ServerEndpointExporter;
-
-/**
- * websocket 配置
- */
-@Configuration
-public class WebSocketConfig {
- @Bean
- public ServerEndpointExporter serverEndpointExporter() {
- return new ServerEndpointExporter();
- }
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketServer.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketServer.java
deleted file mode 100644
index f0cfdd9dc..000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketServer.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package cn.iocoder.yudao.module.infra.websocket;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-import javax.websocket.*;
-import javax.websocket.server.ServerEndpoint;
-import java.util.concurrent.Semaphore;
-
-/**
- * websocket 消息处理
- */
-@Component
-@ServerEndpoint("/websocket/message")
-@Slf4j
-public class WebSocketServer {
-
- /**
- * 默认最多允许同时在线用户数100
- */
- public static int socketMaxOnlineCount = 100;
-
- private static final Semaphore SOCKET_SEMAPHORE = new Semaphore(socketMaxOnlineCount);
-
- /**
- * 连接建立成功调用的方法
- */
- @OnOpen
- public void onOpen(Session session) throws Exception {
- // 尝试获取信号量
- boolean semaphoreFlag = SemaphoreUtils.tryAcquire(SOCKET_SEMAPHORE);
- if (!semaphoreFlag) {
- // 未获取到信号量
- log.error("当前在线人数超过限制数:{}", socketMaxOnlineCount);
- WebSocketUsers.sendMessage(session, "当前在线人数超过限制数:" + socketMaxOnlineCount);
- session.close();
- } else {
- String userId = WebSocketUsers.getParam("userId", session);
- if (userId != null) {
- // 添加用户
- WebSocketUsers.addSession(userId, session);
- log.info("用户【userId={}】建立连接,当前连接用户总数:{}", userId, WebSocketUsers.getUsers().size());
- WebSocketUsers.sendMessage(session, "接收内容:连接成功");
- } else {
- WebSocketUsers.sendMessage(session, "接收内容:连接失败");
- }
- }
- }
-
- /**
- * 连接关闭时处理
- */
- @OnClose
- public void onClose(Session session) {
- log.info("用户【sessionId={}】关闭连接!", session.getId());
- // 移除用户
- WebSocketUsers.removeSession(session);
- // 获取到信号量则需释放
- SemaphoreUtils.release(SOCKET_SEMAPHORE);
- }
-
- /**
- * 抛出异常时处理
- */
- @OnError
- public void onError(Session session, Throwable exception) throws Exception {
- if (session.isOpen()) {
- // 关闭连接
- session.close();
- }
- String sessionId = session.getId();
- log.info("用户【sessionId={}】连接异常!异常信息:{}", sessionId, exception);
- // 移出用户
- WebSocketUsers.removeSession(session);
- // 获取到信号量则需释放
- SemaphoreUtils.release(SOCKET_SEMAPHORE);
- }
-
- /**
- * 收到客户端消息时调用的方法
- */
- @OnMessage
- public void onMessage(Session session, String message) {
- WebSocketUsers.sendMessage(session, "接收内容:" + message);
- }
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketUsers.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketUsers.java
deleted file mode 100644
index 281a97c7d..000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/WebSocketUsers.java
+++ /dev/null
@@ -1,178 +0,0 @@
-package cn.iocoder.yudao.module.infra.websocket;
-
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.bouncycastle.util.Strings;
-
-import javax.validation.constraints.NotNull;
-import javax.websocket.Session;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * websocket 客户端用户
- */
-@Slf4j
-public class WebSocketUsers {
-
- /**
- * 用户集
- * TODO 需要登录用户的session?
- */
- private static final Map SESSION_MAP = new ConcurrentHashMap<>();
-
- /**
- * 存储用户
- *
- * @param userId 唯一键
- * @param session 用户信息
- */
- public static void addSession(String userId, Session session) {
- SESSION_MAP.put(userId, session);
- }
-
- /**
- * 移除用户
- *
- * @param session 用户信息
- * @return 移除结果
- */
- public static boolean removeSession(Session session) {
- String key = null;
- boolean flag = SESSION_MAP.containsValue(session);
- if (flag) {
- Set> entries = SESSION_MAP.entrySet();
- for (Map.Entry entry : entries) {
- Session value = entry.getValue();
- if (value.equals(session)) {
- key = entry.getKey();
- break;
- }
- }
- } else {
- return true;
- }
- return removeSession(key);
- }
-
- /**
- * 移出用户
- *
- * @param userId 用户id
- */
- public static boolean removeSession(String userId) {
- log.info("用户【userId={}】退出", userId);
- Session remove = SESSION_MAP.remove(userId);
- if (remove != null) {
- boolean containsValue = SESSION_MAP.containsValue(remove);
- log.info("用户【userId={}】退出{},当前连接用户总数:{}", userId, containsValue ? "失败" : "成功", SESSION_MAP.size());
- return containsValue;
- } else {
- return true;
- }
- }
-
- /**
- * 获取在线用户列表
- *
- * @return 返回用户集合
- */
- public static Map getUsers() {
- return SESSION_MAP;
- }
-
- /**
- * 向所有在线人发送消息
- *
- * @param message 消息内容
- */
- public static void sendMessageToAll(String message) {
- SESSION_MAP.forEach((userId, session) -> {
- if (session.isOpen()) {
- sendMessage(session, message);
- }
- });
- }
-
- /**
- * 异步发送文本消息
- *
- * @param session 用户session
- * @param message 消息内容
- */
- public static void sendMessageAsync(Session session, String message) {
- if (session.isOpen()) {
- // TODO 需要加synchronized锁(synchronized(session))?单个session创建线程?
- session.getAsyncRemote().sendText(message);
- } else {
- log.warn("用户【session={}】不在线", session.getId());
- }
- }
-
- /**
- * 同步发送文本消息
- *
- * @param session 用户session
- * @param message 消息内容
- */
- public static void sendMessage(Session session, String message) {
- try {
- if (session.isOpen()) {
- // TODO 需要加synchronized锁(synchronized(session))?单个session创建线程?
- session.getBasicRemote().sendText(message);
- } else {
- log.warn("用户【session={}】不在线", session.getId());
- }
- } catch (IOException e) {
- log.error("发送消息异常", e);
- }
-
- }
-
- /**
- * 根据用户id发送消息
- *
- * @param userId 用户id
- * @param message 消息内容
- */
- public static void sendMessage(String userId, String message) {
- Session session = SESSION_MAP.get(userId);
- //判断是否存在该用户的session,并且是否在线
- if (session == null || !session.isOpen()) {
- return;
- }
- sendMessage(session, message);
- }
-
-
- /**
- * 获取session中的指定参数值
- *
- * @param key 参数key
- * @param session 用户session
- */
- public static String getParam(@NotNull String key, Session session) {
- //TODO 目前只针对获取一个key的值,后期根据情况拓展多个 或者直接在onClose onOpen上获取参数?
- String value = null;
- Map> parameters = session.getRequestParameterMap();
- if (MapUtil.isNotEmpty(parameters)) {
- value = parameters.get(key).get(0);
- } else {
- String queryString = session.getQueryString();
- if (!StrUtil.isEmpty(queryString)) {
- String[] params = Strings.split(queryString, '&');
- for (String paramPair : params) {
- String[] nameValues = Strings.split(paramPair, '=');
- if (key.equals(nameValues[0])) {
- value = nameValues[1];
- }
- }
- }
- }
- return value;
- }
-}
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index a51fa9bbb..605eef273 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -146,9 +146,7 @@ yudao:
- /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,不需要登录
websocket:
enable: true # websocket的开关
- path: /websocket/message # 路径
- maxOnlineCount: 0 # 最大连接人数
- sessionMap: true # 保存sessionMap
+ path: /infra/ws # 路径
swagger:
title: 芋道快速开发平台
description: 提供管理后台、用户 App 的所有功能