瀏覽代碼

Merge remote-tracking branch 'origin/package-lingfeng' into package

bilingfeng 9 月之前
父節點
當前提交
8c690fd6fc
共有 50 個文件被更改,包括 2084 次插入56 次删除
  1. 6 0
      game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TtAppOrderBackQueryRpcDTO.java
  2. 6 0
      game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TtAppRoleRegisterBackQueryRpcDTO.java
  3. 6 0
      game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TtAppUserBackQueryRpcDTO.java
  4. 84 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/CpPushErrorLog.java
  5. 16 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/Game.java
  6. 15 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/User.java
  7. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/CpPushErrorLogMapper.java
  8. 1 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java
  9. 3 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/adapter/ArgumentAdapter.java
  10. 50 2
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/adapter/WebHandlerAdapter.java
  11. 14 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/annotation/PushDataCheck.java
  12. 14 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/constant/RedisKeyConstant.java
  13. 50 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/CallBackController.java
  14. 53 2
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/PushController.java
  15. 39 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/enums/CallBackTypeEnum.java
  16. 44 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/enums/CpPushDataEnum.java
  17. 31 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CallBackControlParam.java
  18. 32 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushActiveParam.java
  19. 70 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushOrderParam.java
  20. 73 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushRoleParam.java
  21. 34 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushServerParam.java
  22. 62 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CpPushUserParam.java
  23. 15 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/UserData.java
  24. 23 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/vo/CpPushResultVO.java
  25. 33 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/vo/CpPushUserVO.java
  26. 6 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/vo/UserLoginVO.java
  27. 11 2
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICallBackService.java
  28. 58 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICpPushDataService.java
  29. 32 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICpPushErrorLogService.java
  30. 12 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameServerService.java
  31. 11 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameService.java
  32. 12 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameSupperService.java
  33. 9 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IKafkaService.java
  34. 8 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IOrderService.java
  35. 9 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IUserShareService.java
  36. 5 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/AgentServiceImpl.java
  37. 234 21
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CallBackServiceImpl.java
  38. 648 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CpPushDataServiceImpl.java
  39. 57 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CpPushErrorLogServiceImpl.java
  40. 18 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameServerServiceImpl.java
  41. 21 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameServiceImpl.java
  42. 18 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameSupperServiceImpl.java
  43. 1 3
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java
  44. 20 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/KafkaServiceImpl.java
  45. 19 12
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java
  46. 2 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/OrderServiceImpl.java
  47. 8 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/PerformOrderServiceImpl.java
  48. 14 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserShareServiceImpl.java
  49. 18 11
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserTokenServiceImpl.java
  50. 47 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/task/CpPushDataTask.java

+ 6 - 0
game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TtAppOrderBackQueryRpcDTO.java

@@ -1,6 +1,9 @@
 package com.zanxiang.game.back.base.pojo.dto;
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import java.io.Serializable;
 
@@ -8,6 +11,9 @@ import java.io.Serializable;
  * 创角是否回传
  */
 @Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
 public class TtAppOrderBackQueryRpcDTO implements Serializable {
     private static final long serialVersionUID = 1L;
     /**

+ 6 - 0
game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TtAppRoleRegisterBackQueryRpcDTO.java

@@ -1,6 +1,9 @@
 package com.zanxiang.game.back.base.pojo.dto;
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import java.io.Serializable;
 
@@ -8,6 +11,9 @@ import java.io.Serializable;
  * 创角是否回传
  */
 @Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 public class TtAppRoleRegisterBackQueryRpcDTO implements Serializable {
     private static final long serialVersionUID = 1L;
 

+ 6 - 0
game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TtAppUserBackQueryRpcDTO.java

@@ -1,6 +1,9 @@
 package com.zanxiang.game.back.base.pojo.dto;
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import java.io.Serializable;
 
@@ -8,6 +11,9 @@ import java.io.Serializable;
  * 创角是否回传
  */
 @Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
 public class TtAppUserBackQueryRpcDTO implements Serializable {
     private static final long serialVersionUID = 1L;
     /**

+ 84 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/CpPushErrorLog.java

@@ -0,0 +1,84 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-27
+ * @description : CP推送数据错误日志
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_cp_push_error_log")
+public class CpPushErrorLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public final static Integer STATUS_VALID = 0;
+    public final static Integer STATUS_INVALID = 1;
+
+    /**
+     * id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 数据分类
+     */
+    private String dataType;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 玩家应用唯一标识
+     */
+    private String openId;
+
+    /**
+     * 订单id
+     */
+    private String orderId;
+
+    /**
+     * 区服id
+     */
+    private String serverId;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 请求参数
+     */
+    private String param;
+
+    /**
+     * 异常消息
+     */
+    private String errorMsg;
+
+    /**
+     * 有效状态, 0 : 有效, 1 : 无效,可删除
+     */
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+}

+ 16 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/Game.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.*;
 import lombok.*;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 /**
@@ -107,6 +108,11 @@ public class Game implements Serializable {
      */
     private String h5GameConfig;
 
+    /**
+     * 广告媒体sdk配置
+     */
+    private String adSdkConfig;
+
     /**
      * 超父游戏id
      */
@@ -137,4 +143,14 @@ public class Game implements Serializable {
      */
     private String appType;
 
+    /**
+     * 是否虚拟游戏
+     */
+    private Boolean isUnrealGame;
+
+    /**
+     * 充值折扣
+     */
+    private BigDecimal rechargeRebate;
+
 }

+ 15 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/User.java

@@ -151,11 +151,26 @@ public class User implements Serializable {
      */
     private String imei;
 
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
     /**
      * 安卓id, (仅安卓设备才有值)
      */
     private String androidId;
 
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+
     /**
      * 注册时间
      */

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

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.module.mybatis.entity.CpPushErrorLog;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-27
+ * @description : CP推送数据错误日志
+ */
+public interface CpPushErrorLogMapper extends BaseMapper<CpPushErrorLog> {
+}

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

+ 3 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/adapter/ArgumentAdapter.java

@@ -72,7 +72,10 @@ public class ArgumentAdapter implements HandlerMethodArgumentResolver {
                 .deviceSystem(request.getHeader("os"))
                 .mac(request.getHeader("mac"))
                 .imei(request.getHeader("imei"))
+                .oaid(request.getHeader("oaid"))
                 .androidId(request.getHeader("androidId"))
+                .idfa(request.getHeader("idfa"))
+                .caid(request.getHeader("caid"))
                 .channel(request.getHeader("channel"))
                 .build();
         //不需要登录, token不存在

+ 50 - 2
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.PushDataCheck;
 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,9 +37,13 @@ public class WebHandlerAdapter implements HandlerInterceptor {
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         HandlerMethod handlerMethod = (HandlerMethod) handler;
-        //排除签名认证接口注解
-        UnSignCheck unSignCheck = handlerMethod.getMethod().getAnnotation(UnSignCheck.class);
+        //数据推送请求加密验证
+        PushDataCheck pushDataCheck = handlerMethod.getMethod().getAnnotation(PushDataCheck.class);
+        if (pushDataCheck != null) {
+            this.pushCheck(request);
+        }
         //接口签名验证
+        UnSignCheck unSignCheck = handlerMethod.getMethod().getAnnotation(UnSignCheck.class);
         if (unSignCheck == null) {
             return this.signCheck(request);
         }
@@ -77,4 +82,47 @@ 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)) {
+            log.error("参数非法, 缺少必传参数");
+            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("非法参数, pushCheck签名错误, 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/PushDataCheck.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 PushDataCheck {
+}

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

@@ -72,4 +72,18 @@ 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_";
+
+    /**
+     * 回调判断锁
+     */
+    public static final String CALL_BACK_JUDGE_LOCK = RedisKeyConstant.REDIS_PREFIX + "call_back_judge_lock_";
+
+    /**
+     * CP推送数据错误日志删除锁
+     */
+    public static final String CP_PUSH_ERROR_LOG_LOCK = RedisKeyConstant.REDIS_PREFIX + "cp_push_error_log_lock";
 }

+ 50 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/CallBackController.java

@@ -0,0 +1,50 @@
+package com.zanxiang.game.module.sdk.controller;
+
+import com.zanxiang.game.module.sdk.annotation.ValidLogin;
+import com.zanxiang.game.module.sdk.pojo.param.CallBackControlParam;
+import com.zanxiang.game.module.sdk.pojo.param.UserData;
+import com.zanxiang.game.module.sdk.service.ICallBackService;
+import com.zanxiang.game.module.sdk.service.IGameService;
+import com.zanxiang.module.util.pojo.ResultVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-26
+ * @description : 回传相关接口
+ */
+@Api(tags = "回传控制相关接口")
+@RestController
+@Slf4j
+@RequestMapping(value = "/api/ad/back")
+public class CallBackController {
+
+    @Autowired
+    private IGameService gameService;
+
+    @Autowired
+    private ICallBackService callBackService;
+
+    @ApiOperation(value = "获取游戏广告媒体回传sdk配置")
+    @GetMapping("/ad/sdk/config")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Map.class)})
+    public ResultVO<Map<String, Object>> getAdSdkConfig(UserData userData) {
+        return ResultVO.ok(gameService.getAdSdkConfig(userData));
+    }
+
+    @ApiOperation(value = "判断事件是否回传")
+    @PostMapping("/call/judge")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<Map<String, Object>> callBackJudge(@Validated @RequestBody CallBackControlParam param, @ValidLogin UserData userData) {
+        return ResultVO.ok(callBackService.callBackJudge(param, userData));
+    }
+}

+ 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.PushDataCheck;
 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));
     }
 
+    @UnSignCheck
+    @PushDataCheck
+    @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));
+    }
+
+    @UnSignCheck
+    @PushDataCheck
+    @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));
+    }
+
+    @UnSignCheck
+    @PushDataCheck
+    @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));
+    }
+
+    @UnSignCheck
+    @PushDataCheck
+    @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));
+    }
+
+    @UnSignCheck
+    @PushDataCheck
+    @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));
+    }
+
 }

+ 39 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/enums/CallBackTypeEnum.java

@@ -0,0 +1,39 @@
+package com.zanxiang.game.module.sdk.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-26
+ * @description : 回传类型枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum CallBackTypeEnum {
+
+    /**
+     * 注册(新创建账号)
+     */
+    CALL_BACK_REGISTER("CALL_BACK_REGISTER"),
+
+    /**
+     * 激活(创建角色)
+     */
+    CALL_BACK_ACTIVATE("CALL_BACK_ACTIVATE"),
+
+    /**
+     * 登录(老账号登录)
+     */
+    CALL_BACK_LOGIN_IN("CALL_BACK_LOGIN_IN"),
+
+    /**
+     * 支付
+     */
+    CALL_BACK_PAY_ORDER("CALL_BACK_PAY_ORDER");
+
+    /**
+     * 枚举值
+     */
+    private String value;
+}

+ 44 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/enums/CpPushDataEnum.java

@@ -0,0 +1,44 @@
+package com.zanxiang.game.module.sdk.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-27
+ * @description : CP推送数据类型枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum CpPushDataEnum {
+
+    /**
+     * 玩家数据
+     */
+    CP_PUSH_DATA_USER("CP_PUSH_DATA_USER"),
+
+    /**
+     * 玩家角色数据
+     */
+    CP_PUSH_DATA_ROLE("CP_PUSH_DATA_ROLE"),
+
+    /**
+     * 玩家角色订单数据
+     */
+    CP_PUSH_DATA_ORDER("CP_PUSH_DATA_ORDER"),
+
+    /**
+     * 区服数据
+     */
+    CP_PUSH_DATA_SERVER("CP_PUSH_DATA_SERVER"),
+
+    /**
+     * 玩家活跃数据
+     */
+    CP_PUSH_DATA_ACTIVE("CP_PUSH_DATA_ACTIVE");
+
+    /**
+     * 枚举值
+     */
+    private String value;
+}

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

@@ -0,0 +1,31 @@
+package com.zanxiang.game.module.sdk.pojo.param;
+
+import com.zanxiang.game.module.sdk.enums.CallBackTypeEnum;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-26
+ * @description : 回传控制请求参数
+ */
+@Data
+public class CallBackControlParam {
+
+    /**
+     * 用户行为回传类型
+     */
+    @NotNull(message = "用户行为回传类型不可为空")
+    private CallBackTypeEnum callBackTypeEnum;
+
+    /**
+     * 订单id, 当行为类型为下单, 支付时, 则订单id必传
+     */
+    private String orderId;
+
+    /**
+     * 角色id, 当行为类型为激活(创建角色)时, 需要传
+     */
+    private String roleId;
+}

+ 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;
+}

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

@@ -0,0 +1,62 @@
+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
+     */
+    private String ua;
+
+    /**
+     * 操作系统
+     */
+    @NotBlank(message = "操作系统不可为空")
+    private String os;
+
+    /**
+     * 分享玩家openId
+     */
+    private String shareOpenId;
+}

+ 15 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/UserData.java

@@ -70,11 +70,26 @@ public class UserData implements Serializable {
      */
     private String imei;
 
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
     /**
      * 安卓id, (仅安卓设备才有值)
      */
     private String androidId;
 
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+
     /**
      * 用户渠道唯一标识
      */

+ 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;
+}

+ 6 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/vo/UserLoginVO.java

@@ -88,4 +88,10 @@ public class UserLoginVO {
      */
     @ApiModelProperty(notes = "小游戏壳包控制开关")
     private Integer appletShellSwitch;
+
+    /**
+     * 是否注册用户
+     */
+    @ApiModelProperty(notes = "是否注册用户")
+    private Boolean regUser;
 }

+ 11 - 2
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICallBackService.java

@@ -3,6 +3,7 @@ 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.CallBackControlParam;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 
 import java.util.Map;
@@ -14,6 +15,15 @@ import java.util.Map;
  */
 public interface ICallBackService {
 
+    /**
+     * 回传判断
+     *
+     * @param param    : 参数
+     * @param userData : 玩家信息
+     * @return : 返回是否回传
+     */
+    Map<String, Object> callBackJudge(CallBackControlParam param, UserData userData);
+
     /**
      * 用户回传
      *
@@ -26,9 +36,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);
+}

+ 32 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/ICpPushErrorLogService.java

@@ -0,0 +1,32 @@
+package com.zanxiang.game.module.sdk.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.CpPushErrorLog;
+import com.zanxiang.game.module.sdk.enums.CpPushDataEnum;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-27
+ * @description : CP推送数据错误日志
+ */
+public interface ICpPushErrorLogService extends IService<CpPushErrorLog> {
+
+    /**
+     * CP推送数据错误日志保存
+     *
+     * @param gameId         : 游戏id
+     * @param cpPushDataEnum : 数据类型枚举
+     * @param param          : 参数
+     * @param errorMsg       : 错误信息
+     */
+    void createLog(Long gameId, CpPushDataEnum cpPushDataEnum, Object param, String errorMsg);
+
+    /**
+     * CP推送数据错误日志更新
+     *
+     * @param gameId         : 游戏id
+     * @param cpPushDataEnum : 数据类型枚举
+     * @param param          : 参数
+     */
+    void errorLogUpdate(Long gameId, CpPushDataEnum cpPushDataEnum, Object 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> {
+}

+ 11 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameService.java

@@ -2,6 +2,9 @@ package com.zanxiang.game.module.sdk.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zanxiang.game.module.mybatis.entity.Game;
+import com.zanxiang.game.module.sdk.pojo.param.UserData;
+
+import java.util.Map;
 
 /**
  * @author : xufeng
@@ -9,4 +12,12 @@ import com.zanxiang.game.module.mybatis.entity.Game;
  * @description : 游戏管理
  */
 public interface IGameService extends IService<Game> {
+
+    /**
+     * 获取游戏广告媒体sdk配置
+     *
+     * @param userData : 玩家信息
+     * @return : 返回配置
+     */
+    Map<String, Object> getAdSdkConfig(UserData userData);
 }

+ 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);
 }

+ 5 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/AgentServiceImpl.java

@@ -164,6 +164,11 @@ public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements
     private Tuple3<Long, Map<String, String>, String> appChannelTransform(Game game, UserData userData) {
         //游戏拓展信息
         GameExt gameExt = gameExtService.getByGameId(userData.getGameId());
+        //直投的APP, 必定兼容了媒体的回传sdk
+        if (Strings.isNotBlank(game.getAdSdkConfig())) {
+            Agent agent = super.getOne(new LambdaQueryWrapper<Agent>().eq(Agent::getAgentKey, userData.getChannel()));
+            return Tuples.of(agent == null ? 0 : agent.getId(), Collections.singletonMap("agentKey", userData.getChannel()), Strings.EMPTY);
+        }
         //不投放, 不回传, 只归因渠道, -- 不投放, 且回传只存在于导量情况, 导量无注册行为
         if (Objects.equals(game.getIsPut(), Boolean.FALSE) && Objects.equals(gameExt.getAdCallBackSwitch(), Boolean.FALSE)) {
             //查询访问记录

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

@@ -4,15 +4,21 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.zanxiang.game.back.base.ServerInfo;
 import com.zanxiang.game.back.base.pojo.dto.*;
+import com.zanxiang.game.back.base.pojo.vo.OrderBackQueryRpcVO;
 import com.zanxiang.game.back.base.rpc.ITencentMiniGameBackRpc;
 import com.zanxiang.game.back.base.rpc.ITencentUserActionBackRpc;
+import com.zanxiang.game.back.base.rpc.ITtAppBackRpc;
 import com.zanxiang.game.back.base.rpc.ITtMiniGameBackRpc;
 import com.zanxiang.game.module.base.pojo.enums.AccountTypeEnum;
 import com.zanxiang.game.module.base.pojo.enums.GameCategoryEnum;
 import com.zanxiang.game.module.mybatis.entity.*;
+import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
+import com.zanxiang.game.module.sdk.enums.CallBackTypeEnum;
 import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
+import com.zanxiang.game.module.sdk.pojo.param.CallBackControlParam;
 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 lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
@@ -21,8 +27,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author : lingfeng
@@ -42,6 +50,9 @@ public class CallBackServiceImpl implements ICallBackService {
     @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
     private ITtMiniGameBackRpc ttMiniGameBackRpc;
 
+    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
+    private ITtAppBackRpc ttAppBackRpc;
+
     @Autowired
     private IAgentService agentService;
 
@@ -57,9 +68,114 @@ public class CallBackServiceImpl implements ICallBackService {
     @Autowired
     private IGameExtService gameExtService;
 
+    @Autowired
+    private IOrderService orderService;
+
     @Autowired
     private IGameBackLogService gameBackLogService;
 
+    @Autowired
+    private IDistributedLockComponent distributedLockComponent;
+
+    @Override
+    public Map<String, Object> callBackJudge(CallBackControlParam param, UserData userData) {
+        log.error("事件回传判断, param : {},  userData : {}", JsonUtil.toString(param), JsonUtil.toString(userData));
+        Map<String, Object> resultMap = new HashMap<>(7);
+        resultMap.put("callBackTypeEnum", param.getCallBackTypeEnum().getValue());
+        resultMap.put("userId", userData.getUserId());
+        resultMap.put("gameId", userData.getGameId());
+        //事件类型枚举
+        CallBackTypeEnum callBackTypeEnum = param.getCallBackTypeEnum();
+        //线程锁
+        String lockKey = RedisKeyConstant.CALL_BACK_JUDGE_LOCK + callBackTypeEnum.getValue() + "_" + userData.getUserId();
+        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_ACTIVATE)) {
+            lockKey = lockKey + "_" + param.getRoleId();
+        }
+        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_PAY_ORDER)) {
+            lockKey = lockKey + "_" + param.getOrderId();
+        }
+        //上锁
+        if (!distributedLockComponent.doLock(lockKey, 0L, 1L, TimeUnit.MINUTES)) {
+            log.error("重复请求触发线程锁, 直接返回false, lockKey : {}", lockKey);
+            resultMap.put("callBack", Boolean.FALSE);
+            return resultMap;
+        }
+        Game game = gameService.getById(userData.getGameId());
+        //判断游戏状态, 对接上包过程中, 全量回传
+        if (Objects.equals(game.getStatus(), 2)) {
+            resultMap.put("callBack", Boolean.TRUE);
+            if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_PAY_ORDER)) {
+                PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(param.getOrderId());
+                resultMap.put("amount", platformOrderDTO.getAmount());
+            }
+            return resultMap;
+        }
+        //查询玩家
+        User user = userService.getById(userData.getUserId());
+        //不存在渠道
+        Agent agent = agentService.getById(user.getAgentId());
+        if (agent == null) {
+            resultMap.put("callBack", Boolean.FALSE);
+            return resultMap;
+        }
+        //判断回传
+        try {
+            this.checkCallBack(user.getId(), agent, callBackTypeEnum, resultMap, param);
+        } catch (Exception e) {
+            log.error("事件回传判断, 出现异常, param : {},  userData : {}, e : {}", JsonUtil.toString(param),
+                    JsonUtil.toString(userData), e.getMessage(), e);
+            resultMap.put("callBack", Boolean.FALSE);
+        }
+        //返回结果
+        log.error("事件回传判断, param : {}, 返回结果 resultMap : {}", JsonUtil.toString(param), JsonUtil.toString(resultMap));
+        return resultMap;
+    }
+
+    private void checkCallBack(Long userId, Agent agent, CallBackTypeEnum callBackTypeEnum, Map<String, Object> resultMap,
+                               CallBackControlParam param) {
+        switch (callBackTypeEnum) {
+            case CALL_BACK_REGISTER:
+                // 注册
+                TtAppUserBackQueryRpcDTO userTransform = this.transform(userId, agent);
+                resultMap.put("callBack", ttAppBackRpc.userBackQuery(userTransform).getData());
+                break;
+            case CALL_BACK_LOGIN_IN:
+                // 登录
+                resultMap.put("callBack", Boolean.FALSE);
+                break;
+            case CALL_BACK_ACTIVATE:
+                // 激活(创角)
+                if (Strings.isBlank(param.getRoleId())) {
+                    resultMap.put("callBack", Boolean.FALSE);
+                    resultMap.put("roleId", "unknown");
+                    break;
+                }
+                TtAppRoleRegisterBackQueryRpcDTO roleTransform = this.transform(userId, agent, param.getRoleId());
+                resultMap.put("roleId", param.getRoleId());
+                resultMap.put("callBack", ttAppBackRpc.roleRegisterBackQuery(roleTransform).getData());
+                break;
+            case CALL_BACK_PAY_ORDER:
+                // 支付
+                if (Strings.isBlank(param.getOrderId())) {
+                    resultMap.put("callBack", Boolean.FALSE);
+                    resultMap.put("orderId", "unknown");
+                    break;
+                }
+                TtAppOrderBackQueryRpcDTO orderTransform = this.transform(userId, param.getOrderId(), agent);
+                OrderBackQueryRpcVO orderBackQueryRpcVO = ttAppBackRpc.orderBackQuery(orderTransform).getData();
+                resultMap.put("orderId", param.getOrderId());
+                resultMap.put("callBack", orderBackQueryRpcVO.getDoBack());
+                resultMap.put("backMsg", orderBackQueryRpcVO.getBackMsg());
+                if (Objects.equals(orderBackQueryRpcVO.getDoBack(), Boolean.TRUE)) {
+                    resultMap.put("amount", orderBackQueryRpcVO.getBackMoney());
+                }
+                break;
+            default:
+                // 处理未知类型
+                resultMap.put("callBack", Boolean.FALSE);
+        }
+    }
+
     @Override
     public void userCallBack(User user, Map<String, String> urlParamMap) {
         //判断游戏是否开启广告回传, 未开启, 不回传
@@ -107,20 +223,26 @@ public class CallBackServiceImpl implements ICallBackService {
                     gameBackLogService.addLog(user.getId(), null, "头条用户回传提交", JsonUtil.toString(activeReportRpcDTO));
                 }
             }
+            //头条APP直投回传
+            if (CollectionUtils.isNotEmpty(urlParamMap) && Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE_APP.getValue())) {
+                TtUserActiveAppRpcDTO ttUserActiveAppRpcDTO = this.transform(user, agent);
+                ttAppBackRpc.userActiveReport(ttUserActiveAppRpcDTO);
+                gameBackLogService.addLog(user.getId(), null, "头条APP用户回传提交", JsonUtil.toString(ttUserActiveAppRpcDTO));
+            }
         } catch (Exception e) {
             log.error("用户回传异常, userId : {}, e : {}", user.getId(), e.getMessage());
         }
     }
 
     @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) {
@@ -153,6 +275,12 @@ public class CallBackServiceImpl implements ICallBackService {
                     gameBackLogService.addLog(user.getId(), gameUserRole.getRoleId(), "头条创角回传提交", JsonUtil.toString(ttRoleRegisterRpcDTO));
                 }
             }
+            //头条APP直投回传
+            if (Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE_APP.getValue())) {
+                TtRoleRegisterAppRpcDTO ttRoleRegisterAppRpcDTO = this.transform(user, agent, gameUserRole);
+                ttAppBackRpc.roleRegisterReport(ttRoleRegisterAppRpcDTO);
+                gameBackLogService.addLog(user.getId(), gameUserRole.getRoleId(), "头条APP创角回传提交", JsonUtil.toString(ttRoleRegisterAppRpcDTO));
+            }
         } catch (Exception e) {
             log.error("创角回传异常, userId : {}, e : {}", user.getId(), e.getMessage());
         }
@@ -203,6 +331,12 @@ public class CallBackServiceImpl implements ICallBackService {
                     gameBackLogService.addLog(user.getId(), platformOrderDTO.getOrderId(), "头条订单回传提交", JsonUtil.toString(ttOrderRpcDTO));
                 }
             }
+            //头条APP直投回传
+            if (Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE_APP.getValue())) {
+                TtOrderAppRpcDTO ttOrderAppRpcDTO = this.transform(platformOrderDTO, user, agent);
+                ttAppBackRpc.orderReport(ttOrderAppRpcDTO);
+                gameBackLogService.addLog(user.getId(), platformOrderDTO.getOrderId(), "头条APP订单回传提交", JsonUtil.toString(ttOrderAppRpcDTO));
+            }
         } catch (Exception e) {
             log.error("订单回传异常, orderId : {}, e : {}", platformOrderDTO.getOrderId(), e.getMessage());
         }
@@ -224,15 +358,10 @@ public class CallBackServiceImpl implements ICallBackService {
     }
 
     private TtRoleRegisterRpcDTO transform(User user, Agent agent, GameUserRole gameUserRole, GameApplet gameApplet) {
-        TtAccountRpcDTO ttAccountRpcDTO = TtAccountRpcDTO.builder()
-                .accountId(agent.getAccountId())
-                .reportToken(agent.getReportToken())
-                .reportUrl(agent.getReportUrl())
-                .build();
         return TtRoleRegisterRpcDTO.builder()
                 .gameId(agent.getGameId())
                 .backPolicyId(agent.getBackPolicyId())
-                .accountReport(ttAccountRpcDTO)
+                .accountReport(this.transform(agent))
                 .wechatAppId(gameApplet == null ? null : gameApplet.getAppId())
                 .wechatOpenId(user.getOpenId())
                 .agentKey(agent.getAgentKey())
@@ -256,16 +385,11 @@ public class CallBackServiceImpl implements ICallBackService {
     }
 
     private TtUserActiveRpcDTO transform(User user, Agent agent, GameApplet gameApplet, Map<String, String> urlParamMap) {
-        TtAccountRpcDTO ttAccountRpcDTO = TtAccountRpcDTO.builder()
-                .accountId(agent.getAccountId())
-                .reportToken(agent.getReportToken())
-                .reportUrl(agent.getReportUrl())
-                .build();
         return TtUserActiveRpcDTO.builder()
                 .gameId(agent.getGameId())
                 .wechatAppId(gameApplet == null ? null : gameApplet.getAppId())
                 .wechatOpenId(user.getOpenId())
-                .accountReport(ttAccountRpcDTO)
+                .accountReport(this.transform(agent))
                 .agentKey(agent.getAgentKey())
                 .backPolicyId(agent.getBackPolicyId())
                 .activeTime(user.getCreateTime())
@@ -298,14 +422,9 @@ public class CallBackServiceImpl implements ICallBackService {
     }
 
     private TtOrderRpcDTO transform(PlatformOrderDTO platformOrderDTO, String openId, Agent agent, GameApplet gameApplet, LocalDateTime regTime) {
-        TtAccountRpcDTO ttAccountRpcDTO = TtAccountRpcDTO.builder()
-                .accountId(agent.getAccountId())
-                .reportToken(agent.getReportToken())
-                .reportUrl(agent.getReportUrl())
-                .build();
         return TtOrderRpcDTO.builder()
                 .gameId(agent.getGameId())
-                .accountReport(ttAccountRpcDTO)
+                .accountReport(this.transform(agent))
                 .wechatAppId(gameApplet == null ? null : gameApplet.getAppId())
                 .wechatOpenId(openId)
                 .orderId(platformOrderDTO.getOrderId())
@@ -320,4 +439,98 @@ public class CallBackServiceImpl implements ICallBackService {
                 .roleName(platformOrderDTO.getRoleName())
                 .build();
     }
+
+    private TtAccountRpcDTO transform(Agent agent) {
+        return TtAccountRpcDTO.builder()
+                .accountId(agent.getAccountId())
+                .reportToken(agent.getReportToken())
+                .reportUrl(agent.getReportUrl())
+                .build();
+    }
+
+    private TtUserActiveAppRpcDTO transform(User user, Agent agent) {
+        return TtUserActiveAppRpcDTO.builder()
+                .gameId(agent.getGameId())
+                .accountReport(this.transform(agent))
+                .agentKey(agent.getAgentKey())
+                .activeTime(user.getCreateTime())
+                .backPolicyId(agent.getBackPolicyId())
+                .userId(user.getId().toString())
+                .mac(user.getMac())
+                .imei(user.getImei())
+                .oaid(user.getOaid())
+                .androidId(user.getAndroidId())
+                .idfa(user.getIdfa())
+                .caid(user.getCaid())
+                .build();
+    }
+
+    private TtOrderAppRpcDTO transform(PlatformOrderDTO platformOrderDTO, User user, Agent agent) {
+        return TtOrderAppRpcDTO.builder()
+                .gameId(agent.getGameId())
+                .accountReport(this.transform(agent))
+                .userId(user.getId().toString())
+                .orderId(platformOrderDTO.getOrderId())
+                .agentKey(agent.getAgentKey())
+                .backPolicyId(agent.getBackPolicyId())
+                .rechargeMoney(platformOrderDTO.getAmount().longValue() * 100)
+                .orderStatus(platformOrderDTO.getStatus())
+                .regTime(user.getCreateTime())
+                .createTime(platformOrderDTO.getCreateTime())
+                .payTime(platformOrderDTO.getPayTime())
+                .roleId(platformOrderDTO.getRoleId())
+                .roleName(platformOrderDTO.getRoleName())
+                .mac(user.getMac())
+                .imei(user.getImei())
+                .oaid(user.getOaid())
+                .androidId(user.getAndroidId())
+                .idfa(user.getIdfa())
+                .caid(user.getCaid())
+                .build();
+    }
+
+    private TtRoleRegisterAppRpcDTO transform(User user, Agent agent, GameUserRole gameUserRole) {
+        return TtRoleRegisterAppRpcDTO.builder()
+                .gameId(agent.getGameId())
+                .userId(user.getId().toString())
+                .backPolicyId(agent.getBackPolicyId())
+                .accountReport(this.transform(agent))
+                .agentKey(agent.getAgentKey())
+                .roleId(gameUserRole.getRoleId())
+                .roleName(gameUserRole.getRoleName())
+                .registerTime(gameUserRole.getCreateTime())
+                .mac(user.getMac())
+                .imei(user.getImei())
+                .oaid(user.getOaid())
+                .androidId(user.getAndroidId())
+                .idfa(user.getIdfa())
+                .caid(user.getCaid())
+                .build();
+    }
+
+    private TtAppUserBackQueryRpcDTO transform(Long userId, Agent agent) {
+        return TtAppUserBackQueryRpcDTO.builder()
+                .gameId(agent.getGameId())
+                .userId(userId.toString())
+                .agentKey(agent.getAgentKey())
+                .build();
+    }
+
+    private TtAppOrderBackQueryRpcDTO transform(Long userId, String orderId, Agent agent) {
+        return TtAppOrderBackQueryRpcDTO.builder()
+                .gameId(agent.getGameId())
+                .userId(userId.toString())
+                .orderId(orderId)
+                .agentKey(agent.getAgentKey())
+                .build();
+    }
+
+    private TtAppRoleRegisterBackQueryRpcDTO transform(Long userId, Agent agent, String roleId) {
+        return TtAppRoleRegisterBackQueryRpcDTO.builder()
+                .gameId(agent.getGameId())
+                .userId(userId.toString())
+                .agentKey(agent.getAgentKey())
+                .roleId(roleId)
+                .build();
+    }
 }

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

@@ -0,0 +1,648 @@
+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.CpPushDataEnum;
+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.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
+import reactor.util.function.Tuple2;
+import reactor.util.function.Tuple3;
+import reactor.util.function.Tuples;
+
+import java.time.LocalDateTime;
+import java.util.Base64;
+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 TransactionDefinition transactionDefinition;
+
+    @Autowired
+    private ICpPushErrorLogService cpPushErrorLogService;
+
+    @Autowired
+    private DataSourceTransactionManager dataSourceTransactionManager;
+
+    @Autowired
+    private IDistributedLockComponent distributedLockComponent;
+
+    @Override
+    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()));
+        // 手动开启事务
+        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
+        try {
+            if (order == null) {
+                order = this.createOrder(game, param);
+            } else {
+                order.setStatus(param.getStatus());
+                order.setPayTime(param.getPayTime());
+                order.setUpdateTime(LocalDateTime.now());
+            }
+            //订单更新或者保存
+            orderService.saveOrUpdate(order);
+            // 提交事务
+            dataSourceTransactionManager.commit(transactionStatus);
+        } catch (Exception e) {
+            // 回滚事务
+            dataSourceTransactionManager.rollback(transactionStatus);
+            //保存错误日志
+            cpPushErrorLogService.createLog(game.getId(), CpPushDataEnum.CP_PUSH_DATA_ORDER, param, e.getMessage());
+            //抛出异常
+            throw new BaseException(e.getMessage());
+        }
+        //订单回传
+        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));
+        }
+        //日志更新
+        cpPushErrorLogService.errorLogUpdate(game.getId(), CpPushDataEnum.CP_PUSH_DATA_ORDER, param);
+        //构造返回
+        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()));
+            if (gameUser == null) {
+                throw new BaseException("[订单推送]游戏玩家信息不存在");
+            }
+            //角色信息
+            GameUserRole gameUserRole = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
+                    .eq(GameUserRole::getGameId, game.getId())
+                    .eq(GameUserRole::getUserId, user.getId())
+                    .eq(GameUserRole::getRoleId, param.getRoleId()));
+            if (gameUserRole == null) {
+                throw new BaseException("[订单推送]角色信息不存在");
+            }
+            //构造订单
+            return this.transform(game, param, user, gameUser, gameUserRole);
+        } catch (Exception e) {
+            log.error("[订单推送]订单创建出现异常, game : {}, param : {}, e : {}", JsonUtil.toString(game), 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())
+                .realAmount(param.getStatus() == 2 ? param.getAmount() : null)
+                .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);
+        try {
+            //查询玩家信息
+            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());
+        } catch (Exception e) {
+            //保存错误日志
+            cpPushErrorLogService.createLog(game.getId(), CpPushDataEnum.CP_PUSH_DATA_ACTIVE, param, e.getMessage());
+            //抛出异常
+            throw new BaseException(e.getMessage());
+        }
+        //日志更新
+        cpPushErrorLogService.errorLogUpdate(game.getId(), CpPushDataEnum.CP_PUSH_DATA_ACTIVE, param);
+        //构造返回
+        return CpPushResultVO.builder().result(Boolean.TRUE).build();
+    }
+
+    @Override
+    public CpPushResultVO pushServer(String gameAppId, CpPushServerParam param) {
+        //查询游戏
+        Game game = this.getGameByGameAppId(gameAppId);
+        boolean result;
+        // 手动开启事务
+        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
+        try {
+            //查询区服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());
+            }
+            //区服添加或者更新
+            result = gameServerService.saveOrUpdate(gameServer);
+            //提交事务
+            dataSourceTransactionManager.commit(transactionStatus);
+        } catch (Exception e) {
+            //回滚事务
+            dataSourceTransactionManager.rollback(transactionStatus);
+            //保存错误日志
+            cpPushErrorLogService.createLog(game.getId(), CpPushDataEnum.CP_PUSH_DATA_SERVER, param, e.getMessage());
+            //抛出异常
+            throw new BaseException(e.getMessage());
+        }
+        //日志更新
+        cpPushErrorLogService.errorLogUpdate(game.getId(), CpPushDataEnum.CP_PUSH_DATA_SERVER, param);
+        //构造返回
+        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
+    public CpPushResultVO pushRole(String gameAppId, CpPushRoleParam param) {
+        //查询游戏
+        Game game = this.getGameByGameAppId(gameAppId);
+        boolean result;
+        // 手动开启事务
+        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
+        try {
+            //查询玩家信息
+            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()));
+            if (gameUserRole == null) {
+                result = this.gameRoleCreate(param, user);
+            } else {
+                result = this.gameRoleUpdate(param, gameUserRole, user);
+            }
+            // 提交事务
+            dataSourceTransactionManager.commit(transactionStatus);
+        } catch (Exception e) {
+            // 回滚事务
+            dataSourceTransactionManager.rollback(transactionStatus);
+            //保存错误日志
+            cpPushErrorLogService.createLog(game.getId(), CpPushDataEnum.CP_PUSH_DATA_ROLE, param, e.getMessage());
+            //抛出异常
+            throw new BaseException(e.getMessage());
+        }
+        //日志更新
+        cpPushErrorLogService.errorLogUpdate(game.getId(), CpPushDataEnum.CP_PUSH_DATA_ROLE, param);
+        //构造返回
+        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)) {
+            log.error("[角色推送]角色正在创建中, param : {}, user : {}", JsonUtil.toString(param), JsonUtil.toString(user));
+            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
+    public CpPushUserVO pushUser(String gameAppId, CpPushUserParam param) {
+        //查询游戏
+        Game game = this.getGameByGameAppId(gameAppId);
+        //结果信息
+        CpPushUserVO cpPushUserVO;
+        // 手动开启事务
+        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
+        try {
+            //中间数据
+            UserData userData = UserData.builder().gameId(game.getId()).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) {
+                cpPushUserVO = this.createUser(game, userData, param);
+            } else {
+                //玩家信息更新
+                Map<String, String> channelMap = this.updateUser(user, param, game, userData);
+                cpPushUserVO = CpPushUserVO.builder().userId(user.getId())
+                        .sdkUser(!Objects.equals(user.getAgentId(), Agent.DEFAULT_AGENT))
+                        .channel(JsonUtil.toString(channelMap))
+                        .build();
+            }
+            // 提交事务
+            dataSourceTransactionManager.commit(transactionStatus);
+        } catch (Exception e) {
+            // 回滚事务
+            dataSourceTransactionManager.rollback(transactionStatus);
+            //保存错误日志
+            cpPushErrorLogService.createLog(game.getId(), CpPushDataEnum.CP_PUSH_DATA_USER, param, e.getMessage());
+            //抛出异常
+            throw new BaseException(e.getMessage());
+        }
+        //日志更新
+        cpPushErrorLogService.errorLogUpdate(game.getId(), CpPushDataEnum.CP_PUSH_DATA_USER, param);
+        //返回结果
+        return cpPushUserVO;
+    }
+
+    private CpPushUserVO createUser(Game game, UserData userData, CpPushUserParam param) {
+        //游戏id
+        Long gameId = game.getId();
+        //线程锁Key
+        String lockKey = RedisKeyConstant.USER_CREATE + gameId + "_" + param.getOpenId();
+        //上锁
+        if (!distributedLockComponent.doLock(lockKey, 0L, 3L, TimeUnit.MINUTES)) {
+            log.error("[用户推送]用户正在创建中, param : {}, game : {}", JsonUtil.toString(param), JsonUtil.toString(game));
+            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, param);
+            } else {
+                Tuple3<Long, Map<String, String>, String> tuple3 = this.getUserAgentChannel(game, userData);
+                user = this.transform(gameId, tuple3, param);
+                channelMap = tuple3.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) {
+        //活跃间隔时长
+        long inactiveDay = 15L;
+        //判定不活跃时长是否达到限制
+        if (param.getActiveTime().plusDays(inactiveDay).isAfter(LocalDateTime.now())) {
+            return Collections.emptyMap();
+        }
+        //渠道id, 链接参数
+        Tuple3<Long, Map<String, String>, String> tuple3 = this.getUserAgentChannel(game, userData);
+        //查询渠道
+        Agent agent = agentService.getById(tuple3.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, tuple3.getT2());
+        //渠道变更信息埋点数据发送到卡夫卡
+        kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_AGENT_UPDATE, JsonUtil.toString(user));
+        //返回更新的渠道信息
+        return tuple3.getT2();
+    }
+
+    private Tuple3<Long, Map<String, String>, String> getUserAgentChannel(Game game, UserData userData) {
+        //解析渠道信息
+        String channel = new String(Base64.getDecoder().decode(userData.getChannel()));
+        //没有携带渠道标识, 无法解析渠道信息, 判定为自然量
+        if (!channel.contains("agentKey") || !channel.contains("state")) {
+            return Tuples.of(0L, Collections.emptyMap(), channel);
+        }
+        Tuple2<Boolean, Boolean> jsonAndEmpty = this.isJsonAndEmpty(channel);
+        //channel渠道信息非json格式, 或者是空json
+        if (!jsonAndEmpty.getT1() || jsonAndEmpty.getT2()) {
+            //返回自然量渠道
+            return Tuples.of(0L, Collections.emptyMap(), channel);
+        }
+        //渠道id, 链接参数, 分享人id
+        Tuple3<Long, Map<String, String>, String> tuple3 = agentService.getUserAgentId(game, userData);
+        //返回渠道id, 渠道参数
+        return Tuples.of(tuple3.getT1(), tuple3.getT2(), channel);
+    }
+
+    private User transform(Long gameId, User shareUser, CpPushUserParam param) {
+        return User.builder()
+                .openId(param.getOpenId())
+                .regAgentId(shareUser.getRegAgentId())
+                .agentId(shareUser.getAgentId())
+                .channel(shareUser.getChannel())
+                .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.getId())
+                .build();
+    }
+
+    private User transform(Long gameId, Tuple3<Long, Map<String, String>, String> tuple3, CpPushUserParam param) {
+        return User.builder()
+                .openId(param.getOpenId())
+                .regAgentId(tuple3.getT1())
+                .agentId(tuple3.getT1())
+                .channel(tuple3.getT2().isEmpty() ? tuple3.getT3() : JsonUtil.toString(tuple3.getT2()))
+                .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())
+                .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;
+    }
+}

+ 57 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CpPushErrorLogServiceImpl.java

@@ -0,0 +1,57 @@
+package com.zanxiang.game.module.sdk.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.mybatis.entity.CpPushErrorLog;
+import com.zanxiang.game.module.mybatis.mapper.CpPushErrorLogMapper;
+import com.zanxiang.game.module.sdk.enums.CpPushDataEnum;
+import com.zanxiang.game.module.sdk.service.ICpPushErrorLogService;
+import com.zanxiang.module.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.logging.log4j.util.Strings;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Map;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-06-27
+ * @description : CP推送数据错误日志
+ */
+@Slf4j
+@Service
+public class CpPushErrorLogServiceImpl extends ServiceImpl<CpPushErrorLogMapper, CpPushErrorLog> implements ICpPushErrorLogService {
+
+    @Override
+    public void createLog(Long gameId, CpPushDataEnum cpPushDataEnum, Object param, String errorMsg) {
+        String paramJsonStr = JsonUtil.toString(param);
+        Map<String, String> paramMap = JsonUtil.toMap(paramJsonStr, Map.class, String.class);
+        super.save(CpPushErrorLog.builder()
+                .dataType(cpPushDataEnum.getValue())
+                .gameId(gameId)
+                .openId(paramMap.get("openId"))
+                .orderId(paramMap.get("orderId"))
+                .serverId(paramMap.get("serverId"))
+                .roleId(paramMap.get("roleId"))
+                .param(paramJsonStr)
+                .errorMsg(errorMsg)
+                .status(CpPushErrorLog.STATUS_VALID)
+                .createTime(LocalDateTime.now())
+                .build());
+    }
+
+    @Override
+    public void errorLogUpdate(Long gameId, CpPushDataEnum cpPushDataEnum, Object param) {
+        Map<String, String> paramMap = JsonUtil.toMap(JsonUtil.toString(param), Map.class, String.class);
+        super.update(new LambdaUpdateWrapper<CpPushErrorLog>()
+                .set(CpPushErrorLog::getStatus, CpPushErrorLog.STATUS_INVALID)
+                .eq(CpPushErrorLog::getGameId, gameId)
+                .eq(CpPushErrorLog::getDataType, cpPushDataEnum.getValue())
+                .eq(Strings.isNotBlank(paramMap.get("openId")), CpPushErrorLog::getOpenId, paramMap.get("openId"))
+                .eq(Strings.isNotBlank(paramMap.get("orderId")), CpPushErrorLog::getOrderId, paramMap.get("orderId"))
+                .eq(Strings.isNotBlank(paramMap.get("serverId")), CpPushErrorLog::getServerId, paramMap.get("serverId"))
+                .eq(Strings.isNotBlank(paramMap.get("roleId")), CpPushErrorLog::getRoleId, paramMap.get("roleId"))
+        );
+    }
+}

+ 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 {
+}

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

@@ -3,10 +3,16 @@ package com.zanxiang.game.module.sdk.service.impl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zanxiang.game.module.mybatis.entity.Game;
 import com.zanxiang.game.module.mybatis.mapper.GameMapper;
+import com.zanxiang.game.module.sdk.pojo.param.UserData;
 import com.zanxiang.game.module.sdk.service.IGameService;
+import com.zanxiang.module.util.JsonUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.logging.log4j.util.Strings;
 import org.springframework.stereotype.Service;
 
+import java.util.Collections;
+import java.util.Map;
+
 /**
  * @author : lingfeng
  * @time : 2022-07-13
@@ -15,4 +21,19 @@ import org.springframework.stereotype.Service;
 @Slf4j
 @Service
 public class GameServiceImpl extends ServiceImpl<GameMapper, Game> implements IGameService {
+
+    @Override
+    public Map<String, Object> getAdSdkConfig(UserData userData) {
+
+        log.error("媒体初始化配置请求 userData : {}", JsonUtil.toString(userData));
+
+        Game game = super.getById(userData.getGameId());
+        if (Strings.isBlank(game.getAdSdkConfig())) {
+            return Collections.singletonMap("adSdk", 0);
+        }
+        Map<String, Object> map = JsonUtil.toMap(game.getAdSdkConfig(), Map.class, Object.class);
+
+        log.error("媒体初始化配置返回, map : {}", JsonUtil.toString(map));
+        return map;
+    }
 }

+ 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());
+        }
+    }
 }

+ 19 - 12
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;
@@ -200,6 +203,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
             if (tuple2.getT1()) {
                 userLoginVO.setUserId(tuple2.getT2());
             }
+            userLoginVO.setRegUser(Boolean.FALSE);
             //返回登录信息
             return ResultVO.ok(userLoginVO);
         }
@@ -211,7 +215,9 @@ public class LoginServiceImpl implements IRegisterLoginService {
         //用户注册
         user = userCreateSave(userData, username, RegisterUtil.cmfPassword(password), null, null, null);
         //返回登录信息
-        return ResultVO.ok(this.createUserLoginVO(user, userData));
+        UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
+        userLoginVO.setRegUser(Boolean.TRUE);
+        return ResultVO.ok(userLoginVO);
     }
 
     @Override
@@ -247,13 +253,16 @@ public class LoginServiceImpl implements IRegisterLoginService {
                 userLoginVO.setUserId(tuple2.getT2());
                 log.error("导量用户返回用户信息, userLoginVO : {}", JsonUtil.toString(userLoginVO));
             }
+            userLoginVO.setRegUser(Boolean.FALSE);
             //返回登录信息
             return ResultVO.ok(userLoginVO);
         }
         //用户注册
         user = userCreateSave(userData, mobile, null, mobile, null, null);
         //返回登录信息
-        return ResultVO.ok(this.createUserLoginVO(user, userData));
+        UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
+        userLoginVO.setRegUser(Boolean.TRUE);
+        return ResultVO.ok(userLoginVO);
     }
 
     private Tuple2<Boolean, Long> userGuideCheck(User user) {
@@ -308,18 +317,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;
     }
 
@@ -338,7 +342,10 @@ public class LoginServiceImpl implements IRegisterLoginService {
                 .deviceSystem(userData.getDeviceSystem())
                 .mac(userData.getMac())
                 .imei(userData.getImei())
+                .oaid(userData.getOaid())
                 .androidId(userData.getAndroidId())
+                .idfa(userData.getIdfa())
+                .caid(userData.getCaid())
                 .ip(userData.getIp())
                 .build();
     }

+ 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);

+ 8 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/PerformOrderServiceImpl.java

@@ -25,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.client.RestTemplate;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.Map;
@@ -98,7 +99,13 @@ public class PerformOrderServiceImpl implements IPerformOrderService {
         } else {
             map.put("userId", String.valueOf(orderInfo.getUserId()));
         }
-        map.put("money", orderInfo.getAmount().stripTrailingZeros().toPlainString());
+        //判断是否设置充值打折
+        if (game.getRechargeRebate() != null) {
+            BigDecimal money = orderInfo.getAmount().multiply(game.getRechargeRebate()).setScale(2, RoundingMode.HALF_UP);
+            map.put("money", money.stripTrailingZeros().toPlainString());
+        } else {
+            map.put("money", orderInfo.getAmount().stripTrailingZeros().toPlainString());
+        }
         map.put("time", String.valueOf(System.currentTimeMillis()));
         map.put("serverId", orderInfo.getServerId());
         map.put("roleId", orderInfo.getRoleId());

+ 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());
+    }
 }

+ 18 - 11
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserTokenServiceImpl.java

@@ -301,20 +301,27 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
     }
 
     private UserToken tokenSave(Long userId, String token, Integer deviceType) {
+        String lockKey = RedisKeyConstant.TOKEN_CREATE_LOCK + userId + "_" + deviceType;
         //上锁
-        if (!distributedLockComponent.doLock(RedisKeyConstant.TOKEN_CREATE_LOCK + userId + "_" + deviceType, 0L, 3L, TimeUnit.MINUTES)) {
+        if (!distributedLockComponent.doLock(lockKey, 0L, 3L, TimeUnit.MINUTES)) {
             throw new BaseException("token创建中, 请勿重复请求!");
         }
-        UserToken userToken = UserToken.builder()
-                .userId(userId)
-                .token(token)
-                .deviceType(deviceType)
-                .createTime(LocalDateTime.now())
-                .updateTime(LocalDateTime.now())
-                .expireTime(DateUtils.localDateTimeToSecond(LocalDateTime.now()) + ExpireTimeEnum.ONE_WEEK.getTime())
-                .build();
-        super.save(userToken);
-        return userToken;
+        try {
+            UserToken userToken = UserToken.builder()
+                    .userId(userId)
+                    .token(token)
+                    .deviceType(deviceType)
+                    .createTime(LocalDateTime.now())
+                    .updateTime(LocalDateTime.now())
+                    .expireTime(DateUtils.localDateTimeToSecond(LocalDateTime.now()) + ExpireTimeEnum.ONE_WEEK.getTime())
+                    .build();
+            super.save(userToken);
+            return userToken;
+        } catch (Exception e) {
+            throw new BaseException("创建token异常!");
+        } finally {
+            distributedLockComponent.unlock(lockKey);
+        }
     }
 
     @Override

+ 47 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/task/CpPushDataTask.java

@@ -0,0 +1,47 @@
+package com.zanxiang.game.module.sdk.task;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.zanxiang.game.module.mybatis.entity.CpPushErrorLog;
+import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
+import com.zanxiang.game.module.sdk.service.ICpPushErrorLogService;
+import com.zanxiang.module.redis.service.IDistributedLockComponent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-07-02
+ * @description : CP推送数据
+ */
+@Slf4j
+@Component
+@RefreshScope
+public class CpPushDataTask {
+
+    @Autowired
+    private ICpPushErrorLogService cpPushErrorLogService;
+
+    @Autowired
+    private IDistributedLockComponent distributedLockComponent;
+
+    /**
+     * 无效错误日志定时删除, 1小时执行一次
+     */
+    @Scheduled(cron = "0 0 0/1 * * ?")
+    public void cpPushErrorLogDel() {
+        //上锁
+        if (!distributedLockComponent.doLock(RedisKeyConstant.CP_PUSH_ERROR_LOG_LOCK, 0L, 30L, TimeUnit.MINUTES)) {
+            return;
+        }
+        log.error("[CP推送数据]无效错误日志定时删除");
+        //删除无效的日志
+        cpPushErrorLogService.remove(new LambdaQueryWrapper<CpPushErrorLog>()
+                .eq(CpPushErrorLog::getStatus, CpPushErrorLog.STATUS_INVALID)
+        );
+    }
+}