فهرست منبع

Merge remote-tracking branch 'origin/master'

ZHAOXIAN 1 سال پیش
والد
کامیت
db0d355d1f
41فایلهای تغییر یافته به همراه1808 افزوده شده و 36 حذف شده
  1. 5 0
      job-bytedance/pom.xml
  2. 48 5
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/controller/BytedanceController.java
  3. 15 3
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/controller/FeishuController.java
  4. 16 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/AdvertiserAccount.java
  5. 78 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ByteDanceFlowDetailsReportDaily.java
  6. 19 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/UserLoginRecord.java
  7. 22 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/enums/AccountTypeEnum.java
  8. 21 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/ByteDanceFlowDetailsReportMapper.java
  9. 20 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/BytedanceMiniAppMapper.java
  10. 2 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/BytedanceProductReportMapper.java
  11. 20 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/BytedanceProductUpdateMapper.java
  12. 18 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/UserLoginRecordMapper.java
  13. 53 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/ByteDanceFlowDetailsReportMapper.xml
  14. 19 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/BytedanceMiniAppMapper.xml
  15. 5 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/BytedanceProductReportMapper.xml
  16. 63 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/BytedanceProductUpdateMapper.xml
  17. 14 12
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/ReportsAdPushMapper.xml
  18. 22 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/UserLoginRecordMapper.xml
  19. 17 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IByteDanceFlowDetailsReportService.java
  20. 11 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IBytedanceMiniAppService.java
  21. 2 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IBytedanceProductReportService.java
  22. 8 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IBytedanceProductUpdateService.java
  23. 5 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IDateReportsPushService.java
  24. 16 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IUserLoginRecordService.java
  25. 15 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/BindAppErrorWarnServiceImpl.java
  26. 160 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/ByteDanceFlowDetailsReportServiceImpl.java
  27. 153 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/BytedanceMiniAppServiceImpl.java
  28. 6 1
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/BytedanceProductReportServiceImpl.java
  29. 407 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/BytedanceProductUpdateServiceImpl.java
  30. 54 9
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/DateReportsPushServiceImpl.java
  31. 50 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/UserLoginRecordServiceImpl.java
  32. 1 1
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/utils/HttpUtils.java
  33. 55 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/ByteDanceFlowDetailsReportJob.java
  34. 29 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedanceMiniAppJob.java
  35. 31 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedanceProductUploadJob.java
  36. 1 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedancePromoterReportJob.java
  37. 36 4
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedanceReportRetryJob.java
  38. 12 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/FeishuPushJob.java
  39. 62 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/utils/FeishuRobotUtil.java
  40. 206 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/utils/Redis2Config.java
  41. 11 1
      job-bytedance/src/main/resources/application.yml

+ 5 - 0
job-bytedance/pom.xml

@@ -178,6 +178,11 @@
             <artifactId>spring-boot-starter-data-redis</artifactId>
         </dependency>
         <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+            <version>2.9.0</version>
+        </dependency>
+        <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-pool2</artifactId>
             <version>2.6.2</version>

+ 48 - 5
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/controller/BytedanceController.java

@@ -1,9 +1,9 @@
 package cn.com.ctop.job.bytedance.controller;
 
-import cn.com.ctop.job.bytedance.data.service.IAdminAdvertiserListService;
-import cn.com.ctop.job.bytedance.data.service.IBytedanceAccountReportService;
-import cn.com.ctop.job.bytedance.data.service.IBytedanceProductReportService;
-import cn.com.ctop.job.bytedance.data.service.IBytedancePromoterReportService;
+import cn.com.ctop.job.bytedance.data.service.*;
+import cn.com.ctop.job.bytedance.data.entity.AdvertiserAccount;
+import cn.com.ctop.job.bytedance.data.enums.AccountTypeEnum;
+import cn.com.ctop.job.bytedance.data.service.*;
 import cn.com.ctop.job.bytedance.data.utils.Check;
 import com.alibaba.fastjson.JSONObject;
 import io.swagger.annotations.Api;
@@ -38,16 +38,22 @@ public class BytedanceController {
     private IAdminAdvertiserListService adminAdvertiserListService;
 
     @Autowired
+    private IByteDanceFlowDetailsReportService byteDanceFlowDetailsReportService;
+
+    @Autowired
     private IBytedanceAccountReportService accountReportService;
 
     @Autowired
     private IBytedancePromoterReportService promoterReportService;
 
+    @Autowired
+    private IBytedanceMiniAppService bytedanceMiniAppService;
+
 
     @GetMapping("/accountReport")
     public void getChannel(String param) {
 
-        List<JSONObject>  infos = adminAdvertiserListService.getAdvertiserInfos();
+        List<JSONObject> infos = adminAdvertiserListService.getAdvertiserInfos();
         System.err.println(infos);
         adminAdvertiserListService.batchUpdateAdvertiserName(infos);
 
@@ -85,13 +91,50 @@ public class BytedanceController {
                 @Override
                 public void run() {
                     if (!Check.isNull(accessToken)) {
+                        productReportService.deleteBytedanceProductDailyReport(advertiserId, startDate, endDate);
                         productReportService.getBytedanceProductDailyReport(adverId, accessToken, startDate, endDate, 1, null, 1, null);
                     }
                 }
             });
         }
+    }
+
+    @GetMapping("/maEcpm")
+    public void maEcpm(@RequestParam(required = false) String appId, String startDate, String endDate) {
+        if (Check.isNull(startDate) || Check.isNull(endDate)) {
+            return;
+        }
+        List<String> iaaAppIds = bytedanceMiniAppService.getIaaAppIds();
+        for (String iaaAppId : iaaAppIds) {
+            if (appId != null && !appId.equals(iaaAppId)) {
+                continue;
+            }
+            bytedanceMiniAppService.getMaEcpm(startDate, endDate, appId, 1, null, null);
+        }
+    }
+
+    @GetMapping("/flowDetailsReport")
+    public void flowDetailsReport(@RequestParam(required = false) Long advertiserId, String accessToken, String startDate, String endDate) {
+
+        byteDanceFlowDetailsReportService.getBytedanceFlowDetailsDailyReport(advertiserId, accessToken, AccountTypeEnum.AD.getValue(), startDate, endDate, 1, null);
 
     }
 
+    @GetMapping("/generateFlowDetailsReport")
+    public void generateFlowDetailsReport(String startDate, String endDate) {
+        List<AdvertiserAccount> adAccount = byteDanceFlowDetailsReportService.getAdAccount();
+        adAccount.forEach(advertiserAccount -> {
+            dailyRetrExecutorService.submit(new Runnable() {
+
+                @Override
+                public void run() {
+                    if (!Check.isNull(advertiserAccount.getAccessToken())) {
+                        byteDanceFlowDetailsReportService.getBytedanceFlowDetailsDailyReport(advertiserAccount.getAdvertiserId(), advertiserAccount.getAccessToken(), AccountTypeEnum.AD.getValue(), startDate, endDate, 1, null);
+                    }
+                }
+            });
+
+        });
+    }
 
 }

+ 15 - 3
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/controller/FeishuController.java

@@ -26,16 +26,28 @@ public class FeishuController {
     IUnbindWarnPushService unbindWarnPushService;
     @Autowired
     IBindAppErrorWarnService bindAppErrorWarnService;
+    @Autowired
+    IBytedanceProductUpdateService bytedanceProductUpdateService;
+
+    @Autowired
+    IUserLoginRecordService userLoginRecordService;
     @GetMapping("/test")
-    public void test(){
-        bindAppErrorWarnService.send();
-        System.out.println("---feishu test 收到---");
+    public void test() throws Exception {
+//        bytedanceProductUpdateService.initNew();
+        bytedanceProductUpdateService.run();
+
     }
 
+
     @PostMapping("/sendUnbind")
     public void sendUnbind(@RequestBody JSONObject obj){
         JSONArray array = obj.getJSONArray("advertiserIds");
         List<String> list = array.toJavaList(String.class);
         unbindWarnPushService.send(list, false);
     }
+
+    @PostMapping("/sendLoginTimes")
+    public String sendLoginTimes(){
+      return userLoginRecordService.queryUserLoginRecord();
+    }
 }

+ 16 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/AdvertiserAccount.java

@@ -0,0 +1,16 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.Data;
+
+/**
+ * @author lining
+ * @ClassName AdvertiserAccount
+ * @description: TODO
+ * @date 2025年04月27日
+ * @version: 1.0
+ */
+@Data
+public class AdvertiserAccount {
+    private Long advertiserId;
+    private String accessToken;
+}

+ 78 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ByteDanceFlowDetailsReportDaily.java

@@ -0,0 +1,78 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @author lining
+ * @ClassName ByteDanceFlowDetailsReport
+ * @description: TODO
+ * @date 2025年04月16日
+ * @version: 1.0
+ */
+@Data
+@Builder
+public class ByteDanceFlowDetailsReportDaily {
+    /**
+     * 广告主ID
+     */
+    private Long advertiserId;
+    /**
+     * 数据起始时间
+     */
+    private Integer statTimeDay;
+
+    /**
+     * 日终结余(单位元)
+     */
+    private BigDecimal balance;
+    /**
+     * 日终赠款结余(单位元)。包括冻结和实结订单pending部分
+     */
+    private BigDecimal grantBalance;
+    /**
+     * 日终非赠款结余(单位元)。包括冻结和实结订单pending部分
+     */
+    private BigDecimal nonGrantBalance;
+    /**
+     * 现金支出(单位元)
+     */
+    private BigDecimal cashCost;
+    /**
+     * 总支出(单位元)
+     */
+    private BigDecimal cost;
+
+    /**
+     * 冻结(单位元)
+     */
+    private BigDecimal frozen;
+    /**
+     * 总存入(单位元)
+     */
+    private BigDecimal income;
+    /**
+     * 赠款支出(单位元)
+     */
+    private BigDecimal rewardCost;
+
+    /**
+     * 共享返货支出(单位元)
+     */
+    private BigDecimal sharedWalletCost;
+    /**
+     * 账户消耗的子钱包的共享余额(单位元)
+     */
+    private BigDecimal companyWalletCost;
+    /**
+     * 总转入(单位元)
+     */
+    private BigDecimal transferIn;
+
+    /**
+     * 总转出(单位元)
+     */
+    private BigDecimal transferOut;
+}

+ 19 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/UserLoginRecord.java

@@ -0,0 +1,19 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.Data;
+
+/**
+ * @author lining
+ * @ClassName UserLoginRecord
+ * @description: TODO
+ * @date 2025年05月09日
+ * @version: 1.0
+ */
+@Data
+public class UserLoginRecord {
+    private Long uid;
+
+    private String openId;
+
+    private Long loginTimes;
+}

+ 22 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/enums/AccountTypeEnum.java

@@ -0,0 +1,22 @@
+package cn.com.ctop.job.bytedance.data.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author lining
+ * @ClassName FlowTransactionTypeEnum
+ * @description: TODO
+ * @date 2025年04月21日
+ * @version: 1.0
+ */
+@AllArgsConstructor
+@Getter
+public enum AccountTypeEnum {
+    AD("AD"),
+    LOCAL("LOCAL"),
+    STAR("STAR")
+    ;
+
+    private String value;
+}

+ 21 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/ByteDanceFlowDetailsReportMapper.java

@@ -0,0 +1,21 @@
+package cn.com.ctop.job.bytedance.data.mapper;
+
+import cn.com.ctop.job.bytedance.data.entity.AdvertiserAccount;
+import cn.com.ctop.job.bytedance.data.entity.ByteDanceFlowDetailsReportDaily;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author lining
+ * @ClassName ByteDanceFlowDetailsReportMapper
+ * @description: TODO
+ * @date 2025年04月17日
+ * @version: 1.0
+ */
+public interface ByteDanceFlowDetailsReportMapper {
+
+    List<AdvertiserAccount> getAdAccount();
+
+    void replaceDanceFlowDetailsReportDailies(@Param("list") List<ByteDanceFlowDetailsReportDaily> dailyList);
+}

+ 20 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/BytedanceMiniAppMapper.java

@@ -0,0 +1,20 @@
+package cn.com.ctop.job.bytedance.data.mapper;
+
+import cn.com.ctop.job.bytedance.data.entity.BytedanceAdvertiserReportDaily;
+import cn.com.ctop.job.bytedance.data.entity.BytedanceAdvertiserReportHour;
+import cn.com.ctop.job.bytedance.data.entity.BytedanceErrorLog;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface BytedanceMiniAppMapper {
+    List<String> getEcpmKeys(@Param("startDate") Integer startDate,
+                             @Param("endDate") Integer endDate,
+                             @Param("startTime") String startTime,
+                             @Param("endTime") String endTime);
+
+    List<String> getIaaAppIds();
+
+    List<JSONObject> getMaEcpmRetryJob();
+}

+ 2 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/BytedanceProductReportMapper.java

@@ -11,4 +11,6 @@ public interface BytedanceProductReportMapper {
     List<JSONObject> queryCostAccount(@Param("advertiserId") Long advertiserId, @Param("startDate") String startDate, @Param("endDate") String endDate);
 
     void replaceProductReportDailies(List<BytedanceProductReportDaily> reports);
+
+    void deleteProductReportDailies(@Param("advertiserId") Long advertiserId,@Param("startDate") Integer startDate,@Param("endDate")  Integer endDate);
 }

+ 20 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/BytedanceProductUpdateMapper.java

@@ -0,0 +1,20 @@
+package cn.com.ctop.job.bytedance.data.mapper;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface BytedanceProductUpdateMapper {
+
+    List<JSONObject> getIAPPlatformList();
+    List<JSONObject> getIAPVideoList();
+    List<JSONObject> getIAPProductTypeEnums();
+
+    List<JSONObject> getIAAPlatformList();
+    List<JSONObject> getIAAVideoList();
+    List<JSONObject> getIAAProductTypeEnums();
+
+    int updateInstanceId(@Param("advertiserId") String advertiserId, @Param("instanceId") String instanceId);
+
+}

+ 18 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/UserLoginRecordMapper.java

@@ -0,0 +1,18 @@
+package cn.com.ctop.job.bytedance.data.mapper;
+
+import cn.com.ctop.job.bytedance.data.entity.UserLoginRecord;
+
+import java.util.List;
+
+/**
+ * @author lining
+ * @ClassName UserLoginRecordMapper
+ * @description: TODO
+ * @date 2025年05月09日
+ * @version: 1.0
+ */
+public interface UserLoginRecordMapper {
+
+    List<UserLoginRecord> queryUserLoginRecord();
+
+}

+ 53 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/ByteDanceFlowDetailsReportMapper.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="cn.com.ctop.job.bytedance.data.mapper.ByteDanceFlowDetailsReportMapper">
+
+    <select id="getAdAccount" resultType="cn.com.ctop.job.bytedance.data.entity.AdvertiserAccount">
+        select
+            tb1.advertiser_id as advertiserId,
+            tb2.access_token as accessToken
+        from
+            deepthink.bytedance_advertiser_list tb1
+                left join deepthink.bytedance_auth_list tb2 on
+                tb1.admin_advertiser_id = tb2.admin_advertiser_id
+    </select>
+
+    <insert id="replaceDanceFlowDetailsReportDailies">
+        replace into bytedance_flow_details_report_daily (
+            advertiser_id,
+            stat_time_day,
+        balance,
+        grant_balance,
+        non_grant_balance,
+        cash_cost,
+        cost,
+        frozen,
+        income,
+        reward_cost,
+        shared_wallet_cost,
+        company_wallet_cost,
+        transfer_in,
+        transfer_out
+        )
+        values
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.advertiserId},
+            #{item.statTimeDay},
+            #{item.balance},
+            #{item.grantBalance},
+            #{item.nonGrantBalance},
+            #{item.cashCost},
+            #{item.cost},
+            #{item.frozen},
+            #{item.income},
+            #{item.rewardCost},
+            #{item.sharedWalletCost},
+            #{item.companyWalletCost},
+            #{item.transferIn},
+            #{item.transferOut}
+            )
+        </foreach>
+    </insert>
+</mapper>
+

+ 19 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/BytedanceMiniAppMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="cn.com.ctop.job.bytedance.data.mapper.BytedanceMiniAppMapper">
+    <select id="getEcpmKeys" resultType="java.lang.String">
+        SELECT concat(event_timestamp,open_id,cost) as ecpm_keys from media_api.bytedance_webhook_ma_ecpm
+        where stat_date >=  #{startDate} and stat_date &lt;= #{endDate}
+          and event_time >= #{startTime} and event_time &lt;= #{endTime}
+    </select>
+
+    <select id="getIaaAppIds" resultType="java.lang.String">
+        select app_id from deepthink.mini_app_info where business_method in('IAA','MIX') and app_id like 'tt%'
+    </select>
+
+    <select id="getMaEcpmRetryJob" resultType="com.alibaba.fastjson.JSONObject">
+        SELECT is_send AS 'isSend', id AS 'id', start_date AS 'startDate', end_date AS 'endDate', data_dimension AS 'dataDimension', data_type AS 'dataType'
+        FROM media_api.bytedance_error_log where   data_dimension like 'ma_ecpm:%'
+    </select>
+
+</mapper>

+ 5 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/BytedanceProductReportMapper.xml

@@ -57,4 +57,9 @@
         </foreach>
     </insert>
 
+    <delete id="deleteProductReportDailies">
+        DELETE FROM media_api.bytedance_product_report_daily
+        WHERE advertiser_id = ${advertiserId} and stat_time_day >= ${startDate} and stat_time_day &lt;= ${endDate}
+    </delete>
+
 </mapper>

+ 63 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/BytedanceProductUpdateMapper.xml

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="cn.com.ctop.job.bytedance.data.mapper.BytedanceProductUpdateMapper">
+    <update id="updateInstanceId">
+        update deepthink.bytedance_product_main_advertiser
+        set mini_instance_id = #{instanceId}
+        where advertiser_id = #{advertiserId}
+    </update>
+
+
+    <select id="getIAPPlatformList" resultType="com.alibaba.fastjson.JSONObject">
+        select t1.*, t3.access_token
+        FROM deepthink.bytedance_product_main_advertiser t1
+        left join deepthink.bytedance_advertiser_list t2 on t1.advertiser_id=t2.advertiser_id
+        left join deepthink.bytedance_auth_list t3 on t2.admin_advertiser_id=t3.admin_advertiser_id
+        where t1.`type`=2 and t1.`_status`=0
+    </select>
+    <select id="getIAAPlatformList" resultType="com.alibaba.fastjson.JSONObject">
+        select t1.*, t3.access_token
+        FROM deepthink.bytedance_product_main_advertiser t1
+        left join deepthink.bytedance_advertiser_list t2 on t1.advertiser_id=t2.advertiser_id
+        left join deepthink.bytedance_auth_list t3 on t2.admin_advertiser_id=t3.admin_advertiser_id
+        where t1.`type`=1 and t1.`_status`=0
+    </select>
+    <select id="getIAPVideoList" resultType="com.alibaba.fastjson.JSONObject">
+        select third.album_id, detail.platform_config, video.id as video_id, detail.id as video_detail_id, video.name as name, video.cover_img as img,ifnull(category.name, '其他') as 'theme',
+        '天津星链视界信息技术有限公司' as copyright_owner, '其他' as playlet_gender, video.update_sort as playlet_num, '2' as playlet_duration,
+        payinfo.sort as start_pay_playlet, '5' as membership_types, '1' as has_paid_content, '2' as has_motivation_types,
+        payinfo.maxpay as max_charge, payinfo.minpay as min_charge, maxpay as recommended_charge, maxpay as price,
+        2 as has_paid_content, 1 as has_motivation_content
+        FROM (select * from thirtpart.ly_third_videos where submission_status=2
+        and video_id in (select video_id from thirtpart.ly_video_audit_records where audit_status=2)
+        ) third
+        LEFT JOIN (select * from thirtpart.ly_video_details where platform_config is not null group by video_id) detail ON third.video_id = detail.video_id
+        LEFT JOIN (select * from thirtpart.ly_videos where play_platform like '%2%') video ON third.video_id = video.id and third.platform=2
+        LEFT JOIN thirtpart.ly_video_category category ON video.category_id = category.id
+        LEFT JOIN (select video_id, min(sort) as sort, max(pay_amount/100.0) as maxpay, min(pay_amount/100.0) as minpay from thirtpart.ly_video_details where is_pay=1 group by video_id) payinfo ON video.id = payinfo.video_id
+        where video.is_free = 1
+    </select>
+    <select id="getIAAVideoList" resultType="com.alibaba.fastjson.JSONObject">
+        select third.album_id, detail.platform_config, video.id as video_id, detail.id as video_detail_id, video.name as name, video.cover_img as img,ifnull(category.name, '其他') as 'theme',
+        '天津星链视界信息技术有限公司' as copyright_owner, '其他' as playlet_gender, video.update_sort as playlet_num, '2' as playlet_duration,
+        '5' as membership_types, '1' as has_paid_content, '2' as has_motivation_types,payinfo.sort as starting_unlock_episode
+        FROM (select * from thirtpart.ly_third_videos where submission_status=2
+        and video_id in (select video_id from thirtpart.ly_video_audit_records where audit_status=2)
+        ) third
+        LEFT JOIN (select * from thirtpart.ly_video_details where platform_config is not null group by video_id) detail ON third.video_id = detail.video_id
+        LEFT JOIN (select * from thirtpart.ly_videos where play_platform like '%2%') video ON third.video_id = video.id and third.platform=2
+        LEFT JOIN thirtpart.ly_video_category category ON video.category_id = category.id
+        LEFT JOIN (select video_id, max(sort) as sort from thirtpart.ly_video_details where is_pay=0 group by video_id) payinfo ON video.id = payinfo.video_id
+        where video.is_free = 0
+    </select>
+    <select id="getIAPProductTypeEnums" resultType="com.alibaba.fastjson.JSONObject">
+        select first_type_name, first_type_id, second_type_name, second_type_id, third_type_name, third_type_id
+        from deepthink.bytedance_product_group_types
+        where `type`=2
+    </select>
+    <select id="getIAAProductTypeEnums" resultType="com.alibaba.fastjson.JSONObject">
+        select first_type_name, first_type_id, second_type_name, second_type_id, third_type_name, third_type_id
+        from deepthink.bytedance_product_group_types
+        where `type`=1
+    </select>
+</mapper>

+ 14 - 12
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/ReportsAdPushMapper.xml

@@ -41,15 +41,18 @@
         ) tb2
     </select>
     <select id="tmpTopMovie" resultType="java.util.Map">
-        select ot1.*, ot2.cost_amount, ot2.real_cost_amount,
+        select case ot1.movie_source when 2 then movie_name else concat('欢瑞自制-', movie_name) end as movie_name,
+        ot1.rk, ot1.share_end_amount, ot1.end_amount,  ot2.cost_amount, ot2.real_cost_amount,
         concat(round(if(ifnull(cost_amount,0)=0, 0, end_amount/cost_amount*100), 2), '%') AS water_roi,
-        concat(round(if(ifnull(real_cost_amount,0)=0, 0, end_amount/real_cost_amount*100), 2), '%') AS real_roi
+        concat(round(if(ifnull(real_cost_amount,0)=0, 0, share_end_amount/real_cost_amount*100), 2), '%') AS real_roi
         FROM
-        (select ifnull(COALESCE (tb2.movie_name, tb3.movie_name), tb1.movie_id) as movie_name,
-        rank() over (order by sum(end_amount) desc) as rk, sum(end_amount) as end_amount
+        (select ifnull(tb2.movie_name, tb1.movie_id) as movie_name, movie_source,
+        rank() over (order by sum(end_amount) desc) as rk, sum(end_amount) as end_amount, sum(share_end_amount) as share_end_amount
         from
         (select
-        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as end_amount, movie_id
+        ifnull(sum(iaa_amount), 0) + ifnull(sum(pay_amount), 0) as end_amount,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as share_end_amount,
+        movie_id
         from
         application.user_data_report_daily udrd
         where stat_date = ${date}
@@ -58,13 +61,12 @@
         order by end_amount desc
         ) tb1
         left join
-        (select distinct movie_id, movie_name from deepthink.channel_changdunovel_info cci) tb2 on tb1.movie_id = tb2.movie_id
-        left JOIN
-        (select id, movie_name from hr_playlet.m_movie mm) tb3 on tb1.movie_id = tb3.id
+        (select distinct moive_id, movie_name, movie_source from deepthink.all_moive union
+        select series_id, ifnull(replace(series_name,REGEXP_SUBSTR(series_name, '[0-9]+$'),''),series_name),4 from media_api.kuaishou_drama_series_info) tb2 on tb1.movie_id=tb2.moive_id
         group by movie_name order by rk limit 10) ot1
         left join
-        (select product_name, round(ifnull(sum(cost_amount),0),2) as cost_amount, sum(real_cost_amount) as real_cost_amount
-        from application.media_product_report_daily where stat_date=${date} group by product_name
+        (select ifnull(replace(product_name,REGEXP_SUBSTR(product_name, '[0-9]+$'),''),product_name) as product_name, round(ifnull(sum(cost_amount),0),2) as cost_amount, sum(real_cost_amount) as real_cost_amount
+        from application.media_product_report_daily where stat_date=${date} group by ifnull(replace(product_name,REGEXP_SUBSTR(product_name, '[0-9]+$'),''),product_name)
         ) ot2 on ot1.movie_name=ot2.product_name
         order by rk
     </select>
@@ -221,9 +223,9 @@
         group by advertiser_id having count(distinct ma_appid)>1)
         ) tb2
         on tb1.advertiser_id = tb2.advertiser_id
-        left join (select distinct app_id, app_name from hr_playlet.m_mini_app_info) tb3
+        left join (select distinct app_id, app_name from deepthink.mini_app_info) tb3
         on tb1.ma_appid = tb3.app_id
-        left join (select distinct app_id, app_name from hr_playlet.m_mini_app_info) tb4
+        left join (select distinct app_id, app_name from deepthink.mini_app_info) tb4
         on tb2.ma_appid = tb4.app_id
         left join (select user_id, phonenumber, nick_name from deepthink.sys_user) tb5
         on tb2.user_id=tb5.user_id

+ 22 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/UserLoginRecordMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="cn.com.ctop.job.bytedance.data.mapper.UserLoginRecordMapper">
+
+    <select id="queryUserLoginRecord" resultType="cn.com.ctop.job.bytedance.data.entity.UserLoginRecord">
+        SELECT
+            lulr.uid,
+            lu.open_id,
+            COUNT(1) AS login_times
+        FROM
+            thirtpart.ly_user_login_record lulr
+                LEFT JOIN thirtpart.ly_user lu ON lulr.uid = lu.id
+                LEFT JOIN thirtpart.ly_user_black_list b ON lulr.uid = b.uid
+        WHERE
+            lulr.created_at >= UNIX_TIMESTAMP(CURDATE())
+          AND b.uid IS NULL
+        GROUP BY lulr.uid
+        HAVING login_times > 100
+        ORDER BY login_times DESC
+    </select>
+
+</mapper>

+ 17 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IByteDanceFlowDetailsReportService.java

@@ -0,0 +1,17 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+import cn.com.ctop.job.bytedance.data.entity.AdvertiserAccount;
+
+import java.util.List;
+
+/**
+ * @author lining
+ * @ClassName IByteDanceFlowDetailsReportService
+ * @description: TODO
+ * @date 2025年04月16日
+ * @version: 1.0
+ */
+public interface IByteDanceFlowDetailsReportService {
+    List<AdvertiserAccount> getAdAccount();
+    void getBytedanceFlowDetailsDailyReport(Long advertiserId, String accessToken, String transactionType, String startDate, String endDate, Integer page, Long errorId);
+}

+ 11 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IBytedanceMiniAppService.java

@@ -0,0 +1,11 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.List;
+
+public interface IBytedanceMiniAppService {
+    List<String> getIaaAppIds();
+    void getMaEcpm(String startDt, String endDt, String appId, Integer isLog, Long errId, Integer isSend);
+    List<JSONObject> getMaEcpmRetryJob();
+}

+ 2 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IBytedanceProductReportService.java

@@ -9,4 +9,6 @@ public interface IBytedanceProductReportService {
                                         Long errorId, Integer isLog, Integer isSend);
 
     List<JSONObject> getCostAccount(Long advertiserId, String startDate, String endDate);
+
+    void deleteBytedanceProductDailyReport(Long advertiserId, String startDate, String endDate);
 }

+ 8 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IBytedanceProductUpdateService.java

@@ -0,0 +1,8 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+public interface IBytedanceProductUpdateService {
+
+    void run() throws Exception;
+
+    void initNew();
+}

+ 5 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IDateReportsPushService.java

@@ -2,4 +2,9 @@ package cn.com.ctop.job.bytedance.data.service;
 
 public interface IDateReportsPushService {
     void send();
+
+    /**
+     * 零点15执行的 比上一天多了个描述
+     */
+    void preSend();
 }

+ 16 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IUserLoginRecordService.java

@@ -0,0 +1,16 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+import cn.com.ctop.job.bytedance.data.entity.UserLoginRecord;
+
+import java.util.List;
+
+/**
+ * @author lining
+ * @ClassName IUserLoginRecord
+ * @description: TODO
+ * @date 2025年05月09日
+ * @version: 1.0
+ */
+public interface IUserLoginRecordService {
+    String queryUserLoginRecord();
+}

+ 15 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/BindAppErrorWarnServiceImpl.java

@@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -46,6 +47,20 @@ public class BindAppErrorWarnServiceImpl implements IBindAppErrorWarnService {
         Long last30DayLong = Long.parseLong(last30Day.substring(0, 10).replaceAll("-", ""));
 
         List<Map<String, String>> bindAppErrorWarnList = reportsPushMapper.bindAppErrorData(last30DayLong);
+
+        List<String> excludeAdvertiserIds = Arrays.asList(
+                "1828195222746121",
+                "1828195223251978",
+                "1827747808620748",
+                "1828195223976330",
+                "1823380338347136",
+                "1823380339959123",
+                "1823380339196992",
+                "1827747810800964",
+                "1829811636122179"
+        );
+        bindAppErrorWarnList.removeIf(i -> excludeAdvertiserIds.contains(String.valueOf(i.get("advertiser_id"))));
+
         if (Check.isNull(bindAppErrorWarnList)){
             log.info(">>>> 没有绑定错误的广告账号,任务结束");
             return;

+ 160 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/ByteDanceFlowDetailsReportServiceImpl.java

@@ -0,0 +1,160 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.entity.AdvertiserAccount;
+import cn.com.ctop.job.bytedance.data.entity.ByteDanceFlowDetailsReportDaily;
+import cn.com.ctop.job.bytedance.data.entity.BytedanceErrorLog;
+import cn.com.ctop.job.bytedance.data.mapper.ByteDanceFlowDetailsReportMapper;
+import cn.com.ctop.job.bytedance.data.mapper.BytedancePromoterReportMapper;
+import cn.com.ctop.job.bytedance.data.service.IByteDanceFlowDetailsReportService;
+import cn.com.ctop.job.bytedance.data.utils.AlertUtils;
+import cn.com.ctop.job.bytedance.data.utils.Check;
+import cn.com.ctop.job.bytedance.data.utils.HttpUtils;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author lining
+ * @ClassName ByteDanceFlowDetailsReportServiceImpl
+ * @description: TODO
+ * @date 2025年04月16日
+ * @version: 1.0
+ */
+@Service
+@Slf4j
+public class ByteDanceFlowDetailsReportServiceImpl implements IByteDanceFlowDetailsReportService {
+
+    @Resource
+    private ByteDanceFlowDetailsReportMapper byteDanceFlowDetailsReportMapper;
+
+    @Autowired
+    private BytedancePromoterReportMapper promoterReportMapper;
+
+    @Override
+    public List<AdvertiserAccount> getAdAccount() {
+        return byteDanceFlowDetailsReportMapper.getAdAccount();
+    }
+
+    @Override
+    public void getBytedanceFlowDetailsDailyReport(Long advertiserId, String accessToken, String accountType, String startDate, String endDate, Integer page, Long errorId) {
+        String url = "https://ad.oceanengine.com/open_api/2/advertiser/fund/daily_stat/";
+        Map<String, Object> paramsMap = new HashMap<>();
+        paramsMap.put("advertiser_id", advertiserId);
+        paramsMap.put("start_date", startDate);
+        paramsMap.put("end_date", endDate);
+        /**
+         * 账户业务类型,可选值:
+         * AD AD广告主(默认)
+         * LOCAL 本地推
+         * STAR 星图
+         */
+        paramsMap.put("account_type", accountType);
+        paramsMap.put("page", page);
+        paramsMap.put("page_size", 100);
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Access-Token", accessToken);
+        headers.put("Content-Type", "application/json");
+        String s = HttpUtils.httpGet(url, paramsMap, headers);
+        JSONObject result = JSONObject.parseObject(s);
+        if (Check.isNull(result)) {
+            log.info("账户id:{},广告流水详情请求结果返回为空", advertiserId);
+            return;
+        }
+
+        List<ByteDanceFlowDetailsReportDaily> dailies = new ArrayList<>();
+        Integer code = result.getInteger("code");
+        Integer totalPage = 1;
+        if (code == 0) {
+            JSONObject data = result.getJSONObject("data");
+            if (!Check.isNull(data)) {
+                JSONArray list = data.getJSONArray("list");
+                totalPage = data.getJSONObject("page_info").getIntValue("total_page");
+                if (!Check.isNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        JSONObject object = list.getJSONObject(i);
+                        String advertiser_id = object.getString("advertiser_id");
+                        String date = object.getString("date").replace("-", "");
+                        BigDecimal balance = object.getBigDecimal("balance");
+                        BigDecimal grant_balance = object.getBigDecimal("grant_balance");
+                        BigDecimal non_grant_balance = object.getBigDecimal("non_grant_balance");
+                        BigDecimal cash_cost = object.getBigDecimal("cash_cost");
+                        BigDecimal cost = object.getBigDecimal("cost");
+                        BigDecimal frozen = object.getBigDecimal("frozen");
+                        BigDecimal income = object.getBigDecimal("income");
+                        BigDecimal reward_cost = object.getBigDecimal("reward_cost");
+                        BigDecimal shared_wallet_cost = object.getBigDecimal("shared_wallet_cost");
+                        BigDecimal company_wallet_cost = object.getBigDecimal("company_wallet_cost");
+                        BigDecimal transfer_in = object.getBigDecimal("transfer_in");
+                        BigDecimal transfer_out = object.getBigDecimal("transfer_out");
+
+
+                        ByteDanceFlowDetailsReportDaily daily = ByteDanceFlowDetailsReportDaily.builder()
+                                .advertiserId(Long.parseLong(advertiser_id))
+                                .statTimeDay(Integer.parseInt(date))
+                                .balance(balance)
+                                .grantBalance(grant_balance)
+                                .nonGrantBalance(non_grant_balance)
+                                .cashCost(cash_cost)
+                                .cost(cost)
+                                .frozen(frozen)
+                                .income(income)
+                                .rewardCost(reward_cost)
+                                .sharedWalletCost(shared_wallet_cost)
+                                .companyWalletCost(company_wallet_cost)
+                                .transferIn(transfer_in)
+                                .transferOut(transfer_out)
+                                .build();
+                        dailies.add(daily);
+                    }
+
+                    if (!Check.isNull(dailies)) {
+                        log.info("账户id:{},流水日报请求结果返回成功", advertiserId);
+                        byteDanceFlowDetailsReportMapper.replaceDanceFlowDetailsReportDailies(dailies);
+                    }
+                }
+
+            }
+        }else{
+            //增加错误日志保存
+            log.info("账户id:{},日期:{}流水日报请求结果失败,返回数据:{}", advertiserId, startDate, result);
+            insertErrorLog(advertiserId, startDate, endDate);
+            return;
+        }
+
+        if (page < totalPage) {
+            try {
+                Thread.sleep(500L);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+            getBytedanceFlowDetailsDailyReport(advertiserId, accessToken, accountType, startDate, endDate, page + 1, errorId);
+        }else{
+            log.info("账户ID:{},日期:{},流水日报详情数据日报请求成功", advertiserId, startDate);
+
+            if(errorId != null){
+                promoterReportMapper.deleteInfoByErrorId(errorId);
+            }
+            //send message to feishu
+//            AlertUtils.sendMessage(advertiserId, startDate, endDate, 1, "flow_details_daily", 1,null);
+        }
+    }
+
+    private void insertErrorLog(Long advertiserId, String startDate, String endDate) {
+        BytedanceErrorLog errorInfo = new BytedanceErrorLog();
+        errorInfo.setAdvertiserId(advertiserId);
+        errorInfo.setStartDate(startDate);
+        errorInfo.setEndDate(endDate);
+        errorInfo.setDataDimension("flow_details");
+        errorInfo.setDataType("daily");
+        promoterReportMapper.replaceErrorInfo(errorInfo);
+    }
+}

+ 153 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/BytedanceMiniAppServiceImpl.java

@@ -0,0 +1,153 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.entity.BytedanceErrorLog;
+import cn.com.ctop.job.bytedance.data.mapper.BytedanceMiniAppMapper;
+import cn.com.ctop.job.bytedance.data.mapper.BytedancePromoterReportMapper;
+import cn.com.ctop.job.bytedance.data.service.IBytedanceMiniAppService;
+import cn.com.ctop.job.bytedance.data.utils.Check;
+import cn.com.ctop.job.bytedance.data.utils.DateUtils;
+import cn.com.ctop.job.bytedance.data.utils.HttpUtils;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class BytedanceMiniAppServiceImpl implements IBytedanceMiniAppService {
+    @Resource
+    private RedisTemplate redis2Template_db5;
+
+    @Autowired
+    private BytedanceMiniAppMapper bytedanceMiniAppMapper;
+
+    @Autowired
+    private BytedancePromoterReportMapper promoterReportMapper;
+
+    @Override
+    public List<String> getIaaAppIds() {
+        return bytedanceMiniAppMapper.getIaaAppIds();
+    }
+
+    @Override
+    public List<JSONObject> getMaEcpmRetryJob() {
+        return bytedanceMiniAppMapper.getMaEcpmRetryJob();
+    }
+
+    @Override
+    public void getMaEcpm(String startDt, String endDt, String appId, Integer isLog, Long errId, Integer isSend) {
+        try {
+            Long dataRangeSeconds = 3600L;
+            String startTime = startDt + (startDt.contains(" ") ? "" : " 00:00:00");
+            Long startT = DateUtils.getTimestampFormat("yyyy-MM-dd HH:mm:ss", startTime);
+            String endTime = endDt + (endDt.contains(" ") ? "" : " 23:59:59");
+            Long endT = DateUtils.getTimestampFormat("yyyy-MM-dd HH:mm:ss", endTime) + 1;
+            boolean hasMoreTime = true;
+            while (hasMoreTime) {
+                Long thisEndT = startT + dataRangeSeconds;
+                if (thisEndT >= endT) {
+                    thisEndT = endT;
+                }
+                String endEventTime = DateUtils.formatTimestamp("yyyy-MM-dd HH:mm:ss", thisEndT);
+                Integer endEventDate = Integer.parseInt(DateUtils.formatTimestamp("yyyyMMdd", thisEndT));
+                String startEventTime = DateUtils.formatTimestamp("yyyy-MM-dd HH:mm:ss", startT);
+                Integer startEventDate = Integer.parseInt(DateUtils.formatTimestamp("yyyyMMdd", startT));
+                List<String> ecpmKeys = bytedanceMiniAppMapper.getEcpmKeys(startEventDate, endEventDate, startEventTime, endEventTime);
+                String nextCursor = null;
+                boolean hasMore = true;
+                while (hasMore) {
+                    log.info("抖小ma_ecpm拉取 [{}] {}~{} {}", appId, startEventTime, endEventTime, nextCursor);
+                    String url = "https://open.douyin.com/api/traffic/v2/rt_ecpm/query/";
+                    Object tokenObj = redis2Template_db5.opsForValue().get("client-token-key_" + appId);
+                    if (tokenObj == null) {
+                        log.error("抖小ma_ecpm拉取获取token为空 [{}] {}~{} {}", appId, startEventTime, endEventTime, nextCursor);
+                        return;
+                    }
+                    Map<String, Object> params = new JSONObject();
+                    params.put("start_date", startEventTime);
+                    params.put("end_date", endEventTime);
+                    params.put("page_size", 500);
+                    if (nextCursor != null) {
+                        params.put("cursor", nextCursor);
+                    }
+                    HashMap<String, String> headers = new HashMap<>();
+                    headers.put("access-token", tokenObj.toString());
+                    headers.put("Content-Type", "application/json; charset=UTF-8");
+
+                    String s = HttpUtils.httpPostRequest(url, params, headers);
+                    JSONObject jsonObject = JSONObject.parseObject(s);
+                    JSONArray content = new JSONArray();
+                    if (!Check.isNull(jsonObject)) {
+                        Integer code = jsonObject.getInteger("err_no");
+                        if (code == 0) {
+                            JSONObject data = jsonObject.getJSONObject("data");
+
+                            nextCursor = data.getString("next_cursor");
+                            JSONArray result = data.getJSONArray("records");
+                            if (!Check.isNull(result)) {
+                                for (int i = 0; i < result.size(); i++) {
+                                    JSONObject resultJson = result.getJSONObject(i);
+                                    String eventTime = resultJson.getString("event_time");
+                                    String openId = resultJson.getString("open_id");
+                                    String cost = resultJson.getString("cost");
+                                    if (ecpmKeys.contains(eventTime + openId + cost)) {
+                                        continue;
+                                    }
+                                    content.add(resultJson);
+                                }
+                            } else {
+                                hasMore = false;
+                            }
+                        } else {
+                            log.error("[{}]抖小ma_ecpm拉取失败{}~{},返回信息:{}", appId, startEventTime, endEventTime, s);
+                            insertErrorLog(isLog, startEventTime, endTime, "ma_ecpm:" + appId, "daily", s);
+                            return;
+                        }
+                    } else {
+                        log.error("[{}]抖小ma_ecpm拉取返回数据为空{}~{}", appId, startEventTime, endEventTime);
+                        insertErrorLog(isLog, startEventTime, endTime, "ma_ecpm:" + appId, "daily", "数据返回空");
+                        return;
+                    }
+
+                    if (!content.isEmpty()) {
+                        String pushUrl = "https://push.artlinkworld.cn/bytedance/push/miniapp/webhook";
+                        JSONObject pushParam = new JSONObject();
+                        pushParam.put("content", content.toJSONString());
+                        pushParam.put("client_key", appId);
+                        pushParam.put("event", "ma_ecpm");
+                        HttpUtils.httpPostRequest(pushUrl, pushParam, null);
+                    }
+                }
+                hasMoreTime = thisEndT < endT;
+                startT = thisEndT;
+                if (errId != null) {
+                    promoterReportMapper.deleteInfoByErrorId(errId);
+                }
+            }
+            log.info("[{}]抖小ma_ecpm拉取完成{}~{}.", appId, startDt, endDt);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void insertErrorLog(Integer isLog, String startDate, String endDate, String dataDimension, String dataType, String msg) {
+        if (isLog != null && 1 == isLog) {
+            BytedanceErrorLog errorInfo = new BytedanceErrorLog();
+            errorInfo.setAdvertiserId(-1L);
+            errorInfo.setStartDate(startDate);
+            errorInfo.setEndDate(endDate);
+            errorInfo.setDataDimension(dataDimension);
+            errorInfo.setDataType(dataType);
+            errorInfo.setMsg(msg);
+            promoterReportMapper.replaceErrorInfo(errorInfo);
+        }
+    }
+
+}

+ 6 - 1
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/BytedanceProductReportServiceImpl.java

@@ -30,6 +30,11 @@ public class BytedanceProductReportServiceImpl implements IBytedanceProductRepor
     private BytedanceProductReportMapper productReportMapper;
 
     @Override
+    public void deleteBytedanceProductDailyReport(Long advertiserId, String startDate, String endDate) {
+        productReportMapper.deleteProductReportDailies(advertiserId, Integer.parseInt(startDate.replace("-", "")), Integer.parseInt(endDate.replace("-", "")));
+    }
+
+    @Override
     public void getBytedanceProductDailyReport(Long advertiserId, String accessToken, String startDate, String endDate, Integer page,
                                                Long errorId, Integer isLog, Integer isSend) {
         try {
@@ -98,7 +103,7 @@ public class BytedanceProductReportServiceImpl implements IBytedanceProductRepor
                     promoterReportMapper.deleteInfoByErrorId(errorId);
                 }
                 if (isSend != null && 1 == isSend) {
-                    AlertUtils.sendMessage(advertiserId, startDate, endDate, 1, "product_daily", 1,null);
+                    AlertUtils.sendMessage(advertiserId, startDate, endDate, 1, "product_daily", 1, null);
                 }
             }
         } catch (Exception e) {

+ 407 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/BytedanceProductUpdateServiceImpl.java

@@ -0,0 +1,407 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.mapper.BytedanceProductUpdateMapper;
+import cn.com.ctop.job.bytedance.data.service.IBytedanceProductUpdateService;
+import cn.com.ctop.job.bytedance.data.utils.Check;
+import cn.com.ctop.job.bytedance.data.utils.HttpUtils;
+import cn.com.ctop.job.bytedance.utils.FeishuRobotUtil;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+@Slf4j
+@Service
+public class BytedanceProductUpdateServiceImpl implements IBytedanceProductUpdateService {
+    @Resource
+    BytedanceProductUpdateMapper bytedanceProductUpdateMapper;
+
+    public static final String APP_ID = "ttba3acc87250c6f7901";
+    public static final String HOOK_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/92e2445e-76df-44fb-8f61-f55ac94f49bd";
+
+    @Override
+    public void run() throws Exception {
+        List<String> errors = new ArrayList<>();
+        log.info("付费商品库更新中...");
+        iapProductUpdate(errors);
+        log.info("付费商品库更新完成");
+        log.info("免费商品库更新中...");
+        iaaProductUpdate(errors);
+        log.info("免费商品库更新完成");
+        if (!Check.isNull(errors)){
+//            List<String> users = Arrays.asList(
+//                    "44fbaed6" //zhaotx
+//                    ,"58e7a98a"  //xsq
+//            );
+            FeishuRobotUtil.sendTextToWebhook(StringUtils.join(errors, "\n"), HOOK_URL);
+        }
+
+    }
+
+
+    @Override
+    public void initNew(){
+        List<JSONObject> iapPlatformList = bytedanceProductUpdateMapper.getIAPPlatformList();
+        List<JSONObject> iaaPlatformList = bytedanceProductUpdateMapper.getIAAPlatformList();
+        initNew(iaaPlatformList);
+        initNew(iapPlatformList);
+    }
+
+    public void initNew(List<JSONObject> platformList)  {
+        //为广告主账户创建新的字节小程序
+        //step1 查询表里广告主是否都有自己的字节小程序、
+        //step2 如果没有,创建新的字节小程序
+        for (JSONObject obj : platformList){
+            if (obj.containsKey("mini_instance_id") && !Check.isNull(obj.get("mini_instance_id"))){
+                //已经有字节小程序了
+                continue;
+            }
+            String createLinkUrl = "https://api.oceanengine.com/open_api/v3.0/tools/micro_app/create/";
+            String advertiser_id = obj.getString("advertiser_id");
+            String access_token = obj.getString("access_token");
+            Map<String, String> headers = new HashMap<>();
+            headers.put("Access-Token", access_token);
+            JSONObject requestBody = new JSONObject();
+            requestBody.put("advertiser_id", Long.parseLong(advertiser_id));
+            requestBody.put("app_id", obj.getString("mi_appid"));
+            requestBody.put("remark", "商品库管理用");
+            requestBody.put("tag_info", "1050107001");
+            JSONObject app_page = new JSONObject();
+            app_page.put("link", "");
+            app_page.put("link_remark", "init");
+            app_page.put("start_page", "pages/index/index");
+            app_page.put("start_param", "link_id=574391");
+            requestBody.put("app_page", app_page);
+
+            String res = HttpUtils.httpPostRequest(createLinkUrl, requestBody, headers);
+            if (res == null || res.isEmpty()){
+                System.out.println("创建字节小程序失败");
+                return;
+            }else {
+                JSONObject jsonObject = JSONObject.parseObject(res);
+                int code = jsonObject.getIntValue("code");
+                if (code != 0) {
+                    System.out.println("创建字节小程序失败,code: " + code + ", msg: " + jsonObject.getString("message"));
+                    break;
+                }else {
+                    String instanceId = jsonObject.getJSONObject("data").getString("instance_id");
+                    //更新表
+                    bytedanceProductUpdateMapper.updateInstanceId(advertiser_id, instanceId);
+                }
+            }
+        }
+    }
+
+    private void iapProductUpdate(List<String> errorBlanket){
+        //IAP
+        List<JSONObject> iapPlatformList = bytedanceProductUpdateMapper.getIAPPlatformList();
+        List<JSONObject> iapVideoList = bytedanceProductUpdateMapper.getIAPVideoList();
+        List<JSONObject> iapProductTypeEnums = bytedanceProductUpdateMapper.getIAPProductTypeEnums();
+        Map<String, Map<String, String>> advertiserToLinkMap = new HashMap<>();
+
+        for (JSONObject obj : iapPlatformList){
+            String advertiserId = obj.getString("advertiser_id");
+            String accessToken = obj.getString("access_token");
+            String instanceId = obj.getString("mini_instance_id");
+            if (instanceId == null){
+                errorBlanket.add("没有找到字节小程序的实例ID: " + advertiserId);
+                System.out.println("没有找到字节小程序的实例ID: " + advertiserId);
+                continue;
+            }
+            Map<String, String> albumIdToLinkMap = getBytedancePlayletPromoteLink(advertiserId, accessToken, instanceId, iapVideoList, errorBlanket);
+            advertiserToLinkMap.put(advertiserId, albumIdToLinkMap);
+        }
+
+        Map<String, JSONObject> iapProductTypeMap = iapProductTypeEnums.stream().collect(Collectors.toMap(
+                obj -> obj.getString("third_type_name"),
+                obj -> obj,
+                (existing, replacement) -> replacement
+        ));
+        for (JSONObject obj : iapVideoList){
+            String theme = obj.getString("theme");
+            if (!iapProductTypeMap.containsKey(theme)){
+                errorBlanket.add("上传商品:没有找到对应的产品类型:" + theme);
+                System.out.println(">>>>没有找到对应的产品类型:" + theme);
+                System.out.println("\n\n\n");
+                continue;
+            }
+            for (JSONObject platform : iapPlatformList) {
+                String url = "https://ad.oceanengine.com/open_api/2/dpa/product/update/";
+                Map<String, String> headers = new HashMap<>();
+                headers.put("Access-Token", platform.getString("access_token"));
+                headers.put("Content-Type", "application/json");
+
+                JSONObject request = new JSONObject();
+                String album_id = obj.getString("album_id");
+                request.put("product_id", obj.getLongValue("album_id"));
+                JSONObject product_info = new JSONObject();
+                product_info.put("name", obj.getString("name"));
+                product_info.put("image_url", obj.getString("img"));
+                product_info.put("status", 1);
+                product_info.put("stock", 1);
+                JSONObject productType = iapProductTypeMap.get(theme);
+                product_info.put("first_category_id", productType.getString("first_type_id"));
+                product_info.put("sub_category_id", productType.getString("second_type_id"));
+                product_info.put("third_category_id", productType.getString("third_type_id"));
+                product_info.put("first_category", productType.getString("first_type_name"));
+                product_info.put("sub_category", productType.getString("second_type_name"));
+                product_info.put("third_category", productType.getString("third_type_name"));
+                request.put("advertiser_id", platform.getLongValue("advertiser_id"));
+                request.put("platform_id", platform.getLongValue("platform_id"));
+                JSONObject profession = new JSONObject();
+                profession.put("copyright_owner", obj.getString("copyright_owner"));
+                profession.put("playlet_gender", "3");
+                profession.put("playlet_num", obj.getString("playlet_num"));
+                profession.put("playlet_duration", obj.getString("playlet_duration"));
+                profession.put("start_pay_playlet", obj.getString("start_pay_playlet"));
+                if (platform.getDouble("max_charge") > 0.0){
+                    profession.put("max_charge", platform.getString("max_charge"));
+                }
+                if (platform.getDouble("min_charge") > 0.0){
+                    profession.put("min_charge", platform.getString("min_charge"));
+                }
+                if (platform.getDouble("recommended_charge") > 0.0){
+                    profession.put("recommended_charge", platform.getString("recommended_charge"));
+                }
+                profession.put("has_paid_content", "1");
+                profession.put("has_motivation_content", "2");
+                profession.put("membership_types", platform.getString("membership_types"));
+                profession.put("micro_app_link", advertiserToLinkMap.get(platform.getString("advertiser_id")).get(album_id));
+                //price_info
+                JSONObject price_info = new JSONObject();
+                price_info.put("price", platform.getDoubleValue("single_price"));
+                product_info.put("price_info", price_info);
+                product_info.put("profession", profession);
+                request.put("product_info", product_info);
+
+
+//                System.out.println("请求地址:" + url);
+//                System.out.println("请求头:" + headers);
+//                System.out.println("请求参数:" + request.toJSONString());
+                String res = HttpUtils.httpPostRequest(url, request, headers);
+//                System.out.println(res);
+                JSONObject btRes = JSONObject.parseObject(res);
+                int code = btRes.getIntValue("code");
+                if (code != 0){
+                    errorBlanket.add("上传付费商品失败,code: " + code + ", msg: " + btRes.getString("message")+", 关联剧目: " + obj.getString("name"));
+                }
+//                System.out.println("\n\n\n");
+            }
+        }
+    }
+
+    private void iaaProductUpdate(List<String> errorBlanket){
+        //IAA
+        List<JSONObject> iaaPlatformList = bytedanceProductUpdateMapper.getIAAPlatformList();
+        List<JSONObject> iaaVideoList = bytedanceProductUpdateMapper.getIAAVideoList();
+        List<JSONObject> iaaProductTypeEnums = bytedanceProductUpdateMapper.getIAAProductTypeEnums();
+
+        Map<String, Map<String, String>> advertiserToLinkMap = new HashMap<>();
+
+        for (JSONObject obj : iaaPlatformList){
+            String advertiserId = obj.getString("advertiser_id");
+            String accessToken = obj.getString("access_token");
+            String instanceId = obj.getString("mini_instance_id");
+            if (instanceId == null){
+                errorBlanket.add("没有找到字节小程序的实例ID: " + advertiserId);
+                System.out.println("没有找到字节小程序的实例ID: " + advertiserId);
+                continue; //todo
+            }
+            Map<String, String> albumIdToLinkMap = getBytedancePlayletPromoteLink(advertiserId, accessToken, instanceId, iaaVideoList, errorBlanket);
+            advertiserToLinkMap.put(advertiserId, albumIdToLinkMap);
+        }
+
+        Map<String, JSONObject> iaaProductTypeMap = iaaProductTypeEnums.stream().collect(Collectors.toMap(
+                obj -> obj.getString("third_type_name"),
+                obj -> obj,
+                (existing, replacement) -> replacement
+        ));
+        for (JSONObject obj : iaaVideoList){
+            String theme = obj.getString("theme");
+            if (!iaaProductTypeMap.containsKey(theme)){
+                errorBlanket.add("上传商品:没有找到对应的产品类型:" + theme);
+                System.out.println(">>>>没有找到对应的产品类型:" + theme);
+                System.out.println("\n\n\n");
+                continue;
+            }
+
+
+            for (JSONObject platform : iaaPlatformList) {
+                String url = "https://ad.oceanengine.com/open_api/2/dpa/product/update/";
+                Map<String, String> headers = new HashMap<>();
+                headers.put("Access-Token", platform.getString("access_token"));
+                headers.put("Content-Type", "application/json");
+                String album_id = obj.getString("album_id");
+                JSONObject request = new JSONObject();
+                request.put("product_id", obj.getLongValue("album_id"));
+                JSONObject product_info = new JSONObject();
+                product_info.put("name", obj.getString("name"));
+                product_info.put("image_url", obj.getString("img"));
+                product_info.put("status", 1);
+                product_info.put("stock", 1);
+                JSONObject productType = iaaProductTypeMap.get(theme);
+                product_info.put("first_category_id", productType.getString("first_type_id"));
+                product_info.put("sub_category_id", productType.getString("second_type_id"));
+                product_info.put("third_category_id", productType.getString("third_type_id"));
+                product_info.put("first_category", productType.getString("first_type_name"));
+                product_info.put("sub_category", productType.getString("second_type_name"));
+                product_info.put("third_category", productType.getString("third_type_name"));
+                JSONObject profession = new JSONObject();
+                profession.put("copyright_owner", obj.getString("copyright_owner"));
+                profession.put("playlet_gender", "3");
+                profession.put("playlet_num", obj.getString("playlet_num"));
+                profession.put("playlet_duration", obj.getString("playlet_duration"));
+                profession.put("has_paid_content", "2");
+                profession.put("has_motivation_content", "2");
+                profession.put("membership_types", "5");
+                profession.put("starting_unlock_episode", obj.getString("starting_unlock_episode"));
+                profession.put("micro_app_link", advertiserToLinkMap.get(platform.getString("advertiser_id")).get(album_id));
+                product_info.put("profession", profession);
+                request.put("product_info", product_info);
+                request.put("advertiser_id", platform.getLongValue("advertiser_id"));
+                request.put("platform_id", platform.getLongValue("platform_id"));
+
+//                System.out.println("请求地址:" + url);
+//                System.out.println("请求头:" + headers);
+//                System.out.println("请求参数:" + request.toJSONString());
+                String res = HttpUtils.httpPostRequest(url, request, headers);
+                JSONObject btRes = JSONObject.parseObject(res);
+                int code = btRes.getIntValue("code");
+                if (code != 0){
+                    errorBlanket.add("上传免费商品失败,code: " + code + ", msg: " + btRes.getString("message")+", 关联剧目: " + obj.getString("name"));
+                }
+//                System.out.println(res);
+//                System.out.println("\n\n\n");
+            }
+        }
+    }
+
+    /**
+     *
+     * @param advertiserId 商品库主账户ID,也是创建字节小程序的ID
+     * @param accessToken 广告账号的access_token
+     * @param instanceId 字节小程序的实例ID
+     * @return map key: album_id, value: 对应的字节小程序推广链接,sslocal开头
+     */
+    private Map<String, String> getBytedancePlayletPromoteLink(String advertiserId, String accessToken, String instanceId, List<JSONObject> requests, List<String> errorBlanket){
+        JSONArray playletDetailInfo = getPlayletDetail(advertiserId, accessToken, instanceId, errorBlanket);
+        if (playletDetailInfo == null || playletDetailInfo.isEmpty()){
+            return null;
+        }
+        List<String> reqAlbumIds = requests.stream()
+                .map(obj -> obj.getString("album_id"))
+                .collect(Collectors.toList());
+        Map<String, String> res = new HashMap<>(reqAlbumIds.size());
+
+        for (int i = 0; i < playletDetailInfo.size(); i++) {
+            JSONObject obj = playletDetailInfo.getJSONObject(i);
+            String startParam = obj.getString("start_param");
+            for (String album_id : reqAlbumIds){
+                if (startParam.contains("tt_album_id="+album_id)){
+                    res.put(album_id, obj.getString("link"));
+                }
+            }
+        }
+        List<JSONObject> unfound = requests.stream()
+                .filter(obj -> !res.containsKey(obj.getString("album_id")))
+                .collect(Collectors.toList());
+        if (unfound.isEmpty()){
+            return res;
+        }
+        //没有找到对应的album_id,需要新建链接
+        for (JSONObject obj : unfound){
+            String editLinkUrl = "https://api.oceanengine.com/open_api/v3.0/tools/micro_app/update/";
+            Map<String, String> headers = new HashMap<>();
+            headers.put("Access-Token", accessToken);
+            JSONObject requestBody = new JSONObject();
+            requestBody.put("advertiser_id", Long.parseLong(advertiserId));
+            requestBody.put("instance_id", Long.parseLong(instanceId));
+            JSONObject app_page = new JSONObject();
+            String name = obj.getString("name");
+            app_page.put("link", "");
+            app_page.put("remark", "生成商品用-"+name);
+            app_page.put("link_remark", "生成商品用-"+name);
+            app_page.put("operate_type", "NEW");
+            app_page.put("start_page", "pages/index/index");
+            String video_id = obj.getString("video_id");
+            String album_id = obj.getString("album_id");
+            String video_detail_id = obj.getString("video_detail_id");
+            String platform_config = obj.getString("platform_config");
+            JSONObject pconfig = JSONObject.parseObject(platform_config);
+            String tt_episode_id = pconfig.getJSONObject("douyinyun").getString("episode_id");
+            String start_param = "tt_album_id=" + album_id + "&video_id=" + video_id + "&video_detail_id=" + video_detail_id + "&tt_episode_id=" + tt_episode_id + "&source=link&type=1";
+            app_page.put("start_param", start_param);
+            JSONArray app_page_list = new JSONArray();
+            app_page_list.add(app_page);
+            requestBody.put("app_page", app_page_list);
+            requestBody.put("tag_info", "1050107001");
+//            System.out.println("\n\n请求地址:" + editLinkUrl);
+//            System.out.println("请求头:" + headers);
+//            System.out.println("请求参数:" + requestBody.toJSONString());
+
+            String rr = HttpUtils.httpPostRequest(editLinkUrl, requestBody, headers);
+            if (rr == null || rr.isEmpty()){
+                errorBlanket.add("创建字节小程序推广链接失败, 广告账号ID:" + advertiserId + ", 小程序资产ID:" + instanceId);
+                System.out.println("创建字节小程序推广链接失败");
+                return res;
+            }else {
+                JSONObject jsonObject = JSONObject.parseObject(rr);
+                int code = jsonObject.getIntValue("code");
+                if (code != 0) {
+                    errorBlanket.add("创建字节小程序推广链接失败,code: " + code + ", msg: " + jsonObject.getString("message") + ", 广告账号ID:" + advertiserId + ", 小程序资产ID:" + instanceId);
+                    System.out.println("创建字节小程序推广链接失败,code: " + code + ", msg: " + jsonObject.getString("message"));
+                    continue;
+                }else {
+                    String instanceid = jsonObject.getJSONObject("data").getString("instance_id");
+                    JSONArray newPlayletDetailInfo = getPlayletDetail(advertiserId, accessToken, instanceid, errorBlanket);
+                    if (newPlayletDetailInfo == null || newPlayletDetailInfo.isEmpty()){
+                        continue;
+                    }
+                    for (int i = 0; i < newPlayletDetailInfo.size(); i++) {
+                        JSONObject link = newPlayletDetailInfo.getJSONObject(i);
+                        String startParam = link.getString("start_param");
+                        if (startParam.contains("tt_album_id="+album_id)){
+                            res.put(album_id, link.getString("link"));
+                        }
+                    }
+                }
+            }
+        }
+        return res;
+
+    }
+
+    private JSONArray getPlayletDetail(String advertiserId, String accessToken, String instanceId, List<String> errorBlanket) {
+        String getPlayletDetailUrl = "https://api.oceanengine.com/open_api/v3.0/tools/asset_link/list/";
+        getPlayletDetailUrl = getPlayletDetailUrl.replace("${ADVERTISER_ID}", advertiserId);
+        getPlayletDetailUrl = getPlayletDetailUrl.replace("${INSTANCE_ID}", instanceId);
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Access-Token", accessToken);
+        Map<String, Object> params = new HashMap<>();
+        params.put("advertiser_id", advertiserId);
+        params.put("page", "1");
+        params.put("page_size", "100");
+        params.put("filtering", "{\"instance_id\":" + instanceId + "}");
+        String res = HttpUtils.httpGet(getPlayletDetailUrl, params, headers);
+        if (res == null || res.isEmpty()) {
+            errorBlanket.add("获取字节小程序推广链接失败, 广告账号ID:" + advertiserId + ", 小程序资产ID:" + instanceId);
+            return null;
+        }else {
+            JSONObject jsonObject = JSONObject.parseObject(res);
+            int code = jsonObject.getIntValue("code");
+            if (code != 0) {
+                errorBlanket.add("获取字节小程序推广链接失败,code: " + code + ", msg: " + jsonObject.getString("message") + ", 广告账号ID:" + advertiserId + ", 小程序资产ID:" + instanceId);
+                System.out.println("获取字节小程序推广链接失败,code: " + code + ", msg: " + jsonObject.getString("message"));
+                return null;
+            }else {
+                return jsonObject.getJSONObject("data").getJSONArray("list");
+            }
+        }
+
+    }
+
+}

+ 54 - 9
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/DateReportsPushServiceImpl.java

@@ -33,10 +33,20 @@ public class DateReportsPushServiceImpl implements IDateReportsPushService {
     @Autowired
     ReportsAdPushMapper reportsPushMapper;
 
-    private static final String TEMPLATE_VERSION = "1.0.2";
+    private static final String TEMPLATE_VERSION = "1.0.3";
     private static final String TEMPLATE_ID = "AAqRhJzMBoO0E";
     @Override
     public void send() {
+        sendParam("");
+    }
+
+    @Override
+    public void preSend() {
+        sendParam("注:当天ROI数据有微小1%-2%差异,以次日9点推送为准");
+    }
+
+
+    private void sendParam(String description){
         String token;
         try {
             token = FeishuRobotUtil.getTenantToken(APP_ID, APP_KEY);
@@ -107,7 +117,7 @@ public class DateReportsPushServiceImpl implements IDateReportsPushService {
             JSONObject obj = new JSONObject();
             obj.put("rank", String.valueOf(movie.get("rk")));
             obj.put("movieName", String.valueOf(movie.get("movie_name")));
-            obj.put("endAmount", String.valueOf(movie.get("end_amount")));
+            obj.put("endAmount", String.valueOf(movie.get("share_end_amount")));
             obj.put("realROI", String.valueOf(movie.get("real_roi")));
             movieRankTable.add(obj);
         }
@@ -130,20 +140,28 @@ public class DateReportsPushServiceImpl implements IDateReportsPushService {
         template_variable.put("dateIndicatorTable", dateIndicatorTable);
         template_variable.put("movieRankTable", movieRankTable);
         template_variable.put("deptRank", deptRank);
+        template_variable.put("description", description);
 
         List<String> userIds = Arrays.asList(
                 "44fbaed6" //zhaotx
-                ,"ebeeced9" //yumeng
-                ,"265g9ef1" //wangshu
-                ,"ee518161" //zhaozhicheng
-                ,"c5f6b59d" //yuyang
-                ,"d834552b" //hemenghao
-                ,"a3235a61" //louzijun
-                ,"58e7a98a"  //xsq
+//                ,"ebeeced9" //yumeng
+//                ,"265g9ef1" //wangshu
+//                ,"ee518161" //zhaozhicheng
+//                ,"c5f6b59d" //yuyang
+//                ,"d834552b" //hemenghao
+//                ,"a3235a61" //louzijun
+//                ,"58e7a98a"  //xsq
         );
         for (String userId : userIds) {
             sendToFeishu(token, template_variable, userId);
         }
+
+        List<String> chatid = Arrays.asList(
+                "oc_275f17aa4bf8fae649ef281a2b9250f6"
+        );
+        for (String chat : chatid) {
+            sendToChatGroup(token, template_variable, chat);
+        }
     }
 
     private void sendToFeishu(String token, JSONObject template_variable, String receive_id) {
@@ -172,4 +190,31 @@ public class DateReportsPushServiceImpl implements IDateReportsPushService {
             log.error("发送飞书消息失败", e);
         }
     }
+
+    private void sendToChatGroup(String token, JSONObject template_variable, String chatid) {
+        JSONObject content = new JSONObject();
+        JSONObject contentData = new JSONObject();
+        contentData.put("template_id",TEMPLATE_ID);
+        contentData.put("template_version_name", TEMPLATE_VERSION);
+        contentData.put("template_variable", template_variable);
+        content.put("type", "template");
+        content.put("data", contentData);
+
+        JSONObject request = new JSONObject();
+        request.put("receive_id", chatid);
+        request.put("msg_type", "interactive");
+        request.put("content", content.toJSONString());
+        //发送
+        try (CloseableHttpClient client = HttpClients.createDefault()){
+            HttpPost httpPost = new HttpPost("https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id");
+            httpPost.setHeader("Authorization", "Bearer "+token);
+            HttpEntity entity = new StringEntity(request.toJSONString(), "UTF-8");
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
+            CloseableHttpResponse respon = client.execute(httpPost);
+            System.out.println("发送结果: "+ EntityUtils.toString(respon.getEntity(), "UTF-8"));
+        }catch (IOException e){
+            log.error("发送飞书消息失败", e);
+        }
+    }
 }

+ 50 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/UserLoginRecordServiceImpl.java

@@ -0,0 +1,50 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.entity.UserLoginRecord;
+import cn.com.ctop.job.bytedance.data.mapper.UserLoginRecordMapper;
+import cn.com.ctop.job.bytedance.data.service.IUserLoginRecordService;
+import cn.com.ctop.job.bytedance.data.utils.DateUtils;
+import cn.com.ctop.job.bytedance.utils.FeishuRobotUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author lining
+ * @ClassName UserLoginRecordServiceImpl
+ * @description: TODO
+ * @date 2025年05月09日
+ * @version: 1.0
+ */
+@Service
+@Slf4j
+public class UserLoginRecordServiceImpl implements IUserLoginRecordService {
+    public static final String WEBHOOK_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/cddc4569-c5bb-4764-b79e-7d2e896a97b1";
+    @Autowired
+    private UserLoginRecordMapper userLoginRecordMapper;
+    @Override
+    public String queryUserLoginRecord() {
+        List<UserLoginRecord> userLoginRecords = userLoginRecordMapper.queryUserLoginRecord();
+
+        String date = DateUtils.getNowDate("YYYY-MM-dd HH:mm:ss");
+        StringBuilder text = new StringBuilder(date);
+        text.append("\n");
+        if (userLoginRecords != null && userLoginRecords.size() > 0){
+            userLoginRecords.forEach(userLoginRecord -> {
+                text.append("用户ID:").append(userLoginRecord.getUid()).append("\n")
+                        .append("用户openid:").append(userLoginRecord.getOpenId()).append("\n")
+                        .append("登录次数:").append(userLoginRecord.getLoginTimes()).append("\n");
+            });
+            log.info("登录次数消息:{}", text);
+            try {
+                FeishuRobotUtil.sendTextToWebhook(text.toString(), WEBHOOK_URL);
+            } catch (Exception e) {
+                log.error("发送飞书消息失败", e);
+            }
+        }
+
+        return text.toString();
+    }
+}

+ 1 - 1
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/utils/HttpUtils.java

@@ -122,7 +122,7 @@ public class HttpUtils {
                 return httpPostRequest(newUrl, param, headers);
             } else if (statusCode == HttpStatus.SC_OK) {
                 respentity = response.getEntity();
-                strReturn = EntityUtils.toString(respentity);
+                strReturn = EntityUtils.toString(respentity,"UTF-8");
                 return strReturn;
             }
         } catch (Exception e) {

+ 55 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/ByteDanceFlowDetailsReportJob.java

@@ -0,0 +1,55 @@
+package cn.com.ctop.job.bytedance.handler;
+
+import cn.com.ctop.job.bytedance.data.enums.AccountTypeEnum;
+import cn.com.ctop.job.bytedance.data.service.IByteDanceFlowDetailsReportService;
+import cn.com.ctop.job.bytedance.data.utils.Check;
+import cn.com.ctop.job.bytedance.data.utils.DateUtils;
+import com.xxl.job.core.context.XxlJobHelper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * @author lining
+ * @ClassName ByteDanceFlowDetailsReportJob
+ * @description: TODO
+ * @date 2025年04月16日
+ * @version: 1.0
+ */
+@Slf4j
+@Component
+public class ByteDanceFlowDetailsReportJob {
+
+    @Autowired
+    private IByteDanceFlowDetailsReportService byteDanceFlowDetailsReportService;
+
+    private static ExecutorService executorService = Executors.newFixedThreadPool(2);
+
+    @XxlJob("flowDetailsDaily")
+    public void flowDetailsDaily() throws Exception {
+        String param = XxlJobHelper.getJobParam(); // 执行参数
+        if (Check.isNull(param)) {
+            log.error("入参为空");
+            return;
+        }
+
+        String nowDate = DateUtils.getNowDate("yyyy-MM-dd");
+        String data = DateUtils.getAnotherDay("yyyy-MM-dd", nowDate, -1);
+        String[] s = param.split("_");
+        Long advertiserId = Long.valueOf(s[0]);
+        String accessToken = s[1];
+
+        executorService.submit(new Runnable() {
+            @Override
+            public void run() {
+                if (!Check.isNull(accessToken)) {
+                    byteDanceFlowDetailsReportService.getBytedanceFlowDetailsDailyReport(advertiserId, accessToken, AccountTypeEnum.AD.getValue(), data, data, 1, null);
+                }
+            }
+        });
+    }
+}

+ 29 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedanceMiniAppJob.java

@@ -0,0 +1,29 @@
+package cn.com.ctop.job.bytedance.handler;
+
+
+import cn.com.ctop.job.bytedance.data.service.IBytedanceMiniAppService;
+import cn.com.ctop.job.bytedance.data.utils.DateUtils;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+
+@Slf4j
+@Component
+public class BytedanceMiniAppJob {
+    @Autowired
+    private IBytedanceMiniAppService bytedanceMiniAppService;
+
+    @XxlJob("yesterdayMaEcpm")
+    public void yesterdayMaEcpm() throws Exception {
+        List<String> iaaAppIds = bytedanceMiniAppService.getIaaAppIds();
+        for (String iaaAppId : iaaAppIds) {
+            String date = DateUtils.getNowAddDate("yyyy-MM-dd", -1);
+            bytedanceMiniAppService.getMaEcpm(date, date, iaaAppId, 1, null, null);
+        }
+    }
+
+}

+ 31 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedanceProductUploadJob.java

@@ -0,0 +1,31 @@
+package cn.com.ctop.job.bytedance.handler;
+
+import cn.com.ctop.job.bytedance.data.service.IBytedanceProductUpdateService;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class BytedanceProductUploadJob {
+    @Autowired
+    IBytedanceProductUpdateService bytedanceProductUpdateService;
+
+    /**
+     * 创建和更新推广链
+     * @throws Exception
+     */
+    @XxlJob("productUploadJob")
+    public void productUploadJob() throws Exception {
+        bytedanceProductUpdateService.run();
+    }
+
+    /**
+     * 初始化推广链接用的字节小程序
+     */
+    @XxlJob("initProductLinkApp")
+    public void initProductLinkApp(){
+        bytedanceProductUpdateService.initNew();
+    }
+}

+ 1 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedancePromoterReportJob.java

@@ -105,6 +105,7 @@ public class BytedancePromoterReportJob {
         productDailyExecutor.submit(new Runnable() {
             @Override
             public void run() {
+                productReportService.deleteBytedanceProductDailyReport(advertiserId, data, data);
                 productReportService.getBytedanceProductDailyReport(advertiserId, accessToken, data, data, 1, null, 1, null);
             }
         });

+ 36 - 4
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedanceReportRetryJob.java

@@ -1,10 +1,9 @@
 package cn.com.ctop.job.bytedance.handler;
 
 
-import cn.com.ctop.job.bytedance.data.service.IBytedanceAccountReportService;
-import cn.com.ctop.job.bytedance.data.service.IBytedanceProductReportService;
-import cn.com.ctop.job.bytedance.data.service.IBytedancePromoterReportService;
-import cn.com.ctop.job.bytedance.data.service.IChannelService;
+import cn.com.ctop.job.bytedance.data.service.*;
+import cn.com.ctop.job.bytedance.data.enums.AccountTypeEnum;
+import cn.com.ctop.job.bytedance.data.service.*;
 import cn.com.ctop.job.bytedance.data.utils.Check;
 import com.alibaba.fastjson.JSONObject;
 import com.xxl.job.core.handler.annotation.XxlJob;
@@ -37,6 +36,12 @@ public class BytedanceReportRetryJob {
     @Autowired
     private IChannelService channelService;
 
+    @Autowired
+    private IBytedanceMiniAppService miniAppService;
+
+    @Autowired
+    private IByteDanceFlowDetailsReportService byteDanceFlowDetailsReportService;
+
 
     private static ExecutorService hourAccountRetrExecutorService = Executors.newFixedThreadPool(2);
     private static ExecutorService dailyAccountRetrExecutorService = Executors.newFixedThreadPool(2);
@@ -46,6 +51,8 @@ public class BytedanceReportRetryJob {
 
     private static ExecutorService productDailyRetrExecutor = Executors.newFixedThreadPool(2);
 
+    private static ExecutorService flowDailyRetrExecutor = Executors.newFixedThreadPool(2);
+
 
     @XxlJob("retryReport")
     public void retryReport() throws Exception {
@@ -115,6 +122,14 @@ public class BytedanceReportRetryJob {
                         });
                     }
 
+                } else if ("flow_details".equals(dataDimension)) { //日报流水详情
+                    flowDailyRetrExecutor.submit(new Runnable() {
+
+                        @Override
+                        public void run() {
+                            byteDanceFlowDetailsReportService.getBytedanceFlowDetailsDailyReport(advertiserId, accessToken, AccountTypeEnum.AD.getValue(), startDate, endDate, 1, id);
+                        }
+                    });
                 }
 
 
@@ -147,6 +162,23 @@ public class BytedanceReportRetryJob {
             log.info("常读重试列表为空");
         }
 
+        List<JSONObject> maEcpmRetryJob = miniAppService.getMaEcpmRetryJob();
+        if (!Check.isNull(maEcpmRetryJob)) {
+            for (int i = 0; i < maEcpmRetryJob.size(); i++) {
+                JSONObject jsonObject = maEcpmRetryJob.get(i);
+                Long id = jsonObject.getLong("id");
+                String startDate = jsonObject.getString("startDate");
+                String endDate = jsonObject.getString("endDate");
+                String dataDimension = jsonObject.getString("dataDimension");
+                Integer isSend = jsonObject.getInteger("isSend");
+                if (dataDimension != null && dataDimension.startsWith("ma_ecpm:")) {
+                    String appId = dataDimension.split(":")[1];
+                    miniAppService.getMaEcpm(startDate, endDate, appId, 1, id, isSend);
+                }
+            }
+        } else {
+            log.info("抖小IAA重试列表为空");
+        }
 
     }
 

+ 12 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/FeishuPushJob.java

@@ -21,6 +21,8 @@ public class FeishuPushJob {
     ITotalRankPushService totalRankPushService;
     @Autowired
     IDateReportsPushService dateReportsPushService;
+    @Autowired
+    IUserLoginRecordService userLoginRecordService;
 
     @XxlJob("unbindWarnPush")
     public void unbindWarnPush(){
@@ -46,4 +48,14 @@ public class FeishuPushJob {
     public void dateReportsPush(){
         dateReportsPushService.send();
     }
+
+    @XxlJob("loginRecordPush")
+    public void loginRecordPush(){
+        userLoginRecordService.queryUserLoginRecord();
+    }
+
+    @XxlJob("preDateReportsPush")
+    public void preDataReportsPush(){
+        dateReportsPushService.preSend();
+    }
 }

+ 62 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/utils/FeishuRobotUtil.java

@@ -80,6 +80,23 @@ public class FeishuRobotUtil {
         if (Check.isNull(telephones)){
             return null;
         }
+        Map<String, String> map = new HashMap<>();
+        int size = telephones.size();
+        int pageSize = 30;
+        int pageNum = size / pageSize + (size % pageSize == 0 ? 0 : 1);
+        for (int i = 0; i < pageNum; i++) {
+            int start = i * pageSize;
+            int end = Math.min(start + pageSize, size);
+            List<String> subList = telephones.subList(start, end);
+            Map<String, String> subMap = getFsUserIdOnePage(token, subList);
+            if (!Check.isNull(subMap)){
+                map.putAll(subMap);
+            }
+        }
+        return map;
+    }
+
+    private static Map<String, String> getFsUserIdOnePage(String token, List<String> telephones) throws IOException {
         //获取token
         String url = "https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id?user_id_type=user_id";
         HttpPost httpPost = new HttpPost(url);
@@ -114,4 +131,49 @@ public class FeishuRobotUtil {
             return null;
         }
     }
+
+    public static void sendTextToDt(String text, List<String> userIds) throws IOException {
+        JSONObject content = new JSONObject();
+        content.put("text", text);
+        String token = getFsZtTenantToken();
+
+        for (String userId : userIds){
+            JSONObject request = new JSONObject();
+            request.put("receive_id", userId);
+            request.put("msg_type", "text");
+            request.put("content", content.toJSONString());
+            //发送
+            try (CloseableHttpClient client = HttpClients.createDefault()){
+                HttpPost httpPost = new HttpPost("https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=user_id");
+                httpPost.setHeader("Authorization", "Bearer "+token);
+                HttpEntity entity = new StringEntity(request.toJSONString(), "UTF-8");
+                httpPost.setEntity(entity);
+                httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
+                CloseableHttpResponse respon = client.execute(httpPost);
+//            System.out.println("发送结果: "+ EntityUtils.toString(respon.getEntity(), "UTF-8"));
+            }catch (IOException e){
+                System.out.println("发送飞书消息失败");
+            }
+        }
+    }
+
+    public static void sendTextToWebhook(String text, String url) throws IOException {
+        JSONObject content = new JSONObject();
+        content.put("text", text);
+        JSONObject request = new JSONObject();
+        request.put("msg_type", "text");
+        request.put("content", content.toJSONString());
+        //发送
+        try (CloseableHttpClient client = HttpClients.createDefault()){
+            HttpPost httpPost = new HttpPost(url);
+            HttpEntity entity = new StringEntity(request.toJSONString(), "UTF-8");
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
+            CloseableHttpResponse respon = client.execute(httpPost);
+//            System.out.println("发送结果: "+ EntityUtils.toString(respon.getEntity(), "UTF-8"));
+        }catch (IOException e){
+            System.out.println("发送飞书消息失败");
+        }
+
+    }
 }

+ 206 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/utils/Redis2Config.java

@@ -0,0 +1,206 @@
+package cn.com.ctop.job.bytedance.utils;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import redis.clients.jedis.JedisPoolConfig;
+
+@Configuration
+public class Redis2Config {
+
+    @Value("${spring.redis2.host}")
+    private String host;
+
+    @Value("${spring.redis2.port}")
+    private int port;
+
+    @Value("${spring.redis2.database}")
+    private int database;
+
+    @Value("${spring.redis2.password}")
+    private String password;
+
+    @Value("${spring.redis2.pool.max-idle}")
+    private int maxIdle;
+
+    @Value("${spring.redis2.pool.min-idle}")
+    private int minIdle;
+
+    @Value("${spring.redis2.pool.max-active}")
+    private int maxActive;
+
+    @Value("${spring.redis2.pool.max-wait}")
+    private int maxWait;
+
+
+    /**
+     * JedisPoolConfig 连接池
+     * @return
+     */
+    public JedisPoolConfig redis2PoolConfig() {
+        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
+        // 最大空闲数
+        jedisPoolConfig.setMaxIdle(maxIdle);
+        jedisPoolConfig.setMinIdle(minIdle);
+        // 连接池的最大数据库连接数
+        jedisPoolConfig.setMaxTotal(maxActive);
+        // 最大建立连接等待时间
+        jedisPoolConfig.setMaxWaitMillis(maxWait);
+        return jedisPoolConfig;
+    }
+
+    private JedisConnectionFactory redis2ConnectionFactory(JedisPoolConfig redisPoolConfig, int database){
+
+        JedisConnectionFactory JedisConnectionFactory = new JedisConnectionFactory(redisPoolConfig);
+
+        JedisConnectionFactory.setHostName(host);
+        JedisConnectionFactory.setPort(port);
+        JedisConnectionFactory.setPassword(password);
+        JedisConnectionFactory.setDatabase(database);
+        JedisConnectionFactory.afterPropertiesSet();
+        return JedisConnectionFactory;
+    }
+
+    private RedisTemplate buildRedis2Template(RedisConnectionFactory connectionFactory){
+
+        RedisTemplate template = new RedisTemplate();
+        template.setConnectionFactory(connectionFactory);
+
+        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
+        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+
+        template.setKeySerializer(stringSerializer);
+        template.setValueSerializer(stringSerializer);
+        template.setHashKeySerializer(stringSerializer);
+        template.setHashValueSerializer(jackson2JsonRedisSerializer);
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean(name = "redis2TemplateDefault")
+    public RedisTemplate redis2TemplateDefault() {
+        RedisTemplate redisTemplate = buildRedis2Template(redis2ConnectionFactory(redis2PoolConfig(), database));
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        return redisTemplate;
+    }
+
+    @Bean(name = "redis2Template_db2")
+    public RedisTemplate redis2Template_db2() {
+        RedisTemplate redisTemplate = buildRedis2Template(redis2ConnectionFactory(redis2PoolConfig(), 2));
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        return redisTemplate;
+    }
+
+    @Bean(name = "redis2Template_db5")
+    public RedisTemplate redis2Template_db5() {
+        RedisTemplate redisTemplate = buildRedis2Template(redis2ConnectionFactory(redis2PoolConfig(), 5));
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        return redisTemplate;
+    }
+
+    @Bean(name = "redis2Template_db6")
+    public RedisTemplate redis2Template_db6() {
+        RedisTemplate redisTemplate = buildRedis2Template(redis2ConnectionFactory(redis2PoolConfig(), 6));
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        return redisTemplate;
+    }
+
+    @Bean(name = "redis2Template_db7")
+    public RedisTemplate redis2Template_db7() {
+        RedisTemplate redisTemplate = buildRedis2Template(redis2ConnectionFactory(redis2PoolConfig(), 7));
+        return redisTemplate;
+    }
+
+    @Bean(name = "redis2Template_db8")
+    public RedisTemplate redis2Template_db8() {
+        RedisTemplate redisTemplate = buildRedis2Template(redis2ConnectionFactory(redis2PoolConfig(), 8));
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        return redisTemplate;
+    }
+
+    @Bean(name = "redis2Template_db9")
+    public RedisTemplate redis2Template_db9() {
+        RedisTemplate redisTemplate = buildRedis2Template(redis2ConnectionFactory(redis2PoolConfig(), 9));
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        return redisTemplate;
+    }
+
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public void setDatabase(int database) {
+        this.database = database;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public void setMaxIdle(int maxIdle) {
+        this.maxIdle = maxIdle;
+    }
+
+    public void setMinIdle(int minIdle) {
+        this.minIdle = minIdle;
+    }
+
+    public void setMaxActive(int maxActive) {
+        this.maxActive = maxActive;
+    }
+
+    public void setMaxWait(int maxWait) {
+        this.maxWait = maxWait;
+    }
+
+    public String getHost() {
+
+        return host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public int getDatabase() {
+        return database;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public int getMaxIdle() {
+        return maxIdle;
+    }
+
+    public int getMinIdle() {
+        return minIdle;
+    }
+
+    public int getMaxActive() {
+        return maxActive;
+    }
+
+    public int getMaxWait() {
+        return maxWait;
+    }
+}

+ 11 - 1
job-bytedance/src/main/resources/application.yml

@@ -81,7 +81,7 @@ spring:
       datasource:
         master:
           url: jdbc:mysql://172.16.0.117:3390/media_api?characterEncoding=UTF-8&useUnicode=true&useSSL=false
-         # url: jdbc:mysql://8.147.135.80:3390/media_api?characterEncoding=UTF-8&useUnicode=true&allowMultiQueries=true&useSSL=false
+          #url: jdbc:mysql://8.147.135.80:3390/media_api?characterEncoding=UTF-8&useUnicode=true&allowMultiQueries=true&useSSL=false
           username: data
           password: hr@2025..
           driver-class-name: com.mysql.jdbc.Driver
@@ -109,6 +109,16 @@ spring:
         # #连接池最大阻塞等待时间(使用负值表示没有限制)
         max-wait: -1ms
 
+  redis2:
+    database: 0
+    host: r-2zelacjrljt205nsb1pd.redis.rds.aliyuncs.com
+    password: Huanrui@2025!@
+    pool:
+      max-active: 10
+      max-idle: 10
+      max-wait: 80
+      min-idle: 8
+    port: 6379
 
 
 #mybatis plus 设置