Browse Source

腾讯头条监测链接

wcc 9 months ago
parent
commit
ad0c5902e7
16 changed files with 1116 additions and 0 deletions
  1. 6 0
      game-back/game-back-serve/pom.xml
  2. 27 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/config/KafkaConfig.java
  3. 3 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/config/NacosDynamicParamConfig.java
  4. 30 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/controller/api/OceanengineCallbackApi.java
  5. 29 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/controller/api/TencentCallbackApi.java
  6. 7 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameOceanengineAppCallbackMapper.java
  7. 7 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppCallbackMapper.java
  8. 218 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/dto/GameOceanengineAppCallbackDTO.java
  9. 185 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/dto/GameTencentAppCallbackDTO.java
  10. 239 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameOceanengineAppCallback.java
  11. 204 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/entity/GameTencentAppCallback.java
  12. 10 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameOceanengineAppCallbackService.java
  13. 10 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/IGameTencentAppCallbackService.java
  14. 44 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameOceanengineAppCallbackServiceImpl.java
  15. 44 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppCallbackServiceImpl.java
  16. 53 0
      game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/task/CallbackClearTask.java

+ 6 - 0
game-back/game-back-serve/pom.xml

@@ -70,6 +70,12 @@
             <groupId>com.alibaba.nacos</groupId>
             <groupId>com.alibaba.nacos</groupId>
             <artifactId>nacos-client</artifactId>
             <artifactId>nacos-client</artifactId>
         </dependency>
         </dependency>
+        <!-- kafka -->
+        <dependency>
+            <groupId>org.apache.kafka</groupId>
+            <artifactId>kafka-clients</artifactId>
+            <version>2.2.1</version>
+        </dependency>
         <dependency>
         <dependency>
             <groupId>com.zanxiang.erp</groupId>
             <groupId>com.zanxiang.erp</groupId>
             <artifactId>erp-security</artifactId>
             <artifactId>erp-security</artifactId>

+ 27 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/config/KafkaConfig.java

@@ -0,0 +1,27 @@
+package com.zanxiang.game.back.serve.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.common.serialization.StringSerializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+@Slf4j
+@Configuration
+public class KafkaConfig {
+    @Value("${spring.kafka.bootstrap-servers}")
+    private String kafkaSevers;
+
+    @Bean
+    public KafkaProducer<String, String> gameKafkaProducer() {
+        Properties props = new Properties();
+        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaSevers);
+        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+        return new KafkaProducer<>(props);
+    }
+}

+ 3 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/config/NacosDynamicParamConfig.java

@@ -9,4 +9,7 @@ import org.springframework.stereotype.Component;
 @Component
 @Component
 @RefreshScope
 @RefreshScope
 public class NacosDynamicParamConfig {
 public class NacosDynamicParamConfig {
+
+    @Value("${nacos.dynamic.callbackClearDay:15}")
+    private Integer callbackClearDay;
 }
 }

+ 30 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/controller/api/OceanengineCallbackApi.java

@@ -0,0 +1,30 @@
+package com.zanxiang.game.back.serve.controller.api;
+
+import com.zanxiang.game.back.serve.pojo.dto.GameOceanengineAppCallbackDTO;
+import com.zanxiang.game.back.serve.service.IGameOceanengineAppCallbackService;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.pojo.ResultVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 头条监测链接
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/oceanengineCallback")
+public class OceanengineCallbackApi {
+
+    @Autowired
+    private IGameOceanengineAppCallbackService gameOceanengineAppCallbackService;
+
+    // https://xxxx.xxx.com?promotionid=__PROMOTION_ID__&mid1=__MID1__&idfa=__IDFA__&mac=__MAC__&os=__OS__&TIMESTAMP=__TS__&callback=__CALLBACK_PARAM__
+    @GetMapping("/callback")
+    public ResultVO<Boolean> callback(GameOceanengineAppCallbackDTO dto) {
+        log.info("头条监测链接:{}", JsonUtil.toString(dto));
+        return ResultVO.ok(gameOceanengineAppCallbackService.callback(dto));
+    }
+}

+ 29 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/controller/api/TencentCallbackApi.java

@@ -0,0 +1,29 @@
+package com.zanxiang.game.back.serve.controller.api;
+
+import com.zanxiang.game.back.serve.pojo.dto.GameTencentAppCallbackDTO;
+import com.zanxiang.game.back.serve.service.IGameTencentAppCallbackService;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.pojo.ResultVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 腾讯监测链接
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/tencentCallback")
+public class TencentCallbackApi {
+    @Autowired
+    private IGameTencentAppCallbackService gameTencentAppCallbackService;
+
+    // https://www.example.com/click/?click_id=__CLICK_ID__&click_time=__CLICK_TIME__&adgroup_id=__ADGROUP_ID__&ad_platform_type=__AD_PLATFORM_TYPE__&ad_type=__AD_TYPE__&account_id=__ACCOUNT_ID__&agency_id=__AGENCY_ID__&click_sku_id=__CLICK_SKU_ID__&billing_event=__BILLING_EVENT__&device_os_type=__DEVICE_OS_TYPE__&process_time=__PROCESS_TIME__&promoted_object_type=__PROMOTED_OBJECT_TYPE__&request_id=__REQUEST_ID__&callback=__CALLBACK__&adgroup_name=__ADGROUP_NAME__&dynamic_creative_id=__DYNAMIC_CREATIVE_ID__&dynamic_creative_name=__DYNAMIC_CREATIVE_NAME__&creative_components_info=__CREATIVE_COMPONENTS_INFO__&element_info=__ELEMENT_INFO__&marketing_goal=__MARKETING_GOAL__&marketing_sub_goal=__MARKETING_SUB_GOAL__&marketing_target_id=__MARKETING_TARGET_ID__&marketing_carrier_id=__MARKETING_CARRIER_ID__&marketing_sub_carrier_id=__MARKETING_SUB_CARRIER_ID__&marketing_asset_id=__MARKETING_ASSET_ID__&material_package_id=__MATERIAL_PACKAGE_ID__&hash_android_id=__HASH_ANDROID_ID__&hash_oaid=__HASH_OAID__&muid=__MUID__
+    @GetMapping("/callback")
+    public ResultVO<Boolean> callback(GameTencentAppCallbackDTO dto) {
+        log.info("腾讯监测链接:{}", JsonUtil.toString(dto));
+        return ResultVO.ok(gameTencentAppCallbackService.callback(dto));
+    }
+}

+ 7 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameOceanengineAppCallbackMapper.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.GameOceanengineAppCallback;
+
+public interface GameOceanengineAppCallbackMapper extends BaseMapper<GameOceanengineAppCallback> {
+}

+ 7 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/dao/mapper/GameTencentAppCallbackMapper.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.GameTencentAppCallback;
+
+public interface GameTencentAppCallbackMapper extends BaseMapper<GameTencentAppCallback> {
+}

+ 218 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/dto/GameOceanengineAppCallbackDTO.java

@@ -0,0 +1,218 @@
+package com.zanxiang.game.back.serve.pojo.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 头条游戏 APP 监测链接回传数据(文档:https://open.oceanengine.com/labels/7/docs/1696710655781900)
+ * </p>
+ *
+ */
+@Data
+public class GameOceanengineAppCallbackDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 巨量广告体验版中特有的宏参,代表巨量广告体验版的广告ID, ex: ID: 7074140945750507528, 宏: PROMOTION_ID
+     */
+    private Long promotionId;
+
+    /**
+     * 巨量广告体验版中特有的宏参,代表巨量广告体验版的项目ID, ex: ID: 7074140945750507528, 宏: PROJECT_ID
+     */
+    private Long projectId;
+
+    /**
+     * 针对巨量广告体验版,图片素材宏参数(下发原始素材id), 宏: MID1
+     */
+    private String mid1;
+
+    /**
+     * 针对巨量广告体验版,标题素材宏参数(下发原始素材id), 宏: MID2
+     */
+    private String mid2;
+
+    /**
+     * 针对巨量广告体验版,视频素材宏参数(下发原始素材id), 宏: MID3
+     */
+    private String mid3;
+
+    /**
+     * 针对巨量广告体验版,搭配试玩素材宏参数(下发原始素材id), 宏: MID4
+     */
+    private String mid4;
+
+    /**
+     * 针对巨量广告体验版,落地页素材宏参数(下发原始素材id), 宏: MID5
+     */
+    private String mid5;
+
+    /**
+     * 针对巨量广告体验版,安卓下载详情页素材宏参数(下发原始素材id), 宏: MID6
+     */
+    private String mid6;
+
+    /**
+     * 广告计划id, ex: 1645988237525045, 宏: AID
+     */
+    private Long aid;
+
+    /**
+     * 广告创意 id, ex: 1650703686054530, 宏: CID
+     */
+    private Long cid;
+
+    /**
+     * 广告组 id, ex: 1651688272934434, 宏: CAMPAIGN_ID
+     */
+    private Long campaignId;
+
+    /**
+     * 创意样式, 2=小图模式、3=大图模式、4=组图模式、5=视频, 宏: CTYPE
+     */
+    private Integer ctype;
+
+    /**
+     * 广告主id, ex: 1631857582073864, 宏: ADVERTISER_ID
+     */
+    private Long advertiserId;
+
+    /**
+     * 广告投放位置, 今日头条:1-10000,80000-110001、西瓜视频:10001-10099、火山小视频:30001-30099、抖音:40001-40099、番茄小说:26001-26099、穿山甲开屏广告:800000000、穿山甲网盟非开屏广告:900000000、通投广告位:33013、搜索:38016, 宏: CSITE
+     */
+    private Integer csite;
+
+    /**
+     * 转化id, ex: 1681681272671401, 宏: CONVERT_ID
+     */
+    private Long convertId;
+
+    /**
+     * 请求下发的id, ex: 20191202180607010026077068090BA60A, 宏: REQUEST_ID
+     */
+    private String requestId;
+
+    /**
+     * 请求下发的id&创意id的md5,16位, ex: b2f97be5b363764d, 宏: TRACK_ID
+     */
+    private String trackId;
+
+    /**
+     * 这次请求的语言, ex: zh, 宏: SL
+     */
+    private String sl;
+
+    /**
+     * 安卓的设备 ID 的 md5 摘要,32位, 假设原始的 imei 是 868823031739689我们最终发送的是 imei 的 md5 摘要:0c2bd03c39f19845bf54ea0abafae70e,会存在部分无法获取imei的情况,则imei会回传空字符串或者0的md5摘要, 宏: IMEI
+     */
+    private String imei;
+
+    /**
+     * IOS 6+的设备id字段,32位, ex: 4FCFEFA1-096D-4176-B352-1870ED6DB777注意,一些例外00000000-0000-0000-0000-000000000000, 如果用户关闭里读取idfa的权限,会导致idfa全部是0, 宏: IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS 6+的设备id的md5摘要,32位, ex: 09c593c62a6074ae5f859e97a222c0e8,注意,用户关闭读取idfa权限,0值也会进行MD5加密, 宏: IDFA_MD5
+     */
+    private String idfaMd5;
+
+    /**
+     * 安卓id原值的md5,32位, ex: androidId的原值是: 7b5ca2d57178d2f1我们进行md5摘要,最终结果是:873541edf36da9170af47d5b69e82193, 宏: ANDROIDID
+     */
+    private String androidid;
+
+    /**
+     * Android Q及更高版本的设备号,32位, ex: 97e7ef3f-e5f2-d0b8-ccfc-f79bbeaf4841注意,一些例外如果无法获取oaid,oaid会传空字符串, 宏: OAID
+     */
+    private String oaid;
+
+    /**
+     * Android Q及更高版本的设备号的md5摘要,32位, ex: 87f8274c36eb73fabcbf143a10eca6a4,会存在部分无法获取oaid的情况,则该字段会回传空字符串, 宏: OAID_MD5
+     */
+    private String oaidMd5;
+
+    /**
+     * 操作系统平台, 安卓:0、IOS:1、其它:3, 宏: OS
+     */
+    private Integer os;
+
+    /**
+     * 移动设备mac地址,转换成大写字母,去掉“:”,并且取md5摘要后的结果, 假设原始的mac地址是34:d7:12:9b:3a:89去掉“:”后变成 34d7129b3a89变成大写字母34D7129B3A89取MD5摘要,最后变成如下结果:df97bc5021e14256e141b2f036df5a3c, 宏: MAC
+     */
+    private String mac;
+
+    /**
+     * 移动设备 mac 地址,转换成大写字母,并且取md5摘要后的结果,32位, 假设原始的mac地址是 34:d7:12:9b:3a:89变成大写字母: 34:D7:12:9B:3A:89进行MD5摘要,最后变成如下结果:4d0433eb614f5d5ad409a65395426cbe, 宏: MAC1
+     */
+    private String mac1;
+
+    /**
+     * 优先使用上报请求的对端 IP 地址。如果该IP为 IPv6, 则使用客户端获取的 client_ipv4 地址, ex: 61.168.146.195或为空, 宏: IPV4
+     */
+    private String ipv4;
+
+    /**
+     * 优先使用上报请求的对端 IP 地址。如果该IP为 IPv4, 则使用客户端获取的 client_ipv6 地址, ex: 240e:498:1c90:9d00:5513:b9c9:650a:d9d2或为空, 宏: IPV6
+     */
+    private String ipv6;
+
+    /**
+     * [下发逻辑修改 - 0727生效]正常情况下,全量下发IPv4地址。极少数情况如IPV4无法取数,则下发 IPv6, ex: 61.158.146.195或240e:398:1c90:9d00:5513:b9c9:650a:d9d2, 宏: IP
+     */
+    private String ip;
+
+    /**
+     * 用户代理(User Agent),一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。经过url encode, ex: News+7.4.5+rv%3A7.4.5.23%5C%28iPhone%3B+iOS+12.4.1%3B+zh_CN%5C%29Cronet, 宏: UA
+     */
+    private String ua;
+
+    /**
+     * 位置信息,包含三部分:latitude(纬度),longitude(经度)以及precise(确切信息,精度)十进制保留1位小数,西经南纬保留负数,用字母 x 分 割纬度与精度 (先纬后经,最后精度), ex: 35.7x122.4x100.0, 宏: GEO
+     */
+    private String geo;
+
+    /**
+     * 客户端发生广告点击事件的时间,以毫秒为单位时间戳, ex: 1575194434000, 宏: TS
+     */
+    private Long ts;
+
+    /**
+     * 一些跟广告信息相关的回调参数,内容是一个加密字符串,在调用事件回传接口的时候会用到, ex: EJiw267wvfQCGKf2g74ZIPD89-vIATAMOAFCIjIwMTkxMTI3MTQxMTEzMDEwMDI2MDc3MjE1MTUwNTczNTBIAQ==, 宏: CALLBACK_PARAM
+     */
+    private String callbackParam;
+
+    /**
+     * 直接把调用事件回传接口的url生成出来,广告主可以直接使用, ex: https://ad.toutiao.com/track/activate/?callback=EJiw267wvfQCGKf2g74ZIPD89-vIATAMOAFCIjIwMTkxMTI3MTQxMTEzMDEwMDI2MDc3MjE1MTUwNTczNTBIAQ==&os=0&muid=db94e6a60a9c6661e3e03a7d301c25b6, 宏: CALLBACK_URL
+     */
+    private String callbackUrl;
+
+    /**
+     * 手机型号, 包含多种格式, ex: iPhone12,2 (urlencode之后为iPhone12%2c2),iPhone X (urlencode之后为iPhone+X),SM-A750GN (urlencode之后为SM-A750GN), 宏: MODEL
+     */
+    private String model;
+
+    /**
+     * 对外广告位编码, ex: 2045891290, 宏: UNION_SITE
+     */
+    private String unionSite;
+
+    /**
+     * 中国广告协会互联网广告标识,包含最新两个版本的CAID和版本号,url encode之后的json字符串。(【CAID】和【CAID1、CAID2】的信息一致,使用一种即可;建议使用【CAID】,参数中包含多个信息,后续维护成本低), ex: %5B%7B%22version%22%3A%2220220111%22%2C%22caid%22%3A%22912ec803b2ce49e4a541068d495ab570%22%7D%2C%7B%22version%22%3A%2220211207%22%2C%22caid%22%3A%22e332a76c29654fcb7f6e6b31ced090c7%22%7D%5D, 宏: CAID
+     */
+    private String caid;
+
+    /**
+     * 商品id,仅支持站内(不支持穿山甲), 宏: PRODUCTID
+     */
+    private String productid;
+
+    /**
+     * 商品id,同时支持站内和穿山甲, 宏: OUTERID
+     */
+    private String outerid;
+
+
+}

+ 185 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/pojo/dto/GameTencentAppCallbackDTO.java

@@ -0,0 +1,185 @@
+package com.zanxiang.game.back.serve.pojo.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 腾讯游戏 APP 监测链接回传数据(文档:https://datanexus.qq.com/doc/develop/guider/interface/conversion/ad_track_click#5-%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8)
+ * </p>
+ *
+ * @author wcc
+ * @since 2024-06-27
+ */
+@Data
+public class GameTencentAppCallbackDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 点击id, ex: 24oi6xq2aaakvagnqu7a, 宏: __CLICK_ID__
+     */
+    private String clickId;
+
+    /**
+     * 点击时间, ex: 1586437362, 宏: __CLICK_TIME__
+     */
+    private Long clickTime;
+
+    /**
+     * 曝光时间, ex: 1586437361, ex: , 宏: __IMPRESSION_TIME__
+     */
+    private Long impressionTime;
+
+    /**
+     * 广告组id(实际为广告id), ex: 228691429, ex: , 宏: __ADGROUP_ID__
+     */
+    private Long adgroupId;
+
+    /**
+     * 创意 ID, ex: 654321, 宏: __DYNAMIC_CREATIVE_ID__
+     */
+    private Long dynamicCreativeId;
+
+    /**
+     * 营销资产ID, ex: 12345, 宏: __MARKETING_ASSET_ID__
+     */
+    private Long marketingAssetId;
+
+    /**
+     * 素材标签ID, 宏: __MATERIAL_PACKAGE_ID__
+     */
+    private String materialPackageId;
+
+    /**
+     * 广告投放平台, 1:GDT entrance、3:京东直投、5:经 wechat mp 投放的广告、6:京东运营、8:QQ 公众账号平台、9:移动联盟 SSP、10:58 运营、11:58 商户、12:易车运营、13:易车商户、14:融 360 运营、15:融 360 商户、16:点评运营、17:点评商户、18:来自 OMG 的广告主、19:京东外单, 宏: __AD_PLATFORM_TYPE__
+     */
+    private Integer adPlatformType;
+
+    /**
+     * 广告主id, ex: 9471147, 宏: __ACCOUNT_ID__
+     */
+    private Long accountId;
+
+    /**
+     * 代理商id, ex: 1050262, 宏: __AGENCY_ID__
+     */
+    private Long agencyId;
+
+    /**
+     * 点击sku, ex: 478c4a93a054f7c9087b4ecb1f03f8a1, 宏: __CLICK_SKU_ID__
+     */
+    private String clickSkuId;
+
+    /**
+     * 设备类型, ex: ios、android, 宏: __DEVICE_OS_TYPE__
+     */
+    private String deviceOsType;
+
+    /**
+     * 请求时间, ex: 1586437335, 宏: __PROCESS_TIME__
+     */
+    private Long processTime;
+
+    /**
+     * 应用id, ex: 1101072624、wx69618ae091cf2c76, 宏: __PROMOTED_OBJECT_ID__
+     */
+    private String promotedObjectId;
+
+    /**
+     * 请求id, ex: vqp7xdombqonw, 宏: __REQUEST_ID__
+     */
+    private String requestId;
+
+    /**
+     * 曝光id, ex: xkrx5et47h7g401, 宏: __IMPRESSION_ID__
+     */
+    private String impressionId;
+
+    /**
+     * 设备id(imei或idfa的加密值),对 IMEI 设备号转成小写,再进行md5编码,再小写,32位、对 IDFA 设备号保持大写,再进行 md5 编码,再小写,32位, 宏: __MUID__
+     */
+    private String muid;
+
+    /**
+     * 安卓id做md5加密后小写, ex: 797745b011e3286de9e1a1c59ba72c97, 宏: __HASH_ANDROID_ID__
+     */
+    private String hashAndroidId;
+
+    /**
+     * 媒体投放系统获取的用户终端的公共IPV4地址, ex: 183.226.102.120, 宏: __IP__
+     */
+    private String ip;
+
+    /**
+     * 用户代理(user_agent), ex: Dalvik%2F2.1.0+%28Linux%3B+U%3B+Android+8.0.0%3B+PIC-AL00+Build%2FHUAWEIPIC-AL00%29, 宏: __USER_AGENT__
+     */
+    private String userAgent;
+
+    /**
+     * 情况1:使用 DataNexus 配置,并与广告直接绑定(ex: 空值)、情况2:新版转化归因中的监测链接信息(使用 DataNexus 或直接填写监测链接)直接提供上报信息回传接口的 url,示例为url encode 编码原值,广告主需要 decode 作为 post 请求url回传至腾讯广告(ex: http%3A%2F%2Ftracking.e.qq.com%2Fconv%3Fcb%3DxXx%252BxXx%253D%26conv_id%3D123)、情况3:使用投放管理平台 - 工具(ex: http://tracking.e.qq.com/conv?cb=%s&&%s&&%s&&%s 其中%s代表的参数分别为 productId、productType、advertiserId、clickId), 宏: __CALLBACK__
+     */
+    private String callback;
+
+    /**
+     * 联盟广告位id, ex: 8144201, 宏: __ENCRYPTED_POSITION_ID__
+     */
+    private String encryptedPositionId;
+
+    /**
+     * 媒体投放系统获取的用户终端的公共IPV6地址, ex: 2409%3A8a55%3A4cc0%3A4050%3A2507%3A4922%3Abbe0%3A524b, 宏: __IPV6__
+     */
+    private String ipv6;
+
+    /**
+     * Android Q 及更高版本的设备号,64位及以下,取原值后做md5加密, ex: 9d271e4d04de7e4b0b4f1df20e79ce64, 宏: __HASH_OAID__
+     */
+    private String hashOaid;
+
+    /**
+     * URL Encode后的JSON数组;其中qaid为中广协ID(即CAID),hash_qaid为CAID原值MD5加密后的结果, version为腾讯版本号,支持两个版本同时下发(即最新版和上一版),腾讯版本号与中广协版本对应关系为:腾讯 1001 = 中广协 20200901;腾讯 1003 = 中广协 20201230; 腾讯1004 = 中广协 20211207;腾讯1005=中广协 20220111;腾讯1006=中广协 20230330, 宏: __QAID_CAA__
+     */
+    private String caid;
+
+    /**
+     * 机型, ex:  PCKM00、Redmi 7、iPhone 7..., 宏: __MODEL__
+     */
+    private String model;
+
+    /**
+     * 专用于网页类小程序转化规则的点击监测下发,其它类型不支持该字段下发。每个用户针对小程序应用会产生一个安全的OpenID,只针对当前的小程序有效, ex: ozWH25VK0aodxYMZrX0Lqj9HHhrg, 宏: __WECHAT_OPEN_ID__
+     */
+    private String wechatOpenid;
+
+    /**
+     * 媒体投放系统获取的用户终端的公共IPV4地址MD5加密后转小写,仅在新版转化里支持配置, 宏: __IP_MD5__
+     */
+    private String ipMd5;
+
+    /**
+     * 媒体投放系统获取的用户终端的公共IPV6地址MD5加密后转小写,仅在新版转化里支持配置, 宏: __IPV6_MD5__
+     */
+    private String ipv6Md5;
+
+    /**
+     * 渠道包id, 只 for Android 设备生效, 宏: __CHANNEL_PACKAGE_ID__
+     */
+    private String channelPackageId;
+
+    /**
+     * 操作系统版本, 只 for iOS 和 Android 设备生效, 宏: __DEVICE_OS_VERSION__
+     */
+    private String deviceOsVersion;
+
+    /**
+     * 行为类型, LANDING_PAGE_CLICK:点击跳转按钮,RESERVATION:表单预约, 宏: __ACT_TYPE__
+     */
+    private String actType;
+
+    /**
+     * 行为时间, ex: 1586437361, 宏: __ACT_TIME__
+     */
+    private Long actTime;
+
+
+}

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

@@ -0,0 +1,239 @@
+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 java.io.Serializable;
+import java.time.LocalDate;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * <p>
+ * 头条游戏 APP 监测链接回传数据(文档:https://open.oceanengine.com/labels/7/docs/1696710655781900)
+ * </p>
+ *
+ * @author wcc
+ * @since 2024-06-27
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@TableName(GameOceanengineAppCallback.TABLE_NAME)
+public class GameOceanengineAppCallback implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String TABLE_NAME = "t_game_oceanengine_app_callback";
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 时间发生日期
+     */
+    private LocalDate day;
+
+    /**
+     * 巨量广告体验版中特有的宏参,代表巨量广告体验版的广告ID, ex: ID: 7074140945750507528, 宏: PROMOTION_ID
+     */
+    private Long promotionId;
+
+    /**
+     * 巨量广告体验版中特有的宏参,代表巨量广告体验版的项目ID, ex: ID: 7074140945750507528, 宏: PROJECT_ID
+     */
+    private Long projectId;
+
+    /**
+     * 针对巨量广告体验版,图片素材宏参数(下发原始素材id), 宏: MID1
+     */
+    private String mid1;
+
+    /**
+     * 针对巨量广告体验版,标题素材宏参数(下发原始素材id), 宏: MID2
+     */
+    private String mid2;
+
+    /**
+     * 针对巨量广告体验版,视频素材宏参数(下发原始素材id), 宏: MID3
+     */
+    private String mid3;
+
+    /**
+     * 针对巨量广告体验版,搭配试玩素材宏参数(下发原始素材id), 宏: MID4
+     */
+    private String mid4;
+
+    /**
+     * 针对巨量广告体验版,落地页素材宏参数(下发原始素材id), 宏: MID5
+     */
+    private String mid5;
+
+    /**
+     * 针对巨量广告体验版,安卓下载详情页素材宏参数(下发原始素材id), 宏: MID6
+     */
+    private String mid6;
+
+    /**
+     * 广告计划id, ex: 1645988237525045, 宏: AID
+     */
+    private Long aid;
+
+    /**
+     * 广告创意 id, ex: 1650703686054530, 宏: CID
+     */
+    private Long cid;
+
+    /**
+     * 广告组 id, ex: 1651688272934434, 宏: CAMPAIGN_ID
+     */
+    private Long campaignId;
+
+    /**
+     * 创意样式, 2=小图模式、3=大图模式、4=组图模式、5=视频, 宏: CTYPE
+     */
+    private Integer ctype;
+
+    /**
+     * 广告主id, ex: 1631857582073864, 宏: ADVERTISER_ID
+     */
+    private Long advertiserId;
+
+    /**
+     * 广告投放位置, 今日头条:1-10000,80000-110001、西瓜视频:10001-10099、火山小视频:30001-30099、抖音:40001-40099、番茄小说:26001-26099、穿山甲开屏广告:800000000、穿山甲网盟非开屏广告:900000000、通投广告位:33013、搜索:38016, 宏: CSITE
+     */
+    private Integer csite;
+
+    /**
+     * 转化id, ex: 1681681272671401, 宏: CONVERT_ID
+     */
+    private Long convertId;
+
+    /**
+     * 请求下发的id, ex: 20191202180607010026077068090BA60A, 宏: REQUEST_ID
+     */
+    private String requestId;
+
+    /**
+     * 请求下发的id&创意id的md5,16位, ex: b2f97be5b363764d, 宏: TRACK_ID
+     */
+    private String trackId;
+
+    /**
+     * 这次请求的语言, ex: zh, 宏: SL
+     */
+    private String sl;
+
+    /**
+     * 安卓的设备 ID 的 md5 摘要,32位, 假设原始的 imei 是 868823031739689我们最终发送的是 imei 的 md5 摘要:0c2bd03c39f19845bf54ea0abafae70e,会存在部分无法获取imei的情况,则imei会回传空字符串或者0的md5摘要, 宏: IMEI
+     */
+    private String imei;
+
+    /**
+     * IOS 6+的设备id字段,32位, ex: 4FCFEFA1-096D-4176-B352-1870ED6DB777注意,一些例外00000000-0000-0000-0000-000000000000, 如果用户关闭里读取idfa的权限,会导致idfa全部是0, 宏: IDFA
+     */
+    private String idfa;
+
+    /**
+     * IOS 6+的设备id的md5摘要,32位, ex: 09c593c62a6074ae5f859e97a222c0e8,注意,用户关闭读取idfa权限,0值也会进行MD5加密, 宏: IDFA_MD5
+     */
+    private String idfaMd5;
+
+    /**
+     * 安卓id原值的md5,32位, ex: androidId的原值是: 7b5ca2d57178d2f1我们进行md5摘要,最终结果是:873541edf36da9170af47d5b69e82193, 宏: ANDROIDID
+     */
+    private String androidid;
+
+    /**
+     * Android Q及更高版本的设备号,32位, ex: 97e7ef3f-e5f2-d0b8-ccfc-f79bbeaf4841注意,一些例外如果无法获取oaid,oaid会传空字符串, 宏: OAID
+     */
+    private String oaid;
+
+    /**
+     * Android Q及更高版本的设备号的md5摘要,32位, ex: 87f8274c36eb73fabcbf143a10eca6a4,会存在部分无法获取oaid的情况,则该字段会回传空字符串, 宏: OAID_MD5
+     */
+    private String oaidMd5;
+
+    /**
+     * 操作系统平台, 安卓:0、IOS:1、其它:3, 宏: OS
+     */
+    private Integer os;
+
+    /**
+     * 移动设备mac地址,转换成大写字母,去掉“:”,并且取md5摘要后的结果, 假设原始的mac地址是34:d7:12:9b:3a:89去掉“:”后变成 34d7129b3a89变成大写字母34D7129B3A89取MD5摘要,最后变成如下结果:df97bc5021e14256e141b2f036df5a3c, 宏: MAC
+     */
+    private String mac;
+
+    /**
+     * 移动设备 mac 地址,转换成大写字母,并且取md5摘要后的结果,32位, 假设原始的mac地址是 34:d7:12:9b:3a:89变成大写字母: 34:D7:12:9B:3A:89进行MD5摘要,最后变成如下结果:4d0433eb614f5d5ad409a65395426cbe, 宏: MAC1
+     */
+    private String mac1;
+
+    /**
+     * 优先使用上报请求的对端 IP 地址。如果该IP为 IPv6, 则使用客户端获取的 client_ipv4 地址, ex: 61.168.146.195或为空, 宏: IPV4
+     */
+    private String ipv4;
+
+    /**
+     * 优先使用上报请求的对端 IP 地址。如果该IP为 IPv4, 则使用客户端获取的 client_ipv6 地址, ex: 240e:498:1c90:9d00:5513:b9c9:650a:d9d2或为空, 宏: IPV6
+     */
+    private String ipv6;
+
+    /**
+     * [下发逻辑修改 - 0727生效]正常情况下,全量下发IPv4地址。极少数情况如IPV4无法取数,则下发 IPv6, ex: 61.158.146.195或240e:398:1c90:9d00:5513:b9c9:650a:d9d2, 宏: IP
+     */
+    private String ip;
+
+    /**
+     * 用户代理(User Agent),一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。经过url encode, ex: News+7.4.5+rv%3A7.4.5.23%5C%28iPhone%3B+iOS+12.4.1%3B+zh_CN%5C%29Cronet, 宏: UA
+     */
+    private String ua;
+
+    /**
+     * 位置信息,包含三部分:latitude(纬度),longitude(经度)以及precise(确切信息,精度)十进制保留1位小数,西经南纬保留负数,用字母 x 分 割纬度与精度 (先纬后经,最后精度), ex: 35.7x122.4x100.0, 宏: GEO
+     */
+    private String geo;
+
+    /**
+     * 客户端发生广告点击事件的时间,以毫秒为单位时间戳, ex: 1575194434000, 宏: TS
+     */
+    private Long ts;
+
+    /**
+     * 一些跟广告信息相关的回调参数,内容是一个加密字符串,在调用事件回传接口的时候会用到, ex: EJiw267wvfQCGKf2g74ZIPD89-vIATAMOAFCIjIwMTkxMTI3MTQxMTEzMDEwMDI2MDc3MjE1MTUwNTczNTBIAQ==, 宏: CALLBACK_PARAM
+     */
+    private String callbackParam;
+
+    /**
+     * 直接把调用事件回传接口的url生成出来,广告主可以直接使用, ex: https://ad.toutiao.com/track/activate/?callback=EJiw267wvfQCGKf2g74ZIPD89-vIATAMOAFCIjIwMTkxMTI3MTQxMTEzMDEwMDI2MDc3MjE1MTUwNTczNTBIAQ==&os=0&muid=db94e6a60a9c6661e3e03a7d301c25b6, 宏: CALLBACK_URL
+     */
+    private String callbackUrl;
+
+    /**
+     * 手机型号, 包含多种格式, ex: iPhone12,2 (urlencode之后为iPhone12%2c2),iPhone X (urlencode之后为iPhone+X),SM-A750GN (urlencode之后为SM-A750GN), 宏: MODEL
+     */
+    private String model;
+
+    /**
+     * 对外广告位编码, ex: 2045891290, 宏: UNION_SITE
+     */
+    private String unionSite;
+
+    /**
+     * 中国广告协会互联网广告标识,包含最新两个版本的CAID和版本号,url encode之后的json字符串。(【CAID】和【CAID1、CAID2】的信息一致,使用一种即可;建议使用【CAID】,参数中包含多个信息,后续维护成本低), ex: %5B%7B%22version%22%3A%2220220111%22%2C%22caid%22%3A%22912ec803b2ce49e4a541068d495ab570%22%7D%2C%7B%22version%22%3A%2220211207%22%2C%22caid%22%3A%22e332a76c29654fcb7f6e6b31ced090c7%22%7D%5D, 宏: CAID
+     */
+    private String caid;
+
+    /**
+     * 商品id,仅支持站内(不支持穿山甲), 宏: PRODUCTID
+     */
+    private String productid;
+
+    /**
+     * 商品id,同时支持站内和穿山甲, 宏: OUTERID
+     */
+    private String outerid;
+
+
+}

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

@@ -0,0 +1,204 @@
+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 java.io.Serializable;
+import java.time.LocalDate;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * <p>
+ * 腾讯游戏 APP 监测链接回传数据(文档:https://datanexus.qq.com/doc/develop/guider/interface/conversion/ad_track_click#5-%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8)
+ * </p>
+ *
+ * @author wcc
+ * @since 2024-06-27
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@TableName(GameTencentAppCallback.TABLE_NAME)
+public class GameTencentAppCallback implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String TABLE_NAME = "t_game_tencent_app_callback";
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 时间发生日期
+     */
+    private LocalDate day;
+
+    /**
+     * 点击id, ex: 24oi6xq2aaakvagnqu7a, 宏: __CLICK_ID__
+     */
+    private String clickId;
+
+    /**
+     * 点击时间, ex: 1586437362, 宏: __CLICK_TIME__
+     */
+    private Long clickTime;
+
+    /**
+     * 曝光时间, ex: 1586437361, ex: , 宏: __IMPRESSION_TIME__
+     */
+    private Long impressionTime;
+
+    /**
+     * 广告组id(实际为广告id), ex: 228691429, ex: , 宏: __ADGROUP_ID__
+     */
+    private Long adgroupId;
+
+    /**
+     * 创意 ID, ex: 654321, 宏: __DYNAMIC_CREATIVE_ID__
+     */
+    private Long dynamicCreativeId;
+
+    /**
+     * 营销资产ID, ex: 12345, 宏: __MARKETING_ASSET_ID__
+     */
+    private Long marketingAssetId;
+
+    /**
+     * 素材标签ID, 宏: __MATERIAL_PACKAGE_ID__
+     */
+    private String materialPackageId;
+
+    /**
+     * 广告投放平台, 1:GDT entrance、3:京东直投、5:经 wechat mp 投放的广告、6:京东运营、8:QQ 公众账号平台、9:移动联盟 SSP、10:58 运营、11:58 商户、12:易车运营、13:易车商户、14:融 360 运营、15:融 360 商户、16:点评运营、17:点评商户、18:来自 OMG 的广告主、19:京东外单, 宏: __AD_PLATFORM_TYPE__
+     */
+    private Integer adPlatformType;
+
+    /**
+     * 广告主id, ex: 9471147, 宏: __ACCOUNT_ID__
+     */
+    private Long accountId;
+
+    /**
+     * 代理商id, ex: 1050262, 宏: __AGENCY_ID__
+     */
+    private Long agencyId;
+
+    /**
+     * 点击sku, ex: 478c4a93a054f7c9087b4ecb1f03f8a1, 宏: __CLICK_SKU_ID__
+     */
+    private String clickSkuId;
+
+    /**
+     * 设备类型, ex: ios、android, 宏: __DEVICE_OS_TYPE__
+     */
+    private String deviceOsType;
+
+    /**
+     * 请求时间, ex: 1586437335, 宏: __PROCESS_TIME__
+     */
+    private Long processTime;
+
+    /**
+     * 应用id, ex: 1101072624、wx69618ae091cf2c76, 宏: __PROMOTED_OBJECT_ID__
+     */
+    private String promotedObjectId;
+
+    /**
+     * 请求id, ex: vqp7xdombqonw, 宏: __REQUEST_ID__
+     */
+    private String requestId;
+
+    /**
+     * 曝光id, ex: xkrx5et47h7g401, 宏: __IMPRESSION_ID__
+     */
+    private String impressionId;
+
+    /**
+     * 设备id(imei或idfa的加密值),对 IMEI 设备号转成小写,再进行md5编码,再小写,32位、对 IDFA 设备号保持大写,再进行 md5 编码,再小写,32位, 宏: __MUID__
+     */
+    private String muid;
+
+    /**
+     * 安卓id做md5加密后小写, ex: 797745b011e3286de9e1a1c59ba72c97, 宏: __HASH_ANDROID_ID__
+     */
+    private String hashAndroidId;
+
+    /**
+     * 媒体投放系统获取的用户终端的公共IPV4地址, ex: 183.226.102.120, 宏: __IP__
+     */
+    private String ip;
+
+    /**
+     * 用户代理(user_agent), ex: Dalvik%2F2.1.0+%28Linux%3B+U%3B+Android+8.0.0%3B+PIC-AL00+Build%2FHUAWEIPIC-AL00%29, 宏: __USER_AGENT__
+     */
+    private String userAgent;
+
+    /**
+     * 情况1:使用 DataNexus 配置,并与广告直接绑定(ex: 空值)、情况2:新版转化归因中的监测链接信息(使用 DataNexus 或直接填写监测链接)直接提供上报信息回传接口的 url,示例为url encode 编码原值,广告主需要 decode 作为 post 请求url回传至腾讯广告(ex: http%3A%2F%2Ftracking.e.qq.com%2Fconv%3Fcb%3DxXx%252BxXx%253D%26conv_id%3D123)、情况3:使用投放管理平台 - 工具(ex: http://tracking.e.qq.com/conv?cb=%s&&%s&&%s&&%s 其中%s代表的参数分别为 productId、productType、advertiserId、clickId), 宏: __CALLBACK__
+     */
+    private String callback;
+
+    /**
+     * 联盟广告位id, ex: 8144201, 宏: __ENCRYPTED_POSITION_ID__
+     */
+    private String encryptedPositionId;
+
+    /**
+     * 媒体投放系统获取的用户终端的公共IPV6地址, ex: 2409%3A8a55%3A4cc0%3A4050%3A2507%3A4922%3Abbe0%3A524b, 宏: __IPV6__
+     */
+    private String ipv6;
+
+    /**
+     * Android Q 及更高版本的设备号,64位及以下,取原值后做md5加密, ex: 9d271e4d04de7e4b0b4f1df20e79ce64, 宏: __HASH_OAID__
+     */
+    private String hashOaid;
+
+    /**
+     * URL Encode后的JSON数组;其中qaid为中广协ID(即CAID),hash_qaid为CAID原值MD5加密后的结果, version为腾讯版本号,支持两个版本同时下发(即最新版和上一版),腾讯版本号与中广协版本对应关系为:腾讯 1001 = 中广协 20200901;腾讯 1003 = 中广协 20201230; 腾讯1004 = 中广协 20211207;腾讯1005=中广协 20220111;腾讯1006=中广协 20230330, 宏: __QAID_CAA__
+     */
+    private String caid;
+
+    /**
+     * 机型, ex:  PCKM00、Redmi 7、iPhone 7..., 宏: __MODEL__
+     */
+    private String model;
+
+    /**
+     * 专用于网页类小程序转化规则的点击监测下发,其它类型不支持该字段下发。每个用户针对小程序应用会产生一个安全的OpenID,只针对当前的小程序有效, ex: ozWH25VK0aodxYMZrX0Lqj9HHhrg, 宏: __WECHAT_OPEN_ID__
+     */
+    private String wechatOpenid;
+
+    /**
+     * 媒体投放系统获取的用户终端的公共IPV4地址MD5加密后转小写,仅在新版转化里支持配置, 宏: __IP_MD5__
+     */
+    private String ipMd5;
+
+    /**
+     * 媒体投放系统获取的用户终端的公共IPV6地址MD5加密后转小写,仅在新版转化里支持配置, 宏: __IPV6_MD5__
+     */
+    private String ipv6Md5;
+
+    /**
+     * 渠道包id, 只 for Android 设备生效, 宏: __CHANNEL_PACKAGE_ID__
+     */
+    private String channelPackageId;
+
+    /**
+     * 操作系统版本, 只 for iOS 和 Android 设备生效, 宏: __DEVICE_OS_VERSION__
+     */
+    private String deviceOsVersion;
+
+    /**
+     * 行为类型, LANDING_PAGE_CLICK:点击跳转按钮,RESERVATION:表单预约, 宏: __ACT_TYPE__
+     */
+    private String actType;
+
+    /**
+     * 行为时间, ex: 1586437361, 宏: __ACT_TIME__
+     */
+    private Long actTime;
+
+
+}

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

@@ -0,0 +1,10 @@
+package com.zanxiang.game.back.serve.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.back.serve.pojo.dto.GameOceanengineAppCallbackDTO;
+import com.zanxiang.game.back.serve.pojo.entity.GameOceanengineAppCallback;
+
+public interface IGameOceanengineAppCallbackService extends IService<GameOceanengineAppCallback> {
+
+    boolean callback(GameOceanengineAppCallbackDTO dto);
+}

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

@@ -0,0 +1,10 @@
+package com.zanxiang.game.back.serve.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.back.serve.pojo.dto.GameTencentAppCallbackDTO;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppCallback;
+
+public interface IGameTencentAppCallbackService extends IService<GameTencentAppCallback> {
+
+    boolean callback(GameTencentAppCallbackDTO dto);
+}

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

@@ -0,0 +1,44 @@
+package com.zanxiang.game.back.serve.service.impl;
+
+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.module.util.DateUtil;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.bean.BeanUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+
+@Slf4j
+@Service
+public class GameOceanengineAppCallbackServiceImpl extends ServiceImpl<GameOceanengineAppCallbackMapper, GameOceanengineAppCallback>
+implements IGameOceanengineAppCallbackService {
+
+    @Value("${spring.kafka.oceanengineAppCallbackTopic}")
+    private String oceanengineAppCallbackTopic;
+    @Autowired
+    private KafkaProducer<String, String> kafkaProducer;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean callback(GameOceanengineAppCallbackDTO dto) {
+        GameOceanengineAppCallback appCallback = BeanUtil.copy(dto, GameOceanengineAppCallback.class);
+        appCallback.setDay(dto.getTs() == null || dto.getTs() < 1000 ? LocalDate.now() : DateUtil.milliToLocalDateTime(dto.getTs()).toLocalDate());
+        save(appCallback);
+        try {
+            kafkaProducer.send(new ProducerRecord<>(oceanengineAppCallbackTopic, dto.getAdvertiserId().toString(), JsonUtil.toString(appCallback)));
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return true;
+    }
+}

+ 44 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/service/impl/GameTencentAppCallbackServiceImpl.java

@@ -0,0 +1,44 @@
+package com.zanxiang.game.back.serve.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+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.module.util.DateUtil;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.bean.BeanUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+
+@Slf4j
+@Service
+public class GameTencentAppCallbackServiceImpl extends ServiceImpl<GameTencentAppCallbackMapper, GameTencentAppCallback>
+implements IGameTencentAppCallbackService {
+
+    @Value("${spring.kafka.tencentAppCallbackTopic}")
+    private String tencentAppCallbackTopic;
+    @Autowired
+    private KafkaProducer<String, String> kafkaProducer;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean callback(GameTencentAppCallbackDTO dto) {
+        GameTencentAppCallback appCallback = BeanUtil.copy(dto, GameTencentAppCallback.class);
+        appCallback.setDay(dto.getClickTime() == null || dto.getClickTime() < 1000 ? LocalDate.now() : DateUtil.milliToLocalDateTime(dto.getClickTime()).toLocalDate());
+        save(appCallback);
+        try {
+            kafkaProducer.send(new ProducerRecord<>(tencentAppCallbackTopic, dto.getAccountId().toString(), JsonUtil.toString(appCallback)));
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return true;
+    }
+}

+ 53 - 0
game-back/game-back-serve/src/main/java/com/zanxiang/game/back/serve/task/CallbackClearTask.java

@@ -0,0 +1,53 @@
+package com.zanxiang.game.back.serve.task;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.zanxiang.game.back.base.ServerInfo;
+import com.zanxiang.game.back.serve.config.NacosDynamicParamConfig;
+import com.zanxiang.game.back.serve.pojo.entity.GameOceanengineAppCallback;
+import com.zanxiang.game.back.serve.pojo.entity.GameTencentAppCallback;
+import com.zanxiang.game.back.serve.service.IGameOceanengineAppCallbackService;
+import com.zanxiang.game.back.serve.service.IGameTencentAppCallbackService;
+import com.zanxiang.module.redis.service.IDistributedLockComponent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+
+@Slf4j
+@Component
+public class CallbackClearTask {
+    private static final String CLEAR_KEY = ServerInfo.SERVER_NAME + ":CALLBACK_DATA_CLEAR";
+
+    @Autowired
+    private NacosDynamicParamConfig nacosDynamicParamConfig;
+    @Autowired
+    private IDistributedLockComponent distributedLockComponent;
+    @Autowired
+    private IGameOceanengineAppCallbackService gameOceanengineAppCallbackService;
+    @Autowired
+    private IGameTencentAppCallbackService gameTencentAppCallbackService;
+
+    /**
+     * 每天凌晨 2点清理一次 15天前的监测链接数据
+     */
+    @Scheduled(cron = "0 0 2 * * ?")
+    public void execute() {
+        log.error("开始清理监测链接的日志");
+        try {
+            gameOceanengineAppCallbackService.remove(new LambdaQueryWrapper<GameOceanengineAppCallback>()
+                    .lt(GameOceanengineAppCallback::getDay, LocalDate.now().minusDays(nacosDynamicParamConfig.getCallbackClearDay()))
+            );
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        try {
+            gameTencentAppCallbackService.remove(new LambdaQueryWrapper<GameTencentAppCallback>()
+                    .lt(GameTencentAppCallback::getDay, LocalDate.now().minusDays(nacosDynamicParamConfig.getCallbackClearDay()))
+            );
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+    }
+}