Sfoglia il codice sorgente

fix :
1.修改sdk线上并发导致的问题
2.sdk添加玩家行为数据埋点, 数据发送到卡夫卡

bilingfeng 1 anno fa
parent
commit
ddd995d9ae
27 ha cambiato i file con 401 aggiunte e 285 eliminazioni
  1. 5 5
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/controller/KfMsgController.java
  2. 25 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/dto/KfWebSocketMsgDTO.java
  3. 1 3
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfMsgService.java
  4. 3 2
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfRoomService.java
  5. 9 6
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java
  6. 2 5
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfMsgServiceImpl.java
  7. 33 11
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfRoomMsgServiceImpl.java
  8. 12 9
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfRoomServiceImpl.java
  9. 11 1
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgRedisListener.java
  10. 22 11
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebsocketHandler.java
  11. 0 42
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfLink.java
  12. 0 83
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfMsgContent.java
  13. 0 1
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfRoomMsg.java
  14. 0 12
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/KfLinkMapper.java
  15. 0 12
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/KfMsgContentMapper.java
  16. 1 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java
  17. 5 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/constant/RedisKeyConstant.java
  18. 49 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/enums/KafkaEventTrackEnum.java
  19. 2 2
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/enums/TokenCheckEnum.java
  20. 8 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/listener/OrderPaySuccessListener.java
  21. 6 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/dto/PlatformOrderDTO.java
  22. 29 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IKafkaService.java
  23. 76 73
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java
  24. 65 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/KafkaServiceImpl.java
  25. 10 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java
  26. 21 5
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/OrderPayServiceImpl.java
  27. 6 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/OrderServiceImpl.java

+ 5 - 5
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/controller/KfMsgController.java

@@ -4,7 +4,7 @@ import com.zanxiang.erp.security.annotation.PreAuthorize;
 import com.zanxiang.game.module.manage.pojo.params.KfApiParam;
 import com.zanxiang.game.module.manage.pojo.params.KfUserUpdateParam;
 import com.zanxiang.game.module.manage.pojo.vo.KfGameVO;
-import com.zanxiang.game.module.manage.service.IKfMsgContentService;
+import com.zanxiang.game.module.manage.service.IKfMsgService;
 import com.zanxiang.game.module.manage.service.IKfUserService;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.pojo.ResultVO;
@@ -33,7 +33,7 @@ import java.util.List;
 public class KfMsgController {
 
     @Autowired
-    private IKfMsgContentService kfMsgContentService;
+    private IKfMsgService kfMsgService;
 
     @Autowired
     private IKfUserService kfUserService;
@@ -43,7 +43,7 @@ public class KfMsgController {
     @PreAuthorize(permissionKey = "manage:kf:uploadMsg")
     @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Tuple2.class)})
     public ResultVO<Tuple2<String, String>> uploadMsg(String param, MultipartFile files) {
-        return ResultVO.ok(kfMsgContentService.uploadMsg(JsonUtil.toObj(param, KfApiParam.class), files));
+        return ResultVO.ok(kfMsgService.uploadMsg(JsonUtil.toObj(param, KfApiParam.class), files));
     }
 
     @ApiOperation(value = "小游戏列表查询")
@@ -51,7 +51,7 @@ public class KfMsgController {
     @PreAuthorize(permissionKey = "manage:kf:gameList")
     @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = KfGameVO.class)})
     public ResultVO<List<KfGameVO>> list() {
-        return ResultVO.ok(kfMsgContentService.getKfGameList());
+        return ResultVO.ok(kfMsgService.getKfGameList());
     }
 
     @ApiOperation(value = "客服接口通用api")
@@ -59,7 +59,7 @@ public class KfMsgController {
     @PreAuthorize(permissionKey = "manage:kf:commApi")
     @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = String.class)})
     public ResultVO<String> list(@Validated @RequestBody KfApiParam param) {
-        return ResultVO.ok(kfMsgContentService.kfApi(param));
+        return ResultVO.ok(kfMsgService.kfApi(param));
     }
 
     @ApiOperation(value = "获取小游戏授权码")

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

@@ -198,6 +198,11 @@ public class KfWebSocketMsgDTO {
          * 最后充值时间
          */
         private LocalDateTime lastPayTime;
+
+        /**
+         * 是否添加企微
+         */
+        private Boolean addCorpWechat;
     }
 
     @Data
@@ -370,6 +375,11 @@ public class KfWebSocketMsgDTO {
          * 开始等待时间
          */
         private LocalDateTime waitStartTime;
+
+        /**
+         * 是否添加企微
+         */
+        private Boolean addCorpWechat;
     }
 
     @Data
@@ -462,6 +472,11 @@ public class KfWebSocketMsgDTO {
          * 未读消息总数
          */
         private Integer unReadMsgCount;
+
+        /**
+         * 是否添加企微
+         */
+        private Boolean addCorpWechat;
     }
 
     @Data
@@ -497,6 +512,16 @@ public class KfWebSocketMsgDTO {
          */
         private Boolean readStatus;
 
+        /**
+         * 客服id, 当消息归属于客服得时候有值
+         */
+        private Long kfUserId;
+
+        /**
+         * 客服名称, 当消息归属于客服得时候有值
+         */
+        private String kfUserName;
+
         /**
          * 消息归属
          */

+ 1 - 3
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfMsgContentService.java → game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfMsgService.java

@@ -1,9 +1,7 @@
 package com.zanxiang.game.module.manage.service;
 
-import com.baomidou.mybatisplus.extension.service.IService;
 import com.zanxiang.game.module.manage.pojo.params.KfApiParam;
 import com.zanxiang.game.module.manage.pojo.vo.KfGameVO;
-import com.zanxiang.game.module.mybatis.entity.KfMsgContent;
 import org.springframework.web.multipart.MultipartFile;
 import reactor.util.function.Tuple2;
 
@@ -14,7 +12,7 @@ import java.util.List;
  * @time : 2023-11-28
  * @description :
  */
-public interface IKfMsgContentService extends IService<KfMsgContent> {
+public interface IKfMsgService {
 
     /**
      * 消息上传

+ 3 - 2
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfRoomService.java

@@ -43,10 +43,11 @@ public interface IKfRoomService extends IService<KfRoom> {
     /**
      * 根据游戏获取游戏信息
      *
-     * @param gameId : 游戏id
+     * @param gameId   : 游戏id
+     * @param kfUserId : 客服号id
      * @return : 返回游戏信息
      */
-    List<KfWebSocketMsgDTO.GameBean> getKfGameByGameId(Long gameId);
+    List<KfWebSocketMsgDTO.GameBean> getKfGameByGameId(Long gameId, Long kfUserId);
 
     /**
      * 待接入房间列表

+ 9 - 6
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java

@@ -196,8 +196,6 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
                 .gameId(gameId)
                 .roomId(kfRoom == null ? null : kfRoom.getId())
                 .build();
-        //游戏列表
-        kfWebSocketMsgDTO.setGameList(kfRoomService.getKfGameByGameId(gameId));
         //待接入消息
         if (Objects.equals(kfWebSocketMsgEnum, KfWebSocketMsgEnum.WEBSOCKET_MSG_WAIT_LIST)) {
             List<KfWebSocketMsgDTO.WaitUserBean> waitUserList = kfSessionUserService.getWaitUserList(gameId);
@@ -209,8 +207,11 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
             KfWebSocketMsgDTO.RoomMsgBean roomMsgBean = BeanUtil.copy(kfRoomMsg, KfWebSocketMsgDTO.RoomMsgBean.class);
             roomMsgBean.setContent(msgContent);
             kfWebSocketMsgDTO.setRoomMsgList(Collections.singletonList(roomMsgBean));
-            //房间, 空指针警告只是逻辑警告, kfRoom为空不会走到这里, 走上面的 if 条件
-            kfWebSocketMsgDTO.setRoomList(kfRoomService.getRoomByRoomId(kfRoom.getId()));
+            //设置房间列表和游戏列表
+            if (kfRoom != null) {
+                kfWebSocketMsgDTO.setGameList(kfRoomService.getKfGameByGameId(gameId, kfRoom.getKfUserId()));
+                kfWebSocketMsgDTO.setRoomList(kfRoomService.getRoomByRoomId(kfRoom.getId()));
+            }
         }
         return kfWebSocketMsgDTO;
     }
@@ -246,8 +247,8 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
                 .gameId(gameApplet.getGameId())
                 .userId(user == null ? null : user.getId())
                 .isWait(Boolean.FALSE)
-                .lastRoleId(gameUserRole == null ? "0" : gameUserRole.getRoleId())
-                .lastRoleName(gameUserRole == null ? kfAppletMsgDTO.getFromUserName() : gameUserRole.getRoleName())
+                .lastRoleId(gameUserRole == null ? null : gameUserRole.getRoleId())
+                .lastRoleName(gameUserRole == null ? "神秘人[未创角]" : gameUserRole.getRoleName())
                 .serverId(gameUserRole == null ? null : gameUserRole.getServerId())
                 .serverName(gameUserRole == null ? null : gameUserRole.getServerName())
                 .createTime(LocalDateTime.now())
@@ -262,6 +263,7 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
                 .msgType(kfAppletMsgDTO.getMsgType())
                 .gameId(gameApplet.getGameId())
                 .openId(kfAppletMsgDTO.getFromUserName())
+                .userId(kfRoom == null ? null : kfRoom.getUserId())
                 .readStatus(Boolean.FALSE)
                 .roomId(kfRoom == null ? null : kfRoom.getId())
                 .kfUserId(kfRoom == null ? null : kfRoom.getKfUserId())
@@ -343,6 +345,7 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
                 .msgType(KfRoomMsgTypeEnum.KF_MSG_TYPE_LINK.getValue())
                 .gameId(gameId)
                 .openId(openId)
+                .userId(kfRoom == null ? null : kfRoom.getUserId())
                 .readStatus(kfRoom != null)
                 .roomId(kfRoom == null ? null : kfRoom.getId())
                 .kfUserId(kfRoom == null ? null : kfRoom.getKfUserId())

+ 2 - 5
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfMsgContentServiceImpl.java → game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfMsgServiceImpl.java

@@ -2,7 +2,6 @@ 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.erp.security.util.SecurityUtil;
 import com.zanxiang.game.module.manage.enums.KfActionEnum;
 import com.zanxiang.game.module.manage.enums.KfApiEnum;
@@ -11,13 +10,11 @@ import com.zanxiang.game.module.manage.pojo.params.KfApiParam;
 import com.zanxiang.game.module.manage.pojo.vo.KfGameVO;
 import com.zanxiang.game.module.manage.service.IGameAppletService;
 import com.zanxiang.game.module.manage.service.IGameAuthService;
-import com.zanxiang.game.module.manage.service.IKfMsgContentService;
+import com.zanxiang.game.module.manage.service.IKfMsgService;
 import com.zanxiang.game.module.manage.service.IKfUserService;
 import com.zanxiang.game.module.mybatis.entity.GameApplet;
 import com.zanxiang.game.module.mybatis.entity.GameAuth;
-import com.zanxiang.game.module.mybatis.entity.KfMsgContent;
 import com.zanxiang.game.module.mybatis.entity.KfUser;
-import com.zanxiang.game.module.mybatis.mapper.KfMsgContentMapper;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.URIUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
@@ -48,7 +45,7 @@ import java.util.stream.Collectors;
  */
 @Slf4j
 @Service
-public class KfMsgContentServiceImpl extends ServiceImpl<KfMsgContentMapper, KfMsgContent> implements IKfMsgContentService {
+public class KfMsgServiceImpl implements IKfMsgService {
 
     @Autowired
     private RestTemplate restTemplate;

+ 33 - 11
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfRoomMsgServiceImpl.java

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.erp.base.ErpServer;
+import com.zanxiang.erp.base.rpc.ISysUserRpc;
 import com.zanxiang.game.module.manage.enums.KfRoomMsgOwnerEnum;
 import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
 import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
@@ -16,14 +18,13 @@ import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.springframework.stereotype.Service;
 import reactor.util.function.Tuple2;
 import reactor.util.function.Tuples;
 
 import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Objects;
-import java.util.UUID;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -35,6 +36,9 @@ import java.util.stream.Collectors;
 @Service
 public class KfRoomMsgServiceImpl extends ServiceImpl<KfRoomMsgMapper, KfRoomMsg> implements IKfRoomMsgService {
 
+    @DubboReference(providedBy = ErpServer.SERVER_DUBBO_NAME)
+    private ISysUserRpc sysUserRpc;
+
     @Override
     public Tuple2<KfWebSocketMsgDTO.PageBean, List<KfWebSocketMsgDTO.RoomMsgBean>> msgRoomHistory(Long roomId, KfWebSocketMsgParam.PageBean pageBean) {
         Page<KfRoomMsg> kfRoomMsgPage = super.page(new Page<>(pageBean.getPageNum(), pageBean.getPageSize()),
@@ -42,15 +46,18 @@ public class KfRoomMsgServiceImpl extends ServiceImpl<KfRoomMsgMapper, KfRoomMsg
                         .eq(KfRoomMsg::getRoomId, roomId)
                         .orderByDesc(KfRoomMsg::getCreateTime)
         );
+        //客服名字列表
+        Map<Long, String> kfUserNameMap = this.getKfUserNameMap(kfRoomMsgPage.getRecords());
         //构造消息列表
         List<KfWebSocketMsgDTO.RoomMsgBean> roomMsgBeanList = kfRoomMsgPage.getRecords().stream()
-                .map(this::transform).collect(Collectors.toList());
-        //更新消息已读状态
-        List<String> msgIdList = roomMsgBeanList.stream()
-                .filter(msg -> Objects.equals(msg.getReadStatus(), Boolean.FALSE))
-                .map(KfWebSocketMsgDTO.RoomMsgBean::getMsgId)
-                .collect(Collectors.toList());
-        if (CollectionUtils.isNotEmpty(msgIdList)) {
+                .map(roomMsgBean -> this.transform(roomMsgBean, kfUserNameMap)).collect(Collectors.toList());
+        //消息列表不为空
+        if (CollectionUtils.isNotEmpty(roomMsgBeanList)) {
+            //更新消息已读状态
+            List<String> msgIdList = roomMsgBeanList.stream()
+                    .filter(msg -> Objects.equals(msg.getReadStatus(), Boolean.FALSE))
+                    .map(KfWebSocketMsgDTO.RoomMsgBean::getMsgId)
+                    .collect(Collectors.toList());
             super.update(new LambdaUpdateWrapper<KfRoomMsg>()
                     .set(KfRoomMsg::getReadStatus, Boolean.TRUE)
                     .set(KfRoomMsg::getUpdateTime, LocalDateTime.now())
@@ -60,13 +67,16 @@ public class KfRoomMsgServiceImpl extends ServiceImpl<KfRoomMsgMapper, KfRoomMsg
         return Tuples.of(KfWebSocketMsgDTO.transformPage(kfRoomMsgPage), roomMsgBeanList);
     }
 
-    private KfWebSocketMsgDTO.RoomMsgBean transform(KfRoomMsg kfRoomMsg) {
+    private KfWebSocketMsgDTO.RoomMsgBean transform(KfRoomMsg kfRoomMsg, Map<Long, String> kfUserNameMap) {
         if (kfRoomMsg == null) {
             return null;
         }
         KfWebSocketMsgDTO.RoomMsgBean roomMsgBean = BeanUtil.copy(kfRoomMsg, KfWebSocketMsgDTO.RoomMsgBean.class);
         roomMsgBean.setMsgType(KfRoomMsgTypeEnum.getMsgTypeEnum(kfRoomMsg.getMsgType()));
         roomMsgBean.setContent(JsonUtil.toObj(kfRoomMsg.getContent(), KfWebSocketMsgDTO.MsgContentBean.class));
+        //客服消息, 设置客服名字
+        String kfUserName = kfUserNameMap.get(roomMsgBean.getKfUserId());
+        roomMsgBean.setKfUserName(kfUserName == null ? "查询erp客服名字异常" : kfUserName);
         return roomMsgBean;
     }
 
@@ -88,4 +98,16 @@ public class KfRoomMsgServiceImpl extends ServiceImpl<KfRoomMsgMapper, KfRoomMsg
                 .updateTime(LocalDateTime.now())
                 .build());
     }
+
+    private Map<Long, String> getKfUserNameMap(List<KfRoomMsg> roomMsgList) {
+        List<Long> kfUserIdList = roomMsgList.stream().map(KfRoomMsg::getKfUserId).collect(Collectors.toList());
+        Map<Long, String> kfUserNameMap;
+        try {
+            kfUserNameMap = sysUserRpc.getUserNameByIds(kfUserIdList).getData();
+        } catch (Exception e) {
+            log.error("获取erp客服名字异常, kfUserIdList : {}, e : {}", JsonUtil.toString(kfUserIdList), e.getMessage());
+            kfUserNameMap = Collections.emptyMap();
+        }
+        return kfUserNameMap;
+    }
 }

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

@@ -73,15 +73,14 @@ public class KfRoomServiceImpl extends ServiceImpl<KfRoomMapper, KfRoom> impleme
 
     @Override
     public Long userJoinRoom(String openId, Long gameId) {
-        KfRoom kfRoom = super.getOne(new LambdaQueryWrapper<KfRoom>()
-                .eq(KfRoom::getOpenId, openId)
-                .eq(KfRoom::getKfUserId, SecurityUtil.getUserId()));
+        KfRoom kfRoom = super.getOne(new LambdaQueryWrapper<KfRoom>().eq(KfRoom::getOpenId, openId));
         if (kfRoom == null) {
             kfRoom = this.transform(openId, gameId);
             super.save(kfRoom);
             return kfRoom.getId();
         }
         super.update(new LambdaUpdateWrapper<KfRoom>()
+                .set(KfRoom::getKfUserId, SecurityUtil.getUserId())
                 .set(KfRoom::getOnline, Boolean.TRUE)
                 .set(KfRoom::getUpdateTime, LocalDateTime.now())
                 .eq(KfRoom::getId, kfRoom.getId())
@@ -106,8 +105,9 @@ public class KfRoomServiceImpl extends ServiceImpl<KfRoomMapper, KfRoom> impleme
 
     @Override
     public List<KfWebSocketMsgDTO.GameBean> getKfGameList() {
+        Long kfUserId = SecurityUtil.getUserId();
         List<GameAuth> gameAuthList = gameAuthService.list(new LambdaQueryWrapper<GameAuth>()
-                .eq(!SecurityUtil.isAdmin(), GameAuth::getUserId, SecurityUtil.getUserId()));
+                .eq(!SecurityUtil.isAdmin(), GameAuth::getUserId, kfUserId));
         if (!SecurityUtil.isAdmin() && CollectionUtils.isEmpty(gameAuthList)) {
             return Collections.emptyList();
         }
@@ -115,26 +115,29 @@ public class KfRoomServiceImpl extends ServiceImpl<KfRoomMapper, KfRoom> impleme
                 .in(!SecurityUtil.isAdmin(), GameApplet::getGameId,
                         gameAuthList.stream().map(GameAuth::getGameId).collect(Collectors.toSet()))
                 .eq(GameApplet::getType, 1)
-        ).stream().map(this::transform).collect(Collectors.toList());
+        ).stream().map(gameApplet -> this.transform(gameApplet, kfUserId)).collect(Collectors.toList());
     }
 
     @Override
-    public List<KfWebSocketMsgDTO.GameBean> getKfGameByGameId(Long gameId) {
+    public List<KfWebSocketMsgDTO.GameBean> getKfGameByGameId(Long gameId, Long kfUserId) {
         GameApplet gameApplet = gameAppletService.getOne(new LambdaQueryWrapper<GameApplet>()
                 .eq(GameApplet::getGameId, gameId));
         if (gameApplet == null) {
             return Collections.emptyList();
         }
-        return Collections.singletonList(this.transform(gameApplet));
+        return Collections.singletonList(this.transform(gameApplet, kfUserId));
     }
 
-    private KfWebSocketMsgDTO.GameBean transform(GameApplet gameApplet) {
+    private KfWebSocketMsgDTO.GameBean transform(GameApplet gameApplet, Long kfUserId) {
         if (gameApplet == null) {
             return null;
         }
         int unReadMsgCount = kfRoomMsgService.count(new LambdaQueryWrapper<KfRoomMsg>()
                 .eq(KfRoomMsg::getGameId, gameApplet.getGameId())
                 .eq(KfRoomMsg::getReadStatus, Boolean.FALSE)
+                .and(qw -> qw.eq(KfRoomMsg::getKfUserId, kfUserId)
+                        .or().isNull(KfRoomMsg::getRoomId)
+                )
                 .ne(KfRoomMsg::getMsgOwner, KfRoomMsgOwnerEnum.KF_MSG_OWNER_SYSTEM.getValue()));
         KfWebSocketMsgDTO.GameBean gameBean = BeanUtil.copy(gameApplet, KfWebSocketMsgDTO.GameBean.class);
         gameBean.setUnReadMsgCount(unReadMsgCount);
@@ -175,7 +178,7 @@ public class KfRoomServiceImpl extends ServiceImpl<KfRoomMapper, KfRoom> impleme
         int unReadMsgCount = kfRoomMsgService.count(new LambdaQueryWrapper<KfRoomMsg>()
                 .eq(KfRoomMsg::getRoomId, kfRoom.getId())
                 .eq(KfRoomMsg::getReadStatus, Boolean.FALSE)
-                .ne(KfRoomMsg::getMsgOwner, KfRoomMsgOwnerEnum.KF_MSG_OWNER_SYSTEM.getValue()));
+                .eq(KfRoomMsg::getMsgOwner, KfRoomMsgOwnerEnum.KF_MSG_OWNER_USER.getValue()));
         roomBean.setUnReadMsgCount(unReadMsgCount);
         //最后一条消息
         KfRoomMsg kfRoomMsg = kfRoomMsgService.getOne(new LambdaQueryWrapper<KfRoomMsg>()

+ 11 - 1
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgRedisListener.java

@@ -1,6 +1,8 @@
 package com.zanxiang.game.module.manage.websocket;
 
+import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
 import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
+import com.zanxiang.game.module.manage.service.IKfRoomService;
 import com.zanxiang.module.util.JsonUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -10,8 +12,8 @@ import org.springframework.stereotype.Component;
 import org.springframework.web.socket.TextMessage;
 import org.springframework.web.socket.WebSocketSession;
 
-import java.io.IOException;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * @author : lingfeng
@@ -25,6 +27,9 @@ public class KfMsgRedisListener implements MessageListener {
     @Autowired
     private KfMsgWebSocketSessionRegistry kfMsgWebSocketSessionRegistry;
 
+    @Autowired
+    private IKfRoomService kfRoomService;
+
     @Override
     public void onMessage(Message message, byte[] pattern) {
         //从redis中拿到的消息
@@ -53,6 +58,11 @@ public class KfMsgRedisListener implements MessageListener {
         openSessions.forEach(session -> {
             if (session != null && session.isOpen()) {
                 try {
+                    //待接入的游戏消息, 设置个人游戏列表
+                    if (Objects.equals(kfWebSocketMsgDTO.getWebSocketMsgType(), KfWebSocketMsgEnum.WEBSOCKET_MSG_WAIT_LIST)) {
+                        Long finalKfUserId = Long.valueOf(session.getAttributes().get("kfUserId").toString());
+                        kfWebSocketMsgDTO.setGameList(kfRoomService.getKfGameByGameId(kfWebSocketMsgDTO.getGameId(), finalKfUserId));
+                    }
                     session.sendMessage(new TextMessage(JsonUtil.toString(kfWebSocketMsgDTO)));
                 } catch (Exception e) {
                     log.error("发送消息给所有客服异常, kfUserId : {}, kfWebSocketMsgDTO : {}, e : {}",

+ 22 - 11
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebsocketHandler.java

@@ -300,11 +300,21 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
     }
 
     private void kfFinishSession(WebSocketSession session, KfWebSocketMsgEnum webSocketMsgType, Long gameId, Long roomId) {
+        //查询房间信息
+        KfRoom kfRoom = kfRoomService.getById(roomId);
+        if (kfRoom == null) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(webSocketMsgType, "参数错误,房间信息不存在"));
+            return;
+        }
         //房间在线状态更新
         kfRoomService.update(new LambdaUpdateWrapper<KfRoom>()
                 .set(KfRoom::getOnline, Boolean.FALSE)
                 .set(KfRoom::getUpdateTime, LocalDateTime.now())
                 .eq(KfRoom::getId, roomId));
+        //玩家信息更新
+        kfSessionUserService.update(new LambdaUpdateWrapper<KfSessionUser>()
+                .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
+                .eq(KfSessionUser::getOpenId, kfRoom.getOpenId()));
         //推送完整的已链接房间列表
         List<KfWebSocketMsgDTO.RoomBean> onlineRoomList = kfRoomService.getOnlineRoomList(gameId);
         this.sendMessage(session, KfWebSocketMsgDTO.builder()
@@ -339,7 +349,7 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
         //房间信息设置
         List<KfWebSocketMsgDTO.RoomBean> roomList = kfRoomService.getRoomByRoomId(param.getRoomId());
         //游戏列表
-        List<KfWebSocketMsgDTO.GameBean> gameList = kfRoomService.getKfGameByGameId(param.getGameId());
+        List<KfWebSocketMsgDTO.GameBean> gameList = kfRoomService.getKfGameByGameId(param.getGameId(), SecurityUtil.getUserId());
         //发送消息
         this.sendMessage(session, KfWebSocketMsgDTO.builder()
                 .webSocketMsgType(param.getWebSocketMsgType())
@@ -361,25 +371,26 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
         }
         //触发玩家接入线程锁
         if (!distributedLockComponent.doLock(RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + param.getOpenId(),
-                0L, 1L, TimeUnit.MINUTES)) {
-            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
-                    "玩家已被其他客服接入, param : " + JsonUtil.toString(param)));
+                0L, 5L, TimeUnit.MINUTES)) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "玩家已被其他客服接入"));
             return;
         }
-        //玩家更新
+        //玩家信息, 判断玩家是否已经被接入
+        KfSessionUser kfSessionUser = kfSessionUserService.getById(param.getOpenId());
+        if (!kfSessionUser.getIsWait()) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "玩家已被其他客服接入"));
+            return;
+        }
+        //玩家更新为已接入
         kfSessionUserService.update(new LambdaUpdateWrapper<KfSessionUser>()
                 .set(KfSessionUser::getIsWait, Boolean.FALSE)
                 .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
-                .eq(KfSessionUser::getOpenId, param.getOpenId())
-        );
-        //玩家信息
-        KfSessionUser kfSessionUser = kfSessionUserService.getById(param.getOpenId());
+                .eq(KfSessionUser::getOpenId, 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())

+ 0 - 42
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfLink.java

@@ -1,42 +0,0 @@
-package com.zanxiang.game.module.mybatis.entity;
-
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.*;
-
-import java.time.LocalDateTime;
-
-/**
- * @author : lingfeng
- * @time : 2023-12-06
- * @description : 客服链接
- */
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-@ToString
-@Builder
-@TableName("t_kf_link")
-public class KfLink {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 应用id
-     */
-    private String appId;
-
-    /**
-     * 第三方客服链接
-     */
-    private String customLink;
-
-    /**
-     * 创建时间
-     */
-    private LocalDateTime createTime;
-
-    /**
-     * 更新时间
-     */
-    private LocalDateTime updateTime;
-}

+ 0 - 83
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfMsgContent.java

@@ -1,83 +0,0 @@
-package com.zanxiang.game.module.mybatis.entity;
-
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.*;
-
-import java.io.Serializable;
-import java.time.LocalDateTime;
-
-/**
- * @author : lingfeng
- * @time : 2023-11-27
- * @description : 客服消息记录
- */
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-@ToString
-@Builder
-@TableName("t_kf_msg_content")
-public class KfMsgContent implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 消息id
-     */
-    private String msgId;
-
-    /**
-     * 消息类型
-     */
-    private Integer msgType;
-
-    /**
-     * 房间id
-     */
-    private String roomId;
-
-    /**
-     * 消息内容
-     */
-    private String msgKfContent;
-
-    /**
-     * 拓展信息
-     */
-    private String extraInfo;
-
-    /**
-     * 发送账号(客服消息才有值)
-     */
-    private String sendOpenAccount;
-
-    /**
-     * 发送者openid
-     */
-    private String sendOpenid;
-
-    /**
-     * 发送场景
-     */
-    private Integer sendScene;
-
-    /**
-     * session
-     */
-    private String sessionId;
-
-    /**
-     * 是否删除
-     */
-    private Integer isDelete;
-
-    /**
-     * 创建时间
-     */
-    private LocalDateTime createTime;
-
-    /**
-     * 更新时间
-     */
-    private LocalDateTime updateTime;
-}

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

@@ -48,7 +48,6 @@ public class KfRoomMsg {
      */
     private Long userId;
 
-
     /**
      * 客服id
      */

+ 0 - 12
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/KfLinkMapper.java

@@ -1,12 +0,0 @@
-package com.zanxiang.game.module.mybatis.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.zanxiang.game.module.mybatis.entity.KfLink;
-
-/**
- * @author : lingfeng
- * @time : 2023-12-06
- * @description :  ${description}
- */
-public interface KfLinkMapper extends BaseMapper<KfLink> {
-}

+ 0 - 12
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/KfMsgContentMapper.java

@@ -1,12 +0,0 @@
-package com.zanxiang.game.module.mybatis.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.zanxiang.game.module.mybatis.entity.KfMsgContent;
-
-/**
- * @author : lingfeng
- * @time : 2023-11-27
- * @description :  ${description}
- */
-public interface KfMsgContentMapper extends BaseMapper<KfMsgContent> {
-}

+ 1 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java

@@ -23,7 +23,7 @@ public class SDKApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(SDKApplication.class, args);
-        System.out.println("赞象SDK服务启动成功 <关闭订单查询配置优化> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <一大波修改> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 5 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/constant/RedisKeyConstant.java

@@ -62,4 +62,9 @@ public class RedisKeyConstant {
      */
     public static final String GAME_CUSTOM_PAY_SIGN = "game_sdk_manage_custom_pay_sign_";
 
+    /**
+     * 获取支付参数锁
+     */
+    public static final String GET_PAY_PARAM_LOCK = RedisKeyConstant.REDIS_PREFIX + "get_pay_param_lock_";
+
 }

+ 49 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/enums/KafkaEventTrackEnum.java

@@ -0,0 +1,49 @@
+package com.zanxiang.game.module.sdk.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-03-07
+ * @description : 卡夫卡数据埋点类型
+ */
+@Getter
+@AllArgsConstructor
+public enum KafkaEventTrackEnum {
+
+    /**
+     * 注册
+     */
+    KAFKA_EVENT_TRACK_REG("KAFKA_EVENT_TRACK_REG"),
+
+    /**
+     * 登录
+     */
+    KAFKA_EVENT_TRACK_LOGIN("KAFKA_EVENT_TRACK_LOGIN"),
+
+    /**
+     * 下单
+     */
+    KAFKA_EVENT_TRACK_ORDER_CREATE("KAFKA_EVENT_TRACK_ORDER_CREATE"),
+
+    /**
+     * 支付
+     */
+    KAFKA_EVENT_TRACK_ORDER_PAY("KAFKA_EVENT_TRACK_ORDER_PAY"),
+
+    /**
+     * 创角
+     */
+    KAFKA_EVENT_TRACK_ROLE_CREATE("KAFKA_EVENT_TRACK_ROLE_CREATE"),
+
+    /**
+     * 角色更新
+     */
+    KAFKA_EVENT_TRACK_ROLE_UPDATE("KAFKA_EVENT_TRACK_ROLE_UPDATE");
+
+    /**
+     * 数据埋点类型
+     */
+    private String value;
+}

+ 2 - 2
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/enums/TokenCheckEnum.java

@@ -30,10 +30,10 @@ public enum TokenCheckEnum {
     /**
      * 支付方式
      */
-    private final Integer code;
+    private Integer code;
 
     /**
      * 支付类前缀
      */
-    private final String msg;
+    private String msg;
 }

+ 8 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/listener/OrderPaySuccessListener.java

@@ -1,10 +1,13 @@
 package com.zanxiang.game.module.sdk.listener;
 
 import com.zanxiang.game.module.base.pojo.enums.PayDeviceEnum;
+import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
 import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
 import com.zanxiang.game.module.sdk.service.ICallBackService;
+import com.zanxiang.game.module.sdk.service.IKafkaService;
 import com.zanxiang.game.module.sdk.service.IOrderService;
 import com.zanxiang.game.module.sdk.service.IPerformOrderService;
+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;
@@ -27,6 +30,9 @@ import java.util.Objects;
 @Slf4j
 public class OrderPaySuccessListener {
 
+    @Autowired
+    private IKafkaService kafkaService;
+
     @Resource
     private IOrderService orderService;
 
@@ -69,6 +75,8 @@ public class OrderPaySuccessListener {
         }
         //订单回传
         callBackService.orderCallBack(platformOrderDTO);
+        //订单支付埋点数据发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ORDER_PAY, JsonUtil.toString(platformOrderDTO));
         log.info("订单:{} 支付成功履约监听逻辑 ------end---------", event.getOrderId());
     }
 }

+ 6 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/dto/PlatformOrderDTO.java

@@ -121,6 +121,12 @@ public class PlatformOrderDTO {
      */
     private String merchantOrderNo;
 
+    /**
+     * 米大师商户订单号
+     * PS : 微信和支付宝都是以业务订单号作为商户订单号, 只有米大师个狗东西不一样, 独有字段
+     */
+    private String miPayMerchantNo;
+
     /**
      * 收款商户号
      */

+ 29 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IKafkaService.java

@@ -0,0 +1,29 @@
+package com.zanxiang.game.module.sdk.service;
+
+import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
+import com.zanxiang.game.module.sdk.pojo.param.GameRoleActiveCallParam;
+import com.zanxiang.game.module.sdk.pojo.param.UserData;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-03-07
+ * @description : 卡夫卡队列
+ */
+public interface IKafkaService {
+
+    /**
+     * 埋点数据发送到卡夫卡
+     *
+     * @param eventTrackEnum : 埋点类型key
+     * @param data           : 数据
+     */
+    void eventTrack(KafkaEventTrackEnum eventTrackEnum, String data);
+
+    /**
+     * 角色活跃信息发送到卡夫卡
+     *
+     * @param userData : 用户信息
+     * @param param    : 提交参数
+     */
+    void roleActiveTrack(UserData userData, GameRoleActiveCallParam param);
+}

+ 76 - 73
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java

@@ -10,20 +10,16 @@ import com.zanxiang.game.module.mybatis.entity.User;
 import com.zanxiang.game.module.mybatis.mapper.GameUserRoleMapper;
 import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
 import com.zanxiang.game.module.sdk.enums.DataTypeEnum;
+import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
 import com.zanxiang.game.module.sdk.enums.LoginTypeEnum;
 import com.zanxiang.game.module.sdk.pojo.param.GameRoleActiveCallParam;
 import com.zanxiang.game.module.sdk.pojo.param.GameUserRoleUpdateParam;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 import com.zanxiang.game.module.sdk.service.*;
 import com.zanxiang.module.redis.service.IDistributedLockComponent;
-import com.zanxiang.module.util.DateUtil;
 import com.zanxiang.module.util.JsonUtil;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.kafka.clients.producer.Producer;
-import org.apache.kafka.clients.producer.ProducerRecord;
-import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -41,36 +37,32 @@ import java.util.concurrent.TimeUnit;
 @Service
 public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, GameUserRole> implements IGameUserRoleService {
 
-    @Autowired
-    private RestTemplate restTemplate;
-
-    @Autowired
-    private IGameUserService gameUserService;
+    @Value("${server.domain}")
+    private String serverUrl;
 
     @Autowired
     private IUserService userService;
 
     @Autowired
-    private IUserLoginLogService userLoginLogService;
+    private IKafkaService kafkaService;
 
     @Autowired
-    private IDistributedLockComponent distributedLockComponent;
+    private RestTemplate restTemplate;
 
     @Autowired
-    private IUserTokenService userTokenService;
+    private IGameUserService gameUserService;
 
     @Autowired
     private ICallBackService callBackService;
 
-    @Value("${spring.kafka.game-sdk.gameRoleActiveTopic}")
-    private String gameRoleActiveTopic;
+    @Autowired
+    private IUserTokenService userTokenService;
 
-    @Value("${server.domain}")
-    private String serverUrl;
+    @Autowired
+    private IUserLoginLogService userLoginLogService;
 
     @Autowired
-    @Qualifier("gameSdkKafkaProducer")
-    private Producer<String, String> kafkaProducer;
+    private IDistributedLockComponent distributedLockComponent;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -84,7 +76,7 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         //创建角色
         if (Objects.equals(dataType, DataTypeEnum.TYPE_CREATE_ROLE.getDateType())) {
             //新建游戏角色
-            this.createRoleUpdate(param, userData);
+            this.gameRoleCreate(param, userData);
             //插入用户登录记录
             return userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(), LoginTypeEnum.LOGIN_IN.getLoginType());
         }
@@ -96,7 +88,11 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         //逻辑兜底, 万一角色未创建成功, 补充创建
         if (gameUserRole == null) {
             //新建游戏角色
-            this.createRoleUpdate(param, userData);
+            this.gameRoleCreate(param, userData);
+            //刚创建的角色, 如果是更新请求, 则不需要执行
+            if (Objects.equals(dataType, DataTypeEnum.TYPE_LEVEL_UP.getDateType())) {
+                return Boolean.TRUE;
+            }
         }
         //进入游戏
         if (Objects.equals(dataType, DataTypeEnum.TYPE_ENTER_GAME.getDateType())) {
@@ -105,22 +101,7 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         }
         //等级提升更新
         if (Objects.equals(dataType, DataTypeEnum.TYPE_LEVEL_UP.getDateType())) {
-            //更新频率限制, 20秒更新一次, 避免游戏实时战力高频上报
-            if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_LEVEL_UP + userData.getUserId(), 0L, 20L, TimeUnit.SECONDS)) {
-                return Boolean.TRUE;
-            }
-            return super.update(new LambdaUpdateWrapper<GameUserRole>()
-                    .set(GameUserRole::getRoleName, param.getRoleName())
-                    .set(GameUserRole::getRoleLevel, param.getRoleLevel())
-                    .set(gameUserRole == null || Strings.isBlank(gameUserRole.getServerId()), GameUserRole::getServerId, param.getServerId())
-                    .set(GameUserRole::getServerName, param.getServerName())
-                    .set(param.getRoleVipLevel() != null, GameUserRole::getRoleVipLevel, param.getRoleVipLevel())
-                    .set(param.getRolePower() != null, GameUserRole::getRolePower, param.getRolePower())
-                    .set(GameUserRole::getUpdateTime, param.getRoleGradeUpdateTime() == null || param.getRoleGradeUpdateTime() <= 0 ? LocalDateTime.now() : DateUtil.secondToLocalDateTime(param.getRoleGradeUpdateTime()))
-                    .set(param.getExtra() != null, GameUserRole::getExtra, JsonUtil.toString(param.getExtra()))
-                    .eq(GameUserRole::getUserId, userData.getUserId())
-                    .eq(GameUserRole::getGameId, userData.getGameId())
-                    .eq(GameUserRole::getRoleId, param.getRoleId()));
+            return this.gameRoleUpdate(param, gameUserRole, userData);
         }
         //退出游戏
         if (Objects.equals(dataType, DataTypeEnum.TYPE_EXIT_GAME.getDateType())) {
@@ -130,7 +111,35 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         return Boolean.FALSE;
     }
 
-    private void createRoleUpdate(GameUserRoleUpdateParam param, UserData userData) {
+    private boolean gameRoleUpdate(GameUserRoleUpdateParam param, GameUserRole gameUserRole, UserData userData) {
+        //更新频率限制, 20秒更新一次, 避免游戏实时战力高频上报
+        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_LEVEL_UP + userData.getUserId(), 0L, 20L, TimeUnit.SECONDS)) {
+            return Boolean.TRUE;
+        }
+        //玩家角色信息更新
+        if (gameUserRole.getServerId() == null) {
+            gameUserRole.setServerId(param.getServerId());
+        }
+        if (param.getRoleVipLevel() != null) {
+            gameUserRole.setRoleVipLevel(param.getRoleVipLevel());
+        }
+        if (param.getRolePower() != null) {
+            gameUserRole.setRolePower(param.getRolePower());
+        }
+        if (param.getExtra() != null) {
+            gameUserRole.setExtra(JsonUtil.toString(param.getExtra()));
+        }
+        gameUserRole.setRoleName(param.getRoleName());
+        gameUserRole.setRoleLevel(param.getRoleLevel());
+        gameUserRole.setServerName(param.getServerName());
+        gameUserRole.setUpdateTime(LocalDateTime.now());
+        boolean result = super.updateById(gameUserRole);
+        //角色更新数据埋点发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ROLE_UPDATE, JsonUtil.toString(gameUserRole));
+        return result;
+    }
+
+    private void gameRoleCreate(GameUserRoleUpdateParam param, UserData userData) {
         //上锁
         if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_UPDATE_KEY + "_" + userData.getUserId(), 0L, 1L, TimeUnit.MINUTES)) {
             return;
@@ -148,7 +157,28 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
             return;
         }
         User user = userService.getById(userData.getUserId());
-        userRole = GameUserRole.builder()
+        userRole = this.transform(param, userData, gameUser, user);
+        super.save(userRole);
+        //更新玩家创角数
+        gameUserService.update(new LambdaUpdateWrapper<GameUser>()
+                .setSql("role_count=role_count+" + 1)
+                .set(GameUser::getUpdateTime, LocalDateTime.now())
+                .eq(GameUser::getId, gameUser.getId()));
+        //更新用户创角数
+        userService.update(new LambdaUpdateWrapper<User>()
+                .setSql("role_count=role_count+" + 1)
+                .set(User::getUpdateTime, LocalDateTime.now())
+                .eq(User::getId, gameUser.getUserId()));
+        //用户创角回传
+        callBackService.roleCallBack(userRole, userData);
+        //用户创角埋点数据发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ROLE_CREATE, JsonUtil.toString(userRole));
+        //释放锁
+        distributedLockComponent.unlock(RedisKeyConstant.ROLE_UPDATE_KEY + "_" + userData.getUserId());
+    }
+
+    private GameUserRole transform(GameUserRoleUpdateParam param, UserData userData, GameUser gameUser, User user) {
+        return GameUserRole.builder()
                 .userId(userData.getUserId())
                 .gameUserId(gameUser.getId())
                 .gameId(userData.getGameId())
@@ -161,26 +191,11 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
                 .rolePower(param.getRolePower())
                 .os(userData.getDeviceSystem())
                 .regTime(user == null ? null : user.getCreateTime())
-                .createTime(param.getRoleGradeUpdateTime() == null || param.getRoleGradeUpdateTime() <= 0 ? LocalDateTime.now() : DateUtil.secondToLocalDateTime(param.getRoleGradeUpdateTime()))
+                .createTime(LocalDateTime.now())
                 .updateTime(LocalDateTime.now())
                 .lastLoginTime(LocalDateTime.now())
                 .extra(param.getExtra() == null ? null : JsonUtil.toString(param.getExtra()))
                 .build();
-        super.save(userRole);
-        //更新玩家创角数
-        gameUserService.update(new LambdaUpdateWrapper<GameUser>()
-                .setSql("role_count=role_count+" + 1)
-                .set(GameUser::getUpdateTime, LocalDateTime.now())
-                .eq(GameUser::getId, gameUser.getId()));
-        //更新用户创角数
-        userService.update(new LambdaUpdateWrapper<User>()
-                .setSql("role_count=role_count+" + 1)
-                .set(User::getUpdateTime, LocalDateTime.now())
-                .eq(User::getId, gameUser.getUserId()));
-        //用户创角回传
-        callBackService.roleCallBack(userRole, userData);
-        //释放锁
-        distributedLockComponent.unlock(RedisKeyConstant.ROLE_UPDATE_KEY + "_" + userData.getUserId());
     }
 
     @Override
@@ -255,29 +270,17 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
 
     @Override
     public boolean gameRoleActiveCall(UserData userData, GameRoleActiveCallParam param) {
-        //token临期失效判断
-        try {
-            userTokenService.userTokenUpdateCheck(userData);
-        } catch (Exception ignored) {
-        }
-        //测试环境不使用kafka
+        //测试不执行
         if (this.serverUrl.contains("test")) {
             return Boolean.TRUE;
         }
-        //活跃提交
-        Map<String, Object> activeParamMap = new HashMap<>(6);
-        activeParamMap.put("userId", userData.getUserId());
-        activeParamMap.put("gameId", userData.getGameId());
-        activeParamMap.put("serverId", param.getServerId());
-        activeParamMap.put("roleId", param.getRoleId());
-        activeParamMap.put("roleLevel", param.getRoleLevel());
-        activeParamMap.put("activeTime", System.currentTimeMillis());
+        //token临期失效判断
         try {
-            kafkaProducer.send(new ProducerRecord<>(gameRoleActiveTopic, userData.getUserId().toString(), JsonUtil.toString(activeParamMap)));
-        } catch (Exception e) {
-            log.error("用户角色活跃信息发送到 Kafka 异常!, activeParamMap : {}, e : {}", JsonUtil.toString(activeParamMap), e.getMessage());
-            return Boolean.FALSE;
+            userTokenService.userTokenUpdateCheck(userData);
+        } catch (Exception ignored) {
         }
+        //活跃信息发送到卡夫卡
+        kafkaService.roleActiveTrack(userData, param);
         return Boolean.TRUE;
     }
 }

+ 65 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/KafkaServiceImpl.java

@@ -0,0 +1,65 @@
+package com.zanxiang.game.module.sdk.service.impl;
+
+import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
+import com.zanxiang.game.module.sdk.pojo.param.GameRoleActiveCallParam;
+import com.zanxiang.game.module.sdk.pojo.param.UserData;
+import com.zanxiang.game.module.sdk.service.IKafkaService;
+import com.zanxiang.module.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.producer.Producer;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-03-07
+ * @description : 卡夫卡队列
+ */
+@Slf4j
+@Service
+public class KafkaServiceImpl implements IKafkaService {
+
+    @Value("${spring.kafka.game-sdk.gameRoleActiveTopic}")
+    private String gameRoleActiveTopic;
+
+    @Value("${spring.kafka.game-sdk.gameEventTrackTopic}")
+    private String gameEventTrackTopic;
+
+    @Autowired
+    @Qualifier("gameSdkKafkaProducer")
+    private Producer<String, String> kafkaProducer;
+
+    @Override
+    public void eventTrack(KafkaEventTrackEnum eventTrackEnum, String data) {
+        try {
+            kafkaProducer.send(new ProducerRecord<>(this.gameEventTrackTopic, eventTrackEnum.getValue(), data));
+        } catch (Exception e) {
+            log.error("玩家数据埋点发送到 Kafka 异常!, eventTrackTopic : {}, eventTrackEnum : {}, data : {}, e : {}",
+                    this.gameEventTrackTopic, eventTrackEnum.getValue(), data, e.getMessage());
+        }
+    }
+
+    @Override
+    public void roleActiveTrack(UserData userData, GameRoleActiveCallParam param) {
+        //活跃提交
+        Map<String, Object> activeParamMap = new HashMap<>(6);
+        activeParamMap.put("userId", userData.getUserId());
+        activeParamMap.put("gameId", userData.getGameId());
+        activeParamMap.put("serverId", param.getServerId());
+        activeParamMap.put("roleId", param.getRoleId());
+        activeParamMap.put("roleLevel", param.getRoleLevel());
+        activeParamMap.put("activeTime", System.currentTimeMillis());
+        try {
+            kafkaProducer.send(new ProducerRecord<>(this.gameRoleActiveTopic, userData.getUserId().toString(), JsonUtil.toString(activeParamMap)));
+        } catch (Exception e) {
+            log.error("玩家角色活跃信息发送到 Kafka 异常!, eventTrackTopic : {}, data : {}, e : {}",
+                    this.gameRoleActiveTopic, JsonUtil.toString(activeParamMap), e.getMessage());
+        }
+    }
+}

+ 10 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java

@@ -9,6 +9,7 @@ import com.zanxiang.game.module.mybatis.entity.User;
 import com.zanxiang.game.module.mybatis.entity.UserCard;
 import com.zanxiang.game.module.mybatis.entity.UserShare;
 import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
+import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
 import com.zanxiang.game.module.sdk.enums.LoginTypeEnum;
 import com.zanxiang.game.module.sdk.enums.SmsTypeEnum;
 import com.zanxiang.game.module.sdk.pojo.dto.GameAppletDTO;
@@ -91,6 +92,9 @@ public class LoginServiceImpl implements IRegisterLoginService {
     @Autowired
     private IAgentService agentService;
 
+    @Autowired
+    private IKafkaService kafkaService;
+
     @Autowired
     private IGameAppletShellService gameAppletShellService;
 
@@ -306,6 +310,8 @@ public class LoginServiceImpl implements IRegisterLoginService {
                 .createTime(LocalDateTime.now())
                 .updateTime(LocalDateTime.now())
                 .build());
+        //注册信息埋点数据发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_REG, JsonUtil.toString(user));
         //释放锁
         distributedLockComponent.unlock(lockKey);
         return user;
@@ -332,9 +338,12 @@ public class LoginServiceImpl implements IRegisterLoginService {
     }
 
     private UserLoginVO createUserLoginVO(User user, UserData userData) {
-        //补全 userData 中的用户id
+        //注册, 补全 userData 中的用户id
         if (userData.getUserId() == null) {
             userData.setUserId(user.getId());
+        } else {
+            //登录, 发送登录埋点数据到卡夫卡
+            kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_LOGIN, JsonUtil.toString(user));
         }
         //获取token
         String userToken = userTokenService.getUserToken(user.getId(), userData.getDeviceType());

+ 21 - 5
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/OrderPayServiceImpl.java

@@ -20,6 +20,7 @@ import com.zanxiang.game.module.sdk.service.*;
 import com.zanxiang.game.module.sdk.service.pay.PayBaseService;
 import com.zanxiang.game.module.sdk.util.RedisUtil;
 import com.zanxiang.game.module.sdk.util.SpringUtils;
+import com.zanxiang.module.redis.service.IDistributedLockComponent;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
 import com.zanxiang.module.util.exception.BaseException;
@@ -36,6 +37,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author : lingfeng
@@ -64,6 +66,9 @@ public class OrderPayServiceImpl implements IOrderPayService {
     @Autowired
     private RedisUtil<String> redisUtil;
 
+    @Autowired
+    private IDistributedLockComponent distributedLockComponent;
+
     @Value("${payConfig.wxPay.customH5Url}")
     private String customH5Url;
 
@@ -83,11 +88,7 @@ public class OrderPayServiceImpl implements IOrderPayService {
             return BeanUtil.copy(orderPayParam, PayParamVO.class);
         }
         //创建支付参数
-        ProductPayParamDTO productPayParamDTO = this.transform(order, code, openId, IpUtil.getRealIp(request));
-        //调起支付
-        PayTypeEnum payTypeEnum = PayTypeEnum.getByPayType(productPayParamDTO.getPayWay().intValue());
-        PayBaseService service = SpringUtils.getBean(payTypeEnum.getClazz());
-        service.payCreate(productPayParamDTO);
+        this.payParamCreate(code, openId, order, request);
         //重新查询
         orderPayParam = orderPayParamService.getOne(new LambdaQueryWrapper<OrderPayParam>()
                 .eq(OrderPayParam::getOrderId, order.getOrderId()));
@@ -99,6 +100,21 @@ public class OrderPayServiceImpl implements IOrderPayService {
         return BeanUtil.copy(orderPayParam, PayParamVO.class);
     }
 
+    private void payParamCreate(String code, String openId, Order order, HttpServletRequest request) {
+        //上锁
+        if (!distributedLockComponent.doLock(RedisKeyConstant.GET_PAY_PARAM_LOCK + order.getOrderId(), 0L, 1L, TimeUnit.MINUTES)) {
+            return;
+        }
+        //创建支付参数
+        ProductPayParamDTO productPayParamDTO = this.transform(order, code, openId, IpUtil.getRealIp(request));
+        //调起支付
+        PayTypeEnum payTypeEnum = PayTypeEnum.getByPayType(productPayParamDTO.getPayWay().intValue());
+        PayBaseService service = SpringUtils.getBean(payTypeEnum.getClazz());
+        service.payCreate(productPayParamDTO);
+        //释放锁
+        distributedLockComponent.unlock(RedisKeyConstant.GET_PAY_PARAM_LOCK + order.getOrderId());
+    }
+
     private ProductPayParamDTO transform(Order order, String code, String openId, String ip) {
         return ProductPayParamDTO.builder()
                 .gameId(order.getGameId())

+ 6 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/OrderServiceImpl.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zanxiang.game.back.base.pojo.enums.OrderStatusEnum;
 import com.zanxiang.game.module.mybatis.entity.*;
 import com.zanxiang.game.module.mybatis.mapper.OrderMapper;
+import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
 import com.zanxiang.game.module.sdk.pojo.dto.GamePayWayDTO;
 import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
 import com.zanxiang.game.module.sdk.pojo.param.ProductPayParam;
@@ -35,6 +36,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
     @Autowired
     private IGameService gameService;
 
+    @Autowired
+    private IKafkaService kafkaService;
+
     @Autowired
     private IGameUserService gameUserService;
 
@@ -78,6 +82,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
         payParam.setOrderId(orderNum);
         //订单回传
         callBackService.orderCallBack(BeanUtil.copy(order, PlatformOrderDTO.class));
+        //下单数据埋点发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ORDER_CREATE, JsonUtil.toString(order));
         //返回
         return Boolean.TRUE;
     }