소스 검색

feat:新模版

pudongliang 4 달 전
부모
커밋
86ee441278
38개의 변경된 파일398개의 추가작업 그리고 163개의 파일을 삭제
  1. 0 1
      .gitignore
  2. 2 2
      pom.xml
  3. 1 1
      portal/dist/assets/accountGroup-D7rbsEFD.js
  4. 1 0
      portal/dist/assets/adPlanConf-CI3n-C42.js
  5. 0 1
      portal/dist/assets/adPlanConf-DA-fk5eY.js
  6. 1 1
      portal/dist/assets/auth-BZSOUw1f.js
  7. 1 1
      portal/dist/assets/conversion-BUAwWA1R.js
  8. 0 0
      portal/dist/assets/detail-3ik5T0l5.js
  9. 0 0
      portal/dist/assets/detail-CSztW6DS.js
  10. 1 1
      portal/dist/assets/drama-DbDVW1CF.js
  11. 0 0
      portal/dist/assets/index-94JXDX4l.js
  12. 0 0
      portal/dist/assets/index-B5RC8pyB.js
  13. 0 0
      portal/dist/assets/index-B9EjsjRH.js
  14. 0 0
      portal/dist/assets/index-CPOxlP7O.js
  15. 0 0
      portal/dist/assets/index-Cipr3pLQ.js
  16. 0 0
      portal/dist/assets/index-CxUHcITU.js
  17. 0 0
      portal/dist/assets/index-DQS9C_sS.js
  18. 0 0
      portal/dist/assets/index-DmiIrrfi.js
  19. 0 0
      portal/dist/assets/index-N7t_qe4j.js
  20. 1 1
      portal/dist/assets/index-_BAzpnfB.js
  21. 0 0
      portal/dist/assets/index-e_ZWN26u.js
  22. 0 0
      portal/dist/assets/index-kjG_KYJD.js
  23. 0 0
      portal/dist/assets/index-kvWQh56_.js
  24. 1 1
      portal/dist/assets/ossFile-Bf1xorcM.js
  25. 0 0
      portal/dist/assets/request-CXIq51ov.js
  26. 1 1
      portal/dist/assets/user-xPLDcMxJ.js
  27. 1 1
      portal/dist/index.html
  28. 16 0
      portal/src/api/adPlanConf.js
  29. 1 1
      portal/src/utils/request.js
  30. 50 2
      portal/src/views/ad-plan-conf/detail.vue
  31. 18 7
      portal/src/views/ad-plan-conf/index.vue
  32. 17 13
      src/main/java/com/moka/gdtauto/common/Result.java
  33. 21 4
      src/main/java/com/moka/gdtauto/controller/AdPlanConfController.java
  34. 11 6
      src/main/java/com/moka/gdtauto/service/AdPlanConfService.java
  35. 185 117
      src/main/java/com/moka/gdtauto/service/impl/AdPlanConfServiceImpl.java
  36. 7 0
      src/main/java/com/moka/gdtauto/util/SiteConfUtil.java
  37. 57 0
      src/main/java/com/moka/gdtauto/util/TargetConfUtil.java
  38. 4 1
      src/test/java/com/moka/gdtauto/service/CreateAdGroupTest.java

+ 0 - 1
.gitignore

@@ -22,7 +22,6 @@ target/
 ### NetBeans ###
 ### NetBeans ###
 /nbproject/private/
 /nbproject/private/
 /nbbuild/
 /nbbuild/
-/dist/
 /nbdist/
 /nbdist/
 /.nb-gradle/
 /.nb-gradle/
 build/
 build/

+ 2 - 2
pom.xml

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

+ 1 - 1
portal/dist/assets/accountGroup-BzAGj6fn.js → portal/dist/assets/accountGroup-D7rbsEFD.js

@@ -1 +1 @@
-import{r as u}from"./request-Dj2m30gK.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-CXIq51ov.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-CI3n-C42.js

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

+ 0 - 1
portal/dist/assets/adPlanConf-DA-fk5eY.js

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

@@ -1 +1 @@
-import{r as t}from"./request-Dj2m30gK.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-CXIq51ov.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-BOLK9Y_d.js → portal/dist/assets/conversion-BUAwWA1R.js

@@ -1 +1 @@
-import{r as t}from"./request-Dj2m30gK.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-CXIq51ov.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-3ik5T0l5.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/detail-CSztW6DS.js


+ 1 - 1
portal/dist/assets/drama-ZEX6Zd-a.js → portal/dist/assets/drama-DbDVW1CF.js

@@ -1 +1 @@
-import{r as t}from"./request-Dj2m30gK.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-CXIq51ov.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/index-94JXDX4l.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-B5RC8pyB.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-B9EjsjRH.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-CPOxlP7O.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-Cipr3pLQ.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-CxUHcITU.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-DQS9C_sS.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-DmiIrrfi.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-N7t_qe4j.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
portal/dist/assets/index-_BAzpnfB.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-e_ZWN26u.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-kjG_KYJD.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
portal/dist/assets/index-kvWQh56_.js


+ 1 - 1
portal/dist/assets/ossFile-BGmv9sY9.js → portal/dist/assets/ossFile-Bf1xorcM.js

@@ -1 +1 @@
-import{r as t}from"./request-Dj2m30gK.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-CXIq51ov.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-CXIq51ov.js


+ 1 - 1
portal/dist/assets/user-DofYv8ML.js → portal/dist/assets/user-xPLDcMxJ.js

@@ -1 +1 @@
-import{r as e}from"./request-Dj2m30gK.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-CXIq51ov.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" />
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>腾讯广告自动化管理后台</title>
     <title>腾讯广告自动化管理后台</title>
-    <script type="module" crossorigin src="/assets/index-CKvrzot2.js"></script>
+    <script type="module" crossorigin src="/assets/index-_BAzpnfB.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-CdDwsIxv.css">
     <link rel="stylesheet" crossorigin href="/assets/index-CdDwsIxv.css">
   </head>
   </head>
   <body>
   <body>

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

@@ -116,3 +116,19 @@ export function getAdPlanCreativesByPlanId(planId) {
     method: 'get'
     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 实例
 // 创建 axios 实例
 const request = axios.create({
 const request = axios.create({
   baseURL: '/api',
   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>
           <el-icon><Document /></el-icon>
           配置信息
           配置信息
         </h2>
         </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-icon><Refresh /></el-icon>
             刷新状态</el-button>
             刷新状态</el-button>
+        </div>
       </div>
       </div>
       
       
       <div v-if="confDetail">
       <div v-if="confDetail">
@@ -159,6 +164,9 @@
           广告计划列表
           广告计划列表
         </h2>
         </h2>
         <div>
         <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-button type="primary" @click="handleSystemRefresh(confDetail.id)">
             <el-icon><Refresh /></el-icon>
             <el-icon><Refresh /></el-icon>
             同步平台状态</el-button>
             同步平台状态</el-button>
@@ -213,7 +221,9 @@
         row-key="id"
         row-key="id"
         :expand-row-keys="expandedRows"
         :expand-row-keys="expandedRows"
         @expand-change="handleExpandChange"
         @expand-change="handleExpandChange"
+        @selection-change="handleSelectionChange"
       >
       >
+        <el-table-column type="selection"  width="55" />
         <el-table-column type="expand">
         <el-table-column type="expand">
           <template #default="{ row }">
           <template #default="{ row }">
             <div class="p-4 bg-gray-50">
             <div class="p-4 bg-gray-50">
@@ -366,7 +376,9 @@ import {
   getAdPlanList, 
   getAdPlanList, 
   reGeneratePlan,
   reGeneratePlan,
   getAdPlanCreativesByPlanId,
   getAdPlanCreativesByPlanId,
-  syncSystemStatus
+  syncSystemStatus,
+    suspendByConfId,
+  suspendByPlanIds
 } from '@/api/adPlanConf'
 } from '@/api/adPlanConf'
 
 
 const route = useRoute()
 const route = useRoute()
@@ -382,6 +394,7 @@ const adPlanList = ref([])
 const planLoading = ref(false)
 const planLoading = ref(false)
 const total = ref(0)
 const total = ref(0)
 const expandedRows = ref([])
 const expandedRows = ref([])
+const selectedRows = ref([])
 
 
 // 查询参数
 // 查询参数
 const queryParams = ref({
 const queryParams = ref({
@@ -466,6 +479,10 @@ const fetchAdPlans = async () => {
   }
   }
 }
 }
 
 
+const handleSelectionChange = (rows) => {
+  selectedRows.value = rows
+}
+
 // 处理展开行变化
 // 处理展开行变化
 const handleExpandChange = async (row, expandedRowsArray) => {
 const handleExpandChange = async (row, expandedRowsArray) => {
   expandedRows.value = expandedRowsArray.map(r => r.id)
   expandedRows.value = expandedRowsArray.map(r => r.id)
@@ -561,6 +578,37 @@ const handleRefresh = async () => {
   await fetchAdPlans()
   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) => {
 const handleSystemRefresh = async (confId) => {
   try {
   try {
     const res = await syncSystemStatus(confId)
     const res = await syncSystemStatus(confId)

+ 18 - 7
portal/src/views/ad-plan-conf/index.vue

@@ -63,6 +63,7 @@
         <el-table-column prop="conversionId" label="转化ID" width="120" />
         <el-table-column prop="conversionId" label="转化ID" width="120" />
         <el-table-column prop="dramaId" label="短剧ID" width="120" />
         <el-table-column prop="dramaId" label="短剧ID" width="120" />
         <el-table-column prop="dramaAmount" label="短剧数量" width="100" />
         <el-table-column prop="dramaAmount" label="短剧数量" width="100" />
+        <el-table-column prop="sites" label="版位模版" width="100" />
         <el-table-column prop="wxCorpid" label="企微主体ID" width="150" show-overflow-tooltip />
         <el-table-column prop="wxCorpid" label="企微主体ID" width="150" show-overflow-tooltip />
         <el-table-column label="创建时间" width="180">
         <el-table-column label="创建时间" width="180">
           <template #default="{ row }">
           <template #default="{ row }">
@@ -75,10 +76,10 @@
               <el-icon><Edit /></el-icon>
               <el-icon><Edit /></el-icon>
               编辑
               编辑
             </el-button>
             </el-button>
-            <el-button type="danger" size="small" link @click="handleDelete(row)">
+            <!-- <el-button type="danger" size="small" link @click="handleDelete(row)">
               <el-icon><Delete /></el-icon>
               <el-icon><Delete /></el-icon>
               删除
               删除
-            </el-button>
+            </el-button> -->
             <el-button size="small" :disabled="row.status  === 1" link @click="goToDetail(row)">
             <el-button size="small" :disabled="row.status  === 1" link @click="goToDetail(row)">
               <el-icon><View /></el-icon>
               <el-icon><View /></el-icon>
               详情
               详情
@@ -194,7 +195,7 @@
             placeholder="多个定向ID用逗号分隔,例如:1,2,3"
             placeholder="多个定向ID用逗号分隔,例如:1,2,3"
           />
           />
           <el-collapse class="mt-1 text-xs text-gray-500" >
           <el-collapse class="mt-1 text-xs text-gray-500" >
-            <el-collapse-item title="定向模版ID配置说明">
+            <el-collapse-item title="定向模版ID配置说明(新模版【2】)">
               <div>【1】</div>
               <div>【1】</div>
               <p class="whitespace-pre-line">
               <p class="whitespace-pre-line">
                 地域:中国(常住地)<br />
                 地域:中国(常住地)<br />
@@ -204,6 +205,15 @@
                 排除人群:44106830,44107982,43712084,44241932<br />
                 排除人群:44106830,44107982,43712084,44241932<br />
                 不限:学历,婚恋育儿状态,用户消费能力,应用安装,设备品牌型号,操作系统版本,联网方式,移动运营商,定向人群,设备价格,微信再营销
                 不限:学历,婚恋育儿状态,用户消费能力,应用安装,设备品牌型号,操作系统版本,联网方式,移动运营商,定向人群,设备价格,微信再营销
               </p>
               </p>
+              <div style="color: red;">【2】</div>
+              <p style="color: red;" class="whitespace-pre-line">
+                地域:中国(常住地)<br />
+                年龄:大于等于50岁<br />
+                性别:男性<br />
+                排除已转化用户:同集团(自定义转化行为:指定页面曝光,加企业微信客服)(转化时间区间:1个月)<br />
+                排除人群:44241932, 44135417, 44107982, 43712084<br />
+                不限:学历,婚恋育儿状态,用户消费能力,应用安装,设备品牌型号,操作系统版本,联网方式,移动运营商,定向人群,设备价格,微信再营销
+              </p>
             </el-collapse-item>
             </el-collapse-item>
           </el-collapse>
           </el-collapse>
         </el-form-item>
         </el-form-item>
@@ -216,7 +226,7 @@
             placeholder="多个版位ID用逗号分隔,例如:1,2,3"
             placeholder="多个版位ID用逗号分隔,例如:1,2,3"
           />
           />
           <el-collapse class="mt-1 text-xs text-gray-500" >
           <el-collapse class="mt-1 text-xs text-gray-500" >
-            <el-collapse-item title="版位模版ID配置说明">
+            <el-collapse-item title="版位模版ID配置说明(新模版【11】)">
               <div>1:[KANDIAN, QQ_MUSIC_GAME, TENCENT_NEWS, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>1:[KANDIAN, QQ_MUSIC_GAME, TENCENT_NEWS, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>2:[QQ_MUSIC_GAME, TENCENT_NEWS, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>2:[QQ_MUSIC_GAME, TENCENT_NEWS, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>3:[TENCENT_NEWS, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>3:[TENCENT_NEWS, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
@@ -227,6 +237,7 @@
               <div>8:[KANDIAN, QQ_MUSIC_GAME, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>8:[KANDIAN, QQ_MUSIC_GAME, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>9:[KANDIAN, TENCENT_NEWS, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>9:[KANDIAN, TENCENT_NEWS, TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>10:[TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
               <div>10:[TENCENT_VIDEO, SEARCH_SCENE, MOBILE_UNION, WECHAT, WECHAT_PLUGIN]</div>
+              <div style="color: red;">* 11:[SITE_SET_WECHAT, SITE_SET_WECHAT_PLUGIN, SITE_SET_SEARCH_SCENE]</div>
             </el-collapse-item>
             </el-collapse-item>
           </el-collapse>
           </el-collapse>
         </el-form-item>
         </el-form-item>
@@ -758,9 +769,9 @@ const handleAdTypeChange = () => {
 }
 }
 
 
 const handleCreate = () => {
 const handleCreate = () => {
-  //1,2,3,4,5,6,7,8,9,10
-  //随机一个1-10
-  const site = Math.floor(Math.random() * 10) + 1
+  //1,2,3,4,5,6,7,8,9,10,11
+  //随机一个1-11
+  const site = Math.floor(Math.random() * 11) + 1
   dialogTitle.value = '创建配置'
   dialogTitle.value = '创建配置'
   editingId.value = null
   editingId.value = null
   formData.value = {
   formData.value = {

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

@@ -5,7 +5,7 @@ import java.io.Serializable;
 
 
 /**
 /**
  * 统一响应结果封装类
  * 统一响应结果封装类
- * 
+ *
  * @author moka
  * @author moka
  * @since 2026-02-06
  * @since 2026-02-06
  */
  */
@@ -72,7 +72,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 成功响应(无数据)
      * 成功响应(无数据)
-     * 
+     *
      * @param message 成功消息
      * @param message 成功消息
      * @return Result实例
      * @return Result实例
      */
      */
@@ -85,7 +85,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 成功响应(带数据)
      * 成功响应(带数据)
-     * 
+     *
      * @param data 响应数据
      * @param data 响应数据
      * @param message 成功消息
      * @param message 成功消息
      * @return Result实例
      * @return Result实例
@@ -100,7 +100,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 成功响应(仅数据,默认消息"操作成功")
      * 成功响应(仅数据,默认消息"操作成功")
-     * 
+     *
      * @param data 响应数据
      * @param data 响应数据
      * @return Result实例
      * @return Result实例
      */
      */
@@ -108,9 +108,13 @@ public class Result<T> implements Serializable {
         return success(data, "操作成功");
         return success(data, "操作成功");
     }
     }
 
 
+    public static <T> Result<T> success() {
+        return success(null, "操作成功");
+    }
+
     /**
     /**
      * 失败响应
      * 失败响应
-     * 
+     *
      * @param message 失败消息
      * @param message 失败消息
      * @return Result实例
      * @return Result实例
      */
      */
@@ -123,7 +127,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 失败响应(带数据)
      * 失败响应(带数据)
-     * 
+     *
      * @param data 响应数据
      * @param data 响应数据
      * @param message 失败消息
      * @param message 失败消息
      * @return Result实例
      * @return Result实例
@@ -138,7 +142,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 错误响应(别名方法,与fail功能相同)
      * 错误响应(别名方法,与fail功能相同)
-     * 
+     *
      * @param message 错误消息
      * @param message 错误消息
      * @return Result实例
      * @return Result实例
      */
      */
@@ -148,7 +152,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 错误响应(带数据,别名方法)
      * 错误响应(带数据,别名方法)
-     * 
+     *
      * @param data 响应数据
      * @param data 响应数据
      * @param message 错误消息
      * @param message 错误消息
      * @return Result实例
      * @return Result实例
@@ -159,7 +163,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 设置数量
      * 设置数量
-     * 
+     *
      * @param count 数量
      * @param count 数量
      * @return Result实例
      * @return Result实例
      */
      */
@@ -170,7 +174,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 设置备注
      * 设置备注
-     * 
+     *
      * @param note 备注信息
      * @param note 备注信息
      * @return Result实例
      * @return Result实例
      */
      */
@@ -181,7 +185,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 设置使用说明
      * 设置使用说明
-     * 
+     *
      * @param usage 使用说明
      * @param usage 使用说明
      * @return Result实例
      * @return Result实例
      */
      */
@@ -192,7 +196,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 设置分页信息
      * 设置分页信息
-     * 
+     *
      * @param page 当前页码
      * @param page 当前页码
      * @param pageSize 每页数量
      * @param pageSize 每页数量
      * @param total 总记录数
      * @param total 总记录数
@@ -208,7 +212,7 @@ public class Result<T> implements Serializable {
 
 
     /**
     /**
      * 设置总记录数(用于统计)
      * 设置总记录数(用于统计)
-     * 
+     *
      * @param total 总记录数
      * @param total 总记录数
      * @return Result实例
      * @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.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.constraints.NotNull;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 
 
@@ -41,7 +42,7 @@ import java.time.format.DateTimeFormatter;
 
 
 /**
 /**
  * 广告计划配置管理控制器
  * 广告计划配置管理控制器
- * 
+ *
  * @author moka
  * @author moka
  * @since 2026-02-24
  * @since 2026-02-24
  */
  */
@@ -60,6 +61,22 @@ public class AdPlanConfController {
     private final AccountGroupService accountGroupService;
     private final AccountGroupService accountGroupService;
     private final TencentAssetDramaMapper tencentAssetDramaMapper;
     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,重新创建广告计划配置")
     @Operation(summary = "预创建广告计划配置", description = "根据账户分组ID和配置ID,重新创建广告计划配置")
     @PostMapping("/reCreate")
     @PostMapping("/reCreate")
     public Result<List<AdPlan>> reCreateAdPlanConf(
     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"));
             dramas = tencentAssetDramaMapper.selectList(new LambdaQueryWrapper<TencentAssetDrama>().eq(TencentAssetDrama::getMarketingAssetId, dramaId).orderByDesc(TencentAssetDrama::getId).last("limit 1"));
         }
         }
-    
+
         String targets = request.getTargets();
         String targets = request.getTargets();
         String sites = request.getSites();
         String sites = request.getSites();
         String[] targetArray = targets.split(",");
         String[] targetArray = targets.split(",");
@@ -168,8 +185,8 @@ public class AdPlanConfController {
                 }
                 }
             }
             }
             }
             }
-            
-            
+
+
         }
         }
         return plans;
         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.baomidou.mybatisplus.extension.service.IService;
 import com.moka.gdtauto.entity.AdPlan;
 import com.moka.gdtauto.entity.AdPlan;
 import com.moka.gdtauto.entity.AdPlanConf;
 import com.moka.gdtauto.entity.AdPlanConf;
+import jakarta.validation.constraints.NotNull;
 
 
 /**
 /**
  * 广告计划配置服务接口
  * 广告计划配置服务接口
- * 
+ *
  * @author moka
  * @author moka
  * @since 2026-02-24
  * @since 2026-02-24
  */
  */
@@ -17,7 +18,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
 
     /**
     /**
      * 创建广告计划配置
      * 创建广告计划配置
-     * 
+     *
      * @param adPlanConf 广告计划配置
      * @param adPlanConf 广告计划配置
      * @return 配置ID
      * @return 配置ID
      */
      */
@@ -25,7 +26,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
 
     /**
     /**
      * 更新广告计划配置
      * 更新广告计划配置
-     * 
+     *
      * @param adPlanConf 广告计划配置
      * @param adPlanConf 广告计划配置
      * @return 是否成功
      * @return 是否成功
      */
      */
@@ -33,7 +34,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
 
     /**
     /**
      * 删除广告计划配置
      * 删除广告计划配置
-     * 
+     *
      * @param confId 配置ID
      * @param confId 配置ID
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 是否成功
      * @return 是否成功
@@ -42,7 +43,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
 
     /**
     /**
      * 获取用户的广告计划配置列表(分页)
      * 获取用户的广告计划配置列表(分页)
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @param pageNum 页码
      * @param pageNum 页码
      * @param pageSize 每页大小
      * @param pageSize 每页大小
@@ -52,7 +53,7 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
 
 
     /**
     /**
      * 获取广告计划配置详情
      * 获取广告计划配置详情
-     * 
+     *
      * @param confId 配置ID
      * @param confId 配置ID
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 配置信息
      * @return 配置信息
@@ -64,4 +65,8 @@ public interface AdPlanConfService extends IService<AdPlanConf> {
     void asyncTencentInterface(List<AdPlan> plans);
     void asyncTencentInterface(List<AdPlan> plans);
 
 
     void asyncReGeneratePlan(Long id);
     void asyncReGeneratePlan(Long id);
+
+    void suspendByConfId(@NotNull Long confId);
+
+    void suspendByPlanIds(Long confId, Long planIds);
 }
 }

+ 185 - 117
src/main/java/com/moka/gdtauto/service/impl/AdPlanConfServiceImpl.java

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

+ 7 - 0
src/main/java/com/moka/gdtauto/util/SiteConfUtil.java

@@ -119,6 +119,13 @@ public class SiteConfUtil {
                 "SITE_SET_WECHAT_PLUGIN"
                 "SITE_SET_WECHAT_PLUGIN"
             )
             )
         );
         );
+        siteConfMap.put(11,
+            Arrays.asList(
+                "SITE_SET_WECHAT",
+                "SITE_SET_WECHAT_PLUGIN",
+                "SITE_SET_SEARCH_SCENE"
+            )
+        );
     }
     }
 
 
     public static List<String> getSiteConf(Integer id) {
     public static List<String> getSiteConf(Integer id) {

+ 57 - 0
src/main/java/com/moka/gdtauto/util/TargetConfUtil.java

@@ -21,6 +21,7 @@ public class TargetConfUtil {
 
 
     static {
     static {
         targetingSettingCache.put(1, build1());
         targetingSettingCache.put(1, build1());
+        targetingSettingCache.put(2, build2());
     }
     }
 
 
     public static WriteTargetingSetting getTargetingSetting(Integer id) {
     public static WriteTargetingSetting getTargetingSetting(Integer id) {
@@ -92,4 +93,60 @@ public class TargetConfUtil {
 
 
         return targeting;
         return targeting;
     }
     }
+
+    private static WriteTargetingSetting build2() {
+        WriteTargetingSetting targeting = new WriteTargetingSetting();
+
+        // 地域定向
+        GeoLocations geoLocation = new GeoLocations();
+        List<String> locationTypes = new ArrayList<>();
+        locationTypes.add("LIVE_IN"); // 居住地
+        geoLocation.setLocationTypes(locationTypes);
+        List<Long> regions = new ArrayList<>();
+        regions.add(1156L); // 地域ID:1156(中国)
+        geoLocation.setRegions(regions);
+        targeting.setGeoLocation(geoLocation);
+
+        // 性别定向
+        List<String> gender = new ArrayList<>();
+        gender.add("MALE"); // 男性
+        targeting.setGender(gender);
+
+        // 年龄定向
+        List<AgeStruct> age = new ArrayList<>();
+        AgeStruct ageRange = new AgeStruct();
+        ageRange.setMin(50L);
+        ageRange.setMax(66L);
+        age.add(ageRange);
+        targeting.setAge(age);
+
+        // 指定页面曝光 =? 品牌转化
+        //排除已转化用户:同集团(自定义转化行为:指定页面曝光,加企业微信客服)(转化时间区间:1个月)
+        // 排除已转化人群行为定向,排除已转化用户定向范围
+        ExcludedConvertedAudience excludedConverted = new ExcludedConvertedAudience();
+        // 排除已转化人群行为定向 EXCLUDED_DIMENSION_GROUP 集团
+        excludedConverted.setExcludedDimension(ExcludedDimension.EXCLUDED_DIMENSION_GROUP);
+        
+        //转化行为
+        List<String> conversionBehaviorList = new ArrayList<>();
+        conversionBehaviorList.add("OPTIMIZATIONGOAL_BRAND_CONVERSION"); //品牌转化
+        conversionBehaviorList.add("OPTIMIZATIONGOAL_PAGE_SCAN_CODE"); //加企业微信客服
+        excludedConverted.setConversionBehaviorList(conversionBehaviorList);
+        
+        // 转化时间区间:1个月
+        //排除天数 EXCLUDED_DAY_ONE_MONTH 1 个月
+        excludedConverted.setExcludedDay(ExcludedDay.ONE_MONTH);
+        // 排除已转化人群行为定向,排除已转化用户定向范围
+        targeting.setExcludedConvertedAudience(excludedConverted);
+
+        // 排除自定义人群
+        List<Long> excludedCustomAudience = new ArrayList<>();
+        excludedCustomAudience.add(44241932L);
+        excludedCustomAudience.add(44135417L);
+        excludedCustomAudience.add(44107982L);
+        excludedCustomAudience.add(43712084L);
+        targeting.setExcludedCustomAudience(excludedCustomAudience);
+
+        return targeting;
+    }
 }
 }

+ 4 - 1
src/test/java/com/moka/gdtauto/service/CreateAdGroupTest.java

@@ -9,6 +9,9 @@ import org.springframework.test.context.ActiveProfiles;
 
 
 import java.util.List;
 import java.util.List;
 
 
+/**
+ * mvn test -Dtest=CreateAdGroupTest#createAdGroup -Dspring.profiles.active=test
+ */
 @SpringBootTest(classes = GdtAutoApplication.class)
 @SpringBootTest(classes = GdtAutoApplication.class)
 @ActiveProfiles("test")
 @ActiveProfiles("test")
 public class CreateAdGroupTest {
 public class CreateAdGroupTest {
@@ -20,7 +23,7 @@ public class CreateAdGroupTest {
     
     
     @Test
     @Test
     public void createAdGroup() {
     public void createAdGroup() {
-        AdPlan  adPlan = adPlanService.getById(126);
+        AdPlan  adPlan = adPlanService.getById(7580);
         adPlanConfService.asyncTencentInterface(List.of(adPlan));
         adPlanConfService.asyncTencentInterface(List.of(adPlan));
     }
     }
 }
 }

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.