From 65ae5ab362c53e609bafcad06049228b86b2d60f Mon Sep 17 00:00:00 2001
From: zendwang <63756+zendwang@user.noreply.gitee.com>
Date: 星期三, 29 三月 2023 22:18:42 +0800
Subject: [PATCH] !317 集成websocket功能 * add 增加 ruoyi-common-websocket 模块 支持token鉴权 支持分布式集群消息同步
---
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/constant/WebSocketConstants.java | 28 ++
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/utils/WebSocketUtils.java | 102 ++++++++++
ruoyi-common/ruoyi-common-bom/pom.xml | 6
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/handler/PlusWebSocketHandler.java | 104 ++++++++++
ruoyi-admin/src/main/resources/application.yml | 8
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/dto/WebSocketMessageDto.java | 29 ++
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/properties/WebSocketProperties.java | 27 ++
ruoyi-common/ruoyi-common-websocket/pom.xml | 41 ++++
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/holder/WebSocketSessionHolder.java | 37 +++
ruoyi-common/ruoyi-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports | 1
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/listener/WebSocketTopicListener.java | 38 +++
ruoyi-modules/ruoyi-demo/pom.xml | 4
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/interceptor/PlusWebSocketInterceptor.java | 51 +++++
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/WeSocketController.java | 33 +++
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/WebSocketConfig.java | 62 ++++++
15 files changed, 571 insertions(+), 0 deletions(-)
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 628f7ae..758b264 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -280,3 +280,11 @@
show-details: ALWAYS
logfile:
external-file: ./logs/sys-console.log
+
+--- # websocket
+websocket:
+ enabled: true
+ # 璺緞
+ path: /websocket
+ # 璁剧疆璁块棶婧愬湴鍧�
+ allowedOrigins: '*'
diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml
index 3bd6c09..f2a36f2 100644
--- a/ruoyi-common/ruoyi-common-bom/pom.xml
+++ b/ruoyi-common/ruoyi-common-bom/pom.xml
@@ -159,6 +159,12 @@
<version>${revision}</version>
</dependency>
+ <!-- WebSocket妯″潡 -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-websocket</artifactId>
+ <version>${revision}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
diff --git a/ruoyi-common/ruoyi-common-websocket/pom.xml b/ruoyi-common/ruoyi-common-websocket/pom.xml
new file mode 100644
index 0000000..21a5727
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-common-websocket</artifactId>
+
+ <description>
+ ruoyi-common-websocket 妯″潡
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-redis</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-satoken</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-json</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-websocket</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/WebSocketConfig.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/WebSocketConfig.java
new file mode 100644
index 0000000..c9cb613
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/WebSocketConfig.java
@@ -0,0 +1,62 @@
+package com.ruoyi.common.websocket.config;
+
+import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.websocket.config.properties.WebSocketProperties;
+import com.ruoyi.common.websocket.constant.WebSocketConstants;
+import com.ruoyi.common.websocket.handler.PlusWebSocketHandler;
+import com.ruoyi.common.websocket.interceptor.PlusWebSocketInterceptor;
+import com.ruoyi.common.websocket.listener.WebSocketTopicListener;
+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;
+
+/**
+ * WebSocket 閰嶇疆
+ *
+ * @author zendwang
+ */
+@AutoConfiguration
+@ConditionalOnProperty(value = "websocket.enabled", havingValue = "true")
+@EnableConfigurationProperties(WebSocketProperties.class)
+@EnableWebSocket
+public class WebSocketConfig {
+
+ @Bean
+ public WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor handshakeInterceptor,
+ WebSocketHandler webSocketHandler,
+ WebSocketProperties webSocketProperties) {
+ if (StrUtil.isBlank(webSocketProperties.getPath())) {
+ webSocketProperties.setPath("/websocket");
+ }
+
+ if (StrUtil.isBlank(webSocketProperties.getAllowedOrigins())) {
+ webSocketProperties.setAllowedOrigins("*");
+ }
+
+ return registry -> registry
+ .addHandler(webSocketHandler, webSocketProperties.getPath())
+ .addInterceptors(handshakeInterceptor)
+ .setAllowedOrigins(webSocketProperties.getAllowedOrigins());
+ }
+
+ @Bean
+ public HandshakeInterceptor handshakeInterceptor() {
+ return new PlusWebSocketInterceptor();
+ }
+
+ @Bean
+ public WebSocketHandler webSocketHandler() {
+ return new PlusWebSocketHandler();
+ }
+
+ @Bean
+ public WebSocketTopicListener topicListener() {
+ return new WebSocketTopicListener();
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/properties/WebSocketProperties.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/properties/WebSocketProperties.java
new file mode 100644
index 0000000..ed57b9c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/properties/WebSocketProperties.java
@@ -0,0 +1,27 @@
+package com.ruoyi.common.websocket.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * WebSocket 閰嶇疆椤�
+ *
+ * @author zendwang
+ */
+@ConfigurationProperties("websocket")
+@Data
+public class WebSocketProperties {
+
+ private Boolean enable;
+
+ /**
+ * 璺緞
+ */
+ private String path;
+
+ /**
+ * 璁剧疆璁块棶婧愬湴鍧�
+ */
+ private String allowedOrigins;
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/constant/WebSocketConstants.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/constant/WebSocketConstants.java
new file mode 100644
index 0000000..8c88832
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/constant/WebSocketConstants.java
@@ -0,0 +1,28 @@
+package com.ruoyi.common.websocket.constant;
+
+/**
+ * websocket鐨勫父閲忛厤缃�
+ *
+ * @author zendwang
+ */
+public interface WebSocketConstants {
+ /**
+ * websocketSession涓殑鍙傛暟鐨刱ey
+ */
+ String LOGIN_USER_KEY = "loginUser";
+
+ /**
+ * 璁㈤槄鐨勯閬�
+ */
+ String WEB_SOCKET_TOPIC = "global:websocket";
+
+ /**
+ * 鍓嶇蹇冭烦妫�鏌ョ殑鍛戒护
+ */
+ String PING = "ping";
+
+ /**
+ * 鏈嶅姟绔績璺虫仮澶嶇殑瀛楃涓�
+ */
+ String PONG = "pong";
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/dto/WebSocketMessageDto.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/dto/WebSocketMessageDto.java
new file mode 100644
index 0000000..c3b1b33
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/dto/WebSocketMessageDto.java
@@ -0,0 +1,29 @@
+package com.ruoyi.common.websocket.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 娑堟伅鐨刣to
+ *
+ * @author zendwang
+ */
+@Builder
+@Data
+public class WebSocketMessageDto implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 闇�瑕佹帹閫佸埌鐨剆ession key 鍒楄〃
+ */
+ private List<Long> sessionKeys;
+
+ /**
+ * 闇�瑕佸彂閫佺殑娑堟伅
+ */
+ private String message;
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/handler/PlusWebSocketHandler.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/handler/PlusWebSocketHandler.java
new file mode 100644
index 0000000..8f4c866
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/handler/PlusWebSocketHandler.java
@@ -0,0 +1,104 @@
+package com.ruoyi.common.websocket.handler;
+
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.websocket.dto.WebSocketMessageDto;
+import com.ruoyi.common.websocket.holder.WebSocketSessionHolder;
+import com.ruoyi.common.websocket.utils.WebSocketUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.socket.*;
+import org.springframework.web.socket.handler.AbstractWebSocketHandler;
+
+import java.util.List;
+
+import static com.ruoyi.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
+
+/**
+ * WebSocketHandler 瀹炵幇绫�
+ *
+ * @author zendwang
+ */
+@Slf4j
+public class PlusWebSocketHandler extends AbstractWebSocketHandler {
+
+ /**
+ * 杩炴帴鎴愬姛鍚�
+ *
+ * @param session
+ */
+ @Override
+ public void afterConnectionEstablished(WebSocketSession session) {
+ LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
+ WebSocketSessionHolder.addSession(loginUser.getUserId(), session);
+ log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
+ }
+
+ /**
+ * 澶勭悊鍙戦�佹潵鐨勬枃鏈秷鎭�
+ *
+ * @param session
+ * @param message
+ * @throws Exception
+ */
+ @Override
+ protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+ LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
+ log.info("PlusWebSocketHandler, 杩炴帴锛�" + session.getId() + "锛屽凡鏀跺埌娑堟伅:" + message.getPayload());
+ List<Long> userIds = List.of(loginUser.getUserId());
+ WebSocketMessageDto webSocketMessageDto = WebSocketMessageDto.builder()
+ .sessionKeys(userIds).message(message.getPayload()).build();
+ WebSocketUtils.publishMessage(webSocketMessageDto);
+ }
+
+ @Override
+ protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
+ super.handleBinaryMessage(session, message);
+ }
+
+ /**
+ * 蹇冭烦鐩戞祴鐨勫洖澶�
+ *
+ * @param session
+ * @param message
+ * @throws Exception
+ */
+ @Override
+ protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
+ WebSocketUtils.sendPongMessage(session);
+ }
+
+ /**
+ * 杩炴帴鍑洪敊鏃�
+ *
+ * @param session
+ * @param exception
+ * @throws Exception
+ */
+ @Override
+ public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
+ log.error("[transport error] sessionId: {} , exception:{}", session.getId(), exception.getMessage());
+ }
+
+ /**
+ * 杩炴帴鍏抽棴鍚�
+ *
+ * @param session
+ * @param status
+ */
+ @Override
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
+ LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
+ WebSocketSessionHolder.removeSession(loginUser.getUserId());
+ log.info("[disconnect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
+ }
+
+ /**
+ * 鏄惁鏀寔鍒嗙墖娑堟伅
+ *
+ * @return
+ */
+ @Override
+ public boolean supportsPartialMessages() {
+ return false;
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/holder/WebSocketSessionHolder.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/holder/WebSocketSessionHolder.java
new file mode 100644
index 0000000..facc84d
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/holder/WebSocketSessionHolder.java
@@ -0,0 +1,37 @@
+package com.ruoyi.common.websocket.holder;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * WebSocketSession 鐢ㄤ簬淇濆瓨褰撳墠鎵�鏈夊湪绾跨殑浼氳瘽淇℃伅
+ *
+ * @author zendwang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class WebSocketSessionHolder {
+
+ private static final Map<Long, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();
+
+ public static void addSession(Long sessionKey, WebSocketSession session) {
+ USER_SESSION_MAP.put(sessionKey, session);
+ }
+
+ public static void removeSession(Long sessionKey) {
+ if (USER_SESSION_MAP.containsKey(sessionKey)) {
+ USER_SESSION_MAP.remove(sessionKey);
+ }
+ }
+
+ public static WebSocketSession getSessions(Long sessionKey) {
+ return USER_SESSION_MAP.get(sessionKey);
+ }
+
+ public static Boolean existSession(Long sessionKey) {
+ return USER_SESSION_MAP.containsKey(sessionKey);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/interceptor/PlusWebSocketInterceptor.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/interceptor/PlusWebSocketInterceptor.java
new file mode 100644
index 0000000..bbb1d48
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/interceptor/PlusWebSocketInterceptor.java
@@ -0,0 +1,51 @@
+package com.ruoyi.common.websocket.interceptor;
+
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.satoken.utils.LoginHelper;
+import lombok.extern.slf4j.Slf4j;
+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;
+
+import static com.ruoyi.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
+
+/**
+ * WebSocket鎻℃墜璇锋眰鐨勬嫤鎴櫒
+ *
+ * @author zendwang
+ */
+@Slf4j
+public class PlusWebSocketInterceptor implements HandshakeInterceptor {
+
+ /**
+ * 鎻℃墜鍓�
+ *
+ * @param request request
+ * @param response response
+ * @param wsHandler wsHandler
+ * @param attributes attributes
+ * @return 鏄惁鎻℃墜鎴愬姛
+ */
+ @Override
+ public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
+ LoginUser loginUser = LoginHelper.getLoginUser();
+ attributes.put(LOGIN_USER_KEY, loginUser);
+ return true;
+ }
+
+ /**
+ * 鎻℃墜鍚�
+ *
+ * @param request request
+ * @param response response
+ * @param wsHandler wsHandler
+ * @param exception 寮傚父
+ */
+ @Override
+ public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
+
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/listener/WebSocketTopicListener.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/listener/WebSocketTopicListener.java
new file mode 100644
index 0000000..cdc141f
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/listener/WebSocketTopicListener.java
@@ -0,0 +1,38 @@
+package com.ruoyi.common.websocket.listener;
+
+import cn.hutool.core.collection.CollUtil;
+import com.ruoyi.common.websocket.holder.WebSocketSessionHolder;
+import com.ruoyi.common.websocket.utils.WebSocketUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.Ordered;
+
+/**
+ * WebSocket 涓婚璁㈤槄鐩戝惉鍣�
+ *
+ * @author zendwang
+ */
+@Slf4j
+public class WebSocketTopicListener implements ApplicationRunner, Ordered {
+
+ @Override
+ public void run(ApplicationArguments args) throws Exception {
+ WebSocketUtils.subscribeMessage((message) -> {
+ log.info("WebSocket涓婚璁㈤槄鏀跺埌娑堟伅session keys={} message={}锛�", message.getSessionKeys(), message.getMessage());
+ if (CollUtil.isNotEmpty(message.getSessionKeys())) {
+ message.getSessionKeys().forEach(key -> {
+ if (WebSocketSessionHolder.existSession(key)) {
+ WebSocketUtils.sendMessage(key, message.getMessage());
+ }
+ });
+ }
+ });
+ log.info("鍒濆鍖朩ebSocket涓婚璁㈤槄鐩戝惉鍣ㄦ垚鍔�");
+ }
+
+ @Override
+ public int getOrder() {
+ return -1;
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/utils/WebSocketUtils.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/utils/WebSocketUtils.java
new file mode 100644
index 0000000..b92ebc4
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/utils/WebSocketUtils.java
@@ -0,0 +1,102 @@
+package com.ruoyi.common.websocket.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.json.utils.JsonUtils;
+import com.ruoyi.common.redis.utils.RedisUtils;
+import com.ruoyi.common.satoken.utils.LoginHelper;
+import com.ruoyi.common.websocket.dto.WebSocketMessageDto;
+import com.ruoyi.common.websocket.holder.WebSocketSessionHolder;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.socket.PongMessage;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static com.ruoyi.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
+import static com.ruoyi.common.websocket.constant.WebSocketConstants.WEB_SOCKET_TOPIC;
+
+/**
+ * 宸ュ叿绫�
+ *
+ * @author zendwang
+ */
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class WebSocketUtils {
+
+ /**
+ * 鍙戦�佹秷鎭�
+ * @param sessionKey
+ * @param message
+ */
+ public static void sendMessage(Long sessionKey, String message) {
+ WebSocketSession session = WebSocketSessionHolder.getSessions(sessionKey);
+ sendMessage(session, message);
+ }
+
+ /**
+ * 璁㈤槄娑堟伅
+ *
+ * @param consumer
+ */
+ public static void subscribeMessage(Consumer<WebSocketMessageDto> consumer) {
+ RedisUtils.subscribe(WEB_SOCKET_TOPIC, WebSocketMessageDto.class, consumer);
+ }
+
+ /**
+ * 鍙戝竷璁㈤槄鐨勬秷鎭�
+ *
+ * @param webSocketMessage
+ */
+ public static void publishMessage(WebSocketMessageDto webSocketMessage) {
+ List<Long> unsentSessionKeys = new ArrayList<>();
+ // 褰撳墠鏈嶅姟鍐卻ession,鐩存帴鍙戦�佹秷鎭�
+ for (Long sessionKey: webSocketMessage.getSessionKeys()) {
+ if (WebSocketSessionHolder.existSession(sessionKey)) {
+ WebSocketUtils.sendMessage(sessionKey, webSocketMessage.getMessage());
+ continue;
+ }
+ unsentSessionKeys.add(sessionKey);
+ }
+ // 涓嶅湪褰撳墠鏈嶅姟鍐卻ession,鍙戝竷璁㈤槄娑堟伅
+ if (CollUtil.isNotEmpty(unsentSessionKeys)) {
+ WebSocketMessageDto broadcastMessage = WebSocketMessageDto.builder()
+ .message(webSocketMessage.getMessage()).sessionKeys(unsentSessionKeys).build();
+ RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> {
+ log.info(" WebSocket鍙戦�佷富棰樿闃呮秷鎭痶opic:{} session keys:{} message:{}",
+ WEB_SOCKET_TOPIC, unsentSessionKeys, webSocketMessage.getMessage());
+ });
+ }
+ }
+
+ public static void sendPongMessage(WebSocketSession session) {
+ sendMessage(session, new PongMessage());
+ }
+
+ public static void sendMessage(WebSocketSession session, String message) {
+ sendMessage(session, new TextMessage(message));
+ }
+
+ private static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) {
+ if (session == null || !session.isOpen()) {
+ log.error("[send] session浼氳瘽宸茬粡鍏抽棴");
+ } else {
+ try {
+ // 鑾峰彇褰撳墠浼氳瘽涓殑鐢ㄦ埛
+ LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
+ session.sendMessage(message);
+ log.info("[send] sessionId: {},userId:{},userType:{},message:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType(), message);
+ } catch (IOException e) {
+ log.error("[send] session({}) 鍙戦�佹秷鎭�({}) 寮傚父", session, message, e);
+ }
+ }
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..3a059fa
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.ruoyi.common.websocket.config.WebSocketConfig
diff --git a/ruoyi-modules/ruoyi-demo/pom.xml b/ruoyi-modules/ruoyi-demo/pom.xml
index dc8d335..e107149 100644
--- a/ruoyi-modules/ruoyi-demo/pom.xml
+++ b/ruoyi-modules/ruoyi-demo/pom.xml
@@ -94,6 +94,10 @@
<artifactId>ruoyi-common-tenant</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-websocket</artifactId>
+ </dependency>
<!-- 鐭俊 鐢ㄥ摢涓鍏ュ摢涓緷璧� -->
<!-- <dependency>-->
<!-- <groupId>com.aliyun</groupId>-->
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/WeSocketController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/WeSocketController.java
new file mode 100644
index 0000000..49595c7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/WeSocketController.java
@@ -0,0 +1,33 @@
+package com.ruoyi.demo.controller;
+
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.websocket.dto.WebSocketMessageDto;
+import com.ruoyi.common.websocket.utils.WebSocketUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * WebSocket 婕旂ず妗堜緥
+ *
+ * @author zendwang
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/websocket")
+@Slf4j
+public class WeSocketController {
+
+ /**
+ * 鍙戝竷娑堟伅
+ *
+ * @param dto 鍙戦�佸唴瀹�
+ */
+ @GetMapping("/send")
+ public R<Void> send(WebSocketMessageDto dto) throws InterruptedException {
+ WebSocketUtils.publishMessage(dto);
+ return R.ok("鎿嶄綔鎴愬姛");
+ }
+}
--
Gitblit v1.9.3