Преглед изворни кода

fix : 客服聊天系统重构交互方式, 全部使用websocket交互

bilingfeng пре 1 година
родитељ
комит
7bfb2ab94e
19 измењених фајлова са 1101 додато и 102 уклоњено
  1. 64 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/KfWebSocketMsgEnum.java
  2. 0 49
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/dto/KfMsgDTO.java
  3. 340 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/dto/KfWebSocketMsgDTO.java
  4. 76 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/params/KfWebSocketMsgParam.java
  5. 57 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/vo/KfMsgRoomVO.java
  6. 58 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/vo/KfMsgWaitUserVO.java
  7. 25 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/vo/KfWebsocketGameVO.java
  8. 17 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfRoomService.java
  9. 10 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfSessionUserService.java
  10. 25 10
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/CpCallServiceImpl.java
  11. 82 17
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfRoomMsgServiceImpl.java
  12. 92 2
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfRoomServiceImpl.java
  13. 63 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfSessionUserServiceImpl.java
  14. 25 9
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgRedisListener.java
  15. 10 4
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebSocketSessionRegistry.java
  16. 117 7
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebsocketHandler.java
  17. 10 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfRoom.java
  18. 21 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfRoomMsg.java
  19. 9 4
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfSessionUser.java

+ 64 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/KfWebSocketMsgEnum.java

@@ -0,0 +1,64 @@
+package com.zanxiang.game.module.manage.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-02-26
+ * @description : WebSocket通讯消息枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum KfWebSocketMsgEnum {
+
+    /**
+     * 首次建立连接, 接收前端消息, 返回游戏列表, 待接入列表, 已接入房间列表
+     */
+    WEBSOCKET_MSG_KF_CREATE_CONNECT("WEBSOCKET_MSG_KF_CREATE_CONNECT"),
+
+    /**
+     * 玩家待接入, 主动推送玩家待接入列表
+     */
+    WEBSOCKET_MSG_WAIT_LIST("WEBSOCKET_MSG_WAIT_LIST"),
+
+    /**
+     * 玩家被接入, 接收前端消息, 直接返回完整已接入房间列表, 全部客服重新推送完整待接入列表, 消息类型 : WEBSOCKET_MSG_WAIT_LIST
+     */
+    WEBSOCKET_MSG_USER_CONNECT_JOIN("WEBSOCKET_MSG_USER_JOIN"),
+
+    /**
+     * 房间历史消息, 接收前端消息, 前端必须分页获取, 需要携带分页参数
+     */
+    WEBSOCKET_MSG_ROOM_HISTORY("WEBSOCKET_MSG_ROOM_HISTORY"),
+
+    /**
+     * 客服发送消息, 接收前端消息, 返回发送结果
+     */
+    WEBSOCKET_MSG_KF_SEND("WEBSOCKET_MSG_KF_SEND"),
+
+    /**
+     * 房间消息, 推送玩家消息
+     */
+    WEBSOCKET_MSG_ROOM_MSG("WEBSOCKET_MSG_ROOM_MSG"),
+
+    /**
+     * 获取已结束房间列表, 接收前端消息, 必须分页获取, 需要携带分页参数
+     */
+    WEBSOCKET_MSG_FINISH_ROOM_LIST("WEBSOCKET_MSG_FINISH_ROOM_LIST"),
+
+    /**
+     * 结束会话, 接收前端消息, 推送完整的已连接房间列表
+     */
+    WEBSOCKET_MSG_FINISH_SESSION("WEBSOCKET_MSG_FINISH_SESSION"),
+
+    /**
+     * 快捷回复, 接收前端消息,请求动作包含查询, 修改, 删除, 新增
+     */
+    WEBSOCKET_MSG_QUICK_REPLY("WEBSOCKET_MSG_QUICK_REPLY");
+
+    /**
+     * 消息类型
+     */
+    private String value;
+}

+ 0 - 49
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/dto/KfMsgDTO.java

@@ -1,49 +0,0 @@
-package com.zanxiang.game.module.manage.pojo.dto;
-
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * @author : lingfeng
- * @time : 2024-02-26
- * @description : 客服消息
- */
-@Data
-public class KfMsgDTO {
-
-    /**
-     * 消息id
-     */
-    private String msgId;
-
-    /**
-     * 消息类型
-     */
-    private String msgType;
-
-    /**
-     * 已读状态
-     */
-    private Boolean readStatus;
-
-    /**
-     * 房间id
-     */
-    private Long roomId;
-
-    /**
-     * 消息归属
-     */
-    private String msgOwner;
-
-    /**
-     * 消息内容
-     */
-    private String content;
-
-    /**
-     * 创建时间
-     */
-    private LocalDateTime createTime;
-}

+ 340 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/dto/KfWebSocketMsgDTO.java

@@ -0,0 +1,340 @@
+package com.zanxiang.game.module.manage.pojo.dto;
+
+import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
+import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-02-26
+ * @description : WebSocket客服消息通信返回
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class KfWebSocketMsgDTO {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * webSocket消息类型
+     */
+    private KfWebSocketMsgEnum webSocketMsgType;
+
+    /**
+     * 客服id, 存在 : 表示该消息发送给指定客服, 不存在 : 表示该消息发送给所有在线客服
+     */
+    private Long kfUserId;
+
+    /**
+     * 分页信息
+     */
+    private PageBean page;
+
+    /**
+     * 结果信息
+     */
+    private ResultBean result;
+
+    /**
+     * 游戏列表, 全量不分页
+     */
+    private List<GameBean> gameList;
+
+    /**
+     * 待接入列表, 全量不分页
+     */
+    private List<WaitUserBean> waitUserList;
+
+    /**
+     * 房间列表, 已接入房间全量返回, 已经结束房间分页返回, 配套返回 page 信息
+     */
+    private List<RoomBean> roomList;
+
+    /**
+     * 房间消息 : 房间消息推送和历史消息列表都用该字段, 历史消息列表会配套返回 page 信息
+     */
+    private List<RoomMsgBean> roomMsgList;
+
+    public KfWebSocketMsgDTO(KfWebSocketMsgEnum webSocketMsgType, String errorMsg) {
+        this.webSocketMsgType = webSocketMsgType;
+        this.result = ResultBean.builder()
+                .errorCode(500)
+                .errorMsg(errorMsg)
+                .build();
+    }
+
+    public static KfWebSocketMsgDTO fail(KfWebSocketMsgEnum webSocketMsgType, String errorMsg) {
+        return new KfWebSocketMsgDTO(webSocketMsgType, errorMsg);
+    }
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class GameBean {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 游戏id
+         */
+        private Long gameId;
+
+        /**
+         * 小程序名称
+         */
+        private String appName;
+    }
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class WaitUserBean {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 玩家openId
+         */
+        private String openId;
+
+        /**
+         * 消息游戏id
+         */
+        private Long gameId;
+
+        /**
+         * 最近角色id
+         */
+        private String lastRoleId;
+
+        /**
+         * 最近角色名称
+         */
+        private String lastRoleName;
+
+        /**
+         * 最后一条消息
+         */
+        private String lastMsg;
+
+        /**
+         * 最后一条消息时间
+         */
+        private LocalDateTime lastMsgTime;
+
+        /**
+         * 开始等待时间
+         */
+        private LocalDateTime waitStartTime;
+    }
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class RoomBean {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 房间id
+         */
+        private Long roomId;
+
+        /**
+         * 消息游戏id
+         */
+        private Long gameId;
+
+        /**
+         * 玩家open_id
+         */
+        private String openId;
+
+        /**
+         * 最近角色id
+         */
+        private String lastRoleId;
+
+        /**
+         * 最近角色名称
+         */
+        private String lastRoleName;
+
+        /**
+         * 客服id
+         */
+        private Long kfUserId;
+
+        /**
+         * 房间在线状态
+         */
+        private Boolean online;
+
+        /**
+         * 最后一条消息
+         */
+        private String lastMsg;
+
+        /**
+         * 最后一条消息时间
+         */
+        private LocalDateTime lastMsgTime;
+    }
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class RoomMsgBean {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 消息id
+         */
+        private String msgId;
+
+        /**
+         * 消息游戏id
+         */
+        private Long gameId;
+
+        /**
+         * 房间id
+         */
+        private Long roomId;
+
+        /**
+         * 消息类型
+         */
+        private KfRoomMsgTypeEnum msgType;
+
+        /**
+         * 已读状态
+         */
+        private Boolean readStatus;
+
+        /**
+         * 消息归属
+         */
+        private String msgOwner;
+
+        /**
+         * 消息内容
+         */
+        private MsgContentBean content;
+
+        /**
+         * 创建时间
+         */
+        private LocalDateTime createTime;
+    }
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class MsgContentBean {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 文本内容
+         */
+        private String text;
+
+        /**
+         * 图片地址
+         */
+        private String image;
+
+        /**
+         * 图文消息
+         */
+        private LinkBean link;
+    }
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class LinkBean {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 标题
+         */
+        private String title;
+
+        /**
+         * 描述
+         */
+        private String description;
+
+        /**
+         * 跳转链接
+         */
+        private String url;
+
+        /**
+         * 图片地址
+         */
+        private String thumbUrl;
+    }
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class PageBean {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 当前页数
+         */
+        private Long pageNum;
+
+        /**
+         * 单页数量
+         */
+        private Long pageSize;
+
+        /**
+         * 总页数
+         */
+        private Long pageTotal;
+    }
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class ResultBean {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 0 : 成功, 其他失败
+         */
+        private Integer errorCode;
+
+        /**
+         * 错误消息
+         */
+        private String errorMsg;
+    }
+
+}

+ 76 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/params/KfWebSocketMsgParam.java

@@ -0,0 +1,76 @@
+package com.zanxiang.game.module.manage.pojo.params;
+
+import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
+import lombok.Data;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-02-27
+ * @description : WebSocket客服消息通信参数
+ */
+@Data
+public class KfWebSocketMsgParam {
+
+    /**
+     * webSocket消息类型
+     */
+    private KfWebSocketMsgEnum webSocketMsgType;
+
+    /**
+     * 客服登录token, 消息类型为首次连接(WEBSOCKET_MSG_CONNECT)时, 必须携带, 否则无法建立连接
+     */
+    private String token;
+
+    /**
+     * 分页信息, 分页获取数据的时候, 必须携带
+     */
+    private PageBean page;
+
+    /**
+     * 玩家id, 玩家接入的时候必须传
+     */
+    private String openId;
+
+    /**
+     * 房间id, 获取房间历史消息必传
+     */
+    private Long roomId;
+
+    /**
+     * 消息内容, 客服发送消息的时候必传
+     */
+    private MsgContentBean msgContent;
+
+    @Data
+    public static class MsgContentBean {
+
+        /**
+         * 消息类型, 文本传 : text, 图片传 : image
+         */
+        private String msgType;
+
+        /**
+         * 文本内容
+         */
+        private String text;
+
+        /**
+         * 图片地址(oss下载地址)
+         */
+        private String picUrl;
+    }
+
+    @Data
+    public static class PageBean {
+
+        /**
+         * 当前页数
+         */
+        private Long pageNum;
+
+        /**
+         * 单页数量
+         */
+        private Long pageSize;
+    }
+}

+ 57 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/vo/KfMsgRoomVO.java

@@ -0,0 +1,57 @@
+package com.zanxiang.game.module.manage.pojo.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-02-26
+ * @description : 客服消息房间
+ */
+@Data
+public class KfMsgRoomVO {
+
+    /**
+     * 主键
+     */
+    @ApiModelProperty(notes = "主键")
+    private Long id;
+
+    /**
+     * 玩家open_id
+     */
+    @ApiModelProperty(notes = "玩家open_id")
+    private String openId;
+
+    /**
+     * 游戏id
+     */
+    @ApiModelProperty(notes = "游戏id")
+    private String gameId;
+
+    /**
+     * 客服id
+     */
+    @ApiModelProperty(notes = "客服id")
+    private Long kfUserId;
+
+    /**
+     * 房间在线状态
+     */
+    @ApiModelProperty(notes = "房间在线状态")
+    private Boolean online;
+
+    /**
+     * 最后一条消息
+     */
+    @ApiModelProperty(notes = "最后一条消息")
+    private String lastMsg;
+
+    /**
+     * 最后一条消息时间
+     */
+    @ApiModelProperty(notes = "最后一条消息时间")
+    private LocalDateTime lastMsgTime;
+}

+ 58 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/vo/KfMsgWaitUserVO.java

@@ -0,0 +1,58 @@
+package com.zanxiang.game.module.manage.pojo.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-02-26
+ * @description : 待接入玩家
+ */
+@Data
+public class KfMsgWaitUserVO {
+
+    /**
+     * 玩家openId
+     */
+    @ApiModelProperty(notes = "玩家openId")
+    private String openId;
+
+    /**
+     * 游戏id
+     */
+    @ApiModelProperty(notes = "游戏id")
+    private Long gameId;
+
+    /**
+     * 最近角色id
+     */
+    @ApiModelProperty(notes = "最近角色id")
+    private String lastRoleId;
+
+    /**
+     * 最近角色名称
+     */
+    @ApiModelProperty(notes = "最近角色名称")
+    private String lastRoleName;
+
+    /**
+     * 最后一条消息
+     */
+    @ApiModelProperty(notes = "最后一条消息")
+    private String lastMsg;
+
+    /**
+     * 最后一条消息时间
+     */
+    @ApiModelProperty(notes = "最后一条消息时间")
+    private LocalDateTime lastMsgTime;
+
+    /**
+     * 开始等待时间
+     */
+    @ApiModelProperty(notes = "开始等待时间")
+    private LocalDateTime waitStartTime;
+
+}

+ 25 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/vo/KfWebsocketGameVO.java

@@ -0,0 +1,25 @@
+package com.zanxiang.game.module.manage.pojo.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-02-26
+ * @description : 客服游戏
+ */
+@Data
+public class KfWebsocketGameVO {
+
+    /**
+     * 应用id
+     */
+    @ApiModelProperty(notes = "应用id")
+    private String appId;
+
+    /**
+     * 应用名称
+     */
+    @ApiModelProperty(notes = "应用名称")
+    private String appName;
+}

+ 17 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfRoomService.java

@@ -1,12 +1,29 @@
 package com.zanxiang.game.module.manage.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
 import com.zanxiang.game.module.mybatis.entity.KfRoom;
 
+import java.util.List;
+
 /**
  * @author : lingfeng
  * @time : 2024-02-23
  * @description : 客服聊天房间
  */
 public interface IKfRoomService extends IService<KfRoom> {
+
+    /**
+     * 客服获取游戏列表
+     *
+     * @return : 返回游戏列表
+     */
+    List<KfWebSocketMsgDTO.GameBean> getKfGameList();
+
+    /**
+     * 待接入房间列表
+     *
+     * @return : 返回待接入房间列表
+     */
+    List<KfWebSocketMsgDTO.RoomBean> getOnlineRoomList();
 }

+ 10 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfSessionUserService.java

@@ -1,12 +1,22 @@
 package com.zanxiang.game.module.manage.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
 import com.zanxiang.game.module.mybatis.entity.KfSessionUser;
 
+import java.util.List;
+
 /**
  * @author : lingfeng
  * @time : 2024-02-23
  * @description : 客服聊天玩家信息
  */
 public interface IKfSessionUserService extends IService<KfSessionUser> {
+
+    /**
+     * 获取待接入列表
+     *
+     * @return : 返回待接入列表
+     */
+    List<KfWebSocketMsgDTO.WaitUserBean> getWaitUserList();
 }

+ 25 - 10
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/CpCallServiceImpl.java

@@ -1,6 +1,10 @@
 package com.zanxiang.game.module.manage.service.impl;
 
+import com.zanxiang.game.module.manage.pojo.params.KfWebSocketMsgParam;
 import com.zanxiang.module.util.JsonUtil;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
 import org.springframework.web.client.RestTemplate;
 
 import java.nio.charset.StandardCharsets;
@@ -22,25 +26,25 @@ public class CpCallServiceImpl {
      */
     private static final String SIGN_MD5 = "MD5";
 
-//    public static void main(String[] args) throws Exception{
+//    public static void main(String[] args) throws Exception {
 //        test();
 //    }
 
     public static void test() throws Exception {
-        String key =  "355b7f07125c1ef71cfd10166e0b90aa";
+        String key = "355b7f07125c1ef71cfd10166e0b90aa";
         RestTemplate restTemplate = new RestTemplate();
 
         String url = "https://ht.lttx.t5yx.cn/extapi?action=BgzszhSendTip";
 
         Map<String, Object> param = new HashMap<>();
 
-        String msgId = "testMsgId";
+        String msgId = "testMsgId2";
         param.put("msgId", msgId);
 
-        String strRan = "strRan";
+        String strRan = "strRan1";
         param.put("strRan", strRan);
 
-        Long time = 1708484041L;
+        Long time = 1709002794L;
         param.put("time", time);
 
         String signStr = "key=355b7f07125c1ef71cfd10166e0b90aa&msgId=" + msgId + "&strRan=" + strRan + "&time=" + time;
@@ -50,24 +54,35 @@ public class CpCallServiceImpl {
 
         param.put("sign", CpCallServiceImpl.MD5(signStr));
 
-        param.put("serverid", 226);
+//        param.put("serverid", 226);
+        param.put("serverid", 592);
         List<String> roleIds = new ArrayList<>();
-        roleIds.add("798136551028973749");
+//        roleIds.add("798136461189027272");
+        roleIds.add("815728771283100062");
         param.put("roleIds", roleIds);
 
         param.put("pushType", 1);
 
+        String text = "111尊敬的尊享玩家“角色名”:\n" +
+                "叮,尊享管家小诗正在微信上等待与您的见面,根据您的游戏角色成长,小诗特意为您定制一份战力快速升级的攻略和几个尊享限定礼包,助您快速提升~\n" +
+                "您要尽快通过游戏内“联系客服”按钮与我联系,时间有限,请您尽快与我联系哦。\n" +
+                "联系时请您带上此页面截图,同时请您保密勿将此内容分享给其他玩家,可能会导致尊享限定礼包被冒领哦!";
         Map<String, Object> msgContent = new HashMap<>();
-        msgContent.put("text", "测试消息");
+        msgContent.put("text", text);
         List<String> imgs = new ArrayList<>();
-        imgs.add("https://t7.baidu.com/it/u=4162611394,4275913936&fm=193&f=GIF");
+        imgs.add("https://test.84game.cn/1709024863.jpeg");
         msgContent.put("imgs", JsonUtil.toString(imgs));
         param.put("msgContent", msgContent);
 
         System.out.println("接口参数 param :" + JsonUtil.toString(param));
 
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        headers.set(HttpHeaders.ACCEPT_CHARSET, "UTF-8");
 
-        String result = restTemplate.postForObject(url, JsonUtil.toString(param), String.class);
+        HttpEntity<String> request = new HttpEntity<>(JsonUtil.toString(param), headers);
+
+        String result = restTemplate.postForObject(url, request, String.class);
 
         System.out.println("返回结果" + result);
     }

+ 82 - 17
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfRoomMsgServiceImpl.java

@@ -1,13 +1,16 @@
 package com.zanxiang.game.module.manage.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zanxiang.game.module.manage.enums.KfRoomMsgOwnerEnum;
 import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
+import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
 import com.zanxiang.game.module.manage.pojo.dto.KfAppletMsgDTO;
-import com.zanxiang.game.module.manage.pojo.dto.KfMsgDTO;
-import com.zanxiang.game.module.manage.service.IKfRoomMsgService;
+import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
+import com.zanxiang.game.module.manage.service.*;
 import com.zanxiang.game.module.manage.websocket.KfMsgWebsocketHandler;
-import com.zanxiang.game.module.mybatis.entity.KfRoomMsg;
+import com.zanxiang.game.module.mybatis.entity.*;
 import com.zanxiang.game.module.mybatis.mapper.KfRoomMsgMapper;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
@@ -16,8 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Collections;
 import java.util.Objects;
 
 /**
@@ -29,49 +31,112 @@ import java.util.Objects;
 @Service
 public class KfRoomMsgServiceImpl extends ServiceImpl<KfRoomMsgMapper, KfRoomMsg> implements IKfRoomMsgService {
 
+    @Autowired
+    private IKfSessionUserService kfSessionUserService;
 
     @Autowired
     private KfMsgWebsocketHandler kfMsgWebsocketHandler;
 
+    @Autowired
+    private IKfRoomService kfRoomService;
+
+    @Autowired
+    private IGameAppletService gameAppletService;
+
+    @Autowired
+    private IUserService userService;
+
     @Override
     public void appletMsg(String postData) {
         log.error("接收到SDK转发的小程序消息, postData : {}", postData);
         KfAppletMsgDTO kfAppletMsgDTO = JsonUtil.toObj(postData, KfAppletMsgDTO.class);
+        GameApplet gameApplet = gameAppletService.getOne(new LambdaQueryWrapper<GameApplet>()
+                .eq(GameApplet::getGhId, kfAppletMsgDTO.getToUserName()));
         //用户进入会话事件
         if (Objects.equals(kfAppletMsgDTO.getMsgType(), KfAppletMsgDTO.MSG_TYPE_EVENT)
                 && Objects.equals(kfAppletMsgDTO.getEvent(), KfAppletMsgDTO.EVENT_USER_ENTER_TEMP_SESSION)) {
-            //创建用户信息
+            KfSessionUser kfSessionUser = kfSessionUserService.getById(kfAppletMsgDTO.getFromUserName());
+            if (kfSessionUser != null) {
+                return;
+            }
+            User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, kfAppletMsgDTO.getFromUserName()));
+            //创建保存用户信息
+            kfSessionUserService.save(this.transform(kfAppletMsgDTO, gameApplet, user));
             return;
         }
         //非玩家消息, 不做处理
         if (kfAppletMsgDTO.getMsgId() == null) {
             return;
         }
+        //查询玩家的房间连接状态
+        KfRoom kfRoom = kfRoomService.getOne(new LambdaQueryWrapper<KfRoom>()
+                .eq(KfRoom::getOpenId, kfAppletMsgDTO.getFromUserName())
+                .eq(KfRoom::getOnline, Boolean.TRUE));
         //消息存储
-        KfRoomMsg kfRoomMsg = KfRoomMsg.builder()
+        KfWebSocketMsgDTO.MsgContentBean msgContent = this.getMsgContent(kfAppletMsgDTO);
+        KfRoomMsg kfRoomMsg = this.transform(kfAppletMsgDTO, gameApplet, kfRoom, postData, msgContent);
+        super.save(kfRoomMsg);
+        //玩家状态更新为待接入状态
+        if (kfRoom == null) {
+            kfSessionUserService.update(new LambdaUpdateWrapper<KfSessionUser>()
+                    .set(KfSessionUser::getIsWait, Boolean.TRUE)
+                    .set(KfSessionUser::getWaitStartTime, LocalDateTime.now())
+                    .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
+                    .eq(KfSessionUser::getOpenId, kfAppletMsgDTO.getFromUserName()));
+        }
+        //消息转发到redis频道
+        kfMsgWebsocketHandler.pushMessage(this.transform(kfRoomMsg, msgContent));
+    }
+
+    private KfWebSocketMsgDTO transform(KfRoomMsg kfRoomMsg, KfWebSocketMsgDTO.MsgContentBean msgContent) {
+        //todo : 需要修改
+        KfWebSocketMsgDTO.RoomMsgBean roomMsgBean = BeanUtil.copy(kfRoomMsg, KfWebSocketMsgDTO.RoomMsgBean.class);
+        roomMsgBean.setContent(msgContent);
+        KfWebSocketMsgEnum kfWebSocketMsgEnum = roomMsgBean.getRoomId() == null ?
+                KfWebSocketMsgEnum.WEBSOCKET_MSG_WAIT_LIST : KfWebSocketMsgEnum.WEBSOCKET_MSG_ROOM_MSG;
+        return KfWebSocketMsgDTO.builder()
+                .webSocketMsgType(kfWebSocketMsgEnum)
+                .roomMsgList(Collections.singletonList(roomMsgBean))
+                .build();
+    }
+
+    private KfSessionUser transform(KfAppletMsgDTO kfAppletMsgDTO, GameApplet gameApplet, User user) {
+        return KfSessionUser.builder()
+                .openId(kfAppletMsgDTO.getFromUserName())
+                .gameId(gameApplet.getGameId())
+                .userId(user == null ? null : user.getId())
+                .isWait(Boolean.FALSE)
+                .createTime(LocalDateTime.now())
+                .updateTime(LocalDateTime.now())
+                .build();
+    }
+
+    private KfRoomMsg transform(KfAppletMsgDTO kfAppletMsgDTO, GameApplet gameApplet, KfRoom kfRoom, String postData,
+                                KfWebSocketMsgDTO.MsgContentBean msgContent) {
+        return KfRoomMsg.builder()
                 .msgId(String.valueOf(kfAppletMsgDTO.getMsgId()))
                 .msgType(kfAppletMsgDTO.getMsgType())
+                .gameId(gameApplet.getGameId())
+                .openId(kfAppletMsgDTO.getFromUserName())
                 .readStatus(Boolean.FALSE)
+                .roomId(kfRoom == null ? null : kfRoom.getId())
                 .msgOwner(KfRoomMsgOwnerEnum.KF_MSG_OWNER_USER.getValue())
-                .content(this.getMsgContent(kfAppletMsgDTO))
+                .content(JsonUtil.toString(msgContent))
                 .source(postData)
                 .createTime(LocalDateTime.now())
                 .build();
-        super.save(kfRoomMsg);
-        // 消息转发到redis频道
-        kfMsgWebsocketHandler.pushMessage(JsonUtil.toString(BeanUtil.copy(kfRoomMsg, KfMsgDTO.class)));
     }
 
-    private String getMsgContent(KfAppletMsgDTO kfAppletMsgDTO) {
-        Map<String, String> contentMap = new HashMap<>(2);
+    private KfWebSocketMsgDTO.MsgContentBean getMsgContent(KfAppletMsgDTO kfAppletMsgDTO) {
+        //todo : 这里要考虑图片是否要传到oss上, 用自己的网络地址, 腾讯的图片地址不能保证一直有效
+        KfWebSocketMsgDTO.MsgContentBean msgContentBean = new KfWebSocketMsgDTO.MsgContentBean();
         if (Objects.equals(KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue(), kfAppletMsgDTO.getMsgType())) {
-            contentMap.put("text", kfAppletMsgDTO.getContent());
+            msgContentBean.setText(kfAppletMsgDTO.getContent());
         }
         if (Objects.equals(KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue(), kfAppletMsgDTO.getMsgType())) {
-            contentMap.put("picUrl", kfAppletMsgDTO.getPicUrl());
-            contentMap.put("mediaId", kfAppletMsgDTO.getMediaId());
+            msgContentBean.setImage(kfAppletMsgDTO.getPicUrl());
         }
-        return JsonUtil.toString(contentMap);
+        return msgContentBean;
     }
 
 }

+ 92 - 2
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfRoomServiceImpl.java

@@ -1,12 +1,26 @@
 package com.zanxiang.game.module.manage.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zanxiang.game.module.manage.service.IKfRoomService;
-import com.zanxiang.game.module.mybatis.entity.KfRoom;
+import com.zanxiang.erp.security.util.SecurityUtil;
+import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
+import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
+import com.zanxiang.game.module.manage.service.*;
+import com.zanxiang.game.module.mybatis.entity.*;
 import com.zanxiang.game.module.mybatis.mapper.KfRoomMapper;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.bean.BeanUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
 /**
  * @author : lingfeng
  * @time : 2024-02-23
@@ -15,4 +29,80 @@ import org.springframework.stereotype.Service;
 @Slf4j
 @Service
 public class KfRoomServiceImpl extends ServiceImpl<KfRoomMapper, KfRoom> implements IKfRoomService {
+
+    @Autowired
+    private IGameAuthService gameAuthService;
+
+    @Autowired
+    private IGameAppletService gameAppletService;
+
+    @Autowired
+    private IGameUserRoleService gameUserRoleService;
+
+    @Autowired
+    private IKfRoomMsgService kfRoomMsgService;
+
+    @Override
+    public List<KfWebSocketMsgDTO.GameBean> getKfGameList() {
+        List<GameAuth> gameAuthList = gameAuthService.list(new LambdaQueryWrapper<GameAuth>()
+                .eq(!SecurityUtil.isAdmin(), GameAuth::getUserId, SecurityUtil.getUserId()));
+        if (!SecurityUtil.isAdmin() && CollectionUtils.isEmpty(gameAuthList)) {
+            return Collections.emptyList();
+        }
+        return gameAppletService.list(new LambdaQueryWrapper<GameApplet>()
+                .in(!SecurityUtil.isAdmin(), GameApplet::getGameId,
+                        gameAuthList.stream().map(GameAuth::getGameId).collect(Collectors.toSet()))
+                .eq(GameApplet::getType, 1)
+        ).stream().map(this::transform).collect(Collectors.toList());
+    }
+
+    private KfWebSocketMsgDTO.GameBean transform(GameApplet gameApplet) {
+        if (gameApplet == null) {
+            return null;
+        }
+        return BeanUtil.copy(gameApplet, KfWebSocketMsgDTO.GameBean.class);
+    }
+
+    @Override
+    public List<KfWebSocketMsgDTO.RoomBean> getOnlineRoomList() {
+        return super.list(new LambdaQueryWrapper<KfRoom>()
+                .eq(KfRoom::getKfUserId, SecurityUtil.getUserId())
+                .eq(KfRoom::getOnline, Boolean.TRUE)
+        ).stream().map(this::transform).collect(Collectors.toList());
+    }
+
+    private KfWebSocketMsgDTO.RoomBean transform(KfRoom kfRoom) {
+        if (kfRoom == null) {
+            return null;
+        }
+        KfWebSocketMsgDTO.RoomBean roomBean = BeanUtil.copy(kfRoom, KfWebSocketMsgDTO.RoomBean.class);
+        //最近角色信息
+        GameUserRole gameUserRole = null;
+        if (kfRoom.getUserId() != null) {
+            gameUserRole = gameUserRoleService.getLastGameUserRoleName(kfRoom.getUserId(), kfRoom.getGameId());
+        }
+        if (gameUserRole != null) {
+            roomBean.setLastRoleName(gameUserRole.getRoleName());
+            roomBean.setLastRoleId(gameUserRole.getRoleId());
+        }
+        //最后一条消息
+        KfRoomMsg kfRoomMsg = kfRoomMsgService.getOne(new LambdaQueryWrapper<KfRoomMsg>()
+                .eq(KfRoomMsg::getRoomId, kfRoom.getId())
+                .orderByDesc(KfRoomMsg::getCreateTime)
+                .last("limit 1"));
+        if (kfRoomMsg != null) {
+            if (Objects.equals(kfRoomMsg.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue())) {
+                Map<String, String> textMap = JsonUtil.toMap(kfRoomMsg.getContent(), Map.class, String.class);
+                roomBean.setLastMsg(textMap.get("text"));
+            }
+            if (Objects.equals(kfRoomMsg.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_LINK.getValue())) {
+                roomBean.setLastMsg("充值消息, 请在聊天中查看");
+            }
+            if (Objects.equals(kfRoomMsg.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue())) {
+                roomBean.setLastMsg("图片消息, 请在聊天中查看");
+            }
+            roomBean.setLastMsgTime(kfRoomMsg.getCreateTime());
+        }
+        return roomBean;
+    }
 }

+ 63 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfSessionUserServiceImpl.java

@@ -1,12 +1,27 @@
 package com.zanxiang.game.module.manage.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
+import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
+import com.zanxiang.game.module.manage.service.IGameUserRoleService;
+import com.zanxiang.game.module.manage.service.IKfRoomMsgService;
 import com.zanxiang.game.module.manage.service.IKfSessionUserService;
+import com.zanxiang.game.module.mybatis.entity.GameUserRole;
+import com.zanxiang.game.module.mybatis.entity.KfRoomMsg;
 import com.zanxiang.game.module.mybatis.entity.KfSessionUser;
 import com.zanxiang.game.module.mybatis.mapper.KfSessionUserMapper;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.bean.BeanUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
 /**
  * @author : lingfeng
  * @time : 2024-02-23
@@ -15,4 +30,52 @@ import org.springframework.stereotype.Service;
 @Slf4j
 @Service
 public class KfSessionUserServiceImpl extends ServiceImpl<KfSessionUserMapper, KfSessionUser> implements IKfSessionUserService {
+
+    @Autowired
+    private IGameUserRoleService gameUserRoleService;
+
+    @Autowired
+    private IKfRoomMsgService kfRoomMsgService;
+
+    @Override
+    public List<KfWebSocketMsgDTO.WaitUserBean> getWaitUserList() {
+        return super.list(new LambdaQueryWrapper<KfSessionUser>()
+                .eq(KfSessionUser::getIsWait, Boolean.TRUE)
+        ).stream().map(this::transform).collect(Collectors.toList());
+    }
+
+    private KfWebSocketMsgDTO.WaitUserBean transform(KfSessionUser kfSessionUser) {
+        if (kfSessionUser == null) {
+            return null;
+        }
+        KfWebSocketMsgDTO.WaitUserBean waitUserBean = BeanUtil.copy(kfSessionUser, KfWebSocketMsgDTO.WaitUserBean.class);
+        //最近角色信息
+        GameUserRole gameUserRole = null;
+        if (kfSessionUser.getUserId() != null) {
+            gameUserRole = gameUserRoleService.getLastGameUserRoleName(kfSessionUser.getUserId(), kfSessionUser.getGameId());
+        }
+        if (gameUserRole != null) {
+            waitUserBean.setLastRoleId(gameUserRole.getRoleId());
+            waitUserBean.setLastRoleName(gameUserRole.getRoleName());
+        }
+        //最后一条消息
+        KfRoomMsg kfRoomMsg = kfRoomMsgService.getOne(new LambdaQueryWrapper<KfRoomMsg>()
+                .eq(KfRoomMsg::getOpenId, kfSessionUser.getOpenId())
+                .orderByDesc(KfRoomMsg::getCreateTime)
+                .last("limit 1"));
+        if (kfRoomMsg != null) {
+            if (Objects.equals(kfRoomMsg.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue())) {
+                Map<String, String> textMap = JsonUtil.toMap(kfRoomMsg.getContent(), Map.class, String.class);
+                waitUserBean.setLastMsg(textMap.get("text"));
+            }
+            if (Objects.equals(kfRoomMsg.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue())) {
+                waitUserBean.setLastMsg("图片消息, 请在聊天中查看");
+            }
+            if (Objects.equals(kfRoomMsg.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_LINK.getValue())) {
+                waitUserBean.setLastMsg("充值消息, 请在聊天中查看");
+            }
+            waitUserBean.setLastMsgTime(kfRoomMsg.getCreateTime());
+        }
+        return waitUserBean;
+    }
 }

+ 25 - 9
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgRedisListener.java

@@ -1,5 +1,7 @@
 package com.zanxiang.game.module.manage.websocket;
 
+import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
+import com.zanxiang.module.util.JsonUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.connection.Message;
@@ -9,6 +11,7 @@ import org.springframework.web.socket.TextMessage;
 import org.springframework.web.socket.WebSocketSession;
 
 import java.io.IOException;
+import java.util.List;
 
 /**
  * @author : lingfeng
@@ -27,16 +30,29 @@ public class KfMsgRedisListener implements MessageListener {
         //从redis中拿到的消息
         String messageBody = new String(message.getBody());
         log.error("redis监听器监听到消息 messageBody : {}", messageBody);
-        String[] by = messageBody.split("_");
-        //从消息中获取userId
-        WebSocketSession session = kfMsgWebSocketSessionRegistry.getSession(by[0]);
-        //判断连接存在, 且未来断开, 发送消息
-        if (session != null && session.isOpen()) {
-            try {
-                session.sendMessage(new TextMessage(by[1]));
-            } catch (IOException e) {
-                // Handle exception
+        //转化消息对象
+        KfWebSocketMsgDTO kfWebSocketMsgDTO = JsonUtil.toObj(messageBody, KfWebSocketMsgDTO.class);
+        Long kfUserId = kfWebSocketMsgDTO.getKfUserId();
+        //发送给指定客服
+        if (kfUserId != null) {
+            WebSocketSession session = kfMsgWebSocketSessionRegistry.getSession(kfUserId);
+            if (session != null && session.isOpen()) {
+                try {
+                    session.sendMessage(new TextMessage(JsonUtil.toString(kfWebSocketMsgDTO)));
+                } catch (IOException ignored) {
+                }
             }
+            return;
         }
+        //发送给所有在线客服
+        List<WebSocketSession> openSessions = kfMsgWebSocketSessionRegistry.getAllAessions();
+        openSessions.forEach(session -> {
+            if (session != null && session.isOpen()) {
+                try {
+                    session.sendMessage(new TextMessage(JsonUtil.toString(kfWebSocketMsgDTO)));
+                } catch (IOException ignored) {
+                }
+            }
+        });
     }
 }

+ 10 - 4
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebSocketSessionRegistry.java

@@ -3,6 +3,8 @@ package com.zanxiang.game.module.manage.websocket;
 import org.springframework.stereotype.Component;
 import org.springframework.web.socket.WebSocketSession;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -13,17 +15,21 @@ import java.util.concurrent.ConcurrentHashMap;
 @Component
 public class KfMsgWebSocketSessionRegistry {
 
-    private final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
+    private final ConcurrentHashMap<Long, WebSocketSession> sessions = new ConcurrentHashMap<>();
 
-    public void addSession(String userId, WebSocketSession session) {
+    public void addSession(Long userId, WebSocketSession session) {
         sessions.put(userId, session);
     }
 
-    public WebSocketSession getSession(String userId) {
+    public WebSocketSession getSession(Long userId) {
         return sessions.get(userId);
     }
 
-    public void removeSession(String userId) {
+    public void removeSession(Long userId) {
         sessions.remove(userId);
     }
+
+    public List<WebSocketSession> getAllAessions() {
+        return new ArrayList<>(sessions.values());
+    }
 }

+ 117 - 7
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebsocketHandler.java

@@ -1,7 +1,18 @@
 package com.zanxiang.game.module.manage.websocket;
 
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.zanxiang.erp.base.pojo.TokenInfo;
+import com.zanxiang.erp.security.util.SecurityUtil;
 import com.zanxiang.game.module.manage.constant.RedisKeyConstant;
+import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
+import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
+import com.zanxiang.game.module.manage.pojo.params.KfWebSocketMsgParam;
+import com.zanxiang.game.module.manage.service.IKfRoomService;
+import com.zanxiang.game.module.manage.service.IKfSessionUserService;
+import com.zanxiang.game.module.mybatis.entity.KfRoom;
+import com.zanxiang.module.util.JsonUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -11,6 +22,10 @@ import org.springframework.web.socket.WebSocketHandler;
 import org.springframework.web.socket.WebSocketMessage;
 import org.springframework.web.socket.WebSocketSession;
 
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+
 /**
  * @author : lingfeng
  * @time : 2024-02-23
@@ -27,12 +42,17 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
     @Autowired
     private KfMsgWebSocketSessionRegistry kfMsgWebSocketSessionRegistry;
 
+    @Autowired
+    private IKfRoomService kfRoomService;
+
+    @Autowired
+    private IKfSessionUserService kfSessionUserService;
+
     /**
      * websocket连接建立成功
      */
     @Override
     public void afterConnectionEstablished(WebSocketSession session) throws Exception {
-        log.error("前端长连接建立成功");
     }
 
     /**
@@ -42,8 +62,56 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
     public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
         String msgStr = message.getPayload().toString();
         log.error("收到前端消息 msgStr : {}", msgStr);
-        kfMsgWebSocketSessionRegistry.addSession(msgStr, session);
-        this.pushMessage(msgStr + "_" + "你好!武哥");
+        //消息解析
+        KfWebSocketMsgParam kfWebSocketMsgParam = JsonUtil.toObj(msgStr, KfWebSocketMsgParam.class);
+        //消息类型
+        KfWebSocketMsgEnum webSocketMsgType = kfWebSocketMsgParam.getWebSocketMsgType();
+        //请求令牌
+        String token = kfWebSocketMsgParam.getToken();
+        //连接身份验证
+        boolean tokenCheckResult = this.tokenCheck(webSocketMsgType, token);
+        //身份验证失败, 没有后续动作
+        if (!tokenCheckResult) {
+            return;
+        }
+        //将token设置到当前线程
+        SecurityUtil.fillToken(token);
+        //添加会话
+        Long kfUserId = SecurityUtil.getUserId();
+        session.getAttributes().put("kfUserId", kfUserId);
+        kfMsgWebSocketSessionRegistry.addSession(kfUserId, session);
+        //客服创建连接-消息处理
+        if (Objects.equals(kfWebSocketMsgParam.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_KF_CREATE_CONNECT)) {
+            this.kfConnect(kfWebSocketMsgParam);
+        }
+        //玩家接入-消息处理
+        if (Objects.equals(kfWebSocketMsgParam.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_USER_CONNECT_JOIN)) {
+
+        }
+        //获取房间历史消息-消息处理
+        if (Objects.equals(kfWebSocketMsgParam.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_ROOM_HISTORY)) {
+
+        }
+        //客服发送消息-消息处理
+        if (Objects.equals(kfWebSocketMsgParam.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_KF_SEND)) {
+
+        }
+        //客服发送消息-消息处理
+        if (Objects.equals(kfWebSocketMsgParam.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_KF_SEND)) {
+
+        }
+        //获取已结束房间列表-消息处理
+        if (Objects.equals(kfWebSocketMsgParam.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_FINISH_ROOM_LIST)) {
+
+        }
+        //结束会话-消息处理
+        if (Objects.equals(kfWebSocketMsgParam.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_FINISH_SESSION)) {
+
+        }
+        //快捷回复-消息处理
+        if (Objects.equals(kfWebSocketMsgParam.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_QUICK_REPLY)) {
+
+        }
     }
 
     /**
@@ -51,7 +119,6 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
      */
     @Override
     public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
-        log.error("发生错误");
     }
 
     /**
@@ -59,7 +126,17 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
      */
     @Override
     public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
-        log.error("连接被关闭");
+        //从session中获取客服id
+        Long kfUserId = Long.valueOf(session.getAttributes().get("kfUserId").toString());
+        //关闭session关联的房间, 有在线状态的更新为断开状态
+        kfRoomService.update(new LambdaUpdateWrapper<KfRoom>()
+                .set(KfRoom::getOnline, Boolean.FALSE)
+                .set(KfRoom::getUpdateTime, LocalDateTime.now())
+                .eq(KfRoom::getKfUserId, kfUserId));
+        //关闭连接
+        session.close();
+        //移除连接
+        kfMsgWebSocketSessionRegistry.removeSession(kfUserId);
     }
 
     @Override
@@ -67,10 +144,43 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
         return false;
     }
 
+    private boolean tokenCheck(KfWebSocketMsgEnum webSocketMsgType, String token) {
+        //令牌为空
+        if (Strings.isBlank(token)) {
+            this.pushMessage(KfWebSocketMsgDTO.fail(webSocketMsgType, "非法参数, token令牌不可为空"));
+            return Boolean.FALSE;
+        }
+        //令牌验证
+        TokenInfo tokenInfo = SecurityUtil.parseToken(token);
+        if (tokenInfo == null) {
+            this.pushMessage(KfWebSocketMsgDTO.fail(webSocketMsgType, "参数错误, 令牌验证不通过"));
+            return Boolean.FALSE;
+        }
+        //返回验证通过
+        return Boolean.TRUE;
+    }
+
+    private void kfConnect(KfWebSocketMsgParam kfWebSocketMsgParam) {
+        //获取游戏列表
+        List<KfWebSocketMsgDTO.GameBean> gameList = kfRoomService.getKfGameList();
+        //获取待接入列表
+        List<KfWebSocketMsgDTO.WaitUserBean> waitUserList = kfSessionUserService.getWaitUserList();
+        //获取已接入房间列表
+        List<KfWebSocketMsgDTO.RoomBean> roomList = kfRoomService.getOnlineRoomList();
+        //发送消息
+        this.pushMessage(KfWebSocketMsgDTO.builder()
+                .webSocketMsgType(kfWebSocketMsgParam.getWebSocketMsgType())
+                .kfUserId(SecurityUtil.getUserId())
+                .gameList(gameList)
+                .waitUserList(waitUserList)
+                .roomList(roomList)
+                .build());
+    }
+
     /**
      * 消息发送到redis广播
      */
-    public void pushMessage(String message) {
-        redisTemplate.convertAndSend(RedisKeyConstant.KF_MSG_REDIS_LISTEN_TOPIC, message);
+    public void pushMessage(KfWebSocketMsgDTO kfWebSocketMsgDTO) {
+        redisTemplate.convertAndSend(RedisKeyConstant.KF_MSG_REDIS_LISTEN_TOPIC, JsonUtil.toString(kfWebSocketMsgDTO));
     }
 }

+ 10 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfRoom.java

@@ -33,6 +33,16 @@ public class KfRoom {
      */
     private String openId;
 
+    /**
+     * 玩家id
+     */
+    private Long userId;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
     /**
      * 客服id
      */

+ 21 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfRoomMsg.java

@@ -33,6 +33,27 @@ public class KfRoomMsg {
      */
     private String msgType;
 
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 玩家open_id
+     */
+    private String openId;
+
+    /**
+     * 玩家id
+     */
+    private Long userId;
+
+
+    /**
+     * 客服id
+     */
+    private Long kfUserId;
+
     /**
      * 已读状态
      */

+ 9 - 4
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfSessionUser.java

@@ -29,20 +29,25 @@ public class KfSessionUser {
     private String openId;
 
     /**
-     * 玩家id
+     * 游戏id
      */
-    private Long userId;
+    private Long gameId;
 
     /**
-     * 游戏id
+     * 玩家id
      */
-    private Long gameId;
+    private Long userId;
 
     /**
      * 是否待接入
      */
     private Boolean isWait;
 
+    /**
+     * 开始等待时间
+     */
+    private LocalDateTime waitStartTime;
+
     /**
      * 创建时间
      */