浏览代码

fix : 解决修仙游戏漏上报角色的问题

bilingfeng 1 年之前
父节点
当前提交
0322182491

+ 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" +

+ 8 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/UserController.java

@@ -99,6 +99,14 @@ public class UserController {
         return ResultVO.ok(userService.getUserCustomer(userData));
     }
 
+    @UnSignCheck
+    @ApiOperation(value = "CP服务端上传角色信息")
+    @PostMapping("/cp/server/update/game/role")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<Boolean> updateUserGameRole(@Validated @RequestBody GameUserRoleSubmitParam param) {
+        return ResultVO.ok(gameUserRoleService.updateUserGameRole(param));
+    }
+
     @ApiOperation(value = "上传角色信息")
     @PostMapping("/update/game/role")
     @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})

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

@@ -17,6 +17,11 @@ public enum KafkaEventTrackEnum {
      */
     KAFKA_EVENT_TRACK_REG("KAFKA_EVENT_TRACK_REG"),
 
+    /**
+     * 渠道变更
+     */
+    KAFKA_EVENT_TRACK_AGENT_UPDATE("KAFKA_EVENT_TRACK_AGENT_UPDATE"),
+
     /**
      * 登录
      */

+ 10 - 3
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/handler/GlobalExceptionHandler.java

@@ -70,13 +70,20 @@ public class GlobalExceptionHandler {
 
     /**
      * 参数类型不匹配导致转换异常
-     *
-     * @param e
-     * @return
      */
     @ExceptionHandler(MethodArgumentTypeMismatchException.class)
     public ResultVO<?> mismatchErrorHandler(MethodArgumentTypeMismatchException e) {
         log.error("方法:{},字段:{},参数:{},错误信息:{}", e.getParameter().getMethod(), e.getName(), e.getValue(), e.getMessage());
         return ResultVO.fail("请求参数异常,请勿非法操作");
     }
+
+    /**
+     * 空指针异常全局处理
+     */
+    @ExceptionHandler(NullPointerException.class)
+    public ResultVO<?> validExceptionHandler(NullPointerException e) {
+        log.error(e.getMessage(), e);
+        e.printStackTrace();
+        return ResultVO.fail("空指针异常,请勿非法操作");
+    }
 }

+ 57 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/GameUserRoleSubmitParam.java

@@ -0,0 +1,57 @@
+package com.zanxiang.game.module.sdk.pojo.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-17
+ * @description : 角色信息提交接口
+ */
+@Data
+public class GameUserRoleSubmitParam {
+
+    /**
+     * 游戏id
+     */
+    @ApiModelProperty("游戏id")
+    @NotBlank(message = "游戏id不可为空")
+    private String gameId;
+
+    /**
+     * 玩家id
+     */
+    @ApiModelProperty("玩家id")
+    @NotNull(message = "玩家id不可为空")
+    private Long userId;
+
+    /**
+     * 操作系统
+     */
+    @NotNull(message = "操作系统不可为空")
+    private String os;
+
+    /**
+     * 接口token
+     */
+    @ApiModelProperty("接口token")
+    @NotBlank(message = "接口token不可为空")
+    private String token;
+
+    /**
+     * 加密参数
+     */
+    @ApiModelProperty("加密参数")
+    @NotBlank(message = "加密参数不可为空")
+    private String sign;
+
+    /**
+     * 角色信息参数
+     */
+    @ApiModelProperty("角色信息")
+    @NotNull(message = "角色信息不可为null")
+    private GameUserRoleUpdateParam gameUserRoleParam;
+}

+ 0 - 31
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/GameUserRoleUpdateParam.java

@@ -5,7 +5,6 @@ import lombok.Data;
 
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
-import java.math.BigDecimal;
 
 /**
  * 游戏角色
@@ -44,24 +43,6 @@ public class GameUserRoleUpdateParam {
     @NotNull(message = "角色等级不可为空")
     private Long roleLevel;
 
-    /**
-     * 角色等级描述
-     */
-    @ApiModelProperty("角色等级描述")
-    private String roleGradeDesc;
-
-    /**
-     * 角色创建时间, 当TYPE_CREATE_ROLE为必须,时间戳
-     */
-    @ApiModelProperty("角色创建时间, 当TYPE_CREATE_ROLE为必须,时间戳")
-    private Long roleCreateTime;
-
-    /**
-     * 角色等级变化时间,时间戳
-     */
-    @ApiModelProperty("角色等级变化时间,时间戳")
-    private Long roleGradeUpdateTime;
-
     /**
      * 游戏服务器id
      */
@@ -88,18 +69,6 @@ public class GameUserRoleUpdateParam {
     @ApiModelProperty("玩家角色战力")
     private Long rolePower;
 
-    /**
-     * 角色余额
-     */
-    @ApiModelProperty("角色余额")
-    private BigDecimal platformCoin;
-
-    /**
-     * 游戏在线时长
-     */
-    @ApiModelProperty("游戏在线时长")
-    private Long totalOnlineTime;
-
     /**
      * 角色拓展属性
      */

+ 8 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameUserRoleService.java

@@ -4,12 +4,20 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.zanxiang.game.module.mybatis.entity.GameUserRole;
 import com.zanxiang.game.module.mybatis.entity.User;
 import com.zanxiang.game.module.sdk.pojo.param.GameRoleActiveCallParam;
+import com.zanxiang.game.module.sdk.pojo.param.GameUserRoleSubmitParam;
 import com.zanxiang.game.module.sdk.pojo.param.GameUserRoleUpdateParam;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 
 
 public interface IGameUserRoleService extends IService<GameUserRole> {
 
+    /**
+     * CP 服务端玩家角色提交
+     *
+     * @param param : 提交参数
+     */
+    Boolean updateUserGameRole(GameUserRoleSubmitParam param);
+
     /**
      * 更新用户游戏角色
      *

+ 21 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IUserTokenService.java

@@ -1,11 +1,13 @@
 package com.zanxiang.game.module.sdk.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.GameExt;
 import com.zanxiang.game.module.mybatis.entity.UserToken;
 import com.zanxiang.game.module.sdk.pojo.dto.UserTokenDTO;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 import com.zanxiang.game.module.sdk.pojo.vo.CpTokenCheckVO;
 import com.zanxiang.module.util.pojo.ResultVO;
+import reactor.util.function.Tuple2;
 
 /**
  * @author : lingfeng
@@ -36,6 +38,25 @@ public interface IUserTokenService extends IService<UserToken> {
      */
     ResultVO<Long> cpTokenCheck(String appId, Long userId, String token, String sign);
 
+    /**
+     * CP服务端加密方法
+     *
+     * @param gameExt : 游戏信息
+     * @param userId  : 玩家id
+     * @param token   : 令牌
+     * @return
+     */
+    Tuple2<String, String> getMySign(GameExt gameExt, Long userId, String token);
+
+    /**
+     * 获取玩家token
+     *
+     * @param userId : 玩家id
+     * @param token  : 令牌
+     * @return : 返回token信息
+     */
+    UserToken getCheckUserToken(Long userId, String token);
+
     /**
      * 用户活跃token更新
      *

+ 9 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/AgentServiceImpl.java

@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zanxiang.game.module.base.pojo.enums.GameCategoryEnum;
 import com.zanxiang.game.module.mybatis.entity.*;
 import com.zanxiang.game.module.mybatis.mapper.AgentMapper;
+import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 import com.zanxiang.game.module.sdk.service.*;
 import com.zanxiang.module.util.DateUtil;
@@ -50,6 +51,9 @@ public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements
     @Autowired
     private IGameExtService gameExtService;
 
+    @Autowired
+    private IKafkaService kafkaService;
+
     @Override
     public void userAgentUpdate(User user, String channel) {
         log.error("用户登录接收到的渠道参数 userId : {}, channel : {}", user.getId(), channel);
@@ -79,18 +83,22 @@ public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements
             if (agent == null) {
                 return;
             }
+            LocalDateTime localDateTime = LocalDateTime.now();
             //更新用户信息
             userService.update(new LambdaUpdateWrapper<User>()
                     .set(User::getAgentId, agent.getId())
                     .set(User::getChannel, channel)
-                    .set(User::getUpdateTime, LocalDateTime.now())
+                    .set(User::getUpdateTime, localDateTime)
                     .eq(User::getId, user.getId()));
             //添加渠道变更记录
             userAgentLogService.agentUpdateLog(user, agent.getId(), channel);
             //回传用户信息
             user.setAgentId(agent.getId());
             user.setChannel(channel);
+            user.setUpdateTime(localDateTime);
             callBackService.userCallBack(user, urlParamMap);
+            //注册信息埋点数据发送到卡夫卡
+            kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_AGENT_UPDATE, JsonUtil.toString(user));
         } catch (Exception e) {
             log.error("用户渠道更新异常, userId : {}, channel : {}, e : {}", user.getId(), channel, e.getMessage());
         }

+ 66 - 8
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java

@@ -4,26 +4,28 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zanxiang.game.module.mybatis.entity.GameUser;
-import com.zanxiang.game.module.mybatis.entity.GameUserRole;
-import com.zanxiang.game.module.mybatis.entity.User;
+import com.zanxiang.game.module.mybatis.entity.*;
 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.GameUserRoleSubmitParam;
 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.JsonUtil;
+import com.zanxiang.module.util.exception.BaseException;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.client.RestTemplate;
+import reactor.util.function.Tuple2;
 
 import java.time.LocalDateTime;
 import java.util.*;
@@ -64,6 +66,56 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
     @Autowired
     private IDistributedLockComponent distributedLockComponent;
 
+    @Autowired
+    private IGameExtService gameExtService;
+
+    @Override
+    public Boolean updateUserGameRole(GameUserRoleSubmitParam param) {
+        //查询游戏信息
+        GameExt gameExt = gameExtService.getByGameAppId(param.getGameId());
+        if (gameExt == null || Strings.isBlank(gameExt.getLoginKey())) {
+            log.error("参数错误, 游戏信息不存在, param : {}", JsonUtil.toString(param));
+            throw new BaseException("参数错误, 游戏信息不存在");
+        }
+        //根据token检验
+        UserToken userToken = userTokenService.getCheckUserToken(param.getUserId(), param.getToken());
+        if (userToken == null) {
+            log.error("参数错误, token令牌无效, param : {}", JsonUtil.toString(param));
+            throw new BaseException("参数错误, token令牌无效");
+        }
+        //验证签名
+        Tuple2<String, String> tuple2 = userTokenService.getMySign(gameExt, param.getUserId(), param.getToken());
+        if (!Objects.equals(tuple2.getT2(), param.getSign())) {
+            log.error("参数错误 , str : {}, mySign : {}, sign : {}", tuple2.getT1(), tuple2.getT2(), param.getSign());
+            throw new BaseException("参数错误, sign验证失败, str : " + tuple2.getT1() + ", mySign : " + tuple2.getT2());
+        }
+        //提交的角色信息
+        GameUserRoleUpdateParam gameUserRoleParam = param.getGameUserRoleParam();
+        //查询玩家信息
+        User user = userService.getById(userToken.getUserId());
+        //构建 userData
+        UserData userData = new UserData();
+        userData.setUserId(user.getId());
+        userData.setGameId(user.getGameId());
+        userData.setDeviceSystem(param.getOs());
+        //判断角色是否存在
+        GameUserRole gameUserRole = super.getOne(new LambdaQueryWrapper<GameUserRole>()
+                .eq(GameUserRole::getUserId, user.getId())
+                .eq(GameUserRole::getGameId, user.getGameId())
+                .eq(GameUserRole::getRoleId, gameUserRoleParam.getRoleId()));
+        //更新游戏角色
+        if (gameUserRole != null) {
+            this.gameRoleUpdate(gameUserRoleParam, gameUserRole, userData);
+            return Boolean.TRUE;
+        }
+        //新建游戏角色
+        this.gameRoleCreate(gameUserRoleParam, userData);
+        //创建角色通知监听服务
+        this.callListenIn(gameUserRoleParam, userData);
+        //返回结果
+        return Boolean.TRUE;
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean updateUserGameRole(GameUserRoleUpdateParam param, UserData userData) {
@@ -78,7 +130,8 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
             //新建游戏角色
             this.gameRoleCreate(param, userData);
             //插入用户登录记录
-            return userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(), LoginTypeEnum.LOGIN_IN.getLoginType());
+            return userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(),
+                    LoginTypeEnum.LOGIN_IN.getLoginType());
         }
         //判断角色是否存在
         GameUserRole gameUserRole = super.getOne(new LambdaQueryWrapper<GameUserRole>()
@@ -97,7 +150,8 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         //进入游戏
         if (Objects.equals(dataType, DataTypeEnum.TYPE_ENTER_GAME.getDateType())) {
             //插入用户登录记录
-            return userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(), LoginTypeEnum.LOGIN_IN.getLoginType());
+            return userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(),
+                    LoginTypeEnum.LOGIN_IN.getLoginType());
         }
         //等级提升更新
         if (Objects.equals(dataType, DataTypeEnum.TYPE_LEVEL_UP.getDateType())) {
@@ -106,14 +160,16 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         //退出游戏
         if (Objects.equals(dataType, DataTypeEnum.TYPE_EXIT_GAME.getDateType())) {
             //插入用户退出记录
-            return userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(), LoginTypeEnum.LOGIN_OUT.getLoginType());
+            return userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(),
+                    LoginTypeEnum.LOGIN_OUT.getLoginType());
         }
         return Boolean.FALSE;
     }
 
     private boolean gameRoleUpdate(GameUserRoleUpdateParam param, GameUserRole gameUserRole, UserData userData) {
         //更新频率限制, 20秒更新一次, 避免游戏实时战力高频上报
-        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_LEVEL_UP + userData.getUserId(), 0L, 20L, TimeUnit.SECONDS)) {
+        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_LEVEL_UP + userData.getUserId(),
+                0L, 20L, TimeUnit.SECONDS)) {
             return Boolean.TRUE;
         }
         //玩家角色信息更新
@@ -142,13 +198,15 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
     private void gameRoleCreate(GameUserRoleUpdateParam param, UserData userData) {
         //查询玩家角色信息
         GameUserRole userRole = this.getOne(new LambdaQueryWrapper<GameUserRole>()
+                .eq(GameUserRole::getUserId, userData.getUserId())
                 .eq(GameUserRole::getGameId, userData.getGameId())
                 .eq(GameUserRole::getRoleId, param.getRoleId()));
         if (userRole != null) {
             return;
         }
         //上锁
-        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_CREATE_LOCK + param.getRoleId(), 0L, 3L, TimeUnit.MINUTES)) {
+        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_CREATE_LOCK + param.getRoleId(),
+                0L, 3L, TimeUnit.MINUTES)) {
             return;
         }
         //查询玩家信息

+ 4 - 2
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserTokenServiceImpl.java

@@ -125,7 +125,8 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
         return ResultVO.ok(userId);
     }
 
-    private Tuple2<String, String> getMySign(GameExt gameExt, Long userId, String token) {
+    @Override
+    public Tuple2<String, String> getMySign(GameExt gameExt, Long userId, String token) {
         //计算用户签名
         StringBuilder sb = new StringBuilder();
         sb.append("loginKey=").append(gameExt.getLoginKey());
@@ -142,7 +143,8 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
         return Tuples.of(sb.toString(), mySign);
     }
 
-    private UserToken getCheckUserToken(Long userId, String token) {
+    @Override
+    public UserToken getCheckUserToken(Long userId, String token) {
         //非导量用户
         UserToken userToken = super.getOne(new LambdaQueryWrapper<UserToken>()
                 .eq(UserToken::getToken, token)