Selaa lähdekoodia

feat : 非嵌入式SDK接入, 接口提交

bilingfeng 11 kuukautta sitten
vanhempi
commit
0384517f67
28 muutettua tiedostoa jossa 1190 lisäystä ja 23 poistoa
  1. 1 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java
  2. 49 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/adapter/WebHandlerAdapter.java
  3. 14 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/annotation/PushCheck.java
  4. 5 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/constant/RedisKeyConstant.java
  5. 53 2
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/PushController.java
  6. 32 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushActiveParam.java
  7. 70 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushOrderParam.java
  8. 73 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushRoleParam.java
  9. 34 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushServerParam.java
  10. 63 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushUserParam.java
  11. 23 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/vo/CpPushResultVO.java
  12. 33 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/vo/CpPushUserVO.java
  13. 1 3
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICallBackService.java
  14. 58 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICpPushDataService.java
  15. 12 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameServerService.java
  16. 12 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameSupperService.java
  17. 9 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IKafkaService.java
  18. 8 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IOrderService.java
  19. 9 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IUserShareService.java
  20. 3 3
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CallBackServiceImpl.java
  21. 547 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CpPushDataServiceImpl.java
  22. 18 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameServerServiceImpl.java
  23. 18 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameSupperServiceImpl.java
  24. 1 3
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java
  25. 20 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/KafkaServiceImpl.java
  26. 8 10
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java
  27. 2 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/OrderServiceImpl.java
  28. 14 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserShareServiceImpl.java

+ 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服务启动成功 <非嵌入式SDK接入, 接口提交> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 49 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/adapter/WebHandlerAdapter.java

@@ -2,6 +2,7 @@ package com.zanxiang.game.module.sdk.adapter;
 
 import com.zanxiang.game.module.base.pojo.enums.HttpStatusEnum;
 import com.zanxiang.game.module.mybatis.entity.GameExt;
+import com.zanxiang.game.module.sdk.annotation.PushCheck;
 import com.zanxiang.game.module.sdk.annotation.UnSignCheck;
 import com.zanxiang.game.module.sdk.enums.DeviceTypeEnum;
 import com.zanxiang.game.module.sdk.service.IGameExtService;
@@ -36,6 +37,12 @@ public class WebHandlerAdapter implements HandlerInterceptor {
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         HandlerMethod handlerMethod = (HandlerMethod) handler;
+        //推送数据的签名校验
+        PushCheck pushCheck = handlerMethod.getMethod().getAnnotation(PushCheck.class);
+        //数据推送签名校验
+        if (pushCheck != null) {
+            this.pushCheck(request);
+        }
         //排除签名认证接口注解
         UnSignCheck unSignCheck = handlerMethod.getMethod().getAnnotation(UnSignCheck.class);
         //接口签名验证
@@ -77,4 +84,46 @@ public class WebHandlerAdapter implements HandlerInterceptor {
         }
         return Boolean.TRUE;
     }
+
+    private void pushCheck(HttpServletRequest request) throws Exception {
+        String sign = request.getParameter("sign");
+        String gameId = request.getParameter("gameId");
+        String timestamp = request.getParameter("timestamp");
+        String nonce = request.getParameter("nonce");
+        String server = request.getParameter("server");
+        //检查加密必传参数
+        if (StringUtils.isAnyBlank(gameId, timestamp, nonce, sign, server)) {
+            throw new BaseException(HttpStatusEnum.INVALID_PARAMS.getMsg());
+        }
+        //判断时间戳是否有效
+        if (Long.valueOf(timestamp) + 300L > System.currentTimeMillis() / 1000) {
+            throw new BaseException(HttpStatusEnum.INVALID_PARAMS.getMsg());
+        }
+        //签名验证
+        GameExt gameExt = gameExtService.getByGameAppId(gameId);
+        if (gameExt == null || Strings.isBlank(gameExt.getLoginKey())) {
+            throw new BaseException(HttpStatusEnum.INVALID_PARAMS.getMsg());
+        }
+        //密钥
+        String key = null;
+        String keyName = null;
+        if (Objects.equals(server, "0")) {
+            keyName = "appKey=";
+            key = gameExt.getAppKey();
+        }
+        if (Objects.equals(server, "1")) {
+            keyName = "loginKey=";
+            key = gameExt.getLoginKey();
+        }
+        if (StringUtils.isAnyBlank(key, keyName)) {
+            throw new BaseException(HttpStatusEnum.INVALID_PARAMS.getMsg());
+        }
+        String str = keyName + key + "&gameId=" + gameId + "&timestamp=" + timestamp + "&nonce=" + nonce + "&server=" + server;
+        String mySign = SignUtil.MD5(str);
+        //签名对比
+        if (!Objects.equals(mySign, sign)) {
+            log.error("非法参数, 签名错误, mySign : {}, sign : {}, str : {}", mySign, sign, str);
+            throw new BaseException(HttpStatusEnum.INVALID_PARAMS.getMsg());
+        }
+    }
 }

+ 14 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/annotation/PushCheck.java

@@ -0,0 +1,14 @@
+package com.zanxiang.game.module.sdk.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-25
+ * @description : 推送接口签名检测
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface PushCheck {
+}

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

@@ -72,4 +72,9 @@ public class RedisKeyConstant {
      */
     public static final String TOKEN_CREATE_LOCK = RedisKeyConstant.REDIS_PREFIX + "create_token_lock_";
 
+    /**
+     * 订单创建锁
+     */
+    public static final String ORDER_CREATE_LOCK = RedisKeyConstant.REDIS_PREFIX + "order_create_lock_";
+
 }

+ 53 - 2
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/PushController.java

@@ -1,8 +1,11 @@
 package com.zanxiang.game.module.sdk.controller;
 
+import com.zanxiang.game.module.sdk.annotation.PushCheck;
 import com.zanxiang.game.module.sdk.annotation.UnSignCheck;
-import com.zanxiang.game.module.sdk.pojo.param.GameRemitLogParam;
-import com.zanxiang.game.module.sdk.pojo.param.UserVisitLogParam;
+import com.zanxiang.game.module.sdk.pojo.param.*;
+import com.zanxiang.game.module.sdk.pojo.vo.CpPushResultVO;
+import com.zanxiang.game.module.sdk.pojo.vo.CpPushUserVO;
+import com.zanxiang.game.module.sdk.service.ICpPushDataService;
 import com.zanxiang.game.module.sdk.service.IGameRemitLogService;
 import com.zanxiang.game.module.sdk.service.IUserVisitLogService;
 import com.zanxiang.module.util.pojo.ResultVO;
@@ -26,6 +29,9 @@ import javax.servlet.http.HttpServletRequest;
 @RequestMapping(value = "/api")
 public class PushController {
 
+    @Autowired
+    private ICpPushDataService cpPushDataService;
+
     @Autowired
     private IGameRemitLogService gameRemitLogService;
 
@@ -50,4 +56,49 @@ public class PushController {
         return ResultVO.ok(userVisitLogService.visitLogCreate(param.getUrl(), userAgent, httpServletRequest));
     }
 
+    @PushCheck
+    @UnSignCheck
+    @ApiOperation(value = "玩家注册 / 登录信息推送")
+    @PostMapping("/cp/push/user")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<CpPushUserVO> cpPushUser(@RequestParam String gameId, @Validated @RequestBody CpPushUserParam param) {
+        return ResultVO.ok(cpPushDataService.pushUser(gameId, param));
+    }
+
+    @PushCheck
+    @UnSignCheck
+    @ApiOperation(value = "玩家角色信息推送")
+    @PostMapping("/cp/push/role")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<CpPushResultVO> cpPushRole(@RequestParam String gameId, @Validated @RequestBody CpPushRoleParam param) {
+        return ResultVO.ok(cpPushDataService.pushRole(gameId, param));
+    }
+
+    @PushCheck
+    @UnSignCheck
+    @ApiOperation(value = "玩家订单信息推送")
+    @PostMapping("/cp/push/order")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<CpPushResultVO> cpPushOrder(@RequestParam String gameId, @Validated @RequestBody CpPushOrderParam param) {
+        return ResultVO.ok(cpPushDataService.pushOrder(gameId, param));
+    }
+
+    @PushCheck
+    @UnSignCheck
+    @ApiOperation(value = "开服信息推送")
+    @PostMapping("/cp/push/server")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<CpPushResultVO> cpPushServer(@RequestParam String gameId, @Validated @RequestBody CpPushServerParam param) {
+        return ResultVO.ok(cpPushDataService.pushServer(gameId, param));
+    }
+
+    @PushCheck
+    @UnSignCheck
+    @ApiOperation(value = "玩家活跃信息推送")
+    @PostMapping("/cp/push/active")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<CpPushResultVO> cpPushActive(@RequestParam String gameId, @Validated @RequestBody CpPushActiveParam param) {
+        return ResultVO.ok(cpPushDataService.pushActive(gameId, param));
+    }
+
 }

+ 32 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushActiveParam.java

@@ -0,0 +1,32 @@
+package com.zanxiang.game.module.sdk.pojo.param;
+
+import lombok.Data;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-25
+ * @description : CP推送玩家活跃数据
+ */
+@Data
+public class CpPushActiveParam {
+
+    /**
+     * 玩家应用唯一标识
+     */
+    private String openId;
+
+    /**
+     * 区服id
+     */
+    private String serverId;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 活跃类型 : 0 上线, 1 下线
+     */
+    private Integer activeType;
+}

+ 70 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushOrderParam.java

@@ -0,0 +1,70 @@
+package com.zanxiang.game.module.sdk.pojo.param;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-25
+ * @description : CP推送玩家订单数据
+ */
+@Data
+public class CpPushOrderParam {
+
+    /**
+     * 玩家应用唯一标识
+     */
+    private String openId;
+
+    /**
+     * 订单id
+     */
+    private String orderId;
+
+    /**
+     * 订单状态
+     */
+    private Integer status;
+
+    /**
+     * 支付时间
+     */
+    private LocalDateTime payTime;
+
+    /**
+     * 产品id
+     */
+    private String productId;
+
+    /**
+     * 产品名称
+     */
+    private String productName;
+
+    /**
+     * 订单金额
+     */
+    private BigDecimal amount;
+
+    /**
+     * 区服id
+     */
+    private String serverId;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 下单时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 扩展字段
+     */
+    private String extension;
+}

+ 73 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushRoleParam.java

@@ -0,0 +1,73 @@
+package com.zanxiang.game.module.sdk.pojo.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-24
+ * @description : CP推送角色数据
+ */
+@Data
+public class CpPushRoleParam {
+
+    /**
+     * 玩家唯一应用标识
+     */
+    @NotBlank(message = "玩家openId不可为空")
+    private String openId;
+
+    /**
+     * 角色id
+     */
+    @NotBlank(message = "角色id不可为空")
+    private String roleId;
+
+    /**
+     * 角色名称
+     */
+    @NotBlank(message = "角色名称不可为空")
+    private String roleName;
+
+    /**
+     * 角色等级
+     */
+    @NotNull(message = "角色等级不可为空")
+    private Long roleLevel;
+
+    /**
+     * 区服id
+     */
+    @NotBlank(message = "区服id不可为空")
+    private String serverId;
+
+    /**
+     * 区服名称
+     */
+    @NotBlank(message = "区服名称不可为空")
+    private String serverName;
+
+    /**
+     * 角色创建时间
+     */
+    @NotNull(message = "角色创建时间不可为空")
+    private LocalDateTime createTime;
+
+    /**
+     * 角色战力
+     */
+    private Long rolePower;
+
+    /**
+     * 角色VIP等级
+     */
+    private Long roleVipLevel;
+
+    /**
+     * 角色拓展信息
+     */
+    private Object extra;
+}

+ 34 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushServerParam.java

@@ -0,0 +1,34 @@
+package com.zanxiang.game.module.sdk.pojo.param;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-24
+ * @description : CP推送区服数据
+ */
+@Data
+public class CpPushServerParam {
+
+    /**
+     * 区服id
+     */
+    private String serverId;
+
+    /**
+     * 区服名称
+     */
+    private String serverName;
+
+    /**
+     * 开服时间
+     */
+    private LocalDateTime startTime;
+
+    /**
+     * 区服冠名
+     */
+    private String nickName;
+}

+ 63 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushUserParam.java

@@ -0,0 +1,63 @@
+package com.zanxiang.game.module.sdk.pojo.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-20
+ * @description : CP推送玩家数据
+ */
+@Data
+public class CpPushUserParam {
+
+    /**
+     * 玩家唯一应用标识
+     */
+    @NotBlank(message = "玩家openId不可为空")
+    private String openId;
+
+    /**
+     * 玩家注册时间
+     */
+    @NotNull(message = "玩家注册时间不可为空")
+    private LocalDateTime regTime;
+
+    /**
+     * 最近活跃时间
+     */
+    @NotNull(message = "最近活跃时间不可为空")
+    private LocalDateTime activeTime;
+
+    /**
+     * 广告渠道
+     */
+    @NotBlank(message = "广告渠道channel不可为空")
+    private String channel;
+
+    /**
+     * 网络IP
+     */
+    @NotBlank(message = "网络IP不可为空")
+    private String ip;
+
+    /**
+     * 请求UA
+     */
+    @NotBlank(message = "请求UA不可为空")
+    private String ua;
+
+    /**
+     * 操作系统
+     */
+    @NotBlank(message = "操作系统不可为空")
+    private String os;
+
+    /**
+     * 分享玩家openId
+     */
+    private String shareOpenId;
+}

+ 23 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/vo/CpPushResultVO.java

@@ -0,0 +1,23 @@
+package com.zanxiang.game.module.sdk.pojo.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-24
+ * @description : 接口结果
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class CpPushResultVO {
+
+    /**
+     * 接口结果
+     */
+    private Boolean result;
+}

+ 33 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/vo/CpPushUserVO.java

@@ -0,0 +1,33 @@
+package com.zanxiang.game.module.sdk.pojo.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-20
+ * @description : CP推送玩家消息结果
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class CpPushUserVO {
+
+    /**
+     * sdk唯一用户标识
+     */
+    private Long userId;
+
+    /**
+     * 是否sdk用户
+     */
+    private Boolean sdkUser;
+
+    /**
+     * 买量渠道标识
+     */
+    private String channel;
+}

+ 1 - 3
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICallBackService.java

@@ -3,7 +3,6 @@ package com.zanxiang.game.module.sdk.service;
 import com.zanxiang.game.module.mybatis.entity.GameUserRole;
 import com.zanxiang.game.module.mybatis.entity.User;
 import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
-import com.zanxiang.game.module.sdk.pojo.param.UserData;
 
 import java.util.Map;
 
@@ -26,9 +25,8 @@ public interface ICallBackService {
      * 创角回传
      *
      * @param gameUserRole : 角色信息
-     * @param userData     : 用户信息
      */
-    void roleCallBack(GameUserRole gameUserRole, UserData userData);
+    void roleCallBack(GameUserRole gameUserRole);
 
     /**
      * 订单回传

+ 58 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICpPushDataService.java

@@ -0,0 +1,58 @@
+package com.zanxiang.game.module.sdk.service;
+
+import com.zanxiang.game.module.sdk.pojo.param.*;
+import com.zanxiang.game.module.sdk.pojo.vo.CpPushResultVO;
+import com.zanxiang.game.module.sdk.pojo.vo.CpPushUserVO;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-20
+ * @description : CP推送数据
+ */
+public interface ICpPushDataService {
+
+    /**
+     * 订单信息推送
+     *
+     * @param gameAppId : 游戏唯一标识
+     * @param param     : 上报参数
+     * @return : 返回接口执行结果
+     */
+    CpPushResultVO pushOrder(String gameAppId, CpPushOrderParam param);
+
+    /**
+     * 活跃信息上报
+     *
+     * @param gameAppId : 游戏唯一标识
+     * @param param     : 上报参数
+     * @return : 返回接口执行结果
+     */
+    CpPushResultVO pushActive(String gameAppId, CpPushActiveParam param);
+
+    /**
+     * 游戏区服信息数据推送
+     *
+     * @param gameAppId : 游戏唯一标识
+     * @param param     : 接口参数
+     * @return : 返回接口执行结果
+     */
+    CpPushResultVO pushServer(String gameAppId, CpPushServerParam param);
+
+    /**
+     * 玩家角色信息数据推送
+     *
+     * @param gameAppId : 游戏唯一标识
+     * @param param     : 接口参数
+     * @return : 返回接口执行结果
+     */
+    CpPushResultVO pushRole(String gameAppId, CpPushRoleParam param);
+
+    /**
+     * 玩家信息数据推送
+     *
+     * @param gameAppId : 游戏唯一标识
+     * @param param     : 接口参数
+     * @return : 返回数据判定结果
+     */
+    CpPushUserVO pushUser(String gameAppId, CpPushUserParam param);
+}

+ 12 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameServerService.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.sdk.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.GameServer;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-08-07
+ * @description : 游戏区服
+ */
+public interface IGameServerService extends IService<GameServer> {
+}

+ 12 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameSupperService.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.sdk.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.GameSupper;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-09-12
+ * @description : 超父游戏
+ */
+public interface IGameSupperService extends IService<GameSupper> {
+}

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

@@ -1,5 +1,6 @@
 package com.zanxiang.game.module.sdk.service;
 
+import com.zanxiang.game.module.mybatis.entity.GameUserRole;
 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;
@@ -26,4 +27,12 @@ public interface IKafkaService {
      * @param param    : 提交参数
      */
     void roleActiveTrack(UserData userData, GameRoleActiveCallParam param);
+
+    /**
+     * 角色活跃信息发送到卡夫卡
+     *
+     * @param gameUserRole : 角色信息
+     * @param activeType   : 活跃类型
+     */
+    void roleActiveTrack(GameUserRole gameUserRole, Integer activeType);
 }

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

@@ -33,4 +33,12 @@ public interface IOrderService extends IService<Order> {
      * @return {@link PlatformOrderDTO}
      */
     PlatformOrderDTO getByOrderId(String orderId);
+
+    /**
+     * 生成订单id
+     *
+     * @param userId : 玩家id
+     * @return : 返回订单id
+     */
+    String getOrderNum(Long userId);
 }

+ 9 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IUserShareService.java

@@ -1,6 +1,7 @@
 package com.zanxiang.game.module.sdk.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.User;
 import com.zanxiang.game.module.mybatis.entity.UserShare;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 import com.zanxiang.game.module.sdk.pojo.vo.GameShareVO;
@@ -27,4 +28,12 @@ public interface IUserShareService extends IService<UserShare> {
      * @return {@link GameShareVO}
      */
     GameShareVO getShowCard(UserData userData);
+
+    /**
+     * 创建保存分享记录
+     *
+     * @param user     : 玩家信息
+     * @param userData : 用户数据
+     */
+    void createShareLog(User user, UserData userData);
 }

+ 3 - 3
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CallBackServiceImpl.java

@@ -113,14 +113,14 @@ public class CallBackServiceImpl implements ICallBackService {
     }
 
     @Override
-    public void roleCallBack(GameUserRole gameUserRole, UserData userData) {
+    public void roleCallBack(GameUserRole gameUserRole) {
         //判断游戏是否开启广告回传, 未开启, 不回传
-        GameExt gameExt = gameExtService.getByGameId(userData.getGameId());
+        GameExt gameExt = gameExtService.getByGameId(gameUserRole.getGameId());
         if (!Objects.equals(gameExt.getAdCallBackSwitch(), Boolean.TRUE)) {
             return;
         }
         //用户信息
-        User user = userService.getById(userData.getUserId());
+        User user = userService.getById(gameUserRole.getUserId());
         //用户渠道信息
         Agent agent = agentService.getAgentByChannel(user.getChannel());
         if (agent == null) {

+ 547 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CpPushDataServiceImpl.java

@@ -0,0 +1,547 @@
+package com.zanxiang.game.module.sdk.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.zanxiang.game.module.base.pojo.enums.BanStatusEnum;
+import com.zanxiang.game.module.base.pojo.enums.DeleteEnum;
+import com.zanxiang.game.module.mybatis.entity.*;
+import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
+import com.zanxiang.game.module.sdk.enums.DeviceTypeEnum;
+import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
+import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
+import com.zanxiang.game.module.sdk.pojo.param.*;
+import com.zanxiang.game.module.sdk.pojo.vo.CpPushResultVO;
+import com.zanxiang.game.module.sdk.pojo.vo.CpPushUserVO;
+import com.zanxiang.game.module.sdk.service.*;
+import com.zanxiang.game.module.sdk.util.RegisterUtil;
+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;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.logging.log4j.util.Strings;
+import org.json.JSONObject;
+import org.redisson.api.RLock;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import reactor.util.function.Tuple2;
+import reactor.util.function.Tuple3;
+import reactor.util.function.Tuples;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-20
+ * @description : CP推送数据
+ */
+@Slf4j
+@Service
+public class CpPushDataServiceImpl implements ICpPushDataService {
+
+    @Autowired
+    private IGameExtService gameExtService;
+
+    @Autowired
+    private IUserService userService;
+
+    @Autowired
+    private IAgentService agentService;
+
+    @Autowired
+    private IGameService gameService;
+
+    @Autowired
+    private ICallBackService callBackService;
+
+    @Autowired
+    private IUserAgentLogService userAgentLogService;
+
+    @Autowired
+    private IKafkaService kafkaService;
+
+    @Autowired
+    private IGameUserService gameUserService;
+
+    @Autowired
+    private IUserShareService userShareService;
+
+    @Autowired
+    private IGameUserRoleService gameUserRoleService;
+
+    @Autowired
+    private IGameServerService gameServerService;
+
+    @Autowired
+    private IOrderService orderService;
+
+    @Autowired
+    private IDistributedLockComponent distributedLockComponent;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public CpPushResultVO pushOrder(String gameAppId, CpPushOrderParam param) {
+        //查询游戏
+        Game game = this.getGameByGameAppId(gameAppId);
+        //查询订单信息
+        Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
+                .eq(Order::getGameId, game.getId())
+                .eq(Order::getCpOrderId, param.getOrderId()));
+        if (order == null) {
+            order = this.createOrder(game, param);
+        } else {
+            order.setStatus(param.getStatus());
+            order.setPayTime(param.getPayTime());
+            order.setUpdateTime(LocalDateTime.now());
+        }
+        //订单更新或者保存
+        orderService.saveOrUpdate(order);
+        //订单回传
+        callBackService.orderCallBack(BeanUtil.copy(order, PlatformOrderDTO.class));
+        //判断订单行为
+        if (Objects.equals(param.getStatus(), 2)) {
+            //支付
+            kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ORDER_PAY, JsonUtil.toString(BeanUtil.copy(order, PlatformOrderDTO.class)));
+        } else {
+            //下单
+            kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ORDER_PAY, JsonUtil.toString(order));
+        }
+        //构造返回
+        return CpPushResultVO.builder().result(Boolean.TRUE).build();
+    }
+
+    private Order createOrder(Game game, CpPushOrderParam param) {
+        String lockKey = RedisKeyConstant.ORDER_CREATE_LOCK + param.getOpenId() + "_" + param.getOrderId();
+        if (!distributedLockComponent.doLock(lockKey, 0L, 1L, TimeUnit.MINUTES)) {
+            log.error("订单正在创建中, param : {}", JsonUtil.toString(param));
+            throw new BaseException("接口调用订单正在创建中");
+        }
+        try {
+            //查询玩家信息
+            User user = userService.getOne(new LambdaQueryWrapper<User>()
+                    .eq(User::getOpenId, param.getOpenId())
+                    .eq(User::getGameId, game.getId()));
+            if (user == null) {
+                throw new BaseException("参数错误, 玩家信息不存在");
+            }
+            //玩家信息
+            GameUser gameUser = gameUserService.getOne(new LambdaQueryWrapper<GameUser>()
+                    .eq(GameUser::getUserId, user.getId()));
+            //角色信息
+            GameUserRole gameUserRole = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
+                    .eq(GameUserRole::getGameId, game.getId())
+                    .eq(GameUserRole::getUserId, user.getId())
+                    .eq(GameUserRole::getRoleId, param.getRoleId()));
+            if (gameUser == null || gameUserRole == null) {
+                log.error("游戏用户信息不全, gameUser : {}, gameUserRole : {}", JsonUtil.toString(gameUser), JsonUtil.toString(gameUserRole));
+                throw new BaseException("参数错误, 游戏用户信息不全");
+            }
+            //构造订单
+            return this.transform(game, param, user, gameUser, gameUserRole);
+        } catch (Exception e) {
+            log.error("订单数据推送创建订单异常, param : {}, e : {}", JsonUtil.toString(param), e.getMessage(), e);
+            throw new BaseException("订单数据推送创建订单异常");
+        } finally {
+            RLock rLock = distributedLockComponent.getLock(lockKey);
+            if (rLock != null) {
+                distributedLockComponent.unlock(lockKey);
+            }
+        }
+    }
+
+    private Order transform(Game game, CpPushOrderParam param, User user, GameUser gameUser, GameUserRole gameUserRole) {
+        return Order.builder()
+                .orderId(orderService.getOrderNum(user.getId()))
+                .cpOrderId(param.getOrderId())
+                .agentId(user.getAgentId())
+                .cpId(game.getCpId())
+                .userId(user.getId())
+                .mgUserId(gameUser.getId())
+                .regTime(user.getCreateTime())
+                .gameId(game.getId())
+                .roleId(gameUserRole.getRoleId())
+                .roleName(gameUserRole.getRoleName())
+                .roleLevel(gameUserRole.getRoleLevel())
+                .roleVipLevel(gameUserRole.getRoleVipLevel())
+                .serverId(gameUserRole.getServerId())
+                .serverName(gameUserRole.getServerName())
+                .amount(param.getAmount())
+                .productId(param.getProductId())
+                .productName(param.getProductName())
+                .deviceSystem(user.getDeviceSystem())
+                .payWayId(0L)
+                .payDeviceId(0L)
+                .gamePayWayId(0L)
+                .ext(param.getExtension())
+                .status(param.getStatus())
+                .payTime(param.getPayTime())
+                .createTime(param.getCreateTime())
+                .updateTime(LocalDateTime.now())
+                .build();
+    }
+
+    @Override
+    public CpPushResultVO pushActive(String gameAppId, CpPushActiveParam param) {
+        //查询游戏
+        Game game = this.getGameByGameAppId(gameAppId);
+        //查询玩家信息
+        User user = userService.getOne(new LambdaQueryWrapper<User>()
+                .eq(User::getOpenId, param.getOpenId())
+                .eq(User::getGameId, game.getId()));
+        if (user == null) {
+            throw new BaseException("参数错误, 玩家信息不存在");
+        }
+        //查询角色信息
+        GameUserRole gameUserRole = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
+                .eq(GameUserRole::getUserId, user.getId())
+                .eq(GameUserRole::getGameId, game.getId())
+                .eq(GameUserRole::getServerId, param.getServerId())
+                .eq(GameUserRole::getRoleId, param.getRoleId())
+                .orderByDesc(GameUserRole::getCreateTime)
+                .last("limit 1"));
+        if (gameUserRole == null) {
+            throw new BaseException("参数错误, 玩家角色信息不存在");
+        }
+        //角色活跃信息上报到卡夫卡
+        kafkaService.roleActiveTrack(gameUserRole, param.getActiveType());
+        //构造返回
+        return CpPushResultVO.builder().result(Boolean.TRUE).build();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public CpPushResultVO pushServer(String gameAppId, CpPushServerParam param) {
+        //查询游戏
+        Game game = this.getGameByGameAppId(gameAppId);
+        //查询区服id
+        GameServer gameServer = gameServerService.getOne(new LambdaQueryWrapper<GameServer>()
+                .eq(GameServer::getGameId, game.getSuperGameId())
+                .eq(GameServer::getServerId, param.getServerId())
+        );
+        GameServer transform = this.transform(game.getSuperGameId(), param);
+        if (gameServer == null) {
+            gameServer = transform;
+        } else {
+            gameServer.setGameId(transform.getGameId());
+            gameServer.setServerId(transform.getServerId());
+            gameServer.setServerName(transform.getServerName());
+            gameServer.setNickName(transform.getNickName());
+            gameServer.setStartTime(transform.getStartTime());
+            gameServer.setUpdateTime(LocalDateTime.now());
+        }
+        //区服添加或者更新
+        boolean result = gameServerService.saveOrUpdate(gameServer);
+        //构造返回
+        return CpPushResultVO.builder().result(result).build();
+    }
+
+    private GameServer transform(Long superGameId, CpPushServerParam param) {
+        return GameServer.builder()
+                .gameId(superGameId)
+                .serverId(param.getServerId())
+                .serverName(param.getServerName())
+                .nickName(param.getNickName())
+                .startTime(param.getStartTime())
+                .isDelete(DeleteEnum.NO.getCode())
+                .createBy(0L)
+                .createTime(LocalDateTime.now())
+                .isSourceServer(Boolean.TRUE)
+                .isMerge(Boolean.FALSE)
+                .updateBy(0L)
+                .updateTime(LocalDateTime.now())
+                .build();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public CpPushResultVO pushRole(String gameAppId, CpPushRoleParam param) {
+        //查询游戏
+        Game game = this.getGameByGameAppId(gameAppId);
+        //查询玩家信息
+        User user = userService.getOne(new LambdaQueryWrapper<User>()
+                .eq(User::getGameId, game.getId())
+                .eq(User::getOpenId, param.getOpenId()));
+        if (user == null) {
+            throw new BaseException("参数错误, 玩家信息不存在");
+        }
+        //查询玩家角色信息
+        GameUserRole gameUserRole = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
+                .eq(GameUserRole::getUserId, user.getId())
+                .eq(GameUserRole::getGameId, user.getGameId())
+                .eq(GameUserRole::getRoleId, param.getRoleId()));
+        boolean result;
+        if (gameUserRole == null) {
+            result = this.gameRoleCreate(param, user);
+        } else {
+            result = this.gameRoleUpdate(param, gameUserRole, user);
+        }
+        //构造返回
+        return CpPushResultVO.builder().result(result).build();
+    }
+
+    private boolean gameRoleCreate(CpPushRoleParam param, User user) {
+        GameUser gameUser = gameUserService.getOne(new LambdaQueryWrapper<GameUser>()
+                .select(GameUser::getId)
+                .eq(GameUser::getGameId, user.getGameId())
+                .eq(GameUser::getUserId, user.getId()));
+        if (gameUser == null) {
+            throw new BaseException("参数错误, 玩家游戏信息不存在");
+        }
+        //上锁
+        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_CREATE_LOCK + user.getGameId() + "_" + param.getRoleId(),
+                0L, 1L, TimeUnit.MINUTES)) {
+            return Boolean.TRUE;
+        }
+        //创建角色
+        GameUserRole userRole = this.transform(param, gameUser, user);
+        boolean result = gameUserRoleService.save(userRole);
+        //更新用户创角数
+        userService.update(new LambdaUpdateWrapper<User>()
+                .setSql("role_count=role_count+" + 1)
+                .set(User::getUpdateTime, LocalDateTime.now())
+                .eq(User::getId, user.getId()));
+        //更新玩家创角数
+        gameUserService.update(new LambdaUpdateWrapper<GameUser>()
+                .setSql("role_count=role_count+" + 1)
+                .set(GameUser::getUpdateTime, LocalDateTime.now())
+                .eq(GameUser::getId, gameUser.getId()));
+        //用户创角回传
+        callBackService.roleCallBack(userRole);
+        //用户创角埋点数据发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ROLE_CREATE, JsonUtil.toString(userRole));
+        return result;
+    }
+
+    private boolean gameRoleUpdate(CpPushRoleParam param, GameUserRole gameUserRole, User user) {
+        //更新频率限制, 20秒更新一次, 避免游戏实时战力高频上报
+        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_LEVEL_UP + user.getGameId() + "_" + param.getRoleId(),
+                0L, 20L, TimeUnit.SECONDS)) {
+            return Boolean.TRUE;
+        }
+        //玩家角色信息更新
+        if (param.getExtra() != null && Strings.isNotBlank(JsonUtil.toString(param.getExtra()))) {
+            gameUserRole.setExtra(JsonUtil.toString(param.getExtra()));
+        }
+        if (gameUserRole.getServerId() == null) {
+            gameUserRole.setServerId(param.getServerId());
+        }
+        if (param.getRolePower() != null) {
+            gameUserRole.setRolePower(param.getRolePower());
+        }
+        if (param.getRoleVipLevel() != null) {
+            gameUserRole.setRoleVipLevel(param.getRoleVipLevel());
+        }
+        gameUserRole.setRoleName(param.getRoleName());
+        gameUserRole.setRoleLevel(param.getRoleLevel());
+        gameUserRole.setServerName(param.getServerName());
+        gameUserRole.setUpdateTime(LocalDateTime.now());
+        boolean result = gameUserRoleService.updateById(gameUserRole);
+        //角色更新数据埋点发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ROLE_UPDATE, JsonUtil.toString(gameUserRole));
+        return result;
+    }
+
+    private GameUserRole transform(CpPushRoleParam param, GameUser gameUser, User user) {
+        String extraJson = JsonUtil.toString(param.getExtra());
+        return GameUserRole.builder()
+                .userId(user.getId())
+                .gameUserId(gameUser.getId())
+                .gameId(user.getGameId())
+                .serverId(param.getServerId())
+                .serverName(param.getServerName())
+                .roleId(param.getRoleId())
+                .roleName(param.getRoleName())
+                .roleLevel(param.getRoleLevel())
+                .roleVipLevel(param.getRoleVipLevel())
+                .rolePower(param.getRolePower())
+                .os(user.getDeviceSystem())
+                .regTime(user.getCreateTime())
+                .createTime(param.getCreateTime())
+                .updateTime(LocalDateTime.now())
+                .lastLoginTime(LocalDateTime.now())
+                .extra(param.getExtra() == null || Strings.isBlank(extraJson) ? null : extraJson)
+                .build();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public CpPushUserVO pushUser(String gameAppId, CpPushUserParam param) {
+        //渠道参数判断
+        String channel = param.getChannel();
+        Tuple2<Boolean, Boolean> jsonAndEmpty = this.isJsonAndEmpty(channel);
+        //非json, 参数错误
+        if (!jsonAndEmpty.getT1()) {
+            throw new BaseException("参数错误, channel字段非json格式");
+        }
+        //查询游戏
+        Game game = this.getGameByGameAppId(gameAppId);
+        //中间数据
+        UserData userData = UserData.builder().channel(param.getChannel()).ip(param.getIp()).ua(param.getUa()).build();
+        //根据openId查询用户
+        User user = userService.getOne(new LambdaQueryWrapper<User>()
+                .eq(User::getGameId, game.getId())
+                .eq(User::getOpenId, param.getOpenId()));
+        //玩家信息不存在
+        if (user == null) {
+            return this.createUser(game, userData, param, jsonAndEmpty.getT2());
+        }
+        //玩家信息更新
+        Map<String, String> channelMap = this.updateUser(user, param, game, userData, jsonAndEmpty.getT2());
+        //构造返回
+        return CpPushUserVO.builder().userId(user.getId())
+                .sdkUser(!Objects.equals(user.getAgentId(), Agent.DEFAULT_AGENT))
+                .channel(JsonUtil.toString(channelMap))
+                .build();
+    }
+
+    private CpPushUserVO createUser(Game game, UserData userData, CpPushUserParam param, boolean isEmptyJson) {
+        //游戏id
+        Long gameId = game.getId();
+        //线程锁Key
+        String lockKey = RedisKeyConstant.USER_CREATE + gameId + "_" + param.getOpenId();
+        //上锁
+        if (!distributedLockComponent.doLock(lockKey, 0L, 3L, TimeUnit.MINUTES)) {
+            throw new BaseException("用户信息正在注册中, 请勿重复请求!");
+        }
+        //玩家信息
+        User user;
+        //渠道map
+        Map<String, String> channelMap = null;
+        try {
+            //判断是否分享玩家
+            if (Strings.isNotBlank(param.getShareOpenId())) {
+                User shareUser = userService.getOne(new LambdaQueryWrapper<User>()
+                        .eq(User::getGameId, gameId)
+                        .eq(User::getOpenId, param.getShareOpenId())
+                        .last("limit 1"));
+                user = this.transform(gameId, shareUser.getAgentId(), shareUser, param);
+            } else {
+                Tuple2<Long, Map<String, String>> tuple2 = this.getUserAgentChannel(game, userData, isEmptyJson);
+                user = this.transform(gameId, tuple2.getT1(), null, param);
+                channelMap = tuple2.getT2();
+            }
+            userService.save(user);
+            gameUserService.createGameUser(user);
+            userAgentLogService.regAgentLog(user);
+            //非分享用户, 回传注册
+            if (Strings.isBlank(param.getShareOpenId())) {
+                callBackService.userCallBack(user, channelMap);
+            } else {
+                //记录分享信息, 用户不回传
+                userShareService.createShareLog(user, userData);
+            }
+            //注册信息埋点数据发送到卡夫卡
+            kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_REG, JsonUtil.toString(user));
+            //返回构造数据
+            return CpPushUserVO.builder().userId(user.getId())
+                    .sdkUser(!Objects.equals(user.getAgentId(), Agent.DEFAULT_AGENT))
+                    .channel(JsonUtil.toString(channelMap))
+                    .build();
+        } catch (Exception e) {
+            log.error("玩家信息推送数据处理异常, gameId : {}, param : {}, e : {}", gameId, JsonUtil.toString(param), e.getMessage(), e);
+            throw new BaseException("玩家信息推送数据处理异常");
+        } finally {
+            RLock rLock = distributedLockComponent.getLock(lockKey);
+            if (rLock != null) {
+                distributedLockComponent.unlock(lockKey);
+            }
+        }
+    }
+
+    private Map<String, String> updateUser(User user, CpPushUserParam param, Game game, UserData userData, boolean isEmptyJson) {
+        //活跃间隔时长
+        long inactiveDay = 15L;
+        //判定不活跃时长是否达到限制
+        if (param.getActiveTime().plusDays(inactiveDay).isAfter(LocalDateTime.now())) {
+            return Collections.emptyMap();
+        }
+        //如果没有携带参数, 不判定重新买量
+        if (!param.getChannel().contains("clue_token")) {
+            return Collections.emptyMap();
+        }
+        //渠道id, 链接参数
+        Tuple2<Long, Map<String, String>> tuple2 = this.getUserAgentChannel(game, userData, isEmptyJson);
+        //查询渠道
+        Agent agent = agentService.getById(tuple2.getT1());
+        if (agent == null) {
+            return Collections.emptyMap();
+        }
+        //更新用户信息
+        user.setAgentId(agent.getId());
+        user.setChannel(userData.getChannel());
+        user.setUpdateTime(LocalDateTime.now());
+        userService.updateById(user);
+        //添加渠道变更记录
+        userAgentLogService.agentUpdateLog(user, agent.getId(), userData.getChannel());
+        //回传用户信息
+        callBackService.userCallBack(user, tuple2.getT2());
+        //渠道变更信息埋点数据发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_AGENT_UPDATE, JsonUtil.toString(user));
+        //返回更新的渠道信息
+        return tuple2.getT2();
+    }
+
+    private Tuple2<Long, Map<String, String>> getUserAgentChannel(Game game, UserData userData, boolean isEmptyJson) {
+        //传入的是空json, 走监测链接匹配
+        if (isEmptyJson) {
+            //todo : 走监测链接匹配
+            return Tuples.of(0L, Collections.emptyMap());
+        }
+        //渠道id, 链接参数, 分享人id
+        Tuple3<Long, Map<String, String>, String> tuple3 = agentService.getUserAgentId(game, userData);
+        //包含 clue_token 但是未匹配到渠道, 防止 agentKey 参数丢失的情况 , 走监测链接补偿匹配
+        if (Objects.equals(tuple3.getT1(), Agent.DEFAULT_AGENT) && tuple3.getT2().containsKey("clue_token")) {
+            //todo : 走监测链接匹配
+            return Tuples.of(0L, Collections.emptyMap());
+        }
+        return Tuples.of(tuple3.getT1(), tuple3.getT2());
+    }
+
+    private User transform(Long gameId, Long agentId, User shareUser, CpPushUserParam param) {
+        return User.builder()
+                .regAgentId(agentId)
+                .agentId(agentId)
+                .gameId(gameId)
+                .username(param.getOpenId())
+                .nickname(RegisterUtil.randomNickName(param.getOpenId()))
+                .deviceType(DeviceTypeEnum.DEVICE_TYPE_MINI_APP.getDeviceType())
+                .status(BanStatusEnum.NORMAL_STATUS.getStatus())
+                .authentication(0)
+                .createTime(param.getRegTime())
+                .updateTime(LocalDateTime.now())
+                .deviceSystem(param.getOs())
+                .ip(param.getIp())
+                .shareUserId(shareUser == null ? null : shareUser.getId())
+                .build();
+    }
+
+    private Tuple2<Boolean, Boolean> isJsonAndEmpty(String str) {
+        try {
+            JSONObject jsonObject = new JSONObject(str);
+            return Tuples.of(Boolean.TRUE, jsonObject.keySet().isEmpty());
+        } catch (Exception e) {
+            return Tuples.of(Boolean.FALSE, Boolean.FALSE);
+        }
+    }
+
+    private Game getGameByGameAppId(String gameAppId) {
+        //游戏拓展信息
+        GameExt gameExt = gameExtService.getByGameAppId(gameAppId);
+        if (gameExt == null) {
+            log.error("参数错误, 游戏拓展信息配置不存在, gameAppId : {}", gameAppId);
+            throw new BaseException("参数错误, 游戏拓展信息配置不存在");
+        }
+        //查询游戏
+        Game game = gameService.getById(gameExt.getGameId());
+        if (game == null) {
+            log.error("参数错误, 游戏配置不存在, gameAppId : {}", gameAppId);
+            throw new BaseException("参数错误, 游戏配置不存在");
+        }
+        return game;
+    }
+}

+ 18 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameServerServiceImpl.java

@@ -0,0 +1,18 @@
+package com.zanxiang.game.module.sdk.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.mybatis.entity.GameServer;
+import com.zanxiang.game.module.mybatis.mapper.GameServerMapper;
+import com.zanxiang.game.module.sdk.service.IGameServerService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-08-07
+ * @description : 游戏区服
+ */
+@Slf4j
+@Service
+public class GameServerServiceImpl extends ServiceImpl<GameServerMapper, GameServer> implements IGameServerService {
+}

+ 18 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameSupperServiceImpl.java

@@ -0,0 +1,18 @@
+package com.zanxiang.game.module.sdk.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.mybatis.entity.GameSupper;
+import com.zanxiang.game.module.mybatis.mapper.GameSupperMapper;
+import com.zanxiang.game.module.sdk.service.IGameSupperService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-09-12
+ * @description : 超父游戏
+ */
+@Slf4j
+@Service
+public class GameSupperServiceImpl extends ServiceImpl<GameSupperMapper, GameSupper> implements IGameSupperService {
+}

+ 1 - 3
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java

@@ -22,8 +22,6 @@ 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.context.ApplicationContext;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.client.RestTemplate;
@@ -265,7 +263,7 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
                 .set(User::getUpdateTime, LocalDateTime.now())
                 .eq(User::getId, user.getId()));
         //用户创角回传
-        callBackService.roleCallBack(userRole, userData);
+        callBackService.roleCallBack(userRole);
         //用户创角埋点数据发送到卡夫卡
         kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ROLE_CREATE, JsonUtil.toString(userRole));
         //创建角色通知监听服务

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

@@ -1,5 +1,6 @@
 package com.zanxiang.game.module.sdk.service.impl;
 
+import com.zanxiang.game.module.mybatis.entity.GameUserRole;
 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;
@@ -63,4 +64,23 @@ public class KafkaServiceImpl implements IKafkaService {
                     this.gameRoleActiveTopic, JsonUtil.toString(activeParamMap), e.getMessage());
         }
     }
+
+    @Override
+    public void roleActiveTrack(GameUserRole gameUserRole, Integer activeType) {
+        //活跃提交
+        Map<String, Object> activeParamMap = new HashMap<>(6);
+        activeParamMap.put("userId", gameUserRole.getUserId());
+        activeParamMap.put("gameId", gameUserRole.getGameId());
+        activeParamMap.put("roleId", gameUserRole.getRoleId());
+        activeParamMap.put("serverId", gameUserRole.getServerId());
+        activeParamMap.put("roleLevel", gameUserRole.getRoleLevel());
+        activeParamMap.put("activeType", activeType);
+        activeParamMap.put("activeTime", System.currentTimeMillis());
+        try {
+            kafkaProducer.send(new ProducerRecord<>(this.gameRoleActiveTopic, gameUserRole.getUserId().toString(), JsonUtil.toString(activeParamMap)));
+        } catch (Exception e) {
+            log.error("玩家角色活上下线跃信息发送到 Kafka 异常!, eventTrackTopic : {}, data : {}, e : {}",
+                    this.gameRoleActiveTopic, JsonUtil.toString(activeParamMap), e.getMessage());
+        }
+    }
 }

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

@@ -5,7 +5,10 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.zanxiang.game.module.base.pojo.enums.BanStatusEnum;
 import com.zanxiang.game.module.base.pojo.enums.GameCategoryEnum;
 import com.zanxiang.game.module.base.pojo.enums.HttpStatusEnum;
-import com.zanxiang.game.module.mybatis.entity.*;
+import com.zanxiang.game.module.mybatis.entity.Game;
+import com.zanxiang.game.module.mybatis.entity.GameExt;
+import com.zanxiang.game.module.mybatis.entity.User;
+import com.zanxiang.game.module.mybatis.entity.UserCard;
 import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
 import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
 import com.zanxiang.game.module.sdk.enums.LoginTypeEnum;
@@ -308,18 +311,13 @@ public class LoginServiceImpl implements IRegisterLoginService {
         //非分享用户, 回传注册
         if (shareUserId == null) {
             callBackService.userCallBack(user, tuple3.getT2());
-            return user;
+        } else {
+            //分享用户,记录分享信息, 用户不回传
+            userShareService.createShareLog(user, userData);
         }
-        //记录分享信息, 用户不回传
-        userShareService.save(UserShare.builder()
-                .gameId(userData.getGameId())
-                .fromUserId(shareUserId)
-                .toUserId(user.getId())
-                .createTime(LocalDateTime.now())
-                .updateTime(LocalDateTime.now())
-                .build());
         //注册信息埋点数据发送到卡夫卡
         kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_REG, JsonUtil.toString(user));
+        //返回用户信息
         return user;
     }
 

+ 2 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/OrderServiceImpl.java

@@ -119,7 +119,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
                 .build();
     }
 
-    private String getOrderNum(Long userId) {
+    @Override
+    public String getOrderNum(Long userId) {
         //时间(精确到毫秒)
         DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
         String localDate = LocalDateTime.now().format(ofPattern);

+ 14 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserShareServiceImpl.java

@@ -1,6 +1,7 @@
 package com.zanxiang.game.module.sdk.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.mybatis.entity.User;
 import com.zanxiang.game.module.mybatis.entity.UserShare;
 import com.zanxiang.game.module.mybatis.mapper.UserShareMapper;
 import com.zanxiang.game.module.sdk.pojo.dto.GameAppletDTO;
@@ -12,6 +13,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
+
 /**
  * @author : lingfeng
  * @time : 2023-01-16
@@ -47,4 +50,15 @@ public class UserShareServiceImpl extends ServiceImpl<UserShareMapper, UserShare
                 .path(gameAppletDTO.getSharePath())
                 .build();
     }
+
+    @Override
+    public void createShareLog(User user, UserData userData) {
+        super.save(UserShare.builder()
+                .gameId(userData.getGameId())
+                .fromUserId(user.getShareUserId())
+                .toUserId(user.getId())
+                .createTime(LocalDateTime.now())
+                .updateTime(LocalDateTime.now())
+                .build());
+    }
 }