Przeglądaj źródła

Merge remote-tracking branch 'origin/V0.1.1' into V0.1.1

jingyuanchao 1 rok temu
rodzic
commit
a562875706
39 zmienionych plików z 1024 dodań i 333 usunięć
  1. 3 0
      pom.xml
  2. 14 14
      project_data/sql/0.1.1/升级文档.txt
  3. 36 0
      soc-api/soc-api-system/src/main/java/com/xunmei/system/api/domain/websocket/RedisWebsocketMsg.java
  4. 4 4
      soc-auth/src/main/resources/bootstrap.yml
  5. 7 5
      soc-gateway/src/main/resources/bootstrap.yml
  6. 4 4
      soc-modules/soc-modules-core/src/main/resources/bootstrap.yml
  7. 2 2
      soc-modules/soc-modules-gen/pom.xml
  8. 0 120
      soc-modules/soc-modules-gen/src/main/java/com/xunmei/gen/util/CodeGenerators.java
  9. 4 4
      soc-modules/soc-modules-gen/src/main/resources/bootstrap.yml
  10. 3 6
      soc-modules/soc-modules-gen/src/main/resources/vm/java/controller.java.vm
  11. 4 25
      soc-modules/soc-modules-gen/src/main/resources/vm/java/domain.java.vm
  12. 0 2
      soc-modules/soc-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm
  13. 1 1
      soc-modules/soc-modules-gen/src/main/resources/vm/vue/index.vue.vm
  14. 6 4
      soc-modules/soc-modules-iot/src/main/resources/bootstrap.yml
  15. 6 4
      soc-modules/soc-modules-job/src/main/resources/bootstrap.yml
  16. 2 3
      soc-modules/soc-modules-mediator/pom.xml
  17. 21 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/domain/websocket/InputMessage.java
  18. 16 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/domain/websocket/LoginRequest.java
  19. 20 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/domain/websocket/OutputMessage.java
  20. 19 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/domain/websocket/RegisterRequest.java
  21. 0 21
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/WebSocketConfig.java
  22. 0 45
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/WebSocketHandler.java
  23. 0 36
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/WebSocketInterceptor.java
  24. 0 21
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/WebSocketService.java
  25. 36 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/config/WebSocketConfig.java
  26. 33 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/constant/WebSocketConstants.java
  27. 88 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/controller/WebsocketController.java
  28. 28 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/dto/WebSocketMessageDto.java
  29. 40 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/dto/WebsocketResult.java
  30. 152 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/handler/SocWebSocketHandler.java
  31. 102 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/holder/WebSocketSessionHolder.java
  32. 67 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/interceptor/WebSocketInterceptor.java
  33. 52 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/listener/WebSocketTopicListener.java
  34. 41 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/redis/WebsocketPublisher.java
  35. 68 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/redis/WebsocketSubscriber.java
  36. 129 0
      soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/utils/WebSocketUtils.java
  37. 4 4
      soc-modules/soc-modules-mediator/src/main/resources/bootstrap.yml
  38. 6 4
      soc-modules/soc-modules-sms/src/main/resources/bootstrap.yml
  39. 6 4
      soc-modules/soc-modules-system/src/main/resources/bootstrap.yml

+ 3 - 0
pom.xml

@@ -233,11 +233,14 @@
                 <version>${soc.version}</version>
             </dependency>
 
+
             <dependency>
                 <groupId>com.xunmei</groupId>
                 <artifactId>soc-modules-system</artifactId>
                 <version>${soc.version}</version>
             </dependency>
+
+
             <dependency>
                 <groupId>org.projectlombok</groupId>
                 <artifactId>lombok</artifactId>

+ 14 - 14
project_data/sql/0.1.1/升级文档.txt

@@ -1,16 +1,4 @@
-
-
-1、nacos gateway配置文件修改:
-mediator的路由配置uri修改为lb:ws://soc-mediator
-完整示例(对比以往配置仅修改uri项的值):
-        - id: soc-mediator
-          uri: lb:ws://soc-mediator
-          predicates:
-            - Path=/mediator/**
-          filters:
-            - StripPrefix=1
-
-2、nacos gateway配置文件增加-部署中心路由节点:
+1、nacos gateway配置文件增加-部署中心路由节点:
 spring:
   cloud:
     gateway:
@@ -22,7 +10,17 @@ spring:
               - Path=/api/delpoy/**
             filters:
               - StripPrefix=2
-3、nacos gateway配置文件增加-部署中心不校验白名单:
+           # websocket模块
+          - id: soc-websocket
+            uri: lb:ws://soc-mediator
+            predicates:
+              - Path=/ws/**
+          - id: soc-websocket
+            uri: lb:ws://soc-mediator
+            predicates:
+              - Path=/ws1/**
+
+2、nacos gateway配置文件增加-部署中心不校验白名单:
 security:
   ignore:
      whites:
@@ -36,3 +34,5 @@ security:
         - /api/deploy/report
         - /api/deploy/task
         - /api/deploy/taskReport
+        - /ws/**
+        - /ws1/**

+ 36 - 0
soc-api/soc-api-system/src/main/java/com/xunmei/system/api/domain/websocket/RedisWebsocketMsg.java

@@ -0,0 +1,36 @@
+package com.xunmei.system.api.domain.websocket;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author gaoxiong
+ * @Title: redis订阅发布消息,定义实体
+ * @Package
+ * @Description:
+ * @date 2024/7/914:00
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class RedisWebsocketMsg implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 发送的消息体
+     */
+    private Object content;
+
+    /**
+     * 可以发送的消息的session token
+     */
+    private Set<String> tokens = new HashSet<>();
+
+
+}

+ 4 - 4
soc-auth/src/main/resources/bootstrap.yml

@@ -14,17 +14,17 @@ spring:
     nacos:
       discovery:
         # 服务注册地址
-        server-addr: 10.87.23.39:8848
-        namespace: c2fe98f1-97c0-4c1a-9df8-4d63e5b625de
+        server-addr: 10.87.23.48:8848
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
       config:
         # 配置中心地址
-        server-addr: 10.87.23.39:8848
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置
         shared-configs:
           - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
-        namespace: c2fe98f1-97c0-4c1a-9df8-4d63e5b625de
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
 logging:
   file:
     name: logs/${spring.application.name}/info.log

+ 7 - 5
soc-gateway/src/main/resources/bootstrap.yml

@@ -12,19 +12,21 @@ spring:
     active: dev
   cloud:
     nacos:
+#      username: nacos
+#      password: nacos
       discovery:
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 服务注册地址
-        server-addr: 10.87.21.103:8847
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        server-addr: 10.87.23.48:8848
       config:
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 配置中心地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置
         shared-configs:
           - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
     sentinel:
       #取消控制台懒加载
       eager: true
@@ -33,7 +35,7 @@ spring:
       datasource:
         ds1:
           nacos:
-            server-addr: 10.87.21.103:8847
+            server-addr: 10.87.23.48:8848
             dataId: sentinel-soc-gateway
             groupId: DEFAULT_GROUP
             data-type: json

+ 4 - 4
soc-modules/soc-modules-core/src/main/resources/bootstrap.yml

@@ -12,21 +12,21 @@ spring:
     active: dev
   cloud:
     nacos:
+      username: nacos
+      password: nacos
       discovery:
         namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 服务注册地址
-        server-addr: 47.92.229.224:8848
+        server-addr: 10.87.23.48:8848
       config:
         namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 配置中心地址
-        server-addr: 47.92.229.224:8848
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置
         shared-configs:
           - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
-      username: nacos
-      password: nacos
 logging:
   file:
     name: logs/${spring.application.name}/info.log

+ 2 - 2
soc-modules/soc-modules-gen/pom.xml

@@ -73,11 +73,11 @@
             <artifactId>soc-common-swagger</artifactId>
             <version>0.0.1-SNAPSHOT</version>
         </dependency>
-        <dependency>
+     <!--   <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-generator</artifactId>
             <version>${generator.version}</version>
-        </dependency>
+        </dependency>-->
     </dependencies>
 
     <build>

+ 0 - 120
soc-modules/soc-modules-gen/src/main/java/com/xunmei/gen/util/CodeGenerators.java

@@ -1,120 +0,0 @@
-package com.xunmei.gen.util;
-
-
-import com.baomidou.mybatisplus.generator.AutoGenerator;
-import com.baomidou.mybatisplus.generator.InjectionConfig;
-import com.baomidou.mybatisplus.generator.config.*;
-import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
-
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class CodeGenerators {
-
-    /**
-     * 读取控制填的内容
-     * @param tip
-     * @return
-     */
-  /*  public static String scanner(String tip) {
-        Scanner scanner = new Scanner(System.in);
-        System.out.println("请输入" + tip + ":");
-        if (scanner.hasNext()) {
-            String ipt = scanner.next();
-            if (ObjectUtils.isNotEmpty(ipt)) {
-                return ipt;
-            }
-        }
-        throw new MybatisPlusException("请输入正确的" + tip + "!");
-    }*/
-
-    public static final String moduleName="com.xunmei.iot";
-    public static final String tableNames="iot_server_info";
-
-    public static void main(String[] args) {
-        // 代码生成器
-        AutoGenerator mpg = new AutoGenerator();
-        // 全局配置
-        GlobalConfig gc = new GlobalConfig();
-        // System.getProperty("user.dir");
-        String projectPath = "/Users/jingyuanchao/Downloads";
-        //生成文件输出目录
-        gc.setOutputDir(projectPath + "/java");
-        gc.setAuthor("jingYuanChao");
-        //生成代码后,是否打开文件夹
-        gc.setOpen(true);
-        // 实体属性 Swagger2 注解
-        gc.setSwagger2(true);
-        mpg.setGlobalConfig(gc);
-        // 数据源配置
-        DataSourceConfig dsc = new DataSourceConfig();
-       /* dsc.setUrl("jdbc:mysql://39.103.229.41:3306/isp_ah?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&characterSetResults=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true");
-        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
-        dsc.setUsername("root");
-        dsc.setPassword("jinJie@2021");*/
-        dsc.setUrl("jdbc:mysql://10.87.23.57:3306/soc?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&characterSetResults=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true");
-        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
-        dsc.setUsername("root");
-        dsc.setPassword("XM_zm2019");
-        mpg.setDataSource(dsc);
-
-
-        // 包配置
-        PackageConfig pc = new PackageConfig();
-        pc.setModuleName(moduleName);
-        pc.setEntity("domain");
-        pc.setMapper("mapper");
-        pc.setService("service");
-        pc.setController("controller");
-        mpg.setPackageInfo(pc);
-        // 自定义配置
-        InjectionConfig cfg = new InjectionConfig() {
-            @Override
-            public void initMap() {
-                // to do nothing
-            }
-        };
-        // 如果模板引擎是 freemarker
-        //String templatePath = "/templates/mapper.xml.ftl";
-        // 自定义输出配置
-        List<FileOutConfig> focList = new ArrayList<>();
-        // 自定义配置会被优先输出
-//        focList.add(new FileOutConfig(templatePath) {
-//            @Override
-//            pub String outputFile(TableInfo tableInfo) {
-//                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
-//                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
-//                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
-//            }
-//        });
-        cfg.setFileOutConfigList(focList);
-        //这个必须要,需要提供一个默认的
-        mpg.setCfg(cfg);
-
-        // 策略配置
-        StrategyConfig strategy = new StrategyConfig();
-        // 表名生成策略
-        strategy.setNaming(NamingStrategy.underline_to_camel);
-        // 实体字段生成策略
-        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
-        // 需要生成的表
-        strategy.setInclude(tableNames.split(","));
-        // 使用lombook
-        strategy.setEntityLombokModel(true);
-        strategy.setRestControllerStyle(true);
-        // 生成注解
-        strategy.setEntityTableFieldAnnotationEnable(true);
-        // 自动生成实体类继承基类(基类必须已存在)
-        strategy.setTablePrefix("t_","t_app_");
-        //strategy.setSuperEntityClass("com.isp.common.jpa.BaseEntity");
-        // 写于父类中的公共字段
-        //strategy.setSuperEntityColumns("create_time","update_time", "modified_id", "modified_name");
-        mpg.setStrategy(strategy);
-        //mpg.setTemplateEngine(new FreemarkerTemplateEngine());
-        mpg.execute();
-    }
-
-
-
-}

+ 4 - 4
soc-modules/soc-modules-gen/src/main/resources/bootstrap.yml

@@ -13,13 +13,13 @@ spring:
   cloud:
     nacos:
       discovery:
-        namespace: 13d6af5d-c288-40d6-b1ee-4fc370665aba
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 服务注册地址
-        server-addr: 10.87.10.54:8848
+        server-addr: 10.87.23.48:8848
       config:
-        namespace: 13d6af5d-c288-40d6-b1ee-4fc370665aba
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 配置中心地址
-        server-addr: 10.87.10.54:8848
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置

+ 3 - 6
soc-modules/soc-modules-gen/src/main/resources/vm/java/controller.java.vm

@@ -22,7 +22,6 @@ import com.xunmei.common.core.web.controller.BaseController;
 import com.xunmei.common.core.web.domain.AjaxResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-##import com.xunmei.common.core.utils.poi.ExcelUtil;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 #if($table.crud || $table.sub)
 import com.xunmei.common.core.web.page.TableDataInfo;
@@ -60,8 +59,6 @@ public class ${ClassName}Controller extends BaseController {
         }
     #end
 
-
-
     /**
      * 获取${functionName}详细信息
      */
@@ -77,7 +74,7 @@ public class ${ClassName}Controller extends BaseController {
      */
     @ApiOperation(value = "新增${ClassName}")
     @RequiresPermissions("${permissionPrefix}:add")
-    @Log(title = "${functionName}" , businessType = BusinessType.INSERT)
+    @Log(title = "${functionName}", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ${ClassName} ${className}) {
         return toAjax(${className}Service.insert${ClassName}(${className}));
@@ -88,7 +85,7 @@ public class ${ClassName}Controller extends BaseController {
      */
     @ApiOperation(value = "修改${ClassName}")
     @RequiresPermissions("${permissionPrefix}:edit")
-    @Log(title = "${functionName}" , businessType = BusinessType.UPDATE)
+    @Log(title = "${functionName}", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ${ClassName} ${className}) {
         return toAjax(${className}Service.update${ClassName}(${className}));
@@ -99,7 +96,7 @@ public class ${ClassName}Controller extends BaseController {
      */
     @ApiOperation(value = "删除${ClassName}")
     @RequiresPermissions("${permissionPrefix}:remove")
-    @Log(title = "${functionName}" , businessType = BusinessType.DELETE)
+    @Log(title = "${functionName}", businessType = BusinessType.DELETE)
     @DeleteMapping("/{${pkColumn.javaField}s}")
     public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
         return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));

+ 4 - 25
soc-modules/soc-modules-gen/src/main/resources/vm/java/domain.java.vm

@@ -18,7 +18,6 @@ import com.xunmei.common.core.web.domain.BaseEntity;
 #elseif($table.tree)
 import com.xunmei.common.core.web.domain.TreeEntity;
 #end
-
 /**
  * ${functionName}对象 ${tableName}
  *
@@ -34,7 +33,7 @@ import com.xunmei.common.core.web.domain.TreeEntity;
 @EqualsAndHashCode(callSuper = false)
 @Accessors(chain = true)
 @TableName("${tableName}")
-@ApiModel(value = "${ClassName}对象" , description = "${functionName}")
+@ApiModel(value = "${ClassName}对象", description = "${functionName}")
 public class ${ClassName} extends ${Entity}
         {
 private static final long serialVersionUID=1L;
@@ -50,7 +49,7 @@ private static final long serialVersionUID=1L;
                 #set($comment=$column.columnComment)
             #end
             #if($parentheseIndex != -1)
-            @ApiModelProperty(value = "${comment}" , notes = "$column.readConverterExp()")
+            @ApiModelProperty(value = "${comment}", notes = "$column.readConverterExp()")
             #elseif($column.javaType == 'Date')
             @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
             @ApiModelProperty(value = "${comment}")
@@ -74,36 +73,16 @@ private List<${subClassName}> ${subclassName}List;
         #else
             #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
         #end
-        ##    public void set${AttrName}($column.javaType $column.javaField)
-        ##    {
-        ##        this.$column.javaField = $column.javaField;
-        ##    }
-        ##
-        ##    public $column.javaType get${AttrName}()
-        ##    {
-        ##        return $column.javaField;
-        ##    }
     #end
 #end
 
 #if($table.sub)
-    ##    public List<${subClassName}> get${subClassName}List()
-    ##    {
-    ##        return ${subclassName}List;
-    ##    }
-    ##
-    ##    public void set${subClassName}List(List<${subClassName}> ${subclassName}List)
-    ##    {
-    ##        this.${subclassName}List = ${subclassName}List;
-    ##    }
 
 #end
 @Override
 public String toString(){
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
     #foreach ($column in $columns)
-
-
         #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
             #set($AttrName=$column.javaField)
         #else
@@ -111,11 +90,11 @@ public String toString(){
         #end
         #if(${column.javaField}=="remark")
         #else
-                .append("${column.javaField}" ,get${AttrName}())
+                .append("${column.javaField}",get${AttrName}())
         #end
     #end
     #if($table.sub)
-            .append("${subclassName}List" ,get${subClassName}List())
+            .append("${subclassName}List",get${subClassName}List())
     #end
         .toString();
         }

+ 0 - 2
soc-modules/soc-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm

@@ -36,8 +36,6 @@ import ${packageName}.service.I${ClassName}Service;
 public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service {
     @Autowired
     private ${ClassName}Mapper ${className}Mapper;
-    @Autowired
-    private ISysOrgService orgService;
 
     @Override
     public TableDataInfo<${ClassName}> selectPage(${ClassName} ${className}) {

+ 1 - 1
soc-modules/soc-modules-gen/src/main/resources/vm/vue/index.vue.vm

@@ -449,7 +449,7 @@ export default {
 #end
       list${BusinessName}(this.queryParams).then(response => {
         this.${businessName}List = response.rows;
-        this.total = response.total;
+        this.total = Number(response.total);
         this.loading = false;
       });
     },

+ 6 - 4
soc-modules/soc-modules-iot/src/main/resources/bootstrap.yml

@@ -12,14 +12,16 @@ spring:
     active: dev
   cloud:
     nacos:
+      username: nacos
+      password: nacos
       discovery:
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 服务注册地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
       config:
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 配置中心地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置

+ 6 - 4
soc-modules/soc-modules-job/src/main/resources/bootstrap.yml

@@ -12,14 +12,16 @@ spring:
     active: dev
   cloud:
     nacos:
+      username: nacos
+      password: nacos
       discovery:
-        namespace: 34306a91-1bb7-45ce-b80d-4092dd08ea64
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 服务注册地址
-        server-addr: 10.87.10.54:8848
+        server-addr: 10.87.23.48:8848
       config:
-        namespace: 34306a91-1bb7-45ce-b80d-4092dd08ea64
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 配置中心地址
-        server-addr: 10.87.10.54:8848
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置

+ 2 - 3
soc-modules/soc-modules-mediator/pom.xml

@@ -85,9 +85,8 @@
             <optional>true</optional>
         </dependency>
         <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-websocket</artifactId>
-            <version>5.3.20</version>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
         </dependency>
 
     </dependencies>

+ 21 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/domain/websocket/InputMessage.java

@@ -0,0 +1,21 @@
+package com.xunmei.mediator.domain.websocket;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author gaoxiong
+ * @Title:
+ * @Package
+ * @Description:
+ * @date 2024/7/811:21
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class InputMessage {
+    private String content;
+    private String sender;
+
+}

+ 16 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/domain/websocket/LoginRequest.java

@@ -0,0 +1,16 @@
+package com.xunmei.mediator.domain.websocket;
+
+import lombok.Data;
+
+/**
+ * @author gaoxiong
+ * @Title:
+ * @Package
+ * @Description:
+ * @date 2024/7/811:23
+ */
+@Data
+public class LoginRequest {
+
+    private String token;
+}

+ 20 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/domain/websocket/OutputMessage.java

@@ -0,0 +1,20 @@
+package com.xunmei.mediator.domain.websocket;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author gaoxiong
+ * @Title:
+ * @Package
+ * @Description:
+ * @date 2024/7/811:21
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class OutputMessage {
+    private String content;
+    private String sender;
+}

+ 19 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/domain/websocket/RegisterRequest.java

@@ -0,0 +1,19 @@
+package com.xunmei.mediator.domain.websocket;
+
+import lombok.Data;
+
+/**
+ * @author gaoxiong
+ * @Title:
+ * @Package
+ * @Description:
+ * @date 2024/7/811:31
+ */
+@Data
+public class RegisterRequest {
+
+    /**
+     * 验证码
+     */
+    private String code;
+}

+ 0 - 21
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/WebSocketConfig.java

@@ -1,21 +0,0 @@
-package com.xunmei.mediator.websocket;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.socket.config.annotation.EnableWebSocket;
-import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
-import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
-
-@Configuration
-@EnableWebSocket()
-public class WebSocketConfig implements WebSocketConfigurer {
-    @Autowired
-    private WebSocketHandler handler;
-
-    @Autowired
-    private WebSocketInterceptor interceptor;
-    @Override
-    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
-        registry.addHandler(handler, "/login/**").setAllowedOrigins("*").addInterceptors(interceptor);;
-    }
-}

+ 0 - 45
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/WebSocketHandler.java

@@ -1,45 +0,0 @@
-package com.xunmei.mediator.websocket;
-
-import org.springframework.stereotype.Component;
-import org.springframework.web.socket.CloseStatus;
-import org.springframework.web.socket.TextMessage;
-import org.springframework.web.socket.WebSocketSession;
-import org.springframework.web.socket.handler.TextWebSocketHandler;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-@Component
-//@ServerEndpoint(value = "",subprotocols = {"protocol"})
-public class WebSocketHandler extends TextWebSocketHandler {
-
-    private final Map<String,WebSocketSession> sessions = new ConcurrentHashMap<String,WebSocketSession>();
-
-    @Override
-    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
-        sessions.put("123",session);
-
-        System.out.println("New connection established: " + session.getId());
-    }
-
-    @Override
-    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
-        String payload = message.getPayload();
-        System.out.println("Received message: " + payload);
-        session.sendMessage(new TextMessage("Hello, client!"));
-        // 处理消息并入数据库
-    }
-
-    @Override
-    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
-        sessions.remove(session);
-        System.out.println("Connection closed: " + session.getId());
-    }
-
-    public void sendMessageToAll(String message) throws IOException {
-        for (WebSocketSession value : sessions.values()) {
-            value.sendMessage(new TextMessage(message));
-        }
-    }
-}

+ 0 - 36
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/WebSocketInterceptor.java

@@ -1,36 +0,0 @@
-package com.xunmei.mediator.websocket;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.server.ServerHttpRequest;
-import org.springframework.http.server.ServerHttpResponse;
-import org.springframework.http.server.ServletServerHttpRequest;
-import org.springframework.stereotype.Component;
-import org.springframework.web.socket.WebSocketHandler;
-import org.springframework.web.socket.server.HandshakeInterceptor;
-
-import java.util.Map;
- 
-@Component
-public class WebSocketInterceptor implements HandshakeInterceptor {
- 
-    private static final Logger log = LoggerFactory.getLogger(WebSocketInterceptor.class);
- 
-    //在握手之前执行该方法, 继续握手返回true, 中断握手返回false. 通过attributes参数设置WebSocketSession的属性
-    @Override
-    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes){
-        if (request instanceof ServletServerHttpRequest) {
-            String uri = request.getURI().getPath();
-            String token = uri.substring(uri.lastIndexOf("/")+1);
-            attributes.put("token",token);
-            log.info("current token is:"+token);
-        }
-        return true;
-    }
- 
-    @Override
-    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
-        log.info("coming webSocketInterceptor afterHandshake method...");
-    }
- 
-}

+ 0 - 21
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/WebSocketService.java

@@ -1,21 +0,0 @@
-package com.xunmei.mediator.websocket;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.io.IOException;
-
-@Service
-public class WebSocketService {
-
-    private final WebSocketHandler webSocketHandler;
-
-    @Autowired
-    public WebSocketService(WebSocketHandler webSocketHandler) {
-        this.webSocketHandler = webSocketHandler;
-    }
-
-    public void sendMessageToAll(String message) throws IOException {
-        webSocketHandler.sendMessageToAll(message);
-    }
-}

+ 36 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/config/WebSocketConfig.java

@@ -0,0 +1,36 @@
+package com.xunmei.mediator.websocket.config;
+
+import com.xunmei.mediator.websocket.handler.SocWebSocketHandler;
+import com.xunmei.mediator.websocket.interceptor.WebSocketInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+
+/**
+ * @author gaoxiong
+ * @Title: websocket配置类
+ * @Package
+ * @Description:
+ * @date 2024/7/517:05
+ */
+@Configuration
+@EnableWebSocket
+public class WebSocketConfig implements WebSocketConfigurer  {
+
+    @Autowired
+    private SocWebSocketHandler socWebSocketHandler;
+
+    @Autowired
+    private WebSocketInterceptor webSocketInterceptor;
+    @Override
+    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+        //新建string 字符串
+        String[] paths = new String[]{"/ws/socket","/ws1/socket"};
+        registry.addHandler(socWebSocketHandler, paths)
+                .setAllowedOrigins("*")
+                .addInterceptors(webSocketInterceptor);
+    }
+}

+ 33 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/constant/WebSocketConstants.java

@@ -0,0 +1,33 @@
+package com.xunmei.mediator.websocket.constant;
+
+/**
+ * @author gaoxiong
+ * @Title: websocket 常量
+ * @Package
+ * @Description:
+ * @date 2024/7/517:33
+ */
+public interface WebSocketConstants {
+
+
+    /**
+     * websocketSession中的参数的key
+     */
+    String LOGIN_TOKEN_KEY = "token";
+
+    /**
+     * 订阅的频道
+     */
+    String WEB_SOCKET_TOPIC = "global:websocket";
+
+    /**
+     * 前端心跳检查的命令
+     */
+    String PING = "ping";
+
+    /**
+     * 服务端心跳恢复的字符串
+     */
+    String PONG = "pong";
+
+}

+ 88 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/controller/WebsocketController.java

@@ -0,0 +1,88 @@
+package com.xunmei.mediator.websocket.controller;
+
+import com.xunmei.common.core.domain.R;
+import com.xunmei.common.core.web.domain.AjaxResult;
+import com.xunmei.common.redis.utils.RedisUtils;
+import com.xunmei.mediator.websocket.holder.WebSocketSessionHolder;
+import com.xunmei.mediator.websocket.redis.WebsocketPublisher;
+import com.xunmei.system.api.domain.SysUser;
+import com.xunmei.system.api.domain.websocket.RedisWebsocketMsg;
+import org.aspectj.weaver.loadtime.Aj;
+import org.redisson.api.RKeys;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.*;
+
+/**
+ * @author gaoxiong
+ * @Title:
+ * @Package
+ * @Description:
+ * @date 2024/7/914:07
+ */
+@RestController
+@RequestMapping
+public class WebsocketController {
+
+    @Autowired
+    private WebsocketPublisher websocketPublisher;
+
+
+
+
+
+    @GetMapping("/send")
+    public String sendMessage(String message) {
+        RedisWebsocketMsg msg = new RedisWebsocketMsg();
+        Map<String,String> map = new HashMap<>();
+        map.put("name","王燕妮");
+        map.put("sex","女");
+        msg.setContent(map);
+        RedissonClient client = RedisUtils.getClient();
+        RKeys keys = client.getKeys();
+        Iterable<String> keyTokens = keys.getKeysByPattern(WebSocketSessionHolder.REDIS_TOPIC_WEBSOCKET_TOKEN + "*");
+        Set<String> tokens = new HashSet<>();
+        for (String keyToken : keyTokens) {
+            String token = RedisUtils.getCacheObject(keyToken);
+            tokens.add(token);
+        }
+        msg.setTokens(tokens);
+        websocketPublisher.sendMessage(msg);
+        return "发送成功";
+    }
+
+    /**
+     * 给所有连接的主机发送命令消息
+     * @param obj
+     * @return
+     */
+    @PostMapping("/sendAllMsg")
+    public AjaxResult sendAllMessage(Object obj){
+        RedisWebsocketMsg msg = new RedisWebsocketMsg();
+        msg.setContent(msg);
+        RedissonClient client = RedisUtils.getClient();
+        RKeys keys = client.getKeys();
+        Iterable<String> keyTokens = keys.getKeysByPattern(WebSocketSessionHolder.REDIS_TOPIC_WEBSOCKET_TOKEN + "*");
+        Set<String> tokens = new HashSet<>();
+        for (String keyToken : keyTokens) {
+            String token = RedisUtils.getCacheObject(keyToken);
+            tokens.add(token);
+        }
+        msg.setTokens(tokens);
+        websocketPublisher.sendMessage(msg);
+        return AjaxResult.success();
+    }
+
+
+    @PostMapping("/sendListMsg")
+    public AjaxResult sendListMessage(RedisWebsocketMsg msg){
+        websocketPublisher.sendMessage(msg);
+        return AjaxResult.success();
+    }
+}

+ 28 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/dto/WebSocketMessageDto.java

@@ -0,0 +1,28 @@
+package com.xunmei.mediator.websocket.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author gaoxiong
+ * @Title: 消息类
+ * @Package
+ * @Description:
+ * @date 2024/7/517:36
+ */
+@Data
+public class WebSocketMessageDto {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 需要推送到的session key 列表
+     */
+    private List<String> sessionKeys;
+
+    /**
+     * 需要发送的消息
+     */
+    private String message;
+}

+ 40 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/dto/WebsocketResult.java

@@ -0,0 +1,40 @@
+package com.xunmei.mediator.websocket.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author gaoxiong
+ * @Title: 消息返回结果
+ * @Package
+ * @Description:
+ * @date 2024/7/916:02
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class WebsocketResult implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+    /**
+     * 消息编号
+     */
+    private String id;
+    /**
+     * 接口请求路径
+     */
+    private String topic;
+    /**
+     * 消息请求时间
+     */
+    private String timestamp;
+    /**
+     * 消息内容
+     */
+    private Object payload;
+
+}
+

+ 152 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/handler/SocWebSocketHandler.java

@@ -0,0 +1,152 @@
+package com.xunmei.mediator.websocket.handler;
+
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSON;
+import com.xunmei.common.core.utils.StringUtils;
+import com.xunmei.common.redis.utils.RedisUtils;
+import com.xunmei.mediator.websocket.dto.WebsocketResult;
+import com.xunmei.mediator.websocket.holder.WebSocketSessionHolder;
+import com.xunmei.mediator.websocket.utils.WebSocketUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RedissonClient;
+import org.springframework.stereotype.Component;
+import org.springframework.web.socket.*;
+import org.springframework.web.socket.handler.AbstractWebSocketHandler;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author gaoxiong
+ * @Title: 实现类
+ * @Package
+ * @Description:
+ * @date 2024/7/517:18
+ */
+@Slf4j
+@Component
+public class SocWebSocketHandler extends AbstractWebSocketHandler {
+    /**
+     * 连接成功后
+     */
+    @Override
+    public void afterConnectionEstablished(WebSocketSession session) throws IOException {
+
+        String registerCode = (String) session.getAttributes().get("registerCode");
+        if (registerCode != null) {
+            Map<String,Object> map = new HashMap<>();
+            map.put("token","kkjkkjjiikkjkjkj");
+            map.put("statusCode",200);
+            WebsocketResult register = createWebsocketResult(null, "register" , map);
+            WebSocketUtils.sendMessage(session, JSON.toJSONString(register));
+            log.info("[建立注册连接] sessionId: {},registerCode:{}", session.getId(), registerCode);
+            session.close();
+            return;
+        }
+
+        String token = (String) session.getAttributes().get("token");
+        if(StringUtils.isNotEmpty(token)){
+            WebSocketSessionHolder.addSession(token, session);
+            Map<String,Object> map = new HashMap<>();
+            map.put("statusCode",200);
+            WebsocketResult result = createWebsocketResult(null, "login" , map);
+            WebSocketUtils.sendMessage(session, JSON.toJSONString(result));
+            log.info("[建立连接] sessionId: {},token:{}", session.getId(), token);
+            return;
+        }
+        session.close();
+    }
+
+    private WebsocketResult createWebsocketResult(String id,String topic,Object object){
+        WebsocketResult result = new WebsocketResult();
+        result.setId(id);
+        result.setTopic(topic);
+        //当前时间转换为 格式:2024-07-02T14:17:32
+        result.setTimestamp(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
+        result.setPayload(object);
+        return result;
+    }
+
+    /**
+     * 处理接收到的文本消息
+     *
+     * @param session WebSocket会话
+     * @param message 接收到的文本消息
+     * @throws Exception 处理消息过程中可能抛出的异常
+     */
+    @Override
+    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+        // 从WebSocket会话中获取登录用户信息
+        WebSocketSessionHolder.updateToken(session);
+        log.info("接收到消息:{}",message.getPayload());
+    }
+
+    /**
+     * 处理接收到的二进制消息
+     *
+     * @param session WebSocket会话
+     * @param message 接收到的二进制消息
+     * @throws Exception 处理消息过程中可能抛出的异常
+     */
+    @Override
+    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
+        WebSocketSessionHolder.updateToken(session);
+
+        super.handleBinaryMessage(session, message);
+    }
+
+    /**
+     * 处理接收到的Pong消息(心跳监测)
+     *
+     * @param session WebSocket会话
+     * @param message 接收到的Pong消息
+     * @throws Exception 处理消息过程中可能抛出的异常
+     */
+    @Override
+    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
+        WebSocketSessionHolder.updateToken(session);
+
+        WebSocketUtils.sendPongMessage(session);
+    }
+
+    /**
+     * 处理WebSocket传输错误
+     *
+     * @param session   WebSocket会话
+     * @param exception 发生的异常
+     * @throws Exception 处理过程中可能抛出的异常
+     */
+    @Override
+    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
+        WebSocketSessionHolder.updateToken(session);
+
+        log.error("[传输错误] sessionId: {} , exception:{}", session.getId(), exception.getMessage());
+    }
+
+    /**
+     * 在WebSocket连接关闭后执行清理操作
+     *
+     * @param session WebSocket会话
+     * @param status  关闭状态信息
+     */
+    @Override
+    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
+        String token = (String) session.getAttributes().get("token");
+        WebSocketSessionHolder.removeSession(token);
+        log.info("[断开连接] sessionId: {},token:{}",session.getId(),token);
+    }
+
+    /**
+     * 指示处理程序是否支持接收部分消息
+     *
+     * @return 如果支持接收部分消息,则返回true;否则返回false
+     */
+    @Override
+    public boolean supportsPartialMessages() {
+        return false;
+    }
+
+}

+ 102 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/holder/WebSocketSessionHolder.java

@@ -0,0 +1,102 @@
+package com.xunmei.mediator.websocket.holder;
+
+import com.xunmei.common.core.utils.StringUtils;
+import com.xunmei.common.redis.utils.RedisUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.redisson.api.RBucket;
+import org.redisson.api.RedissonClient;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author gaoxiong
+ * @Title: 用于保存当前所有在线会话的信息
+ * @Package
+ * @Description:
+ * @date 2024/7/517:22
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class WebSocketSessionHolder {
+
+    private static final Map<String, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();
+
+    public static final String REDIS_TOPIC_WEBSOCKET_TOKEN = "websocket:token_";
+
+    public static void updateToken(WebSocketSession session){
+        /**
+         * 根据session,获取token
+         */
+        //循环USER_SESSION_MAP
+        for (Map.Entry<String, WebSocketSession> entry : USER_SESSION_MAP.entrySet()) {
+            WebSocketSession se = entry.getValue();
+            String id = se.getId();
+            if(id.equals(session.getId())){
+                RedissonClient client = RedisUtils.getClient();
+                RBucket<Object> bucket = client.getBucket(WebSocketSessionHolder.REDIS_TOPIC_WEBSOCKET_TOKEN + entry.getKey());
+                bucket.expire(Duration.ofMillis(1000 * 60 * 35));
+
+            }
+        }
+    }
+
+    /**
+     * 将WebSocket会话添加到用户会话Map中
+     *
+     * @param sessionKey 会话键,用于检索会话
+     * @param session    要添加的WebSocket会话
+     */
+    public static void addSession(String sessionKey, WebSocketSession session) {
+        USER_SESSION_MAP.put(sessionKey, session);
+        /**
+         * 设置缓存,半个小时失效
+         */
+        RedisUtils.setCacheObject(REDIS_TOPIC_WEBSOCKET_TOKEN + sessionKey, sessionKey, Duration.ofMillis(1000 * 60 * 35));
+    }
+
+    /**
+     * 从用户会话Map中移除指定会话键对应的WebSocket会话
+     *
+     * @param sessionKey 要移除的会话键
+     */
+    public static void removeSession(String sessionKey) {
+        if (USER_SESSION_MAP.containsKey(sessionKey)) {
+            USER_SESSION_MAP.remove(sessionKey);
+            RedisUtils.deleteObject(REDIS_TOPIC_WEBSOCKET_TOKEN + sessionKey);
+        }
+    }
+
+    /**
+     * 根据会话键从用户会话Map中获取WebSocket会话
+     *
+     * @param sessionKey 要获取的会话键
+     * @return 与给定会话键对应的WebSocket会话,如果不存在则返回null
+     */
+    public static WebSocketSession getSessions(String sessionKey) {
+        return USER_SESSION_MAP.get(sessionKey);
+    }
+
+    /**
+     * 获取存储在用户会话Map中所有WebSocket会话的会话键集合
+     *
+     * @return 所有WebSocket会话的会话键集合
+     */
+    public static Set<String> getSessionsAll() {
+        return USER_SESSION_MAP.keySet();
+    }
+
+    /**
+     * 检查给定的会话键是否存在于用户会话Map中
+     *
+     * @param sessionKey 要检查的会话键
+     * @return 如果存在对应的会话键,则返回true;否则返回false
+     */
+    public static Boolean existSession(String sessionKey) {
+        return USER_SESSION_MAP.containsKey(sessionKey);
+    }
+}

+ 67 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/interceptor/WebSocketInterceptor.java

@@ -0,0 +1,67 @@
+package com.xunmei.mediator.websocket.interceptor;
+
+import com.xunmei.common.core.utils.StringUtils;
+import com.xunmei.mediator.websocket.holder.WebSocketSessionHolder;
+import com.xunmei.mediator.websocket.utils.WebSocketUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.stereotype.Service;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import java.util.Map;
+
+/**
+ * WebSocket握手请求的拦截器
+ *
+ * @author gaoxiong
+ * @Title:
+ * @Package
+ * @Description:
+ * @date 2024/7/517:12
+ */
+@Slf4j
+@Service
+public class WebSocketInterceptor implements HandshakeInterceptor {
+
+    /**
+     * websocket 握手之前的处理方法
+     *
+     * @param request    握手请求
+     * @param response   握手响应
+     * @param wsHandler  处理程序
+     * @param attributes 与websocket会话关联属性
+     * @return 如果允许握手继续进行,通过返回true, 失败返回false
+     * @throws Exception
+     */
+    @Override
+    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
+        // 从请求中获取查询参数并存入attributes中
+        String queryString = request.getURI().getQuery();
+        if (queryString != null) {
+            String[] queryParams = queryString.split("&");
+            for (String queryParam : queryParams) {
+                String[] keyValue = queryParam.split("=");
+                if (keyValue.length == 2) {
+                    attributes.put(keyValue[0], keyValue[1]);
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * WebSocket握手成功后执行的后置处理方法
+     *
+     * @param request   WebSocket握手请求
+     * @param response  WebSocket握手响应
+     * @param wsHandler WebSocket处理程序
+     * @param exception 握手过程中可能出现的异常
+     */
+    @Override
+    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
+        // 在这个方法中可以执行一些握手成功后的后续处理逻辑,比如记录日志或者其他操作
+        log.info("WebSocket握手成功");
+    }
+}

+ 52 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/listener/WebSocketTopicListener.java

@@ -0,0 +1,52 @@
+package com.xunmei.mediator.websocket.listener;
+
+import cn.hutool.core.collection.CollUtil;
+import com.xunmei.mediator.websocket.holder.WebSocketSessionHolder;
+import com.xunmei.mediator.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 gaoxiong
+ * @Title:
+ * @Package
+ * @Description:
+ * @date 2024/7/517:41
+ */
+@Slf4j
+public class WebSocketTopicListener  implements ApplicationRunner, Ordered {
+    /**
+     * 在Spring Boot应用程序启动时初始化WebSocket主题订阅监听器
+     *
+     * @param args 应用程序参数
+     * @throws Exception 初始化过程中可能抛出的异常
+     */
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        // 订阅WebSocket消息
+        WebSocketUtils.subscribeMessage((message) -> {
+            log.info("WebSocket主题订阅收到消息session keys={} message={}", message.getSessionKeys(), message.getMessage());
+            // 如果key不为空就按照key发消息 如果为空就群发
+            if (CollUtil.isNotEmpty(message.getSessionKeys())) {
+                message.getSessionKeys().forEach(key -> {
+                    if (WebSocketSessionHolder.existSession(key)) {
+                        WebSocketUtils.sendMessage(key, message.getMessage());
+                    }
+                });
+            } else {
+                WebSocketSessionHolder.getSessionsAll().forEach(key -> {
+                    WebSocketUtils.sendMessage(key, message.getMessage());
+                });
+            }
+        });
+        log.info("初始化WebSocket主题订阅监听器成功");
+    }
+
+    @Override
+    public int getOrder() {
+        return -1;
+    }
+}

+ 41 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/redis/WebsocketPublisher.java

@@ -0,0 +1,41 @@
+package com.xunmei.mediator.websocket.redis;
+
+import com.xunmei.common.redis.utils.RedisUtils;
+import com.xunmei.system.api.domain.websocket.RedisWebsocketMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RTopic;
+import org.redisson.api.RedissonClient;
+import org.redisson.codec.SerializationCodec;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * websocket 消息发布者
+ * @author gaoxiong
+ * @Title:
+ * @Package
+ * @Description:
+ * @date 2024/7/913:40
+ */
+@Slf4j
+@Component
+public class WebsocketPublisher {
+
+    public static final String TOPIC_KEY_WEBSOCKET = "websocket_topic_key";
+
+    public void sendMessage(RedisWebsocketMsg message) {
+        try{
+            RedissonClient client = RedisUtils.getClient();
+            //订阅的主题
+            RTopic clientTopic = client.getTopic(TOPIC_KEY_WEBSOCKET, new SerializationCodec());
+            //消息发布
+            clientTopic.publishAsync(message);
+            log.info("websocket 发布消息:{}",message);
+            long l = clientTopic.countSubscribers();
+            log.info("发送消息:{}",l);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+
+    }
+}

+ 68 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/redis/WebsocketSubscriber.java

@@ -0,0 +1,68 @@
+package com.xunmei.mediator.websocket.redis;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSON;
+import com.xunmei.common.redis.utils.RedisUtils;
+import com.xunmei.mediator.websocket.holder.WebSocketSessionHolder;
+import com.xunmei.mediator.websocket.utils.WebSocketUtils;
+import com.xunmei.system.api.domain.websocket.RedisWebsocketMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RTopic;
+import org.redisson.api.RedissonClient;
+import org.redisson.api.listener.MessageListener;
+import org.redisson.codec.SerializationCodec;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author gaoxiong
+ * @Title: 订阅者
+ * @Package
+ * @Description:
+ * @date 2024/7/913:47
+ */
+@Slf4j
+@Component
+public class WebsocketSubscriber implements ApplicationRunner, Ordered {
+
+    private static final String TOPIC_KEY_WEBSOCKET = WebsocketPublisher.TOPIC_KEY_WEBSOCKET;
+
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        log.info("WebsocketSubscriber 开始运行");
+        RedissonClient client = RedisUtils.getClient();
+        RTopic clientTopic = client.getTopic(TOPIC_KEY_WEBSOCKET, new SerializationCodec());
+
+        clientTopic.addListener(RedisWebsocketMsg.class, new MessageListener<RedisWebsocketMsg>() {
+            @Override
+            public void onMessage(CharSequence channel, RedisWebsocketMsg msg) {
+                String str = JSON.toJSONString(msg.getContent());
+                log.info("接收到订阅消息:{}",JSON.toJSONString(msg));
+                Set<String> tokens = msg.getTokens();
+                if(ObjectUtil.isNotEmpty(tokens)){
+                    for (String token : tokens) {
+                        WebSocketSession sessions = WebSocketSessionHolder.getSessions(token);
+                        if(sessions != null){
+                            WebSocketUtils.sendMessage(sessions,str);
+                        }
+
+                    }
+                }
+
+            }
+        });
+    }
+
+    @Override
+    public int getOrder() {
+        return 0;
+    }
+}

+ 129 - 0
soc-modules/soc-modules-mediator/src/main/java/com/xunmei/mediator/websocket/utils/WebSocketUtils.java

@@ -0,0 +1,129 @@
+package com.xunmei.mediator.websocket.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import com.xunmei.common.redis.utils.RedisUtils;
+import com.xunmei.mediator.websocket.dto.WebSocketMessageDto;
+import com.xunmei.mediator.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.xunmei.mediator.websocket.constant.WebSocketConstants.WEB_SOCKET_TOPIC;
+
+/**
+ * @author gaoxiong
+ * @Title: websocket 工具类
+ * @Package
+ * @Description:
+ * @date 2024/7/517:30
+ */
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class WebSocketUtils {
+
+    /**
+     * 向指定的WebSocket会话发送消息
+     *
+     * @param sessionKey 要发送消息的用户id
+     * @param message    要发送的消息内容
+     */
+    public static void sendMessage(String sessionKey, String message) {
+        WebSocketSession session = WebSocketSessionHolder.getSessions(sessionKey);
+        sendMessage(session, message);
+    }
+
+    /**
+     * 订阅WebSocket消息主题,并提供一个消费者函数来处理接收到的消息
+     *
+     * @param consumer 处理WebSocket消息的消费者函数
+     */
+    public static void subscribeMessage(Consumer<WebSocketMessageDto> consumer) {
+        RedisUtils.subscribe(WEB_SOCKET_TOPIC, WebSocketMessageDto.class, consumer);
+    }
+
+    /**
+     * 发布WebSocket订阅消息
+     *
+     * @param webSocketMessage 要发布的WebSocket消息对象
+     */
+    public static void publishMessage(WebSocketMessageDto webSocketMessage) {
+        List<String> unsentSessionKeys = new ArrayList<>();
+        // 当前服务内session,直接发送消息
+        for (String sessionKey : webSocketMessage.getSessionKeys()) {
+            if (WebSocketSessionHolder.existSession(sessionKey)) {
+                WebSocketUtils.sendMessage(sessionKey, webSocketMessage.getMessage());
+                continue;
+            }
+            unsentSessionKeys.add(sessionKey);
+        }
+        // 不在当前服务内session,发布订阅消息
+        if (CollUtil.isNotEmpty(unsentSessionKeys)) {
+            WebSocketMessageDto broadcastMessage = new WebSocketMessageDto();
+            broadcastMessage.setMessage(webSocketMessage.getMessage());
+            broadcastMessage.setSessionKeys(unsentSessionKeys);
+            RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> {
+                log.info("WebSocket发送主题订阅消息topic:{} session keys:{} message:{}",
+                        WEB_SOCKET_TOPIC, unsentSessionKeys, webSocketMessage.getMessage());
+            });
+        }
+    }
+
+    /**
+     * 向所有的WebSocket会话发布订阅的消息(群发)
+     *
+     * @param message 要发布的消息内容
+     */
+    public static void publishAll(String message) {
+        WebSocketMessageDto broadcastMessage = new WebSocketMessageDto();
+        broadcastMessage.setMessage(message);
+        RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> {
+            log.info("WebSocket发送主题订阅消息topic:{} message:{}", WEB_SOCKET_TOPIC, message);
+        });
+    }
+
+    /**
+     * 向指定的WebSocket会话发送Pong消息
+     *
+     * @param session 要发送Pong消息的WebSocket会话
+     */
+    public static void sendPongMessage(WebSocketSession session) {
+        sendMessage(session, new PongMessage());
+    }
+
+    /**
+     * 向指定的WebSocket会话发送文本消息
+     *
+     * @param session WebSocket会话
+     * @param message 要发送的文本消息内容
+     */
+    public static void sendMessage(WebSocketSession session, String message) {
+        sendMessage(session, new TextMessage(message));
+    }
+
+    /**
+     * 向指定的WebSocket会话发送WebSocket消息对象
+     *
+     * @param session WebSocket会话
+     * @param message 要发送的WebSocket消息对象
+     */
+    private static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) {
+        if (session == null || !session.isOpen()) {
+            log.warn("[send] session会话已经关闭");
+        } else {
+            try {
+                session.sendMessage(message);
+            } catch (IOException e) {
+                log.error("[send] session({}) 发送消息({}) 异常", session, message, e);
+            }
+        }
+    }
+}

+ 4 - 4
soc-modules/soc-modules-mediator/src/main/resources/bootstrap.yml

@@ -13,13 +13,13 @@ spring:
   cloud:
     nacos:
       discovery:
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 服务注册地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
       config:
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 配置中心地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置

+ 6 - 4
soc-modules/soc-modules-sms/src/main/resources/bootstrap.yml

@@ -12,14 +12,16 @@ spring:
     active: dev
   cloud:
     nacos:
+      username: nacos
+      password: nacos
       discovery:
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 服务注册地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
       config:
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 配置中心地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置

+ 6 - 4
soc-modules/soc-modules-system/src/main/resources/bootstrap.yml

@@ -12,14 +12,16 @@ spring:
     active: dev
   cloud:
     nacos:
+      username: nacos
+      password: nacos
       discovery:
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 服务注册地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
       config:
-        namespace: ffd30d7d-0a40-4674-ab19-e00aef378714
+        namespace: 4bb89334-98df-4ffc-904a-65bc848a8ea0
         # 配置中心地址
-        server-addr: 10.87.21.103:8847
+        server-addr: 10.87.23.48:8848
         # 配置文件格式
         file-extension: yml
         # 共享配置