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