pudongliang 4 месяцев назад
Родитель
Сommit
22e9cc3109
35 измененных файлов с 453 добавлено и 12 удалено
  1. 1 1
      portal/dist/assets/accountGroup-CXtnd01s.js
  2. 1 1
      portal/dist/assets/adAutoConf-Bpg3pRT4.js
  3. 1 1
      portal/dist/assets/adPlanConf-Cleib550.js
  4. 1 1
      portal/dist/assets/auth-DAcME6eg.js
  5. 1 1
      portal/dist/assets/conversion-7rIK4RQ3.js
  6. 0 0
      portal/dist/assets/detail-BfNRq6Be.js
  7. 0 0
      portal/dist/assets/detail-yG7f6VbD.js
  8. 1 1
      portal/dist/assets/drama-CpOh2ANv.js
  9. 0 0
      portal/dist/assets/edit-BUenr18P.js
  10. 1 1
      portal/dist/assets/index-B4w4dPRS.js
  11. 0 0
      portal/dist/assets/index-BW2D3A5n.js
  12. 0 0
      portal/dist/assets/index-BXDb06OD.js
  13. 0 0
      portal/dist/assets/index-BcYUtfuP.js
  14. 0 0
      portal/dist/assets/index-Ber1sLNx.js
  15. 0 0
      portal/dist/assets/index-BiT2jf3G.js
  16. 0 0
      portal/dist/assets/index-CMZntfTs.js
  17. 0 0
      portal/dist/assets/index-Cc2ZeZYX.js
  18. 0 0
      portal/dist/assets/index-Clt9w9v5.js
  19. 0 0
      portal/dist/assets/index-Cnbkqx6H.js
  20. 0 0
      portal/dist/assets/index-CrdWDlDc.js
  21. 0 0
      portal/dist/assets/index-DL-VIQj1.js
  22. 0 0
      portal/dist/assets/index-Dtn_3ZpV.js
  23. 0 0
      portal/dist/assets/index-_UYEJ_75.js
  24. 0 0
      portal/dist/assets/index-v7snH43f.js
  25. 1 1
      portal/dist/assets/loadPageConf-BRk0tePB.js
  26. 1 1
      portal/dist/assets/ossFile-DTGoTfRr.js
  27. 0 0
      portal/dist/assets/request-BpIb_E5y.js
  28. 1 1
      portal/dist/assets/user-HOM5VEPD.js
  29. 1 1
      portal/dist/index.html
  30. 22 0
      portal/src/views/ad-plan-conf/detail.vue
  31. 3 0
      src/main/java/com/moka/gdtauto/controller/AdPlanConfController.java
  32. 43 1
      src/main/java/com/moka/gdtauto/entity/AdPlanCreative.java
  33. 15 0
      src/main/java/com/moka/gdtauto/mapper/AdPlanCreativeMapper.java
  34. 331 0
      src/main/java/com/moka/gdtauto/service/AdPlanCreativeDailyReportService.java
  35. 28 0
      src/main/resources/mapper/AdPlanCreativeMapper.xml

+ 1 - 1
portal/dist/assets/accountGroup-m56sQDEF.js → portal/dist/assets/accountGroup-CXtnd01s.js

@@ -1 +1 @@
-import{r as u}from"./request-BoOZFIOM.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-BpIb_E5y.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};

+ 1 - 1
portal/dist/assets/adAutoConf-DzLWg-RZ.js → portal/dist/assets/adAutoConf-Bpg3pRT4.js

@@ -1 +1 @@
-import{r as e}from"./request-BoOZFIOM.js";function u(t){return e({url:"/api/ad-auto-conf/list",method:"get",params:t})}function n(t){return e({url:`/api/ad-auto-conf/${t}`,method:"get"})}function a(t){return e({url:"/api/ad-auto-conf",method:"post",data:t})}function r(t){return e({url:"/api/ad-auto-conf/update",method:"post",data:t})}function i(t){return e({url:`/api/ad-auto-conf/execute?id=${t}`,method:"post"})}function d(t){return e({url:"/api/execute-log/list",method:"get",params:t})}function c(t){return e({url:"/api/execute-log/getChildLog",method:"get",params:t})}export{c as a,d as b,n as c,i as e,u as g,a as s,r as u};
+import{r as e}from"./request-BpIb_E5y.js";function u(t){return e({url:"/api/ad-auto-conf/list",method:"get",params:t})}function n(t){return e({url:`/api/ad-auto-conf/${t}`,method:"get"})}function a(t){return e({url:"/api/ad-auto-conf",method:"post",data:t})}function r(t){return e({url:"/api/ad-auto-conf/update",method:"post",data:t})}function i(t){return e({url:`/api/ad-auto-conf/execute?id=${t}`,method:"post"})}function d(t){return e({url:"/api/execute-log/list",method:"get",params:t})}function c(t){return e({url:"/api/execute-log/getChildLog",method:"get",params:t})}export{c as a,d as b,n as c,i as e,u as g,a as s,r as u};

+ 1 - 1
portal/dist/assets/adPlanConf-m4iaNP9O.js → portal/dist/assets/adPlanConf-Cleib550.js

@@ -1 +1 @@
-import{r as a}from"./request-BoOZFIOM.js";function d(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 u(n){return a({url:`/api/ad-plan-conf/reGeneratePlan/${n}`,method:"get"})}function l(n){return a({url:`/api/ad-plan-conf/syncSystemStatus/${n}`,method:"get"})}function p(n){return a({url:"/api/ad-plan-conf/create",method:"post",data:n})}function s(n){return a({url:"/api/ad-plan-conf/update",method:"post",data:n})}function i(n=1,e=10,t={}){return a({url:"/api/ad-plan-conf/list",method:"get",params:{pageNum:n,pageSize:e,...t}})}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 g(n){return a({url:`/api/ad-plan-creative/plan/${n}`,method:"get"})}function m(n){return a({url:"/api/ad-plan-conf/gdtSuspendByConfId",params:{confId:n},method:"get"})}function h(n){return a({url:"/api/ad-plan-conf/gdtDeleteByConfId",params:{confId:n},method:"get"})}function C(n,e){return a({url:`/api/ad-plan-conf/gdtSuspendByPlanIds/${n}`,method:"post",data:e})}function P(n,e){return a({url:`/api/ad-plan-conf/gdtDeleteByPlanIds/${n}`,method:"post",data:e})}function y(n=[]){return a({url:"/api/ad-plan-conf/getOneCreativeImage",method:"post",data:n})}function I(){return a({url:"/api/ad-plan-conf/getOneBrandImage",method:"get"})}function B(n=[]){return a({url:"/api/ad-plan-conf/getOneCreativeVideo",method:"post",data:n})}export{y as a,I as b,B as c,o as d,p as e,f,i as g,c as h,h as i,C as j,P as k,l,u as m,g as n,d as r,m as s,s as u};
+import{r as a}from"./request-BpIb_E5y.js";function d(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 u(n){return a({url:`/api/ad-plan-conf/reGeneratePlan/${n}`,method:"get"})}function l(n){return a({url:`/api/ad-plan-conf/syncSystemStatus/${n}`,method:"get"})}function p(n){return a({url:"/api/ad-plan-conf/create",method:"post",data:n})}function s(n){return a({url:"/api/ad-plan-conf/update",method:"post",data:n})}function i(n=1,e=10,t={}){return a({url:"/api/ad-plan-conf/list",method:"get",params:{pageNum:n,pageSize:e,...t}})}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 g(n){return a({url:`/api/ad-plan-creative/plan/${n}`,method:"get"})}function m(n){return a({url:"/api/ad-plan-conf/gdtSuspendByConfId",params:{confId:n},method:"get"})}function h(n){return a({url:"/api/ad-plan-conf/gdtDeleteByConfId",params:{confId:n},method:"get"})}function C(n,e){return a({url:`/api/ad-plan-conf/gdtSuspendByPlanIds/${n}`,method:"post",data:e})}function P(n,e){return a({url:`/api/ad-plan-conf/gdtDeleteByPlanIds/${n}`,method:"post",data:e})}function y(n=[]){return a({url:"/api/ad-plan-conf/getOneCreativeImage",method:"post",data:n})}function I(){return a({url:"/api/ad-plan-conf/getOneBrandImage",method:"get"})}function B(n=[]){return a({url:"/api/ad-plan-conf/getOneCreativeVideo",method:"post",data:n})}export{y as a,I as b,B as c,o as d,p as e,f,i as g,c as h,h as i,C as j,P as k,l,u as m,g as n,d as r,m as s,s as u};

+ 1 - 1
portal/dist/assets/auth-BHJoRLvv.js → portal/dist/assets/auth-DAcME6eg.js

@@ -1 +1 @@
-import{r as t}from"./request-BoOZFIOM.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-BpIb_E5y.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-C3YryWGn.js → portal/dist/assets/conversion-7rIK4RQ3.js

@@ -1 +1 @@
-import{r as t}from"./request-BoOZFIOM.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-BpIb_E5y.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};

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/detail-BfNRq6Be.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/detail-yG7f6VbD.js


+ 1 - 1
portal/dist/assets/drama-Br7t0i_b.js → portal/dist/assets/drama-CpOh2ANv.js

@@ -1 +1 @@
-import{r as t}from"./request-BoOZFIOM.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-BpIb_E5y.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};

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/edit-BUenr18P.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
portal/dist/assets/index-B4w4dPRS.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-BW2D3A5n.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-BXDb06OD.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-BcYUtfuP.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-Ber1sLNx.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-BiT2jf3G.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-CMZntfTs.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-Cc2ZeZYX.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-Clt9w9v5.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-Cnbkqx6H.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-CrdWDlDc.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-DL-VIQj1.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-Dtn_3ZpV.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-_UYEJ_75.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/index-v7snH43f.js


+ 1 - 1
portal/dist/assets/loadPageConf-woWRv-PC.js → portal/dist/assets/loadPageConf-BRk0tePB.js

@@ -1 +1 @@
-import{r as o}from"./request-BoOZFIOM.js";function t(a){return o({url:"/api/load-page-conf/list",method:"get",params:a})}function n(a){return o({url:`/api/load-page-conf/${a}`,method:"get"})}function r(a){return o({url:"/api/load-page-conf",method:"post",data:a})}function u(a){return o({url:"/api/load-page-conf",method:"put",data:a})}function d(){return o({url:"/api/load-page-conf/all",method:"get"})}export{t as a,n as b,r as c,d as g,u};
+import{r as o}from"./request-BpIb_E5y.js";function t(a){return o({url:"/api/load-page-conf/list",method:"get",params:a})}function n(a){return o({url:`/api/load-page-conf/${a}`,method:"get"})}function r(a){return o({url:"/api/load-page-conf",method:"post",data:a})}function u(a){return o({url:"/api/load-page-conf",method:"put",data:a})}function d(){return o({url:"/api/load-page-conf/all",method:"get"})}export{t as a,n as b,r as c,d as g,u};

+ 1 - 1
portal/dist/assets/ossFile-DOF_3m_L.js → portal/dist/assets/ossFile-DTGoTfRr.js

@@ -1 +1 @@
-import{r as t}from"./request-BoOZFIOM.js";function i(e){return t({timeout:12e5,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-BpIb_E5y.js";function i(e){return t({timeout:12e5,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};

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
portal/dist/assets/request-BpIb_E5y.js


+ 1 - 1
portal/dist/assets/user-CFPXa6PO.js → portal/dist/assets/user-HOM5VEPD.js

@@ -1 +1 @@
-import{r as e}from"./request-BoOZFIOM.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-BpIb_E5y.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-BRRcaIyg.js"></script>
+    <script type="module" crossorigin src="/assets/index-B4w4dPRS.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-DLIfzCMM.css">
   </head>
   <body>

+ 22 - 0
portal/src/views/ad-plan-conf/detail.vue

@@ -331,6 +331,28 @@
                     <span class="text-red-500">{{ creative.failReason || '-' }}</span>
                   </template>
                 </el-table-column>
+                <el-table-column label="花费" prop="cost" width="100">
+          <template #default="{ row }">
+            {{ row.cost && (row.cost / 100).toFixed(2) || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="目标转化成本" prop="conversionsCost" width="120">
+          <template #default="{ row }">
+            {{ row.conversionsCost && (row.conversionsCost / 100).toFixed(2) || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="加粉成本" prop="scanFollowUserCost" width="100">
+          <template #default="{ row }">
+            {{ row.scanFollowUserCost && (row.scanFollowUserCost / 100).toFixed(2) || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="曝光" prop="viewCount" width="100"></el-table-column>
+        <el-table-column label="有效点击" prop="validClickCount" width="100"></el-table-column>
+        <el-table-column label="点击率" prop="ctr" width="100"></el-table-column>
+        <el-table-column label="播放首日变现" prop="reqIncomeVal1" width="120"></el-table-column>
+        <el-table-column label="播放首日ROI" prop="reqIncomeRoi1" width="120"></el-table-column>
+        <el-table-column label="转化首日变现" prop="repIncomeVal1" width="120"></el-table-column>
+        <el-table-column label="转化首日ROI" prop="repIncomeRoi1" width="120"></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 }">

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

@@ -23,6 +23,7 @@ import com.moka.gdtauto.mapper.TencentAssetDramaMapper;
 import com.moka.gdtauto.mapper.TencentAssetFictionMapper;
 import com.moka.gdtauto.service.AccountGroupService;
 import com.moka.gdtauto.service.AdPlanConfService;
+import com.moka.gdtauto.service.AdPlanCreativeDailyReportService;
 import com.moka.gdtauto.service.AdPlanCreativeService;
 import com.moka.gdtauto.service.AdPlanDailyReportService;
 import com.moka.gdtauto.service.AdPlanService;
@@ -66,6 +67,7 @@ public class AdPlanConfController {
     private final AdPlanConfService adPlanConfService;
     private final AdPlanService adPlanService;
     private final AdPlanDailyReportService adPlanDailyReportService;
+    private final AdPlanCreativeDailyReportService adPlanCreativeDailyReportService;
     private final AdPlanCreativeService adPlanCreativeService;
     private final AccountGroupAccountRelationshipMapper relationshipMapper;
     private final SysUserService sysUserService;
@@ -505,6 +507,7 @@ public class AdPlanConfController {
         adPlanService.syncAdPlanStatus(id);
         adPlanCreativeService.syncAdPlanCreativeStatus(id);
         adPlanDailyReportService.syncDailyReport(id);
+        adPlanCreativeDailyReportService.syncDailyReport(id);
         try {
             TimeUnit.SECONDS.sleep(2L);
         } catch (InterruptedException e) {

+ 43 - 1
src/main/java/com/moka/gdtauto/entity/AdPlanCreative.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 /**
@@ -49,7 +50,6 @@ public class AdPlanCreative {
      * 创意名称60个中文字
      */
     private String creativeName;
-    
 
     /**
      * 图片ID
@@ -85,6 +85,48 @@ public class AdPlanCreative {
      * 计划状态:1-待执行,2-执行中,3-失败,4-已结束
      */
     private Integer status;
+    /**
+     * 花费
+     * 单位:分
+     */
+    private Long cost;
+    /**
+     * 转化成本
+     */
+    private Long conversionsCost;
+    /**
+     * 曝光次数
+     */
+    private Long viewCount;
+    /**
+     * 有效点击次数
+     */
+    private Long validClickCount;
+    /**
+     * 点击率
+     */
+    private BigDecimal ctr;
+
+    /**
+     * 广告播放口径 首日广告变现金额
+     */
+    private Long reqIncomeVal1;
+    /**
+     * 广告播放口径 首日ROI
+     */
+    private BigDecimal reqIncomeRoi1;
+    /**
+     * 转化回传口径 首日广告变现金额
+     */
+    private Long repIncomeVal1;
+    /**
+     * 转化回传口径 首日ROI
+     */
+    private BigDecimal repIncomeRoi1;
+
+    // 加企业微信客服成本。计算公式:花费/加企业微信客服人数
+    // 单位:分
+    private Long scanFollowUserCost;
 
     /**
      * 客户设置的状态

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

@@ -1,6 +1,7 @@
 package com.moka.gdtauto.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.moka.gdtauto.entity.AdPlan;
 import com.moka.gdtauto.entity.AdPlanCreative;
 
 import java.util.List;
@@ -18,4 +19,18 @@ import org.apache.ibatis.annotations.Param;
 public interface AdPlanCreativeMapper extends BaseMapper<AdPlanCreative> {
 
     void batchUpdateApprovalStatus(@Param("creatives") List<AdPlanCreative> toUpdate);
+
+    /**
+     * 批量更新广告计划的 REQUEST_TIME 口径日报数据
+     *
+     * @param plans 包含 adgroupId 和日报字段的广告计划列表
+     */
+    void batchUpdateReqDailyReport(@Param("creatives") List<AdPlanCreative> plans);
+
+    /**
+     * 批量更新广告计划的 REPORTING_TIME 口径日报数据
+     *
+     * @param plans 包含 adgroupId 和日报字段的广告计划列表
+     */
+    void batchUpdateRepDailyReport(@Param("creatives") List<AdPlanCreative> plans);
 }

+ 331 - 0
src/main/java/com/moka/gdtauto/service/AdPlanCreativeDailyReportService.java

@@ -0,0 +1,331 @@
+package com.moka.gdtauto.service;
+
+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.common.Constant;
+import com.moka.gdtauto.entity.AdPlan;
+import com.moka.gdtauto.entity.AdPlanConf;
+import com.moka.gdtauto.entity.AdPlanCreative;
+import com.moka.gdtauto.mapper.AdPlanConfMapper;
+import com.moka.gdtauto.mapper.AdPlanCreativeMapper;
+import com.moka.gdtauto.mapper.AdPlanMapper;
+import com.tencent.ads.model.v3.DailyReportsGetResponseData;
+import com.tencent.ads.model.v3.DailyReportsFilteringStruct;
+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;
+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 java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * 广告计划日报表同步服务
+ * 按广告组级别同步日报数据到 ad_plan 表
+ *
+ * @author moka
+ * @since 2026-03-10
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AdPlanCreativeDailyReportService {
+
+    private final TencentAdsApiClientFactory clientFactory;
+    private final AdPlanConfMapper adPlanConfMapper;
+    private final AdPlanCreativeMapper adPlanCreativeMapper;
+
+    /** REQUEST_TIME 口径同步字段 */
+    private static final List<String> REQ_FIELDS = Arrays.asList(
+            "adgroup_id", "dynamic_creative_id", "date", "cost", "income_val_1", "income_roi_1",
+            "view_count", "valid_click_count", "ctr", "conversions_cost" ,"scan_follow_user_cost"
+    );
+
+    /** REPORTING_TIME 口径同步字段 */
+    private static final List<String> REP_FIELDS = Arrays.asList(
+            "adgroup_id",  "dynamic_creative_id", "date", "income_val_1", "income_roi_1"
+    );
+
+    private static final int PAGE_SIZE = 100;
+    private static final int ADGROUP_BATCH_LIMIT = 100;
+
+    @Async
+    public void syncDailyReport(Long confId) {
+        LocalDateTime start = LocalDateTime.now();
+        LocalDateTime end = LocalDateTime.now();
+        syncReqDailyReport(confId, start, end);
+        syncRepDailyReport(confId, start, end);
+    }
+
+    /**
+     * 同步 REQUEST_TIME 口径日报数据到 ad_plan 表
+     *
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     */
+    public void syncReqDailyReport(Long confId, LocalDateTime startTime, LocalDateTime endTime) {
+        log.info("[syncCreativeReqDailyReport] 开始同步 REQUEST_TIME 口径日报 [{}, {}]", startTime, endTime);
+        doSyncDailyReport(confId, startTime, endTime, TimeLine.REQUEST_TIME.getValue(), true);
+    }
+
+    /**
+     * 同步 REPORTING_TIME 口径日报数据到 ad_plan 表
+     *
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     */
+    public void syncRepDailyReport(Long confId, LocalDateTime startTime, LocalDateTime endTime) {
+        log.info("[syncCreativeRepDailyReport] 开始同步 REPORTING_TIME 口径日报 [{}, {}]", startTime, endTime);
+        doSyncDailyReport(confId, startTime, endTime, TimeLine.REPORTING_TIME.getValue(), false);
+    }
+
+    /**
+     * 执行同步逻辑
+     *
+     * @param startTime   开始时间
+     * @param endTime     结束时间
+     * @param timeLine    时间口径
+     * @param isReqTime   是否为 REQUEST_TIME 口径(决定更新哪些字段)
+     */
+    private void doSyncDailyReport(Long confId, LocalDateTime startTime, LocalDateTime endTime, String timeLine, boolean isReqTime) {
+        // 1. 分页查询 create_time 在范围内的 adPlanConf
+        int confPageNum = 1;
+        int totalUpdated = 0;
+        Map<Long, Integer> confUpdatedCount = new ConcurrentHashMap<>();
+
+        while (true) {
+            LambdaQueryWrapper<AdPlanConf> confWrapper = new LambdaQueryWrapper<>();
+            confWrapper
+                    .eq(confId != null, AdPlanConf::getId, confId)
+                    .eq(AdPlanConf::getDeleted, 0)
+                    .orderByDesc(AdPlanConf::getId);
+            Page<AdPlanConf> confPage = adPlanConfMapper.selectPage(new Page<>(confPageNum, PAGE_SIZE), confWrapper);
+            List<AdPlanConf> confList = confPage.getRecords();
+
+            if (confList.isEmpty()) {
+                break;
+            }
+
+            // 2. 每个 conf 启动一个线程处理
+            List<CompletableFuture<Void>> futures = confList.stream()
+                    .map(conf -> CompletableFuture.runAsync(() -> {
+                        try {
+                            int updated = processConf(conf, startTime, endTime, timeLine, isReqTime);
+                            confUpdatedCount.put(conf.getId(), updated);
+                        } catch (Exception e) {
+                            log.error("[doSyncDailyReport] 处理 confId={} 失败", conf.getId(), e);
+                        }
+                    }))
+                    .collect(Collectors.toList());
+
+            // 等待所有线程完成
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+
+            if (!confPage.hasNext()) {
+                break;
+            }
+            confPageNum++;
+        }
+
+        totalUpdated = confUpdatedCount.values().stream().mapToInt(Integer::intValue).sum();
+        log.info("[doSyncDailyReport] 同步完成 timeLine={} totalUpdated={}", timeLine, totalUpdated);
+    }
+
+    /**
+     * 处理单个 AdPlanConf
+     */
+    private int processConf(AdPlanConf conf, LocalDateTime startTime, LocalDateTime endTime, String timeLine, boolean isReqTime) throws Exception {
+        String jobNumber = conf.getJobNumber();
+        TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
+
+        String startStr = startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        String endStr = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        ReportDateRange dateRange = new ReportDateRange();
+        dateRange.setStartDate(startStr);
+        dateRange.setEndDate(endStr);
+
+        log.info("[广告日报] start={} end={} timeLine={} isReqTime={}", startStr, endStr, timeLine, isReqTime);
+
+        List<String> groupBy = Arrays.asList("dynamic_creative_id", "date");
+        List<String> fields = isReqTime ? REQ_FIELDS : REP_FIELDS;
+
+        int totalUpdated = 0;
+        int planPageNum = 1;
+
+        // 3. 分页查询该 conf 下的所有 adPlan
+        while (true) {
+            LambdaQueryWrapper<AdPlanCreative> planWrapper = new LambdaQueryWrapper<>();
+            planWrapper.eq(AdPlanCreative::getAdPlanConfId, conf.getId())
+                    .isNotNull(AdPlanCreative::getCreativeId)
+                    .gt(AdPlanCreative::getCreativeId, 0)
+                    .eq(AdPlanCreative::getDeleted, 0)
+                    .orderByAsc(AdPlanCreative::getId);
+            Page<AdPlanCreative> planPage = adPlanCreativeMapper.selectPage(new Page<>(planPageNum, PAGE_SIZE), planWrapper);
+            List<AdPlanCreative> plans = planPage.getRecords();
+
+            if (plans.isEmpty()) {
+                break;
+            }
+
+            // 4. 按 accountId 分组,每 100 个 adgroup_id 一批调用 API
+            Map<Long, List<AdPlanCreative>> groupByAccount = plans.stream()
+                    .filter(p -> p.getAccountId() != null && p.getAdgroupId() != null)
+                    .collect(Collectors.groupingBy(AdPlanCreative::getAccountId));
+
+            for (Map.Entry<Long, List<AdPlanCreative>> entry : groupByAccount.entrySet()) {
+                Long accountId = entry.getKey();
+                List<AdPlanCreative> plansOfAccount = entry.getValue();
+
+                // 按 100 条分批
+                int batchCount = (int) Math.ceil((double) plansOfAccount.size() / ADGROUP_BATCH_LIMIT);
+                for (int i = 0; i < batchCount; i++) {
+                    int fromIdx = i * ADGROUP_BATCH_LIMIT;
+                    int toIdx = Math.min(fromIdx + ADGROUP_BATCH_LIMIT, plansOfAccount.size());
+                    List<AdPlanCreative> batch = plansOfAccount.subList(fromIdx, toIdx);
+
+                    try {
+                        int updated = syncBatch(tencentAds, accountId, batch, dateRange, groupBy, fields, timeLine, isReqTime);
+                        totalUpdated += updated;
+                    } catch (Exception e) {
+                        log.error("[processConf] 同步失败 accountId={} confId={} batch={}", accountId, conf.getId(), i, e);
+                    }
+                }
+            }
+
+            if (!planPage.hasNext()) {
+                break;
+            }
+            planPageNum++;
+        }
+
+        return totalUpdated;
+    }
+
+    /**
+     * 批量同步单批 adPlan
+     */
+    private int syncBatch(TencentAds tencentAds, Long accountId, List<AdPlanCreative> batch,
+                          ReportDateRange dateRange, List<String> groupBy, List<String> fields,
+                          String timeLine, boolean isReqTime) throws Exception {
+
+        // 构建 filtering: adgroup_id IN [...]
+        List<String> creativeIdValues = batch.stream()
+                .map(p -> String.valueOf(p.getCreativeId()))
+                .collect(Collectors.toList());
+
+        DailyReportsFilteringStruct filter = new DailyReportsFilteringStruct();
+        filter.setField("dynamic_creative_id");
+        filter.setOperator(FilterOperator.IN);
+        filter.setValues(creativeIdValues);
+
+        List<DailyReportsFilteringStruct> filtering = new ArrayList<>();
+        filtering.add(filter);
+
+        // 调用 API
+        DailyReportsGetResponseData response = tencentAds.dailyReports().dailyReportsGet(
+                "REPORT_LEVEL_DYNAMIC_CREATIVE",
+                dateRange,
+                groupBy,
+                fields,
+                accountId,
+                filtering,
+                null,  // orderBy
+                timeLine,
+                1L,
+                (long) ADGROUP_BATCH_LIMIT,
+                null   // modularDetailMode
+        );
+
+        if (response == null) {
+            log.warn("[syncBatch] accountId={} 未返回日报数据", accountId);
+            return 0;
+        }
+
+        // 构建更新数据
+        List<AdPlanCreative> toUpdate = new ArrayList<>();
+        if (response.getList() != null) {
+            for (ReportStruct rs : response.getList()) {
+                if (rs.getAdgroupId() == null) {
+                    continue;
+                }
+                AdPlanCreative update = new AdPlanCreative();
+                update.setCreativeId(rs.getDynamicCreativeId());
+                if (isReqTime) {
+                    // REQUEST_TIME 口径:更新 cost, view_count, valid_click_count, ctr, conversions_cost, req_income_val_1, req_income_roi_1
+                    update.setCost(rs.getCost());
+                    update.setViewCount(rs.getViewCount());
+                    update.setValidClickCount(rs.getValidClickCount());
+                    update.setCtr(d2b(rs.getCtr()));
+                    update.setConversionsCost(rs.getConversionsCost());
+                    update.setReqIncomeVal1(rs.getIncomeVal1());
+                    update.setReqIncomeRoi1(d2b(rs.getIncomeRoi1()));
+                    update.setScanFollowUserCost(rs.getScanFollowUserCost());
+                } else {
+                    // REPORTING_TIME 口径:更新 rep_income_val_1, rep_income_roi_1
+                    update.setRepIncomeVal1(rs.getIncomeVal1());
+                    update.setRepIncomeRoi1(d2b(rs.getIncomeRoi1()));
+                }
+                update.setUpdateTime(LocalDateTime.now());
+                toUpdate.add(update);
+            }   
+        }
+
+        //无报表数据的,填充0值
+        Map<Long, AdPlanCreative> updateMap = toUpdate.stream().collect(Collectors.toMap(AdPlanCreative::getCreativeId, p -> p));
+        batch.forEach(p -> {
+            if (!updateMap.containsKey(p.getCreativeId())) {
+                AdPlanCreative update = new AdPlanCreative();
+                update.setAdgroupId(p.getAdgroupId());
+
+                if (isReqTime) { 
+                    update.setCost(0L);
+                    update.setViewCount(0L);
+                    update.setValidClickCount(0L);
+                    update.setCtr(BigDecimal.ZERO);
+                    update.setConversionsCost(0L);
+                    update.setReqIncomeVal1(0L);
+                    update.setReqIncomeRoi1(BigDecimal.ZERO);
+                    update.setScanFollowUserCost(0L);
+                } else {
+                    update.setRepIncomeVal1(0L);
+                    update.setRepIncomeRoi1(BigDecimal.ZERO);
+                }
+                update.setUpdateTime(LocalDateTime.now());
+                toUpdate.add(update);
+            }
+        });
+
+        if (toUpdate.isEmpty()) {
+            return 0;
+        }
+
+        // 批量更新
+        if (isReqTime) {
+            adPlanCreativeMapper.batchUpdateReqDailyReport(toUpdate);
+        } else {
+            adPlanCreativeMapper.batchUpdateRepDailyReport(toUpdate);
+        }
+
+        log.info("[syncBatch] accountId={} 批量更新 {} 条 timeLine={}", accountId, toUpdate.size(), timeLine);
+        return toUpdate.size();
+    }
+
+    /** Double → BigDecimal 安全转换 */
+    private BigDecimal d2b(Double d) {
+        return d == null ? null : BigDecimal.valueOf(d);
+    }
+}

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

@@ -13,4 +13,32 @@
         </foreach>
     </update>
 
+     <!-- 批量更新广告计划 REQUEST_TIME 口径日报数据 -->
+    <update id="batchUpdateReqDailyReport" parameterType="java.util.List">
+        <foreach collection="creatives" item="item" separator=";">
+            UPDATE ad_plan_creative
+            SET cost              = #{item.cost},
+                view_count        = #{item.viewCount},
+                valid_click_count = #{item.validClickCount},
+                ctr               = #{item.ctr},
+                conversions_cost  = #{item.conversionsCost},
+                req_income_val1  = #{item.reqIncomeVal1},
+                req_income_roi1  = #{item.reqIncomeRoi1},
+                scan_follow_user_cost = #{item.scanFollowUserCost},
+                update_time       = NOW()
+            WHERE adgroup_id = #{item.adgroupId}
+        </foreach>
+    </update>
+
+    <!-- 批量更新广告计划 REPORTING_TIME 口径日报数据 -->
+    <update id="batchUpdateRepDailyReport" parameterType="java.util.List">
+        <foreach collection="creatives" item="item" separator=";">
+            UPDATE ad_plan_creative
+            SET rep_income_val1 = #{item.repIncomeVal1},
+                rep_income_roi1 = #{item.repIncomeRoi1},
+                update_time      = NOW()
+            WHERE adgroup_id = #{item.adgroupId}
+        </foreach>
+    </update>
+
 </mapper>

Некоторые файлы не были показаны из-за большого количества измененных файлов