Pārlūkot izejas kodu

feat:创意日报

pudongliang 3 mēneši atpakaļ
vecāks
revīzija
59e8181cf1
23 mainītis faili ar 918 papildinājumiem un 211 dzēšanām
  1. 1 1
      AGENTS.md
  2. 13 12
      portal/src/views/ad-plan-conf/detail.vue
  3. 15 12
      src/main/java/com/moka/gdtauto/client/TencentAdsApiClientFactory.java
  4. 0 29
      src/main/java/com/moka/gdtauto/controller/DailyAdgroupReportController.java
  5. 1 0
      src/main/java/com/moka/gdtauto/entity/BaseDailyAdgroupReport.java
  6. 1 0
      src/main/java/com/moka/gdtauto/entity/BaseDailyCreativeReport.java
  7. 4 0
      src/main/java/com/moka/gdtauto/mapper/RepDailyAdgroupReportMapper.java
  8. 4 0
      src/main/java/com/moka/gdtauto/mapper/ReqDailyAdgroupReportMapper.java
  9. 3 0
      src/main/java/com/moka/gdtauto/service/AdPlanConfService.java
  10. 171 105
      src/main/java/com/moka/gdtauto/service/DailyAdgroupReportService.java
  11. 8 7
      src/main/java/com/moka/gdtauto/service/DailyCreativeReportService.java
  12. 13 6
      src/main/java/com/moka/gdtauto/service/SysUserService.java
  13. 21 7
      src/main/java/com/moka/gdtauto/service/TencentAdsAuthService.java
  14. 63 0
      src/main/java/com/moka/gdtauto/service/impl/AdPlanConfServiceImpl.java
  15. 17 0
      src/main/java/com/moka/gdtauto/task/AdAutoTask.java
  16. 103 0
      src/main/java/com/moka/gdtauto/task/DailyAdgroupReportSyncTask.java
  17. 17 17
      src/main/java/com/moka/gdtauto/task/DailyCreativeReportSyncTask.java
  18. 201 3
      src/main/resources/mapper/RepDailyAdgroupReportMapper.xml
  19. 4 4
      src/main/resources/mapper/RepDailyCreativeReportMapper.xml
  20. 202 3
      src/main/resources/mapper/ReqDailyAdgroupReportMapper.xml
  21. 4 4
      src/main/resources/mapper/ReqDailyCreativeReportMapper.xml
  22. 22 0
      src/test/java/com/moka/gdtauto/service/AdAutoDeleteTest.java
  23. 30 1
      src/test/java/com/moka/gdtauto/service/AdPlanDailyReportSyncTest.java

+ 1 - 1
AGENTS.md

@@ -8,11 +8,11 @@
 
 ## Working Rules
 - Prefer existing code patterns in this repository over introducing new abstractions.
-- When the user provides a concrete method context and asks for a change, limit the code change to that method's behavior unless broader refactoring is explicitly confirmed.
 - For backend API responses used by the frontend, prefer `com.moka.gdtauto.common.Result`; frontend business success is determined by the `success` field.
 - Do not read `src/main/resources` configuration files unless the task requires it and the user has confirmed.
 - Do not inspect generated or dependency directories unless strictly necessary, especially `target/`, `node_modules/`, and `dist/`.
 - Avoid reading files and directories already ignored by `.gitignore` unless they are directly relevant to the task.
+- Key logic with Chinese comments
 
 ## Project References
 - Tencent Ads SDK usage reference: check tests under `src/test` when present, especially Tencent Ads create-flow examples such as `TencentAdsAdgroupCreateTest`.

+ 13 - 12
portal/src/views/ad-plan-conf/detail.vue

@@ -415,6 +415,17 @@
             <span class="text-red-500">{{ row.failReason || '-' }}</span>
           </template>
         </el-table-column>
+        <el-table-column label="设置状态" prop="configuredStatus" width="100" >
+          <template #default="{ row }">
+            {{ getConfiguredStatusText(row.configuredStatus) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="系统状态" prop="systemStatus" width="100" show-overflow-tooltip />
+        <el-table-column label="系统删除" prop="systemDelete" width="85" >
+          <template #default="{ row }">
+            {{ row.systemDelete == 'true' ? '是' : '否'}}
+          </template>
+          </el-table-column>
         <el-table-column label="花费" prop="cost" width="100">
           <template #default="{ row }">
             {{ row.cost && (row.cost / 100).toFixed(2) || '-' }}
@@ -444,17 +455,7 @@
         <el-table-column label="转化首日ROI" prop="repIncomeRoi1" width="120"></el-table-column>
         <!-- <el-table-column prop="target" label="定向模版ID" width="100" show-overflow-tooltip /> -->
         <!-- <el-table-column prop="site" label="版位模版ID" width="100" show-overflow-tooltip /> -->
-        <el-table-column label="设置状态" prop="configuredStatus" width="100" >
-          <template #default="{ row }">
-            {{ getConfiguredStatusText(row.configuredStatus) }}
-          </template>
-        </el-table-column>
-        <el-table-column label="系统状态" prop="systemStatus" width="100" show-overflow-tooltip />
-        <el-table-column label="系统删除" prop="systemDelete" width="85" >
-          <template #default="{ row }">
-            {{ row.systemDelete == 'true' ? '是' : '否'}}
-          </template>
-          </el-table-column>
+        
         <el-table-column label="创建时间" width="180">
           <template #default="{ row }">
             {{ formatDate(row.createTime) }}
@@ -739,7 +740,7 @@ const getConfiguredStatusText = (status) => {
     1: '有效',
     2: '暂停'
   }
-  return map[status] || '未'
+  return map[status] || '未设置'
 }
 
 // 格式化日期

+ 15 - 12
src/main/java/com/moka/gdtauto/client/TencentAdsApiClientFactory.java

@@ -1,16 +1,18 @@
 package com.moka.gdtauto.client;
 
+import java.time.LocalDateTime;
+
+import org.springframework.stereotype.Component;
+
 import com.moka.gdtauto.entity.SysUser;
 import com.moka.gdtauto.entity.TencentAdsAuth;
 import com.moka.gdtauto.service.SysUserService;
 import com.moka.gdtauto.service.TencentAdsAuthService;
 import com.tencent.ads.ApiContextConfig;
 import com.tencent.ads.v3.TencentAds;
+
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
 
 /**
  * 腾讯广告API客户端工厂
@@ -39,7 +41,7 @@ public class TencentAdsApiClientFactory {
     public TencentAds getTencentAds(String jobNumber, Long orgAccountId) throws Exception {
        
         // 获取账户的访问令牌
-        TencentAdsAuth auth = authService.getByJobNumberAndAccountId(jobNumber, orgAccountId);
+        TencentAdsAuth auth = authService.getByAccountId(orgAccountId);
         if (auth == null) {
             throw new Exception("未找到账户授权信息:" + orgAccountId);
         }
@@ -52,15 +54,16 @@ public class TencentAdsApiClientFactory {
         }
 
         // 获取用户的实名认证令牌
-        SysUser user = sysUserService.getByJobNumber(jobNumber);
         String userTokenStr = null;
-        if (user != null && sysUserService.isUserTokenValid(user)) {
-            userTokenStr = user.getUserToken();
-            log.debug("使用工号 {} 的实名认证令牌", jobNumber);
-        } else {
-            log.warn("工号 {} 未找到有效的实名认证令牌,部分受限接口将无法调用", jobNumber);
+        if (jobNumber != null) {
+            SysUser user = sysUserService.getByJobNumber(jobNumber);
+            if (user != null && sysUserService.isUserTokenValid(user)) {
+                userTokenStr = user.getUserToken();
+                log.debug("使用工号 {} 的实名认证令牌", jobNumber);
+            } else {
+                log.warn("工号 {} 未找到有效的实名认证令牌,部分受限接口将无法调用", jobNumber);
+            }
         }
-
         // 创建并初始化TencentAds实例
         return createTencentAds(auth.getAccessToken(), userTokenStr);
     }
@@ -75,7 +78,7 @@ public class TencentAdsApiClientFactory {
      */
     public TencentAds getTencentAdsWithoutUserToken(String jobNumber, Long orgAccountId) throws Exception {
         // 获取账户的访问令牌
-        TencentAdsAuth auth = authService.getByJobNumberAndAccountId(jobNumber, orgAccountId);
+        TencentAdsAuth auth = authService.getByAccountId(orgAccountId);
         if (auth == null) {
             throw new Exception("未找到账户授权信息:" + orgAccountId);
         }

+ 0 - 29
src/main/java/com/moka/gdtauto/controller/DailyAdgroupReportController.java

@@ -1,16 +1,13 @@
 package com.moka.gdtauto.controller;
 
 import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.moka.gdtauto.annotation.AuthUser;
 import com.moka.gdtauto.common.Result;
 import com.moka.gdtauto.entity.BaseDailyAdgroupReport;
-import com.moka.gdtauto.entity.SysUser;
 import com.moka.gdtauto.service.DailyAdgroupReportService;
 
 import io.swagger.v3.oas.annotations.Operation;
@@ -35,32 +32,6 @@ public class DailyAdgroupReportController {
     private final DailyAdgroupReportService dailyReportService;
 
     /**
-     * 手动触发同步(指定账户、日期范围、时间口径)
-     */
-    @Operation(summary = "手动同步日报表", description = "指定账户和日期范围,将腾讯广告日报数据同步到本地数据库")
-    @PostMapping("/sync")
-    public Result<Void> sync(
-            @Parameter(description = "工号") @RequestParam String jobNumber,
-            @Parameter(description = "组织账户ID") @RequestParam Long orgAccountId,
-            @Parameter(description = "广告账户ID") @RequestParam Long accountId,
-            @Parameter(description = "账号组ID") @RequestParam(defaultValue = "0") Long groupId,
-            @Parameter(description = "账号组名称") @RequestParam(required = false) String groupName,
-            @Parameter(description = "开始日期 yyyy-MM-dd") @RequestParam String startDate,
-            @Parameter(description = "结束日期 yyyy-MM-dd") @RequestParam String endDate,
-            @Parameter(description = "时间口径 REQUEST_TIME / REPORTING_TIME")
-            @RequestParam(defaultValue = "REQUEST_TIME") String timeLine,
-            @AuthUser SysUser user) {
-        try {
-            int saved = dailyReportService.syncReport(jobNumber, orgAccountId, accountId,
-                    groupId, groupName, startDate, endDate, timeLine);
-            return Result.<Void>success("同步成功,共写入 " + saved + " 条数据").withCount(saved);
-        } catch (Exception e) {
-            log.error("日报表同步失败 accountId={} timeLine={}", accountId, timeLine, e);
-            return Result.fail("同步失败:" + e.getMessage());
-        }
-    }
-
-    /**
      * 分页查询日报表
      */
     @Operation(summary = "查询日报表列表", description = "分页查询指定口径的动态创意日报数据")

+ 1 - 0
src/main/java/com/moka/gdtauto/entity/BaseDailyAdgroupReport.java

@@ -22,6 +22,7 @@ public abstract class BaseDailyAdgroupReport {
     /** 主键ID */
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
+    private Long confId;
 
     /** 广告主账号ID */
     private Long accountId;

+ 1 - 0
src/main/java/com/moka/gdtauto/entity/BaseDailyCreativeReport.java

@@ -23,6 +23,7 @@ public abstract class BaseDailyCreativeReport {
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
 
+    private Long confId;
     /** 广告主账号ID */
     private Long accountId;
 

+ 4 - 0
src/main/java/com/moka/gdtauto/mapper/RepDailyAdgroupReportMapper.java

@@ -1,5 +1,7 @@
 package com.moka.gdtauto.mapper;
 
+import java.util.List;
+
 import org.apache.ibatis.annotations.Param;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@@ -15,4 +17,6 @@ import com.moka.gdtauto.entity.RepDailyAdgroupReport;
  */
 public interface RepDailyAdgroupReportMapper extends BaseMapper<RepDailyAdgroupReport> {
     void upsert(@Param("report") RepDailyAdgroupReport report);
+
+    void batchUpsert(@Param("list") List<RepDailyAdgroupReport> list);
 }

+ 4 - 0
src/main/java/com/moka/gdtauto/mapper/ReqDailyAdgroupReportMapper.java

@@ -1,5 +1,7 @@
 package com.moka.gdtauto.mapper;
 
+import java.util.List;
+
 import org.apache.ibatis.annotations.Param;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@@ -15,4 +17,6 @@ import com.moka.gdtauto.entity.ReqDailyAdgroupReport;
  */
 public interface ReqDailyAdgroupReportMapper extends BaseMapper<ReqDailyAdgroupReport> {
     void upsert(@Param("report") ReqDailyAdgroupReport report);
+
+    void batchUpsert(@Param("list") List<ReqDailyAdgroupReport> list);
 }

+ 3 - 0
src/main/java/com/moka/gdtauto/service/AdPlanConfService.java

@@ -70,4 +70,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
     void gdtSuspendByPlanIds(AdPlanConf conf, List<Long> planIds);
     void gdtSuspendByPlans(AdPlanConf conf, List<AdPlan> plans);
     void gdtSuspendByCreatives(AdPlanConf conf, List<AdPlanCreative> creatives);
+
+
+    void gdtAdgroupPatrolDelete(AdPlanConf conf);
 }

+ 171 - 105
src/main/java/com/moka/gdtauto/service/DailyAdgroupReportService.java

@@ -2,8 +2,16 @@ package com.moka.gdtauto.service;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
@@ -11,16 +19,17 @@ import org.springframework.util.StringUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.moka.gdtauto.client.TencentAdsApiClientFactory;
-import com.moka.gdtauto.entity.AccountGroup;
-import com.moka.gdtauto.entity.AccountGroupAccountRelationship;
+import com.moka.gdtauto.common.Constant;
+import com.moka.gdtauto.entity.AdPlan;
+import com.moka.gdtauto.entity.AdPlanConf;
 import com.moka.gdtauto.entity.BaseDailyAdgroupReport;
 import com.moka.gdtauto.entity.RepDailyAdgroupReport;
 import com.moka.gdtauto.entity.ReqDailyAdgroupReport;
-import com.moka.gdtauto.mapper.AccountGroupAccountRelationshipMapper;
-import com.moka.gdtauto.mapper.AccountGroupMapper;
 import com.moka.gdtauto.mapper.RepDailyAdgroupReportMapper;
 import com.moka.gdtauto.mapper.ReqDailyAdgroupReportMapper;
+import com.tencent.ads.model.v3.DailyReportsFilteringStruct;
 import com.tencent.ads.model.v3.DailyReportsGetResponseData;
+import com.tencent.ads.model.v3.FilterOperator;
 import com.tencent.ads.model.v3.ReportDateRange;
 import com.tencent.ads.model.v3.ReportStruct;
 import com.tencent.ads.model.v3.TimeLine;
@@ -30,7 +39,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
 /**
- * 广告日报表 Service
+ * 广告日报表 Service
  * 支持双口径(REQUEST_TIME / REPORTING_TIME)同步与查询
  *
  * @author moka
@@ -42,14 +51,14 @@ import lombok.extern.slf4j.Slf4j;
 public class DailyAdgroupReportService {
 
     private final TencentAdsApiClientFactory clientFactory;
-    private final TencentAccountService accountService;
-    private final AccountGroupAccountRelationshipMapper relationshipMapper;
-    private final AccountGroupMapper accountGroupMapper;
     private final ReqDailyAdgroupReportMapper reqMapper;
     private final RepDailyAdgroupReportMapper repMapper;
+    private final AdPlanConfService adPlanConfService;
+    private final AdPlanService adPlanService;
 
     /** 同步时请求的指标字段 */
     private static final List<String> SYNC_FIELDS = Arrays.asList(
+            "account_id",
             "adgroup_id", "adgroup_name", "date",
             // 曝光点击
             "view_count", "view_user_count", "avg_view_per_user",
@@ -83,33 +92,135 @@ public class DailyAdgroupReportService {
             "income_val_24h", "income_roi_1_24h"
     );
 
-    // ─── 同步方法 ─────────────────────────────────────────────────────────────
-
     /**
-     * 同步指定账户的日报表(分页拉取直到全量完成)
-     *
-     * @param jobNumber    工号(用于获取 access_token)
-     * @param orgAccountId 组织账户ID
-     * @param accountId    广告账户ID
-     * @param groupId      账号组ID
-     * @param groupName    账号组名称
-     * @param startDate    开始日期 yyyy-MM-dd
-     * @param endDate      结束日期 yyyy-MM-dd
-     * @param timeLine     时间口径 REQUEST_TIME / REPORTING_TIME
-     * @return 成功写入行数
+     * 按广告计划配置同步广告组日报
      */
-    public int syncReport(String jobNumber, Long orgAccountId, Long accountId,
-                          Long groupId, String groupName,
-                          String startDate, String endDate, String timeLine) throws Exception {
-        log.info("开始同步日报表 accountId={} timeLine={} [{}, {}]", accountId, timeLine, startDate, endDate);
-
-        TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, orgAccountId);
+    public int syncReport(AdPlanConf conf, String startDate, String endDate, String timeLine) throws Exception {
+        log.info("开始同步广告组日报 confId={} timeLine={} [{}, {}]", conf.getId(), timeLine, startDate, endDate);
+        TencentAds tencentAds = clientFactory.getTencentAds(null, Constant.MAIN_ORG_ACCOUNT_ID);
+        tencentAds.setDebug(true);
 
         ReportDateRange dateRange = new ReportDateRange();
         dateRange.setStartDate(startDate);
         dateRange.setEndDate(endDate);
 
         List<String> groupBy = Arrays.asList("adgroup_id", "date");
+        int pageNum = 1;
+        int pageSize = 100;
+        int totalSaved = 0;
+
+        while (true) {
+            Page<AdPlan> page = adPlanService.page(
+                    new Page<>(pageNum, pageSize),
+                    new LambdaQueryWrapper<AdPlan>()
+                            .eq(AdPlan::getAdPlanConfId, conf.getId())
+                            .eq(AdPlan::getStatus, 4)
+                            .eq(AdPlan::getSystemDelete, "false"));
+
+            List<AdPlan> planList = page.getRecords();
+            if (planList.isEmpty()) {
+                break;
+            }
+
+            Map<Long, List<Long>> accountAdgroupIdsMap = planList.stream()
+                    .filter(plan -> plan.getAccountId() != null && plan.getAdgroupId() != null)
+                    .collect(Collectors.groupingBy(
+                            AdPlan::getAccountId,
+                            Collectors.mapping(
+                                    AdPlan::getAdgroupId,
+                                    Collectors.collectingAndThen(Collectors.toList(),
+                                            ids -> ids.stream().distinct().toList()))));
+
+            for (Map.Entry<Long, List<Long>> entry : accountAdgroupIdsMap.entrySet()) {
+                totalSaved += syncAccountReports(
+                        tencentAds,
+                        conf.getId(),
+                        conf.getAccountGroupId(),
+                        entry.getKey(),
+                        entry.getValue(),
+                        dateRange,
+                        groupBy,
+                        timeLine);
+            }
+
+            if (!page.hasNext()) {
+                break;
+            }
+            pageNum++;
+        }
+
+        log.info("广告组日报同步完成 confId={} saved={}", conf.getId(), totalSaved);
+        return totalSaved;
+    }
+
+    /**
+     * 批量同步广告组日报
+     */
+    public int syncAllAccounts(Long confId, String startDate, String endDate, String timeLine) {
+        List<AdPlanConf> confList = adPlanConfService.list(
+                new LambdaQueryWrapper<AdPlanConf>()
+                        .eq(confId != null, AdPlanConf::getId, confId)
+                        .eq(AdPlanConf::getAdType, 2)
+                        .in(AdPlanConf::getStatus, List.of(4, 5))
+                        .eq(AdPlanConf::getDeleted, 0));
+
+        log.info("查询到 {} 个广告计划配置", confList.size());
+        if (confList.isEmpty()) {
+            return 0;
+        }
+
+        AtomicInteger total = new AtomicInteger();
+        Semaphore semaphore = new Semaphore(10);
+
+        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
+            for (AdPlanConf conf : confList) {
+                executor.submit(() -> {
+                    try {
+                        semaphore.acquire();
+                        total.addAndGet(syncReport(conf, startDate, endDate, timeLine));
+                    } catch (InterruptedException e) {
+                        log.error("获取信号量被中断", e);
+                        Thread.currentThread().interrupt();
+                    } catch (Exception e) {
+                        log.error("同步广告组日报 {} 失败", conf.getId(), e);
+                    } finally {
+                        semaphore.release();
+                    }
+                });
+            }
+
+            executor.shutdown();
+            boolean terminated = executor.awaitTermination(30, TimeUnit.MINUTES);
+            if (!terminated) {
+                log.warn("同步广告组日报任务未在指定时间内完成");
+            }
+        } catch (InterruptedException e) {
+            log.error("并发处理被中断", e);
+            Thread.currentThread().interrupt();
+        }
+
+        log.info("所有广告组日报同步完成 total={}", total.get());
+        return total.get();
+    }
+
+    private int syncAccountReports(TencentAds tencentAds,
+                                    Long confId,
+                                   Long groupId,
+                                   Long accountId,
+                                   List<Long> adgroupIds,
+                                   ReportDateRange dateRange,
+                                   List<String> groupBy,
+                                   String timeLine) throws Exception {
+        if (adgroupIds == null || adgroupIds.isEmpty()) {
+            return 0;
+        }
+
+        List<DailyReportsFilteringStruct> filtering = new ArrayList<>();
+        DailyReportsFilteringStruct struct = new DailyReportsFilteringStruct();
+        struct.setField("adgroup_id");
+        struct.setOperator(FilterOperator.IN);
+        struct.setValues(adgroupIds.stream().map(String::valueOf).toList());
+        filtering.add(struct);
 
         long page = 1L;
         long pageSize = 1000L;
@@ -122,31 +233,42 @@ public class DailyAdgroupReportService {
                     groupBy,
                     SYNC_FIELDS,
                     accountId,
-                    null,
+                    filtering,
                     null,
                     timeLine,
                     page,
                     pageSize,
-                    null
-            );
+                    null);
 
             if (response == null || response.getList() == null || response.getList().isEmpty()) {
                 break;
             }
 
             List<ReportStruct> list = response.getList();
-            for (ReportStruct rs : list) {
-                if (rs.getDynamicCreativeId() == null || rs.getDate() == null) {
-                    continue;
+            if (TimeLine.REQUEST_TIME.getValue().equals(timeLine)) {
+                List<ReqDailyAdgroupReport> entities = new ArrayList<>();
+                for (ReportStruct rs : list) {
+                    if (rs.getAdgroupId() == null || rs.getDate() == null) {
+                        continue;
+                    }
+                    entities.add(toReqEntity(confId, groupId, rs));
+                }
+                if (!entities.isEmpty()) {
+                    reqMapper.batchUpsert(entities);
+                    totalSaved += entities.size();
+                }
+            } else {
+                List<RepDailyAdgroupReport> entities = new ArrayList<>();
+                for (ReportStruct rs : list) {
+                    if (rs.getAdgroupId() == null || rs.getDate() == null) {
+                        continue;
+                    }
+                    entities.add(toRepEntity(confId, groupId, rs));
                 }
-                if (TimeLine.REQUEST_TIME.getValue().equals(timeLine)) {
-                    ReqDailyAdgroupReport entity = toReqEntity(accountId, groupId, groupName, rs);
-                    reqMapper.upsert(entity);
-                } else {
-                    ReqDailyAdgroupReport entity = toRepEntity(accountId, groupId, groupName, rs);
-                    reqMapper.upsert(entity);
+                if (!entities.isEmpty()) {
+                    repMapper.batchUpsert(entities);
+                    totalSaved += entities.size();
                 }
-                totalSaved++;
             }
 
             long totalNumber = response.getPageInfo() != null
@@ -157,65 +279,9 @@ public class DailyAdgroupReportService {
             page++;
         }
 
-        log.info("日报表同步完成 accountId={} timeLine={} saved={}", accountId, timeLine, totalSaved);
         return totalSaved;
     }
 
-    /**
-     * 同步所有账户(定时任务调用)——通过 AccountGroupAccountRelationship 关联获取 groupId / groupName
-     *
-     * @param startDate 开始日期
-     * @param endDate   结束日期
-     * @param timeLine  时间口径
-     * @return 成功保存总行数
-     */
-    public int syncAllAccounts(String startDate, String endDate, String timeLine) {
-        // 查询所有账号组-账户关系
-        LambdaQueryWrapper<AccountGroupAccountRelationship> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(AccountGroupAccountRelationship::getGroupId, 1L);
-        List<AccountGroupAccountRelationship> relationships = relationshipMapper.selectList(null);
-        if (relationships.isEmpty()) {
-            log.warn("没有可同步的账户(AccountGroupAccountRelationship 为空)");
-            return 0;
-        }
-
-        // 预载所有账号组信息(减少重复查询)
-        java.util.Map<Long, AccountGroup> groupMap = new java.util.HashMap<>();
-        accountGroupMapper.selectList(new LambdaQueryWrapper<AccountGroup>().eq(AccountGroup::getDeleted, 0))
-                .forEach(g -> groupMap.put(g.getId(), g));
-
-        log.info("开始批量同步日报表 relationships={} timeLine={} [{}, {}]",
-                relationships.size(), timeLine, startDate, endDate);
-
-        int total = 0;
-        for (AccountGroupAccountRelationship rel : relationships) {
-            Long groupId = rel.getGroupId();
-            Long accountId = rel.getAccountId();
-            AccountGroup group = groupMap.get(groupId);
-
-            if (group == null) {
-                log.warn("账号组信息不存在 groupId={}", groupId);
-                continue;
-            }
-           
-            try {
-                int saved = syncReport(
-                        "12458",
-                        41644744L,
-                        accountId,
-                        groupId,
-                        group.getGroupName(),
-                        startDate, endDate, timeLine
-                );
-                total += saved;
-            } catch (Exception e) {
-                log.error("同步日报表失败 groupId={} accountId={}", groupId, accountId, e);
-            }
-        }
-        log.info("批量同步日报表完成 total={}", total);
-        return total;
-    }
-
     // ─── 查询方法 ─────────────────────────────────────────────────────────────
 
     /**
@@ -289,22 +355,22 @@ public class DailyAdgroupReportService {
 
     // ─── 映射方法 ─────────────────────────────────────────────────────────────
 
-    private ReqDailyAdgroupReport toReqEntity(Long accountId, Long groupId, String groupName, ReportStruct rs) {
+    private ReqDailyAdgroupReport toReqEntity(Long confId, Long groupId, ReportStruct rs) {
         ReqDailyAdgroupReport e = new ReqDailyAdgroupReport();
-        fillFields(e, accountId, groupId, groupName, rs);
+        fillFields(e, confId, groupId, rs);
         return e;
     }
 
-    private ReqDailyAdgroupReport toRepEntity(Long accountId, Long groupId, String groupName, ReportStruct rs) {
-        ReqDailyAdgroupReport e = new ReqDailyAdgroupReport();
-        fillFields(e, accountId, groupId, groupName, rs);
+    private RepDailyAdgroupReport toRepEntity(Long confId, Long groupId, ReportStruct rs) {
+        RepDailyAdgroupReport e = new RepDailyAdgroupReport();
+        fillFields(e, confId, groupId, rs);
         return e;
     }
 
-    private void fillFields(BaseDailyAdgroupReport e, Long accountId, Long groupId, String groupName, ReportStruct rs) {
-        e.setAccountId(accountId);
+    private void fillFields(BaseDailyAdgroupReport e, Long confId, Long groupId, ReportStruct rs) {
+        e.setConfId(confId);
+        e.setAccountId(rs.getAccountId());
         e.setGroupId(groupId);
-        e.setGroupName(groupName);
         e.setAdgroupId(rs.getAdgroupId());
         e.setAdgroupName(rs.getAdgroupName());
         e.setDate(LocalDate.parse(rs.getDate()));
@@ -378,7 +444,7 @@ public class DailyAdgroupReportService {
         // 关键页面
         e.setKeyPageViewCount(rs.getKeyPageViewCount());
         e.setKeyPageUv(rs.getKeyPageUv());
-        // ROI/LTV - 注意:SDK字段名 incomeRoi124h 对应 API income_roi_1_24h
+        // ROI/LTV
         e.setIncomeVal1(rs.getIncomeVal1());
         e.setIncomeRoi1(d2b(rs.getIncomeRoi1()));
         e.setIncomeVal3(rs.getIncomeVal3());

+ 8 - 7
src/main/java/com/moka/gdtauto/service/DailyCreativeReportService.java

@@ -186,7 +186,7 @@ public class DailyCreativeReportService {
                     if (rs.getDynamicCreativeId() == null || rs.getDate() == null) {
                         continue;
                     }
-                    entities.add(toReqEntity(conf.getAccountGroupId(), rs));
+                    entities.add(toReqEntity(conf.getId(), conf.getAccountGroupId(), rs));
                 }
                 if (!entities.isEmpty()) {
                     reqMapper.batchUpsert(entities);
@@ -197,7 +197,7 @@ public class DailyCreativeReportService {
                     if (rs.getDynamicCreativeId() == null || rs.getDate() == null) {
                         continue;
                     }
-                    entities.add(toRepEntity(conf.getAccountGroupId(), rs));
+                    entities.add(toRepEntity(conf.getId(), conf.getAccountGroupId(), rs));
                 }
                 if (!entities.isEmpty()) {
                     repMapper.batchUpsert(entities);
@@ -352,19 +352,20 @@ public class DailyCreativeReportService {
 
     // ─── 映射方法 ─────────────────────────────────────────────────────────────
 
-    private ReqDailyCreativeReport toReqEntity(Long groupId,  ReportStruct rs) {
+    private ReqDailyCreativeReport toReqEntity(Long confId, Long groupId,  ReportStruct rs) {
         ReqDailyCreativeReport e = new ReqDailyCreativeReport();
-        fillFields(e, groupId, rs);
+        fillFields(e,confId, groupId, rs);
         return e;
     }
 
-    private RepDailyCreativeReport toRepEntity(Long groupId, ReportStruct rs) {
+    private RepDailyCreativeReport toRepEntity(Long confId, Long groupId, ReportStruct rs) {
         RepDailyCreativeReport e = new RepDailyCreativeReport();
-        fillFields(e, groupId, rs);
+        fillFields(e,confId, groupId, rs);
         return e;
     }
 
-    private void fillFields(BaseDailyCreativeReport e,  Long groupId, ReportStruct rs) {
+    private void fillFields(BaseDailyCreativeReport e, Long confId, Long groupId, ReportStruct rs) {
+        e.setConfId(confId);
         e.setAccountId(rs.getAccountId());
         e.setGroupId(groupId);
         e.setDynamicCreativeId(rs.getDynamicCreativeId());

+ 13 - 6
src/main/java/com/moka/gdtauto/service/SysUserService.java

@@ -1,18 +1,22 @@
 package com.moka.gdtauto.service;
 
+import java.time.LocalDateTime;
+import java.util.UUID;
+
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.DigestUtils;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.moka.gdtauto.config.TencentAdsConfig;
 import com.moka.gdtauto.entity.SysUser;
 import com.moka.gdtauto.mapper.SysUserMapper;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.DigestUtils;
 
-import java.time.LocalDateTime;
-import java.util.UUID;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 
 /**
  * 系统用户服务
@@ -27,6 +31,7 @@ public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {
 
     private static final String DEFAULT_PASSWORD = "123456";
     private static final int LOGIN_TOKEN_EXPIRE_DAYS = 7; // 登录令牌有效期:7天
+    private static final String CACHE_NAME = "sysUser";
 
     private final TencentAdsConfig tencentAdsConfig;
     /**
@@ -43,6 +48,7 @@ public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {
      * 
      * @param user 用户信息
      */
+    @CacheEvict(value = CACHE_NAME, key = "#user.jobNumber")
     private void updateLoginToken(SysUser user) {
         String loginToken = generateLoginToken();
         LocalDateTime expiresAt = LocalDateTime.now().plusDays(LOGIN_TOKEN_EXPIRE_DAYS);
@@ -60,6 +66,7 @@ public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {
       * @param jobNumber 工号
      * @return 用户信息
      */
+    @Cacheable(value = CACHE_NAME, key = "#jobNumber", unless = "#result == null")
     public SysUser getByJobNumber(String jobNumber) {
         LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(SysUser::getJobNumber, jobNumber)

+ 21 - 7
src/main/java/com/moka/gdtauto/service/TencentAdsAuthService.java

@@ -1,5 +1,13 @@
 package com.moka.gdtauto.service;
 
+import java.time.LocalDateTime;
+import java.util.List;
+
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.moka.gdtauto.config.TencentAdsConfig;
@@ -11,13 +19,9 @@ import com.tencent.ads.exception.TencentAdsSDKException;
 import com.tencent.ads.model.v3.AuthorizerStruct;
 import com.tencent.ads.model.v3.OauthTokenResponseData;
 import com.tencent.ads.v3.TencentAds;
+
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.time.LocalDateTime;
-import java.util.List;
 
 /**
  * 腾讯广告OAuth授权服务
@@ -32,6 +36,8 @@ import java.util.List;
 public class TencentAdsAuthService extends ServiceImpl<TencentAdsAuthMapper, TencentAdsAuth> {
 
     private final TencentAdsConfig tencentAdsConfig;
+
+    private static final String CACHE_NAME = "tencentAdsAuth";
     /**
      * 获取授权URL
      * 
@@ -104,6 +110,7 @@ public class TencentAdsAuthService extends ServiceImpl<TencentAdsAuthMapper, Ten
      * @throws Exception API异常
      */
     @Transactional(rollbackFor = Exception.class)
+    @CacheEvict(value = CACHE_NAME, key = "#result.accountId")
     public TencentAdsAuth refreshAccessToken(Long id) throws Exception {
         TencentAdsAuth auth = getById(id);
         if (auth == null) {
@@ -121,6 +128,7 @@ public class TencentAdsAuthService extends ServiceImpl<TencentAdsAuthMapper, Ten
      * @throws Exception API异常
      */
     @Transactional(rollbackFor = Exception.class)
+    @CacheEvict(value = CACHE_NAME, key = "#auth.accountId")
     public TencentAdsAuth refreshAccessToken(TencentAdsAuth auth) throws Exception {
         try {
             // 初始化TencentAds实例
@@ -226,6 +234,7 @@ public class TencentAdsAuthService extends ServiceImpl<TencentAdsAuthMapper, Ten
     /**
      * 更新token数据(刷新token)
      */
+    @CacheEvict(value = "tencentAdsAuth", key = "#auth.accountId")
     private TencentAdsAuth updateTokenData(TencentAdsAuth auth, OauthTokenResponseData responseData) {
         String accessToken = responseData.getAccessToken();
         String refreshToken = responseData.getRefreshToken();
@@ -250,9 +259,14 @@ public class TencentAdsAuthService extends ServiceImpl<TencentAdsAuthMapper, Ten
      * 根据账户ID查询授权信息
      * 
      * @param accountId 账户ID
+    /**
+     * 根据账户ID查询授权信息(带缓存,1分钟过期)
+     * 
+     * @param accountId 账户ID
      * @return 授权信息
      */
-    public TencentAdsAuth getByJobNumberAndAccountId(String jobNumber, Long accountId) {
+    @Cacheable(value = "tencentAdsAuth", key = "#accountId", unless = "#result == null", condition = "#accountId != null")
+    public TencentAdsAuth getByAccountId(Long accountId) {
         LambdaQueryWrapper<TencentAdsAuth> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(TencentAdsAuth::getAccountId, accountId)
                 .eq(TencentAdsAuth::getDeleted, 0)
@@ -260,7 +274,7 @@ public class TencentAdsAuthService extends ServiceImpl<TencentAdsAuthMapper, Ten
                 .last("LIMIT 1");
         return getOne(wrapper);
     }
-
+    
     /**
      * 获取所有有效的授权信息
      * 

+ 63 - 0
src/main/java/com/moka/gdtauto/service/impl/AdPlanConfServiceImpl.java

@@ -28,11 +28,13 @@ import com.moka.gdtauto.entity.AdPlan;
 import com.moka.gdtauto.entity.AdPlanConf;
 import com.moka.gdtauto.entity.AdPlanCreative;
 import com.moka.gdtauto.entity.OssFile;
+import com.moka.gdtauto.entity.ReqDailyAdgroupReport;
 import com.moka.gdtauto.entity.TencentAccountIdBrandImage;
 import com.moka.gdtauto.mapper.AdPlanConfMapper;
 import com.moka.gdtauto.service.AdPlanConfService;
 import com.moka.gdtauto.service.AdPlanCreativeService;
 import com.moka.gdtauto.service.AdPlanService;
+import com.moka.gdtauto.service.ReqDailyAdgroupReportService;
 import com.moka.gdtauto.service.TencentAccountIdBrandImageService;
 import com.moka.gdtauto.service.TencentAccountIdImageService;
 import com.moka.gdtauto.service.TencentAccountIdVideoIdService;
@@ -99,6 +101,8 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
     private TencentAccountIdVideoIdService tencentAccountIdVideoIdService;
     @Autowired
     private TencentAccountIdBrandImageService tencentAccountIdBrandImageService;
+    @Autowired
+    private ReqDailyAdgroupReportService reqDailyAdgroupReportService;
 
     @Autowired
     private TencentAssetService tencentAssetService;
@@ -1037,4 +1041,63 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             return 1;
         }
     }
+
+
+    @Override
+    public void gdtAdgroupPatrolDelete(AdPlanConf conf) {
+        try {
+            List<AdPlan> plans = adPlanService.lambdaQuery()
+                    .eq(AdPlan::getAdPlanConfId, conf.getId())
+                    .gt(AdPlan::getAdgroupId, 0)
+                    .ne(AdPlan::getSystemDelete, "true")
+                    .orderByAsc(AdPlan::getId)
+                    .list();
+            if (plans.isEmpty()) {
+                log.info("gdtAdgroupPatrolDelete 无可巡检广告组 confId={}", conf.getId());
+                return;
+            }
+
+            Map<Long, AdPlan> planMap = plans.stream()
+                    .filter(plan -> plan.getAdgroupId() != null)
+                    .collect(Collectors.toMap(AdPlan::getAdgroupId, plan -> plan, (left, right) -> left));
+
+            // 3天前连续5天消费为0
+            LocalDate endDate = LocalDate.now().minusDays(3);
+            LocalDate startDate = endDate.minusDays(5);
+
+            List<ReqDailyAdgroupReport> reports = reqDailyAdgroupReportService.lambdaQuery()
+                    .in(ReqDailyAdgroupReport::getAdgroupId, planMap.keySet())
+                    .between(ReqDailyAdgroupReport::getDate, startDate, endDate)
+                    .list();
+
+            Map<Long, Long> validCostSumMap = reports.stream()
+                    .collect(Collectors.groupingBy(
+                            ReqDailyAdgroupReport::getAdgroupId,
+                            Collectors.summingLong(report -> report.getCost() == null ? 0L : report.getCost())));
+
+            List<AdPlan> deletePlans = planMap.entrySet().stream()
+                    .filter(entry -> validCostSumMap.getOrDefault(entry.getKey(), 0L) == 0L)
+                    .map(Map.Entry::getValue)
+                    .toList();
+
+            if (deletePlans.isEmpty()) {
+                log.info("gdtAdgroupPatrolDelete 无需删除广告组 confId={} [{}, {}]", conf.getId(), startDate, endDate);
+                return;
+            }
+
+            TencentAds tencentAds = clientFactory.getTencentAds(null, Constant.MAIN_ORG_ACCOUNT_ID);
+            doDeletePlans(tencentAds, deletePlans);
+            lambdaUpdate().eq(AdPlanConf::getId, conf.getId())
+                    .set(AdPlanConf::getFailReason, "广告巡检删除" + deletePlans.size() + "个广告")
+                    .update();
+            log.info("gdtAdgroupPatrolDelete 删除广告组完成 confId={} deleteCount={} [{}, {}]",
+                    conf.getId(), deletePlans.size(), startDate, endDate);
+        } catch (Exception e) {
+            log.error("gdtAdgroupPatrolDelete error confId={}", conf.getId(), e);
+            lambdaUpdate()
+            .eq(AdPlanConf::getId, conf.getId())
+                    .set(AdPlanConf::getFailReason, e.getMessage())
+                    .update();
+        }
+    }
 }

+ 17 - 0
src/main/java/com/moka/gdtauto/task/AdAutoTask.java

@@ -1,13 +1,16 @@
 package com.moka.gdtauto.task;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import com.moka.gdtauto.entity.AdAutoConf;
+import com.moka.gdtauto.entity.AdPlanConf;
 import com.moka.gdtauto.service.AdAutoConfService;
 import com.moka.gdtauto.service.AdPlanAutoExecutor;
+import com.moka.gdtauto.service.AdPlanConfService;
 import com.moka.gdtauto.service.AdPlanCreativeAutoExecutor;
 
 import lombok.AllArgsConstructor;
@@ -22,6 +25,7 @@ public class AdAutoTask {
     private final AdAutoConfService adAutoConfService;
     private final AdPlanAutoExecutor adPlanAutoExecutor;
     private final AdPlanCreativeAutoExecutor adPlanCreativeAutoExecutor;
+    private final AdPlanConfService adPlanConfService;
     // private final ExecutorService executorService = Executors.newFixedThreadPool(10);
 
     @Scheduled(cron = "0 0/10 * * * ?")
@@ -39,4 +43,17 @@ public class AdAutoTask {
         List<AdAutoConf> confList = adAutoConfService.lambdaQuery().list();
         confList.forEach(conf -> adPlanCreativeAutoExecutor.asyncExecute(conf));
     }
+
+    @Scheduled(cron = "0 20 0 * * ?")
+    @SchedulerLock(name = "ad-auto-delete-task", lockAtMostFor = "9m", lockAtLeastFor = "2m")
+    public void adAutoDeleteTask() {
+        log.info("AdAutoDeleteTask execute");
+        //3天前
+        LocalDateTime threeDaysAgo = LocalDateTime.now().minusDays(3);
+        List<AdPlanConf> confList = 
+            adPlanConfService.lambdaQuery()
+            .lt(AdPlanConf::getCreateTime, threeDaysAgo)
+            .list();
+        confList.forEach(conf -> adPlanConfService.gdtAdgroupPatrolDelete(conf));
+    }
 }

+ 103 - 0
src/main/java/com/moka/gdtauto/task/DailyAdgroupReportSyncTask.java

@@ -0,0 +1,103 @@
+package com.moka.gdtauto.task;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import com.moka.gdtauto.service.DailyAdgroupReportService;
+import com.tencent.ads.model.v3.TimeLine;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
+
+/**
+ * 动态创意日报表定时同步任务
+ * 支持双口径(REQUEST_TIME / REPORTING_TIME)
+ *
+ * @author moka
+ * @since 2026-03-03
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DailyAdgroupReportSyncTask {
+
+    private final DailyAdgroupReportService dailyAdgroupReportService;
+
+    private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+    // ─── 同步当日数据 ────────────────────────────────────────────────────────────
+
+    /**
+     * 同步当日 REQUEST_TIME 口径日报表
+     * cron: 每小时
+     */
+    @Scheduled(cron = "0 10 * * * *")
+    @SchedulerLock(name = "syncTodayReqDailyAdgroupReport", lockAtMostFor = "4m", lockAtLeastFor = "1m")
+    public void syncTodayRequestTime() {
+        String today = LocalDate.now().format(FMT);
+        log.info("[定时任务] 广告同步当日 REQUEST_TIME 日报表 date={}", today);
+        try {
+            dailyAdgroupReportService.syncAllAccounts(null, today, today, TimeLine.REQUEST_TIME.getValue());
+            log.info("[定时任务] 广告当日 REQUEST_TIME 日报表同步完成");
+        } catch (Exception e) {
+            log.error("[定时任务] 广告当日 REQUEST_TIME 日报表同步异常", e);
+        }
+    }
+
+    /**
+     * 同步当日 REPORTING_TIME 口径日报表
+     * cron: 每小时的1分执行(与 REQUEST_TIME 错开 30s 避免并发)
+     */
+    @Scheduled(cron = "30 1 * * * *")
+    @SchedulerLock(name = "syncTodayRepDailyAdgroupReport", lockAtMostFor = "4m", lockAtLeastFor = "1m")
+    public void syncTodayReportingTime() {
+        String today = LocalDate.now().format(FMT);
+        log.info("[定时任务] 广告同步当日 REPORTING_TIME 日报表 date={}", today);
+        try {
+            dailyAdgroupReportService.syncAllAccounts(null, today, today, TimeLine.REPORTING_TIME.getValue());
+            log.info("[定时任务] 广告当日 REPORTING_TIME 日报表同步完成");
+        } catch (Exception e) {
+            log.error("[定时任务] 广告当日 REPORTING_TIME 日报表同步异常", e);
+        }
+    }
+
+    // ─── 同步昨日数据 ────────────────────────────────────────────────────────────
+
+    /**
+     * 同步昨日 REQUEST_TIME 口径日报表
+     * cron: 每天 0,5,8,11,13,15 点的第 3 分钟执行
+     */
+    @Scheduled(cron = "0 5 0,5,8,11,13,15 * * *")
+    @SchedulerLock(name = "syncYesterdayReqDailyAdgroupReport", lockAtMostFor = "9m", lockAtLeastFor = "2m")
+    public void syncYesterdayRequestTime() {
+        String yesterday = LocalDate.now().minusDays(1).format(FMT);
+        log.info("[定时任务] 广告同步昨日 REQUEST_TIME 日报表 date={}", yesterday);
+        try {
+            dailyAdgroupReportService.syncAllAccounts(null, yesterday, yesterday, TimeLine.REQUEST_TIME.getValue());
+            log.info("[定时任务] 广告昨日 REQUEST_TIME 日报表同步完成");
+        } catch (Exception e) {
+            log.error("[定时任务] 广告昨日 REQUEST_TIME 日报表同步异常", e);
+        }
+    }
+
+    /**
+     * 同步昨日 REPORTING_TIME 口径日报表
+     * cron: 每天 0,5,8,11,13,15 点的第 3 分 30 秒执行
+     */
+    @Scheduled(cron = "30 5 0,5,8,11,13,15 * * *")
+    @SchedulerLock(name = "syncYesterdayRepDailyAdgroupReport", lockAtMostFor = "9m", lockAtLeastFor = "2m")
+    public void syncYesterdayReportingTime() {
+        String yesterday = LocalDate.now().minusDays(1).format(FMT);
+        log.info("[定时任务] 广告同步昨日 REPORTING_TIME 日报表 date={}", yesterday);
+        try {
+            dailyAdgroupReportService.syncAllAccounts(null, yesterday, yesterday, TimeLine.REPORTING_TIME.getValue());
+            log.info("[定时任务] 广告昨日 REPORTING_TIME 日报表同步完成");
+        } catch (Exception e) {
+            log.error("[定时任务] 广告昨日 REPORTING_TIME 日报表同步异常", e);
+        }
+    }
+}

+ 17 - 17
src/main/java/com/moka/gdtauto/task/DailyReportSyncTask.java → src/main/java/com/moka/gdtauto/task/DailyCreativeReportSyncTask.java

@@ -23,7 +23,7 @@ import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
 @Slf4j
 @Component
 @RequiredArgsConstructor
-public class DailyReportSyncTask {
+public class DailyCreativeReportSyncTask {
 
     private final DailyCreativeReportService dailyReportService;
 
@@ -36,15 +36,15 @@ public class DailyReportSyncTask {
      * cron: 每小时
      */
     @Scheduled(cron = "0 0 * * * *")
-    @SchedulerLock(name = "syncTodayReqDailyReport", lockAtMostFor = "4m", lockAtLeastFor = "1m")
+    @SchedulerLock(name = "syncTodayReqDailyCreativeReport", lockAtMostFor = "4m", lockAtLeastFor = "1m")
     public void syncTodayRequestTime() {
         String today = LocalDate.now().format(FMT);
-        log.info("[定时任务] 同步当日 REQUEST_TIME 日报表 date={}", today);
+        log.info("[定时任务] 创意同步当日 REQUEST_TIME 日报表 date={}", today);
         try {
             dailyReportService.syncAllAccounts(null, today, today, TimeLine.REQUEST_TIME.getValue());
-            log.info("[定时任务] 当日 REQUEST_TIME 日报表同步完成");
+            log.info("[定时任务] 创意同步当日 REQUEST_TIME 日报表同步完成");
         } catch (Exception e) {
-            log.error("[定时任务] 当日 REQUEST_TIME 日报表同步异常", e);
+            log.error("[定时任务] 创意同步当日 REQUEST_TIME 日报表同步异常", e);
         }
     }
 
@@ -53,15 +53,15 @@ public class DailyReportSyncTask {
      * cron: 每小时的1分执行(与 REQUEST_TIME 错开 30s 避免并发)
      */
     @Scheduled(cron = "30 0 * * * *")
-    @SchedulerLock(name = "syncTodayRepDailyReport", lockAtMostFor = "4m", lockAtLeastFor = "1m")
+    @SchedulerLock(name = "syncTodayRepDailyCreativeReport", lockAtMostFor = "4m", lockAtLeastFor = "1m")
     public void syncTodayReportingTime() {
         String today = LocalDate.now().format(FMT);
-        log.info("[定时任务] 同步当日 REPORTING_TIME 日报表 date={}", today);
+        log.info("[定时任务] 创意同步当日 REPORTING_TIME 日报表 date={}", today);
         try {
             dailyReportService.syncAllAccounts(null, today, today, TimeLine.REPORTING_TIME.getValue());
-            log.info("[定时任务] 当日 REPORTING_TIME 日报表同步完成");
+            log.info("[定时任务] 创意同步当日 REPORTING_TIME 日报表同步完成");
         } catch (Exception e) {
-            log.error("[定时任务] 当日 REPORTING_TIME 日报表同步异常", e);
+            log.error("[定时任务] 创意同步当日 REPORTING_TIME 日报表同步异常", e);
         }
     }
 
@@ -72,15 +72,15 @@ public class DailyReportSyncTask {
      * cron: 每天 0,5,8,11,13,15 点的第 3 分钟执行
      */
     @Scheduled(cron = "0 3 0,5,8,11,13,15 * * *")
-    @SchedulerLock(name = "syncYesterdayReqDailyReport", lockAtMostFor = "9m", lockAtLeastFor = "2m")
+    @SchedulerLock(name = "syncYesterdayReqDailyCreativeReport", lockAtMostFor = "9m", lockAtLeastFor = "2m")
     public void syncYesterdayRequestTime() {
         String yesterday = LocalDate.now().minusDays(1).format(FMT);
-        log.info("[定时任务] 同步昨日 REQUEST_TIME 日报表 date={}", yesterday);
+        log.info("[定时任务] 创意同步昨日 REQUEST_TIME 日报表 date={}", yesterday);
         try {
             dailyReportService.syncAllAccounts(null, yesterday, yesterday, TimeLine.REQUEST_TIME.getValue());
-            log.info("[定时任务] 昨日 REQUEST_TIME 日报表同步完成");
+            log.info("[定时任务] 创意同步昨日 REQUEST_TIME 日报表同步完成");
         } catch (Exception e) {
-            log.error("[定时任务] 昨日 REQUEST_TIME 日报表同步异常", e);
+            log.error("[定时任务] 创意同步昨日 REQUEST_TIME 日报表同步异常", e);
         }
     }
 
@@ -89,15 +89,15 @@ public class DailyReportSyncTask {
      * cron: 每天 0,5,8,11,13,15 点的第 3 分 30 秒执行
      */
     @Scheduled(cron = "30 3 0,5,8,11,13,15 * * *")
-    @SchedulerLock(name = "syncYesterdayRepDailyReport", lockAtMostFor = "9m", lockAtLeastFor = "2m")
+    @SchedulerLock(name = "syncYesterdayRepDailyCreativeReport", lockAtMostFor = "9m", lockAtLeastFor = "2m")
     public void syncYesterdayReportingTime() {
         String yesterday = LocalDate.now().minusDays(1).format(FMT);
-        log.info("[定时任务] 同步昨日 REPORTING_TIME 日报表 date={}", yesterday);
+        log.info("[定时任务] 创意同步昨日 REPORTING_TIME 日报表 date={}", yesterday);
         try {
             dailyReportService.syncAllAccounts(null, yesterday, yesterday, TimeLine.REPORTING_TIME.getValue());
-            log.info("[定时任务] 昨日 REPORTING_TIME 日报表同步完成");
+            log.info("[定时任务] 创意同步昨日 REPORTING_TIME 日报表同步完成");
         } catch (Exception e) {
-            log.error("[定时任务] 昨日 REPORTING_TIME 日报表同步异常", e);
+            log.error("[定时任务] 创意同步昨日 REPORTING_TIME 日报表同步异常", e);
         }
     }
 }

+ 201 - 3
src/main/resources/mapper/RepDailyAdgroupReportMapper.xml

@@ -1,8 +1,8 @@
 <?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="com.moka.gdtauto.mapper.RepDailyAdgroupReportMapper">
-<insert id="upsert" parameterType="com.moka.gdtauto.entity.RepDailyAdgroupReport">
-        INSERT INTO rep_daily_adgroup_report (
+    <insert id="upsert" parameterType="com.moka.gdtauto.entity.RepDailyAdgroupReport">
+        INSERT INTO rep_daily_adgroup_report (conf_id,
             group_id, group_name, account_id, adgroup_id, adgroup_name, date,
             view_count, view_user_count, avg_view_per_user,
             valid_click_count, click_user_count, cpc, ctr,
@@ -67,7 +67,7 @@
             income_val_7, income_roi_7, income_val_14, income_roi_14,
             income_val_24h, income_roi_1_24h
         ) VALUES (
-            #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.adgroupId}, #{report.adgroupName}, #{report.date},
+            #{report.confId}, #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.adgroupId}, #{report.adgroupName}, #{report.date},
             #{report.viewCount}, #{report.viewUserCount}, #{report.avgViewPerUser},
             #{report.validClickCount}, #{report.clickUserCount}, #{report.cpc}, #{report.ctr},
             #{report.valuableClickCount}, #{report.valuableClickCost}, #{report.valuableClickRate},
@@ -131,6 +131,204 @@
             #{report.incomeVal7}, #{report.incomeRoi7}, #{report.incomeVal14}, #{report.incomeRoi14},
             #{report.incomeVal24h}, #{report.incomeRoi124h}
         ) ON DUPLICATE KEY UPDATE
+            view_count = VALUES(view_count), view_user_count = VALUES(view_user_count), avg_view_per_user = VALUES(avg_view_per_user),
+            valid_click_count = VALUES(valid_click_count), click_user_count = VALUES(click_user_count), cpc = VALUES(cpc), ctr = VALUES(ctr),
+            valuable_click_count = VALUES(valuable_click_count), valuable_click_cost = VALUES(valuable_click_cost), valuable_click_rate = VALUES(valuable_click_rate),
+            cost = VALUES(cost), acquisition_cost = VALUES(acquisition_cost), thousand_display_price = VALUES(thousand_display_price),
+            conversions_count = VALUES(conversions_count), conversions_rate = VALUES(conversions_rate), conversions_cost = VALUES(conversions_cost),
+            deep_conversions_count = VALUES(deep_conversions_count), deep_conversions_rate = VALUES(deep_conversions_rate), deep_conversions_cost = VALUES(deep_conversions_cost),
+            conversions_by_display_count = VALUES(conversions_by_display_count), conversions_by_display_rate = VALUES(conversions_by_display_rate), conversions_by_display_cost = VALUES(conversions_by_display_cost),
+            conversions_by_click_count = VALUES(conversions_by_click_count), conversions_by_click_rate = VALUES(conversions_by_click_rate), conversions_by_click_cost = VALUES(conversions_by_click_cost),
+            preview_conversions_count = VALUES(preview_conversions_count), preview_deep_conversions_count = VALUES(preview_deep_conversions_count),
+            video_outer_play_count = VALUES(video_outer_play_count), video_outer_play_user_count = VALUES(video_outer_play_user_count), avg_user_play_count = VALUES(avg_user_play_count),
+            video_outer_play_time_count = VALUES(video_outer_play_time_count), video_outer_play_time_avg_rate = VALUES(video_outer_play_time_avg_rate),
+            video_outer_play_rate = VALUES(video_outer_play_rate), video_outer_play_cost = VALUES(video_outer_play_cost),
+            video_outer_play10_count = VALUES(video_outer_play10_count), video_outer_play25_count = VALUES(video_outer_play25_count), video_outer_play50_count = VALUES(video_outer_play50_count),
+            video_outer_play75_count = VALUES(video_outer_play75_count), video_outer_play90_count = VALUES(video_outer_play90_count), video_outer_play95_count = VALUES(video_outer_play95_count),
+            video_outer_play100_count = VALUES(video_outer_play100_count), video_outer_play3s_count = VALUES(video_outer_play3s_count), video_outer_play3s_rate = VALUES(video_outer_play3s_rate),
+            video_outer_play5s_count = VALUES(video_outer_play5s_count), video_outer_play7s_count = VALUES(video_outer_play7s_count),
+            read_count = VALUES(read_count), read_cost = VALUES(read_cost), comment_count = VALUES(comment_count), comment_cost = VALUES(comment_cost),
+            praise_count = VALUES(praise_count), praise_cost = VALUES(praise_cost), forward_count = VALUES(forward_count), forward_cost = VALUES(forward_cost), no_interest_count = VALUES(no_interest_count),
+            live_stream_crt_click_cnt = VALUES(live_stream_crt_click_cnt), click_image_count = VALUES(click_image_count), click_head_count = VALUES(click_head_count),
+            click_detail_count = VALUES(click_detail_count), click_poi_count = VALUES(click_poi_count), zone_header_click_count = VALUES(zone_header_click_count),
+            basic_info_client_count = VALUES(basic_info_client_count), account_info_click_count = VALUES(account_info_click_count),
+            clk_account_living_status_pv = VALUES(clk_account_living_status_pv), clk_accountinfo_weapp_pv = VALUES(clk_accountinfo_weapp_pv),
+            clk_accountinfo_finder_pv = VALUES(clk_accountinfo_finder_pv), clk_accountinfo_biz_pv = VALUES(clk_accountinfo_biz_pv),
+            clk_account_info_producttab_pv = VALUES(clk_account_info_producttab_pv), clk_account_info_productdetail_pv = VALUES(clk_account_info_productdetail_pv),
+            activity_info_click_count = VALUES(activity_info_click_count),
+            overall_leads_purchase_count = VALUES(overall_leads_purchase_count), effective_leads_count = VALUES(effective_leads_count), effective_cost = VALUES(effective_cost),
+            effect_leads_purchase_count = VALUES(effect_leads_purchase_count), effect_leads_purchase_cost = VALUES(effect_leads_purchase_cost),
+            platform_page_view_count = VALUES(platform_page_view_count), platform_page_view_rate = VALUES(platform_page_view_rate),
+            lan_button_click_count = VALUES(lan_button_click_count), lan_jump_button_clickers = VALUES(lan_jump_button_clickers), lan_button_click_cost = VALUES(lan_button_click_cost),
+            lan_jump_button_ctr = VALUES(lan_jump_button_ctr), lan_jump_button_click_cost = VALUES(lan_jump_button_click_cost),
+            key_page_view_count = VALUES(key_page_view_count), key_page_view_by_display_count = VALUES(key_page_view_by_display_count), key_page_view_by_click_count = VALUES(key_page_view_by_click_count),
+            key_page_uv = VALUES(key_page_uv), key_page_view_cost = VALUES(key_page_view_cost), key_page_view_rate = VALUES(key_page_view_rate),
+            landing_commodity_detail_exp_pv = VALUES(landing_commodity_detail_exp_pv),
+            app_commodity_page_view_by_display_count = VALUES(app_commodity_page_view_by_display_count), app_commodity_page_view_by_click_count = VALUES(app_commodity_page_view_by_click_count),
+            view_commodity_page_uv = VALUES(view_commodity_page_uv), web_commodity_page_view_rate = VALUES(web_commodity_page_view_rate), web_commodity_page_view_cost = VALUES(web_commodity_page_view_cost),
+            own_page_navigation_count = VALUES(own_page_navigation_count), own_page_navi_cost = VALUES(own_page_navi_cost),
+            platform_page_navigation_count = VALUES(platform_page_navigation_count), platform_page_navigation_cost = VALUES(platform_page_navigation_cost),
+            platform_shop_navigation_count = VALUES(platform_shop_navigation_count), platform_shop_navigation_cost = VALUES(platform_shop_navigation_cost),
+            active_page_views = VALUES(active_page_views), active_page_viewers = VALUES(active_page_viewers),
+            active_page_interaction_amount = VALUES(active_page_interaction_amount), active_page_interaction_users = VALUES(active_page_interaction_users),
+            guide_to_follow_page_views = VALUES(guide_to_follow_page_views), guide_to_follow_page_viewers = VALUES(guide_to_follow_page_viewers),
+            guide_to_follow_page_interaction_amount = VALUES(guide_to_follow_page_interaction_amount), guide_to_follow_page_interaction_users = VALUES(guide_to_follow_page_interaction_users),
+            platform_key_page_view_user_count = VALUES(platform_key_page_view_user_count),
+            page_consult_count = VALUES(page_consult_count), consult_uv_count = VALUES(consult_uv_count), page_consult_rate = VALUES(page_consult_rate), page_consult_cost = VALUES(page_consult_cost),
+            consult_leave_info_users = VALUES(consult_leave_info_users), consult_leave_info_cost = VALUES(consult_leave_info_cost),
+            potential_consult_count = VALUES(potential_consult_count), effective_consult_count = VALUES(effective_consult_count), tool_consult_count = VALUES(tool_consult_count),
+            page_reservation_count = VALUES(page_reservation_count), page_reservation_by_display_count = VALUES(page_reservation_by_display_count), page_reservation_by_click_count = VALUES(page_reservation_by_click_count),
+            reservation_uv = VALUES(reservation_uv), reservation_amount = VALUES(reservation_amount), page_reservation_cost = VALUES(page_reservation_cost), page_reservation_cost_with_people = VALUES(page_reservation_cost_with_people),
+            page_reservation_rate = VALUES(page_reservation_rate), page_reservation_roi = VALUES(page_reservation_roi),
+            biz_reservation_uv = VALUES(biz_reservation_uv), biz_reservation_follow_rate = VALUES(biz_reservation_follow_rate),
+            external_form_reservation_count = VALUES(external_form_reservation_count), potential_reserve_count = VALUES(potential_reserve_count),
+            reservation_check_uv = VALUES(reservation_check_uv), reservation_check_uv_cost = VALUES(reservation_check_uv_cost), reservation_check_uv_rate = VALUES(reservation_check_uv_rate),
+            effective_reserve_count = VALUES(effective_reserve_count), valid_leads_uv = VALUES(valid_leads_uv), try_out_intention_uv = VALUES(try_out_intention_uv), ineffective_leads_uv = VALUES(ineffective_leads_uv),
+            coupon_get_pv = VALUES(coupon_get_pv), coupon_get_cost = VALUES(coupon_get_cost), coupon_get_rate = VALUES(coupon_get_rate),
+            platform_coupon_click_count = VALUES(platform_coupon_click_count), purchase_amount_with_coupon = VALUES(purchase_amount_with_coupon),
+            coupon_purchase_rate = VALUES(coupon_purchase_rate), purchase_amount_with_coupon_cost = VALUES(purchase_amount_with_coupon_cost), coupon_usage_number = VALUES(coupon_usage_number),
+            page_phone_call_direct_count = VALUES(page_phone_call_direct_count), page_phone_call_direct_rate = VALUES(page_phone_call_direct_rate), page_phone_call_direct_cost = VALUES(page_phone_call_direct_cost),
+            potential_phone_count = VALUES(potential_phone_count), potential_customer_phone_uv = VALUES(potential_customer_phone_uv),
+            effective_phone_count = VALUES(effective_phone_count), valid_phone_uv = VALUES(valid_phone_uv),
+            income_val_1 = VALUES(income_val_1), income_roi_1 = VALUES(income_roi_1), income_val_3 = VALUES(income_val_3), income_roi_3 = VALUES(income_roi_3),
+            income_val_7 = VALUES(income_val_7), income_roi_7 = VALUES(income_roi_7), income_val_14 = VALUES(income_val_14), income_roi_14 = VALUES(income_roi_14),
+            income_val_24h = VALUES(income_val_24h), income_roi_1_24h = VALUES(income_roi_1_24h)
+    </insert>
+
+    <insert id="batchUpsert" parameterType="java.util.List">
+        INSERT INTO rep_daily_adgroup_report (
+            conf_id, group_id, group_name, account_id, adgroup_id, adgroup_name, date,
+            view_count, view_user_count, avg_view_per_user,
+            valid_click_count, click_user_count, cpc, ctr,
+            valuable_click_count, valuable_click_cost, valuable_click_rate,
+            cost, acquisition_cost, thousand_display_price,
+            conversions_count, conversions_rate, conversions_cost,
+            deep_conversions_count, deep_conversions_rate, deep_conversions_cost,
+            conversions_by_display_count, conversions_by_display_rate, conversions_by_display_cost,
+            conversions_by_click_count, conversions_by_click_rate, conversions_by_click_cost,
+            preview_conversions_count, preview_deep_conversions_count,
+            video_outer_play_count, video_outer_play_user_count, avg_user_play_count,
+            video_outer_play_time_count, video_outer_play_time_avg_rate,
+            video_outer_play_rate, video_outer_play_cost,
+            video_outer_play10_count, video_outer_play25_count, video_outer_play50_count,
+            video_outer_play75_count, video_outer_play90_count, video_outer_play95_count,
+            video_outer_play100_count, video_outer_play3s_count, video_outer_play3s_rate,
+            video_outer_play5s_count, video_outer_play7s_count,
+            read_count, read_cost, comment_count, comment_cost,
+            praise_count, praise_cost, forward_count, forward_cost, no_interest_count,
+            live_stream_crt_click_cnt, click_image_count, click_head_count,
+            click_detail_count, click_poi_count, zone_header_click_count,
+            basic_info_client_count, account_info_click_count,
+            clk_account_living_status_pv, clk_accountinfo_weapp_pv,
+            clk_accountinfo_finder_pv, clk_accountinfo_biz_pv,
+            clk_account_info_producttab_pv, clk_account_info_productdetail_pv,
+            activity_info_click_count,
+            overall_leads_purchase_count, effective_leads_count, effective_cost,
+            effect_leads_purchase_count, effect_leads_purchase_cost,
+            platform_page_view_count, platform_page_view_rate,
+            lan_button_click_count, lan_jump_button_clickers, lan_button_click_cost,
+            lan_jump_button_ctr, lan_jump_button_click_cost,
+            key_page_view_count, key_page_view_by_display_count, key_page_view_by_click_count,
+            key_page_uv, key_page_view_cost, key_page_view_rate,
+            landing_commodity_detail_exp_pv,
+            app_commodity_page_view_by_display_count, app_commodity_page_view_by_click_count,
+            view_commodity_page_uv, web_commodity_page_view_rate, web_commodity_page_view_cost,
+            own_page_navigation_count, own_page_navi_cost,
+            platform_page_navigation_count, platform_page_navigation_cost,
+            platform_shop_navigation_count, platform_shop_navigation_cost,
+            active_page_views, active_page_viewers,
+            active_page_interaction_amount, active_page_interaction_users,
+            guide_to_follow_page_views, guide_to_follow_page_viewers,
+            guide_to_follow_page_interaction_amount, guide_to_follow_page_interaction_users,
+            platform_key_page_view_user_count,
+            page_consult_count, consult_uv_count, page_consult_rate, page_consult_cost,
+            consult_leave_info_users, consult_leave_info_cost,
+            potential_consult_count, effective_consult_count, tool_consult_count,
+            page_reservation_count, page_reservation_by_display_count, page_reservation_by_click_count,
+            reservation_uv, reservation_amount, page_reservation_cost, page_reservation_cost_with_people,
+            page_reservation_rate, page_reservation_roi,
+            biz_reservation_uv, biz_reservation_follow_rate,
+            external_form_reservation_count, potential_reserve_count,
+            reservation_check_uv, reservation_check_uv_cost, reservation_check_uv_rate,
+            effective_reserve_count, valid_leads_uv, try_out_intention_uv, ineffective_leads_uv,
+            coupon_get_pv, coupon_get_cost, coupon_get_rate,
+            platform_coupon_click_count, purchase_amount_with_coupon,
+            coupon_purchase_rate, purchase_amount_with_coupon_cost, coupon_usage_number,
+            page_phone_call_direct_count, page_phone_call_direct_rate, page_phone_call_direct_cost,
+            potential_phone_count, potential_customer_phone_uv,
+            effective_phone_count, valid_phone_uv,
+            income_val_1, income_roi_1, income_val_3, income_roi_3,
+            income_val_7, income_roi_7, income_val_14, income_roi_14,
+            income_val_24h, income_roi_1_24h
+        ) VALUES
+        <foreach collection="list" item="report" separator=",">
+            (
+                #{report.confId}, #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.adgroupId}, #{report.adgroupName}, #{report.date},
+                #{report.viewCount}, #{report.viewUserCount}, #{report.avgViewPerUser},
+                #{report.validClickCount}, #{report.clickUserCount}, #{report.cpc}, #{report.ctr},
+                #{report.valuableClickCount}, #{report.valuableClickCost}, #{report.valuableClickRate},
+                #{report.cost}, #{report.acquisitionCost}, #{report.thousandDisplayPrice},
+                #{report.conversionsCount}, #{report.conversionsRate}, #{report.conversionsCost},
+                #{report.deepConversionsCount}, #{report.deepConversionsRate}, #{report.deepConversionsCost},
+                #{report.conversionsByDisplayCount}, #{report.conversionsByDisplayRate}, #{report.conversionsByDisplayCost},
+                #{report.conversionsByClickCount}, #{report.conversionsByClickRate}, #{report.conversionsByClickCost},
+                #{report.previewConversionsCount}, #{report.previewDeepConversionsCount},
+                #{report.videoOuterPlayCount}, #{report.videoOuterPlayUserCount}, #{report.avgUserPlayCount},
+                #{report.videoOuterPlayTimeCount}, #{report.videoOuterPlayTimeAvgRate},
+                #{report.videoOuterPlayRate}, #{report.videoOuterPlayCost},
+                #{report.videoOuterPlay10Count}, #{report.videoOuterPlay25Count}, #{report.videoOuterPlay50Count},
+                #{report.videoOuterPlay75Count}, #{report.videoOuterPlay90Count}, #{report.videoOuterPlay95Count},
+                #{report.videoOuterPlay100Count}, #{report.videoOuterPlay3sCount}, #{report.videoOuterPlay3sRate},
+                #{report.videoOuterPlay5sCount}, #{report.videoOuterPlay7sCount},
+                #{report.readCount}, #{report.readCost}, #{report.commentCount}, #{report.commentCost},
+                #{report.praiseCount}, #{report.praiseCost}, #{report.forwardCount}, #{report.forwardCost}, #{report.noInterestCount},
+                #{report.liveStreamCrtClickCnt}, #{report.clickImageCount}, #{report.clickHeadCount},
+                #{report.clickDetailCount}, #{report.clickPoiCount}, #{report.zoneHeaderClickCount},
+                #{report.basicInfoClientCount}, #{report.accountInfoClickCount},
+                #{report.clkAccountLivingStatusPv}, #{report.clkAccountinfoWeappPv},
+                #{report.clkAccountinfoFinderPv}, #{report.clkAccountinfoBizPv},
+                #{report.clkAccountInfoProducttabPv}, #{report.clkAccountInfoProductdetailPv},
+                #{report.activityInfoClickCount},
+                #{report.overallLeadsPurchaseCount}, #{report.effectiveLeadsCount}, #{report.effectiveCost},
+                #{report.effectLeadsPurchaseCount}, #{report.effectLeadsPurchaseCost},
+                #{report.platformPageViewCount}, #{report.platformPageViewRate},
+                #{report.lanButtonClickCount}, #{report.lanJumpButtonClickers}, #{report.lanButtonClickCost},
+                #{report.lanJumpButtonCtr}, #{report.lanJumpButtonClickCost},
+                #{report.keyPageViewCount}, #{report.keyPageViewByDisplayCount}, #{report.keyPageViewByClickCount},
+                #{report.keyPageUv}, #{report.keyPageViewCost}, #{report.keyPageViewRate},
+                #{report.landingCommodityDetailExpPv},
+                #{report.appCommodityPageViewByDisplayCount}, #{report.appCommodityPageViewByClickCount},
+                #{report.viewCommodityPageUv}, #{report.webCommodityPageViewRate}, #{report.webCommodityPageViewCost},
+                #{report.ownPageNavigationCount}, #{report.ownPageNaviCost},
+                #{report.platformPageNavigationCount}, #{report.platformPageNavigationCost},
+                #{report.platformShopNavigationCount}, #{report.platformShopNavigationCost},
+                #{report.activePageViews}, #{report.activePageViewers},
+                #{report.activePageInteractionAmount}, #{report.activePageInteractionUsers},
+                #{report.guideToFollowPageViews}, #{report.guideToFollowPageViewers},
+                #{report.guideToFollowPageInteractionAmount}, #{report.guideToFollowPageInteractionUsers},
+                #{report.platformKeyPageViewUserCount},
+                #{report.pageConsultCount}, #{report.consultUvCount}, #{report.pageConsultRate}, #{report.pageConsultCost},
+                #{report.consultLeaveInfoUsers}, #{report.consultLeaveInfoCost},
+                #{report.potentialConsultCount}, #{report.effectiveConsultCount}, #{report.toolConsultCount},
+                #{report.pageReservationCount}, #{report.pageReservationByDisplayCount}, #{report.pageReservationByClickCount},
+                #{report.reservationUv}, #{report.reservationAmount}, #{report.pageReservationCost}, #{report.pageReservationCostWithPeople},
+                #{report.pageReservationRate}, #{report.pageReservationRoi},
+                #{report.bizReservationUv}, #{report.bizReservationFollowRate},
+                #{report.externalFormReservationCount}, #{report.potentialReserveCount},
+                #{report.reservationCheckUv}, #{report.reservationCheckUvCost}, #{report.reservationCheckUvRate},
+                #{report.effectiveReserveCount}, #{report.validLeadsUv}, #{report.tryOutIntentionUv}, #{report.ineffectiveLeadsUv},
+                #{report.couponGetPv}, #{report.couponGetCost}, #{report.couponGetRate},
+                #{report.platformCouponClickCount}, #{report.purchaseAmountWithCoupon},
+                #{report.couponPurchaseRate}, #{report.purchaseAmountWithCouponCost}, #{report.couponUsageNumber},
+                #{report.pagePhoneCallDirectCount}, #{report.pagePhoneCallDirectRate}, #{report.pagePhoneCallDirectCost},
+                #{report.potentialPhoneCount}, #{report.potentialCustomerPhoneUv},
+                #{report.effectivePhoneCount}, #{report.validPhoneUv},
+                #{report.incomeVal1}, #{report.incomeRoi1}, #{report.incomeVal3}, #{report.incomeRoi3},
+                #{report.incomeVal7}, #{report.incomeRoi7}, #{report.incomeVal14}, #{report.incomeRoi14},
+                #{report.incomeVal24h}, #{report.incomeRoi124h}
+            )
+        </foreach>
+        ON DUPLICATE KEY UPDATE
             group_name = VALUES(group_name),
             view_count = VALUES(view_count), view_user_count = VALUES(view_user_count), avg_view_per_user = VALUES(avg_view_per_user),
             valid_click_count = VALUES(valid_click_count), click_user_count = VALUES(click_user_count), cpc = VALUES(cpc), ctr = VALUES(ctr),

+ 4 - 4
src/main/resources/mapper/RepDailyCreativeReportMapper.xml

@@ -5,7 +5,7 @@
     <!-- 插入或更新(REPORTING_TIME 口径)唯一键: group_id + account_id + dynamic_creative_id + date -->
     <insert id="upsert" parameterType="com.moka.gdtauto.entity.RepDailyCreativeReport">
         INSERT INTO rep_daily_creative_report (
-            group_id, group_name, account_id, dynamic_creative_id, dynamic_creative_name, date,
+            conf_id, group_id, group_name, account_id, dynamic_creative_id, dynamic_creative_name, date,
             view_count, view_user_count, avg_view_per_user,
             valid_click_count, click_user_count, cpc, ctr,
             valuable_click_count, valuable_click_cost, valuable_click_rate,
@@ -69,7 +69,7 @@
             income_val_7, income_roi_7, income_val_14, income_roi_14,
             income_val_24h, income_roi_1_24h
         ) VALUES (
-            #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.dynamicCreativeId}, #{report.dynamicCreativeName}, #{report.date},
+            #{report.confId}, #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.dynamicCreativeId}, #{report.dynamicCreativeName}, #{report.date},
             #{report.viewCount}, #{report.viewUserCount}, #{report.avgViewPerUser},
             #{report.validClickCount}, #{report.clickUserCount}, #{report.cpc}, #{report.ctr},
             #{report.valuableClickCount}, #{report.valuableClickCost}, #{report.valuableClickRate},
@@ -201,7 +201,7 @@
     <!-- 批量插入或更新(REPORTING_TIME 口径)-->
     <insert id="batchUpsert" parameterType="java.util.List">
         INSERT INTO rep_daily_creative_report (
-            group_id, group_name, account_id, dynamic_creative_id, dynamic_creative_name, date,
+            conf_id, group_id, group_name, account_id, dynamic_creative_id, dynamic_creative_name, date,
             view_count, view_user_count, avg_view_per_user,
             valid_click_count, click_user_count, cpc, ctr,
             valuable_click_count, valuable_click_cost, valuable_click_rate,
@@ -267,7 +267,7 @@
         ) VALUES
         <foreach collection="list" item="item" separator=",">
             (
-                #{item.groupId}, #{item.groupName}, #{item.accountId}, #{item.dynamicCreativeId}, #{item.dynamicCreativeName}, #{item.date},
+                #{item.confId}, #{item.groupId}, #{item.groupName}, #{item.accountId}, #{item.dynamicCreativeId}, #{item.dynamicCreativeName}, #{item.date},
                 #{item.viewCount}, #{item.viewUserCount}, #{item.avgViewPerUser},
                 #{item.validClickCount}, #{item.clickUserCount}, #{item.cpc}, #{item.ctr},
                 #{item.valuableClickCount}, #{item.valuableClickCost}, #{item.valuableClickRate},

+ 202 - 3
src/main/resources/mapper/ReqDailyAdgroupReportMapper.xml

@@ -1,9 +1,9 @@
 <?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="com.moka.gdtauto.mapper.ReqDailyAdgroupReportMapper">
-<insert id="upsert" parameterType="com.moka.gdtauto.entity.ReqDailyAdgroupReport">
+    <insert id="upsert" parameterType="com.moka.gdtauto.entity.ReqDailyAdgroupReport">
         INSERT INTO req_daily_adgroup_report (
-            group_id, group_name, account_id, adgroup_id, adgroup_name, date,
+            conf_id, group_id, group_name, account_id, adgroup_id, adgroup_name, date,
             view_count, view_user_count, avg_view_per_user,
             valid_click_count, click_user_count, cpc, ctr,
             valuable_click_count, valuable_click_cost, valuable_click_rate,
@@ -67,7 +67,7 @@
             income_val_7, income_roi_7, income_val_14, income_roi_14,
             income_val_24h, income_roi_1_24h
         ) VALUES (
-            #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.adgroupId}, #{report.adgroupName}, #{report.date},
+            #{report.confId}, #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.adgroupId}, #{report.adgroupName}, #{report.date},
             #{report.viewCount}, #{report.viewUserCount}, #{report.avgViewPerUser},
             #{report.validClickCount}, #{report.clickUserCount}, #{report.cpc}, #{report.ctr},
             #{report.valuableClickCount}, #{report.valuableClickCost}, #{report.valuableClickRate},
@@ -195,4 +195,203 @@
             income_val_7 = VALUES(income_val_7), income_roi_7 = VALUES(income_roi_7), income_val_14 = VALUES(income_val_14), income_roi_14 = VALUES(income_roi_14),
             income_val_24h = VALUES(income_val_24h), income_roi_1_24h = VALUES(income_roi_1_24h)
     </insert>
+
+    <insert id="batchUpsert" parameterType="java.util.List">
+        INSERT INTO req_daily_adgroup_report (
+            conf_id, group_id, group_name, account_id, adgroup_id, adgroup_name, date,
+            view_count, view_user_count, avg_view_per_user,
+            valid_click_count, click_user_count, cpc, ctr,
+            valuable_click_count, valuable_click_cost, valuable_click_rate,
+            cost, acquisition_cost, thousand_display_price,
+            conversions_count, conversions_rate, conversions_cost,
+            deep_conversions_count, deep_conversions_rate, deep_conversions_cost,
+            conversions_by_display_count, conversions_by_display_rate, conversions_by_display_cost,
+            conversions_by_click_count, conversions_by_click_rate, conversions_by_click_cost,
+            preview_conversions_count, preview_deep_conversions_count,
+            video_outer_play_count, video_outer_play_user_count, avg_user_play_count,
+            video_outer_play_time_count, video_outer_play_time_avg_rate,
+            video_outer_play_rate, video_outer_play_cost,
+            video_outer_play10_count, video_outer_play25_count, video_outer_play50_count,
+            video_outer_play75_count, video_outer_play90_count, video_outer_play95_count,
+            video_outer_play100_count, video_outer_play3s_count, video_outer_play3s_rate,
+            video_outer_play5s_count, video_outer_play7s_count,
+            read_count, read_cost, comment_count, comment_cost,
+            praise_count, praise_cost, forward_count, forward_cost, no_interest_count,
+            live_stream_crt_click_cnt, click_image_count, click_head_count,
+            click_detail_count, click_poi_count, zone_header_click_count,
+            basic_info_client_count, account_info_click_count,
+            clk_account_living_status_pv, clk_accountinfo_weapp_pv,
+            clk_accountinfo_finder_pv, clk_accountinfo_biz_pv,
+            clk_account_info_producttab_pv, clk_account_info_productdetail_pv,
+            activity_info_click_count,
+            overall_leads_purchase_count, effective_leads_count, effective_cost,
+            effect_leads_purchase_count, effect_leads_purchase_cost,
+            platform_page_view_count, platform_page_view_rate,
+            lan_button_click_count, lan_jump_button_clickers, lan_button_click_cost,
+            lan_jump_button_ctr, lan_jump_button_click_cost,
+            key_page_view_count, key_page_view_by_display_count, key_page_view_by_click_count,
+            key_page_uv, key_page_view_cost, key_page_view_rate,
+            landing_commodity_detail_exp_pv,
+            app_commodity_page_view_by_display_count, app_commodity_page_view_by_click_count,
+            view_commodity_page_uv, web_commodity_page_view_rate, web_commodity_page_view_cost,
+            own_page_navigation_count, own_page_navi_cost,
+            platform_page_navigation_count, platform_page_navigation_cost,
+            platform_shop_navigation_count, platform_shop_navigation_cost,
+            active_page_views, active_page_viewers,
+            active_page_interaction_amount, active_page_interaction_users,
+            guide_to_follow_page_views, guide_to_follow_page_viewers,
+            guide_to_follow_page_interaction_amount, guide_to_follow_page_interaction_users,
+            platform_key_page_view_user_count,
+            page_consult_count, consult_uv_count, page_consult_rate, page_consult_cost,
+            consult_leave_info_users, consult_leave_info_cost,
+            potential_consult_count, effective_consult_count, tool_consult_count,
+            page_reservation_count, page_reservation_by_display_count, page_reservation_by_click_count,
+            reservation_uv, reservation_amount, page_reservation_cost, page_reservation_cost_with_people,
+            page_reservation_rate, page_reservation_roi,
+            biz_reservation_uv, biz_reservation_follow_rate,
+            external_form_reservation_count, potential_reserve_count,
+            reservation_check_uv, reservation_check_uv_cost, reservation_check_uv_rate,
+            effective_reserve_count, valid_leads_uv, try_out_intention_uv, ineffective_leads_uv,
+            coupon_get_pv, coupon_get_cost, coupon_get_rate,
+            platform_coupon_click_count, purchase_amount_with_coupon,
+            coupon_purchase_rate, purchase_amount_with_coupon_cost, coupon_usage_number,
+            page_phone_call_direct_count, page_phone_call_direct_rate, page_phone_call_direct_cost,
+            potential_phone_count, potential_customer_phone_uv,
+            effective_phone_count, valid_phone_uv,
+            income_val_1, income_roi_1, income_val_3, income_roi_3,
+            income_val_7, income_roi_7, income_val_14, income_roi_14,
+            income_val_24h, income_roi_1_24h
+        ) VALUES
+        <foreach collection="list" item="report" separator=",">
+            (
+                #{report.confId}, #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.adgroupId}, #{report.adgroupName}, #{report.date},
+                #{report.viewCount}, #{report.viewUserCount}, #{report.avgViewPerUser},
+                #{report.validClickCount}, #{report.clickUserCount}, #{report.cpc}, #{report.ctr},
+                #{report.valuableClickCount}, #{report.valuableClickCost}, #{report.valuableClickRate},
+                #{report.cost}, #{report.acquisitionCost}, #{report.thousandDisplayPrice},
+                #{report.conversionsCount}, #{report.conversionsRate}, #{report.conversionsCost},
+                #{report.deepConversionsCount}, #{report.deepConversionsRate}, #{report.deepConversionsCost},
+                #{report.conversionsByDisplayCount}, #{report.conversionsByDisplayRate}, #{report.conversionsByDisplayCost},
+                #{report.conversionsByClickCount}, #{report.conversionsByClickRate}, #{report.conversionsByClickCost},
+                #{report.previewConversionsCount}, #{report.previewDeepConversionsCount},
+                #{report.videoOuterPlayCount}, #{report.videoOuterPlayUserCount}, #{report.avgUserPlayCount},
+                #{report.videoOuterPlayTimeCount}, #{report.videoOuterPlayTimeAvgRate},
+                #{report.videoOuterPlayRate}, #{report.videoOuterPlayCost},
+                #{report.videoOuterPlay10Count}, #{report.videoOuterPlay25Count}, #{report.videoOuterPlay50Count},
+                #{report.videoOuterPlay75Count}, #{report.videoOuterPlay90Count}, #{report.videoOuterPlay95Count},
+                #{report.videoOuterPlay100Count}, #{report.videoOuterPlay3sCount}, #{report.videoOuterPlay3sRate},
+                #{report.videoOuterPlay5sCount}, #{report.videoOuterPlay7sCount},
+                #{report.readCount}, #{report.readCost}, #{report.commentCount}, #{report.commentCost},
+                #{report.praiseCount}, #{report.praiseCost}, #{report.forwardCount}, #{report.forwardCost}, #{report.noInterestCount},
+                #{report.liveStreamCrtClickCnt}, #{report.clickImageCount}, #{report.clickHeadCount},
+                #{report.clickDetailCount}, #{report.clickPoiCount}, #{report.zoneHeaderClickCount},
+                #{report.basicInfoClientCount}, #{report.accountInfoClickCount},
+                #{report.clkAccountLivingStatusPv}, #{report.clkAccountinfoWeappPv},
+                #{report.clkAccountinfoFinderPv}, #{report.clkAccountinfoBizPv},
+                #{report.clkAccountInfoProducttabPv}, #{report.clkAccountInfoProductdetailPv},
+                #{report.activityInfoClickCount},
+                #{report.overallLeadsPurchaseCount}, #{report.effectiveLeadsCount}, #{report.effectiveCost},
+                #{report.effectLeadsPurchaseCount}, #{report.effectLeadsPurchaseCost},
+                #{report.platformPageViewCount}, #{report.platformPageViewRate},
+                #{report.lanButtonClickCount}, #{report.lanJumpButtonClickers}, #{report.lanButtonClickCost},
+                #{report.lanJumpButtonCtr}, #{report.lanJumpButtonClickCost},
+                #{report.keyPageViewCount}, #{report.keyPageViewByDisplayCount}, #{report.keyPageViewByClickCount},
+                #{report.keyPageUv}, #{report.keyPageViewCost}, #{report.keyPageViewRate},
+                #{report.landingCommodityDetailExpPv},
+                #{report.appCommodityPageViewByDisplayCount}, #{report.appCommodityPageViewByClickCount},
+                #{report.viewCommodityPageUv}, #{report.webCommodityPageViewRate}, #{report.webCommodityPageViewCost},
+                #{report.ownPageNavigationCount}, #{report.ownPageNaviCost},
+                #{report.platformPageNavigationCount}, #{report.platformPageNavigationCost},
+                #{report.platformShopNavigationCount}, #{report.platformShopNavigationCost},
+                #{report.activePageViews}, #{report.activePageViewers},
+                #{report.activePageInteractionAmount}, #{report.activePageInteractionUsers},
+                #{report.guideToFollowPageViews}, #{report.guideToFollowPageViewers},
+                #{report.guideToFollowPageInteractionAmount}, #{report.guideToFollowPageInteractionUsers},
+                #{report.platformKeyPageViewUserCount},
+                #{report.pageConsultCount}, #{report.consultUvCount}, #{report.pageConsultRate}, #{report.pageConsultCost},
+                #{report.consultLeaveInfoUsers}, #{report.consultLeaveInfoCost},
+                #{report.potentialConsultCount}, #{report.effectiveConsultCount}, #{report.toolConsultCount},
+                #{report.pageReservationCount}, #{report.pageReservationByDisplayCount}, #{report.pageReservationByClickCount},
+                #{report.reservationUv}, #{report.reservationAmount}, #{report.pageReservationCost}, #{report.pageReservationCostWithPeople},
+                #{report.pageReservationRate}, #{report.pageReservationRoi},
+                #{report.bizReservationUv}, #{report.bizReservationFollowRate},
+                #{report.externalFormReservationCount}, #{report.potentialReserveCount},
+                #{report.reservationCheckUv}, #{report.reservationCheckUvCost}, #{report.reservationCheckUvRate},
+                #{report.effectiveReserveCount}, #{report.validLeadsUv}, #{report.tryOutIntentionUv}, #{report.ineffectiveLeadsUv},
+                #{report.couponGetPv}, #{report.couponGetCost}, #{report.couponGetRate},
+                #{report.platformCouponClickCount}, #{report.purchaseAmountWithCoupon},
+                #{report.couponPurchaseRate}, #{report.purchaseAmountWithCouponCost}, #{report.couponUsageNumber},
+                #{report.pagePhoneCallDirectCount}, #{report.pagePhoneCallDirectRate}, #{report.pagePhoneCallDirectCost},
+                #{report.potentialPhoneCount}, #{report.potentialCustomerPhoneUv},
+                #{report.effectivePhoneCount}, #{report.validPhoneUv},
+                #{report.incomeVal1}, #{report.incomeRoi1}, #{report.incomeVal3}, #{report.incomeRoi3},
+                #{report.incomeVal7}, #{report.incomeRoi7}, #{report.incomeVal14}, #{report.incomeRoi14},
+                #{report.incomeVal24h}, #{report.incomeRoi124h}
+            )
+        </foreach>
+        ON DUPLICATE KEY UPDATE
+            group_name = VALUES(group_name),
+            view_count = VALUES(view_count), view_user_count = VALUES(view_user_count), avg_view_per_user = VALUES(avg_view_per_user),
+            valid_click_count = VALUES(valid_click_count), click_user_count = VALUES(click_user_count), cpc = VALUES(cpc), ctr = VALUES(ctr),
+            valuable_click_count = VALUES(valuable_click_count), valuable_click_cost = VALUES(valuable_click_cost), valuable_click_rate = VALUES(valuable_click_rate),
+            cost = VALUES(cost), acquisition_cost = VALUES(acquisition_cost), thousand_display_price = VALUES(thousand_display_price),
+            conversions_count = VALUES(conversions_count), conversions_rate = VALUES(conversions_rate), conversions_cost = VALUES(conversions_cost),
+            deep_conversions_count = VALUES(deep_conversions_count), deep_conversions_rate = VALUES(deep_conversions_rate), deep_conversions_cost = VALUES(deep_conversions_cost),
+            conversions_by_display_count = VALUES(conversions_by_display_count), conversions_by_display_rate = VALUES(conversions_by_display_rate), conversions_by_display_cost = VALUES(conversions_by_display_cost),
+            conversions_by_click_count = VALUES(conversions_by_click_count), conversions_by_click_rate = VALUES(conversions_by_click_rate), conversions_by_click_cost = VALUES(conversions_by_click_cost),
+            preview_conversions_count = VALUES(preview_conversions_count), preview_deep_conversions_count = VALUES(preview_deep_conversions_count),
+            video_outer_play_count = VALUES(video_outer_play_count), video_outer_play_user_count = VALUES(video_outer_play_user_count), avg_user_play_count = VALUES(avg_user_play_count),
+            video_outer_play_time_count = VALUES(video_outer_play_time_count), video_outer_play_time_avg_rate = VALUES(video_outer_play_time_avg_rate),
+            video_outer_play_rate = VALUES(video_outer_play_rate), video_outer_play_cost = VALUES(video_outer_play_cost),
+            video_outer_play10_count = VALUES(video_outer_play10_count), video_outer_play25_count = VALUES(video_outer_play25_count), video_outer_play50_count = VALUES(video_outer_play50_count),
+            video_outer_play75_count = VALUES(video_outer_play75_count), video_outer_play90_count = VALUES(video_outer_play90_count), video_outer_play95_count = VALUES(video_outer_play95_count),
+            video_outer_play100_count = VALUES(video_outer_play100_count), video_outer_play3s_count = VALUES(video_outer_play3s_count), video_outer_play3s_rate = VALUES(video_outer_play3s_rate),
+            video_outer_play5s_count = VALUES(video_outer_play5s_count), video_outer_play7s_count = VALUES(video_outer_play7s_count),
+            read_count = VALUES(read_count), read_cost = VALUES(read_cost), comment_count = VALUES(comment_count), comment_cost = VALUES(comment_cost),
+            praise_count = VALUES(praise_count), praise_cost = VALUES(praise_cost), forward_count = VALUES(forward_count), forward_cost = VALUES(forward_cost), no_interest_count = VALUES(no_interest_count),
+            live_stream_crt_click_cnt = VALUES(live_stream_crt_click_cnt), click_image_count = VALUES(click_image_count), click_head_count = VALUES(click_head_count),
+            click_detail_count = VALUES(click_detail_count), click_poi_count = VALUES(click_poi_count), zone_header_click_count = VALUES(zone_header_click_count),
+            basic_info_client_count = VALUES(basic_info_client_count), account_info_click_count = VALUES(account_info_click_count),
+            clk_account_living_status_pv = VALUES(clk_account_living_status_pv), clk_accountinfo_weapp_pv = VALUES(clk_accountinfo_weapp_pv),
+            clk_accountinfo_finder_pv = VALUES(clk_accountinfo_finder_pv), clk_accountinfo_biz_pv = VALUES(clk_accountinfo_biz_pv),
+            clk_account_info_producttab_pv = VALUES(clk_account_info_producttab_pv), clk_account_info_productdetail_pv = VALUES(clk_account_info_productdetail_pv),
+            activity_info_click_count = VALUES(activity_info_click_count),
+            overall_leads_purchase_count = VALUES(overall_leads_purchase_count), effective_leads_count = VALUES(effective_leads_count), effective_cost = VALUES(effective_cost),
+            effect_leads_purchase_count = VALUES(effect_leads_purchase_count), effect_leads_purchase_cost = VALUES(effect_leads_purchase_cost),
+            platform_page_view_count = VALUES(platform_page_view_count), platform_page_view_rate = VALUES(platform_page_view_rate),
+            lan_button_click_count = VALUES(lan_button_click_count), lan_jump_button_clickers = VALUES(lan_jump_button_clickers), lan_button_click_cost = VALUES(lan_button_click_cost),
+            lan_jump_button_ctr = VALUES(lan_jump_button_ctr), lan_jump_button_click_cost = VALUES(lan_jump_button_click_cost),
+            key_page_view_count = VALUES(key_page_view_count), key_page_view_by_display_count = VALUES(key_page_view_by_display_count), key_page_view_by_click_count = VALUES(key_page_view_by_click_count),
+            key_page_uv = VALUES(key_page_uv), key_page_view_cost = VALUES(key_page_view_cost), key_page_view_rate = VALUES(key_page_view_rate),
+            landing_commodity_detail_exp_pv = VALUES(landing_commodity_detail_exp_pv),
+            app_commodity_page_view_by_display_count = VALUES(app_commodity_page_view_by_display_count), app_commodity_page_view_by_click_count = VALUES(app_commodity_page_view_by_click_count),
+            view_commodity_page_uv = VALUES(view_commodity_page_uv), web_commodity_page_view_rate = VALUES(web_commodity_page_view_rate), web_commodity_page_view_cost = VALUES(web_commodity_page_view_cost),
+            own_page_navigation_count = VALUES(own_page_navigation_count), own_page_navi_cost = VALUES(own_page_navi_cost),
+            platform_page_navigation_count = VALUES(platform_page_navigation_count), platform_page_navigation_cost = VALUES(platform_page_navigation_cost),
+            platform_shop_navigation_count = VALUES(platform_shop_navigation_count), platform_shop_navigation_cost = VALUES(platform_shop_navigation_cost),
+            active_page_views = VALUES(active_page_views), active_page_viewers = VALUES(active_page_viewers),
+            active_page_interaction_amount = VALUES(active_page_interaction_amount), active_page_interaction_users = VALUES(active_page_interaction_users),
+            guide_to_follow_page_views = VALUES(guide_to_follow_page_views), guide_to_follow_page_viewers = VALUES(guide_to_follow_page_viewers),
+            guide_to_follow_page_interaction_amount = VALUES(guide_to_follow_page_interaction_amount), guide_to_follow_page_interaction_users = VALUES(guide_to_follow_page_interaction_users),
+            platform_key_page_view_user_count = VALUES(platform_key_page_view_user_count),
+            page_consult_count = VALUES(page_consult_count), consult_uv_count = VALUES(consult_uv_count), page_consult_rate = VALUES(page_consult_rate), page_consult_cost = VALUES(page_consult_cost),
+            consult_leave_info_users = VALUES(consult_leave_info_users), consult_leave_info_cost = VALUES(consult_leave_info_cost),
+            potential_consult_count = VALUES(potential_consult_count), effective_consult_count = VALUES(effective_consult_count), tool_consult_count = VALUES(tool_consult_count),
+            page_reservation_count = VALUES(page_reservation_count), page_reservation_by_display_count = VALUES(page_reservation_by_display_count), page_reservation_by_click_count = VALUES(page_reservation_by_click_count),
+            reservation_uv = VALUES(reservation_uv), reservation_amount = VALUES(reservation_amount), page_reservation_cost = VALUES(page_reservation_cost), page_reservation_cost_with_people = VALUES(page_reservation_cost_with_people),
+            page_reservation_rate = VALUES(page_reservation_rate), page_reservation_roi = VALUES(page_reservation_roi),
+            biz_reservation_uv = VALUES(biz_reservation_uv), biz_reservation_follow_rate = VALUES(biz_reservation_follow_rate),
+            external_form_reservation_count = VALUES(external_form_reservation_count), potential_reserve_count = VALUES(potential_reserve_count),
+            reservation_check_uv = VALUES(reservation_check_uv), reservation_check_uv_cost = VALUES(reservation_check_uv_cost), reservation_check_uv_rate = VALUES(reservation_check_uv_rate),
+            effective_reserve_count = VALUES(effective_reserve_count), valid_leads_uv = VALUES(valid_leads_uv), try_out_intention_uv = VALUES(try_out_intention_uv), ineffective_leads_uv = VALUES(ineffective_leads_uv),
+            coupon_get_pv = VALUES(coupon_get_pv), coupon_get_cost = VALUES(coupon_get_cost), coupon_get_rate = VALUES(coupon_get_rate),
+            platform_coupon_click_count = VALUES(platform_coupon_click_count), purchase_amount_with_coupon = VALUES(purchase_amount_with_coupon),
+            coupon_purchase_rate = VALUES(coupon_purchase_rate), purchase_amount_with_coupon_cost = VALUES(purchase_amount_with_coupon_cost), coupon_usage_number = VALUES(coupon_usage_number),
+            page_phone_call_direct_count = VALUES(page_phone_call_direct_count), page_phone_call_direct_rate = VALUES(page_phone_call_direct_rate), page_phone_call_direct_cost = VALUES(page_phone_call_direct_cost),
+            potential_phone_count = VALUES(potential_phone_count), potential_customer_phone_uv = VALUES(potential_customer_phone_uv),
+            effective_phone_count = VALUES(effective_phone_count), valid_phone_uv = VALUES(valid_phone_uv),
+            income_val_1 = VALUES(income_val_1), income_roi_1 = VALUES(income_roi_1), income_val_3 = VALUES(income_val_3), income_roi_3 = VALUES(income_roi_3),
+            income_val_7 = VALUES(income_val_7), income_roi_7 = VALUES(income_roi_7), income_val_14 = VALUES(income_val_14), income_roi_14 = VALUES(income_roi_14),
+            income_val_24h = VALUES(income_val_24h), income_roi_1_24h = VALUES(income_roi_1_24h)
+    </insert>
 </mapper>

+ 4 - 4
src/main/resources/mapper/ReqDailyCreativeReportMapper.xml

@@ -5,7 +5,7 @@
     <!-- 插入或更新(REQUEST_TIME 口径)唯一键: group_id + account_id + dynamic_creative_id + date -->
     <insert id="upsert" parameterType="com.moka.gdtauto.entity.ReqDailyCreativeReport">
         INSERT INTO req_daily_creative_report (
-            group_id, group_name, account_id, dynamic_creative_id, dynamic_creative_name, date,
+            conf_id, group_id, group_name, account_id, dynamic_creative_id, dynamic_creative_name, date,
             view_count, view_user_count, avg_view_per_user,
             valid_click_count, click_user_count, cpc, ctr,
             valuable_click_count, valuable_click_cost, valuable_click_rate,
@@ -69,7 +69,7 @@
             income_val_7, income_roi_7, income_val_14, income_roi_14,
             income_val_24h, income_roi_1_24h
         ) VALUES (
-            #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.dynamicCreativeId}, #{report.dynamicCreativeName}, #{report.date},
+            #{report.confId}, #{report.groupId}, #{report.groupName}, #{report.accountId}, #{report.dynamicCreativeId}, #{report.dynamicCreativeName}, #{report.date},
             #{report.viewCount}, #{report.viewUserCount}, #{report.avgViewPerUser},
             #{report.validClickCount}, #{report.clickUserCount}, #{report.cpc}, #{report.ctr},
             #{report.valuableClickCount}, #{report.valuableClickCost}, #{report.valuableClickRate},
@@ -201,7 +201,7 @@
     <!-- 批量插入或更新(REQUEST_TIME 口径)-->
     <insert id="batchUpsert" parameterType="java.util.List">
         INSERT INTO req_daily_creative_report (
-            group_id, group_name, account_id, dynamic_creative_id, dynamic_creative_name, date,
+            conf_id, group_id, group_name, account_id, dynamic_creative_id, dynamic_creative_name, date,
             view_count, view_user_count, avg_view_per_user,
             valid_click_count, click_user_count, cpc, ctr,
             valuable_click_count, valuable_click_cost, valuable_click_rate,
@@ -267,7 +267,7 @@
         ) VALUES
         <foreach collection="list" item="item" separator=",">
             (
-                #{item.groupId}, #{item.groupName}, #{item.accountId}, #{item.dynamicCreativeId}, #{item.dynamicCreativeName}, #{item.date},
+                #{item.confId}, #{item.groupId}, #{item.groupName}, #{item.accountId}, #{item.dynamicCreativeId}, #{item.dynamicCreativeName}, #{item.date},
                 #{item.viewCount}, #{item.viewUserCount}, #{item.avgViewPerUser},
                 #{item.validClickCount}, #{item.clickUserCount}, #{item.cpc}, #{item.ctr},
                 #{item.valuableClickCount}, #{item.valuableClickCost}, #{item.valuableClickRate},

+ 22 - 0
src/test/java/com/moka/gdtauto/service/AdAutoDeleteTest.java

@@ -0,0 +1,22 @@
+package com.moka.gdtauto.service;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import com.moka.gdtauto.GdtAutoApplication;
+import com.moka.gdtauto.entity.AdPlanConf;
+
+@SpringBootTest(classes = GdtAutoApplication.class)
+@ActiveProfiles("test")
+public class AdAutoDeleteTest {
+    @Autowired
+    private AdPlanConfService adPlanConfService;
+
+    @Test
+    public void testDelete() {
+        AdPlanConf adPlanConf = adPlanConfService.getById(2L);
+        adPlanConfService.gdtAdgroupPatrolDelete(adPlanConf);
+    }
+}

+ 30 - 1
src/test/java/com/moka/gdtauto/service/AdPlanDailyReportSyncTest.java

@@ -29,7 +29,8 @@ public class AdPlanDailyReportSyncTest {
     private AdPlanDailyReportService adPlanDailyReportService;
     @Autowired
     private DailyCreativeReportService dailyCreativeReportService;
-
+    @Autowired
+    private DailyAdgroupReportService dailyAdgroupReportService;
 
     @Test
     public void testSyncReqDailyReport() {
@@ -50,4 +51,32 @@ public class AdPlanDailyReportSyncTest {
         String today = LocalDate.now().format(FMT);
         dailyCreativeReportService.syncAllAccounts(62L, today, today, TimeLine.REQUEST_TIME.getValue());
     }
+
+    @Test
+    public void testSyncTenDaysDailyCreativeReport() {
+        DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        for (int i = 0; i < 10; i++) {
+            String day = LocalDate.now().plusDays(-i).format(FMT);
+            dailyCreativeReportService.syncAllAccounts(null, day, day, TimeLine.REQUEST_TIME.getValue());
+            dailyCreativeReportService.syncAllAccounts(null, day, day, TimeLine.REPORTING_TIME.getValue());
+        }
+    }
+
+    @Test
+    public void testSyncDailyAdgroupReport() {
+        DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        String today = LocalDate.now().plusDays(-1).format(FMT);
+        dailyAdgroupReportService.syncAllAccounts(62L, today, today, TimeLine.REQUEST_TIME.getValue());
+    }
+
+    // mvn test -Dtest=AdPlanDailyReportSyncTest#testSyncTenDaysDailyAdgroupReport -Dspring.profiles.active=test
+    @Test
+    public void testSyncTenDaysDailyAdgroupReport() {
+        DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        for (int i = 0; i < 10; i++) {
+            String day = LocalDate.now().plusDays(-i).format(FMT);
+            dailyAdgroupReportService.syncAllAccounts(62L, day, day, TimeLine.REQUEST_TIME.getValue());
+            dailyAdgroupReportService.syncAllAccounts(62L, day, day, TimeLine.REPORTING_TIME.getValue());
+        }
+    }
 }