Commit 6954150fcb1f2392eb5b1e8d38b4848ec3448074
1 parent
ed30f06a
feat: websocket init
Showing
15 changed files
with
297 additions
and
2 deletions
yudao-dependencies/pom.xml
| @@ -23,8 +23,8 @@ | @@ -23,8 +23,8 @@ | ||
| 23 | <servlet.versoin>2.5</servlet.versoin> | 23 | <servlet.versoin>2.5</servlet.versoin> |
| 24 | <!-- DB 相关 --> | 24 | <!-- DB 相关 --> |
| 25 | <druid.version>1.2.15</druid.version> | 25 | <druid.version>1.2.15</druid.version> |
| 26 | - <mybatis-plus.version>3.5.3</mybatis-plus.version> | ||
| 27 | - <mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version> | 26 | + <mybatis-plus.version>3.5.3.1</mybatis-plus.version> |
| 27 | + <mybatis-plus-generator.version>3.5.3.1</mybatis-plus-generator.version> | ||
| 28 | <dynamic-datasource.version>3.6.1</dynamic-datasource.version> | 28 | <dynamic-datasource.version>3.6.1</dynamic-datasource.version> |
| 29 | <redisson.version>3.18.0</redisson.version> | 29 | <redisson.version>3.18.0</redisson.version> |
| 30 | <!-- 服务保障相关 --> | 30 | <!-- 服务保障相关 --> |
yudao-framework/pom.xml
| @@ -40,6 +40,7 @@ | @@ -40,6 +40,7 @@ | ||
| 40 | 40 | ||
| 41 | <module>yudao-spring-boot-starter-flowable</module> | 41 | <module>yudao-spring-boot-starter-flowable</module> |
| 42 | <module>yudao-spring-boot-starter-captcha</module> | 42 | <module>yudao-spring-boot-starter-captcha</module> |
| 43 | + <module>yudao-spring-boot-starter-websocket</module> | ||
| 43 | </modules> | 44 | </modules> |
| 44 | 45 | ||
| 45 | <artifactId>yudao-framework</artifactId> | 46 | <artifactId>yudao-framework</artifactId> |
yudao-framework/yudao-spring-boot-starter-websocket/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <parent> | ||
| 6 | + <groupId>cn.iocoder.boot</groupId> | ||
| 7 | + <artifactId>yudao-framework</artifactId> | ||
| 8 | + <version>${revision}</version> | ||
| 9 | + </parent> | ||
| 10 | + <modelVersion>4.0.0</modelVersion> | ||
| 11 | + <artifactId>yudao-spring-boot-starter-websocket</artifactId> | ||
| 12 | + <packaging>jar</packaging> | ||
| 13 | + | ||
| 14 | + <name>${project.artifactId}</name> | ||
| 15 | + <description>WebSocket</description> | ||
| 16 | + <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + <dependencies> | ||
| 20 | + | ||
| 21 | + <dependency> | ||
| 22 | + <groupId>cn.iocoder.boot</groupId> | ||
| 23 | + <artifactId>yudao-common</artifactId> | ||
| 24 | + </dependency> | ||
| 25 | + | ||
| 26 | + <dependency> | ||
| 27 | + <groupId>cn.iocoder.boot</groupId> | ||
| 28 | + <artifactId>yudao-spring-boot-starter-security</artifactId> | ||
| 29 | + </dependency> | ||
| 30 | + | ||
| 31 | + <dependency> | ||
| 32 | + <groupId>org.springframework.boot</groupId> | ||
| 33 | + <artifactId>spring-boot-starter-websocket</artifactId> | ||
| 34 | + </dependency> | ||
| 35 | + </dependencies> | ||
| 36 | + | ||
| 37 | +</project> | ||
| 0 | \ No newline at end of file | 38 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketHandlerConfig.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket.config; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.websocket.core.UserHandshakeInterceptor; | ||
| 4 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
| 5 | +import org.springframework.context.annotation.Bean; | ||
| 6 | +import org.springframework.web.socket.server.HandshakeInterceptor; | ||
| 7 | + | ||
| 8 | +@EnableConfigurationProperties(WebSocketProperties.class) | ||
| 9 | +public class WebSocketHandlerConfig { | ||
| 10 | + @Bean | ||
| 11 | + public HandshakeInterceptor handshakeInterceptor() { | ||
| 12 | + return new UserHandshakeInterceptor(); | ||
| 13 | + } | ||
| 14 | +} |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketProperties.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket.config; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | ||
| 5 | +import org.springframework.validation.annotation.Validated; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * WebSocket 配置项 | ||
| 9 | + * | ||
| 10 | + * @author xingyu4j | ||
| 11 | + */ | ||
| 12 | +@ConfigurationProperties("yudao.websocket") | ||
| 13 | +@Data | ||
| 14 | +@Validated | ||
| 15 | +public class WebSocketProperties { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 路径 | ||
| 19 | + */ | ||
| 20 | + private String path = ""; | ||
| 21 | + /** | ||
| 22 | + * 默认最多允许同时在线用户数 | ||
| 23 | + */ | ||
| 24 | + private int maxOnlineCount = 0; | ||
| 25 | + /** | ||
| 26 | + * 是否保存session | ||
| 27 | + */ | ||
| 28 | + private boolean sessionMap = true; | ||
| 29 | +} |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket.config; | ||
| 2 | + | ||
| 3 | +import org.springframework.boot.autoconfigure.AutoConfiguration; | ||
| 4 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
| 5 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| 6 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
| 7 | +import org.springframework.context.annotation.Bean; | ||
| 8 | +import org.springframework.web.socket.WebSocketHandler; | ||
| 9 | +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; | ||
| 10 | +import org.springframework.web.socket.server.HandshakeInterceptor; | ||
| 11 | + | ||
| 12 | +import java.util.List; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * WebSocket 自动配置 | ||
| 16 | + * | ||
| 17 | + * @author xingyu4j | ||
| 18 | + */ | ||
| 19 | +@AutoConfiguration | ||
| 20 | +// 允许使用 yudao.websocket.enable=false 禁用websocket | ||
| 21 | +@ConditionalOnProperty(prefix = "yudao.websocket", value = "enable", matchIfMissing = true) | ||
| 22 | +@EnableConfigurationProperties(WebSocketProperties.class) | ||
| 23 | +public class YudaoWebSocketAutoConfiguration { | ||
| 24 | + @Bean | ||
| 25 | + @ConditionalOnMissingBean | ||
| 26 | + public WebSocketConfigurer webSocketConfigurer(List<HandshakeInterceptor> handshakeInterceptor, | ||
| 27 | + WebSocketHandler webSocketHandler, | ||
| 28 | + WebSocketProperties webSocketProperties) { | ||
| 29 | + | ||
| 30 | + return registry -> registry | ||
| 31 | + .addHandler(webSocketHandler, webSocketProperties.getPath()) | ||
| 32 | + .addInterceptors(handshakeInterceptor.toArray(new HandshakeInterceptor[0])); | ||
| 33 | + } | ||
| 34 | +} |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/UserHandshakeInterceptor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket.core; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 4 | +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | ||
| 5 | +import org.springframework.http.server.ServerHttpRequest; | ||
| 6 | +import org.springframework.http.server.ServerHttpResponse; | ||
| 7 | +import org.springframework.web.socket.WebSocketHandler; | ||
| 8 | +import org.springframework.web.socket.server.HandshakeInterceptor; | ||
| 9 | + | ||
| 10 | +import java.util.Map; | ||
| 11 | + | ||
| 12 | +public class UserHandshakeInterceptor implements HandshakeInterceptor { | ||
| 13 | + @Override | ||
| 14 | + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { | ||
| 15 | + LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); | ||
| 16 | + attributes.put(WebSocketKeyDefine.LOGIN_USER, loginUser); | ||
| 17 | + return true; | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + @Override | ||
| 21 | + public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { | ||
| 22 | + | ||
| 23 | + } | ||
| 24 | +} |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketKeyDefine.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketMessageDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket.core; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import lombok.experimental.Accessors; | ||
| 5 | + | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +@Data | ||
| 9 | +@Accessors(chain = true) | ||
| 10 | +public class WebSocketMessageDO { | ||
| 11 | + /** | ||
| 12 | + * 接收消息的seesion | ||
| 13 | + */ | ||
| 14 | + private List<Object> seesionKeyList; | ||
| 15 | + /** | ||
| 16 | + * 发送消息 | ||
| 17 | + */ | ||
| 18 | + private String msgText; | ||
| 19 | + | ||
| 20 | + public static WebSocketMessageDO build(List<Object> seesionKeyList, String msgText) { | ||
| 21 | + return new WebSocketMessageDO().setMsgText(msgText).setSeesionKeyList(seesionKeyList); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | +} |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketSessionHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket.core; | ||
| 2 | + | ||
| 3 | +import org.springframework.web.socket.WebSocketSession; | ||
| 4 | + | ||
| 5 | +import java.util.Collection; | ||
| 6 | +import java.util.Map; | ||
| 7 | +import java.util.Set; | ||
| 8 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 9 | + | ||
| 10 | +public final class WebSocketSessionHandler { | ||
| 11 | + private WebSocketSessionHandler() { | ||
| 12 | + } | ||
| 13 | + | ||
| 14 | + private static final Map<String, WebSocketSession> SESSION_MAP = new ConcurrentHashMap<>(); | ||
| 15 | + | ||
| 16 | + public static void addSession(Object sessionKey, WebSocketSession session) { | ||
| 17 | + SESSION_MAP.put(sessionKey.toString(), session); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + public static void removeSession(Object sessionKey) { | ||
| 21 | + SESSION_MAP.remove(sessionKey.toString()); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public static WebSocketSession getSession(Object sessionKey) { | ||
| 25 | + return SESSION_MAP.get(sessionKey.toString()); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + public static Collection<WebSocketSession> getSessions() { | ||
| 29 | + return SESSION_MAP.values(); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + public static Set<String> getSessionKeys() { | ||
| 33 | + return SESSION_MAP.keySet(); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | +} |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketUtils.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket.core; | ||
| 2 | + | ||
| 3 | +import lombok.extern.slf4j.Slf4j; | ||
| 4 | +import org.springframework.web.socket.TextMessage; | ||
| 5 | +import org.springframework.web.socket.WebSocketSession; | ||
| 6 | + | ||
| 7 | +import java.io.IOException; | ||
| 8 | + | ||
| 9 | +@Slf4j | ||
| 10 | +public class WebSocketUtils { | ||
| 11 | + public static boolean sendMessage(WebSocketSession seesion, String message) { | ||
| 12 | + if (seesion == null) { | ||
| 13 | + log.error("seesion 不存在"); | ||
| 14 | + return false; | ||
| 15 | + } | ||
| 16 | + if (seesion.isOpen()) { | ||
| 17 | + try { | ||
| 18 | + seesion.sendMessage(new TextMessage(message)); | ||
| 19 | + } catch (IOException e) { | ||
| 20 | + log.error("WebSocket 消息发送异常 Session={} | msg= {} | exception={}", seesion, message, e); | ||
| 21 | + return false; | ||
| 22 | + } | ||
| 23 | + } | ||
| 24 | + return true; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public static boolean sendMessage(Object sessionKey, String message) { | ||
| 28 | + WebSocketSession session = WebSocketSessionHandler.getSession(sessionKey); | ||
| 29 | + return sendMessage(session, message); | ||
| 30 | + } | ||
| 31 | +} |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/YudaoWebSocketHandlerDecorator.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket.core; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 4 | +import org.springframework.web.socket.CloseStatus; | ||
| 5 | +import org.springframework.web.socket.WebSocketHandler; | ||
| 6 | +import org.springframework.web.socket.WebSocketSession; | ||
| 7 | +import org.springframework.web.socket.handler.WebSocketHandlerDecorator; | ||
| 8 | + | ||
| 9 | +public class YudaoWebSocketHandlerDecorator extends WebSocketHandlerDecorator { | ||
| 10 | + public YudaoWebSocketHandlerDecorator(WebSocketHandler delegate) { | ||
| 11 | + super(delegate); | ||
| 12 | + } | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * websocket 连接时执行的动作 | ||
| 16 | + * @param session websocket session 对象 | ||
| 17 | + * @throws Exception 异常对象 | ||
| 18 | + */ | ||
| 19 | + @Override | ||
| 20 | + public void afterConnectionEstablished(final WebSocketSession session) throws Exception { | ||
| 21 | + Object sessionKey = sessionKeyGen(session); | ||
| 22 | + WebSocketSessionHandler.addSession(sessionKey, session); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * websocket 关闭连接时执行的动作 | ||
| 27 | + * @param session websocket session 对象 | ||
| 28 | + * @param closeStatus 关闭状态对象 | ||
| 29 | + * @throws Exception 异常对象 | ||
| 30 | + */ | ||
| 31 | + @Override | ||
| 32 | + public void afterConnectionClosed(final WebSocketSession session, CloseStatus closeStatus) throws Exception { | ||
| 33 | + Object sessionKey = sessionKeyGen(session); | ||
| 34 | + WebSocketSessionHandler.removeSession(sessionKey); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public Object sessionKeyGen(WebSocketSession webSocketSession) { | ||
| 38 | + | ||
| 39 | + Object obj = webSocketSession.getAttributes().get(WebSocketKeyDefine.LOGIN_USER); | ||
| 40 | + | ||
| 41 | + if (obj instanceof LoginUser) { | ||
| 42 | + LoginUser loginUser = (LoginUser) obj; | ||
| 43 | + // userId 作为唯一区分 | ||
| 44 | + return String.valueOf(loginUser.getId()); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + return null; | ||
| 48 | + } | ||
| 49 | +} |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/package-info.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.websocket; |
yudao-framework/yudao-spring-boot-starter-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
0 → 100644
yudao-server/src/main/resources/application.yaml
| @@ -92,6 +92,11 @@ yudao: | @@ -92,6 +92,11 @@ yudao: | ||
| 92 | security: | 92 | security: |
| 93 | permit-all_urls: | 93 | permit-all_urls: |
| 94 | - /admin-ui/** # /resources/admin-ui 目录下的静态资源 | 94 | - /admin-ui/** # /resources/admin-ui 目录下的静态资源 |
| 95 | + websocket: | ||
| 96 | + enable: true # websocket的开关 | ||
| 97 | + path: /websocket/message # 路径 | ||
| 98 | + maxOnlineCount: 0 # 最大连接人数 | ||
| 99 | + sessionMap: true # 保存sessionMap | ||
| 95 | swagger: | 100 | swagger: |
| 96 | title: 管理后台 | 101 | title: 管理后台 |
| 97 | description: 提供管理员管理的所有功能 | 102 | description: 提供管理员管理的所有功能 |