Procházet zdrojové kódy

feat:落地页维护

pudongliang před 4 měsíci
rodič
revize
80dbe2f24d
45 změnil soubory, kde provedl 902 přidání a 26 odebrání
  1. 6 0
      pom.xml
  2. 1 1
      portal/dist/assets/accountGroup-kLwTKZmR.js
  3. 1 1
      portal/dist/assets/adPlanConf-BheiTRbJ.js
  4. 1 1
      portal/dist/assets/auth-CzhhlL42.js
  5. 1 1
      portal/dist/assets/conversion-CmLtY_Pa.js
  6. 0 0
      portal/dist/assets/detail-DDb6Dgxn.js
  7. 1 1
      portal/dist/assets/drama-C7EQZzd7.js
  8. 0 0
      portal/dist/assets/index-1434wt51.js
  9. 0 0
      portal/dist/assets/index-BAtgzrkK.css
  10. 0 0
      portal/dist/assets/index-BBCkLiox.js
  11. 0 0
      portal/dist/assets/index-BSGff9KK.js
  12. 0 0
      portal/dist/assets/index-CCr6YYR4.js
  13. 0 0
      portal/dist/assets/index-CP7pLsnj.js
  14. 1 0
      portal/dist/assets/index-C_7xiWjB.css
  15. 0 0
      portal/dist/assets/index-Ciklf1mZ.js
  16. 0 0
      portal/dist/assets/index-CsLemFkn.css
  17. 0 1
      portal/dist/assets/index-D2Gwvn-S.js
  18. 0 0
      portal/dist/assets/index-D3oioubP.js
  19. 0 0
      portal/dist/assets/index-DLIyT1hW.js
  20. 0 0
      portal/dist/assets/index-DmUwv-k5.js
  21. 0 1
      portal/dist/assets/index-DuXhWDX2.css
  22. 0 0
      portal/dist/assets/index-E3motGVF.js
  23. 0 0
      portal/dist/assets/index-GaI4Dfmg.js
  24. 0 0
      portal/dist/assets/index-OiL8smJX.js
  25. 0 0
      portal/dist/assets/index-PWnQxL_e.js
  26. 0 0
      portal/dist/assets/index-ZPTkvA8s.js
  27. 1 0
      portal/dist/assets/loadPageConf-DfwdfAiV.js
  28. 0 1
      portal/dist/assets/ossFile-BLdRPvRB.js
  29. 1 0
      portal/dist/assets/ossFile-MqFfyJ4q.js
  30. 0 0
      portal/dist/assets/request-GYOOhKo9.js
  31. 1 1
      portal/dist/assets/user-umYKzI0W.js
  32. 2 2
      portal/dist/index.html
  33. 75 0
      portal/src/api/loadPageConf.js
  34. 1 1
      portal/src/api/ossFile.js
  35. 6 0
      portal/src/router/index.js
  36. 36 10
      portal/src/views/ad-plan-conf/index.vue
  37. 397 0
      portal/src/views/load-page-conf/index.vue
  38. 1 2
      portal/src/views/oss-file/index.vue
  39. 3 2
      src/main/java/com/moka/gdtauto/BaseCodeGenerator.java
  40. 159 0
      src/main/java/com/moka/gdtauto/controller/LoadPageConfController.java
  41. 141 0
      src/main/java/com/moka/gdtauto/entity/LoadPageConf.java
  42. 16 0
      src/main/java/com/moka/gdtauto/mapper/LoadPageConfMapper.java
  43. 16 0
      src/main/java/com/moka/gdtauto/service/LoadPageConfService.java
  44. 20 0
      src/main/java/com/moka/gdtauto/service/impl/LoadPageConfServiceImpl.java
  45. 14 0
      src/main/resources/mapper/LoadPageConfMapper.xml

+ 6 - 0
pom.xml

@@ -96,6 +96,12 @@
             <version>${mybatis-plus.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.30</version>
+        </dependency>
+
         <!-- Dynamic DataSource -->
          <dependency>
             <groupId>com.baomidou</groupId>

+ 1 - 1
portal/dist/assets/accountGroup-DxOjQLAv.js → portal/dist/assets/accountGroup-kLwTKZmR.js

@@ -1 +1 @@
-import{r as u}from"./request-Bs8wJWrL.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-GYOOhKo9.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-3SBq-6df.js → portal/dist/assets/adPlanConf-BheiTRbJ.js

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

+ 1 - 1
portal/dist/assets/auth-CUYLa5jV.js → portal/dist/assets/auth-CzhhlL42.js

@@ -1 +1 @@
-import{r as t}from"./request-Bs8wJWrL.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-GYOOhKo9.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-zkvyWa5u.js → portal/dist/assets/conversion-CmLtY_Pa.js

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

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/detail-DDb6Dgxn.js


+ 1 - 1
portal/dist/assets/drama-Dsb4mXGZ.js → portal/dist/assets/drama-C7EQZzd7.js

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

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-1434wt51.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-BAtgzrkK.css


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-BBCkLiox.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-BSGff9KK.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-CCr6YYR4.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-CP7pLsnj.js


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

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

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-Ciklf1mZ.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-CsLemFkn.css


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 1
portal/dist/assets/index-D2Gwvn-S.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-D3oioubP.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-DLIyT1hW.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-DmUwv-k5.js


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

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

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-E3motGVF.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-GaI4Dfmg.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-OiL8smJX.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-PWnQxL_e.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/index-ZPTkvA8s.js


+ 1 - 0
portal/dist/assets/loadPageConf-DfwdfAiV.js

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

+ 0 - 1
portal/dist/assets/ossFile-BLdRPvRB.js

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

+ 1 - 0
portal/dist/assets/ossFile-MqFfyJ4q.js

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

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
portal/dist/assets/request-GYOOhKo9.js


+ 1 - 1
portal/dist/assets/user-CWwWL1qI.js → portal/dist/assets/user-umYKzI0W.js

@@ -1 +1 @@
-import{r as e}from"./request-Bs8wJWrL.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-GYOOhKo9.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-Bdo89sOG.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-CsLemFkn.css">
+    <script type="module" crossorigin src="/assets/index-D2Gwvn-S.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-BAtgzrkK.css">
   </head>
   <body>
     <div id="app"></div>

+ 75 - 0
portal/src/api/loadPageConf.js

@@ -0,0 +1,75 @@
+import request from '../utils/request'
+
+/**
+ * 分页查询落地页配置列表
+ */
+export function getLoadPageConfList(params) {
+  return request({
+    url: '/api/load-page-conf/list',
+    method: 'get',
+    params
+  })
+}
+
+/**
+ * 获取落地页配置详情
+ */
+export function getLoadPageConfDetail(id) {
+  return request({
+    url: `/api/load-page-conf/${id}`,
+    method: 'get'
+  })
+}
+
+/**
+ * 新增落地页配置
+ */
+export function addLoadPageConf(data) {
+  return request({
+    url: '/api/load-page-conf',
+    method: 'post',
+    data
+  })
+}
+
+/**
+ * 更新落地页配置
+ */
+export function updateLoadPageConf(data) {
+  return request({
+    url: '/api/load-page-conf',
+    method: 'put',
+    data
+  })
+}
+
+/**
+ * 删除落地页配置
+ */
+export function deleteLoadPageConf(id) {
+  return request({
+    url: `/api/load-page-conf/${id}`,
+    method: 'delete'
+  })
+}
+
+/**
+ * 批量删除落地页配置
+ */
+export function batchDeleteLoadPageConf(ids) {
+  return request({
+    url: '/api/load-page-conf/batch',
+    method: 'delete',
+    data: ids
+  })
+}
+
+/**
+ * 获取所有落地页配置(下拉选择用)
+ */
+export function getAllLoadPageConf() {
+  return request({
+    url: '/api/load-page-conf/all',
+    method: 'get'
+  })
+}

+ 1 - 1
portal/src/api/ossFile.js

@@ -5,7 +5,7 @@ import request from '../utils/request'
  */
 export function uploadFile(formData) {
   return request({
-    timeout: 300000,
+    timeout: 1200000,
     url: '/api/oss-file/upload',
     method: 'post',
     data: formData,

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

@@ -103,6 +103,12 @@ const routes = [
         name: 'OssFile',
         component: () => import('@/views/oss-file/index.vue'),
         meta: { title: 'OSS文件管理', icon: 'FolderOpened' }
+      },
+      {
+        path: 'load-page-conf',
+        name: 'LoadPageConf',
+        component: () => import('@/views/load-page-conf/index.vue'),
+        meta: { title: '落地页配置', icon: 'Document' }
       }
     ]
   }

+ 36 - 10
portal/src/views/ad-plan-conf/index.vue

@@ -283,12 +283,21 @@
         </el-form-item>
 
         <el-form-item label="落地页IDs" prop="pageIds">
-          <el-input
-            v-model="formData.pageIds"
-            type="textarea"
-            :rows="2"
-            placeholder="多个落地页ID用逗号分隔,例如:1,2,3"
-          />
+          <el-select
+            :disabled="valueDisabled"
+            v-model="formData.pageIdArr"
+            style="width: 100%"
+            placeholder="请选择落地页"
+            multiple
+            filterable
+          >
+            <el-option
+              v-for="page in loadPageList"
+              :key="page.id"
+              :label="`${page.name}[${page.pageId}]`"
+              :value="page.pageId"
+            />
+          </el-select>
         </el-form-item>
 
         <el-form-item label="创意图片/视频">
@@ -453,7 +462,7 @@
         :content="plans.length === 0 ? '请先进行预览操作' : ''"
         placement="top-start"
       >
-        <el-button :disabled="plans.length === 0" @click="handleGeneratePlan" >生成计划</el-button>
+        <el-button  :disabled="plans.length === 0" @click="handleGeneratePlan" >生成计划</el-button>
       </el-tooltip>
         <el-button @click="dialogVisible = false">取消</el-button>
         <el-button type="primary" @click="handleSubmit" :loading="submitting">
@@ -569,6 +578,7 @@ import { myDramaAssetList } from '@/api/drama'
 import { getConversions } from '@/api/conversion'
 import { getAccountGroupListAll } from '@/api/accountGroup'
 import { getFileList } from '@/api/ossFile'
+import { getAllLoadPageConf } from '@/api/loadPageConf'
 
 import {
   Plus,
@@ -588,6 +598,7 @@ const total = ref(0)
 
 const dramaSort = ref(1)
 const dramaList = ref([])
+const loadPageList = ref([])
 
 // 对话框
 const dialogVisible = ref(false)
@@ -622,13 +633,14 @@ const previewUrl = ref('')
 const formData = ref({
   name: '',
   accountGroupId: null,
-  adType: 1,
+  adType: 2,
   bidAmount: 150,
   conversionId: null,
   wxCorpid: '',
   dramaId: null,
   imageIds: '',
   pageIds: '',
+  pageIdArr: [],
   creativeFileUrls: '',
   creativeFileNames: '',
   brandImageUrl: '',
@@ -777,9 +789,9 @@ const handleCreate = () => {
   formData.value = {
     name: '',
     accountGroupId: null,
-    adType: 1,
+    adType: 2,
     bidAmount: 150,
-    deepConversionWorthRate: 100.00,
+    deepConversionWorthRate: 50.00,
     conversionId: null,
     targets: '1',
     sites: site + '',
@@ -788,6 +800,7 @@ const handleCreate = () => {
     dramaAmount: 0,
     imageIds: '',
     pageIds: '',
+    pageIdArr: [],
     creativeFileUrls: '',
     creativeFileNames: '',
     brandImageUrl: '',
@@ -818,6 +831,7 @@ const handleEdit = (row) => {
     dramaAmount: row.dramaAmount || 1,
     imageIds: row.imageIds || '',
     pageIds: row.pageIds || '',
+    pageIdArr: row.pageIds ? row.pageIds.split(',').map(Number) : [],
     creativeFileUrls: row.creativeFileUrls || '',
     creativeFileNames: row.creativeFileNames || '',
     brandImageUrl: row.brandImageUrl || '',
@@ -920,6 +934,8 @@ const handleSubmit = async () => {
     }
     submitting.value = true
     const data = { ...formData.value }
+    data.pageIds = data.pageIdArr.join(',')
+    delete data.pageIdArr
 
     let res
     if (editingId.value) {
@@ -1082,6 +1098,15 @@ const loadDramaList = async () => {
   }
 }
 
+const loadLoadPageList = async () => {
+  const res = await getAllLoadPageConf()
+  if (res.success) {
+    loadPageList.value = res.data
+  } else {
+    ElMessage.error(res.message || '加载落地页列表失败')
+  }
+}
+
 // 监听分页变化
 watch([currentPage, pageSize], () => {
   loadConfList()
@@ -1093,6 +1118,7 @@ onMounted(() => {
   loadConversions()
   loadAccountGroupList()
   loadDramaList()
+  loadLoadPageList()
 })
 </script>
 

+ 397 - 0
portal/src/views/load-page-conf/index.vue

@@ -0,0 +1,397 @@
+<template>
+  <div class="space-y-6">
+    <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
+      <div class="border-b border-gray-200 pb-4 mb-6">
+        <div class="flex items-center justify-between">
+          <h2 class="text-xl font-semibold text-gray-800 flex items-center gap-2">
+            <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
+            </svg>
+            落地页配置管理
+          </h2>
+          <el-button type="primary" @click="handleAdd">
+            <el-icon><Plus /></el-icon>
+            新增配置
+          </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-blue-50 to-blue-100 rounded-lg p-4 border border-blue-200">
+          <div class="flex items-center justify-between">
+            <div>
+              <p class="text-sm text-blue-600 mb-1">配置总数</p>
+              <p class="text-2xl font-bold text-blue-700">{{ pagination.total }}</p>
+            </div>
+            <div class="p-3 bg-blue-200 rounded-lg">
+              <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
+              </svg>
+            </div>
+          </div>
+        </div>
+        <div class="bg-gradient-to-br from-green-50 to-green-100 rounded-lg p-4 border border-green-200">
+          <div class="flex items-center justify-between">
+            <div>
+              <p class="text-sm text-green-600 mb-1">当前页数量</p>
+              <p class="text-2xl font-bold text-green-700">{{ confList.length }}</p>
+            </div>
+            <div class="p-3 bg-green-200 rounded-lg">
+              <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
+              </svg>
+            </div>
+          </div>
+        </div>
+        <div class="bg-gradient-to-br from-purple-50 to-purple-100 rounded-lg p-4 border border-purple-200">
+          <div class="flex items-center justify-between">
+            <div>
+              <p class="text-sm text-purple-600 mb-1">总页数</p>
+              <p class="text-2xl font-bold text-purple-700">{{ totalPages }}</p>
+            </div>
+            <div class="p-3 bg-purple-200 rounded-lg">
+              <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
+              </svg>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 搜索筛选 -->
+      <div class="mb-6 p-4 bg-gray-50 rounded-lg">
+        <el-form :inline="true" :model="queryForm" class="flex flex-wrap gap-4">
+          <el-form-item label="页面名称">
+            <el-input v-model="queryForm.name" placeholder="请输入页面名称" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item label="落地页ID">
+            <el-input v-model="queryForm.pageId" placeholder="请输入落地页ID" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="handleQuery">查询</el-button>
+            <el-button @click="handleReset">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <!-- 数据表格 -->
+      <el-table
+        :data="confList"
+        style="width: 100%"
+        v-loading="loading"
+        stripe
+      >
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="name" label="页面名称" width="200" show-overflow-tooltip />
+        <el-table-column prop="pageId" label="落地页ID" width="150" />
+        <el-table-column prop="createUserName" label="创建人" width="120" />
+        <el-table-column label="创建时间" width="180">
+          <template #default="{ row }">
+            {{ formatDate(row.createTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="更新时间" width="180">
+          <template #default="{ row }">
+            {{ formatDate(row.updateTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="150" fixed="right">
+          <template #default="{ row }">
+            <el-button type="primary" size="small" link @click="handleEdit(row)">编辑</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <div class="mt-6 flex justify-end">
+        <el-pagination
+          v-model:current-page="pagination.page"
+          v-model:page-size="pagination.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </div>
+
+    <!-- 新增/编辑对话框 -->
+    <el-dialog
+      v-model="dialogVisible"
+      :title="isEdit ? '编辑配置' : '新增配置'"
+      width="500px"
+      @close="handleDialogClose"
+    >
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="页面名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入页面名称" />
+        </el-form-item>
+        <el-form-item label="落地页ID" prop="pageId">
+          <el-input v-model.number="form.pageId" placeholder="请输入落地页ID" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="handleSubmit" :loading="submitting">确定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Plus } from '@element-plus/icons-vue'
+import { 
+  getLoadPageConfList, 
+  getLoadPageConfDetail, 
+  addLoadPageConf, 
+  updateLoadPageConf, 
+  deleteLoadPageConf,
+  batchDeleteLoadPageConf 
+} from '@/api/loadPageConf'
+
+// 响应式数据
+const loading = ref(false)
+const submitting = ref(false)
+const confList = ref([])
+const selectedRows = ref([])
+const dialogVisible = ref(false)
+const isEdit = ref(false)
+const formRef = ref(null)
+
+// 查询表单
+const queryForm = reactive({
+  name: '',
+  pageId: ''
+})
+
+// 分页数据
+const pagination = reactive({
+  page: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// 表单数据
+const form = reactive({
+  id: null,
+  name: '',
+  pageId: null
+})
+
+// 表单校验规则
+const rules = {
+  name: [
+    { required: true, message: '请输入页面名称', trigger: 'blur' }
+  ],
+  pageId: [
+    { required: true, message: '请输入落地页ID', trigger: 'blur' }
+  ]
+}
+
+// 计算属性
+const totalPages = computed(() => {
+  return Math.ceil(pagination.total / pagination.pageSize)
+})
+
+// 格式化日期
+const formatDate = (dateStr) => {
+  if (!dateStr) return '-'
+  const date = new Date(dateStr)
+  return date.toLocaleString('zh-CN', {
+    year: 'numeric',
+    month: '2-digit',
+    day: '2-digit',
+    hour: '2-digit',
+    minute: '2-digit',
+    second: '2-digit'
+  })
+}
+
+// 加载列表
+const loadList = async () => {
+  loading.value = true
+  try {
+    const params = {
+      page: pagination.page,
+      size: pagination.pageSize
+    }
+    if (queryForm.name) {
+      params.name = queryForm.name
+    }
+    if (queryForm.pageId) {
+      params.pageId = queryForm.pageId
+    }
+    const response = await getLoadPageConfList(params)
+    if (response.success) {
+      confList.value = response.data.records || []
+      pagination.total = response.data.total || 0
+    } else {
+      ElMessage.error(response.message || '加载列表失败')
+    }
+  } catch (error) {
+    console.error('加载列表失败:', error)
+    ElMessage.error('加载列表失败')
+  } finally {
+    loading.value = false
+  }
+}
+
+// 查询
+const handleQuery = () => {
+  pagination.page = 1
+  loadList()
+}
+
+// 重置
+const handleReset = () => {
+  queryForm.name = ''
+  queryForm.pageId = ''
+  handleQuery()
+}
+
+// 选择变化
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection
+}
+
+// 新增
+const handleAdd = () => {
+  isEdit.value = false
+  form.id = null
+  form.name = ''
+  form.pageId = null
+  dialogVisible.value = true
+}
+
+// 编辑
+const handleEdit = async (row) => {
+  isEdit.value = true
+  try {
+    const response = await getLoadPageConfDetail(row.id)
+    if (response.success) {
+      form.id = response.data.id
+      form.name = response.data.name
+      form.pageId = response.data.pageId
+      dialogVisible.value = true
+    } else {
+      ElMessage.error(response.message || '获取详情失败')
+    }
+  } catch (error) {
+    console.error('获取详情失败:', error)
+    ElMessage.error('获取详情失败')
+  }
+}
+
+// 删除
+const handleDelete = (row) => {
+  ElMessageBox.confirm('确定要删除该配置吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(async () => {
+    try {
+      const response = await deleteLoadPageConf(row.id)
+      if (response.success) {
+        ElMessage.success('删除成功')
+        loadList()
+      } else {
+        ElMessage.error(response.message || '删除失败')
+      }
+    } catch (error) {
+      console.error('删除失败:', error)
+      ElMessage.error('删除失败')
+    }
+  }).catch(() => {})
+}
+
+// 批量删除
+const handleBatchDelete = () => {
+  ElMessageBox.confirm(`确定要删除选中的 ${selectedRows.value.length} 个配置吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(async () => {
+    try {
+      const ids = selectedRows.value.map(item => item.id)
+      const response = await batchDeleteLoadPageConf(ids)
+      if (response.success) {
+        ElMessage.success('批量删除成功')
+        loadList()
+      } else {
+        ElMessage.error(response.message || '批量删除失败')
+      }
+    } catch (error) {
+      console.error('批量删除失败:', error)
+      ElMessage.error('批量删除失败')
+    }
+  }).catch(() => {})
+}
+
+// 对话框关闭
+const handleDialogClose = () => {
+  formRef.value?.resetFields()
+}
+
+// 提交表单
+const handleSubmit = async () => {
+  if (!formRef.value) return
+  
+  await formRef.value.validate(async (valid) => {
+    if (!valid) return
+    
+    submitting.value = true
+    try {
+      const data = {
+        name: form.name,
+        pageId: form.pageId
+      }
+      
+      let response
+      if (isEdit.value) {
+        data.id = form.id
+        response = await updateLoadPageConf(data)
+      } else {
+        response = await addLoadPageConf(data)
+      }
+      
+      if (response.success) {
+        ElMessage.success(isEdit.value ? '更新成功' : '新增成功')
+        dialogVisible.value = false
+        loadList()
+      } else {
+        ElMessage.error(response.message || '操作失败')
+      }
+    } catch (error) {
+      console.error('操作失败:', error)
+      ElMessage.error('操作失败')
+    } finally {
+      submitting.value = false
+    }
+  })
+}
+
+// 分页大小改变
+const handleSizeChange = (newSize) => {
+  pagination.pageSize = newSize
+  pagination.page = 1
+  loadList()
+}
+
+// 当前页改变
+const handleCurrentChange = (newPage) => {
+  pagination.page = newPage
+  loadList()
+}
+
+// 页面加载时获取列表
+onMounted(() => {
+  loadList()
+})
+</script>
+
+<style scoped>
+</style>

+ 1 - 2
portal/src/views/oss-file/index.vue

@@ -136,7 +136,7 @@
               <img :src="row.fileUrl" alt="预览" class="w-full h-full object-cover cursor-pointer" @click="previewFile(row)">
             </div>
             <div v-else class="w-20 h-20 flex items-center justify-center bg-gray-100 rounded border border-gray-200">
-              <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+              <svg @click="previewFile(row)" xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
               </svg>
             </div>
@@ -249,7 +249,6 @@ const clearSelectedFiles = () => {
 const uploadRules = {
   businessType: [{ required: true, message: '请选择业务分类', trigger: 'change' }],
   fileType: [{ required: true, message: '请选择文件类型', trigger: 'change' }],
-  file: [{ required: true, message: '请选择文件', trigger: 'change' }]
 }
 
 const uploadFileList = ref([])

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

@@ -9,7 +9,7 @@ import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
 
 public class BaseCodeGenerator {
 
-    public final static String TABLE_NAMES = "sys_user,oss_file";
+    public final static String TABLE_NAMES = "load_page_conf";
     public static void main(String[] args) {
         String url = "jdbc:mysql://rm-uf65d6xsxrp743w3aio.mysql.rds.aliyuncs.com:3306/gdt_auto?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true";
         FastAutoGenerator.create(url, "gdt_auto", "QA_GDT_AUTO%$PDL")
@@ -30,7 +30,8 @@ public class BaseCodeGenerator {
                 .addInclude(TABLE_NAMES)
                 .entityBuilder().enableFileOverride()
                 .mapperBuilder().enableFileOverride()
-                .serviceBuilder().enableFileOverride()
+                .serviceBuilder().formatServiceFileName("%sService")
+                .formatServiceImplFileName("%sServiceImp").enableFileOverride()
                 .controllerBuilder().enableRestStyle().enableFileOverride()
         )
         .templateEngine(new FreemarkerTemplateEngine())

+ 159 - 0
src/main/java/com/moka/gdtauto/controller/LoadPageConfController.java

@@ -0,0 +1,159 @@
+package com.moka.gdtauto.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+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.LoadPageConf;
+import com.moka.gdtauto.entity.SysUser;
+import com.moka.gdtauto.mapper.LoadPageConfMapper;
+import com.moka.gdtauto.service.LoadPageConfService;
+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 org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+
+/**
+ * <p>
+ * 落地页配置表 前端控制器
+ * </p>
+ *
+ * @author mokamvp
+ * @since 2026-03-10
+ */
+@Tag(name = "落地页配置管理", description = "落地页配置的增删改查")
+@Slf4j
+@RestController
+@RequestMapping("/api/load-page-conf")
+@RequiredArgsConstructor
+public class LoadPageConfController {
+
+    private final LoadPageConfService loadPageConfService;
+    private final LoadPageConfMapper loadPageConfMapper;
+
+    @GetMapping("/all")
+    public Result<List<LoadPageConf>> getAll(@RequestParam String param) {
+        List<LoadPageConf> loadPageConfList = loadPageConfService.lambdaQuery()
+        .eq(LoadPageConf::getDeleted, false)
+        .list();
+        return Result.success(loadPageConfList);
+    }
+    
+
+    /**
+     * 分页查询落地页配置列表
+     */
+    @Operation(summary = "分页查询", description = "根据条件分页查询落地页配置列表")
+    @GetMapping("/list")
+    public Result<Page<LoadPageConf>> list(
+            @Parameter(description = "页码") @RequestParam(defaultValue = "1") Long page,
+            @Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Long size,
+            @Parameter(description = "页面名称") @RequestParam(required = false) String name,
+            @Parameter(description = "落地页ID") @RequestParam(required = false) Long pageId) {
+        try {
+            Page<LoadPageConf> pageParam = new Page<>(page, size);
+            LambdaQueryWrapper<LoadPageConf> wrapper = new LambdaQueryWrapper<>();
+            wrapper.like(StringUtils.hasText(name), LoadPageConf::getName, name)
+                    .eq(pageId != null, LoadPageConf::getPageId, pageId)
+                    .orderByDesc(LoadPageConf::getCreateTime);
+            Page<LoadPageConf> result = loadPageConfService.page(pageParam, wrapper);
+            return Result.success(result);
+        } catch (Exception e) {
+            log.error("查询落地页配置列表失败", e);
+            return Result.fail("查询落地页配置列表失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据ID查询落地页配置详情
+     */
+    @Operation(summary = "查询详情", description = "根据ID查询落地页配置详情")
+    @GetMapping("/{id}")
+    public Result<LoadPageConf> getById(
+            @Parameter(description = "配置ID", required = true)
+            @PathVariable Long id) {
+        try {
+            LoadPageConf conf = loadPageConfService.getById(id);
+            if (conf == null) {
+                return Result.fail("落地页配置不存在");
+            }
+            return Result.success(conf);
+        } catch (Exception e) {
+            log.error("查询落地页配置详情失败,ID: {}", id, e);
+            return Result.fail("查询落地页配置详情失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 新增落地页配置
+     */
+    @Operation(summary = "新增配置", description = "新增落地页配置")
+    @PostMapping
+    public Result<LoadPageConf> save(
+            @RequestBody LoadPageConf loadPageConf,
+            @AuthUser SysUser sysUser) {
+        try {
+            if (!StringUtils.hasText(loadPageConf.getName())) {
+                return Result.fail("页面名称不能为空");
+            }
+            if (loadPageConf.getPageId() == null) {
+                return Result.fail("落地页ID不能为空");
+            }
+            loadPageConf.setCreateUserId(sysUser.getId());
+            loadPageConf.setCreateUserName(sysUser.getName());
+            loadPageConf.setCreateTime(LocalDateTime.now());
+            loadPageConf.setUpdateTime(LocalDateTime.now());
+            loadPageConf.setDeleted(false);
+            boolean success = loadPageConfMapper.saveIfNeed(loadPageConf);
+            if (success) {
+                return Result.success(loadPageConf, "新增成功");
+            } else {
+                return Result.fail("新增失败");
+            }
+        } catch (Exception e) {
+            log.error("新增落地页配置失败", e);
+            return Result.fail("新增落地页配置失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 更新落地页配置
+     */
+    @Operation(summary = "更新配置", description = "更新落地页配置")
+    @PutMapping
+    public Result<LoadPageConf> update(
+        @AuthUser SysUser sysUser,
+            @RequestBody LoadPageConf loadPageConf) {
+        try {
+            if (loadPageConf.getId() == null) {
+                return Result.fail("配置ID不能为空");
+            }
+            LoadPageConf existConf = loadPageConfService.getById(loadPageConf.getId());
+            if (existConf == null) {
+                return Result.fail("落地页配置不存在");
+            }
+            loadPageConf.setCreateUserId(sysUser.getId());
+            loadPageConf.setCreateUserName(sysUser.getName());
+            loadPageConf.setDeleted(false);
+            boolean success = loadPageConfMapper.saveIfNeed(loadPageConf);
+            if (success) {
+                return Result.success(loadPageConf, "更新成功");
+            } else {
+                return Result.fail("更新失败");
+            }
+        } catch (Exception e) {
+            log.error("更新落地页配置失败", e);
+            return Result.fail("更新落地页配置失败: " + e.getMessage());
+        }
+    }
+
+}

+ 141 - 0
src/main/java/com/moka/gdtauto/entity/LoadPageConf.java

@@ -0,0 +1,141 @@
+package com.moka.gdtauto.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 落地页配置表
+ * </p>
+ *
+ * @author mokamvp
+ * @since 2026-03-10
+ */
+@TableName("load_page_conf")
+public class LoadPageConf implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 页面名称
+     */
+    private String name;
+
+    /**
+     * 落地页ID
+     */
+    private Long pageId;
+
+    /**
+     * 创建用户ID
+     */
+    private Long createUserId;
+
+    /**
+     * 创建用户名称
+     */
+    private String createUserName;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 逻辑删除标识:0-未删除,1-已删除
+     */
+    private Boolean deleted;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Long getPageId() {
+        return pageId;
+    }
+
+    public void setPageId(Long pageId) {
+        this.pageId = pageId;
+    }
+
+    public Long getCreateUserId() {
+        return createUserId;
+    }
+
+    public void setCreateUserId(Long createUserId) {
+        this.createUserId = createUserId;
+    }
+
+    public String getCreateUserName() {
+        return createUserName;
+    }
+
+    public void setCreateUserName(String createUserName) {
+        this.createUserName = createUserName;
+    }
+
+    public LocalDateTime getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(LocalDateTime createTime) {
+        this.createTime = createTime;
+    }
+
+    public LocalDateTime getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(LocalDateTime updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Boolean getDeleted() {
+        return deleted;
+    }
+
+    public void setDeleted(Boolean deleted) {
+        this.deleted = deleted;
+    }
+
+    @Override
+    public String toString() {
+        return "LoadPageConf{" +
+            "id = " + id +
+            ", name = " + name +
+            ", pageId = " + pageId +
+            ", createUserId = " + createUserId +
+            ", createUserName = " + createUserName +
+            ", createTime = " + createTime +
+            ", updateTime = " + updateTime +
+            ", deleted = " + deleted +
+            "}";
+    }
+}

+ 16 - 0
src/main/java/com/moka/gdtauto/mapper/LoadPageConfMapper.java

@@ -0,0 +1,16 @@
+package com.moka.gdtauto.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.moka.gdtauto.entity.LoadPageConf;
+
+/**
+ * <p>
+ * 落地页配置表 Mapper 接口
+ * </p>
+ *
+ * @author mokamvp
+ * @since 2026-03-10
+ */
+public interface LoadPageConfMapper extends BaseMapper<LoadPageConf> {
+    boolean saveIfNeed(LoadPageConf loadPageConf);
+}

+ 16 - 0
src/main/java/com/moka/gdtauto/service/LoadPageConfService.java

@@ -0,0 +1,16 @@
+package com.moka.gdtauto.service;
+
+import com.moka.gdtauto.entity.LoadPageConf;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 落地页配置表 服务类
+ * </p>
+ *
+ * @author mokamvp
+ * @since 2026-03-10
+ */
+public interface LoadPageConfService extends IService<LoadPageConf> {
+
+}

+ 20 - 0
src/main/java/com/moka/gdtauto/service/impl/LoadPageConfServiceImpl.java

@@ -0,0 +1,20 @@
+package com.moka.gdtauto.service.impl;
+
+import com.moka.gdtauto.entity.LoadPageConf;
+import com.moka.gdtauto.mapper.LoadPageConfMapper;
+import com.moka.gdtauto.service.LoadPageConfService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 落地页配置表 服务实现类
+ * </p>
+ *
+ * @author mokamvp
+ * @since 2026-03-10
+ */
+@Service
+public class LoadPageConfServiceImpl extends ServiceImpl<LoadPageConfMapper, LoadPageConf> implements LoadPageConfService {
+
+}

+ 14 - 0
src/main/resources/mapper/LoadPageConfMapper.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.moka.gdtauto.mapper.LoadPageConfMapper">
+
+    <insert id="saveIfNeed">
+        INSERT INTO load_page_conf (name, page_id, create_user_id, create_user_name, deleted)
+        VALUES (#{name}, #{pageId}, #{createUserId}, #{createUserName}, #{deleted})
+        ON DUPLICATE KEY UPDATE 
+        name = VALUES(name),
+        create_user_id = VALUES(create_user_id),
+        create_user_name = VALUES(create_user_name), 
+        deleted = VALUES(deleted)
+    </insert>
+</mapper>

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů