pudongliang 4 месяцев назад
Родитель
Сommit
1fcb88e4a6
39 измененных файлов с 318 добавлено и 166 удалено
  1. 2 2
      pom.xml
  2. 1 1
      portal/dist/assets/accountGroup-VtZDXJNk.js
  3. 1 0
      portal/dist/assets/adPlanConf-CSjsB9wl.js
  4. 0 1
      portal/dist/assets/adPlanConf-CfESW9H5.js
  5. 1 1
      portal/dist/assets/auth-DcFegRqy.js
  6. 1 1
      portal/dist/assets/conversion-mKvMxRVo.js
  7. 0 0
      portal/dist/assets/detail-BY1jRtWw.js
  8. 0 0
      portal/dist/assets/detail-DU4pmLZZ.js
  9. 1 1
      portal/dist/assets/drama-BruRhCeR.js
  10. 0 1
      portal/dist/assets/index-0MmA-K0O.css
  11. 0 0
      portal/dist/assets/index-89cN1x9c.js
  12. 1 0
      portal/dist/assets/index-BKtci71u.css
  13. 0 0
      portal/dist/assets/index-BLCqiC-S.js
  14. 0 0
      portal/dist/assets/index-BsMMzBKr.js
  15. 0 0
      portal/dist/assets/index-CG9XAsd6.js
  16. 0 0
      portal/dist/assets/index-CMiubuun.js
  17. 0 0
      portal/dist/assets/index-CMqMQvCe.css
  18. 1 0
      portal/dist/assets/index-CTL58QSW.css
  19. 0 0
      portal/dist/assets/index-CUn534sH.js
  20. 0 0
      portal/dist/assets/index-CZdlt2wU.js
  21. 0 1
      portal/dist/assets/index-CvzQKMxG.css
  22. 0 0
      portal/dist/assets/index-Cwv_eFCY.js
  23. 0 0
      portal/dist/assets/index-CybSq6qI.js
  24. 0 0
      portal/dist/assets/index-D-D9glaS.js
  25. 0 1
      portal/dist/assets/index-D2u6GVqk.css
  26. 1 0
      portal/dist/assets/index-Dti5aB1X.css
  27. 1 1
      portal/dist/assets/index-HkocDL0v.js
  28. 0 0
      portal/dist/assets/index-viwIobCa.js
  29. 1 1
      portal/dist/assets/ossFile-C9wsE69w.js
  30. 0 0
      portal/dist/assets/request-Dz809fuF.js
  31. 1 1
      portal/dist/assets/user-CfF0mQB6.js
  32. 14 14
      portal/dist/index.html
  33. 16 0
      portal/src/api/adPlanConf.js
  34. 1 1
      portal/src/utils/request.js
  35. 50 2
      portal/src/views/ad-plan-conf/detail.vue
  36. 17 13
      src/main/java/com/moka/gdtauto/common/Result.java
  37. 21 4
      src/main/java/com/moka/gdtauto/controller/AdPlanConfController.java
  38. 11 6
      src/main/java/com/moka/gdtauto/service/AdPlanConfService.java
  39. 175 113
      src/main/java/com/moka/gdtauto/service/impl/AdPlanConfServiceImpl.java

+ 2 - 2
pom.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
          http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
@@ -239,7 +239,7 @@
                     </jvmArguments>
                 </configuration>
             </plugin>
-            
+
             <!-- Maven Surefire Plugin - 为Java 21添加JVM参数 -->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>

+ 1 - 1
portal/dist/assets/accountGroup-CsV7PgBZ.js → portal/dist/assets/accountGroup-VtZDXJNk.js

@@ -1 +1 @@
-import{r as u}from"./request-94g2Q2zR.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-Dz809fuF.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 - 0
portal/dist/assets/adPlanConf-CSjsB9wl.js

@@ -0,0 +1 @@
+import{r as a}from"./request-Dz809fuF.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 d(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 l(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 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 m(n){return a({url:`/api/ad-plan-creative/plan/${n}`,method:"get"})}function g(n){return a({url:"/api/ad-plan-conf/suspendByConfId",params:{confId:n},method:"get"})}function h(n,e){return a({url:`/api/ad-plan-conf/suspendByPlanIds/${n}`,method:"post",data:e})}export{o as a,f as b,l as c,p as d,c as e,h as f,i as g,u as h,d as i,m as j,r,g as s,s as u};

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

@@ -1 +0,0 @@
-import{r as a}from"./request-94g2Q2zR.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-PPO_Q_Ws.js → portal/dist/assets/auth-DcFegRqy.js

@@ -1 +1 @@
-import{r as t}from"./request-94g2Q2zR.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-Dz809fuF.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-DYiN6umm.js → portal/dist/assets/conversion-mKvMxRVo.js

@@ -1 +1 @@
-import{r as t}from"./request-94g2Q2zR.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-Dz809fuF.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-BY1jRtWw.js


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


+ 1 - 1
portal/dist/assets/drama-k1LdRq3L.js → portal/dist/assets/drama-BruRhCeR.js

@@ -1 +1 @@
-import{r as t}from"./request-94g2Q2zR.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-Dz809fuF.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 - 1
portal/dist/assets/index-0MmA-K0O.css

@@ -1 +0,0 @@
-[data-v-c35dafee] .el-checkbox{width:100%;margin-right:0}[data-v-c35dafee] .el-checkbox__label{width:100%;white-space:normal;word-break:break-all}

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


+ 1 - 0
portal/dist/assets/index-BKtci71u.css

@@ -0,0 +1 @@
+.conversion-container[data-v-cf5a26d8]{padding:20px}.card-header[data-v-cf5a26d8]{display:flex;justify-content:space-between;align-items:center}.stats-row[data-v-cf5a26d8]{margin-bottom:20px}.pagination-container[data-v-cf5a26d8]{margin-top:20px;display:flex;justify-content:flex-end}[data-v-cf5a26d8] .el-statistic__head{font-size:14px;color:#909399}[data-v-cf5a26d8] .el-statistic__number{font-size:24px;font-weight:700;color:#303133}[data-v-cf5a26d8] .success .el-statistic__number{color:#67c23a}[data-v-cf5a26d8] .info .el-statistic__number{color:#409eff}[data-v-cf5a26d8] .warning .el-statistic__number{color:#e6a23c}

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


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


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


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


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


+ 1 - 0
portal/dist/assets/index-CTL58QSW.css

@@ -0,0 +1 @@
+.btn-primary[data-v-7d104880]{background:linear-gradient(135deg,#667eea,#764ba2);border:none;color:#fff}.btn-primary[data-v-7d104880]:hover{background:linear-gradient(135deg,#764ba2,#667eea)}.btn-secondary[data-v-7d104880]{border-color:#e5e7eb;color:#6b7280}.btn-secondary[data-v-7d104880]:hover{background-color:#f9fafb}[data-v-7d104880] .el-upload-dragger{border:2px dashed #d1d5db;border-radius:8px;background-color:#f9fafb;transition:all .3s}[data-v-7d104880] .el-upload-dragger:hover{border-color:#667eea;background-color:#f3f4f6}

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


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


+ 0 - 1
portal/dist/assets/index-CvzQKMxG.css

@@ -1 +0,0 @@
-.conversion-container[data-v-86e7044b]{padding:20px}.card-header[data-v-86e7044b]{display:flex;justify-content:space-between;align-items:center}.stats-row[data-v-86e7044b]{margin-bottom:20px}.pagination-container[data-v-86e7044b]{margin-top:20px;display:flex;justify-content:flex-end}[data-v-86e7044b] .el-statistic__head{font-size:14px;color:#909399}[data-v-86e7044b] .el-statistic__number{font-size:24px;font-weight:700;color:#303133}[data-v-86e7044b] .success .el-statistic__number{color:#67c23a}[data-v-86e7044b] .info .el-statistic__number{color:#409eff}[data-v-86e7044b] .warning .el-statistic__number{color:#e6a23c}

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


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


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


+ 0 - 1
portal/dist/assets/index-D2u6GVqk.css

@@ -1 +0,0 @@
-.btn-primary[data-v-fe12f8a1]{background:linear-gradient(135deg,#667eea,#764ba2);border:none;color:#fff}.btn-primary[data-v-fe12f8a1]:hover{background:linear-gradient(135deg,#764ba2,#667eea)}.btn-secondary[data-v-fe12f8a1]{border-color:#e5e7eb;color:#6b7280}.btn-secondary[data-v-fe12f8a1]:hover{background-color:#f9fafb}[data-v-fe12f8a1] .el-upload-dragger{border:2px dashed #d1d5db;border-radius:8px;background-color:#f9fafb;transition:all .3s}[data-v-fe12f8a1] .el-upload-dragger:hover{border-color:#667eea;background-color:#f3f4f6}

+ 1 - 0
portal/dist/assets/index-Dti5aB1X.css

@@ -0,0 +1 @@
+[data-v-e17a2035] .el-checkbox{width:100%;margin-right:0}[data-v-e17a2035] .el-checkbox__label{width:100%;white-space:normal;word-break:break-all}

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


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


+ 1 - 1
portal/dist/assets/ossFile-B9lWIgeT.js → portal/dist/assets/ossFile-C9wsE69w.js

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

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


+ 1 - 1
portal/dist/assets/user-DCZ_sCvl.js → portal/dist/assets/user-CfF0mQB6.js

@@ -1 +1 @@
-import{r as e}from"./request-94g2Q2zR.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-Dz809fuF.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};

+ 14 - 14
portal/dist/index.html

@@ -1,14 +1,14 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <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-HWWOxLkr.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-CdDwsIxv.css">
-  </head>
-  <body>
-    <div id="app"></div>
-  </body>
-</html>
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <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-HkocDL0v.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-CMqMQvCe.css">
+  </head>
+  <body>
+    <div id="app"></div>

+  </body>
+</html>

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

@@ -116,3 +116,19 @@ export function getAdPlanCreativesByPlanId(planId) {
     method: 'get'
   })
 }
+
+export function suspendByConfId(confId) {
+  return request({
+    url: `/api/ad-plan-conf/suspendByConfId`,
+    params: { confId },
+    method: 'get'
+  })
+}
+
+export function suspendByPlanIds(confId, ids) {
+  return request({
+    url: `/api/ad-plan-conf/suspendByPlanIds/${confId}`,
+    method: 'post',
+    data: ids
+  })
+}

+ 1 - 1
portal/src/utils/request.js

@@ -4,7 +4,7 @@ import { ElMessage } from 'element-plus'
 // 创建 axios 实例
 const request = axios.create({
   baseURL: '/api',
-  timeout: 15000
+  timeout: 60000
 })
 
 // 请求拦截器

+ 50 - 2
portal/src/views/ad-plan-conf/detail.vue

@@ -15,9 +15,14 @@
           <el-icon><Document /></el-icon>
           配置信息
         </h2>
-        <el-button type="primary" @click="handleRefresh(confDetail.id)">
+        <div>
+          <el-button type="danger" @click="handleSuspendAll(confDetail.id)">
+            <el-icon><SwitchButton /></el-icon>
+            全部暂停</el-button>
+          <el-button type="primary" @click="handleRefresh(confDetail.id)">
             <el-icon><Refresh /></el-icon>
             刷新状态</el-button>
+        </div>
       </div>
       
       <div v-if="confDetail">
@@ -159,6 +164,9 @@
           广告计划列表
         </h2>
         <div>
+          <el-button type="warning" @click="handleSuspendPlans(confDetail.id)">
+            <el-icon><SwitchButton /></el-icon>
+            暂停选中计划</el-button>
            <el-button type="primary" @click="handleSystemRefresh(confDetail.id)">
             <el-icon><Refresh /></el-icon>
             同步平台状态</el-button>
@@ -213,7 +221,9 @@
         row-key="id"
         :expand-row-keys="expandedRows"
         @expand-change="handleExpandChange"
+        @selection-change="handleSelectionChange"
       >
+        <el-table-column type="selection"  width="55" />
         <el-table-column type="expand">
           <template #default="{ row }">
             <div class="p-4 bg-gray-50">
@@ -366,7 +376,9 @@ import {
   getAdPlanList, 
   reGeneratePlan,
   getAdPlanCreativesByPlanId,
-  syncSystemStatus
+  syncSystemStatus,
+    suspendByConfId,
+  suspendByPlanIds
 } from '@/api/adPlanConf'
 
 const route = useRoute()
@@ -382,6 +394,7 @@ const adPlanList = ref([])
 const planLoading = ref(false)
 const total = ref(0)
 const expandedRows = ref([])
+const selectedRows = ref([])
 
 // 查询参数
 const queryParams = ref({
@@ -466,6 +479,10 @@ const fetchAdPlans = async () => {
   }
 }
 
+const handleSelectionChange = (rows) => {
+  selectedRows.value = rows
+}
+
 // 处理展开行变化
 const handleExpandChange = async (row, expandedRowsArray) => {
   expandedRows.value = expandedRowsArray.map(r => r.id)
@@ -561,6 +578,37 @@ const handleRefresh = async () => {
   await fetchAdPlans()
 }
 
+const handleSuspendAll = async (confId) => {
+  try {
+    const res = await suspendByConfId(confId)
+    if (res.success) {
+      handleRefresh()
+    } else {
+      ElMessage.error(res.msg || '全部暂停失败')
+    }
+  } catch (error) {
+    console.error('全部暂停失败:', error)
+    ElMessage.error('全部暂停失败')
+  }
+}
+
+const handleSuspendPlans = async (confId) => {
+  try {
+    if (selectedRows.value.length < 1) {
+      return
+    }
+    const res = await suspendByPlanIds(confId, selectedRows.value.map(r => r.id))
+    if (res.success) {
+      handleRefresh()
+    } else {
+      ElMessage.error(res.msg || '暂停失败')
+    }
+  } catch (error) {
+    console.error('暂停失败:', error)
+    ElMessage.error('暂停失败')
+  }
+}
+
 const handleSystemRefresh = async (confId) => {
   try {
     const res = await syncSystemStatus(confId)

+ 17 - 13
src/main/java/com/moka/gdtauto/common/Result.java

@@ -5,7 +5,7 @@ import java.io.Serializable;
 
 /**
  * 统一响应结果封装类
- * 
+ *
  * @author moka
  * @since 2026-02-06
  */
@@ -72,7 +72,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 成功响应(无数据)
-     * 
+     *
      * @param message 成功消息
      * @return Result实例
      */
@@ -85,7 +85,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 成功响应(带数据)
-     * 
+     *
      * @param data 响应数据
      * @param message 成功消息
      * @return Result实例
@@ -100,7 +100,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 成功响应(仅数据,默认消息"操作成功")
-     * 
+     *
      * @param data 响应数据
      * @return Result实例
      */
@@ -108,9 +108,13 @@ public class Result<T> implements Serializable {
         return success(data, "操作成功");
     }
 
+    public static <T> Result<T> success() {
+        return success(null, "操作成功");
+    }
+
     /**
      * 失败响应
-     * 
+     *
      * @param message 失败消息
      * @return Result实例
      */
@@ -123,7 +127,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 失败响应(带数据)
-     * 
+     *
      * @param data 响应数据
      * @param message 失败消息
      * @return Result实例
@@ -138,7 +142,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 错误响应(别名方法,与fail功能相同)
-     * 
+     *
      * @param message 错误消息
      * @return Result实例
      */
@@ -148,7 +152,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 错误响应(带数据,别名方法)
-     * 
+     *
      * @param data 响应数据
      * @param message 错误消息
      * @return Result实例
@@ -159,7 +163,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 设置数量
-     * 
+     *
      * @param count 数量
      * @return Result实例
      */
@@ -170,7 +174,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 设置备注
-     * 
+     *
      * @param note 备注信息
      * @return Result实例
      */
@@ -181,7 +185,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 设置使用说明
-     * 
+     *
      * @param usage 使用说明
      * @return Result实例
      */
@@ -192,7 +196,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 设置分页信息
-     * 
+     *
      * @param page 当前页码
      * @param pageSize 每页数量
      * @param total 总记录数
@@ -208,7 +212,7 @@ public class Result<T> implements Serializable {
 
     /**
      * 设置总记录数(用于统计)
-     * 
+     *
      * @param total 总记录数
      * @return Result实例
      */

+ 21 - 4
src/main/java/com/moka/gdtauto/controller/AdPlanConfController.java

@@ -25,6 +25,7 @@ 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;
+import jakarta.validation.constraints.NotNull;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
@@ -41,7 +42,7 @@ import java.time.format.DateTimeFormatter;
 
 /**
  * 广告计划配置管理控制器
- * 
+ *
  * @author moka
  * @since 2026-02-24
  */
@@ -60,6 +61,22 @@ public class AdPlanConfController {
     private final AccountGroupService accountGroupService;
     private final TencentAssetDramaMapper tencentAssetDramaMapper;
 
+
+    @GetMapping("/suspendByConfId")
+    public Result<Void> suspendByConfId(@NotNull Long confId) {
+        adPlanConfService.suspendByConfId(confId);
+        return Result.success("全部暂停成功");
+    }
+
+    @PostMapping("/suspendByPlanIds/{confId}")
+    public Result<Void> suspendByPlanIds (@PathVariable Long confId, @RequestBody Long planIds){
+        if (planIds == null) {
+            return  Result.success("请选择要暂停广告");
+        }
+        adPlanConfService.suspendByPlanIds(confId, planIds);
+        return Result.success();
+    }
+
     @Operation(summary = "预创建广告计划配置", description = "根据账户分组ID和配置ID,重新创建广告计划配置")
     @PostMapping("/reCreate")
     public Result<List<AdPlan>> reCreateAdPlanConf(
@@ -105,7 +122,7 @@ public class AdPlanConfController {
             }
             dramas = tencentAssetDramaMapper.selectList(new LambdaQueryWrapper<TencentAssetDrama>().eq(TencentAssetDrama::getMarketingAssetId, dramaId).orderByDesc(TencentAssetDrama::getId).last("limit 1"));
         }
-    
+
         String targets = request.getTargets();
         String sites = request.getSites();
         String[] targetArray = targets.split(",");
@@ -168,8 +185,8 @@ public class AdPlanConfController {
                 }
             }
             }
-            
-            
+
+
         }
         return plans;
     }

+ 11 - 6
src/main/java/com/moka/gdtauto/service/AdPlanConfService.java

@@ -6,10 +6,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.moka.gdtauto.entity.AdPlan;
 import com.moka.gdtauto.entity.AdPlanConf;
+import jakarta.validation.constraints.NotNull;
 
 /**
  * 广告计划配置服务接口
- * 
+ *
  * @author moka
  * @since 2026-02-24
  */
@@ -17,7 +18,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
     /**
      * 创建广告计划配置
-     * 
+     *
      * @param adPlanConf 广告计划配置
      * @return 配置ID
      */
@@ -25,7 +26,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
     /**
      * 更新广告计划配置
-     * 
+     *
      * @param adPlanConf 广告计划配置
      * @return 是否成功
      */
@@ -33,7 +34,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
     /**
      * 删除广告计划配置
-     * 
+     *
      * @param confId 配置ID
      * @param userId 用户ID
      * @return 是否成功
@@ -42,7 +43,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
     /**
      * 获取用户的广告计划配置列表(分页)
-     * 
+     *
      * @param userId 用户ID
      * @param pageNum 页码
      * @param pageSize 每页大小
@@ -52,7 +53,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
     /**
      * 获取广告计划配置详情
-     * 
+     *
      * @param confId 配置ID
      * @param userId 用户ID
      * @return 配置信息
@@ -64,4 +65,8 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
     void asyncTencentInterface(List<AdPlan> plans);
 
     void asyncReGeneratePlan(Long id);
+
+    void suspendByConfId(@NotNull Long confId);
+
+    void suspendByPlanIds(Long confId, Long planIds);
 }

+ 175 - 113
src/main/java/com/moka/gdtauto/service/impl/AdPlanConfServiceImpl.java

@@ -48,7 +48,7 @@ import com.moka.gdtauto.common.Constant;
 
 /**
  * 广告计划配置服务实现类
- * 
+ *
  * @author moka
  * @since 2026-02-24
  */
@@ -57,7 +57,7 @@ import com.moka.gdtauto.common.Constant;
 public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanConf> implements AdPlanConfService {
 
     @Autowired
-    private AdPlanService adPlanService; 
+    private AdPlanService adPlanService;
     @Autowired
     private AdPlanCreativeService adPlanCreativeService;
     @Autowired
@@ -67,17 +67,79 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
     @Autowired
     private TencentAccountService tencentAccountService;
 
-    
+    @Override
+    public void suspendByConfId(Long confId) {
+        AdPlanConf conf = this.getById(confId);
+        String jobNumber = conf.getJobNumber();
+        List<AdPlan> plans = adPlanService.lambdaQuery()
+                .eq(AdPlan::getAdPlanConfId, confId)
+                .gt(AdPlan::getAdgroupId, 0)
+                .orderByAsc(AdPlan::getId).list();
+        try {
+            TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
+            for (AdPlan plan : plans) {
+                try {
+                    AdgroupsUpdateRequest req = new AdgroupsUpdateRequest();
+                    req.setAccountId(plan.getAccountId());
+                    req.setAdgroupId(plan.getAdgroupId());
+                    req.setConfiguredStatus(ConfiguredStatus.SUSPEND);
+                    tencentAds.adgroups().adgroupsUpdate(req);
+                    plan.setSystemStatus("ADGROUP_STATUS_SUSPEND");
+                } catch (Exception e) {
+                    plan.setFailReason(truncateError(e.getMessage()));
+                }
+                adPlanService.lambdaUpdate().eq(AdPlan::getId,plan.getId())
+                        .set(AdPlan::getSystemStatus, plan.getSystemStatus())
+                        .set(AdPlan::getFailReason, plan.getFailReason())
+                        .update();
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void suspendByPlanIds(Long confId, Long planIds) {
+        AdPlanConf conf = this.getById(confId);
+        String jobNumber = conf.getJobNumber();
+        List<AdPlan> plans = adPlanService.lambdaQuery()
+                .eq(AdPlan::getAdPlanConfId, confId)
+                .in(AdPlan::getId, planIds)
+                .gt(AdPlan::getAdgroupId, 0)
+                .orderByAsc(AdPlan::getId).list();
+        try {
+            TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
+            for (AdPlan plan : plans) {
+                try {
+                    AdgroupsUpdateRequest req = new AdgroupsUpdateRequest();
+                    req.setAccountId(plan.getAccountId());
+                    req.setAdgroupId(plan.getAdgroupId());
+                    req.setConfiguredStatus(ConfiguredStatus.SUSPEND);
+                    tencentAds.adgroups().adgroupsUpdate(req);
+                    plan.setSystemStatus("ADGROUP_STATUS_SUSPEND");
+                } catch (Exception e) {
+                    plan.setFailReason(truncateError(e.getMessage()));
+                }
+                adPlanService.lambdaUpdate().eq(AdPlan::getId,plan.getId())
+                        .set(AdPlan::getSystemStatus, plan.getSystemStatus())
+                        .set(AdPlan::getFailReason, plan.getFailReason())
+                        .update();
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createAdPlanConf(AdPlanConf adPlanConf) {
         adPlanConf.setCreateTime(LocalDateTime.now());
         adPlanConf.setUpdateTime(LocalDateTime.now());
         adPlanConf.setDeleted(0);
-        
+
         this.save(adPlanConf);
         log.info("创建广告计划配置成功,配置ID: {}, 配置名称: {}", adPlanConf.getId(), adPlanConf.getName());
-        
+
         return adPlanConf.getId();
     }
 
@@ -87,16 +149,16 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
         AdPlanConf existingConf = this.getOne(new LambdaQueryWrapper<AdPlanConf>()
                 .eq(AdPlanConf::getId, adPlanConf.getId())
                 .eq(AdPlanConf::getDeleted, 0));
-        
+
         if (existingConf == null) {
             log.warn("广告计划配置不存在或无权限,配置ID: {}, 用户ID: {}", adPlanConf.getId(), adPlanConf.getUserId());
             return false;
         }
-        
+
         adPlanConf.setUpdateTime(LocalDateTime.now());
         boolean result = this.updateById(adPlanConf);
         log.info("更新广告计划配置成功,配置ID: {}", adPlanConf.getId());
-        
+
         return result;
     }
 
@@ -107,16 +169,16 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                 .eq(AdPlanConf::getId, confId)
                 .eq(AdPlanConf::getUserId, userId)
                 .eq(AdPlanConf::getDeleted, 0));
-        
+
         if (existingConf == null) {
             log.warn("广告计划配置不存在或无权限,配置ID: {}, 用户ID: {}", confId, userId);
             return false;
         }
-        
+
         existingConf.setDeleted(1);
         existingConf.setUpdateTime(LocalDateTime.now());
         boolean result = this.updateById(existingConf);
-        
+
         log.info("删除广告计划配置成功,配置ID: {}", confId);
         return result;
     }
@@ -124,11 +186,11 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
     @Override
     public Page<AdPlanConf> getUserAdPlanConfs(Long userId, Integer pageNum, Integer pageSize) {
         Page<AdPlanConf> page = new Page<>(pageNum, pageSize);
-        
+
         LambdaQueryWrapper<AdPlanConf> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(AdPlanConf::getDeleted, 0)
                 .orderByDesc(AdPlanConf::getCreateTime);
-        
+
         return this.page(page, queryWrapper);
     }
 
@@ -188,12 +250,12 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
     @Override
     public void asyncTencentInterface(List<AdPlan> plans) {
         log.info("异步对接腾讯接口开始,计划数量: {}", plans.size());
-        
+
         if (plans == null || plans.isEmpty()) {
             log.warn("计划列表为空,无需处理");
             return;
         }
-        
+
         // 获取配置信息
         Long confId = plans.get(0).getAdPlanConfId();
         AdPlanConf conf = this.getById(confId);
@@ -201,14 +263,14 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             log.error("未找到广告计划配置,confId={}", confId);
             return;
         }
-        
+
         String jobNumber = conf.getJobNumber();
-        
+
         // 遍历处理每个 AdPlan
         for (AdPlan plan : plans) {
             try {
                 log.info("开始处理广告计划,planId={}, adgroupName={}", plan.getId(), plan.getAdgroupName());
-                
+
                 if (plan.getAdgroupId() > 0 || plan.getStatus() == AdPlanStatus.SUCCESS) {
                      // 创建动态创意
                     createDynamicCreatives(plan, jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
@@ -218,21 +280,21 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                     adPlanService.updateById(plan);
                     continue;
                 }
-                    
+
 
                 // 更新状态为执行中
                 plan.setStatus(AdPlanStatus.IN_PROGRESS);
                 adPlanService.updateById(plan);
-                
+
                 // 根据 adType 构建请求
                 AdgroupsAddRequest request = buildAdgroupRequest(plan);
-                
+
                 // 创建广告组
                 log.info("调用腾讯API创建广告组,accountId={}, orgAccountId={}", plan.getAccountId(), Constant.MAIN_ORG_ACCOUNT_ID);
                 AdgroupsAddResponseData response = tencentAdsAdgroupService.createAdgroup(
                     jobNumber, Constant.MAIN_ORG_ACCOUNT_ID, request
                 );
-                
+
                 // 更新 adgroupId
                 plan.setAdgroupId(response.getAdgroupId());
                 log.info("广告组创建成功,adgroupId={}", response.getAdgroupId());
@@ -242,14 +304,14 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                 plan.setFailReason("");
                 plan.setSuccessTime(LocalDateTime.now());
                 adPlanService.updateById(plan);
-                
+
                 // 创建动态创意
                 if (plan.getAdgroupId() > 0) {
                     createDynamicCreatives(plan, jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
                 }
-                
+
                 log.info("广告计划处理成功,planId={}, adgroupId={}", plan.getId(), plan.getAdgroupId());
-                
+
             } catch (Exception e) {
                 // 记录失败
                 log.error("创建广告失败, planId={}", plan.getId(), e);
@@ -258,13 +320,13 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                 adPlanService.updateById(plan);
             }
         }
-        
+
         // 更新 AdPlanConf 状态
         updateAdPlanConfStatus(confId, plans);
-        
+
         log.info("异步对接腾讯接口完成,confId={}", confId);
     }
-    
+
     /**
      * 构建广告组请求
      */
@@ -298,19 +360,19 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             );
         }
     }
-    
+
     /**
      * 创建动态创意
      */
-    private void createDynamicCreatives(AdPlan plan, String jobNumber, Long orgAccountId) 
+    private void createDynamicCreatives(AdPlan plan, String jobNumber, Long orgAccountId)
         throws Exception {
-        
+
         TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, orgAccountId);
-        
+
         for (AdPlanCreative creative : plan.getAdCreatives()) {
             try {
                 log.info("开始创建动态创意,planId={}, creativeName={}", plan.getId(), creative.getCreativeName());
-                
+
                 if (creative.getCreativeId() > 0 || creative.getStatus() == AdPlanStatus.SUCCESS)
                     continue;
 
@@ -335,12 +397,12 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                         }
                     }
                 }
-                
+
                 if (creative.getBrandImageId() == 0) {
                     // 上传品牌形象
                     String brandImageUrl = creative.getBrandImageUrl();
                     String brandName = creative.getBrandName();
-                    if (brandImageUrl != null && !brandImageUrl.isEmpty() 
+                    if (brandImageUrl != null && !brandImageUrl.isEmpty()
                         && brandName != null && !brandName.isEmpty()) {
                         try {
                             log.info("开始上传品牌形象,URL: {}, 品牌名称: {}", brandImageUrl, brandName);
@@ -354,24 +416,24 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                         }
                     }
                 }
-                
+
 
                 // 构建动态创意请求
                 DynamicCreativesAddRequest request = buildDynamicCreativeRequest(plan, creative);
-                
+
                 // 调用API创建
-                DynamicCreativesAddResponseData response = 
+                DynamicCreativesAddResponseData response =
                     tencentAds.dynamicCreatives().dynamicCreativesAdd(request);
-                
+
                 // 更新创意ID和状态
                 creative.setCreativeId(response.getDynamicCreativeId());
                 creative.setStatus(AdPlanStatus.SUCCESS);
                 creative.setFailReason("");
                 creative.setSuccessTime(LocalDateTime.now());
                 adPlanCreativeService.updateById(creative);
-                
+
                 log.info("动态创意创建成功,creativeId={}", response.getDynamicCreativeId());
-                
+
             } catch (Exception e) {
                 log.error("创建动态创意失败, creativeId={}", creative.getId(), e);
                 creative.setStatus(AdPlanStatus.FAILED);
@@ -381,7 +443,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             }
         }
     }
-    
+
     /**
      * 构建动态创意请求
      */
@@ -392,61 +454,61 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
         request.setDynamicCreativeName(creative.getCreativeName());
         request.setDeliveryMode(DeliveryMode.COMPONENT);
         request.setDynamicCreativeType(DynamicCreativeType.PROGRAM);
-        
+
         CreativeComponents components = new CreativeComponents();
-        
+
         // 标题组件 (固定标题)
         components.setTitle(buildTitleComponents(creative.getCreativeText()));
-        
+
         // 文案组件 (固定文案)
         components.setDescription(buildDescriptionComponents(creative.getCreativeText()));
-        
+
         // 图片组件 (从 creative.imageId)
         components.setImage(buildImageComponents(creative.getImageId()));
-        
+
         // 品牌形象 (固定品牌)
         components.setBrand(buildBrandComponents(creative.getBrandImageId(), creative.getBrandName()));
-        
+
         // 落地页 (从 creative.pageIds, 逗号分隔)
         components.setMainJumpInfo(buildJumpInfoComponents(creative.getPageIds()));
-        
+
         // 行动按钮 (数量与落地页一致, 文案统一)
         components.setActionButton(buildActionButtonComponents(creative.getPageIds(), creative.getButtonText()));
-        
+
         request.setCreativeComponents(components);
         return request;
     }
-    
+
     /**
      * 构建标题组件
      */
     private List<TitleComponent> buildTitleComponents(String creativeText) {
         List<TitleComponent> titles = new ArrayList<>();
-        
+
         TitleComponent title1 = new TitleComponent();
         TitleStruct titleValue1 = new TitleStruct();
         titleValue1.setContent(creativeText);
         title1.setValue(titleValue1);
         titles.add(title1);
-        
+
         return titles;
     }
-    
+
     /**
      * 构建文案组件
      */
     private List<DescriptionComponent> buildDescriptionComponents(String creativeText) {
         List<DescriptionComponent> descriptions = new ArrayList<>();
-        
+
         DescriptionComponent desc1 = new DescriptionComponent();
         DescriptionStruct descValue1 = new DescriptionStruct();
         descValue1.setContent(creativeText);
         desc1.setValue(descValue1);
         descriptions.add(desc1);
-        
+
         return descriptions;
     }
-    
+
     /**
      * 构建图片组件
      */
@@ -457,40 +519,40 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
         imageValue.setImageId(String.valueOf(imageId));
         image.setValue(imageValue);
         images.add(image);
-        
+
         return images;
     }
-    
+
     /**
      * 构建品牌组件
      */
     private List<BrandComponent> buildBrandComponents(Long brandImageId, String brandName) {
         List<BrandComponent> brands = new ArrayList<>();
-        
+
         BrandComponent brand = new BrandComponent();
         BrandStruct brandValue = new BrandStruct();
         brandValue.setBrandName(brandName);
         brandValue.setBrandImageId(String.valueOf(brandImageId));
         brand.setValue(brandValue);
         brands.add(brand);
-        
+
         return brands;
     }
-    
+
     /**
      * 构建落地页组件
      */
     private List<JumpinfoComponent> buildJumpInfoComponents(String pageIds) {
         List<JumpinfoComponent> jumpInfoList = new ArrayList<>();
-        
+
         if (pageIds == null || pageIds.isEmpty()) {
             return jumpInfoList;
         }
-        
+
         String[] pageIdArray = pageIds.split(",");
         for (String pageIdStr : pageIdArray) {
             Long pageId = Long.parseLong(pageIdStr.trim());
-            
+
             JumpinfoComponent jumpinfo = new JumpinfoComponent();
             JumpinfoStruct jumpinfoValue = new JumpinfoStruct();
             PageSpec pageSpec = new PageSpec();
@@ -502,20 +564,20 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             jumpinfo.setValue(jumpinfoValue);
             jumpInfoList.add(jumpinfo);
         }
-        
+
         return jumpInfoList;
     }
-    
+
     /**
      * 构建行动按钮组件
      */
     private List<ActionButtonComponent> buildActionButtonComponents(String pageIds, String buttonText) {
         List<ActionButtonComponent> actionButtons = new ArrayList<>();
-        
+
         if (pageIds == null || pageIds.isEmpty()) {
             return actionButtons;
         }
-        
+
         String[] pageIdArray = pageIds.split(",");
         for (int i = 0; i < pageIdArray.length; i++) {
             ActionButtonComponent actionButton = new ActionButtonComponent();
@@ -524,10 +586,10 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             actionButton.setValue(actionButtonValue);
             actionButtons.add(actionButton);
         }
-        
+
         return actionButtons;
     }
-    
+
     /**
      * 更新 AdPlanConf 状态
      */
@@ -536,14 +598,14 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
         int totalCount = plans.size();
         int successCount = (int) plans.stream().filter(p -> AdPlanStatus.SUCCESS.equals(p.getStatus())).count();
         int failCount = totalCount - successCount;
-        
+
         // 更新配置状态
         AdPlanConf conf = this.getById(confId);
         if (conf == null) {
             log.warn("未找到广告计划配置,confId={}", confId);
             return;
         }
-        
+
         if (failCount == 0) {
             conf.setStatus(AdPlanStatus.SUCCESS); // 全部成功
             conf.setSuccessTime(LocalDateTime.now());
@@ -555,12 +617,12 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             conf.setStatus(AdPlanStatus.PART_FAILED); // 部分失败
             conf.setFailReason(String.format("成功%d个,失败%d个", successCount, failCount));
         }
-        
+
         this.updateById(conf);
-        log.info("更新广告计划配置状态,confId={}, status={}, 成功:{}, 失败:{}", 
+        log.info("更新广告计划配置状态,confId={}, status={}, 成功:{}, 失败:{}",
             confId, conf.getStatus(), successCount, failCount);
     }
-    
+
     /**
      * 截断错误信息到200字符
      */
@@ -570,15 +632,15 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
         }
         return error.length() > 200 ? error.substring(0, 200) + "..." : error;
     }
-    
+
     /**
      * 计算开始日期 (明天)
      */
     private String calculateBeginDate() {
         return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
     }
-    
-    
+
+
     /**
      * 解析定向配置ID
      */
@@ -593,7 +655,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             return 1;
         }
     }
-    
+
     /**
      * 解析站点配置ID
      */
@@ -608,10 +670,10 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             return 1;
         }
     }
-    
+
     /**
      * 从URL下载图片并上传到腾讯广告
-     * 
+     *
      * @param tencentAds 腾讯广告SDK实例
      * @param accountId 账户ID
      * @param imageUrl 图片URL
@@ -624,14 +686,14 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
         try {
             // 1. 从URL下载图片到临时文件
             tempFile = downloadFileFromUrl(imageUrl);
-            
+
             // 2. 读取文件并转Base64
             byte[] imageBytes = Files.readAllBytes(tempFile.toPath());
             String base64Image = Base64.getEncoder().encodeToString(imageBytes);
-            
+
             // 3. 计算MD5签名
             String signature = calculateMD5(imageBytes);
-            
+
             // 4. 调用腾讯广告API上传图片
             ImagesAddResponseData response = tencentAds.images()
                 .imagesAdd(
@@ -647,16 +709,16 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                     null,                  // resizeHeight
                     null                   // resizeFileSize
                 );
-            
+
             if (response == null || response.getImageId() == null) {
                 throw new Exception("图片上传返回结果为空");
             }
-            
-            log.info("图片上传成功,imageId: {}, 宽度: {}px, 高度: {}px", 
+
+            log.info("图片上传成功,imageId: {}, 宽度: {}px, 高度: {}px",
                 response.getImageId(), response.getImageWidth(), response.getImageHeight());
-            
+
             return Long.parseLong(response.getImageId());
-            
+
         } finally {
             // 5. 删除临时文件
             if (tempFile != null && tempFile.exists()) {
@@ -664,10 +726,10 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             }
         }
     }
-    
+
     /**
      * 从URL下载品牌形象并上传到腾讯广告
-     * 
+     *
      * @param tencentAds 腾讯广告SDK实例
      * @param accountId 账户ID
      * @param brandImageUrl 品牌形象图片URL
@@ -680,7 +742,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
         try {
             // 1. 从URL下载图片到临时文件
             tempFile = downloadFileFromUrl(brandImageUrl);
-            
+
             // 2. 调用腾讯广告API创建品牌形象
             BrandAddResponseData response = tencentAds.brand()
                 .brandAdd(
@@ -688,16 +750,16 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                     brandName,      // name
                     tempFile        // brandImageFile
                 );
-            
+
             if (response == null || response.getImageId() == null) {
                 throw new Exception("品牌形象上传返回结果为空");
             }
-            
-            log.info("品牌形象上传成功,imageId: {}, 品牌名称: {}, 宽度: {}px, 高度: {}px", 
+
+            log.info("品牌形象上传成功,imageId: {}, 品牌名称: {}, 宽度: {}px, 高度: {}px",
                 response.getImageId(), response.getName(), response.getWidth(), response.getHeight());
-            
+
             return Long.parseLong(response.getImageId());
-            
+
         } finally {
             // 3. 删除临时文件
             if (tempFile != null && tempFile.exists()) {
@@ -705,10 +767,10 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             }
         }
     }
-    
+
     /**
      * 从URL下载文件到临时文件
-     * 
+     *
      * @param fileUrl 文件URL
      * @return 临时文件
      * @throws Exception 下载失败
@@ -717,12 +779,12 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
         HttpURLConnection connection = null;
         InputStream inputStream = null;
         FileOutputStream outputStream = null;
-        
+
         try {
             // 创建临时文件
             String suffix = getFileSuffix(fileUrl);
             File tempFile = File.createTempFile("gdt_upload_", suffix);
-            
+
             // 打开HTTP连接
             URL url = new URL(fileUrl);
             connection = (HttpURLConnection) url.openConnection();
@@ -730,25 +792,25 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             connection.setReadTimeout(30000);
             connection.setRequestMethod("GET");
             connection.connect();
-            
+
             int responseCode = connection.getResponseCode();
             if (responseCode != HttpURLConnection.HTTP_OK) {
                 throw new Exception("下载文件失败,HTTP状态码: " + responseCode);
             }
-            
+
             // 读取文件内容
             inputStream = connection.getInputStream();
             outputStream = new FileOutputStream(tempFile);
-            
+
             byte[] buffer = new byte[8192];
             int bytesRead;
             while ((bytesRead = inputStream.read(buffer)) != -1) {
                 outputStream.write(buffer, 0, bytesRead);
             }
-            
+
             log.info("文件下载成功,URL: {}, 大小: {} bytes", fileUrl, tempFile.length());
             return tempFile;
-            
+
         } finally {
             if (outputStream != null) {
                 try { outputStream.close(); } catch (Exception e) { }
@@ -761,27 +823,27 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
             }
         }
     }
-    
+
     /**
      * 获取文件后缀名
-     * 
+     *
      * @param fileUrl 文件URL
      * @return 文件后缀名(包含点号,如:.jpg)
      */
     private String getFileSuffix(String fileUrl) {
         int lastDotIndex = fileUrl.lastIndexOf('.');
         int lastSlashIndex = fileUrl.lastIndexOf('/');
-        
+
         if (lastDotIndex > lastSlashIndex && lastDotIndex < fileUrl.length() - 1) {
             return fileUrl.substring(lastDotIndex);
         }
-        
+
         return ".jpg"; // 默认后缀
     }
-    
+
     /**
      * 计算字节数组的MD5签名
-     * 
+     *
      * @param bytes 字节数组
      * @return MD5签名(32位小写字符串)
      * @throws Exception 计算失败

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