bilingfeng 1 год назад
Родитель
Сommit
e636927406

+ 38 - 14
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/dto/KfWebSocketMsgDTO.java

@@ -1,5 +1,6 @@
 package com.zanxiang.game.module.manage.pojo.dto;
 
+import com.zanxiang.erp.security.util.SecurityUtil;
 import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
 import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
 import lombok.AllArgsConstructor;
@@ -73,21 +74,21 @@ public class KfWebSocketMsgDTO {
      */
     private List<RoomMsgBean> roomMsgList;
 
-    public KfWebSocketMsgDTO(KfWebSocketMsgEnum webSocketMsgType, Integer errorCode, String errorMsg, Long kfUserId) {
+    public KfWebSocketMsgDTO(KfWebSocketMsgEnum webSocketMsgType, Integer errorCode, String errorMsg) {
         this.webSocketMsgType = webSocketMsgType;
-        this.kfUserId = kfUserId;
+        this.kfUserId = SecurityUtil.getUserId();
         this.result = ResultBean.builder()
                 .errorCode(errorCode)
                 .errorMsg(errorMsg)
                 .build();
     }
 
-    public static KfWebSocketMsgDTO ok(Long kfUserId, KfWebSocketMsgEnum webSocketMsgType) {
-        return new KfWebSocketMsgDTO(webSocketMsgType, 0, "success", kfUserId);
+    public static KfWebSocketMsgDTO ok(KfWebSocketMsgEnum webSocketMsgType) {
+        return new KfWebSocketMsgDTO(webSocketMsgType, 0, "success");
     }
 
     public static KfWebSocketMsgDTO fail(KfWebSocketMsgEnum webSocketMsgType, String errorMsg) {
-        return new KfWebSocketMsgDTO(webSocketMsgType, 400, errorMsg, null);
+        return new KfWebSocketMsgDTO(webSocketMsgType, 400, errorMsg);
     }
 
     @Data
@@ -252,7 +253,7 @@ public class KfWebSocketMsgDTO {
         /**
          * 未读消息总数
          */
-        private Long unReadMsgCount;
+        private Integer unReadMsgCount;
     }
 
     @Data
@@ -284,14 +285,9 @@ public class KfWebSocketMsgDTO {
         private String lastRoleName;
 
         /**
-         * 最后一条消息
+         * 未读消息列表
          */
-        private String lastMsg;
-
-        /**
-         * 最后一条消息时间
-         */
-        private LocalDateTime lastMsgTime;
+        private List<WaitUserMsgBean> waitUserMsgList;
 
         /**
          * 未读消息总数
@@ -304,6 +300,34 @@ public class KfWebSocketMsgDTO {
         private LocalDateTime waitStartTime;
     }
 
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class WaitUserMsgBean {
+
+        /**
+         * 消息id
+         */
+        private String msgId;
+
+        /**
+         * 消息类型
+         */
+        private String msgType;
+
+        /**
+         * 创建时间
+         */
+        private LocalDateTime createTime;
+
+        /**
+         * 消息内容
+         */
+        private String content;
+
+    }
+
     @Data
     @Builder
     @AllArgsConstructor
@@ -360,7 +384,7 @@ public class KfWebSocketMsgDTO {
         /**
          * 未读消息总数
          */
-        private Long unReadMsgCount;
+        private Integer unReadMsgCount;
     }
 
     @Data

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

@@ -38,7 +38,7 @@ public class KfWebSocketMsgParam {
     private String openId;
 
     /**
-     * 房间id, 获取房间历史消息必传
+     * 房间id, 获取房间历史消息, 发消息的时候 -> 必传
      */
     private Long roomId;
 
@@ -63,7 +63,7 @@ public class KfWebSocketMsgParam {
         /**
          * 图片地址(oss下载地址)
          */
-        private String picUrl;
+        private String image;
     }
 
     @Data

+ 7 - 1
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/rpc/impl/KfMsgRpcImpl.java

@@ -2,6 +2,7 @@ package com.zanxiang.game.module.manage.rpc.impl;
 
 import com.zanxiang.game.module.base.rpc.IKfMsgRpc;
 import com.zanxiang.game.module.manage.service.IKfAppletMsgService;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -10,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @time : 2024-02-26
  * @description : 客服消息rpc接口
  */
+@Slf4j
 @DubboService
 public class KfMsgRpcImpl implements IKfMsgRpc {
 
@@ -18,6 +20,10 @@ public class KfMsgRpcImpl implements IKfMsgRpc {
 
     @Override
     public void appletMsg(String postData) {
-        kfAppletMsgService.appletMsg(postData);
+        try {
+            kfAppletMsgService.appletMsg(postData);
+        } catch (Exception e) {
+            log.error("消费SDK转发的小程序消息异常, postData : {}", postData);
+        }
     }
 }

+ 13 - 3
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/api/KfWxApiService.java

@@ -1,10 +1,10 @@
 package com.zanxiang.game.module.manage.service.api;
 
 import com.zanxiang.game.module.base.ServerInfo;
-import com.zanxiang.game.module.base.pojo.enums.HttpStatusEnum;
 import com.zanxiang.game.module.base.rpc.IWxApiServiceRpc;
 import com.zanxiang.game.module.manage.pojo.dto.GameAppletDTO;
 import com.zanxiang.game.module.manage.pojo.dto.KfUploadTempMediaDTO;
+import com.zanxiang.game.module.manage.service.IGameAppletService;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.exception.BaseException;
 import lombok.extern.slf4j.Slf4j;
@@ -19,6 +19,8 @@ import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.util.UriComponentsBuilder;
+import reactor.util.function.Tuple2;
+import reactor.util.function.Tuples;
 
 import java.net.URI;
 import java.util.Map;
@@ -38,10 +40,17 @@ public class KfWxApiService {
     @DubboReference(providedBy = ServerInfo.SERVER_SDK_DUBBO_NAME)
     private IWxApiServiceRpc wxApiServiceRpc;
 
+    @Autowired
+    private IGameAppletService gameAppletService;
+
     /**
      * 通过腾讯API给玩家发送消息
      */
-    public String sendCustomMessageApi(GameAppletDTO gameAppletDTO, Map<String, Object> msgParamMap) {
+    public Tuple2<Long, String> sendCustomMessageApi(Long gameId, Map<String, Object> msgParamMap) {
+        GameAppletDTO gameAppletDTO = gameAppletService.getByGameId(gameId);
+        if (gameAppletDTO == null) {
+            return Tuples.of(400L, "小程序信息不存在");
+        }
         //客服消息参数构造
         log.error("客服消息发送参数, paramMap : {}", JsonUtil.toString(msgParamMap));
         //获取接口token
@@ -52,7 +61,8 @@ public class KfWxApiService {
         // 发送请求
         String result = restTemplate.postForObject(uri, msgParamMap, String.class);
         log.error("客服消息发送结果, result : {}", result);
-        return HttpStatusEnum.SUCCESS.getMsg();
+        Map<String, String> resultMap = JsonUtil.toMap(result, Map.class, String.class);
+        return Tuples.of(Long.valueOf(resultMap.get("errcode")), resultMap.get("errmsg"));
     }
 
     /**

+ 0 - 2
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java

@@ -94,7 +94,6 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
     }
 
     private KfWebSocketMsgDTO transform(KfRoom kfRoom, Long gameId, KfRoomMsg kfRoomMsg, KfWebSocketMsgDTO.MsgContentBean msgContent) {
-
         //消息类型
         KfWebSocketMsgEnum kfWebSocketMsgEnum = kfRoom == null ? KfWebSocketMsgEnum.WEBSOCKET_MSG_WAIT_LIST
                 : KfWebSocketMsgEnum.WEBSOCKET_MSG_ROOM_MSG;
@@ -142,7 +141,6 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
                 .eq(KfSessionUser::getOpenId, kfAppletMsgDTO.getFromUserName()));
     }
 
-
     private KfSessionUser transform(KfAppletMsgDTO kfAppletMsgDTO, GameApplet gameApplet, User user, GameUserRole gameUserRole) {
         return KfSessionUser.builder()
                 .openId(kfAppletMsgDTO.getFromUserName())

+ 17 - 9
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfRoomServiceImpl.java

@@ -13,7 +13,6 @@ import com.zanxiang.game.module.manage.pojo.params.KfWebSocketMsgParam;
 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;
@@ -24,7 +23,6 @@ import reactor.util.function.Tuples;
 import java.time.LocalDateTime;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
@@ -123,7 +121,12 @@ public class KfRoomServiceImpl extends ServiceImpl<KfRoomMapper, KfRoom> impleme
         if (gameApplet == null) {
             return null;
         }
-        return BeanUtil.copy(gameApplet, KfWebSocketMsgDTO.GameBean.class);
+        int unReadMsgCount = kfRoomMsgService.count(new LambdaQueryWrapper<KfRoomMsg>()
+                .eq(KfRoomMsg::getGameId, gameApplet.getGameId())
+                .eq(KfRoomMsg::getReadStatus, Boolean.FALSE));
+        KfWebSocketMsgDTO.GameBean gameBean = BeanUtil.copy(gameApplet, KfWebSocketMsgDTO.GameBean.class);
+        gameBean.setUnReadMsgCount(unReadMsgCount);
+        return gameBean;
     }
 
     @Override
@@ -146,6 +149,11 @@ public class KfRoomServiceImpl extends ServiceImpl<KfRoomMapper, KfRoom> impleme
             roomBean.setLastRoleId(kfSessionUser.getLastRoleId());
             roomBean.setLastRoleName(kfSessionUser.getLastRoleName());
         }
+        //未读消息数量
+        int unReadMsgCount = kfRoomMsgService.count(new LambdaQueryWrapper<KfRoomMsg>()
+                .eq(KfRoomMsg::getRoomId, kfRoom.getId())
+                .eq(KfRoomMsg::getReadStatus, Boolean.FALSE));
+        roomBean.setUnReadMsgCount(unReadMsgCount);
         //最后一条消息
         KfRoomMsg kfRoomMsg = kfRoomMsgService.getOne(new LambdaQueryWrapper<KfRoomMsg>()
                 .eq(KfRoomMsg::getRoomId, kfRoom.getId())
@@ -153,14 +161,14 @@ public class KfRoomServiceImpl extends ServiceImpl<KfRoomMapper, KfRoom> impleme
                 .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("充值消息, 请在聊天中查看");
+                KfWebSocketMsgDTO.MsgContentBean msgContent = BeanUtil.copy(kfRoomMsg.getContent(), KfWebSocketMsgDTO.MsgContentBean.class);
+                roomBean.setLastMsg(msgContent.getText());
             }
             if (Objects.equals(kfRoomMsg.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue())) {
-                roomBean.setLastMsg("图片消息, 请在聊天中查看");
+                roomBean.setLastMsg("[图片]");
+            }
+            if (Objects.equals(kfRoomMsg.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_LINK.getValue())) {
+                roomBean.setLastMsg("[充值]");
             }
             roomBean.setLastMsgTime(kfRoomMsg.getCreateTime());
         }

+ 33 - 20
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfSessionUserServiceImpl.java

@@ -1,6 +1,8 @@
 package com.zanxiang.game.module.manage.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 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;
@@ -9,14 +11,12 @@ import com.zanxiang.game.module.manage.service.IKfSessionUserService;
 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;
 
@@ -45,24 +45,37 @@ public class KfSessionUserServiceImpl extends ServiceImpl<KfSessionUserMapper, K
             return null;
         }
         KfWebSocketMsgDTO.WaitUserBean waitUserBean = BeanUtil.copy(kfSessionUser, KfWebSocketMsgDTO.WaitUserBean.class);
-        //最后一条消息
-        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());
-        }
+        Page<KfRoomMsg> kfRoomMsgPage = kfRoomMsgService.page(new Page<>(1, 10),
+                new QueryWrapper<KfRoomMsg>().lambda()
+                        .eq(KfRoomMsg::getOpenId, kfSessionUser.getOpenId())
+                        .eq(KfRoomMsg::getReadStatus, Boolean.FALSE)
+                        .isNull(KfRoomMsg::getRoomId)
+                        .orderByDesc(KfRoomMsg::getCreateTime));
+        List<KfWebSocketMsgDTO.WaitUserMsgBean> waitUserMsgList = kfRoomMsgPage.getRecords()
+                .stream().map(this::transform).collect(Collectors.toList());
+        waitUserBean.setWaitUserMsgList(waitUserMsgList);
+        waitUserBean.setUnReadMsgCount(kfRoomMsgPage.getTotal());
         return waitUserBean;
     }
+
+    private KfWebSocketMsgDTO.WaitUserMsgBean transform(KfRoomMsg kfRoomMsg) {
+        if (kfRoomMsg == null) {
+            return null;
+        }
+        //等待消息对象
+        KfWebSocketMsgDTO.WaitUserMsgBean waitUserMsgBean = BeanUtil.copy(kfRoomMsg.getContent(), KfWebSocketMsgDTO.WaitUserMsgBean.class);
+        //消息内容解析
+        KfWebSocketMsgDTO.MsgContentBean msgContent = BeanUtil.copy(waitUserMsgBean.getContent(), KfWebSocketMsgDTO.MsgContentBean.class);
+        //消息内容转换
+        if (Objects.equals(waitUserMsgBean.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue())) {
+            waitUserMsgBean.setContent(msgContent.getText());
+        }
+        if (Objects.equals(waitUserMsgBean.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue())) {
+            waitUserMsgBean.setContent("[图片]");
+        }
+        if (Objects.equals(waitUserMsgBean.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_LINK.getValue())) {
+            waitUserMsgBean.setContent("[充值]");
+        }
+        return waitUserMsgBean;
+    }
 }

+ 61 - 33
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebsocketHandler.java

@@ -7,10 +7,8 @@ import com.zanxiang.game.module.manage.constant.RedisKeyConstant;
 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.GameAppletDTO;
 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.IGameAppletService;
 import com.zanxiang.game.module.manage.service.IKfRoomMsgService;
 import com.zanxiang.game.module.manage.service.IKfRoomService;
 import com.zanxiang.game.module.manage.service.IKfSessionUserService;
@@ -20,6 +18,7 @@ import com.zanxiang.game.module.mybatis.entity.KfRoomMsg;
 import com.zanxiang.game.module.mybatis.entity.KfSessionUser;
 import com.zanxiang.module.redis.service.IDistributedLockComponent;
 import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.bean.BeanUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -30,10 +29,7 @@ import reactor.util.function.Tuple2;
 
 import java.io.IOException;
 import java.time.LocalDateTime;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -49,9 +45,6 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
     @Autowired
     private KfMsgWebSocketSessionRegistry kfMsgWebSocketSessionRegistry;
 
-    @Autowired
-    private IGameAppletService gameAppletService;
-
     @Autowired
     private IKfRoomService kfRoomService;
 
@@ -98,42 +91,40 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
         //处理不同类型的消息
         switch (webSocketMsgType) {
             case WEBSOCKET_MSG_CONNECT_HEART_BEAT:
-                log.error("收到客户端心跳消息, param : {}", JsonUtil.toString(param));
+                log.error("收到客户端心跳消息, kfUserId : {}, param : {}", SecurityUtil.getUserId(), JsonUtil.toString(param));
                 break;
             case WEBSOCKET_MSG_KF_HAND_SHAKE:
-                log.error("首次握手, kfUserId : {}", SecurityUtil.getUserId());
+                log.error("首次握手, kfUserId : {}, param : {}", SecurityUtil.getUserId(), JsonUtil.toString(param));
                 kfHandShake(session, webSocketMsgType);
                 break;
             case WEBSOCKET_MSG_KF_CREATE_CONNECT:
-                log.error("创建连接, kfUserId : {}, gameId : {}", SecurityUtil.getUserId(), gameId);
+                log.error("创建连接, kfUserId : {}, param : {}", SecurityUtil.getUserId(), JsonUtil.toString(param));
                 kfCreateConnect(session, webSocketMsgType, gameId);
                 break;
             case WEBSOCKET_MSG_USER_CONNECT_JOIN:
-                log.error("玩家接入, kfUserId : {}, gameId : {}", SecurityUtil.getUserId(), gameId);
+                log.error("玩家接入, kfUserId : {}, param : {}", SecurityUtil.getUserId(), JsonUtil.toString(param));
                 userConnectJoin(session, param);
                 break;
             case WEBSOCKET_MSG_ROOM_HISTORY:
-                log.error("获取房间历史消息, kfUserId : {}, gameId : {}, roomId : {}",
-                        SecurityUtil.getUserId(), gameId, param.getRoomId());
+                log.error("获取房间历史消息, kfUserId : {}, param : {}", SecurityUtil.getUserId(), JsonUtil.toString(param));
                 msgRoomHistory(session, param);
                 break;
             case WEBSOCKET_MSG_KF_SEND:
-                // 客服发送消息,可以添加对应的处理逻辑
+                log.error("客服发送消息, kfUserId : {}, param : {}", SecurityUtil.getUserId(), JsonUtil.toString(param));
+                fkSendMsg(session, param);
                 break;
             case WEBSOCKET_MSG_FINISH_ROOM_LIST:
-                log.error("获取已结束房间列表, kfUserId : {}, gameId : {}", SecurityUtil.getUserId(), gameId);
+                log.error("获取已结束房间列表, kfUserId : {}, param : {}", SecurityUtil.getUserId(), JsonUtil.toString(param));
                 finishRoomList(session, param);
                 break;
             case WEBSOCKET_MSG_FINISH_SESSION:
-                log.error("结束会话, kfUserId : {}, gameId : {}, roomId : {}",
-                        SecurityUtil.getUserId(), gameId, param.getRoomId());
+                log.error("结束会话, kfUserId : {}, param : {}", SecurityUtil.getUserId(), JsonUtil.toString(param));
                 kfFinishSession(session, webSocketMsgType, gameId, param.getRoomId());
                 break;
             case WEBSOCKET_MSG_QUICK_REPLY:
                 // 快捷回复,可以添加对应的处理逻辑
                 break;
             default:
-                // 未知消息类型,可以添加对应的处理逻辑
                 this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "参数错误, 未知的消息类型"));
         }
     }
@@ -169,15 +160,21 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
     }
 
     private void fkSendMsg(WebSocketSession session, KfWebSocketMsgParam param) {
-        //查询小程序信息
-        GameAppletDTO gameAppletDTO = gameAppletService.getByGameId(param.getGameId());
-        if (gameAppletDTO == null) {
-            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
-                    "参数错误, 小程序信息不存在, param : " + JsonUtil.toString(param)));
+        if (param.getRoomId() == null) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "参数错误,roomId为空"));
+            return;
+        }
+        KfRoom kfRoom = kfRoomService.getById(param.getRoomId());
+        if (kfRoom == null) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "参数错误,房间信息不存在"));
             return;
         }
-        //发送消息
         KfWebSocketMsgParam.MsgContentBean msgContent = param.getMsgContent();
+        if (msgContent == null) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "参数错误,消息内容不可为空"));
+            return;
+        }
+        //发送消息
         Map<String, Object> msgParamMap = new HashMap<>(3);
         msgParamMap.put("touser", param.getOpenId());
         //文本
@@ -189,18 +186,45 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
         }
         //图片
         if (Objects.equals(msgContent.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE)) {
-//            Map<String, Object> imageMap = new HashMap<>(1);
-//            imageMap.put("media_id", msgContent.getText());
-//            msgParamMap.put("msgtype", KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue());
-//            msgParamMap.put(KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue(), imageMap);
+            Map<String, Object> imageMap = new HashMap<>(1);
+            //图片需要上传到腾讯, 转成 media_id
+            imageMap.put("media_id", "media_id");
 
+            msgParamMap.put("msgtype", KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue());
+            msgParamMap.put(KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue(), imageMap);
             this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
-                    "发图片的逻辑还没写完, 先发文本, param : " + JsonUtil.toString(param)));
+                    "发图片上传素材的逻辑还没写完, 先发文本, param : " + JsonUtil.toString(param)));
+            return;
+        }
+        //调腾讯接口发送消息
+        Tuple2<Long, String> tuple2 = wxApiService.sendCustomMessageApi(param.getGameId(), msgParamMap);
+        Long result = tuple2.getT1();
+        //发送失败, 通知返回通知客户端
+        if (result != 0) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), tuple2.getT2()));
             return;
         }
-        wxApiService.sendCustomMessageApi(gameAppletDTO, msgParamMap);
-        //发送失败, 通知返回
         //发送成功, 消息入库
+        kfRoomMsgService.save(this.transform(param.getGameId(), kfRoom, msgContent));
+        this.sendMessage(session, KfWebSocketMsgDTO.ok(param.getWebSocketMsgType()));
+    }
+
+    private KfRoomMsg transform(Long gameId, KfRoom kfRoom, KfWebSocketMsgParam.MsgContentBean msgContent) {
+        return KfRoomMsg.builder()
+                .msgId(UUID.randomUUID().toString().replace("-", ""))
+                .msgType(msgContent.getMsgType().getValue())
+                .gameId(gameId)
+                .openId(kfRoom.getOpenId())
+                .userId(kfRoom.getUserId())
+                .kfUserId(kfRoom.getKfUserId())
+                .readStatus(Boolean.TRUE)
+                .roomId(kfRoom.getId())
+                .msgOwner(KfRoomMsgOwnerEnum.KF_MSG_OWNER_KF.getValue())
+                .content(JsonUtil.toString(BeanUtil.copy(msgContent, KfWebSocketMsgDTO.MsgContentBean.class)))
+                .source(JsonUtil.toString(msgContent))
+                .createTime(LocalDateTime.now())
+                .updateTime(LocalDateTime.now())
+                .build();
     }
 
     private void kfFinishSession(WebSocketSession session, KfWebSocketMsgEnum webSocketMsgType, Long gameId, Long roomId) {
@@ -274,11 +298,15 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
                 .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
                 .eq(KfSessionUser::getOpenId, param.getOpenId())
         );
+        //玩家信息
+        KfSessionUser kfSessionUser = kfSessionUserService.getById(param.getOpenId());
         //房间更新
         Long roomId = kfRoomService.userJoinRoom(param.getOpenId(), param.getGameId());
         //玩家未读消息更新到房间
         kfRoomMsgService.update(new LambdaUpdateWrapper<KfRoomMsg>()
                 .set(KfRoomMsg::getRoomId, roomId)
+                .set(KfRoomMsg::getUserId, kfSessionUser == null ? null : kfSessionUser.getUserId())
+                .set(KfRoomMsg::getKfUserId, SecurityUtil.getUserId())
                 .set(KfRoomMsg::getUpdateTime, LocalDateTime.now())
                 .eq(KfRoomMsg::getOpenId, param.getOpenId())
                 .eq(KfRoomMsg::getGameId, param.getGameId())