Browse Source

腾讯APP 企微链路回传

wcc 9 months ago
parent
commit
f4eece1c63
41 changed files with 2223 additions and 32 deletions
  1. 37 0
      game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TencentAppApiUserAgentQueryRpcDTO.java
  2. 108 0
      game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TencentOrderAppApiRpcDTO.java
  3. 79 0
      game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TencentRoleRegisterAppApiRpcDTO.java
  4. 77 0
      game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TencentUserAppApiRpcDTO.java
  5. 23 0
      game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/rpc/ITencentAppApiBackRpc.java
  6. 84 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/controller/TencentAppApiLogController.java
  7. 7 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiBackLogMapper.java
  8. 7 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiOrderMapper.java
  9. 7 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiOrderSplitLogMapper.java
  10. 7 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiRoleRegisterMapper.java
  11. 7 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiUserMapper.java
  12. 93 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/TencentAppReport.java
  13. 4 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameOceanengineAppCallback.java
  14. 107 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiBackLog.java
  15. 157 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiOrder.java
  16. 85 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiOrderSplitLog.java
  17. 113 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiRoleRegister.java
  18. 113 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiUser.java
  19. 4 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppCallback.java
  20. 33 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/vo/GameTencentAppApiOrderSplitLogVO.java
  21. 210 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/rpc/impl/TencentAppApiBackRpcImpl.java
  22. 6 6
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/rpc/impl/TencentAppBackRpcImpl.java
  23. 3 6
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/rpc/impl/TtAppBackRpcImpl.java
  24. 2 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameOceanengineAppCallbackService.java
  25. 7 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiBackLogService.java
  26. 18 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiOrderService.java
  27. 13 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiOrderSplitLogService.java
  28. 9 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiRoleRegisterService.java
  29. 13 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiUserService.java
  30. 2 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppCallbackService.java
  31. 38 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameOceanengineAppCallbackServiceImpl.java
  32. 14 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiBackLogServiceImpl.java
  33. 456 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiOrderServiceImpl.java
  34. 49 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiOrderSplitLogServiceImpl.java
  35. 49 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiRoleRegisterServiceImpl.java
  36. 130 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiUserServiceImpl.java
  37. 27 13
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppCallbackServiceImpl.java
  38. 0 6
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentUserServiceImpl.java
  39. 3 1
      game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/pojo/enums/AccountTypeEnum.java
  40. 4 0
      game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/rpc/IAgentRpc.java
  41. 18 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/rpc/impl/AgentRpcImpl.java

+ 37 - 0
game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TencentAppApiUserAgentQueryRpcDTO.java

@@ -0,0 +1,37 @@
+package com.zanxiang.game.back.base.pojo.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TencentAppApiUserAgentQueryRpcDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Long gameId;
+
+    /**
+     * 设备唯一编号IMEI
+     */
+    private String imei;
+
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
+    /**
+     * 安卓id, (仅安卓设备才有值)
+     */
+    private String androidId;
+
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+}

+ 108 - 0
game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TencentOrderAppApiRpcDTO.java

@@ -0,0 +1,108 @@
+package com.zanxiang.game.back.base.pojo.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class TencentOrderAppApiRpcDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 回传策略ID
+     */
+    private Long backPolicyId;
+    /**
+     * sdk 里面的用户 id(要求唯一)
+     */
+    private String userId;
+    /**
+     * 游戏ID
+     */
+    private Long gameId;
+
+    /**
+     * 广告账号ID
+     */
+    private Long adAccountId;
+
+    /**
+     * 注册时间
+     */
+    private LocalDateTime registerTime;
+
+    /**
+     * 关注时间
+     */
+    private LocalDateTime subscribeTime;
+
+    /**
+     * 充值金额(分)
+     */
+    private Long rechargeMoney;
+
+    /**
+     * 充值时间
+     */
+    private LocalDateTime rechargeTime;
+
+    /**
+     * 订单ID
+     */
+    private String orderId;
+
+    /**
+     * 渠道号
+     */
+    private String channel;
+
+    /**
+     * 支付状态,0 : 预下单, 1 : 待支付,2 : 支付成功,-1 : 已取消
+     */
+    private Integer orderStatus;
+
+    /**
+     * 支付时间
+     */
+    private LocalDateTime payTime;
+
+    private String roleId;
+
+    private String roleName;
+
+    /**
+     * 用户设备mac地址
+     */
+    private String mac;
+
+    /**
+     * 设备唯一编号IMEI
+     */
+    private String imei;
+
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
+    /**
+     * 安卓id, (仅安卓设备才有值)
+     */
+    private String androidId;
+
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+}

+ 79 - 0
game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TencentRoleRegisterAppApiRpcDTO.java

@@ -0,0 +1,79 @@
+package com.zanxiang.game.back.base.pojo.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class TencentRoleRegisterAppApiRpcDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+
+    /**
+     * 回传策略ID
+     */
+    private Long backPolicyId;
+    /**
+     * sdk 里面的用户 id(要求唯一)
+     */
+    private String userId;
+    /**
+     * 游戏ID
+     */
+    private Long gameId;
+
+    /**
+     * 广告账号ID
+     */
+    private Long adAccountId;
+    /**
+     * 注册时间
+     */
+    private LocalDateTime registerTime;
+
+    /**
+     * 渠道号
+     */
+    private String channel;
+
+    private String roleId;
+
+    private String roleName;
+
+    /**
+     * 用户设备mac地址
+     */
+    private String mac;
+
+    /**
+     * 设备唯一编号IMEI
+     */
+    private String imei;
+
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
+    /**
+     * 安卓id, (仅安卓设备才有值)
+     */
+    private String androidId;
+
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+}

+ 77 - 0
game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/pojo/dto/TencentUserAppApiRpcDTO.java

@@ -0,0 +1,77 @@
+package com.zanxiang.game.back.base.pojo.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class TencentUserAppApiRpcDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 回传策略ID
+     */
+    private Long backPolicyId;
+    /**
+     * sdk 里面的用户 id(要求唯一)
+     */
+    private String userId;
+    /**
+     * 游戏ID
+     */
+    private Long gameId;
+
+    /**
+     * 广告账号ID
+     */
+    private Long adAccountId;
+    /**
+     * 注册时间
+     */
+    private LocalDateTime registerTime;
+
+    /**
+     * 关注时间
+     */
+    private LocalDateTime subscribeTime;
+
+    /**
+     * 渠道号
+     */
+    private String channel;
+    /**
+     * 用户设备mac地址
+     */
+    private String mac;
+
+    /**
+     * 设备唯一编号IMEI
+     */
+    private String imei;
+
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
+    /**
+     * 安卓id, (仅安卓设备才有值)
+     */
+    private String androidId;
+
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+}

+ 23 - 0
game-back/game-back-base/src/main/java/com/zanxiang/game/back/base/rpc/ITencentAppApiBackRpc.java

@@ -0,0 +1,23 @@
+package com.zanxiang.game.back.base.rpc;
+
+import com.zanxiang.game.back.base.pojo.dto.*;
+import com.zanxiang.module.util.pojo.ResultVO;
+
+/**
+ * 腾讯数据源回传
+ */
+public interface ITencentAppApiBackRpc {
+    /**
+     * 订单回传
+     */
+    ResultVO<Boolean> backOrder(TencentOrderAppApiRpcDTO dto);
+
+    /**
+     * 用户回传
+     */
+    ResultVO<Boolean> backUser(TencentUserAppApiRpcDTO dto);
+
+    ResultVO<Boolean> backRoleRegister(TencentRoleRegisterAppApiRpcDTO dto);
+
+    ResultVO<String> queryUserAgentFromCallback(TencentAppApiUserAgentQueryRpcDTO dto);
+}

+ 84 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/controller/TencentAppApiLogController.java

@@ -0,0 +1,84 @@
+package com.zanxiang.game.back.serve.controller;
+
+import com.zanxiang.erp.security.annotation.PreAuthorize;
+import com.zanxiang.game.back.serve.pojo.dto.OrderReportDTO;
+import com.zanxiang.game.back.serve.pojo.vo.GameTencentAppApiOrderSplitLogVO;
+import com.zanxiang.game.back.serve.service.*;
+import com.zanxiang.module.util.exception.BaseException;
+import com.zanxiang.module.util.pojo.ResultVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.List;
+
+@RestController
+@RequestMapping("/tencentAppApi")
+@Api("腾讯APP API 回传")
+public class TencentAppApiLogController {
+    @Autowired
+    private IGameTencentAppApiUserService gameTencentAppApiUserService;
+    @Autowired
+    private IGameTencentAppApiOrderService gameTencentAppApiOrderService;
+    @Autowired
+    private IGameTencentAppApiOrderSplitLogService gameTencentAppApiOrderSplitLogService;
+
+
+    @PreAuthorize(permissionKey = "gameBack:tencentAppApi:orderSplits")
+    @GetMapping("/orderSplitList/{orderId}")
+    public ResultVO<List<GameTencentAppApiOrderSplitLogVO>> orderSplitList(@PathVariable("orderId") String orderId) {
+        return ResultVO.ok(gameTencentAppApiOrderSplitLogService.listByOrderNo(Collections.singletonList(orderId)));
+    }
+
+    @PreAuthorize(permissionKey = "gameBack:tencentAppApi:orderReport")
+    @PostMapping("/orderReport/{ids}/{backMoney}")
+    @ApiOperation(value = "腾讯订单手动上报")
+    public ResultVO<Boolean> tencentOrderReport(@PathVariable("ids") List<Long> ids, @PathVariable("backMoney") BigDecimal backMoney) {
+        OrderReportDTO dto = new OrderReportDTO();
+        dto.setOrderIds(ids);
+        dto.setSplitOrder(false);
+        dto.setBackMoney(backMoney);
+        return tencentOrderReport(dto);
+    }
+
+    @PreAuthorize(permissionKey = "gameBack:tencentAppApi:orderReport")
+    @PostMapping("/orderReport")
+    @ApiOperation(value = "腾讯订单手动上报")
+    public ResultVO<Boolean> tencentOrderReport(@Validated @RequestBody OrderReportDTO dto) {
+        if (CollectionUtils.isEmpty(dto.getOrderIds())) {
+            throw new BaseException("参数错误!");
+        }
+        if (dto.getSplitOrder()) {
+            if (CollectionUtils.isEmpty(dto.getSplitMoney())) {
+                throw new BaseException("回传金额错误!");
+            }
+            dto.getSplitMoney().forEach(backMoney -> {
+                if (backMoney.compareTo(BigDecimal.ZERO) <= 0) {
+                    throw new BaseException("回传金额错误!");
+                }
+            });
+            if (dto.getBetweenMinuteMin() == null || dto.getBetweenMinuteMax() == null
+                    || dto.getBetweenMinuteMin() < 1 || dto.getBetweenMinuteMax() < 1
+                    || dto.getBetweenMinuteMin().compareTo(dto.getBetweenMinuteMax()) >= 0) {
+                throw new BaseException("回传间隔时间错误!");
+            }
+        } else {
+            if (dto.getBackMoney().compareTo(BigDecimal.ZERO) <= 0) {
+                throw new BaseException("回传金额错误!");
+            }
+        }
+        return ResultVO.ok(gameTencentAppApiOrderService.tencentOrderReport(dto));
+    }
+
+    @PreAuthorize(permissionKey = "gameBack:tencentAppApi:userReport")
+    @PostMapping("/userReport/{ids}")
+    @ApiOperation(value = "腾讯用户手动上报")
+    public ResultVO<Boolean> tencentUserReport(@PathVariable("ids") List<Long> ids) {
+        return ResultVO.ok(gameTencentAppApiUserService.tencentUserReport(ids));
+    }
+}

+ 7 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiBackLogMapper.java

@@ -0,0 +1,7 @@
+package com.zanxiang.game.back.serve.dao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiBackLog;
+
+public interface GameTencentAppApiBackLogMapper extends BaseMapper<GameTencentAppApiBackLog> {
+}

+ 7 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiOrderMapper.java

@@ -0,0 +1,7 @@
+package com.zanxiang.game.back.serve.dao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiOrder;
+
+public interface GameTencentAppApiOrderMapper extends BaseMapper<GameTencentAppApiOrder> {
+}

+ 7 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiOrderSplitLogMapper.java

@@ -0,0 +1,7 @@
+package com.zanxiang.game.back.serve.dao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiOrderSplitLog;
+
+public interface GameTencentAppApiOrderSplitLogMapper extends BaseMapper<GameTencentAppApiOrderSplitLog> {
+}

+ 7 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiRoleRegisterMapper.java

@@ -0,0 +1,7 @@
+package com.zanxiang.game.back.serve.dao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiRoleRegister;
+
+public interface GameTencentAppApiRoleRegisterMapper extends BaseMapper<GameTencentAppApiRoleRegister> {
+}

+ 7 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppApiUserMapper.java

@@ -0,0 +1,7 @@
+package com.zanxiang.game.back.serve.dao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiUser;
+
+public interface GameTencentAppApiUserMapper extends BaseMapper<GameTencentAppApiUser> {
+}

+ 93 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/TencentAppReport.java

@@ -0,0 +1,93 @@
+package com.zanxiang.game.back.serve.pojo;
+
+import com.zanxiang.advertising.tencent.base.pojo.dto.UserActionRpcDTO;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 腾讯 APP 回传 API
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class TencentAppReport implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private List<Action> actions;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @Builder
+    public static class Action implements Serializable {
+        private static final long serialVersionUID = 1L;
+        /**
+         * 选填,若上报可能有重复请填写该id,系统会根据该ID进行去重,详见FAQ
+         */
+        private String outer_action_id;
+        /**
+         * 转化发生时间(单位 秒)
+         */
+        private Long action_time;
+
+        private UserId userr_id;
+
+        private String action_type;
+
+        private String trace;
+
+        private Map<String, Object> action_param;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @Builder
+    public static class UserId implements Serializable {
+        private static final long serialVersionUID = 1L;
+        /**
+         * Android能获取到时必填,IMEI小写进行MD5编码,字段长度为32字节
+         */
+        private String hash_imei;
+        /**
+         * Android能获取到时必填,OAID原值进行MD5编码,字段长度为32字节
+         */
+        private String hash_oaid;
+        /**
+         * Android能获取到时必填,Android ID原值进行MD5编码,字段长度为32字节
+         */
+        private String hash_android_id;
+        /**
+         * iOS能获取到时必填,IDFA大写进行MD5编码,字段长度为32字节
+         */
+        private String hash_idfa;
+
+        /**
+         * 中国广告协会互联网广告标(CAA Advertising id);iOS能获取到时必填,建议上报最新的 caid 版本
+         */
+        private String caid;
+        /**
+         * iOS能获取到时必填,上报 CAID 时 caid_version 为必填项,建议上报最新的 CAID 版本。
+         * CAID版本对应关系:
+         * ○腾讯版本1004 对应 中广协版本20211207
+         * ○腾讯版本1005 对应 中广协版本20220111
+         */
+        private String caid_version;
+        /**
+         * IP地址原文,能获取到时强烈建议填写,支持IPV4和IPV6
+         */
+        private String ip;
+        /**
+         * User Agent原文,能获取到时强烈建议填写,支持IPV4和IPV6
+         */
+        private String user_agent;
+
+    }
+}

+ 4 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameOceanengineAppCallback.java

@@ -30,6 +30,10 @@ public class GameOceanengineAppCallback implements Serializable {
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
 
+    private String agentKey;
+
+    private Long gameId;
+
     /**
      * 时间发生日期
      */

+ 107 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiBackLog.java

@@ -0,0 +1,107 @@
+package com.zanxiang.game.back.serve.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 腾讯 APP回传日志表
+ * </p>
+ *
+ * @author wcc
+ * @since 2024-06-28
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@TableName(GameTencentAppApiBackLog.TABLE_NAME)
+public class GameTencentAppApiBackLog implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String TABLE_NAME = "t_game_tencent_app_api_back_log";
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private String userId;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 广告账号ID
+     */
+    private Long adAccountId;
+
+    /**
+     * 行为发生时,客户端的时间点
+     */
+    private LocalDateTime actionTime;
+
+    /**
+     * 用户设备mac地址
+     */
+    private String mac;
+
+    /**
+     * 设备唯一编号IMEI
+     */
+    private String imei;
+
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
+    /**
+     * 安卓id, (仅安卓设备才有值)
+     */
+    private String androidId;
+
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+
+    /**
+     * 行为类型:(REGISTER/PURCHASE/COMPLETE_ORDER)
+     */
+    private String actionType;
+
+    /**
+     * 订单ID
+     */
+    private String orderId;
+
+    /**
+     * 回传时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 回传日志
+     */
+    private String backLog;
+
+    /**
+     * 行为参数
+     */
+    private String actionParam;
+
+
+}

+ 157 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiOrder.java

@@ -0,0 +1,157 @@
+package com.zanxiang.game.back.serve.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 游戏腾讯APP订单表
+ * </p>
+ *
+ * @author wcc
+ * @since 2024-06-28
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@TableName(GameTencentAppApiOrder.TABLE_NAME)
+public class GameTencentAppApiOrder implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String TABLE_NAME = "t_game_tencent_app_api_order";
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private String userId;
+
+    /**
+     * 渠道号
+     */
+    private String channel;
+
+    /**
+     * 游戏ID
+     */
+    private Long gameId;
+
+    /**
+     * 广告账号ID
+     */
+    private Long adAccountId;
+
+    /**
+     * 注册时间
+     */
+    private LocalDateTime registerTime;
+
+    /**
+     * 关注时间
+     */
+    private LocalDateTime subscribeTime;
+
+    /**
+     * 充值金额(分)
+     */
+    private Long rechargeMoney;
+
+    /**
+     * 充值时间
+     */
+    private LocalDateTime rechargeTime;
+
+    /**
+     * 订单ID
+     */
+    private String orderId;
+
+    /**
+     * 用户设备mac地址
+     */
+    private String mac;
+
+    /**
+     * 设备唯一编号IMEI
+     */
+    private String imei;
+
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
+    /**
+     * 安卓id, (仅安卓设备才有值)
+     */
+    private String androidId;
+
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+
+    /**
+     * 支付状态,0 : 预下单, 1 : 待支付,2 : 支付成功,-1 : 已取消
+     */
+    private Integer orderStatus;
+
+    /**
+     * 支付时间
+     */
+    private LocalDateTime payTime;
+
+    /**
+     * 0:未回传;1:回传
+     */
+    private Integer isBack;
+
+    private LocalDateTime createTime;
+
+    private LocalDateTime updateTime;
+
+    private Long updateBy;
+
+    /**
+     * 回传日志
+     */
+    private String backLog;
+
+    /**
+     * 回传策略id
+     */
+    private Long backPolicyId;
+
+    private String roleId;
+
+    private String roleName;
+
+    /**
+     * 是否是首单
+     */
+    private Boolean isFirstOrder;
+
+    /**
+     * 回传金额
+     */
+    private Long backMoney;
+
+    /**
+     * 回传信息
+     */
+    private String backMsg;
+
+
+}

+ 85 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiOrderSplitLog.java

@@ -0,0 +1,85 @@
+package com.zanxiang.game.back.serve.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author wcc
+ * @since 2024-06-28
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@TableName(GameTencentAppApiOrderSplitLog.TABLE_NAME)
+public class GameTencentAppApiOrderSplitLog implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String TABLE_NAME = "t_game_tencent_app_api_order_split_log";
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private String userId;
+
+    /**
+     * 回传日期
+     */
+    private LocalDate backDay;
+
+    /**
+     * 订单编号
+     */
+    private String orderNo;
+
+    /**
+     * 回传序号
+     */
+    private Integer backIndex;
+
+    /**
+     * 总计拆成几单
+     */
+    private Integer backCount;
+
+    /**
+     * 拆分的回传金额
+     */
+    private Long splitMoney;
+
+    /**
+     * 回传时间
+     */
+    private LocalDateTime backTime;
+
+    /**
+     * 真实的回传时间
+     */
+    private LocalDateTime executeTime;
+
+    /**
+     * 回传状态
+     */
+    private Integer backStatus;
+
+    private LocalDateTime createTime;
+
+    /**
+     * 回传失败消息
+     */
+    private String backErrorMsg;
+
+
+}

+ 113 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiRoleRegister.java

@@ -0,0 +1,113 @@
+package com.zanxiang.game.back.serve.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 游戏腾讯APP角色创建表
+ * </p>
+ *
+ * @author wcc
+ * @since 2024-06-28
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@TableName(GameTencentAppApiRoleRegister.TABLE_NAME)
+public class GameTencentAppApiRoleRegister implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String TABLE_NAME = "t_game_tencent_app_api_role_register";
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private String userId;
+
+    /**
+     * 渠道号
+     */
+    private String channel;
+
+    /**
+     * 游戏ID
+     */
+    private Long gameId;
+
+    /**
+     * 广告账号ID
+     */
+    private Long adAccountId;
+
+    /**
+     * 注册时间
+     */
+    private LocalDateTime registerTime;
+
+    /**
+     * 用户设备mac地址
+     */
+    private String mac;
+
+    /**
+     * 设备唯一编号IMEI
+     */
+    private String imei;
+
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
+    /**
+     * 安卓id, (仅安卓设备才有值)
+     */
+    private String androidId;
+
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+
+    /**
+     * 0:未回传;1:回传
+     */
+    private Integer isBack;
+
+    private LocalDateTime createTime;
+
+    /**
+     * 回传日志
+     */
+    private String backLog;
+
+    /**
+     * 回传策略id
+     */
+    private Long backPolicyId;
+
+    private String roleId;
+
+    private String roleName;
+
+    /**
+     * 回传信息
+     */
+    private String backMsg;
+
+
+}

+ 113 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppApiUser.java

@@ -0,0 +1,113 @@
+package com.zanxiang.game.back.serve.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 游戏腾讯 APP用户表
+ * </p>
+ *
+ * @author wcc
+ * @since 2024-06-28
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@TableName(GameTencentAppApiUser.TABLE_NAME)
+public class GameTencentAppApiUser implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String TABLE_NAME = "t_game_tencent_app_api_user";
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private String userId;
+
+    /**
+     * 渠道号
+     */
+    private String channel;
+
+    /**
+     * 游戏ID
+     */
+    private Long gameId;
+
+    /**
+     * 广告账号ID
+     */
+    private Long adAccountId;
+
+    /**
+     * 注册时间
+     */
+    private LocalDateTime registerTime;
+
+    /**
+     * 关注时间
+     */
+    private LocalDateTime subscribeTime;
+
+    /**
+     * 用户设备mac地址
+     */
+    private String mac;
+
+    /**
+     * 设备唯一编号IMEI
+     */
+    private String imei;
+
+    /**
+     * 设备OAID
+     */
+    private String oaid;
+
+    /**
+     * 安卓id, (仅安卓设备才有值)
+     */
+    private String androidId;
+
+    /**
+     * IOS设备IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS设备CAID
+     */
+    private String caid;
+
+    /**
+     * 0:未回传;1:回传;-1:回传失败
+     */
+    private Integer isBack;
+
+    private LocalDateTime createTime;
+
+    private LocalDateTime updateTime;
+
+    private Long updateBy;
+
+    /**
+     * 回传日志
+     */
+    private String backLog;
+
+    /**
+     * 回传策略id
+     */
+    private Long backPolicyId;
+
+
+}

+ 4 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppCallback.java

@@ -30,6 +30,10 @@ public class GameTencentAppCallback implements Serializable {
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
 
+    private String agentKey;
+
+    private Long gameId;
+
     /**
      * 时间发生日期
      */

+ 33 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/vo/GameTencentAppApiOrderSplitLogVO.java

@@ -0,0 +1,33 @@
+package com.zanxiang.game.back.serve.pojo.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+public class GameTencentAppApiOrderSplitLogVO {
+    
+    private Long id;
+
+    private LocalDate backDay;
+
+    private String orderNo;
+
+    private Integer backIndex;
+
+    private Integer backCount;
+
+    private BigDecimal splitMoney;
+
+    private LocalDateTime backTime;
+
+    private LocalDateTime executeTime;
+
+    private Integer backStatus;
+
+    private String backErrorMsg;
+
+    private LocalDateTime createTime;
+}

+ 210 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/rpc/impl/TencentAppApiBackRpcImpl.java

@@ -0,0 +1,210 @@
+package com.zanxiang.game.back.serve.rpc.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.zanxiang.game.back.base.pojo.dto.*;
+import com.zanxiang.game.back.base.pojo.enums.OrderStatusEnum;
+import com.zanxiang.game.back.base.rpc.ITencentAppApiBackRpc;
+import com.zanxiang.game.back.base.rpc.ITencentAppBackRpc;
+import com.zanxiang.game.back.serve.pojo.entity.*;
+import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
+import com.zanxiang.game.back.serve.service.*;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.encryption.Md5Util;
+import com.zanxiang.module.util.pojo.ResultVO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+@Slf4j
+@DubboService
+public class TencentAppApiBackRpcImpl implements ITencentAppApiBackRpc {
+    @Autowired
+    private IGameTencentAppApiUserService gameTencentAppApiUserService;
+    @Autowired
+    private IGameTencentAppApiRoleRegisterService gameTencentAppApiRoleRegisterService;
+    @Autowired
+    private IGameTencentAppApiOrderService gameTencentAppApiOrderService;
+    @Autowired
+    private IGameTencentAppCallbackService gameTencentAppCallbackService;
+
+    @Override
+    public ResultVO<Boolean> backUser(TencentUserAppApiRpcDTO dto) {
+        log.error("腾讯 APP-API 用户回传收到:{}", JsonUtil.toString(dto));
+        if (StringUtils.isBlank(dto.getMac())) {
+            dto.setMac("");
+        }
+        if(StringUtils.isBlank(dto.getImei())) {
+            dto.setImei("");
+        }
+        if(StringUtils.isBlank(dto.getOaid())) {
+            dto.setOaid("");
+        }
+        if(StringUtils.isBlank(dto.getAndroidId())) {
+            dto.setAndroidId("");
+        }
+        if(StringUtils.isBlank(dto.getIdfa())) {
+            dto.setIdfa("");
+        }
+        if(StringUtils.isBlank(dto.getCaid())) {
+            dto.setCaid("");
+        }
+        if(StringUtils.isBlank(dto.getImei())
+                && StringUtils.isBlank(dto.getOaid())
+                && StringUtils.isBlank(dto.getAndroidId())
+                && StringUtils.isBlank(dto.getIdfa())
+                && StringUtils.isBlank(dto.getCaid())) {
+            return ResultVO.fail("找不到用户唯一标识");
+        }
+        GameTencentAppApiUser gameTencentUser = GameTencentAppApiUser.builder()
+                .userId(dto.getUserId())
+                .adAccountId(dto.getAdAccountId())
+                .gameId(dto.getGameId())
+                .channel(dto.getChannel())
+                .subscribeTime(dto.getSubscribeTime())
+                .registerTime(dto.getRegisterTime())
+                .mac(dto.getMac())
+                .imei(dto.getImei())
+                .oaid(dto.getOaid())
+                .androidId(dto.getAndroidId())
+                .idfa(dto.getIdfa())
+                .caid(dto.getCaid())
+                .isBack(BackStatusEnum.NO.getBackStatus())
+                .createTime(LocalDateTime.now())
+                .build();
+        gameTencentAppApiUserService.save(gameTencentUser);
+        return ResultVO.ok(true);
+        // 激活现在默认不回传了,等创角的时候一起回传
+        // return ResultVO.ok(gameTencentAppApiUserService.userBack(gameTencentUser, false));
+    }
+
+    @Override
+    public ResultVO<Boolean> backRoleRegister(TencentRoleRegisterAppApiRpcDTO dto) {
+        log.error("腾讯 APP创角回传收到:{}", JsonUtil.toString(dto));
+        if (StringUtils.isBlank(dto.getMac())) {
+            dto.setMac("");
+        }
+        if(StringUtils.isBlank(dto.getImei())) {
+            dto.setImei("");
+        }
+        if(StringUtils.isBlank(dto.getOaid())) {
+            dto.setOaid("");
+        }
+        if(StringUtils.isBlank(dto.getAndroidId())) {
+            dto.setAndroidId("");
+        }
+        if(StringUtils.isBlank(dto.getIdfa())) {
+            dto.setIdfa("");
+        }
+        if(StringUtils.isBlank(dto.getCaid())) {
+            dto.setCaid("");
+        }
+        if(StringUtils.isBlank(dto.getImei())
+                && StringUtils.isBlank(dto.getOaid())
+                && StringUtils.isBlank(dto.getAndroidId())
+                && StringUtils.isBlank(dto.getIdfa())
+                && StringUtils.isBlank(dto.getCaid())) {
+            return ResultVO.fail("找不到用户唯一标识");
+        }
+        GameTencentAppApiRoleRegister roleRegister = GameTencentAppApiRoleRegister.builder()
+                .userId(dto.getUserId())
+                .backPolicyId(dto.getBackPolicyId())
+                .channel(dto.getChannel())
+                .gameId(dto.getGameId())
+                .adAccountId(dto.getAdAccountId())
+                .registerTime(dto.getRegisterTime())
+                .mac(dto.getMac())
+                .imei(dto.getImei())
+                .oaid(dto.getOaid())
+                .androidId(dto.getAndroidId())
+                .idfa(dto.getIdfa())
+                .caid(dto.getCaid())
+                .isBack(BackStatusEnum.NO.getBackStatus())
+                .createTime(LocalDateTime.now())
+                .roleId(dto.getRoleId())
+                .roleName(dto.getRoleName())
+                .build();
+        gameTencentAppApiRoleRegisterService.save(roleRegister);
+        return ResultVO.ok(gameTencentAppApiRoleRegisterService.roleRegisterBack(roleRegister));
+    }
+
+    @Override
+    public ResultVO<Boolean> backOrder(TencentOrderAppApiRpcDTO dto) {
+        if (Objects.equals(OrderStatusEnum.SUCCESS_PAY.getValue(), dto.getOrderStatus())) {
+            log.error("腾讯 APP订单回传收到:{}。", JsonUtil.toString(dto));
+        } else {
+            log.error("腾讯H5订单回传收到:{}。订单未支付,直接过滤", JsonUtil.toString(dto));
+            return ResultVO.ok(true);
+        }
+        if (StringUtils.isBlank(dto.getMac())) {
+            dto.setMac("");
+        }
+        if(StringUtils.isBlank(dto.getImei())) {
+            dto.setImei("");
+        }
+        if(StringUtils.isBlank(dto.getOaid())) {
+            dto.setOaid("");
+        }
+        if(StringUtils.isBlank(dto.getAndroidId())) {
+            dto.setAndroidId("");
+        }
+        if(StringUtils.isBlank(dto.getIdfa())) {
+            dto.setIdfa("");
+        }
+        if(StringUtils.isBlank(dto.getCaid())) {
+            dto.setCaid("");
+        }
+        if(StringUtils.isBlank(dto.getImei())
+                && StringUtils.isBlank(dto.getOaid())
+                && StringUtils.isBlank(dto.getAndroidId())
+                && StringUtils.isBlank(dto.getIdfa())
+                && StringUtils.isBlank(dto.getCaid())) {
+            return ResultVO.fail("找不到用户唯一标识");
+        }
+        boolean isFirstOrder = gameTencentAppApiOrderService.getOne(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                .select(GameTencentAppApiOrder::getOrderId)
+                .eq(GameTencentAppApiOrder::getGameId, dto.getGameId())
+                .eq(GameTencentAppApiOrder::getAdAccountId, dto.getAdAccountId())
+                .eq(GameTencentAppApiOrder::getUserId, dto.getUserId())
+                .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                .last("limit 1")
+        ) == null;
+        //订单保存
+        GameTencentAppApiOrder gameTencentOrder = GameTencentAppApiOrder.builder()
+                .userId(dto.getUserId())
+                .adAccountId(dto.getAdAccountId())
+                .gameId(dto.getGameId())
+                .orderId(dto.getOrderId())
+                .channel(dto.getChannel())
+                .rechargeMoney(dto.getRechargeMoney())
+                .rechargeTime(dto.getRechargeTime())
+                .subscribeTime(dto.getSubscribeTime())
+                .registerTime(dto.getRegisterTime())
+                .mac(dto.getMac())
+                .imei(dto.getImei())
+                .oaid(dto.getOaid())
+                .androidId(dto.getAndroidId())
+                .idfa(dto.getIdfa())
+                .caid(dto.getCaid())
+                .orderStatus(dto.getOrderStatus())
+                .payTime(dto.getPayTime())
+                .createTime(LocalDateTime.now())
+                .isBack(BackStatusEnum.NO.getBackStatus())
+                .backPolicyId(dto.getBackPolicyId())
+                .roleId(dto.getRoleId())
+                .roleName(dto.getRoleName())
+                .isFirstOrder(isFirstOrder)
+                .build();
+        gameTencentAppApiOrderService.save(gameTencentOrder);
+        return ResultVO.ok(gameTencentAppApiOrderService.orderBack(gameTencentOrder));
+    }
+
+    @Override
+    public ResultVO<String> queryUserAgentFromCallback(TencentAppApiUserAgentQueryRpcDTO dto) {
+        GameTencentAppCallback callback = gameTencentAppCallbackService.getUserCallback(dto.getGameId(), dto.getImei(), dto.getOaid(), dto.getAndroidId(), dto.getIdfa(), dto.getCaid());
+        return ResultVO.ok(callback == null ? null : callback.getAgentKey());
+    }
+}

+ 6 - 6
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/rpc/impl/TencentAppBackRpcImpl.java

@@ -50,8 +50,7 @@ public class TencentAppBackRpcImpl implements ITencentAppBackRpc {
         if(StringUtils.isBlank(dto.getCaid())) {
             dto.setCaid("");
         }
-        if(StringUtils.isBlank(dto.getMac())
-                && StringUtils.isBlank(dto.getImei())
+        if(StringUtils.isBlank(dto.getImei())
                 && StringUtils.isBlank(dto.getOaid())
                 && StringUtils.isBlank(dto.getAndroidId())
                 && StringUtils.isBlank(dto.getIdfa())
@@ -61,6 +60,7 @@ public class TencentAppBackRpcImpl implements ITencentAppBackRpc {
         GameTencentAppUser gameTencentUser = GameTencentAppUser.builder()
                 .adAccountId(dto.getAdAccountId())
                 .gameId(dto.getGameId())
+                .userId(dto.getUserId())
                 .channel(dto.getChannel())
                 .subscribeTime(dto.getSubscribeTime())
                 .registerTime(dto.getRegisterTime())
@@ -101,8 +101,7 @@ public class TencentAppBackRpcImpl implements ITencentAppBackRpc {
         if(StringUtils.isBlank(dto.getCaid())) {
             dto.setCaid("");
         }
-        if(StringUtils.isBlank(dto.getMac())
-                && StringUtils.isBlank(dto.getImei())
+        if(StringUtils.isBlank(dto.getImei())
                 && StringUtils.isBlank(dto.getOaid())
                 && StringUtils.isBlank(dto.getAndroidId())
                 && StringUtils.isBlank(dto.getIdfa())
@@ -112,6 +111,7 @@ public class TencentAppBackRpcImpl implements ITencentAppBackRpc {
         GameTencentAppRoleRegister roleRegister = GameTencentAppRoleRegister.builder()
                 .backPolicyId(dto.getBackPolicyId())
                 .channel(dto.getChannel())
+                .userId(dto.getUserId())
                 .gameId(dto.getGameId())
                 .adAccountId(dto.getAdAccountId())
                 .registerTime(dto.getRegisterTime())
@@ -158,8 +158,7 @@ public class TencentAppBackRpcImpl implements ITencentAppBackRpc {
         if(StringUtils.isBlank(dto.getCaid())) {
             dto.setCaid("");
         }
-        if(StringUtils.isBlank(dto.getMac())
-                && StringUtils.isBlank(dto.getImei())
+        if(StringUtils.isBlank(dto.getImei())
                 && StringUtils.isBlank(dto.getOaid())
                 && StringUtils.isBlank(dto.getAndroidId())
                 && StringUtils.isBlank(dto.getIdfa())
@@ -182,6 +181,7 @@ public class TencentAppBackRpcImpl implements ITencentAppBackRpc {
         //订单保存
         GameTencentAppOrder gameTencentOrder = GameTencentAppOrder.builder()
                 .adAccountId(dto.getAdAccountId())
+                .userId(dto.getUserId())
                 .gameId(dto.getGameId())
                 .orderId(dto.getOrderId())
                 .channel(dto.getChannel())

+ 3 - 6
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/rpc/impl/TtAppBackRpcImpl.java

@@ -64,8 +64,7 @@ public class TtAppBackRpcImpl implements ITtAppBackRpc {
         if(StringUtils.isBlank(dto.getCaid())) {
             dto.setCaid("");
         }
-        if(StringUtils.isBlank(dto.getUserId()) || (StringUtils.isBlank(dto.getMac())
-                && StringUtils.isBlank(dto.getImei())
+        if(StringUtils.isBlank(dto.getUserId()) || (StringUtils.isBlank(dto.getImei())
                 && StringUtils.isBlank(dto.getOaid())
                 && StringUtils.isBlank(dto.getAndroidId())
                 && StringUtils.isBlank(dto.getIdfa())
@@ -118,8 +117,7 @@ public class TtAppBackRpcImpl implements ITtAppBackRpc {
         if(StringUtils.isBlank(dto.getCaid())) {
             dto.setCaid("");
         }
-        if(StringUtils.isBlank(dto.getUserId()) || (StringUtils.isBlank(dto.getMac())
-                && StringUtils.isBlank(dto.getImei())
+        if(StringUtils.isBlank(dto.getUserId()) || (StringUtils.isBlank(dto.getImei())
                 && StringUtils.isBlank(dto.getOaid())
                 && StringUtils.isBlank(dto.getAndroidId())
                 && StringUtils.isBlank(dto.getIdfa())
@@ -188,8 +186,7 @@ public class TtAppBackRpcImpl implements ITtAppBackRpc {
         if(StringUtils.isBlank(dto.getCaid())) {
             dto.setCaid("");
         }
-        if(StringUtils.isBlank(dto.getUserId()) || (StringUtils.isBlank(dto.getMac())
-                && StringUtils.isBlank(dto.getImei())
+        if(StringUtils.isBlank(dto.getUserId()) || (StringUtils.isBlank(dto.getImei())
                 && StringUtils.isBlank(dto.getOaid())
                 && StringUtils.isBlank(dto.getAndroidId())
                 && StringUtils.isBlank(dto.getIdfa())

+ 2 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameOceanengineAppCallbackService.java

@@ -7,4 +7,6 @@ import com.zanxiang.game.back.serve.pojo.entity.GameOceanengineAppCallback;
 public interface IGameOceanengineAppCallbackService extends IService<GameOceanengineAppCallback> {
 
     boolean callback(GameOceanengineAppCallbackDTO dto);
+
+    GameOceanengineAppCallback getUserCallback(Long gameId, String imei, String oaid, String android, String idfa, String caid);
 }

+ 7 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiBackLogService.java

@@ -0,0 +1,7 @@
+package com.zanxiang.game.back.serve.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiBackLog;
+
+public interface IGameTencentAppApiBackLogService extends IService<GameTencentAppApiBackLog> {
+}

+ 18 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiOrderService.java

@@ -0,0 +1,18 @@
+package com.zanxiang.game.back.serve.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.github.sd4324530.jtuple.Tuple2;
+import com.zanxiang.game.back.serve.pojo.dto.OrderReportDTO;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiOrder;
+import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
+
+import java.time.LocalDateTime;
+
+public interface IGameTencentAppApiOrderService extends IService<GameTencentAppApiOrder> {
+
+    boolean orderBack(GameTencentAppApiOrder orderLog);
+
+    boolean tencentOrderReport(OrderReportDTO dto);
+
+    Tuple2<BackStatusEnum, String> doCallback(GameTencentAppApiOrder orderLog, LocalDateTime backTime, Long backMoney);
+}

+ 13 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiOrderSplitLogService.java

@@ -0,0 +1,13 @@
+package com.zanxiang.game.back.serve.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiOrderSplitLog;
+import com.zanxiang.game.back.serve.pojo.vo.GameTencentAppApiOrderSplitLogVO;
+
+import java.util.Collection;
+import java.util.List;
+
+public interface IGameTencentAppApiOrderSplitLogService extends IService<GameTencentAppApiOrderSplitLog> {
+
+    List<GameTencentAppApiOrderSplitLogVO> listByOrderNo(Collection<String> orderNos);
+}

+ 9 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiRoleRegisterService.java

@@ -0,0 +1,9 @@
+package com.zanxiang.game.back.serve.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiRoleRegister;
+
+public interface IGameTencentAppApiRoleRegisterService extends IService<GameTencentAppApiRoleRegister> {
+
+    boolean roleRegisterBack(GameTencentAppApiRoleRegister roleRegisterLog);
+}

+ 13 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppApiUserService.java

@@ -0,0 +1,13 @@
+package com.zanxiang.game.back.serve.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiUser;
+
+import java.util.List;
+
+public interface IGameTencentAppApiUserService extends IService<GameTencentAppApiUser> {
+
+    boolean userBack(GameTencentAppApiUser userLog, boolean mustBack);
+
+    boolean tencentUserReport(List<Long> ids);
+}

+ 2 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppCallbackService.java

@@ -7,4 +7,6 @@ import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppCallback;
 public interface IGameTencentAppCallbackService extends IService<GameTencentAppCallback> {
 
     boolean callback(GameTencentAppCallbackDTO dto);
+
+    GameTencentAppCallback getUserCallback(Long gameId, String imei, String oaid, String android, String idfa, String caid);
 }

+ 38 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameOceanengineAppCallbackServiceImpl.java

@@ -1,14 +1,21 @@
 package com.zanxiang.game.back.serve.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zanxiang.game.back.serve.dao.mapper.GameOceanengineAppCallbackMapper;
 import com.zanxiang.game.back.serve.pojo.dto.GameOceanengineAppCallbackDTO;
 import com.zanxiang.game.back.serve.pojo.entity.GameOceanengineAppCallback;
 import com.zanxiang.game.back.serve.service.IGameOceanengineAppCallbackService;
+import com.zanxiang.game.module.base.ServerInfo;
+import com.zanxiang.game.module.base.pojo.vo.AgentRpcVO;
+import com.zanxiang.game.module.base.rpc.IAgentRpc;
 import com.zanxiang.module.util.DateUtil;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
+import com.zanxiang.module.util.encryption.Md5Util;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.apache.kafka.clients.producer.KafkaProducer;
 import org.apache.kafka.clients.producer.ProducerRecord;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,11 +34,20 @@ implements IGameOceanengineAppCallbackService {
     private String oceanengineAppCallbackTopic;
     @Autowired
     private KafkaProducer<String, String> kafkaProducer;
+    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
+    private IAgentRpc agentRpc;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean callback(GameOceanengineAppCallbackDTO dto) {
+        AgentRpcVO agent = agentRpc.getByByteAccountId(dto.getAdvertiserId()).getData();
+        if (agent == null) {
+            log.error("头条检测链接数据找不到渠道:{}", JsonUtil.toString(dto));
+            return false;
+        }
         GameOceanengineAppCallback appCallback = BeanUtil.copy(dto, GameOceanengineAppCallback.class);
+        appCallback.setAgentKey(agent.getAgentKey());
+        appCallback.setGameId(agent.getGameId());
         appCallback.setDay(dto.getTs() == null || dto.getTs() < 1000 ? LocalDate.now() : DateUtil.milliToLocalDateTime(dto.getTs()).toLocalDate());
         save(appCallback);
         try {
@@ -41,4 +57,26 @@ implements IGameOceanengineAppCallbackService {
         }
         return true;
     }
+
+    @Override
+    public GameOceanengineAppCallback getUserCallback(Long gameId, String imei, String oaid, String android, String idfa, String caid) {
+        LambdaQueryWrapper<GameOceanengineAppCallback> qw = new LambdaQueryWrapper<GameOceanengineAppCallback>()
+                .eq(GameOceanengineAppCallback::getGameId, gameId)
+                .orderByDesc(GameOceanengineAppCallback::getTs)
+                .last("limit 1");
+        if (StringUtils.isNoneBlank(imei) || StringUtils.isNoneBlank(android) || StringUtils.isNoneBlank(oaid)) {
+            qw.and(and -> {
+                and.eq(StringUtils.isNoneBlank(imei), GameOceanengineAppCallback::getImei, StringUtils.isBlank(imei) ? null : Md5Util.encrypt32(imei).toLowerCase())
+                        .or().eq(StringUtils.isNoneBlank(android), GameOceanengineAppCallback::getAndroidid, StringUtils.isBlank(android) ? null : Md5Util.encrypt32(android).toLowerCase())
+                        .or().eq(StringUtils.isNoneBlank(oaid), GameOceanengineAppCallback::getOaid, oaid)
+                        .or().eq(StringUtils.isNoneBlank(oaid), GameOceanengineAppCallback::getOaidMd5, StringUtils.isBlank(oaid) ? null : Md5Util.encrypt32(oaid).toLowerCase());
+            });
+        } else if (StringUtils.isNoneBlank(idfa)) {
+            qw.and(and -> {
+                and.eq(GameOceanengineAppCallback::getIdfaMd5, StringUtils.isBlank(idfa) ? null : Md5Util.encrypt32(idfa).toLowerCase())
+                        .or().eq(GameOceanengineAppCallback::getIdfa, idfa);
+            });
+        }
+        return getOne(qw);
+    }
 }

+ 14 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiBackLogServiceImpl.java

@@ -0,0 +1,14 @@
+package com.zanxiang.game.back.serve.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.back.serve.dao.mapper.GameTencentAppApiBackLogMapper;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiBackLog;
+import com.zanxiang.game.back.serve.service.IGameTencentAppApiBackLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class GameTencentAppApiBackLogServiceImpl extends ServiceImpl<GameTencentAppApiBackLogMapper, GameTencentAppApiBackLog>
+implements IGameTencentAppApiBackLogService {
+}

+ 456 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiOrderServiceImpl.java

@@ -0,0 +1,456 @@
+package com.zanxiang.game.back.serve.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.github.sd4324530.jtuple.Tuple2;
+import com.github.sd4324530.jtuple.Tuple3;
+import com.zanxiang.game.back.base.pojo.enums.OrderStatusEnum;
+import com.zanxiang.game.back.serve.dao.mapper.GameTencentAppApiOrderMapper;
+import com.zanxiang.game.back.serve.pojo.TencentAppReport;
+import com.zanxiang.game.back.serve.pojo.dto.OrderReportDTO;
+import com.zanxiang.game.back.serve.pojo.entity.*;
+import com.zanxiang.game.back.serve.pojo.enums.ActionTypeEnum;
+import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
+import com.zanxiang.game.back.serve.pojo.enums.BackUnitEnum;
+import com.zanxiang.game.back.serve.service.*;
+import com.zanxiang.game.back.serve.utils.BackPolicyUtil;
+import com.zanxiang.module.util.DateUtil;
+import com.zanxiang.module.util.NumberUtil;
+import com.zanxiang.module.util.URIUtil;
+import com.zanxiang.module.util.encryption.Md5Util;
+import com.zanxiang.module.util.exception.BaseException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+@Slf4j
+@Service
+public class GameTencentAppApiOrderServiceImpl extends ServiceImpl<GameTencentAppApiOrderMapper, GameTencentAppApiOrder>
+implements IGameTencentAppApiOrderService {
+
+    @Autowired
+    private IGameTencentAppApiBackLogService gameTencentAppApiBackLogService;
+    @Autowired
+    private IGameBackPolicyService gameBackPolicyService;
+    @Autowired
+    private IGameTencentAppApiUserService gameTencentAppApiUserService;
+    @Autowired
+    private IGameTencentAppApiOrderSplitLogService gameTencentAppApiOrderSplitLogService;
+    @Autowired
+    private RestTemplate restTemplate;
+    @Autowired
+    private IGameTencentAppCallbackService gameTencentAppCallbackService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean orderBack(GameTencentAppApiOrder orderLog) {
+        if (!Objects.equals(orderLog.getOrderStatus(), OrderStatusEnum.SUCCESS_PAY.getValue())) {
+            // 只要回传 支付行为
+            return false;
+        }
+        GameTencentAppApiUser userLog = gameTencentAppApiUserService.getOne(new LambdaQueryWrapper<GameTencentAppApiUser>()
+                .eq(GameTencentAppApiUser::getGameId, orderLog.getGameId())
+                .eq(GameTencentAppApiUser::getUserId, orderLog.getUserId())
+                .eq(GameTencentAppApiUser::getAdAccountId, orderLog.getAdAccountId())
+                .orderByDesc(GameTencentAppApiUser::getCreateTime)
+                .last("limit 1")
+        );
+        if (userLog == null) {
+            return update(new LambdaUpdateWrapper<GameTencentAppApiOrder>()
+                    .set(GameTencentAppApiOrder::getIsBack, BackStatusEnum.FAILED.getBackStatus())
+                    .set(GameTencentAppApiOrder::getBackMoney, orderLog.getRechargeMoney())
+                    .set(GameTencentAppApiOrder::getBackMsg, "回传失败!找不到回传用户")
+                    .eq(GameTencentAppApiOrder::getId, orderLog.getId())
+            );
+        }
+
+        GameBackPolicy gameBackPolicy = gameBackPolicyService.getById(orderLog.getBackPolicyId());
+        Tuple3<Boolean, Long, String> backInfo = BackPolicyUtil.backOrder(orderLog.getOrderId(), gameBackPolicy, orderLog.getRechargeMoney(),
+                orderLog.getIsFirstOrder(),
+                orderLog.getPayTime(),
+                // 此处使用用户最近一次的重新染色时间
+                userLog.getCreateTime(),
+                orderLog.getUserId(),
+                new TencentOrderBackPolicyCheck(this, gameBackPolicy, userLog, orderLog));
+        boolean doBack = backInfo.first;
+        Long backMoney = backInfo.second;
+        String backMsg = backInfo.third;
+        if (!doBack) {
+            return update(new LambdaUpdateWrapper<GameTencentAppApiOrder>()
+                    .set(GameTencentAppApiOrder::getIsBack, BackStatusEnum.NO.getBackStatus())
+                    .set(GameTencentAppApiOrder::getBackMoney, backMoney)
+                    .set(GameTencentAppApiOrder::getBackMsg, backMsg)
+                    .eq(GameTencentAppApiOrder::getId, orderLog.getId())
+            );
+        }
+        // 拆单
+        Tuple3<Boolean, Long, List<Tuple2<Long, LocalDateTime>>> splitResult = BackPolicyUtil.splitOrder(gameBackPolicy, backMoney);
+        if (splitResult.first) {
+            // 需要拆单
+            List<GameTencentAppApiOrderSplitLog> splitOrderLogList = new ArrayList<>(splitResult.third.size());
+            for (int i = 0; i < splitResult.third.size(); i++) {
+                Tuple2<Long, LocalDateTime> splitOrder = splitResult.third.get(i);
+                splitOrderLogList.add(GameTencentAppApiOrderSplitLog.builder()
+                        .backDay(splitOrder.second.toLocalDate())
+                        .orderNo(orderLog.getOrderId())
+                        .backIndex(i + 1)
+                        .backCount(splitResult.third.size())
+                        .splitMoney(splitOrder.first)
+                        .backTime(splitOrder.second)
+                        .backStatus(BackStatusEnum.NO.getBackStatus())
+                        .createTime(LocalDateTime.now())
+                        .build());
+            }
+            gameTencentAppApiOrderSplitLogService.saveBatch(splitOrderLogList);
+            return update(new LambdaUpdateWrapper<GameTencentAppApiOrder>()
+                    .set(GameTencentAppApiOrder::getIsBack, BackStatusEnum.NO.getBackStatus())
+                    .set(GameTencentAppApiOrder::getBackMoney, splitResult.second)
+                    .set(GameTencentAppApiOrder::getBackMsg, backMsg)
+                    .eq(GameTencentAppApiOrder::getId, orderLog.getId())
+            );
+        }
+
+        Tuple2<BackStatusEnum, String> backResult = doCallback(orderLog, orderLog.getPayTime(), backMoney);
+        if (StringUtils.isNotBlank(backResult.second)) {
+            backMsg = backMsg + ("回传失败:" + backResult.second);
+        }
+        return update(new LambdaUpdateWrapper<GameTencentAppApiOrder>()
+                .set(GameTencentAppApiOrder::getIsBack, backResult.first.getBackStatus())
+                .set(GameTencentAppApiOrder::getBackMoney, backMoney)
+                .set(GameTencentAppApiOrder::getBackMsg, backMsg)
+                .eq(GameTencentAppApiOrder::getId, orderLog.getId())
+        );
+    }
+
+    @Override
+    public boolean tencentOrderReport(OrderReportDTO dto) {
+        listByIds(dto.getOrderIds()).stream()
+                .filter(order -> !Objects.equals(order.getIsBack(), BackStatusEnum.SUCCESS.getBackStatus()))
+                .forEach(orderLog -> {
+                    if (dto.getSplitOrder()) {
+                        AtomicLong total = new AtomicLong(0);
+                        dto.getSplitMoney().forEach(money -> {
+                            total.addAndGet(NumberUtil.multiply100(money).longValue());
+                        });
+                        if (!orderLog.getRechargeMoney().equals(total.get())) {
+                            throw new BaseException("订单充值金额和回传金额对不上");
+                        }
+                        log.error("手动拆单回传:{}-{}", orderLog.getId(), StringUtils.join(dto.getSplitMoney(), ","));
+                        List<GameTencentAppApiOrderSplitLog> splitOrderLogList = new ArrayList<>(dto.getSplitMoney().size());
+                        LocalDateTime beginTime = LocalDateTime.now();
+                        long backMoneyTotal = 0L;
+                        for (int i = 0; i < dto.getSplitMoney().size(); i++) {
+                            long backMoney = NumberUtil.multiply100(dto.getSplitMoney().get(i)).longValue();
+                            LocalDateTime backTime = beginTime;
+                            backMoneyTotal += backMoney;
+                            splitOrderLogList.add(GameTencentAppApiOrderSplitLog.builder()
+                                    .backDay(backTime.toLocalDate())
+                                    .orderNo(orderLog.getOrderId())
+                                    .backIndex(i + 1)
+                                    .backCount(dto.getSplitMoney().size())
+                                    .splitMoney(backMoney)
+                                    .backTime(backTime)
+                                    .backStatus(BackStatusEnum.NO.getBackStatus())
+                                    .createTime(LocalDateTime.now())
+                                    .build());
+                            beginTime = beginTime.plusMinutes(RandomUtils.nextInt(dto.getBetweenMinuteMin(), dto.getBetweenMinuteMax()));
+                        }
+                        gameTencentAppApiOrderSplitLogService.saveBatch(splitOrderLogList);
+                        update(new LambdaUpdateWrapper<GameTencentAppApiOrder>()
+                                .set(GameTencentAppApiOrder::getIsBack, BackStatusEnum.NO.getBackStatus())
+                                .set(GameTencentAppApiOrder::getBackMoney, backMoneyTotal)
+                                .set(GameTencentAppApiOrder::getBackMsg, "手动触发回传(拆单回传)")
+                                .eq(GameTencentAppApiOrder::getId, orderLog.getId())
+                        );
+                    } else {
+                        log.error("手动直接回传:{}-{}", orderLog.getId(), dto.getBackMoney());
+                        long backMoney = NumberUtil.multiply100(dto.getBackMoney()).longValue();
+                        Tuple2<BackStatusEnum, String> backResult = doCallback(orderLog, orderLog.getPayTime(), backMoney);
+                        String backMsg = "手动触发回传!";
+                        if (StringUtils.isNotBlank(backResult.second)) {
+                            backMsg += ("回传失败:" + backResult.second);
+                        }
+                        update(new LambdaUpdateWrapper<GameTencentAppApiOrder>()
+                                .set(GameTencentAppApiOrder::getIsBack, backResult.first.getBackStatus())
+                                .set(GameTencentAppApiOrder::getBackMoney, backMoney)
+                                .set(GameTencentAppApiOrder::getBackMsg, backMsg)
+                                .eq(GameTencentAppApiOrder::getId, orderLog.getId())
+                        );
+                    }
+                });
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Tuple2<BackStatusEnum, String> doCallback(GameTencentAppApiOrder orderLog, LocalDateTime backTime, Long backMoney) {
+
+        GameTencentAppCallback callback = gameTencentAppCallbackService.getUserCallback(orderLog.getGameId(), orderLog.getImei(), orderLog.getOaid(), orderLog.getAndroidId(), orderLog.getIdfa(), orderLog.getCaid());
+        if(callback == null) {
+            return Tuple2.with(BackStatusEnum.FAILED, "找不到监测链接");
+        }
+        String callbackUrl = URIUtil.decodeURIComponent(callback.getCallback());
+
+        Map<String, Object> actionParam = new HashMap<>(2);
+        actionParam.put("claim_type", 4);
+        actionParam.put("value", backMoney);
+        TencentAppReport reportData = TencentAppReport.builder()
+                .actions(Collections.singletonList(TencentAppReport.Action.builder()
+                        .action_time(DateUtil.localDateTimeToSecond(orderLog.getRegisterTime()))
+                        .userr_id(TencentAppReport.UserId.builder()
+                                .hash_imei(StringUtils.isBlank(orderLog.getImei()) ? null : Md5Util.encrypt32(orderLog.getImei().toLowerCase()).toLowerCase())
+                                .hash_oaid(StringUtils.isBlank(orderLog.getOaid()) ? null : Md5Util.encrypt32(orderLog.getOaid()).toLowerCase())
+                                .hash_android_id(StringUtils.isBlank(orderLog.getAndroidId()) ? null : Md5Util.encrypt32(orderLog.getAndroidId()).toLowerCase())
+                                .hash_idfa(StringUtils.isBlank(orderLog.getIdfa()) ? null : Md5Util.encrypt32(orderLog.getIdfa().toUpperCase()).toLowerCase())
+                                .caid(orderLog.getCaid())
+                                .build())
+                        .action_type("PURCHASE")
+                        .trace(callback.getClickId())
+                        .action_param(actionParam)
+                        .build()))
+                .build();
+
+
+        BackStatusEnum backStatus;
+        String backLog;
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(callbackUrl, reportData, String.class);
+            if (response.getStatusCode().is2xxSuccessful()) {
+                backStatus = BackStatusEnum.SUCCESS;
+                backLog = "回传成功";
+            } else {
+                backStatus = BackStatusEnum.FAILED;
+                backLog = "回传识别,失败信息:" + response.getBody();
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            backStatus = BackStatusEnum.FAILED;
+            backLog = "回传识别,失败原因:" + e.getMessage();
+        }
+
+        String actionType = ActionTypeEnum.PURCHASE.getActionType();
+
+        GameTencentAppApiBackLog gameTencentBackLog = GameTencentAppApiBackLog.builder()
+                .gameId(orderLog.getGameId())
+                .userId(orderLog.getUserId())
+                .adAccountId(orderLog.getAdAccountId())
+                .actionTime(backTime)
+                .createTime(LocalDateTime.now())
+                .actionType(actionType)
+                .orderId(orderLog.getOrderId())
+                .imei(orderLog.getImei())
+                .oaid(orderLog.getOaid())
+                .androidId(orderLog.getAndroidId())
+                .idfa(orderLog.getIdfa())
+                .caid(orderLog.getCaid())
+                .backLog(backLog)
+                .actionParam(actionParam.toString())
+                .build();
+        gameTencentAppApiBackLogService.save(gameTencentBackLog);
+        return Tuple2.with(backStatus, gameTencentBackLog.getBackLog());
+    }
+
+    public static class TencentOrderBackPolicyCheck implements BackPolicyUtil.IBackPolicyCheck {
+        private final IGameTencentAppApiOrderService gameTencentAppApiOrderService;
+        private final GameBackPolicy gameBackPolicy;
+        private final GameTencentAppApiUser userLog;
+        private final GameTencentAppApiOrder orderLog;
+
+        public TencentOrderBackPolicyCheck(IGameTencentAppApiOrderService gameTencentAppApiOrderService,
+                                           GameBackPolicy gameBackPolicy,
+                                           GameTencentAppApiUser userLog,
+                                           GameTencentAppApiOrder orderLog) {
+            this.gameTencentAppApiOrderService = gameTencentAppApiOrderService;
+            this.gameBackPolicy = gameBackPolicy;
+            this.userLog = userLog;
+            this.orderLog = orderLog;
+
+        }
+
+        @Override
+        public long backCountForFixedRate(int numberOfRound, BackUnitEnum backUnit, Boolean firstPolicy) {
+            numberOfRound = numberOfRound - 1;
+            if (backUnit == BackUnitEnum.UNIT_ONCE) {
+                return gameTencentAppApiOrderService.list(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .eq(GameTencentAppApiOrder::getIsFirstOrder, firstPolicy)
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                        .orderByDesc(GameTencentAppApiOrder::getCreateTime)
+                        .last("limit " + numberOfRound)
+                ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
+            } else if (backUnit == BackUnitEnum.UNIT_DAY) {
+                return gameTencentAppApiOrderService.list(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .apply(firstPolicy, "date(recharge_time) = date(pay_time)")
+                        .apply(!firstPolicy, "date(recharge_time) != date(pay_time)")
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                        .orderByDesc(GameTencentAppApiOrder::getCreateTime)
+                        .last("limit " + numberOfRound)
+                ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
+            } else if (backUnit == BackUnitEnum.UNIT_TIME_DAY) {
+                return gameTencentAppApiOrderService.list(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) < 24")
+                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) >= 24")
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                        .orderByDesc(GameTencentAppApiOrder::getCreateTime)
+                        .last("limit " + numberOfRound)
+                ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
+            } else if (backUnit == BackUnitEnum.UNIT_TIME_2DAY) {
+                return gameTencentAppApiOrderService.list(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) < 48")
+                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) >= 48")
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                        .orderByDesc(GameTencentAppApiOrder::getCreateTime)
+                        .last("limit " + numberOfRound)
+                ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
+            } else if (backUnit == BackUnitEnum.LARGE_AMOUNT) {
+                return gameTencentAppApiOrderService.list(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                        .orderByDesc(GameTencentAppApiOrder::getCreateTime)
+                        .last("limit " + numberOfRound)
+                ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
+            } else {
+                throw new RuntimeException("不支持的回传单位[" + backUnit.getValue() + "]");
+            }
+        }
+
+        @Override
+        public long backCountForUser(BackUnitEnum backUnit, String userId, Boolean firstPolicy) {
+            if (backUnit == BackUnitEnum.UNIT_ONCE) {
+                if (firstPolicy) {
+                    // 首单直接返回 0,必定回传
+                    return 0;
+                }
+                return gameTencentAppApiOrderService.count(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .eq(GameTencentAppApiOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
+                        .eq(GameTencentAppApiOrder::getUserId, userId)
+                        .eq(GameTencentAppApiOrder::getIsFirstOrder, firstPolicy)
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                );
+            } else if (backUnit == BackUnitEnum.UNIT_DAY) {
+                return gameTencentAppApiOrderService.count(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .eq(GameTencentAppApiOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
+                        .eq(GameTencentAppApiOrder::getUserId, userId)
+                        .apply(firstPolicy, "date(recharge_time) = date(pay_time)")
+                        .apply(!firstPolicy, "date(recharge_time) != date(pay_time)")
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                );
+            } else if (backUnit == BackUnitEnum.UNIT_TIME_DAY) {
+                return gameTencentAppApiOrderService.count(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .eq(GameTencentAppApiOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
+                        .eq(GameTencentAppApiOrder::getUserId, userId)
+                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) < 24")
+                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) >= 24")
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                );
+            } else if (backUnit == BackUnitEnum.UNIT_TIME_2DAY) {
+                return gameTencentAppApiOrderService.count(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .eq(GameTencentAppApiOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
+                        .eq(GameTencentAppApiOrder::getUserId, userId)
+                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) < 48")
+                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) >= 48")
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                );
+            } else if (backUnit == BackUnitEnum.LARGE_AMOUNT) {
+                return gameTencentAppApiOrderService.count(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                        .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                        .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                        .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                        .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                        .eq(GameTencentAppApiOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
+                        .eq(GameTencentAppApiOrder::getUserId, userId)
+                        .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                );
+            } else {
+                throw new RuntimeException("不支持的回传单位[" + backUnit.getValue() + "]");
+            }
+        }
+
+        @Override
+        public long markUpOfFixedRate(int numberOfRound, Long markUpTime) {
+            numberOfRound = numberOfRound - 1;
+            return gameTencentAppApiOrderService.list(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                    .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                    .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                    .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                    .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                    .apply("TIMESTAMPDIFF(MINUTE, recharge_time, pay_time) > {0}", markUpTime)
+                    .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+                    .orderByDesc(GameTencentAppApiOrder::getCreateTime)
+                    .last("limit " + numberOfRound)
+            ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
+        }
+
+        @Override
+        public long markUpForUser(String userId, Long markUpTime) {
+            return gameTencentAppApiOrderService.count(new LambdaQueryWrapper<GameTencentAppApiOrder>()
+                    .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                    .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                    .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                    .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                    .eq(GameTencentAppApiOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
+                    .eq(GameTencentAppApiOrder::getUserId, userId)
+                    .apply("TIMESTAMPDIFF(MINUTE, recharge_time, pay_time) > {0}", markUpTime)
+                    .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+            );
+        }
+
+        @Override
+        public Long totalRechargeAmount() {
+            return gameTencentAppApiOrderService.count(new QueryWrapper<GameTencentAppApiOrder>()
+                    .select("ifnull(sum(recharge_money), 0) as recharge_money").lambda()
+                    .eq(GameTencentAppApiOrder::getGameId, orderLog.getGameId())
+                    .eq(GameTencentAppApiOrder::getAdAccountId, orderLog.getAdAccountId())
+                    .eq(GameTencentAppApiOrder::getBackPolicyId, gameBackPolicy.getId())
+                    .eq(GameTencentAppApiOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
+                    .eq(GameTencentAppApiOrder::getUserId, orderLog.getUserId())
+                    .ne(GameTencentAppApiOrder::getOrderId, orderLog.getOrderId())
+            );
+        }
+    }
+}

+ 49 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiOrderSplitLogServiceImpl.java

@@ -0,0 +1,49 @@
+package com.zanxiang.game.back.serve.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.back.serve.dao.mapper.GameTencentAppApiOrderSplitLogMapper;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiOrderSplitLog;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentOrderSplitLog;
+import com.zanxiang.game.back.serve.pojo.vo.GameTencentAppApiOrderSplitLogVO;
+import com.zanxiang.game.back.serve.pojo.vo.GameTencentOrderSplitLogVO;
+import com.zanxiang.game.back.serve.service.IGameTencentAppApiOrderSplitLogService;
+import com.zanxiang.module.util.NumberUtil;
+import com.zanxiang.module.util.bean.BeanUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class GameTencentAppApiOrderSplitLogServiceImpl extends ServiceImpl<GameTencentAppApiOrderSplitLogMapper, GameTencentAppApiOrderSplitLog>
+implements IGameTencentAppApiOrderSplitLogService {
+
+    @Override
+    public List<GameTencentAppApiOrderSplitLogVO> listByOrderNo(Collection<String> orderNos) {
+        if (CollectionUtils.isEmpty(orderNos)) {
+            return Collections.emptyList();
+        }
+        return list(new LambdaQueryWrapper<GameTencentAppApiOrderSplitLog>()
+                .in(GameTencentAppApiOrderSplitLog::getOrderNo, orderNos)
+                .orderByAsc(GameTencentAppApiOrderSplitLog::getBackIndex)
+        ).stream().map(this::toVOSimple).collect(Collectors.toList());
+    }
+
+    private GameTencentAppApiOrderSplitLogVO toVOSimple(GameTencentAppApiOrderSplitLog log) {
+        if (log == null) {
+            return null;
+        }
+        GameTencentAppApiOrderSplitLogVO vo = BeanUtil.copy(log, GameTencentAppApiOrderSplitLogVO.class);
+        if (log.getSplitMoney() != null) {
+            vo.setSplitMoney(NumberUtil.divide100(new BigDecimal(log.getSplitMoney())));
+        }
+        return vo;
+    }
+}

+ 49 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiRoleRegisterServiceImpl.java

@@ -0,0 +1,49 @@
+package com.zanxiang.game.back.serve.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.back.serve.dao.mapper.GameTencentAppApiRoleRegisterMapper;
+import com.zanxiang.game.back.serve.pojo.entity.*;
+import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
+import com.zanxiang.game.back.serve.service.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Service
+public class GameTencentAppApiRoleRegisterServiceImpl extends ServiceImpl<GameTencentAppApiRoleRegisterMapper, GameTencentAppApiRoleRegister>
+implements IGameTencentAppApiRoleRegisterService {
+
+    @Autowired
+    private IGameTencentAppApiUserService gameTencentAppApiUserService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean roleRegisterBack(GameTencentAppApiRoleRegister roleRegisterLog) {
+        BackStatusEnum backStatus = doCallback(roleRegisterLog);
+        return update(new LambdaUpdateWrapper<GameTencentAppApiRoleRegister>()
+                .set(GameTencentAppApiRoleRegister::getIsBack, backStatus.getBackStatus())
+                .eq(GameTencentAppApiRoleRegister::getId, roleRegisterLog.getId())
+        );
+    }
+
+
+    private BackStatusEnum doCallback(GameTencentAppApiRoleRegister roleRegisterLog) {
+        GameTencentAppApiUser user = gameTencentAppApiUserService.getOne(new LambdaQueryWrapper<GameTencentAppApiUser>()
+                .eq(GameTencentAppApiUser::getGameId, roleRegisterLog.getGameId())
+                .eq(GameTencentAppApiUser::getUserId, roleRegisterLog.getUserId())
+                .eq(GameTencentAppApiUser::getAdAccountId, roleRegisterLog.getAdAccountId())
+                .orderByDesc(GameTencentAppApiUser::getCreateTime)
+                .last("limit 1")
+        );
+        if (user != null) {
+            if (BackStatusEnum.NO.getBackStatus().equals(user.getIsBack())) {
+                gameTencentAppApiUserService.userBack(user, true);
+            }
+        }
+        return BackStatusEnum.NO;
+    }
+}

+ 130 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppApiUserServiceImpl.java

@@ -0,0 +1,130 @@
+package com.zanxiang.game.back.serve.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.back.serve.dao.mapper.GameTencentAppApiUserMapper;
+import com.zanxiang.game.back.serve.pojo.TencentAppReport;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiBackLog;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppApiUser;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppCallback;
+import com.zanxiang.game.back.serve.pojo.enums.ActionTypeEnum;
+import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
+import com.zanxiang.game.back.serve.service.IGameTencentAppApiBackLogService;
+import com.zanxiang.game.back.serve.service.IGameTencentAppApiUserService;
+import com.zanxiang.game.back.serve.service.IGameTencentAppCallbackService;
+import com.zanxiang.module.util.DateUtil;
+import com.zanxiang.module.util.URIUtil;
+import com.zanxiang.module.util.encryption.Md5Util;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+
+@Slf4j
+@Service
+public class GameTencentAppApiUserServiceImpl extends ServiceImpl<GameTencentAppApiUserMapper, GameTencentAppApiUser>
+implements IGameTencentAppApiUserService {
+    @Autowired
+    private IGameTencentAppApiBackLogService gameTencentAppApiBackLogService;
+    @Autowired
+    private IGameTencentAppCallbackService gameTencentAppCallbackService;
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean userBack(GameTencentAppApiUser userLog, boolean mustBack) {
+        BackStatusEnum backStatus = doCallback(userLog, mustBack);
+        return update(new LambdaUpdateWrapper<GameTencentAppApiUser>()
+                .set(GameTencentAppApiUser::getIsBack, backStatus.getBackStatus())
+                .eq(GameTencentAppApiUser::getId, userLog.getId())
+        );
+    }
+
+    @Override
+    public boolean tencentUserReport(List<Long> ids) {
+        listByIds(ids).forEach(userLog -> {
+            BackStatusEnum backStatus = doCallback(userLog, true);
+            update(new LambdaUpdateWrapper<GameTencentAppApiUser>()
+                    .set(GameTencentAppApiUser::getIsBack, backStatus.getBackStatus())
+                    .eq(GameTencentAppApiUser::getId, userLog.getId())
+            );
+        });
+        return true;
+    }
+
+
+    private BackStatusEnum doCallback(GameTencentAppApiUser userLog, boolean mustBack) {
+        boolean isBack = mustBack ? false : gameTencentAppApiBackLogService.count(new LambdaQueryWrapper<GameTencentAppApiBackLog>()
+                .eq(GameTencentAppApiBackLog::getGameId, userLog.getGameId())
+                .eq(GameTencentAppApiBackLog::getUserId, userLog.getUserId())
+                .eq(GameTencentAppApiBackLog::getAdAccountId, userLog.getAdAccountId())
+                .eq(GameTencentAppApiBackLog::getBackLog, "回传成功")
+        ) > 0;
+        if (isBack) {
+            return BackStatusEnum.NO;
+        }
+        GameTencentAppCallback callback = gameTencentAppCallbackService.getUserCallback(userLog.getGameId(), userLog.getImei(), userLog.getOaid(), userLog.getAndroidId(), userLog.getIdfa(), userLog.getCaid());
+        if(callback == null) {
+            return BackStatusEnum.FAILED;
+        }
+        String callbackUrl = URIUtil.decodeURIComponent(callback.getCallback());
+
+        TencentAppReport reportData = TencentAppReport.builder()
+                .actions(Collections.singletonList(TencentAppReport.Action.builder()
+                        .action_time(DateUtil.localDateTimeToSecond(userLog.getRegisterTime()))
+                        .userr_id(TencentAppReport.UserId.builder()
+                                .hash_imei(StringUtils.isBlank(userLog.getImei()) ? null : Md5Util.encrypt32(userLog.getImei().toLowerCase()).toLowerCase())
+                                .hash_oaid(StringUtils.isBlank(userLog.getOaid()) ? null : Md5Util.encrypt32(userLog.getOaid()).toLowerCase())
+                                .hash_android_id(StringUtils.isBlank(userLog.getAndroidId()) ? null : Md5Util.encrypt32(userLog.getAndroidId()).toLowerCase())
+                                .hash_idfa(StringUtils.isBlank(userLog.getIdfa()) ? null : Md5Util.encrypt32(userLog.getIdfa().toUpperCase()).toLowerCase())
+                                .caid(userLog.getCaid())
+                                .build())
+                        .action_type("ACTIVATE_APP")
+                        .trace(callback.getClickId())
+                        .build()))
+                .build();
+
+        BackStatusEnum backStatus;
+        String backLog;
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(callbackUrl, reportData, String.class);
+            if (response.getStatusCode().is2xxSuccessful()) {
+                backStatus = BackStatusEnum.SUCCESS;
+                backLog = "回传成功";
+            } else {
+                backStatus = BackStatusEnum.FAILED;
+                backLog = "回传识别,失败信息:" + response.getBody();
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            backStatus = BackStatusEnum.FAILED;
+            backLog = "回传识别,失败原因:" + e.getMessage();
+        }
+
+        GameTencentAppApiBackLog gameTencentBackLog = GameTencentAppApiBackLog.builder()
+                .gameId(userLog.getGameId())
+                .userId(userLog.getUserId())
+                .adAccountId(userLog.getAdAccountId())
+                .actionTime(userLog.getRegisterTime())
+                .createTime(LocalDateTime.now())
+                .actionType(ActionTypeEnum.REGISTER.getActionType())
+                .imei(userLog.getImei())
+                .oaid(userLog.getOaid())
+                .androidId(userLog.getAndroidId())
+                .idfa(userLog.getIdfa())
+                .caid(userLog.getCaid())
+                .backLog(backLog)
+                .build();
+        gameTencentAppApiBackLogService.save(gameTencentBackLog);
+        return backStatus;
+    }
+}

+ 27 - 13
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppCallbackServiceImpl.java

@@ -6,12 +6,17 @@ import com.zanxiang.game.back.serve.dao.mapper.GameTencentAppCallbackMapper;
 import com.zanxiang.game.back.serve.pojo.dto.GameTencentAppCallbackDTO;
 import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppCallback;
 import com.zanxiang.game.back.serve.service.IGameTencentAppCallbackService;
+import com.zanxiang.game.module.base.ServerInfo;
+import com.zanxiang.game.module.base.pojo.vo.AgentRpcVO;
+import com.zanxiang.game.module.base.rpc.IAgentRpc;
 import com.zanxiang.module.util.DateUtil;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
 import com.zanxiang.module.util.encryption.Md5Util;
+import com.zanxiang.module.util.pojo.ResultVO;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.apache.kafka.clients.producer.KafkaProducer;
 import org.apache.kafka.clients.producer.ProducerRecord;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -30,11 +35,20 @@ implements IGameTencentAppCallbackService {
     private String tencentAppCallbackTopic;
     @Autowired
     private KafkaProducer<String, String> kafkaProducer;
+    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
+    private IAgentRpc agentRpc;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean callback(GameTencentAppCallbackDTO dto) {
+        AgentRpcVO agent = agentRpc.getByTencentAccountId(dto.getAccountId()).getData();
+        if (agent == null) {
+            log.error("腾讯检测链接数据找不到渠道:{}", JsonUtil.toString(dto));
+            return false;
+        }
         GameTencentAppCallback appCallback = BeanUtil.copy(dto, GameTencentAppCallback.class);
+        appCallback.setAgentKey(agent.getAgentKey());
+        appCallback.setGameId(agent.getGameId());
         appCallback.setDay(dto.getClickTime() == null || dto.getClickTime() < 1000 ? LocalDate.now() : DateUtil.milliToLocalDateTime(dto.getClickTime()).toLocalDate());
         save(appCallback);
         try {
@@ -45,21 +59,21 @@ implements IGameTencentAppCallbackService {
         return true;
     }
 
-    public GameTencentAppCallback getUserClickInfo(String imei, String oaid, String android, String idfa, String caid) {
-        LambdaQueryWrapper<GameTencentAppCallback> qw = new LambdaQueryWrapper<>();
-        if (StringUtils.isNoneBlank(imei)) {
-            qw.or().eq(GameTencentAppCallback::getMuid, Md5Util.encrypt32(imei.toLowerCase()).toLowerCase());
+    @Override
+    public GameTencentAppCallback getUserCallback(Long gameId, String imei, String oaid, String android, String idfa, String caid) {
+        LambdaQueryWrapper<GameTencentAppCallback> qw = new LambdaQueryWrapper<GameTencentAppCallback>()
+                .eq(GameTencentAppCallback::getGameId, gameId)
+                .orderByDesc(GameTencentAppCallback::getClickTime)
+                .last("limit 1");
+        if (StringUtils.isNoneBlank(imei) || StringUtils.isNoneBlank(android) || StringUtils.isNoneBlank(oaid)) {
+            qw.and(and -> {
+                and.eq(StringUtils.isNoneBlank(imei), GameTencentAppCallback::getMuid, StringUtils.isBlank(imei) ? null : Md5Util.encrypt32(imei.toLowerCase()).toLowerCase())
+                        .or().eq(StringUtils.isNoneBlank(android), GameTencentAppCallback::getHashAndroidId, StringUtils.isBlank(android) ? null : Md5Util.encrypt32(android).toLowerCase())
+                        .or().eq(StringUtils.isNoneBlank(oaid), GameTencentAppCallback::getHashOaid, StringUtils.isBlank(oaid) ? null : Md5Util.encrypt32(oaid).toLowerCase());
+            });
         } else if (StringUtils.isNoneBlank(idfa)) {
-            qw.or().eq(GameTencentAppCallback::getMuid, Md5Util.encrypt32(idfa).toLowerCase());
+            qw.eq(GameTencentAppCallback::getMuid, Md5Util.encrypt32(idfa).toLowerCase());
         }
-        if(StringUtils.isNoneBlank(android)) {
-            qw.or().eq(GameTencentAppCallback::getHashAndroidId, Md5Util.encrypt32(android).toLowerCase());
-        }
-        if(StringUtils.isNoneBlank(oaid)) {
-            qw.or().eq(GameTencentAppCallback::getHashOaid, Md5Util.encrypt32(oaid).toLowerCase());
-        }
-        qw.orderByDesc(GameTencentAppCallback::getClickTime)
-                .last("limit 1");
         return getOne(qw);
     }
 }

+ 0 - 6
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentUserServiceImpl.java

@@ -9,16 +9,12 @@ import com.zanxiang.advertising.tencent.base.AdvertisingTencentServer;
 import com.zanxiang.advertising.tencent.base.pojo.dto.DataReportOfAppIdRpcDTO;
 import com.zanxiang.advertising.tencent.base.pojo.dto.UserActionRpcDTO;
 import com.zanxiang.advertising.tencent.base.rpc.IUserActionSetRpc;
-import com.zanxiang.erp.base.ErpServer;
-import com.zanxiang.erp.base.rpc.ISysUserRpc;
 import com.zanxiang.game.back.serve.dao.mapper.GameTencentUserMapper;
 import com.zanxiang.game.back.serve.pojo.dto.GameTencentUserDTO;
 import com.zanxiang.game.back.serve.pojo.entity.GameTencentBackLog;
-import com.zanxiang.game.back.serve.pojo.entity.GameTencentMiniGameUser;
 import com.zanxiang.game.back.serve.pojo.entity.GameTencentUser;
 import com.zanxiang.game.back.serve.pojo.enums.ActionTypeEnum;
 import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
-import com.zanxiang.game.back.serve.pojo.vo.GameTencentMiniGameUserVO;
 import com.zanxiang.game.back.serve.pojo.vo.GameTencentUserVO;
 import com.zanxiang.game.back.serve.service.IGameTencentBackLogService;
 import com.zanxiang.game.back.serve.service.IGameTencentUserService;
@@ -60,8 +56,6 @@ public class GameTencentUserServiceImpl extends ServiceImpl<GameTencentUserMappe
 
     @DubboReference(providedBy = AdvertisingTencentServer.SERVER_DUBBO_NAME)
     private IUserActionSetRpc userActionSetRpc;
-    @DubboReference(providedBy = ErpServer.SERVER_DUBBO_NAME)
-    private ISysUserRpc sysUserRpc;
     @Autowired
     private IGameTencentBackLogService gameTencentBackLogService;
     @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)

+ 3 - 1
game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/pojo/enums/AccountTypeEnum.java

@@ -15,7 +15,9 @@ public enum AccountTypeEnum {
     // 腾讯APP
     TENCENT_APP(4),
     // 头条APP
-    BYTE_APP(5)
+    BYTE_APP(5),
+    // 腾讯APP(企微信链路)
+    TENCENT_APP_API(6),
     ;
 
     private final Integer value;

+ 4 - 0
game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/rpc/IAgentRpc.java

@@ -8,4 +8,8 @@ import java.util.List;
 public interface IAgentRpc {
 
     ResultVO<List<AgentRpcVO>> getByAgentKeys(List<String> agentKeys);
+
+    ResultVO<AgentRpcVO>  getByByteAccountId(Long accountId);
+
+    ResultVO<AgentRpcVO>  getByTencentAccountId(Long accountId);
 }

+ 18 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/rpc/impl/AgentRpcImpl.java

@@ -1,6 +1,7 @@
 package com.zanxiang.game.module.manage.rpc.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.zanxiang.game.module.base.pojo.enums.AccountTypeEnum;
 import com.zanxiang.game.module.base.pojo.vo.AgentRpcVO;
 import com.zanxiang.game.module.base.rpc.IAgentRpc;
 import com.zanxiang.game.module.manage.service.IAgentService;
@@ -11,6 +12,7 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -19,6 +21,22 @@ public class AgentRpcImpl implements IAgentRpc {
     @Autowired
     private IAgentService agentService;
 
+    @Override
+    public ResultVO<AgentRpcVO>  getByByteAccountId(Long accountId) {
+        return ResultVO.ok(toVOSimple(agentService.getOne(new LambdaQueryWrapper<Agent>()
+                .eq(Agent::getAccountId, accountId)
+                .in(Agent::getAccountType, Arrays.asList(AccountTypeEnum.BYTE.getValue(), AccountTypeEnum.BYTE_APP.getValue()))
+        )));
+    }
+
+    @Override
+    public ResultVO<AgentRpcVO>  getByTencentAccountId(Long accountId) {
+        return ResultVO.ok(toVOSimple(agentService.getOne(new LambdaQueryWrapper<Agent>()
+                .eq(Agent::getAccountId, accountId)
+                .in(Agent::getAccountType, Arrays.asList(AccountTypeEnum.TENCENT_H5.getValue(), AccountTypeEnum.TENCENT_MINI_GAME.getValue(), AccountTypeEnum.TENCENT_APP.getValue(), AccountTypeEnum.TENCENT_APP_API.getValue()))
+        )));
+    }
+
     @Override
     public ResultVO<List<AgentRpcVO>> getByAgentKeys(List<String> agentKeys) {
         if (CollectionUtils.isEmpty(agentKeys)) {