Bläddra i källkod

feat:同步短剧资源

pudongliang 4 månader sedan
förälder
incheckning
060c2bbf4d
48 ändrade filer med 639 tillägg och 75 borttagningar
  1. 26 0
      .qoder/skills/crud/SKILL.md
  2. 16 0
      for_push.sh
  3. 1 1
      portal/dist/assets/accountGroup-BJssH6-4.js
  4. 1 1
      portal/dist/assets/adPlanConf-DDHyZi2K.js
  5. 1 1
      portal/dist/assets/auth-BZ6E5D1a.js
  6. 1 1
      portal/dist/assets/conversion-DOEK-zRB.js
  7. 0 0
      portal/dist/assets/detail-6kU7pfKW.js
  8. 1 0
      portal/dist/assets/drama-5UzMCiFs.js
  9. 0 0
      portal/dist/assets/index-BFSwNW-w.js
  10. 0 0
      portal/dist/assets/index-BZAMM-Np.js
  11. 0 0
      portal/dist/assets/index-BcVzUPY1.js
  12. 0 0
      portal/dist/assets/index-BeI5MTU7.css
  13. 0 0
      portal/dist/assets/index-BjpgDYP7.js
  14. 0 0
      portal/dist/assets/index-CQZljeCA.js
  15. 0 0
      portal/dist/assets/index-CnCOhbva.js
  16. 0 0
      portal/dist/assets/index-Cqptlhyz.css
  17. 0 1
      portal/dist/assets/index-Crpis3o1.js
  18. 0 0
      portal/dist/assets/index-DTRRt2yb.js
  19. 0 0
      portal/dist/assets/index-DYKUKb8n.js
  20. 0 0
      portal/dist/assets/index-DhxdrWvW.js
  21. 0 0
      portal/dist/assets/index-gF9vvRPH.js
  22. 0 0
      portal/dist/assets/index-tcULDzis.js
  23. 0 0
      portal/dist/assets/index-wYxbwZdZ.js
  24. 1 1
      portal/dist/assets/ossFile-CM3DCBds.js
  25. 0 0
      portal/dist/assets/request-DnCTS0Ee.js
  26. 1 1
      portal/dist/assets/user-BZ8QcBPh.js
  27. 2 2
      portal/dist/index.html
  28. 36 4
      portal/src/api/drama.js
  29. 6 6
      portal/src/router/index.js
  30. 31 4
      portal/src/views/ad-plan-conf/index.vue
  31. 187 32
      portal/src/views/drama/index.vue
  32. 2 2
      portal/vite.config.js
  33. 0 2
      src/main/java/com/moka/gdtauto/GdtAutoApplication.java
  34. 6 0
      src/main/java/com/moka/gdtauto/common/Constant.java
  35. 16 0
      src/main/java/com/moka/gdtauto/config/SchedulingConfig.java
  36. 74 0
      src/main/java/com/moka/gdtauto/controller/AssetDramaSyncConfController.java
  37. 34 4
      src/main/java/com/moka/gdtauto/controller/TencentAssetDramaController.java
  38. 45 0
      src/main/java/com/moka/gdtauto/entity/AssetDramaSyncConf.java
  39. 4 0
      src/main/java/com/moka/gdtauto/entity/TencentAssetDrama.java
  40. 15 0
      src/main/java/com/moka/gdtauto/mapper/AssetDramaSyncConfMapper.java
  41. 49 0
      src/main/java/com/moka/gdtauto/service/AssetDramaSyncConfService.java
  42. 1 2
      src/main/java/com/moka/gdtauto/service/TencentAdsAuthService.java
  43. 7 3
      src/main/java/com/moka/gdtauto/service/TencentAssetDramaService.java
  44. 6 7
      src/main/java/com/moka/gdtauto/service/impl/AdPlanConfServiceImpl.java
  45. 53 0
      src/main/java/com/moka/gdtauto/service/impl/AssetDramaSyncConfServiceImpl.java
  46. 4 0
      src/main/resources/application-test.yml
  47. 4 0
      src/main/resources/mapper/TencentAssetDramaMapper.xml
  48. 8 0
      src/main/resources/sql/create_asset_drama_sync_conf_table.sql

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

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

+ 16 - 0
for_push.sh

@@ -0,0 +1,16 @@
+cd portal/
+echo "========== Building portal =========="
+npm run build
+echo "========== Git adding portal ========== "
+git add .
+cd ../
+git add .
+echo "========== Git status =========="
+git status
+
+// 读参数提交信息
+param1=$1
+
+echo "========== Git commiting =========="
+echo "Commit message: $param1"
+// git commit -m "$param1"

+ 1 - 1
portal/dist/assets/accountGroup-BLSY0F_6.js → portal/dist/assets/accountGroup-BJssH6-4.js

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

+ 1 - 1
portal/dist/assets/adPlanConf-Cl946SEz.js → portal/dist/assets/adPlanConf-DDHyZi2K.js

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

+ 1 - 1
portal/dist/assets/auth-BCrKhoHs.js → portal/dist/assets/auth-BZ6E5D1a.js

@@ -1 +1 @@
-import{r as t}from"./request-Zauzkpwd.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-DnCTS0Ee.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-R6gnIrYC.js → portal/dist/assets/conversion-DOEK-zRB.js

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

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/detail-6kU7pfKW.js


+ 1 - 0
portal/dist/assets/drama-5UzMCiFs.js

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

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-BFSwNW-w.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-BZAMM-Np.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-BcVzUPY1.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-BeI5MTU7.css


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-BjpgDYP7.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-CQZljeCA.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-CnCOhbva.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-Cqptlhyz.css


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 1
portal/dist/assets/index-Crpis3o1.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-DTRRt2yb.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-DYKUKb8n.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-DhxdrWvW.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-gF9vvRPH.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-tcULDzis.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/index-wYxbwZdZ.js


+ 1 - 1
portal/dist/assets/ossFile-CTaupPsJ.js → portal/dist/assets/ossFile-CM3DCBds.js

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

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
portal/dist/assets/request-DnCTS0Ee.js


+ 1 - 1
portal/dist/assets/user-Dx5HXkuH.js → portal/dist/assets/user-BZ8QcBPh.js

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

+ 2 - 2
portal/dist/index.html

@@ -5,8 +5,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-BmDoqHGL.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-Cqptlhyz.css">
+    <script type="module" crossorigin src="/assets/index-Crpis3o1.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-BeI5MTU7.css">
   </head>
   <body>
     <div id="app"></div>

+ 36 - 4
portal/src/api/drama.js

@@ -6,11 +6,11 @@ import request from '@/utils/request'
  * @param {String} phone 手机号
  * @param {Number} accountId 推广账号ID
  */
-export function syncDrama(orgAccountId, accountId) {
+export function syncDrama(accountId) {
   return request({
     url: '/api/gdt/asset/drama/sync',
     method: 'post',
-    params: { orgAccountId, accountId }
+    params: { accountId }
   })
 }
 
@@ -19,11 +19,11 @@ export function syncDrama(orgAccountId, accountId) {
  * @param {Number} page 页码
  * @param {Number} pageSize 每页数量
  */
-export function getDramaList(page = 1, pageSize = 20) {
+export function getDramaList(page = 1, pageSize = 20, dramaName = '') {
   return request({
     url: '/api/gdt/asset/drama/list',
     method: 'get',
-    params: { page, pageSize }
+    params: {dramaName, page, pageSize }
   })
 }
 
@@ -38,3 +38,35 @@ export function getDramaDetail(id) {
     params: { id }
   })
 }
+
+
+export function myDramaAssetList(dramaName) {
+  return request({
+    url: '/api/gdt/asset/drama/myDramaAssets',
+    method: 'get',
+    params: { dramaName }
+  })
+}
+
+export function myConfList() {
+  return request({
+    url: '/api/gdt/asset/drama/sync/conf/myConf',
+    method: 'get'
+  })
+}
+
+export function addConf (data) {
+  return request({
+    url: '/api/gdt/asset/drama/sync/conf/add',
+    method: 'post',
+    data
+  })
+}
+
+export function delConf (id) {
+  return request({
+    url: '/api/gdt/asset/drama/sync/conf/delete',
+    method: 'post',
+    params: { id }
+  })
+}

+ 6 - 6
portal/src/router/index.js

@@ -80,12 +80,12 @@ const routes = [
       //   component: () => import('@/views/adgroup/index.vue'),
       //   meta: { title: '广告组管理', icon: 'DataBoard' }
       // },
-      // {
-      //   path: 'drama',
-      //   name: 'Drama',
-      //   component: () => import('@/views/drama/index.vue'),
-      //   meta: { title: '短剧资产管理', icon: 'VideoPlay' }
-      // },
+      {
+        path: 'drama',
+        name: 'Drama',
+        component: () => import('@/views/drama/index.vue'),
+        meta: { title: '短剧资产管理', icon: 'VideoPlay' }
+      },
       // {
       //   path: 'image',
       //   name: 'Image',

+ 31 - 4
portal/src/views/ad-plan-conf/index.vue

@@ -219,12 +219,20 @@
         </el-form-item>
 
         <el-form-item label="短剧ID" prop="dramaId" v-if="formData.adType === 2">
-          <el-input-number
+          <el-select
+            :disabled="valueDisabled"
             v-model="formData.dramaId"
-            :min="1"
-            placeholder="请输入短剧ID"
             style="width: 100%"
-          />
+            placeholder="请选择短剧"
+            filterable
+          >
+            <el-option
+              v-for="drama in dramaList"
+              :key="drama.id"
+              :label="`${drama.marketingAssetName}[${drama.marketingAssetId}]`"
+              :value="drama.marketingAssetId"
+            />
+          </el-select>
         </el-form-item>
 
         <el-form-item label="落地页IDs" prop="pageIds">
@@ -513,6 +521,7 @@ import {
   reCreateAdPlanConf,
   generatePlan
 } from '@/api/adPlanConf'
+import { myDramaAssetList } from '@/api/drama'
 import { getConversions } from '@/api/conversion'
 import { getAccountGroupListAll } from '@/api/accountGroup'
 import { getFileList } from '@/api/ossFile'
@@ -533,6 +542,8 @@ const currentPage = ref(1)
 const pageSize = ref(10)
 const total = ref(0)
 
+const dramaList = ref([])
+
 // 对话框
 const dialogVisible = ref(false)
 const dialogTitle = ref('创建配置')
@@ -970,6 +981,21 @@ const formatFileSize = (bytes) => {
   return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
 }
 
+const loadDramaList = async () => {
+  try {
+    const res = await myDramaAssetList()
+    if (res.success) {
+      dramaList.value = res.data
+    } else {
+      ElMessage.error(res.message || '加载短剧列表失败')
+    }
+  } catch (error) {
+    console.error('加载短剧列表失败:', error)
+    ElMessage.error('加载短剧列表失败')
+  } finally {
+  }
+}
+
 // 监听分页变化
 watch([currentPage, pageSize], () => {
   loadConfList()
@@ -980,6 +1006,7 @@ onMounted(() => {
   loadConfList()
   loadConversions()
   loadAccountGroupList()
+  loadDramaList()
 })
 </script>
 

+ 187 - 32
portal/src/views/drama/index.vue

@@ -11,31 +11,14 @@
             短剧资产列表
           </h2>
           <div class="flex items-center gap-3">
-            <el-select
-              v-model="orgAccountId"
-              placeholder="选择组织账户"
-              clearable
-              filterable
-              style="width: 250px"
-            >
-              <el-option
-                v-for="auth in orgAccounts"
-                :key="auth.accountId"
-                :label="`${auth.accountId} (${auth.accountType || ''})`"
-                :value="auth.accountId"
-              />
+            <el-button type="primary" @click="showSyncAccountDialog">我的同步账号管理</el-button>
+            <el-select v-model="accountId" placeholder="选择同步账号" clearable filterable style="width: 250px">
+              <el-option v-for="conf in confList" :key="conf.id" :label="`${conf.accountId}`" :value="conf.accountId" />
             </el-select>
-            <el-input
-              v-model="accountId"
-              placeholder="推广账号ID"
-              clearable
-              style="width: 200px"
-            />
             <el-button 
               type="primary" 
               @click="handleSync"
               :loading="syncing"
-              :disabled="!orgAccountId || !accountId"
             >
               <el-icon><Refresh /></el-icon>
               同步资产
@@ -44,6 +27,25 @@
         </div>
       </div>
 
+      <!-- 查询条件 -->
+      <div class="flex flex-col gap-3 mb-6">
+        <div class="flex flex-wrap items-center gap-4">
+          <el-input
+            v-model="queryForm.dramaName"
+            placeholder="短剧名称筛选"
+            clearable
+            style="width: 220px"
+            @clear="handleSearch"
+            @keyup.enter="handleSearch"
+          />
+          <el-button type="primary" @click="handleSearch">
+            <el-icon><Search /></el-icon>
+            查询
+          </el-button>
+          <el-button @click="handleReset">重置</el-button>
+        </div>
+      </div>
+
       <!-- 数据统计 -->
       <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
         <div class="bg-gradient-to-br from-indigo-50 to-indigo-100 rounded-lg p-4 border border-indigo-200">
@@ -139,6 +141,72 @@
       </div>
     </div>
 
+    <!-- 同步账号管理对话框 -->
+    <el-dialog
+      v-model="syncAccountDialogVisible"
+      title="我的同步账号管理"
+      width="600px"
+      @close="resetConfForm"
+    >
+      <!-- 新增表单 -->
+      <div class="bg-gray-50 rounded-lg p-4 mb-5 border border-gray-200">
+        <p class="text-sm font-medium text-gray-700 mb-3">新增同步账号</p>
+        <el-form
+          :model="confForm"
+          :rules="confFormRules"
+          ref="confFormRef"
+          label-width="80px"
+          size="default"
+        >
+          <el-form-item label="账号ID" prop="accountId">
+            <el-input
+              v-model="confForm.accountId"
+              placeholder="请输入推广账号ID"
+              clearable
+              style="width: 100%"
+            />
+          </el-form-item>
+          <el-form-item style="margin-bottom: 0">
+            <el-button type="primary" :loading="addingConf" @click="handleAddConf">确认新增</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <!-- 账号列表 -->
+      <el-table
+        :data="confList"
+        v-loading="confListLoading"
+        stripe
+        border
+        style="width: 100%"
+        empty-text="暂无同步账号配置"
+      >
+        <el-table-column prop="accountId" label="账号ID" />
+        <el-table-column label="创建时间" width="170">
+          <template #default="{ row }">
+            {{ formatDate(row.createTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="80" fixed="right">
+          <template #default="{ row }">
+            <el-button
+              type="danger"
+              size="small"
+              link
+              @click="handleDelConf(row)"
+            >
+              <el-icon><Delete /></el-icon>
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <template #footer>
+        <el-button @click="syncAccountDialogVisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
+
     <!-- 短剧资产详情对话框 -->
     <el-dialog
       v-model="detailDialogVisible"
@@ -179,15 +247,16 @@
 <script setup>
 import { ref, computed, onMounted } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
-import { getDramaList, syncDrama } from '@/api/drama'
-import { getAuthList } from '@/api/auth'
+import { getDramaList, syncDrama, myConfList, addConf, delConf } from '@/api/drama'
 
 import {
   Refresh,
   VideoPlay,
   Document,
   Files,
-  View
+  View,
+  Delete,
+  Search
 } from '@element-plus/icons-vue'
 
 // 数据
@@ -197,11 +266,25 @@ const dramaList = ref([])
 const detailDialogVisible = ref(false)
 const currentDrama = ref(null)
 
+// 同步账号管理 Dialog
+const syncAccountDialogVisible = ref(false)
+const confListLoading = ref(false)
+const addingConf = ref(false)
+const confFormRef = ref(null)
+const confForm = ref({ accountId: '' })
+const confFormRules = {
+  accountId: [
+    { required: true, message: '请输入账号ID', trigger: 'blur' },
+    { pattern: /^\d+$/, message: '账号ID必须为纯数字', trigger: 'blur' }
+  ]
+}
+
 // 同步参数
-const orgAccountId = ref('')
 const phone = ref('')
 const accountId = ref('')
-const orgAccounts = ref([])
+const confList = ref([])
+
+const queryForm = ref({ dramaName: '' })
 
 // 分页数据
 const currentPage = ref(1)
@@ -213,11 +296,23 @@ const totalPages = computed(() => {
   return Math.ceil(total.value / pageSize.value)
 })
 
+const handleSearch = () => {
+  currentPage.value = 1
+  loadDramaList()
+}
+
+const handleReset = () => {
+  queryForm.value = { dramaName: '' }
+  currentPage.value = 1
+  loadDramaList()
+}
+
 // 方法
 const loadDramaList = async () => {
   loading.value = true
   try {
-    const res = await getDramaList(currentPage.value, pageSize.value)
+    const { dramaName } = queryForm.value
+    const res = await getDramaList(currentPage.value, pageSize.value, dramaName)
     if (res.success) {
       dramaList.value = res.data || []
       total.value = res.total || 0
@@ -245,7 +340,7 @@ const handleCurrentChange = async (newPage) => {
 }
 
 const handleSync = async () => {
-  if (!orgAccountId.value || !accountId.value) {
+  if (!accountId.value) {
     ElMessage.warning('请填写所有同步参数')
     return
   }
@@ -262,7 +357,7 @@ const handleSync = async () => {
     )
 
     syncing.value = true
-    const res = await syncDrama(orgAccountId.value, accountId.value)
+    const res = await syncDrama(accountId.value)
     
     if (res.success) {
       ElMessage.success(res.message || '同步成功')
@@ -306,23 +401,83 @@ const formatJSON = (jsonStr) => {
   }
 }
 
-const loadAuthList = async () => {
+const loadConfList = async () => {
+  confListLoading.value = true
   try {
-    const response = await getAuthList()
+    const response = await myConfList()
     if (response.success) {
-      orgAccounts.value = response.data || []
+      confList.value = response.data || []
     } else {
       ElMessage.error(response.message || '加载授权列表失败')
     }
   } catch (error) {
     console.error('加载授权列表失败:', error)
     ElMessage.error('加载授权列表失败')
+  } finally {
+    confListLoading.value = false
+  }
+}
+
+const showSyncAccountDialog = () => {
+  syncAccountDialogVisible.value = true
+}
+
+const resetConfForm = () => {
+  confForm.value = { accountId: '' }
+  confFormRef.value?.clearValidate()
+}
+
+const handleAddConf = async () => {
+  await confFormRef.value?.validate(async (valid) => {
+    if (!valid) return
+    addingConf.value = true
+    try {
+      const res = await addConf({ accountId: confForm.value.accountId })
+      if (res.success) {
+        ElMessage.success('新增成功')
+        resetConfForm()
+        await loadConfList()
+      } else {
+        ElMessage.error(res.message || '新增失败')
+      }
+    } catch (error) {
+      console.error('新增同步账号失败:', error)
+      ElMessage.error('新增失败')
+    } finally {
+      addingConf.value = false
+    }
+  })
+}
+
+const handleDelConf = async (row) => {
+  try {
+    await ElMessageBox.confirm(
+      `确定要删除账号 ${row.accountId} 的同步配置吗?`,
+      '确认删除',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }
+    )
+    const res = await delConf(row.id)
+    if (res.success) {
+      ElMessage.success('删除成功')
+      await loadConfList()
+    } else {
+      ElMessage.error(res.message || '删除失败')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('删除同步账号失败:', error)
+      ElMessage.error('删除失败')
+    }
   }
 }
 
 // 生命周期
 onMounted(async () => {
-  await loadAuthList()
+  await loadConfList()
   loadDramaList()
 })
 </script>

+ 2 - 2
portal/vite.config.js

@@ -14,8 +14,8 @@ export default defineConfig({
     port: 5173,
     proxy: {
       '/api': {
-        // target: 'http://localhost:8080',
-        target: 'https://ad-auto.mokamrp.com/api',
+        target: 'http://localhost:8080',
+        // target: 'https://ad-auto.mokamrp.com/api',
         changeOrigin: true,
         rewrite: (path) => path.replace(/^\/api/, '')
       }

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

@@ -4,7 +4,6 @@ import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.scheduling.annotation.EnableScheduling;
 
 /**
  * 腾讯广告投放平台自动化项目启动类
@@ -13,7 +12,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
  * @since 2026-02-03
  */
 @SpringBootApplication
-@EnableScheduling
 @EnableAsync
 @MapperScan("com.moka.gdtauto.mapper")
 public class GdtAutoApplication {

+ 6 - 0
src/main/java/com/moka/gdtauto/common/Constant.java

@@ -0,0 +1,6 @@
+package com.moka.gdtauto.common;
+
+
+public class Constant {
+    public static final Long MAIN_ORG_ACCOUNT_ID = 41644744L;
+}

+ 16 - 0
src/main/java/com/moka/gdtauto/config/SchedulingConfig.java

@@ -0,0 +1,16 @@
+package com.moka.gdtauto.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * 定时任务配置
+ * 通过 scheduling.enabled=true 控制是否启用定时任务
+ * 测试环境设置为 false 禁用
+ */
+@Configuration
+@EnableScheduling
+@ConditionalOnProperty(name = "scheduling.enabled", havingValue = "true", matchIfMissing = true)
+public class SchedulingConfig {
+}

+ 74 - 0
src/main/java/com/moka/gdtauto/controller/AssetDramaSyncConfController.java

@@ -0,0 +1,74 @@
+package com.moka.gdtauto.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.moka.gdtauto.annotation.AuthUser;
+import com.moka.gdtauto.common.Result;
+import com.moka.gdtauto.entity.AssetDramaSyncConf;
+import com.moka.gdtauto.entity.SysUser;
+import com.moka.gdtauto.service.AssetDramaSyncConfService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+
+/**
+ * 短剧资产同步配置控制器
+ *
+ * @author moka
+ * @since 2026-03-04
+ */
+@Tag(name = "短剧资产同步配置", description = "短剧资产同步配置CRUD")
+@Slf4j
+@RestController
+@RequestMapping("/api/gdt/asset/drama/sync/conf")
+@RequiredArgsConstructor
+public class AssetDramaSyncConfController {
+
+    private final AssetDramaSyncConfService assetDramaSyncConfService;
+
+    /**
+     * 新增
+     */
+    @Operation(summary = "新增短剧资产同步配置")
+    @PostMapping("/add")
+    public Result<Void> add(@RequestBody AssetDramaSyncConf conf, @AuthUser SysUser user) {
+        try {
+            conf.setJobNumber(user.getJobNumber());
+            boolean success = assetDramaSyncConfService.addConf(conf);
+            return success ? Result.success("新增成功") : Result.fail("新增失败");
+        } catch (Exception e) {
+            log.error("新增短剧资产同步配置失败", e);
+            return Result.fail("新增失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 删除
+     */
+    @Operation(summary = "删除短剧资产同步配置")
+    @PostMapping("/delete")
+    public Result<Void> delete(@RequestParam Long id) {
+        try {
+            boolean success = assetDramaSyncConfService.deleteConf(id);
+            return success ? Result.success("删除成功") : Result.fail("删除失败");
+        } catch (Exception e) {
+            log.error("删除短剧资产同步配置失败,id={}", id, e);
+            return Result.fail("删除失败:" + e.getMessage());
+        }
+    }
+
+    @GetMapping("/myConf")
+    public Result<List<AssetDramaSyncConf>> getMyConf(@AuthUser SysUser user) {
+        List<AssetDramaSyncConf> confList = assetDramaSyncConfService.lambdaQuery().eq(AssetDramaSyncConf::getJobNumber, user.getJobNumber()).list();
+        return Result.success(confList, "查询成功");
+    }
+    
+}

+ 34 - 4
src/main/java/com/moka/gdtauto/controller/TencentAssetDramaController.java

@@ -2,6 +2,7 @@ package com.moka.gdtauto.controller;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.moka.gdtauto.annotation.AuthUser;
+import com.moka.gdtauto.common.Constant;
 import com.moka.gdtauto.common.Result;
 import com.moka.gdtauto.entity.TencentAssetDrama;
 import com.moka.gdtauto.service.TencentAssetDramaService;
@@ -10,6 +11,8 @@ import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
@@ -47,11 +50,9 @@ public class TencentAssetDramaController {
     public Result<Void> syncAssets(
             @Parameter(description = "推广账号ID", required = true, example = "69781948")
             @RequestParam Long accountId,
-            @Parameter(description = "组织账户ID", required = true, example = "123456789")
-            @RequestParam Long orgAccountId,
         @AuthUser SysUser user) {
         try {
-            int count = assetDramaService.syncMarketingAssets(user.getJobNumber(), orgAccountId, accountId);
+            int count = assetDramaService.syncMarketingAssets(user.getJobNumber(), Constant.MAIN_ORG_ACCOUNT_ID, accountId);
             return Result.<Void>success("同步成功,共同步" + count + "个短剧资产")
                     .withCount(count);
         } catch (Exception e) {
@@ -74,10 +75,12 @@ public class TencentAssetDramaController {
     )
     @GetMapping("/list")
     public Result<List<TencentAssetDrama>> listAssets(
+            @Parameter(description = "短剧资产名称", example = "dramaName")
+            @RequestParam(required = false) String dramaName,
             @Parameter(description = "页码,从1开始", example = "1")
             @RequestParam(defaultValue = "1") Long page,
             @Parameter(description = "每页数量", example = "20")
-            @RequestParam(defaultValue = "20") Long pageSize) {
+            @RequestParam(defaultValue = "20") Long pageSize, @AuthUser SysUser user) {
         try {
             // 计算偏移量
             long offset = (page - 1) * pageSize;
@@ -85,13 +88,21 @@ public class TencentAssetDramaController {
             // 查询总数
             LambdaQueryWrapper<TencentAssetDrama> countWrapper = new LambdaQueryWrapper<>();
             countWrapper.eq(TencentAssetDrama::getIsDeleted, 0);
+            if (StringUtils.isNotBlank(dramaName)) {
+                countWrapper.like(TencentAssetDrama::getMarketingAssetName, dramaName);
+            }
+            countWrapper.eq(TencentAssetDrama::getJobNumber, user.getJobNumber());
             long total = assetDramaService.count(countWrapper);
             
             // 分页查询
             LambdaQueryWrapper<TencentAssetDrama> wrapper = new LambdaQueryWrapper<>();
             wrapper.eq(TencentAssetDrama::getIsDeleted, 0)
+                   .eq(TencentAssetDrama::getJobNumber, user.getJobNumber())
                    .orderByDesc(TencentAssetDrama::getUpdateTime)
                    .last("LIMIT " + offset + ", " + pageSize);
+            if (StringUtils.isNotBlank(dramaName)) {
+                wrapper.like(TencentAssetDrama::getMarketingAssetName, dramaName);
+            }
             
             List<TencentAssetDrama> assetList = assetDramaService.list(wrapper);
             
@@ -103,6 +114,25 @@ public class TencentAssetDramaController {
         }
     }
 
+    @GetMapping("/myDramaAssets")
+    public Result<List<TencentAssetDrama>> getMyDramaAssets(
+            @Parameter(description = "短剧资产名称", example = "dramaName")
+            @RequestParam(required = false) String dramaName,
+        @AuthUser SysUser user) {
+        try {
+            LambdaQueryWrapper<TencentAssetDrama> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(TencentAssetDrama::getJobNumber, user.getJobNumber());
+            if (StringUtils.isNotBlank(dramaName)) {
+                wrapper.like(TencentAssetDrama::getMarketingAssetName, dramaName);
+            }
+            List<TencentAssetDrama> assetList = assetDramaService.list(wrapper);
+            return Result.success(assetList, "查询成功");
+        } catch (Exception e) {
+            log.error("查询短剧资产列表失败", e);
+            return Result.fail("查询失败:" + e.getMessage());
+        }
+    }
+
     /**
      * 根据短剧资产ID查询详情
      * 

+ 45 - 0
src/main/java/com/moka/gdtauto/entity/AssetDramaSyncConf.java

@@ -0,0 +1,45 @@
+package com.moka.gdtauto.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 短剧资产同步配置实体
+ *
+ * @author moka
+ * @since 2026-03-04
+ */
+@Data
+@TableName("asset_drama_sync_conf")
+public class AssetDramaSyncConf {
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 职位编号
+     */
+    private String jobNumber;
+
+    /**
+     * 账号ID
+     */
+    private Long accountId;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 4 - 0
src/main/java/com/moka/gdtauto/entity/TencentAssetDrama.java

@@ -23,6 +23,10 @@ public class TencentAssetDrama {
     @TableId(type = IdType.AUTO)
     private Long id;
 
+    private String jobNumber;
+
+    private Long accountId;
+
     /**
      * 产品ID
      */

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

@@ -0,0 +1,15 @@
+package com.moka.gdtauto.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.moka.gdtauto.entity.AssetDramaSyncConf;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 短剧资产同步配置Mapper
+ *
+ * @author moka
+ * @since 2026-03-04
+ */
+@Mapper
+public interface AssetDramaSyncConfMapper extends BaseMapper<AssetDramaSyncConf> {
+}

+ 49 - 0
src/main/java/com/moka/gdtauto/service/AssetDramaSyncConfService.java

@@ -0,0 +1,49 @@
+package com.moka.gdtauto.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.moka.gdtauto.entity.AssetDramaSyncConf;
+
+/**
+ * 短剧资产同步配置Service接口
+ *
+ * @author moka
+ * @since 2026-03-04
+ */
+public interface AssetDramaSyncConfService extends IService<AssetDramaSyncConf> {
+
+    /**
+     * 分页查询
+     *
+     * @param page      页码
+     * @param size      每页数量
+     * @param jobNumber 职位编号(模糊)
+     * @param accountId 账号ID
+     * @return 分页结果
+     */
+    Page<AssetDramaSyncConf> listPage(Long page, Long size, String jobNumber, Long accountId);
+
+    /**
+     * 新增配置(job_number + account_id 唯一)
+     *
+     * @param conf 配置信息
+     * @return 是否成功
+     */
+    boolean addConf(AssetDramaSyncConf conf);
+
+    /**
+     * 更新配置
+     *
+     * @param conf 配置信息(需包含id)
+     * @return 是否成功
+     */
+    boolean updateConf(AssetDramaSyncConf conf);
+
+    /**
+     * 删除配置
+     *
+     * @param id 主键ID
+     * @return 是否成功
+     */
+    boolean deleteConf(Long id);
+}

+ 1 - 2
src/main/java/com/moka/gdtauto/service/TencentAdsAuthService.java

@@ -254,8 +254,7 @@ public class TencentAdsAuthService extends ServiceImpl<TencentAdsAuthMapper, Ten
      */
     public TencentAdsAuth getByJobNumberAndAccountId(String jobNumber, Long accountId) {
         LambdaQueryWrapper<TencentAdsAuth> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(TencentAdsAuth::getState, jobNumber)
-                .eq(TencentAdsAuth::getAccountId, accountId)
+        wrapper.eq(TencentAdsAuth::getAccountId, accountId)
                 .eq(TencentAdsAuth::getDeleted, 0)
                 .orderByDesc(TencentAdsAuth::getId)
                 .last("LIMIT 1");

+ 7 - 3
src/main/java/com/moka/gdtauto/service/TencentAssetDramaService.java

@@ -3,6 +3,7 @@ package com.moka.gdtauto.service;
 import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.moka.gdtauto.client.TencentAdsApiClientFactory;
+import com.moka.gdtauto.common.Constant;
 import com.moka.gdtauto.entity.TencentAssetDrama;
 import com.moka.gdtauto.mapper.TencentAssetDramaMapper;
 import com.tencent.ads.exception.TencentAdsResponseException;
@@ -58,7 +59,7 @@ public class TencentAssetDramaService extends ServiceImpl<TencentAssetDramaMappe
         
         log.info("获取短剧资产列表, accountId: {}, page: {}, pageSize: {}", accountId, page, pageSize);
         
-        TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, orgAccountId);
+        TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
         
         MarketingTargetAssetsGetResponseData response = tencentAds.marketingTargetAssets()
             .marketingTargetAssetsGet(
@@ -98,7 +99,7 @@ public class TencentAssetDramaService extends ServiceImpl<TencentAssetDramaMappe
         
         while (totalCount < maxRecords) {
             MarketingTargetAssetsGetResponseData response = getMarketingAssets(
-                orgAccountId, jobNumber, accountId, null, page, pageSize);
+                Constant.MAIN_ORG_ACCOUNT_ID, jobNumber, accountId, null, page, pageSize);
             
             if (response == null || CollectionUtils.isEmpty(response.getList())) {
                 log.info("没有更多数据,结束同步");
@@ -107,6 +108,10 @@ public class TencentAssetDramaService extends ServiceImpl<TencentAssetDramaMappe
             
             // 转换为实体对象
             List<TencentAssetDrama> assets = convertToEntities(response);
+            assets.forEach(asset -> {
+                asset.setJobNumber(jobNumber);
+                asset.setAccountId(accountId);
+            });
             
             // 立即保存当前批次数据
             if (!CollectionUtils.isEmpty(assets)) {
@@ -150,7 +155,6 @@ public class TencentAssetDramaService extends ServiceImpl<TencentAssetDramaMappe
         
         for (var item : response.getList()) {
             TencentAssetDrama entity = new TencentAssetDrama();
-            
             entity.setMarketingAssetId(item.getMarketingAssetId());
             entity.setMarketingAssetName(item.getMarketingAssetName());
             entity.setMarketingAssetType(item.getMarketingAssetType() != null ? 

+ 6 - 7
src/main/java/com/moka/gdtauto/service/impl/AdPlanConfServiceImpl.java

@@ -43,7 +43,7 @@ import java.net.URL;
 import java.nio.file.Files;
 import java.security.MessageDigest;
 import java.util.Base64;
-
+import com.moka.gdtauto.common.Constant;
 
 
 /**
@@ -67,8 +67,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
     @Autowired
     private TencentAccountService tencentAccountService;
 
-    private static final Long MAIN_ORG_ACCOUNT_ID = 41644744L;
-
+    
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createAdPlanConf(AdPlanConf adPlanConf) {
@@ -212,7 +211,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                 
                 if (plan.getAdgroupId() > 0 || plan.getStatus() == AdPlanStatus.SUCCESS) {
                      // 创建动态创意
-                    createDynamicCreatives(plan, jobNumber, MAIN_ORG_ACCOUNT_ID);
+                    createDynamicCreatives(plan, jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
                     plan.setFailReason("");
                     plan.setStatus(AdPlanStatus.SUCCESS);
                     plan.setSuccessTime(LocalDateTime.now());
@@ -229,9 +228,9 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                 AdgroupsAddRequest request = buildAdgroupRequest(plan);
                 
                 // 创建广告组
-                log.info("调用腾讯API创建广告组,accountId={}, orgAccountId={}", plan.getAccountId(), MAIN_ORG_ACCOUNT_ID);
+                log.info("调用腾讯API创建广告组,accountId={}, orgAccountId={}", plan.getAccountId(), Constant.MAIN_ORG_ACCOUNT_ID);
                 AdgroupsAddResponseData response = tencentAdsAdgroupService.createAdgroup(
-                    jobNumber, MAIN_ORG_ACCOUNT_ID, request
+                    jobNumber, Constant.MAIN_ORG_ACCOUNT_ID, request
                 );
                 
                 // 更新 adgroupId
@@ -246,7 +245,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
                 
                 // 创建动态创意
                 if (plan.getAdgroupId() > 0) {
-                    createDynamicCreatives(plan, jobNumber, MAIN_ORG_ACCOUNT_ID);
+                    createDynamicCreatives(plan, jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
                 }
                 
                 log.info("广告计划处理成功,planId={}, adgroupId={}", plan.getId(), plan.getAdgroupId());

+ 53 - 0
src/main/java/com/moka/gdtauto/service/impl/AssetDramaSyncConfServiceImpl.java

@@ -0,0 +1,53 @@
+package com.moka.gdtauto.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.moka.gdtauto.entity.AssetDramaSyncConf;
+import com.moka.gdtauto.mapper.AssetDramaSyncConfMapper;
+import com.moka.gdtauto.service.AssetDramaSyncConfService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+/**
+ * 短剧资产同步配置Service实现
+ *
+ * @author moka
+ * @since 2026-03-04
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AssetDramaSyncConfServiceImpl extends ServiceImpl<AssetDramaSyncConfMapper, AssetDramaSyncConf>
+        implements AssetDramaSyncConfService {
+
+    @Override
+    public Page<AssetDramaSyncConf> listPage(Long page, Long size, String jobNumber, Long accountId) {
+        LambdaQueryWrapper<AssetDramaSyncConf> wrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.hasText(jobNumber)) {
+            wrapper.like(AssetDramaSyncConf::getJobNumber, jobNumber);
+        }
+        if (accountId != null) {
+            wrapper.eq(AssetDramaSyncConf::getAccountId, accountId);
+        }
+        wrapper.orderByDesc(AssetDramaSyncConf::getCreateTime);
+        return this.page(new Page<>(page, size), wrapper);
+    }
+
+    @Override
+    public boolean addConf(AssetDramaSyncConf conf) {
+        return this.save(conf);
+    }
+
+    @Override
+    public boolean updateConf(AssetDramaSyncConf conf) {
+        return this.updateById(conf);
+    }
+
+    @Override
+    public boolean deleteConf(Long id) {
+        return this.removeById(id);
+    }
+}

+ 4 - 0
src/main/resources/application-test.yml

@@ -34,6 +34,10 @@ spring:
           max-wait: -1ms
       timeout: 3000ms
 
+# 定时任务配置(测试环境禁用)
+scheduling:
+  enabled: false
+
 # 阿里云OSS配置(测试环境)
 aliyun:
   oss:

+ 4 - 0
src/main/resources/mapper/TencentAssetDramaMapper.xml

@@ -6,6 +6,8 @@
     <!-- 批量插入或更新短剧资产 -->
     <insert id="batchInsertOrUpdate" parameterType="java.util.List">
         INSERT INTO tencent_asset_drama (
+            job_number,
+            account_id,
             marketing_asset_id,
             marketing_asset_name,
             marketing_asset_type,
@@ -16,6 +18,8 @@
         VALUES
         <foreach collection="dramas" item="item" separator=",">
             (
+                #{item.jobNumber},
+                #{item.accountId},
                 #{item.marketingAssetId},
                 #{item.marketingAssetName},
                 #{item.marketingAssetType},

+ 8 - 0
src/main/resources/sql/create_asset_drama_sync_conf_table.sql

@@ -0,0 +1,8 @@
+create table asset_drama_sync_conf (
+    id bigint not null primary key auto_increment comment '主键ID',
+    job_number varchar(100) not null comment '职位编号',
+    account_id bigint not null comment '账号ID',
+    create_time         datetime   default CURRENT_TIMESTAMP not null comment '创建时间',
+    update_time         datetime   default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
+    unique index uk_job_number_account_id (job_number, account_id)
+) comment '短剧资产同步配置';

Vissa filer visades inte eftersom för många filer har ändrats