Explorar o código

feat:同步创意状态

pudongliang hai 4 meses
pai
achega
bfa5f1da96
Modificáronse 38 ficheiros con 304 adicións e 109 borrados
  1. 16 1
      .qoder/rules/always_on_rule.md
  2. 0 26
      .qoder/skills/crud/SKILL.md
  3. 1 1
      portal/dist/assets/accountGroup-DZPl1Sb9.js
  4. 0 1
      portal/dist/assets/adPlanConf-B5r-MmWE.js
  5. 1 0
      portal/dist/assets/adPlanConf-Cs86z6TE.js
  6. 1 1
      portal/dist/assets/auth-DhUhwMJF.js
  7. 1 1
      portal/dist/assets/conversion-jcb25uV3.js
  8. 0 0
      portal/dist/assets/detail-CaQ4h5nP.js
  9. 0 0
      portal/dist/assets/detail-CuVAZPjh.js
  10. 1 1
      portal/dist/assets/drama-DnSrletO.js
  11. 0 0
      portal/dist/assets/index-64deIwwO.js
  12. 0 0
      portal/dist/assets/index-Ay2B_Lv4.js
  13. 0 0
      portal/dist/assets/index-BSvnLa81.js
  14. 0 0
      portal/dist/assets/index-ByLtP7z9.js
  15. 0 0
      portal/dist/assets/index-CTHKuMSf.js
  16. 0 0
      portal/dist/assets/index-CZZqFwED.js
  17. 0 0
      portal/dist/assets/index-Cv6BG-kk.js
  18. 0 0
      portal/dist/assets/index-DWw8aAr5.js
  19. 1 1
      portal/dist/assets/index-DmN0Z-jo.js
  20. 0 0
      portal/dist/assets/index-Do0lkJo6.js
  21. 0 0
      portal/dist/assets/index-E5T6yD4Q.js
  22. 0 0
      portal/dist/assets/index-nxR3Jfz7.js
  23. 1 1
      portal/dist/assets/ossFile-CN5A2MDC.js
  24. 0 0
      portal/dist/assets/request-CAZk9MYm.js
  25. 1 1
      portal/dist/assets/user-CEZbeuJT.js
  26. 1 1
      portal/dist/index.html
  27. 7 0
      portal/src/api/adPlanConf.js
  28. 37 14
      portal/src/views/ad-plan-conf/detail.vue
  29. 3 2
      src/main/java/com/moka/gdtauto/BaseCodeGenerator.java
  30. 14 0
      src/main/java/com/moka/gdtauto/controller/AdPlanConfController.java
  31. 4 53
      src/main/java/com/moka/gdtauto/controller/AdPlanCreativeController.java
  32. 6 0
      src/main/java/com/moka/gdtauto/mapper/AdPlanCreativeMapper.java
  33. 2 0
      src/main/java/com/moka/gdtauto/service/AdPlanCreativeService.java
  34. 142 0
      src/main/java/com/moka/gdtauto/service/impl/AdPlanCreativeServiceImpl.java
  35. 10 4
      src/main/java/com/moka/gdtauto/service/impl/AdPlanServiceImpl.java
  36. 11 0
      src/main/java/com/moka/gdtauto/task/SyncAdgroupSystemStatusTask.java
  37. 16 0
      src/main/resources/mapper/AdPlanCreativeMapper.xml
  38. 27 0
      src/test/java/com/moka/gdtauto/service/TaskTest.java

+ 16 - 1
.qoder/rules/always_on_rule.md

@@ -1,5 +1,20 @@
 ---
 trigger: always_on
 ---
+这个项目的主要作用是对接腾讯广告平台接口,创建广告计划,广告创意的批量化工具
+服务后端是个典型javaweb项目,springboot+mybatis-plus的项目, controller, service + service.impl , mapper, entity
+腾讯广告SDK可以参考 test下的 com.moka.gdtauto.service 的测试用例,可以先看看TencentAdsAdgroupCreateTest.java参考下
+前端代码直接放在了portal目录下,典型的vue3 vite搭建,依赖了element-plus,样式采用了tailwindcss
 在进行前后端接口对接的时候使用 package com.moka.gdtauto.common.Result 进行相应;前端判定业务逻辑成功使用success字段
-前端界面风格参考 portal/src/conversion/index.vue
+前端界面风格参考 portal/src/conversion/index.vue
+当我给你的事具体的方法上下文时,如果要进行更改,只改动该方法的行为,其他更改给我提醒,我确认后再更改
+如果创建新表的CRUD,请修改java.com.moka.gdtauto.BaseCodeGenerator的TABLE_NAMES后执行main方法
+
+CRUD代码参考
+## controller
+参考 com.moka.gdtauto.controller.OssFileController
+
+## 前端页面
+参考 portal/src/views/oss-file/index.vue
+api参考 portal/src/api/ossFile.js
+在router加入配置 portal/src/router/index.js

+ 0 - 26
.qoder/skills/crud/SKILL.md

@@ -1,26 +0,0 @@
----
-name: crud
-description: 根据提供的信息创建CRUD
----
-
-## entity
-参考 com.moka.gdtauto.entity.OssFile.java
-
-## mapper
-参考 com.moka.gdtauto.mapper.OssFileMapper
-
-## service
-参考 com.moka.gdtauto.service.OssFileService
-
-## service impl
-查考 com.moka.gdtauto.service.impl.OssFileServiceImpl
-
-## controller
-参考 com.moka.gdtauto.controller.OssFileController
-
-## 前端页面
-参考 portal/src/views/oss-file/index.vue
-
-api查考 portal/src/api/ossFile.js
-
-在router加入配置 portal/src/router/index.js

+ 1 - 1
portal/dist/assets/accountGroup-jRfNIFu1.js → portal/dist/assets/accountGroup-DZPl1Sb9.js

@@ -1 +1 @@
-import{r as u}from"./request-BfMmUC0S.js";function c(t){return u({url:"/api/account-group/create",method:"post",data:t})}function e(t){return u({url:"/api/account-group/update",method:"post",data:t})}function n(t){return u({url:`/api/account-group/${t}`,method:"delete"})}function a(t=1,o=10){return u({url:"/api/account-group/list",method:"get",params:{pageNum:t,pageSize:o}})}function p(){return u({url:"/api/account-group/all",method:"get"})}function i(t){return u({url:`/api/account-group/${t}/accounts`,method:"get"})}export{i as a,p as b,c,n as d,a as g,e as u};
+import{r as u}from"./request-CAZk9MYm.js";function c(t){return u({url:"/api/account-group/create",method:"post",data:t})}function e(t){return u({url:"/api/account-group/update",method:"post",data:t})}function n(t){return u({url:`/api/account-group/${t}`,method:"delete"})}function a(t=1,o=10){return u({url:"/api/account-group/list",method:"get",params:{pageNum:t,pageSize:o}})}function p(){return u({url:"/api/account-group/all",method:"get"})}function i(t){return u({url:`/api/account-group/${t}/accounts`,method:"get"})}export{i as a,p as b,c,n as d,a as g,e as u};

+ 0 - 1
portal/dist/assets/adPlanConf-B5r-MmWE.js

@@ -1 +0,0 @@
-import{r as a}from"./request-BfMmUC0S.js";function r(n){return a({url:"/api/ad-plan-conf/reCreate",method:"post",data:n})}function o(n){return a({url:"/api/ad-plan-conf/generate-plan",method:"post",data:n})}function l(n){return a({url:`/api/ad-plan-conf/reGeneratePlan/${n}`,method:"get"})}function d(n){return a({url:"/api/ad-plan-conf/create",method:"post",data:n})}function u(n){return a({url:"/api/ad-plan-conf/update",method:"post",data:n})}function p(n){return a({url:`/api/ad-plan-conf/${n}`,method:"delete"})}function i(n=1,e=10){return a({url:"/api/ad-plan-conf/list",method:"get",params:{pageNum:n,pageSize:e}})}function f(n){return a({url:`/api/ad-plan-conf/${n}`,method:"get"})}function c(n){return a({url:"/api/ad-plan/list",method:"get",params:n})}function s(n){return a({url:`/api/ad-plan-creative/plan/${n}`,method:"get"})}export{o as a,f as b,d as c,p as d,c as e,l as f,i as g,s as h,r,u};

+ 1 - 0
portal/dist/assets/adPlanConf-Cs86z6TE.js

@@ -0,0 +1 @@
+import{r as a}from"./request-CAZk9MYm.js";function r(n){return a({url:"/api/ad-plan-conf/reCreate",method:"post",data:n})}function o(n){return a({url:"/api/ad-plan-conf/generate-plan",method:"post",data:n})}function l(n){return a({url:`/api/ad-plan-conf/reGeneratePlan/${n}`,method:"get"})}function u(n){return a({url:`/api/ad-plan-conf/syncSystemStatus/${n}`,method:"get"})}function d(n){return a({url:"/api/ad-plan-conf/create",method:"post",data:n})}function p(n){return a({url:"/api/ad-plan-conf/update",method:"post",data:n})}function i(n){return a({url:`/api/ad-plan-conf/${n}`,method:"delete"})}function s(n=1,e=10){return a({url:"/api/ad-plan-conf/list",method:"get",params:{pageNum:n,pageSize:e}})}function f(n){return a({url:`/api/ad-plan-conf/${n}`,method:"get"})}function c(n){return a({url:"/api/ad-plan/list",method:"get",params:n})}function m(n){return a({url:`/api/ad-plan-creative/plan/${n}`,method:"get"})}export{o as a,f as b,d as c,i as d,c as e,l as f,s as g,m as h,r,u as s,p as u};

+ 1 - 1
portal/dist/assets/auth-La_Iuvmd.js → portal/dist/assets/auth-DhUhwMJF.js

@@ -1 +1 @@
-import{r as t}from"./request-BfMmUC0S.js";function i(){return t({url:"/api/gdt/auth/authorize-url",method:"get"})}function n(r){return t({url:"/api/gdt/auth/refresh",method:"post",params:r})}function u(){return t({url:"/api/gdt/auth/list",method:"get"})}function a(){return u()}function o(){return t({url:"/api/gdt/auth/expiring",method:"get"})}export{o as a,a as b,i as g,u as l,n as r};
+import{r as t}from"./request-CAZk9MYm.js";function i(){return t({url:"/api/gdt/auth/authorize-url",method:"get"})}function n(r){return t({url:"/api/gdt/auth/refresh",method:"post",params:r})}function u(){return t({url:"/api/gdt/auth/list",method:"get"})}function a(){return u()}function o(){return t({url:"/api/gdt/auth/expiring",method:"get"})}export{o as a,a as b,i as g,u as l,n as r};

+ 1 - 1
portal/dist/assets/conversion-DQVN5TvA.js → portal/dist/assets/conversion-jcb25uV3.js

@@ -1 +1 @@
-import{r as t}from"./request-BfMmUC0S.js";function e(n,o){return t({url:"/api/gdt/conversion/sync",method:"post",params:{orgAccountId:n,accountId:o}})}function s(n=1,o=20){return t({url:"/api/gdt/conversion/list",method:"get",params:{page:n,pageSize:o}})}function i(n){return t({url:"/api/gdt/conversion/detail",method:"get",params:{conversionId:n}})}function a(){return t({url:"/api/gdt/conversion/all",method:"get"})}export{s as a,i as b,a as g,e as s};
+import{r as t}from"./request-CAZk9MYm.js";function e(n,o){return t({url:"/api/gdt/conversion/sync",method:"post",params:{orgAccountId:n,accountId:o}})}function s(n=1,o=20){return t({url:"/api/gdt/conversion/list",method:"get",params:{page:n,pageSize:o}})}function i(n){return t({url:"/api/gdt/conversion/detail",method:"get",params:{conversionId:n}})}function a(){return t({url:"/api/gdt/conversion/all",method:"get"})}export{s as a,i as b,a as g,e as s};

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/detail-CaQ4h5nP.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/detail-CuVAZPjh.js


+ 1 - 1
portal/dist/assets/drama-D7F1zU-V.js → portal/dist/assets/drama-DnSrletO.js

@@ -1 +1 @@
-import{r as t}from"./request-BfMmUC0S.js";function n(a){return t({url:"/api/gdt/asset/drama/sync",method:"post",params:{accountId:a}})}function m(a=1,s=20,r=""){return t({url:"/api/gdt/asset/drama/list",method:"get",params:{dramaName:r,page:a,pageSize:s}})}function d(a){return t({url:"/api/gdt/asset/drama/myDramaAssets",method:"get",params:{dramaName:a}})}function o(){return t({url:"/api/gdt/asset/drama/sync/conf/myConf",method:"get"})}function u(a){return t({url:"/api/gdt/asset/drama/sync/conf/add",method:"post",data:a})}function i(a){return t({url:"/api/gdt/asset/drama/sync/conf/delete",method:"post",params:{id:a}})}export{o as a,u as b,i as d,m as g,d as m,n as s};
+import{r as t}from"./request-CAZk9MYm.js";function n(a){return t({url:"/api/gdt/asset/drama/sync",method:"post",params:{accountId:a}})}function m(a=1,s=20,r=""){return t({url:"/api/gdt/asset/drama/list",method:"get",params:{dramaName:r,page:a,pageSize:s}})}function d(a){return t({url:"/api/gdt/asset/drama/myDramaAssets",method:"get",params:{dramaName:a}})}function o(){return t({url:"/api/gdt/asset/drama/sync/conf/myConf",method:"get"})}function u(a){return t({url:"/api/gdt/asset/drama/sync/conf/add",method:"post",data:a})}function i(a){return t({url:"/api/gdt/asset/drama/sync/conf/delete",method:"post",params:{id:a}})}export{o as a,u as b,i as d,m as g,d as m,n as s};

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-64deIwwO.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-Ay2B_Lv4.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-BSvnLa81.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-ByLtP7z9.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-CTHKuMSf.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-CZZqFwED.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-Cv6BG-kk.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-DWw8aAr5.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
portal/dist/assets/index-DmN0Z-jo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-Do0lkJo6.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-E5T6yD4Q.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/index-nxR3Jfz7.js


+ 1 - 1
portal/dist/assets/ossFile-BOXHnQix.js → portal/dist/assets/ossFile-CN5A2MDC.js

@@ -1 +1 @@
-import{r as t}from"./request-BfMmUC0S.js";function i(e){return t({timeout:3e5,url:"/api/oss-file/upload",method:"post",data:e,headers:{"Content-Type":"multipart/form-data"}})}function o(e){return t({url:"/api/oss-file/list",method:"get",params:e})}function r(e){return t({url:`/api/oss-file/${e}`,method:"delete"})}function l(e){return t({url:"/api/oss-file/batch",method:"delete",params:{fileIds:e}})}export{l as b,r as d,o as g,i as u};
+import{r as t}from"./request-CAZk9MYm.js";function i(e){return t({timeout:3e5,url:"/api/oss-file/upload",method:"post",data:e,headers:{"Content-Type":"multipart/form-data"}})}function o(e){return t({url:"/api/oss-file/list",method:"get",params:e})}function r(e){return t({url:`/api/oss-file/${e}`,method:"delete"})}function l(e){return t({url:"/api/oss-file/batch",method:"delete",params:{fileIds:e}})}export{l as b,r as d,o as g,i as u};

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
portal/dist/assets/request-CAZk9MYm.js


+ 1 - 1
portal/dist/assets/user-CNCqCTQN.js → portal/dist/assets/user-CEZbeuJT.js

@@ -1 +1 @@
-import{r as e}from"./request-BfMmUC0S.js";function s(){return e({url:"/api/user/list",method:"get"})}function u(r){return e({url:"/api/user/info",method:"get",params:r})}function n(r){return e({url:"/api/user/user-token-url",method:"get",params:r})}function o(r){return e({url:"/api/user/login",method:"post",params:r})}function a(r){return e({url:"/api/user/change-password",method:"post",params:r})}export{u as a,s as b,a as c,n as g,o as l};
+import{r as e}from"./request-CAZk9MYm.js";function s(){return e({url:"/api/user/list",method:"get"})}function u(r){return e({url:"/api/user/info",method:"get",params:r})}function n(r){return e({url:"/api/user/user-token-url",method:"get",params:r})}function o(r){return e({url:"/api/user/login",method:"post",params:r})}function a(r){return e({url:"/api/user/change-password",method:"post",params:r})}export{u as a,s as b,a as c,n as g,o as l};

+ 1 - 1
portal/dist/index.html

@@ -5,7 +5,7 @@
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>腾讯广告自动化管理后台</title>
-    <script type="module" crossorigin src="/assets/index-D8nUic44.js"></script>
+    <script type="module" crossorigin src="/assets/index-DmN0Z-jo.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-BeI5MTU7.css">
   </head>
   <body>

+ 7 - 0
portal/src/api/adPlanConf.js

@@ -23,6 +23,13 @@ export function reGeneratePlan(confId) {
   })
 }
 
+export function syncSystemStatus(confId) {
+  return request({
+    url: `/api/ad-plan-conf/syncSystemStatus/${confId}`,
+    method: 'get'
+  })
+}
+
 
 /**
  * 创建广告计划配置

+ 37 - 14
portal/src/views/ad-plan-conf/detail.vue

@@ -17,7 +17,7 @@
         </h2>
         <el-button type="primary" @click="handleRefresh(confDetail.id)">
             <el-icon><Refresh /></el-icon>
-            刷新计划状态</el-button>
+            刷新状态</el-button>
       </div>
       
       <div v-if="confDetail">
@@ -159,9 +159,12 @@
           广告计划列表
         </h2>
         <div>
+           <el-button type="primary" @click="handleSystemRefresh(confDetail.id)">
+            <el-icon><Refresh /></el-icon>
+            同步平台状态</el-button>
            <el-button type="primary" @click="handleRefresh(confDetail.id)">
             <el-icon><Refresh /></el-icon>
-            刷新计划状态</el-button>
+            刷新状态</el-button>
         <el-button type="primary" @click="handleReGeneratePlan(confDetail.id)">
           <el-icon><Refresh /></el-icon>
           重新生成广告/创意</el-button>
@@ -221,9 +224,8 @@
                 border
                 size="small"
               >
-                <el-table-column prop="id" label="创意ID" width="100" />
-                <el-table-column prop="creativeName" label="创意名称" min-width="200" show-overflow-tooltip />
-                <el-table-column prop="creativeId" label="腾讯创意ID" width="150">
+                <el-table-column prop="creativeName" label="创意名称" min-width="100" show-overflow-tooltip />
+                <el-table-column prop="creativeId" label="腾讯创意ID" width="130">
                   <template #default="{ row: creative }">
                     <el-button
                       v-if="creative.creativeId"
@@ -247,7 +249,6 @@
                     <span v-else>-</span>
                   </template>
                 </el-table-column>
-                <el-table-column prop="imageId" label="图片ID" width="120" />
                 <el-table-column prop="creativeText" label="创意文案" min-width="150" show-overflow-tooltip />
                 <el-table-column prop="brandName" label="品牌名称" width="120" />
                 <el-table-column label="品牌形象图片" width="120">
@@ -263,10 +264,14 @@
                     <span v-else>-</span>
                   </template>
                 </el-table-column>
-                <el-table-column prop="brandImageId" label="品牌形象图片ID" width="120" />
                 <el-table-column prop="buttonText" label="按钮文案" width="120" />
-                <el-table-column prop="pageIds" label="落地页IDs" min-width="150" show-overflow-tooltip />
-                <el-table-column label="状态" width="100">
+                <el-table-column prop="approvalStatus" label="审核状态" width="120" 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="100">
                   <template #default="{ row: creative }">
                     <el-tag :type="getPlanStatusColor(creative.status)" size="small">
                       {{ getPlanStatusText(creative.status) }}
@@ -278,6 +283,7 @@
                     <span class="text-red-500">{{ creative.failReason || '-' }}</span>
                   </template>
                 </el-table-column>
+                 <el-table-column prop="pageIds" label="落地页IDs" min-width="150" show-overflow-tooltip />
                 <el-table-column label="创建时间" width="180">
                   <template #default="{ row: creative }">
                     {{ formatDate(creative.createTime) }}
@@ -309,7 +315,7 @@
         </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="状态" width="100">
+        <el-table-column label="提交状态" width="100">
           <template #default="{ row }">
             <el-tag :type="getPlanStatusColor(row.status)" size="small">
               {{ getPlanStatusText(row.status) }}
@@ -322,7 +328,7 @@
           </template>
         </el-table-column>
         <el-table-column label="系统状态" prop="systemStatus" width="100" show-overflow-tooltip />
-        <el-table-column label="系统删除" prop="systemDelete" width="100" >
+        <el-table-column label="系统删除" prop="systemDelete" width="85" >
           <template #default="{ row }">
             {{ row.systemDelete == 'true' ? '是' : '否'}}
           </template>
@@ -359,7 +365,8 @@ import {
   getAdPlanConfDetail, 
   getAdPlanList, 
   reGeneratePlan,
-  getAdPlanCreativesByPlanId 
+  getAdPlanCreativesByPlanId,
+  syncSystemStatus
 } from '@/api/adPlanConf'
 
 const route = useRoute()
@@ -516,7 +523,8 @@ const getPlanStatusText = (status) => {
     1: '待执行',
     2: '执行中',
     3: '失败',
-    4: '已提交'
+    4: '已提交',
+    5: '部分失败'
   }
   return map[status] || '未知'
 }
@@ -527,7 +535,8 @@ const getPlanStatusColor = (status) => {
     1: 'info',
     2: 'warning',
     3: 'danger',
-    4: 'success'
+    4: 'success',
+    5: 'warning'
   }
   return map[status] || ''
 }
@@ -552,6 +561,20 @@ const handleRefresh = async () => {
   await fetchAdPlans()
 }
 
+const handleSystemRefresh = async (confId) => {
+  try {
+    const res = await syncSystemStatus(confId)
+    if (res.success) {
+      handleRefresh()
+    } else {
+      ElMessage.error(res.msg || '刷新计划平台状态失败')
+    }
+  } catch (error) {
+    console.error('刷新计划平台状态失败:', error)
+    ElMessage.error('刷新计划平台状态失败')
+  }
+}
+
 // 初始化
 onMounted(() => {
   if (!queryParams.value.confId) {

+ 3 - 2
src/main/java/com/moka/gdtauto/BaseCodeGenerator.java

@@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.generator.config.OutputFile;
 import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
 
 public class BaseCodeGenerator {
+
+    public final static String TABLE_NAMES = "sys_user,oss_file";
     public static void main(String[] args) {
         String url = "jdbc:mysql://rm-uf65d6xsxrp743w3aio.mysql.rds.aliyuncs.com:3306/gdt_auto?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true";
         FastAutoGenerator.create(url, "gdt_auto", "QA_GDT_AUTO%$PDL")
@@ -25,8 +27,7 @@ public class BaseCodeGenerator {
                 .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper"))
         )
         .strategyConfig(builder -> builder
-                .addInclude("vn_admin_user")
-                .addTablePrefix("vn_")
+                .addInclude(TABLE_NAMES)
                 .entityBuilder().enableFileOverride()
                 .mapperBuilder().enableFileOverride()
                 .serviceBuilder().enableFileOverride()

+ 14 - 0
src/main/java/com/moka/gdtauto/controller/AdPlanConfController.java

@@ -19,6 +19,9 @@ import com.moka.gdtauto.mapper.AccountGroupAccountRelationshipMapper;
 import com.moka.gdtauto.mapper.TencentAssetDramaMapper;
 import com.moka.gdtauto.service.AccountGroupService;
 import com.moka.gdtauto.service.AdPlanConfService;
+import com.moka.gdtauto.service.AdPlanCreativeService;
+import com.moka.gdtauto.service.AdPlanService;
+
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -27,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import org.springframework.beans.BeanUtils;
 import org.springframework.web.bind.annotation.*;
@@ -49,6 +53,8 @@ import java.time.format.DateTimeFormatter;
 public class AdPlanConfController {
 
     private final AdPlanConfService adPlanConfService;
+    private final AdPlanService adPlanService;
+    private final AdPlanCreativeService adPlanCreativeService;
     private final AccountGroupAccountRelationshipMapper relationshipMapper;
     private final SysUserService sysUserService;
     private final AccountGroupService accountGroupService;
@@ -224,6 +230,14 @@ public class AdPlanConfController {
         return Result.success("重新生成广告计划执行中");
     }
 
+    @GetMapping("/syncSystemStatus/{id}")
+    public Result<Void> syncSystemStatus(@PathVariable Long id, @AuthUser SysUser sysUser) {
+        adPlanService.syncAdPlanStatus(id);
+        adPlanCreativeService.syncAdPlanCreativeStatus(id);
+        try {TimeUnit.SECONDS.sleep(2L);} catch (InterruptedException e) {}
+        return Result.success("同步系统状态执行中");
+    }
+
     /**
      * 创建广告计划配置
      */

+ 4 - 53
src/main/java/com/moka/gdtauto/controller/AdPlanCreativeController.java

@@ -1,6 +1,7 @@
 package com.moka.gdtauto.controller;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.moka.gdtauto.common.Constant;
 import com.moka.gdtauto.common.Result;
 import com.moka.gdtauto.entity.AdPlanCreative;
 import com.moka.gdtauto.service.AdPlanCreativeService;
@@ -29,59 +30,6 @@ public class AdPlanCreativeController {
     private final AdPlanCreativeService adPlanCreativeService;
 
     /**
-     * 创建广告计划创意
-     */
-    @Operation(summary = "创建广告计划创意", description = "创建新的广告计划创意")
-    @PostMapping("/create")
-    public Result<Long> createAdPlanCreative(@RequestBody AdPlanCreative adPlanCreative) {
-        try {
-            Long creativeId = adPlanCreativeService.createAdPlanCreative(adPlanCreative);
-            return Result.success(creativeId, "创建广告计划创意成功");
-        } catch (Exception e) {
-            log.error("创建广告计划创意失败", e);
-            return Result.fail("创建广告计划创意失败: " + e.getMessage());
-        }
-    }
-
-    /**
-     * 批量创建广告计划创意
-     */
-    @Operation(summary = "批量创建广告计划创意", description = "批量创建多个广告计划创意")
-    @PostMapping("/batch-create")
-    public Result<Void> batchCreateAdPlanCreatives(@RequestBody List<AdPlanCreative> adPlanCreatives) {
-        try {
-            boolean success = adPlanCreativeService.batchCreateAdPlanCreatives(adPlanCreatives);
-            if (success) {
-                return Result.success("批量创建广告计划创意成功");
-            } else {
-                return Result.fail("批量创建广告计划创意失败");
-            }
-        } catch (Exception e) {
-            log.error("批量创建广告计划创意失败", e);
-            return Result.fail("批量创建广告计划创意失败: " + e.getMessage());
-        }
-    }
-
-    /**
-     * 更新广告计划创意
-     */
-    @Operation(summary = "更新广告计划创意", description = "更新指定的广告计划创意")
-    @PostMapping("/update")
-    public Result<Void> updateAdPlanCreative(@RequestBody AdPlanCreative adPlanCreative) {
-        try {
-            boolean success = adPlanCreativeService.updateAdPlanCreative(adPlanCreative);
-            if (success) {
-                return Result.success("更新广告计划创意成功");
-            } else {
-                return Result.fail("广告计划创意不存在");
-            }
-        } catch (Exception e) {
-            log.error("更新广告计划创意失败", e);
-            return Result.fail("更新广告计划创意失败: " + e.getMessage());
-        }
-    }
-
-    /**
      * 删除广告计划创意
      */
     @Operation(summary = "删除广告计划创意", description = "删除指定的广告计划创意")
@@ -112,6 +60,9 @@ public class AdPlanCreativeController {
             @PathVariable Long planId) {
         try {
             List<AdPlanCreative> creatives = adPlanCreativeService.getCreativesByPlanId(planId);
+            creatives.forEach(c -> {
+                c.setApprovalStatus(Constant.getCreativeSetApprovalStatusValue(c.getApprovalStatus()));
+            });
             return Result.success(creatives, "查询成功");
         } catch (Exception e) {
             log.error("查询广告计划创意列表失败", e);

+ 6 - 0
src/main/java/com/moka/gdtauto/mapper/AdPlanCreativeMapper.java

@@ -2,7 +2,11 @@ package com.moka.gdtauto.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.moka.gdtauto.entity.AdPlanCreative;
+
+import java.util.List;
+
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 广告计划创意Mapper接口
@@ -12,4 +16,6 @@ import org.apache.ibatis.annotations.Mapper;
  */
 @Mapper
 public interface AdPlanCreativeMapper extends BaseMapper<AdPlanCreative> {
+
+    void batchUpdateApprovalStatus(@Param("creatives") List<AdPlanCreative> toUpdate);
 }

+ 2 - 0
src/main/java/com/moka/gdtauto/service/AdPlanCreativeService.java

@@ -74,4 +74,6 @@ public interface AdPlanCreativeService extends IService<AdPlanCreative> {
     AdPlanCreative getAdPlanCreativeById(Long creativeId);
 
     List<AdPlanCreative> getCreativesByConfId(Long confId);
+
+    void syncAdPlanCreativeStatus(Long confId);
 }

+ 142 - 0
src/main/java/com/moka/gdtauto/service/impl/AdPlanCreativeServiceImpl.java

@@ -3,15 +3,32 @@ package com.moka.gdtauto.service.impl;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.moka.gdtauto.client.TencentAdsApiClientFactory;
+import com.moka.gdtauto.common.Constant;
+import com.moka.gdtauto.entity.AdPlan;
 import com.moka.gdtauto.entity.AdPlanCreative;
 import com.moka.gdtauto.mapper.AdPlanCreativeMapper;
 import com.moka.gdtauto.service.AdPlanCreativeService;
+import com.moka.gdtauto.service.AdPlanService;
+import com.tencent.ads.model.v3.AdgroupsGetResponseData;
+import com.tencent.ads.model.v3.DynamicCreativesGetResponseData;
+import com.tencent.ads.model.v3.FilterOperator;
+import com.tencent.ads.model.v3.FilteringStruct;
+import com.tencent.ads.v3.TencentAds;
+
 import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
+import lombok.AllArgsConstructor;
+
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 广告计划创意服务实现类
@@ -21,8 +38,13 @@ import java.util.List;
  */
 @Slf4j
 @Service
+@AllArgsConstructor
 public class AdPlanCreativeServiceImpl extends ServiceImpl<AdPlanCreativeMapper, AdPlanCreative> implements AdPlanCreativeService {
 
+    private final AdPlanCreativeMapper adPlanCreativeMapper;
+    private final TencentAdsApiClientFactory clientFactory;
+    private final AdPlanService adPlanService;
+    
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createAdPlanCreative(AdPlanCreative adPlanCreative) {
@@ -139,4 +161,124 @@ public class AdPlanCreativeServiceImpl extends ServiceImpl<AdPlanCreativeMapper,
                 .eq(AdPlanCreative::getDeleted, 0)
                 .orderByAsc(AdPlanCreative::getId));
     }
+
+    @Async
+    @Override
+    public void syncAdPlanCreativeStatus(Long confId) {
+        final int PAGE_SIZE = 500;
+        final int BATCH_LIMIT = 100; // 腾讯广告 adgroup_id IN 最大 100
+
+        log.info("[定时任务] 开始同步广告创意 system_status");
+        int totalUpdated = 0;
+        try {
+            int pageNum = 1;
+            while (true) {
+                // 1. 分页查询 status=4 的广告创意,ID 降序
+                LambdaQueryWrapper<AdPlanCreative> queryWrapper = new LambdaQueryWrapper<>();
+                queryWrapper.eq(confId != null, AdPlanCreative::getAdPlanConfId, confId);
+                queryWrapper.eq(AdPlanCreative::getDeleted, 0);
+                queryWrapper.eq(AdPlanCreative::getStatus, 4);
+                queryWrapper.orderByDesc(AdPlanCreative::getId);
+                Page<AdPlanCreative> page = this.page(new Page<>(pageNum, PAGE_SIZE), queryWrapper);
+                List<AdPlanCreative> records = page.getRecords();
+                if (records == null || records.isEmpty()) {
+                    break;
+                }
+
+                // 2. 按 accountId 分组
+                Map<Long, List<AdPlanCreative>> groupByAccount = records.stream()
+                        .filter(p -> p.getAdgroupId() != null && p.getAccountId() != null)
+                        .collect(Collectors.groupingBy(AdPlanCreative::getAccountId));
+
+                for (Map.Entry<Long, List<AdPlanCreative>> entry : groupByAccount.entrySet()) {
+                    Long accountId = entry.getKey();
+                    List<AdPlanCreative> plansOfAccount = entry.getValue();
+
+                    // 3. 每组按 100 条分批调用 API
+                    int batchCount = (int) Math.ceil((double) plansOfAccount.size() / BATCH_LIMIT);
+                    for (int i = 0; i < batchCount; i++) {
+                        int fromIdx = i * BATCH_LIMIT;
+                        int toIdx = Math.min(fromIdx + BATCH_LIMIT, plansOfAccount.size());
+                        List<AdPlanCreative> batch = plansOfAccount.subList(fromIdx, toIdx);
+
+                        try {
+                            int updated = syncBatch(accountId, batch);
+                            totalUpdated += updated;
+                        } catch (Exception e) {
+                            log.error("[定时任务] 同步 accountId={} 的广告状态失败,batch index={}", accountId, i, e);
+                        }
+                    }
+                }
+
+                if (pageNum >= page.getPages()) {
+                    break;
+                }
+                pageNum++;
+            }
+
+        } catch (Exception e) {
+            log.error("[定时任务] 同步广告创意 system_status 发生异常", e);
+        }
+
+        log.info("[定时任务] 同步广告创意 system_status 完成,共更新 {} 条", totalUpdated);
+    
+    }
+
+    private int syncBatch(Long accountId, List<AdPlanCreative> batch) throws Exception {
+        final int BATCH_LIMIT = 100; // 腾讯广告 adgroup_id IN 最大 100
+        final List<String> FIELDS = List.of("adgroup_id", "dynamic_creative_id", "creative_set_approval_status", "is_deleted");
+
+        // 构建 adgroup_id IN [...] 过滤条件
+        List<String> creativeIdValues = batch.stream()
+                .map(p -> String.valueOf(p.getCreativeId()))
+                .collect(Collectors.toList());
+
+        FilteringStruct filter = new FilteringStruct();
+        filter.setField("dynamic_creative_id");
+        filter.setOperator(FilterOperator.IN);
+        filter.setValues(creativeIdValues);
+
+        List<FilteringStruct> filtering = new ArrayList<>();
+        filtering.add(filter);
+
+        // 获取 TencentAds 实例(使用主 orgAccountId)
+        TencentAds tencentAds = clientFactory.getTencentAds(null, Constant.MAIN_ORG_ACCOUNT_ID);
+
+        DynamicCreativesGetResponseData response = tencentAds.dynamicCreatives().dynamicCreativesGet(
+                accountId,
+                filtering,
+                1L, 
+                (long) BATCH_LIMIT,
+                FIELDS,
+                null,
+                "PAGINATION_MODE_NORMAL",
+                null ,
+                null                      
+        );
+
+        if (response == null || response.getList() == null || response.getList().isEmpty()) {
+            log.warn("[定时任务] accountId={} 未返回广告状态数据", accountId);
+            return 0;
+        }
+
+        // 构建待更新列表
+        List<AdPlanCreative> toUpdate = new ArrayList<>();
+        response.getList().stream().forEach(item -> {
+            AdPlanCreative update = new AdPlanCreative();
+            update.setCreativeId(item.getDynamicCreativeId());
+            update.setApprovalStatus(item.getCreativeSetApprovalStatus().getValue());
+            update.setSystemDelete(item.isIsDeleted() != null ? String.valueOf(item.isIsDeleted()) : null);
+            toUpdate.add(update);
+        });
+
+        if (toUpdate.isEmpty()) {
+            return 0;
+        }
+
+        adPlanCreativeMapper.batchUpdateApprovalStatus(toUpdate);
+        log.info("[定时任务] accountId={} 批量更新 {} 条 approval_status", accountId, toUpdate.size());
+        return toUpdate.size();
+    }
+
+
 }

+ 10 - 4
src/main/java/com/moka/gdtauto/service/impl/AdPlanServiceImpl.java

@@ -15,6 +15,8 @@ import com.tencent.ads.v3.TencentAds;
 
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -126,12 +128,13 @@ public class AdPlanServiceImpl extends ServiceImpl<AdPlanMapper, AdPlan> impleme
 
 
 
-    private static final int PAGE_SIZE = 500;
-    private static final int BATCH_LIMIT = 100; // 腾讯广告 adgroup_id IN 最大 100
-    private static final List<String> FIELDS = List.of("adgroup_id", "system_status", "is_deleted");
-
+ 
+    @Async
     @Override
     public void syncAdPlanStatus(Long confId) {
+        final int PAGE_SIZE = 500;
+        final int BATCH_LIMIT = 100; // 腾讯广告 adgroup_id IN 最大 100
+
         log.info("[定时任务] 开始同步广告计划 system_status");
         int totalUpdated = 0;
         try {
@@ -195,6 +198,9 @@ public class AdPlanServiceImpl extends ServiceImpl<AdPlanMapper, AdPlan> impleme
      * @return 实际更新条数
      */
     private int syncBatch(Long accountId, List<AdPlan> batch) throws Exception {
+        final int BATCH_LIMIT = 100; // 腾讯广告 adgroup_id IN 最大 100
+        final List<String> FIELDS = List.of("adgroup_id", "system_status", "is_deleted");
+
         // 构建 adgroup_id IN [...] 过滤条件
         List<String> adgroupIdValues = batch.stream()
                 .map(p -> String.valueOf(p.getAdgroupId()))

+ 11 - 0
src/main/java/com/moka/gdtauto/task/SyncAdgroupSystemStatusTask.java

@@ -5,6 +5,7 @@ import com.moka.gdtauto.common.Constant;
 import com.moka.gdtauto.client.TencentAdsApiClientFactory;
 import com.moka.gdtauto.entity.AdPlan;
 import com.moka.gdtauto.mapper.AdPlanMapper;
+import com.moka.gdtauto.service.AdPlanCreativeService;
 import com.moka.gdtauto.service.AdPlanService;
 import com.tencent.ads.model.v3.AdgroupsGetListStruct;
 import com.tencent.ads.model.v3.AdgroupsGetResponseData;
@@ -41,6 +42,7 @@ import java.util.stream.Collectors;
 public class SyncAdgroupSystemStatusTask {
 
     private final AdPlanService adPlanService;
+    private final AdPlanCreativeService adPlanCreativeService;
     /**
      * 每小时同步一次广告状态
      */
@@ -50,4 +52,13 @@ public class SyncAdgroupSystemStatusTask {
         adPlanService.syncAdPlanStatus(11L);
     }
 
+    /**
+     * 同步广告计划创意 approval_status 定时任务
+     */
+    @Scheduled(cron = "0 0 * * * *")
+    @SchedulerLock(name = "syncAdgroupCreativeStatus", lockAtMostFor = "9m", lockAtLeastFor = "1m")
+    public void syncAdgroupCreativeStatus() {
+        adPlanCreativeService.syncAdPlanCreativeStatus(11L);
+    }
+
 }

+ 16 - 0
src/main/resources/mapper/AdPlanCreativeMapper.xml

@@ -0,0 +1,16 @@
+<?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.AdPlanCreativeMapper">
+
+    <!-- 批量更新广告计划 system_status,依赖 allowMultiQueries=true -->
+    <update id="batchUpdateApprovalStatus" parameterType="java.util.List">
+        <foreach collection="creatives" item="item" separator=";">
+            UPDATE ad_plan_creative
+            SET approval_status = #{item.approvalStatus},
+                system_delete = #{item.systemDelete},
+                update_time   = NOW()
+            WHERE creative_id = #{item.creativeId}
+        </foreach>
+    </update>
+
+</mapper>

+ 27 - 0
src/test/java/com/moka/gdtauto/service/TaskTest.java

@@ -0,0 +1,27 @@
+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;
+
+/**
+ * @author moka
+ * @date 2026/3/6
+ * mvn test -Dtest=TaskTest#testSyncAdgroupCreativeStatus -Dspring.profiles.active=test
+ */
+@SpringBootTest(classes = GdtAutoApplication.class)
+@ActiveProfiles("test")
+public class TaskTest {
+
+    @Autowired
+    private AdPlanCreativeService adPlanCreativeService;
+
+    @Test
+    public void testSyncAdgroupCreativeStatus() {
+        adPlanCreativeService.syncAdPlanCreativeStatus(11L);
+    }
+    
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio