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 | 23 | <servlet.versoin>2.5</servlet.versoin> |
| 24 | 24 | <!-- DB 相关 --> |
| 25 | 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 | 28 | <dynamic-datasource.version>3.6.1</dynamic-datasource.version> |
| 29 | 29 | <redisson.version>3.18.0</redisson.version> |
| 30 | 30 | <!-- 服务保障相关 --> | ... | ... |
yudao-framework/pom.xml
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 | 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 | 92 | security: |
| 93 | 93 | permit-all_urls: |
| 94 | 94 | - /admin-ui/** # /resources/admin-ui 目录下的静态资源 |
| 95 | + websocket: | |
| 96 | + enable: true # websocket的开关 | |
| 97 | + path: /websocket/message # 路径 | |
| 98 | + maxOnlineCount: 0 # 最大连接人数 | |
| 99 | + sessionMap: true # 保存sessionMap | |
| 95 | 100 | swagger: |
| 96 | 101 | title: 管理后台 |
| 97 | 102 | description: 提供管理员管理的所有功能 | ... | ... |