Quellcode durchsuchen

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlatformDataScopeInterceptor.java
Lijingyang vor 2 Monaten
Ursprung
Commit
d15707d497
100 geänderte Dateien mit 6045 neuen und 254 gelöschten Zeilen
  1. 4 5
      ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlatformDataScopeInterceptor.java
  2. 41 9
      ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java
  3. 8 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerContactController.java
  4. 16 5
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/pc/CustomerRegisterController.java
  5. 7 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerContact.java
  6. 2 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerContract.java
  7. 5 5
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerDept.java
  8. 5 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/EnterpriseScale.java
  9. 5 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/IndustryCategory.java
  10. 3 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContactBo.java
  11. 6 5
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContractBo.java
  12. 6 5
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerDeptBo.java
  13. 10 5
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/EnterpriseScaleBo.java
  14. 5 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/IndustryCategoryBo.java
  15. 101 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/SupplierRegisterBo.java
  16. 6 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContactVo.java
  17. 2 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContractVo.java
  18. 7 6
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerDeptVo.java
  19. 5 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/EnterpriseScaleVo.java
  20. 5 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/IndustryCategoryVo.java
  21. 8 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerContactService.java
  22. 5 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerInfoService.java
  23. 6 2
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISupplierInfoService.java
  24. 85 10
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerContactServiceImpl.java
  25. 0 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerContractServiceImpl.java
  26. 14 3
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerDeptServiceImpl.java
  27. 120 37
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java
  28. 39 4
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInvoiceInfoServiceImpl.java
  29. 77 31
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerShippingAddressServiceImpl.java
  30. 1 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/EnterpriseScaleServiceImpl.java
  31. 10 9
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/IndustryCategoryServiceImpl.java
  32. 147 72
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SupplierInfoServiceImpl.java
  33. 45 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/config/CacheConstants.java
  34. 83 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/config/CacheWarmupRunner.java
  35. 164 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/MallDiyPcPageController.java
  36. 106 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/MallDiyPcTemplatePageController.java
  37. 285 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/PageCategoryController.java
  38. 162 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/PageLinkController.java
  39. 86 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/pc/DiyPcPageController.java
  40. 76 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/MallDiyPcPage.java
  41. 71 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/MallDiyPcTemplatePage.java
  42. 78 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/PageCategory.java
  43. 80 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/PageLink.java
  44. 82 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/bo/MallDiyPcPageBo.java
  45. 76 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/bo/MallDiyPcTemplatePageBo.java
  46. 80 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/bo/PageCategoryBo.java
  47. 82 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/bo/PageLinkBo.java
  48. 92 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/vo/MallDiyPcPageVo.java
  49. 86 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/vo/MallDiyPcTemplatePageVo.java
  50. 90 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/vo/PageCategoryVo.java
  51. 79 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/vo/PageLinkVo.java
  52. 61 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/event/DataSyncEvent.java
  53. 70 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/event/DataSyncListener.java
  54. 15 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/mapper/MallDiyPcPageMapper.java
  55. 15 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/mapper/MallDiyPcTemplatePageMapper.java
  56. 43 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/mapper/PageCategoryMapper.java
  57. 51 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/mapper/PageLinkMapper.java
  58. 70 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/IMallDiyPcPageService.java
  59. 70 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/IMallDiyPcTemplatePageService.java
  60. 89 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/IPageCategoryService.java
  61. 110 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/IPageLinkService.java
  62. 140 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/impl/MallDiyPcPageServiceImpl.java
  63. 139 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/impl/MallDiyPcTemplatePageServiceImpl.java
  64. 329 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/impl/PageCategoryServiceImpl.java
  65. 365 0
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/impl/PageLinkServiceImpl.java
  66. 7 0
      ruoyi-modules/ruoyi-mall/src/main/resources/mapper/mall/MallDiyPcPageMapper.xml
  67. 7 0
      ruoyi-modules/ruoyi-mall/src/main/resources/mapper/mall/MallDiyPcTemplatePageMapper.xml
  68. 121 0
      ruoyi-modules/ruoyi-mall/src/main/resources/mapper/mall/PageCategoryMapper.xml
  69. 88 0
      ruoyi-modules/ruoyi-mall/src/main/resources/mapper/mall/PageLinkMapper.xml
  70. 106 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteChannelPageController.java
  71. 1 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteController.java
  72. 106 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteFloorController.java
  73. 106 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteFloorLinkController.java
  74. 141 2
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteProductController.java
  75. 1 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteSettingController.java
  76. 1 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProtocolInfoController.java
  77. 14 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProtocolProductsController.java
  78. 53 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/DiyProductController.java
  79. 101 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/SiteProductController.java
  80. 6 3
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSite.java
  81. 76 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteChannelPage.java
  82. 87 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteFloor.java
  83. 82 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteFloorLink.java
  84. 8 2
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteProduct.java
  85. 5 10
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteSetting.java
  86. 1 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProtocolInfo.java
  87. 0 1
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProtocolProducts.java
  88. 7 2
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteBo.java
  89. 82 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteChannelPageBo.java
  90. 89 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteFloorBo.java
  91. 82 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteFloorLinkBo.java
  92. 9 2
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteProductBo.java
  93. 4 9
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteSettingBo.java
  94. 5 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductBaseBo.java
  95. 3 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ServiceCaseBo.java
  96. 93 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteChannelPageVo.java
  97. 94 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteFloorLinkVo.java
  98. 106 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteFloorVo.java
  99. 89 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteProductExportVo.java
  100. 33 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteProductImportVo.java

+ 4 - 5
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlatformDataScopeInterceptor.java

@@ -14,9 +14,7 @@ import org.apache.ibatis.mapping.SqlCommandType;
 import org.apache.ibatis.plugin.*;
 import org.dromara.common.core.context.PlatformContext;
 import org.dromara.common.core.enums.SysPlatformCode;
-import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StringUtils;
-import org.springframework.core.env.Environment;
 
 import java.lang.reflect.Field;
 import java.sql.Connection;
@@ -38,8 +36,6 @@ import java.util.Set;
 })
 public class PlatformDataScopeInterceptor implements Interceptor {
 
-    private static final Environment environment = SpringUtils.getBean(Environment.class);
-
     // ✅ 直接写死忽略表(不参与 platform_code 隔离)
     private static final Set<String> IGNORE_TABLES = new HashSet<>(Arrays.asList(
         "sys_sms_log",
@@ -101,8 +97,11 @@ public class PlatformDataScopeInterceptor implements Interceptor {
         "supply_area",
         "authorize_type_level",
         "order_return",
+        "qualification_file",
         "order_return_item",
-        "qualification_file"
+        "customer_business_info",
+        "mall_page_link",
+        "mall_page_category"
 
 
         // 注意:前缀匹配需特殊处理(如 qrtz_),见 isIgnoreTable 方法

+ 41 - 9
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java

@@ -225,6 +225,26 @@ public class RedisUtils {
         return rBucket.get();
     }
 
+    /**
+     * 获得缓存的基本对象,如果不存在则调用函数获取并设置缓存。
+     *
+     * @param key        缓存键值
+     * @param function   获取数据的函数
+     * @param expireTime 过期时间(秒)
+     * @return 缓存键值对应的数据
+     */
+    public static <T> T getCacheObject(final String key, final java.util.function.Supplier<T> function, final long expireTime) {
+        T result = getCacheObject(key);
+        if (result == null && function != null) {
+            result = function.get();
+            if (result != null) {
+                setCacheObject(key, result, Duration.ofSeconds(expireTime));
+            }
+        }
+        return result;
+    }
+
+
     /**
      * 获得key剩余存活时间
      *
@@ -236,6 +256,16 @@ public class RedisUtils {
         return rBucket.remainTimeToLive();
     }
 
+    /**
+     * 根据模式删除匹配的所有缓存对象
+     *
+     * @param pattern 模式,支持*和?通配符
+     */
+    public static void deleteObjectPattern(final String pattern) {
+        CLIENT.getKeys().deleteByPattern(pattern);
+    }
+
+
     /**
      * 删除单个对象
      *
@@ -533,29 +563,31 @@ public class RedisUtils {
 
     /**
      * 获得缓存的基本对象列表(全局匹配忽略租户 自行拼接租户id)
-     * <P>
+     * <p>
      * limit-设置扫描的限制数量(默认为0,查询全部)
      * pattern-设置键的匹配模式(默认为null)
      * chunkSize-设置每次扫描的块大小(默认为0,本方法设置为1000)
      * type-设置键的类型(默认为null,查询全部类型)
      * </P>
-     * @see KeysScanOptions
+     *
      * @param pattern 字符串前缀
      * @return 对象列表
+     * @see KeysScanOptions
      */
     public static Collection<String> keys(final String pattern) {
-        return  keys(KeysScanOptions.defaults().pattern(pattern).chunkSize(1000));
+        return keys(KeysScanOptions.defaults().pattern(pattern).chunkSize(1000));
     }
 
     /**
      * 通过扫描参数获取缓存的基本对象列表
+     *
      * @param keysScanOptions 扫描参数
-     * <P>
-     * limit-设置扫描的限制数量(默认为0,查询全部)
-     * pattern-设置键的匹配模式(默认为null)
-     * chunkSize-设置每次扫描的块大小(默认为0)
-     * type-设置键的类型(默认为null,查询全部类型)
-     * </P>
+     *                        <p>
+     *                        limit-设置扫描的限制数量(默认为0,查询全部)
+     *                        pattern-设置键的匹配模式(默认为null)
+     *                        chunkSize-设置每次扫描的块大小(默认为0)
+     *                        type-设置键的类型(默认为null,查询全部类型)
+     *                        </P>
      * @see KeysScanOptions
      */
     public static Collection<String> keys(final KeysScanOptions keysScanOptions) {

+ 8 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerContactController.java

@@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.customer.domain.bo.CustomerShippingAddressBo;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -62,7 +63,7 @@ public class CustomerContactController extends BaseController {
      */
     @GetMapping("/{id}")
     public R<CustomerContactVo> getInfo(@NotNull(message = "主键不能为空")
-                                     @PathVariable("id") Long id) {
+                                        @PathVariable("id") Long id) {
         return R.ok(customerContactService.queryById(id));
     }
 
@@ -97,4 +98,10 @@ public class CustomerContactController extends BaseController {
                           @PathVariable("ids") Long[] ids) {
         return toAjax(customerContactService.deleteWithValidByIds(List.of(ids), true));
     }
+
+    /*设置主联系人*/
+    @PutMapping("/changeIsPrimary")
+    public R<Void> changeIsPrimary(@RequestBody CustomerContactBo bo) {
+        return toAjax(customerContactService.changeIsPrimary(bo));
+    }
 }

+ 16 - 5
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/pc/CustomerRegisterController.java

@@ -8,6 +8,7 @@ package org.dromara.customer.controller.pc;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
 import org.dromara.customer.domain.bo.CustomerRegisterBo;
+import org.dromara.customer.domain.bo.SupplierRegisterBo;
 import org.dromara.customer.service.ICustomerInfoService;
 import org.dromara.customer.service.ISupplierInfoService;
 import org.springframework.validation.annotation.Validated;
@@ -27,18 +28,28 @@ public class CustomerRegisterController {
     private final ISupplierInfoService supplierInfoService;
 
     /**
-    * 企业注册
-    * */
+     * 验证密码与验证码
+     */
+    @PostMapping("/validate")
+    public R<Void> verifyCodeAndPassword(@RequestBody CustomerRegisterBo bo) {
+        customerInfoService.verifyCodeAndPssword(bo);
+        return R.ok();
+    }
+
+    /**
+     * 企业注册
+     */
     @PostMapping("/registerCustomer")
     public R<Void> register(@RequestBody CustomerRegisterBo bo) {
         customerInfoService.register(bo);
         return R.ok();
     }
+
     /**
-    * 供应商注册
-    * */
+     * 供应商注册
+     */
     @PostMapping("/registerSupplier")
-    public R<Void> registerSupplier(@RequestBody CustomerRegisterBo bo) {
+    public R<Void> registerSupplier(@RequestBody SupplierRegisterBo bo) {
         supplierInfoService.register(bo);
         return R.ok();
     }

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerContact.java

@@ -28,11 +28,18 @@ public class CustomerContact extends TenantEntity {
     @TableId(value = "id")
     private Long id;
 
+    /*系统用户id*/
+    private Long userId;
+
     /**
      * 所属客户ID
      */
     private Long customerId;
 
+
+    /*联系人编号*/
+    private String contactNo;
+
     /**
      * 联系人姓名
      */

+ 2 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerContract.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.*;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import java.math.BigDecimal;
 import java.util.Date;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -56,7 +57,7 @@ public class CustomerContract extends TenantEntity {
     /**
      * 合同金额
      */
-    private String contractAmount;
+    private BigDecimal contractAmount;
 
     /**
      * 客服负责人

+ 5 - 5
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerDept.java

@@ -66,7 +66,7 @@ public class CustomerDept extends TenantEntity {
     /**
      * 部门负责人
      */
-    private String deptManage;
+    private Long deptManage;
 
     /**
      * 是否限制预算
@@ -79,18 +79,18 @@ public class CustomerDept extends TenantEntity {
     private String selectYear;
 
     /**
-     * 费用类型
+     * 分项费用
      */
-    private String expenseType;
+    private Long expenseTypeId;
 
     /**
      * 年度剩余预算
      */
-    private Long residueYearlyBudget;
+    private BigDecimal residueYearlyBudget;
 
     /**
      * 充值金额
      */
-    private Long recharge;
+    private BigDecimal recharge;
 
 }

+ 5 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/EnterpriseScale.java

@@ -50,4 +50,9 @@ public class EnterpriseScale extends TenantEntity {
      */
     private String remark;
 
+    /**
+     * 数据源
+     */
+    private String dataSource;
+
 }

+ 5 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/IndustryCategory.java

@@ -50,4 +50,9 @@ public class IndustryCategory extends TenantEntity {
      */
     private String remark;
 
+    /**
+     * 数据源
+     */
+    private String dataSource;
+
 }

+ 3 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContactBo.java

@@ -32,6 +32,9 @@ public class CustomerContactBo extends BaseEntity {
      */
     private Long customerId;
 
+    /*联系人编号*/
+    private String contactNo;
+
     /**
      * 联系人姓名
      */

+ 6 - 5
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContractBo.java

@@ -9,6 +9,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import jakarta.validation.constraints.*;
 
+import java.math.BigDecimal;
 import java.util.Date;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -56,8 +57,8 @@ public class CustomerContractBo extends BaseEntity {
     /**
      * 合同金额
      */
-    @NotBlank(message = "合同金额不能为空", groups = {AddGroup.class, EditGroup.class})
-    private String contractAmount;
+//    @NotBlank(message = "合同金额不能为空", groups = {AddGroup.class, EditGroup.class})
+    private BigDecimal contractAmount;
 
     /**
      * 客服负责人
@@ -72,19 +73,19 @@ public class CustomerContractBo extends BaseEntity {
     /**
      * 合同类型
      */
-    @NotBlank(message = "合同类型不能为空", groups = {AddGroup.class, EditGroup.class})
+//    @NotBlank(message = "合同类型不能为空", groups = {AddGroup.class, EditGroup.class})
     private String contractType;
 
     /**
      * 合同开始时间
      */
-    @NotNull(message = "合同开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
+//    @NotNull(message = "合同开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
     private Date startTime;
 
     /**
      * 合同结束时间
      */
-    @NotNull(message = "合同结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
+//    @NotNull(message = "合同结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
     private Date endTime;
 
     /**

+ 6 - 5
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerDeptBo.java

@@ -1,5 +1,6 @@
 package org.dromara.customer.domain.bo;
 
+import cn.idev.excel.annotation.ExcelProperty;
 import org.dromara.customer.domain.CustomerDept;
 import org.dromara.common.mybatis.core.domain.BaseEntity;
 import org.dromara.common.core.validate.AddGroup;
@@ -90,7 +91,7 @@ public class CustomerDeptBo extends BaseEntity {
     /**
      * 部门负责人
      */
-    private String deptManage;
+    private Long deptManage;
 
     /**
      * 是否限制预算
@@ -103,19 +104,19 @@ public class CustomerDeptBo extends BaseEntity {
     private String selectYear;
 
     /**
-     * 费用类型
+     * 分项费用
      */
-    private String expenseType;
+    private Long expenseTypeId;
 
     /**
      * 年度剩余预算
      */
-    private Long residueYearlyBudget;
+    private BigDecimal residueYearlyBudget;
 
     /**
      * 充值金额
      */
-    private Long recharge;
+    private BigDecimal recharge;
 
     /**
      * 状态(0正常 1停用)

+ 10 - 5
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/EnterpriseScaleBo.java

@@ -1,13 +1,13 @@
 package org.dromara.customer.domain.bo;
 
-import org.dromara.customer.domain.EnterpriseScale;
-import org.dromara.common.mybatis.core.domain.BaseEntity;
-import org.dromara.common.core.validate.AddGroup;
-import org.dromara.common.core.validate.EditGroup;
 import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import jakarta.validation.constraints.*;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.EnterpriseScale;
 
 /**
  * 企业规模业务对象 enterprise_scale
@@ -38,6 +38,11 @@ public class EnterpriseScaleBo extends BaseEntity {
      */
     private String status;
 
+    /**
+     * 数据源
+     */
+    private String dataSource;
+
     /**
      * 备注
      */

+ 5 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/IndustryCategoryBo.java

@@ -43,4 +43,9 @@ public class IndustryCategoryBo extends BaseEntity {
      */
     private String remark;
 
+    /**
+     * 数据源
+     */
+    private String dataSource;
+
 }

+ 101 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/SupplierRegisterBo.java

@@ -0,0 +1,101 @@
+package org.dromara.customer.domain.bo;
+
+import lombok.Data;
+import org.dromara.customer.domain.vo.SupplyAreaVo;
+
+import java.util.List;
+
+@Data
+public class SupplierRegisterBo {
+    /**
+     * 供应商名称
+     */
+    private String enterpriseName;
+    /**
+     * 姓名
+     */
+    private String purchaseName;
+    /**
+     * 手机号
+     */
+    private String purchasePhone;
+    /**
+     * 验证码
+     */
+    private String code;
+    /**
+     * 密码
+     */
+    private String password;
+    /**
+     * 确认密码
+     */
+    private String confirmPassword;
+
+    /**
+     * 人员规模
+     */
+    private Long membershipSize;
+
+    /**
+     * 固定电话
+     */
+    private String fixedPhone;
+
+    /**
+     * 传真
+     */
+    private String fax;
+
+    /**
+     * 网址
+     */
+    private String url;
+
+    /**
+     * 邮箱
+     */
+    private String mailbox;
+
+    /**
+     * 办公地址-省
+     */
+    private String officeProvince;
+
+    /**
+     * 办公地址-市
+     */
+    private String officeCity;
+
+    /**
+     * 办公地址-区/县
+     */
+    private String officeCounty;
+
+    /**
+     * 办公详细地址
+     */
+    private String officeAddress;
+
+    /**
+     * 统一社会信用代码
+     */
+    private String socialCreditCode;
+
+    /**
+     * 营业执照图片路径
+     */
+    private String businessLicense;
+
+    /**
+     * 年销售额
+     */
+    private Long yearSales;
+
+    /**
+     * 经营品类
+     */
+    private String operatingCategory;
+
+    List<SupplyAreaBo> supplyAreaList;
+}

+ 6 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContactVo.java

@@ -33,6 +33,12 @@ public class CustomerContactVo implements Serializable {
     @ExcelProperty(value = "联系人ID")
     private Long id;
 
+    /*联系人编号*/
+    private String contactNo;
+
+    /*系统用户id*/
+    private Long userId;
+
     /**
      * 所属客户ID
      */

+ 2 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContractVo.java

@@ -1,5 +1,6 @@
 package org.dromara.customer.domain.vo;
 
+import java.math.BigDecimal;
 import java.util.Date;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -66,7 +67,7 @@ public class CustomerContractVo implements Serializable {
      * 合同金额
      */
     @ExcelProperty(value = "合同金额")
-    private String contractAmount;
+    private BigDecimal contractAmount;
 
     /**
      * 客服负责人

+ 7 - 6
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerDeptVo.java

@@ -106,7 +106,7 @@ public class CustomerDeptVo implements Serializable {
      * 部门负责人
      */
     @ExcelProperty(value = "部门负责人")
-    private String deptManage;
+    private Long deptManage;
 
     /**
      * 是否限制预算
@@ -120,23 +120,24 @@ public class CustomerDeptVo implements Serializable {
     @ExcelProperty(value = "所选年份")
     private String selectYear;
 
+
     /**
-     * 费用类型
+     * 分项费用
      */
-    @ExcelProperty(value = "费用类型")
-    private String expenseType;
+    @ExcelProperty(value = "分项费用")
+    private Long expenseTypeId;
 
     /**
      * 年度剩余预算
      */
     @ExcelProperty(value = "年度剩余预算")
-    private Long residueYearlyBudget;
+    private BigDecimal residueYearlyBudget;
 
     /**
      * 充值金额
      */
     @ExcelProperty(value = "充值金额")
-    private Long recharge;
+    private BigDecimal recharge;
 
     /**
      * 状态(0正常 1停用)

+ 5 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/EnterpriseScaleVo.java

@@ -54,4 +54,9 @@ public class EnterpriseScaleVo implements Serializable {
     @ExcelProperty(value = "备注")
     private String remark;
 
+    /**
+     * 数据源
+     */
+    private String dataSource;
+
 }

+ 5 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/IndustryCategoryVo.java

@@ -54,4 +54,9 @@ public class IndustryCategoryVo implements Serializable {
     @ExcelProperty(value = "备注")
     private String remark;
 
+    /**
+     * 数据源
+     */
+    private String dataSource;
+
 }

+ 8 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerContactService.java

@@ -2,6 +2,7 @@ package org.dromara.customer.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.dromara.customer.domain.CustomerContact;
+import org.dromara.customer.domain.bo.CustomerShippingAddressBo;
 import org.dromara.customer.domain.vo.CustomerContactVo;
 import org.dromara.customer.domain.bo.CustomerContactBo;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -16,7 +17,7 @@ import java.util.List;
  * @author LionLi
  * @date 2025-12-11
  */
-public interface ICustomerContactService extends IService<CustomerContact>{
+public interface ICustomerContactService extends IService<CustomerContact> {
 
     /**
      * 查询客户联系人信息
@@ -59,6 +60,12 @@ public interface ICustomerContactService extends IService<CustomerContact>{
      */
     Boolean updateByBo(CustomerContactBo bo);
 
+    /**
+     * 设置主联系人
+     */
+    int changeIsPrimary(CustomerContactBo bo);
+
+
     /**
      * 校验并批量删除客户联系人信息信息
      *

+ 5 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerInfoService.java

@@ -131,8 +131,13 @@ public interface ICustomerInfoService extends IService<CustomerInfo> {
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
 
+    /*验证密码与验证码*/
+    Boolean verifyCodeAndPssword(CustomerRegisterBo bo);
+
     /**
      * 客户注册
      */
     Boolean register(CustomerRegisterBo bo);
+
+
 }

+ 6 - 2
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISupplierInfoService.java

@@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotNull;
 import org.dromara.customer.domain.SupplierInfo;
 import org.dromara.customer.domain.bo.CustomerRegisterBo;
 import org.dromara.customer.domain.bo.SupplierAuthorizeBo;
+import org.dromara.customer.domain.bo.SupplierRegisterBo;
 import org.dromara.customer.domain.vo.SupplierInfoVo;
 import org.dromara.customer.domain.bo.SupplierInfoBo;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -24,7 +25,7 @@ import java.util.Set;
  * @author LionLi
  * @date 2026-01-09
  */
-public interface ISupplierInfoService extends IService<SupplierInfo>{
+public interface ISupplierInfoService extends IService<SupplierInfo> {
 
     /**
      * 查询供应商信息
@@ -42,6 +43,7 @@ public interface ISupplierInfoService extends IService<SupplierInfo>{
      * @return 供应商信息分页列表
      */
     TableDataInfo<SupplierInfoVo> queryPageList(SupplierInfoBo bo, PageQuery pageQuery);
+
     /**
      * 获取供应商信息列表
      *
@@ -54,6 +56,7 @@ public interface ISupplierInfoService extends IService<SupplierInfo>{
 
     /**
      * 获取供应商审核列表
+     *
      * @param bo
      * @param pageQuery
      * @return
@@ -109,9 +112,10 @@ public interface ISupplierInfoService extends IService<SupplierInfo>{
 
     /**
      * 注册供应商
+     *
      * @param bo
      */
-    Boolean register(CustomerRegisterBo bo);
+    Boolean register(SupplierRegisterBo bo);
 
     List<SupplierInfoVo> getNameList();
 

+ 85 - 10
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerContactServiceImpl.java

@@ -1,27 +1,38 @@
 package org.dromara.customer.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.core.enums.IsDefault;
+import org.dromara.common.core.enums.SysPlatformYesNo;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.dromara.system.api.RemoteDeptService;
-import org.springframework.stereotype.Service;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.redis.utils.SequenceUtils;
+import org.dromara.customer.domain.CustomerContact;
+import org.dromara.customer.domain.CustomerShippingAddress;
 import org.dromara.customer.domain.bo.CustomerContactBo;
+import org.dromara.customer.domain.bo.CustomerShippingAddressBo;
 import org.dromara.customer.domain.vo.CustomerContactVo;
-import org.dromara.customer.domain.CustomerContact;
+import org.dromara.customer.domain.vo.CustomerInfoVo;
+import org.dromara.customer.domain.vo.CustomerShippingAddressVo;
 import org.dromara.customer.mapper.CustomerContactMapper;
+import org.dromara.customer.mapper.CustomerInfoMapper;
 import org.dromara.customer.service.ICustomerContactService;
+import org.dromara.system.api.RemoteDeptService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
+import java.time.Duration;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Collection;
 
 /**
  * 客户联系人信息Service业务层处理
@@ -34,11 +45,14 @@ import java.util.Collection;
 @Service
 public class CustomerContactServiceImpl extends ServiceImpl<CustomerContactMapper, CustomerContact> implements ICustomerContactService {
 
+    private static final String CONTACT_NO_KEY = "customer_contact:contact_no";
     @DubboReference
     private RemoteDeptService remoteDeptService;
 
     private final CustomerContactMapper baseMapper;
 
+    private final CustomerInfoMapper customerInfoMapper;
+
     /**
      * 查询客户联系人信息
      *
@@ -116,7 +130,32 @@ public class CustomerContactServiceImpl extends ServiceImpl<CustomerContactMappe
      */
     @Override
     public Boolean insertByBo(CustomerContactBo bo) {
+        // 1. 获取并校验客户编号
+        String customerNo = null;
+        if (bo.getCustomerId() != null) {
+            CustomerInfoVo customerInfoVo = customerInfoMapper.selectVoById(bo.getCustomerId());
+
+            // 【关键修复】防御性编程:如果查不到客户信息,应直接抛错或返回失败,避免生成 "nullxxx" 数据
+            if (customerInfoVo == null) {
+                log.error("客户不存在,无法创建联系人 (ID: " + bo.getCustomerId() + ")");
+                throw new RuntimeException("客户不存在,无法创建联系人 (ID: " + bo.getCustomerId() + ")");
+            }
+
+            customerNo = customerInfoVo.getCustomerNo();
+
+            // 二次校验:防止 customerNo 本身为空
+            if (StringUtils.isBlank(customerNo)) {
+                log.error("客户编号为空,数据异常 (ID: " + bo.getCustomerId() + ")");
+                throw new RuntimeException("客户编号为空,数据异常 (ID: " + bo.getCustomerId() + ")");
+            }
+        }
+
+        // 2. 生成联系人编号
+        String seqId = SequenceUtils.nextPaddedIdStr(CONTACT_NO_KEY, Duration.ofDays(3650), 3);
+        String contactNo = customerNo + seqId;
+
         CustomerContact add = MapstructUtils.convert(bo, CustomerContact.class);
+        add.setContactNo(contactNo);
         validEntityBeforeSave(add);
         boolean flag = baseMapper.insert(add) > 0;
         if (flag) {
@@ -138,6 +177,42 @@ public class CustomerContactServiceImpl extends ServiceImpl<CustomerContactMappe
         return baseMapper.updateById(update) > 0;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int changeIsPrimary(CustomerContactBo bo) {
+        if (bo == null || bo.getId() == null) {
+            return 0;
+        }
+        CustomerContactVo contactVo = baseMapper.selectVoById(bo.getId());
+        if (SysPlatformYesNo.YES.getCode().equals(bo.getIsPrimary())) {
+            // 1. 清除该客户下所有主联系人
+            CustomerContact updateObj = new CustomerContact();
+            updateObj.setIsPrimary(SysPlatformYesNo.NO.getCode()); //
+
+            baseMapper.update(
+                updateObj,
+                new LambdaUpdateWrapper<CustomerContact>()
+                    .eq(CustomerContact::getCustomerId, contactVo.getCustomerId()) //
+                    .eq(CustomerContact::getIsPrimary, SysPlatformYesNo.YES.getCode())
+            );
+
+            // 2. 设置当前地址为默认
+            CustomerContact current = new CustomerContact();
+            current.setId(bo.getId());
+            current.setIsPrimary(SysPlatformYesNo.YES.getCode());
+            baseMapper.updateById(current);
+
+            return 1;
+        } else {
+            // 取消默认
+            CustomerContact current = new CustomerContact();
+            current.setId(bo.getId());
+            current.setIsPrimary(SysPlatformYesNo.NO.getCode());
+            baseMapper.updateById(current);
+            return 1;
+        }
+    }
+
     /**
      * 保存前的数据校验
      */

+ 0 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerContractServiceImpl.java

@@ -82,7 +82,6 @@ public class CustomerContractServiceImpl extends ServiceImpl<CustomerContractMap
         lqw.eq(bo.getCustomerId() != null, CustomerContract::getCustomerId, bo.getCustomerId());
         lqw.eq(StringUtils.isNotBlank(bo.getContractNo()), CustomerContract::getContractNo, bo.getContractNo());
         lqw.like(StringUtils.isNotBlank(bo.getContractName()), CustomerContract::getContractName, bo.getContractName());
-        lqw.eq(StringUtils.isNotBlank(bo.getContractAmount()), CustomerContract::getContractAmount, bo.getContractAmount());
         lqw.eq(StringUtils.isNotBlank(bo.getCustomerLeader()), CustomerContract::getCustomerLeader, bo.getCustomerLeader());
         lqw.eq(StringUtils.isNotBlank(bo.getProjectLeader()), CustomerContract::getProjectLeader, bo.getProjectLeader());
         lqw.eq(StringUtils.isNotBlank(bo.getContractType()), CustomerContract::getContractType, bo.getContractType());

+ 14 - 3
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerDeptServiceImpl.java

@@ -19,6 +19,7 @@ import org.dromara.customer.mapper.CustomerDeptMapper;
 import org.dromara.customer.mapper.CustomerShippingAddressMapper;
 import org.dromara.customer.service.ICustomerDeptService;
 import org.dromara.system.api.RemoteDeptService;
+import org.dromara.system.api.RemoteUserService;
 import org.dromara.system.api.domain.vo.RemoteDeptVo;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -41,6 +42,9 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
     @DubboReference
     private RemoteDeptService remoteDeptService;
 
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
     private static final String DEPT_NO_KEY = "customer_dept:dept_no";
 
     private final CustomerDeptMapper baseMapper;
@@ -60,6 +64,7 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
         if (null != remoteDeptVo) {
             customerDeptVo.setDeptName(remoteDeptVo.getDeptName());
             customerDeptVo.setParentId(remoteDeptVo.getParentId());
+            customerDeptVo.setStatus(remoteDeptVo.getStatus());
         }
         return customerDeptVo;
     }
@@ -152,7 +157,7 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
         return customerDepts.stream()
             .map(cd -> deptMap.get(cd.getDeptId())) // 直接 get,null 表示不匹配
             .filter(Objects::nonNull) // 过滤掉 deptMap 中不存在的
-            .map(sys -> buildCustomerDeptTreeVo(sys, customerDeptsMap.get(sys.getDeptId()), addressVoMap)) // 假设你有 customerDepts 按 deptId 的 map
+            .map(sys -> buildCustomerDeptTreeVo(sys, customerDeptsMap.get(sys.getDeptId()), addressVoMap))
             .sorted(Comparator.comparing(
                 CustomerDeptTreeVo::getOrderNum,
                 Comparator.nullsLast(Comparator.naturalOrder())
@@ -198,7 +203,6 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
         lqw.eq(bo.getMonthLimit() != null, CustomerDept::getMonthLimit, bo.getMonthLimit());
         lqw.eq(bo.getMonthUsedBudget() != null, CustomerDept::getMonthUsedBudget, bo.getMonthUsedBudget());
         lqw.eq(StringUtils.isNotBlank(bo.getBindStatus()), CustomerDept::getBindStatus, bo.getBindStatus());
-        lqw.eq(StringUtils.isNotBlank(bo.getDeptManage()), CustomerDept::getDeptManage, bo.getDeptManage());
         lqw.eq(StringUtils.isNotBlank(bo.getIsLimit()), CustomerDept::getIsLimit, bo.getIsLimit());
         lqw.eq(StringUtils.isNotBlank(bo.getSelectYear()), CustomerDept::getSelectYear, bo.getSelectYear());
         lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), CustomerDept::getPlatformCode, bo.getPlatformCode());
@@ -219,6 +223,7 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
             RemoteDeptVo remoteDeptVo = new RemoteDeptVo();
             remoteDeptVo.setParentId(bo.getParentId());
             remoteDeptVo.setDeptName(bo.getDeptName());
+            remoteDeptVo.setStatus(bo.getStatus());
             remoteDeptVo.setPlatformCode(PlatformContext.getPlatform());
 
 
@@ -238,6 +243,12 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
             dept.setUsedBudget(bo.getUsedBudget());
             dept.setBindStatus(bo.getBindStatus());
             dept.setBindAddress(bo.getBindAddress());
+            dept.setIsLimit(bo.getIsLimit());
+            dept.setSelectYear(bo.getSelectYear());
+            dept.setExpenseTypeId(bo.getExpenseTypeId());
+            dept.setDeptManage(bo.getDeptManage());
+            dept.setRecharge(bo.getRecharge());
+            dept.setResidueYearlyBudget(bo.getResidueYearlyBudget());
 
             boolean success = baseMapper.insert(dept) > 0;
             if (!success) {
@@ -271,7 +282,7 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
         // 更新远程部门字段
         remoteDeptVo.setDeptName(bo.getDeptName());
         remoteDeptVo.setParentId(bo.getParentId());
-
+        remoteDeptVo.setStatus(bo.getStatus());
         RemoteDeptVo updatedRemoteDept = remoteDeptService.updateDept(remoteDeptVo);
         if (updatedRemoteDept == null) {
             log.error("远程部门更新失败,deptId={}", deptId);

+ 120 - 37
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java

@@ -20,6 +20,7 @@ import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.redis.utils.SequenceUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.customer.domain.*;
 import org.dromara.customer.domain.bo.*;
@@ -35,6 +36,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.time.Duration;
 import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -66,6 +68,9 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
     @DubboReference
     private RemoteCreditLevelService remoteCreditLevelService;
 
+    private static final String CUSTOMER_NO_KEY = "customer_info:customer_no";
+    private static final String CONTACT_NO_KEY = "customer_contact:contact_no";
+
     private final CustomerInfoMapper baseMapper;
     private final CustomerBusinessInfoMapper customerBusinessInfoMapper;
     private final CustomerInvoiceInfoMapper customerInvoiceInfoMapper;
@@ -488,7 +493,7 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
     private LambdaQueryWrapper<CustomerInfo> buildQueryWrapper(CustomerInfoBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<CustomerInfo> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(CustomerInfo::getId);
+        lqw.orderByDesc(CustomerInfo::getId);
         lqw.eq(StringUtils.isNotBlank(bo.getCustomerNo()), CustomerInfo::getCustomerNo, bo.getCustomerNo());
         lqw.eq(bo.getBelongCompanyId() != null, CustomerInfo::getBelongCompanyId, bo.getBelongCompanyId());
         lqw.like(StringUtils.isNotBlank(bo.getCompanyName()), CustomerInfo::getCompanyName, bo.getCompanyName());
@@ -557,7 +562,7 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
             /*新增客户时给每个客户创建自己的组织架构*/
             RemoteDeptVo remoteDeptVo = new RemoteDeptVo();
             remoteDeptVo.setParentId(100L);
-            remoteDeptVo.setDeptName(bo.getCustomerName());
+            remoteDeptVo.setDeptName(bo.getBusinessCustomerName());
             remoteDeptVo.setPlatformCode(platform);
             RemoteDeptVo deptVo = remoteDeptService.insertDept(remoteDeptVo);
 
@@ -604,27 +609,34 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
 
             // 联系人列表
             if (contactList != null && !contactList.isEmpty()) {
-                CustomerContactBo primaryContact = contactList.stream()
-                    .filter(contact -> contact.getIsPrimary().equals("0"))
-                    .findFirst()
-                    .orElse(null);
-                RemoteUserBo remoteUserBo = new RemoteUserBo();
-                remoteUserBo.setNickName(bo.getCustomerName());
-                if (null != primaryContact) {
-                    remoteUserBo.setUserName(primaryContact.getPhone());
-                } else {
-                    remoteUserBo.setUserName(contactList.get(0).getPhone());
-                }
-
-                String password = "123456";
-                remoteUserBo.setPassword(BCrypt.hashpw(password));
-                remoteUserBo.setUserSonType("3");
-                remoteUserBo.setTenantId(LoginHelper.getTenantId());
-                remoteUserService.addUser(remoteUserBo);
                 for (CustomerContactBo contactBo : contactList) {
+                    // 1. 准备创建系统用户的数据
+                    RemoteUserBo remoteUserBo = new RemoteUserBo();
+                    remoteUserBo.setNickName(contactBo.getContactName());
+                    remoteUserBo.setUserName(contactBo.getPhone());// 用手机号做账号
+                    String defaultPassword = "123456";
+                    remoteUserBo.setPassword(BCrypt.hashpw(defaultPassword));
+                    remoteUserBo.setUserSonType("3");
+                    remoteUserBo.setTenantId(LoginHelper.getTenantId());
+
+                    // 2. 调用远程服务创建用户
+                    Long userId = remoteUserService.addUser(remoteUserBo);
+
+                    if (userId == null) {
+                        throw new ServiceException("创建系统用户失败:" + userId);
+                    }
+
+                    // 3. 转换并填充业务联系人数据
                     CustomerContact contact = MapstructUtils.convert(contactBo, CustomerContact.class);
                     contact.setCustomerId(customerId);
-                    customerContactMapper.insert(contact);
+
+                    // 建立关联
+                    contact.setUserId(userId);
+
+                    // 4. 插入联系人表
+                    if (customerContactMapper.insert(contact) <= 0) {
+                        throw new ServiceException("保存联系人信息失败");
+                    }
                 }
             }
 
@@ -872,6 +884,22 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
         return baseMapper.deleteByIds(ids) > 0;
     }
 
+    @Override
+    public Boolean verifyCodeAndPssword(CustomerRegisterBo bo) {
+        //先校验验证码是否正确
+        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + bo.getPurchasePhone());
+        if (!code.equals(bo.getCode())) {
+            throw new ServiceException("验证码错误");
+
+        }
+
+        //校验密码与确认密码是否一致
+        if (!bo.getPassword().equals(bo.getConfirmPassword())) {
+            throw new ServiceException("密码与确认密码不一致");
+        }
+        return true;
+    }
+
     /**
      * 客户注册
      *
@@ -882,7 +910,10 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
     public Boolean register(CustomerRegisterBo bo) {
         //先校验验证码是否正确
         String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + bo.getPurchasePhone());
-        code = "123456";
+        if (code == null) {
+            throw new ServiceException("验证码已过期");
+        }
+
         if (!code.equals(bo.getCode())) {
             throw new ServiceException("验证码错误");
         }
@@ -893,9 +924,13 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
         }
 
         //查询数据库是否存在企业,已存在则提示客户已存在
-        CustomerInfo customerInfo = baseMapper.selectOne(new LambdaQueryWrapper<CustomerInfo>()
-            .eq(CustomerInfo::getCustomerName, bo.getCustomerName())
+        List<CustomerInfo> list = baseMapper.selectList(
+            new LambdaQueryWrapper<CustomerInfo>()
+                .eq(CustomerInfo::getCustomerName, bo.getCustomerName())
+                .last("LIMIT 1")
         );
+
+        CustomerInfo customerInfo = list.isEmpty() ? null : list.get(0);
         if (customerInfo != null) {
             throw new ServiceException("客户已存在");
         }
@@ -909,8 +944,10 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
         //构建客户信息实体
         CustomerInfo customerEntity = new CustomerInfo();
         customerEntity.setCustomerName(bo.getCustomerName());
+        customerEntity.setCustomerNo(SequenceUtils.nextPaddedIdStr(CUSTOMER_NO_KEY, Duration.ofDays(3650), 6));
         customerEntity.setBusinessCustomerName(companyInfo.getResult().getName());
         customerEntity.setShortName(companyInfo.getResult().getName());
+        customerEntity.setInvoiceTop(companyInfo.getResult().getName());
         customerEntity.setStatus("0"); // 正常状态
         customerEntity.setDelFlag("0"); // 未删除
 
@@ -921,27 +958,85 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
 
         Long customerId = customerEntity.getId();
 
+        String customerNo = customerEntity.getCustomerNo();
+
+        /*新增客户时给每个客户创建自己的组织架构*/
+        RemoteDeptVo remoteDeptVo = new RemoteDeptVo();
+        remoteDeptVo.setParentId(100L);
+        remoteDeptVo.setDeptName(companyInfo.getResult().getName());
+        RemoteDeptVo deptVo = remoteDeptService.insertDept(remoteDeptVo);
+
+        CustomerDept dept = new CustomerDept();
+        dept.setDeptId(deptVo.getDeptId());
+        dept.setCustomerId(customerId);
+        dept.setYearlyBudget(BigDecimal.ZERO);
+        dept.setMonthLimit(BigDecimal.ZERO);
+        dept.setUsedBudget(BigDecimal.ZERO);
+        dept.setBindStatus(IsDefault.No.getCode());
+
+        if (customerDeptMapper.insert(dept) <= 0) {
+            throw new ServiceException("客户部门新增失败");
+        }
+
         // 添加客户工商信息
         CustomerBusinessInfo businessInfo = new CustomerBusinessInfo();
         businessInfo.setCustomerId(customerId);
         businessInfo.setBusinessCustomerName(companyInfo.getResult().getName());
         businessInfo.setSocialCreditCode(companyInfo.getResult().getCreditCode());
         businessInfo.setLegalPersonName(companyInfo.getResult().getOperName());
-        businessInfo.setRegisteredCapital(companyInfo.getResult().getRegistCapi());
+//        businessInfo.setRegisteredCapital(companyInfo.getResult().getRegistCapi());
+
+        // 注册资本:拼接数值 + 单位(如 "101万元")
+        String regCap = companyInfo.getResult().getRegisteredCapital();
+        String regUnit = companyInfo.getResult().getRegisteredCapitalUnit();
+        businessInfo.setRegisteredCapital(
+            StringUtils.isNotBlank(regCap) && StringUtils.isNotBlank(regUnit)
+                ? regCap + regUnit
+                : regCap
+        );
+
+        // 实缴资本
+        String paidCap = companyInfo.getResult().getPaidUpCapital();
+        String paidUnit = companyInfo.getResult().getPaidUpCapitalUnit();
+        businessInfo.setPaidInCapital(
+            StringUtils.isNotBlank(paidCap) && StringUtils.isNotBlank(paidUnit)
+                ? paidCap + paidUnit
+                : paidCap
+        );
+
         businessInfo.setEstablishmentDate(DateUtils.parseDate((companyInfo.getResult().getStartDate())));
+        businessInfo.setRevocationDate(null);
         businessInfo.setRegistrationStatus(companyInfo.getResult().getStatus());
+        businessInfo.setRegistrationAuthority(companyInfo.getResult().getBelongOrg());
         businessInfo.setBusinessAddress(companyInfo.getResult().getAddress());
         businessInfo.setBussinessRange(companyInfo.getResult().getScope());
         businessInfo.setStatus("0");
-        businessInfo.setDelFlag("0");
 
         if (customerBusinessInfoMapper.insert(businessInfo) <= 0) {
             throw new ServiceException("客户工商信息新增失败");
         }
 
+        // 创建系统用户
+        RemoteUserBo remoteUserBo = new RemoteUserBo();
+        remoteUserBo.setDeptId(deptVo.getDeptId());
+        remoteUserBo.setUserName(bo.getPurchasePhone());
+        remoteUserBo.setNickName(bo.getCustomerName());
+        remoteUserBo.setPhonenumber(bo.getPurchasePhone());
+        remoteUserBo.setPassword(BCrypt.hashpw(bo.getPassword()));
+        remoteUserBo.setUserSonType("3"); // 商城用户
+        remoteUserBo.setTenantId(LoginHelper.getTenantId());
+        remoteUserBo.setStatus("0"); // 正常状态
+        Long userId = remoteUserService.addUser(remoteUserBo);
+
+
         // 添加客户联系人信息
         CustomerContact contact = new CustomerContact();
         contact.setCustomerId(customerId);
+        //  生成联系人编号
+        String seqId = SequenceUtils.nextPaddedIdStr(CONTACT_NO_KEY, Duration.ofDays(3650), 3);
+        String contactNo = customerNo + seqId;
+        contact.setContactNo(contactNo);
+        contact.setUserId(userId);
         contact.setContactName(bo.getPurchaseName());
         contact.setPhone(bo.getPurchasePhone());
         contact.setCustomLoginName(bo.getPurchasePhone());
@@ -953,18 +1048,6 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
             throw new ServiceException("客户联系人信息新增失败");
         }
 
-        // 创建系统用户
-        RemoteUserBo remoteUserBo = new RemoteUserBo();
-        remoteUserBo.setUserName(bo.getPurchasePhone());
-        remoteUserBo.setNickName(bo.getCustomerName());
-        remoteUserBo.setPhonenumber(bo.getPurchasePhone());
-        remoteUserBo.setPassword(BCrypt.hashpw(bo.getPassword()));
-        remoteUserBo.setUserSonType("3"); // 商城用户
-        remoteUserBo.setTenantId(LoginHelper.getTenantId());
-        remoteUserBo.setStatus("0"); // 正常状态
-
-        remoteUserService.addUser(remoteUserBo);
-
         log.info("客户注册成功,客户ID:{},客户名称:{}", customerId, bo.getCustomerName());
         return true;
     }

+ 39 - 4
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInvoiceInfoServiceImpl.java

@@ -1,6 +1,8 @@
 package org.dromara.customer.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.dromara.common.core.enums.SysPlatformYesNo;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -16,6 +18,7 @@ import org.dromara.customer.domain.vo.CustomerInvoiceInfoVo;
 import org.dromara.customer.domain.CustomerInvoiceInfo;
 import org.dromara.customer.mapper.CustomerInvoiceInfoMapper;
 import org.dromara.customer.service.ICustomerInvoiceInfoService;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 import java.util.Map;
@@ -30,7 +33,7 @@ import java.util.Collection;
 @Slf4j
 @RequiredArgsConstructor
 @Service
-public class CustomerInvoiceInfoServiceImpl  extends ServiceImpl<CustomerInvoiceInfoMapper, CustomerInvoiceInfo> implements ICustomerInvoiceInfoService {
+public class CustomerInvoiceInfoServiceImpl extends ServiceImpl<CustomerInvoiceInfoMapper, CustomerInvoiceInfo> implements ICustomerInvoiceInfoService {
 
     private final CustomerInvoiceInfoMapper baseMapper;
 
@@ -41,7 +44,7 @@ public class CustomerInvoiceInfoServiceImpl  extends ServiceImpl<CustomerInvoice
      * @return 客户开票信息
      */
     @Override
-    public CustomerInvoiceInfoVo queryById(Long id){
+    public CustomerInvoiceInfoVo queryById(Long id) {
         return baseMapper.selectVoById(id);
     }
 
@@ -96,9 +99,22 @@ public class CustomerInvoiceInfoServiceImpl  extends ServiceImpl<CustomerInvoice
      * @return 是否新增成功
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean insertByBo(CustomerInvoiceInfoBo bo) {
         CustomerInvoiceInfo add = MapstructUtils.convert(bo, CustomerInvoiceInfo.class);
+
+        // 1. 如果设为“主账户”,先将该客户下其他所有“主账户”降级为“非主账户”
+        if (SysPlatformYesNo.YES.getCode().equals(bo.getIsPrimaryAccount())) {
+            baseMapper.update(null, new LambdaUpdateWrapper<CustomerInvoiceInfo>()
+                .eq(CustomerInvoiceInfo::getCustomerId, add.getCustomerId())
+                .eq(CustomerInvoiceInfo::getIsPrimaryAccount, SysPlatformYesNo.YES.getCode())
+                .set(CustomerInvoiceInfo::getIsPrimaryAccount, SysPlatformYesNo.NO.getCode()));
+        }
+
+        // 2. 业务校验
         validEntityBeforeSave(add);
+
+        // 3. 执行插入
         boolean flag = baseMapper.insert(add) > 0;
         if (flag) {
             bo.setId(add.getId());
@@ -113,16 +129,35 @@ public class CustomerInvoiceInfoServiceImpl  extends ServiceImpl<CustomerInvoice
      * @return 是否修改成功
      */
     @Override
+    @Transactional(rollbackFor = Exception.class) // 确保事务控制
     public Boolean updateByBo(CustomerInvoiceInfoBo bo) {
         CustomerInvoiceInfo update = MapstructUtils.convert(bo, CustomerInvoiceInfo.class);
+
+        // 防御性检查:确保 ID 存在
+        if (update.getId() == null) {
+            return false;
+        }
+
+        // 1. 如果设为“主账户”,先将该客户下其他所有“主账户”降级
+        if (SysPlatformYesNo.YES.getCode().equals(bo.getIsPrimaryAccount())) {
+            baseMapper.update(null, new LambdaUpdateWrapper<CustomerInvoiceInfo>()
+                .eq(CustomerInvoiceInfo::getCustomerId, update.getCustomerId())
+                .eq(CustomerInvoiceInfo::getIsPrimaryAccount, SysPlatformYesNo.YES.getCode())
+                .ne(CustomerInvoiceInfo::getId, update.getId()) // 排除自己
+                .set(CustomerInvoiceInfo::getIsPrimaryAccount, SysPlatformYesNo.NO.getCode()));
+        }
+
+        // 2. 业务校验
         validEntityBeforeSave(update);
+
+        // 3. 执行更新
         return baseMapper.updateById(update) > 0;
     }
 
     /**
      * 保存前的数据校验
      */
-    private void validEntityBeforeSave(CustomerInvoiceInfo entity){
+    private void validEntityBeforeSave(CustomerInvoiceInfo entity) {
         //TODO 做一些数据校验,如唯一约束
     }
 
@@ -135,7 +170,7 @@ public class CustomerInvoiceInfoServiceImpl  extends ServiceImpl<CustomerInvoice
      */
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
+        if (isValid) {
             //TODO 做一些业务上的校验,判断是否需要校验
         }
         return baseMapper.deleteByIds(ids) > 0;

+ 77 - 31
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerShippingAddressServiceImpl.java

@@ -15,12 +15,15 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.redis.utils.SequenceUtils;
 import org.dromara.customer.domain.CustomerShippingAddress;
 import org.dromara.customer.domain.bo.CustomerShippingAddressBo;
+import org.dromara.customer.domain.vo.CustomerInfoVo;
 import org.dromara.customer.domain.vo.CustomerShippingAddressVo;
+import org.dromara.customer.mapper.CustomerInfoMapper;
 import org.dromara.customer.mapper.CustomerShippingAddressMapper;
 import org.dromara.customer.service.ICustomerShippingAddressService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.rmi.ServerException;
 import java.time.Duration;
 import java.util.Collection;
 import java.util.List;
@@ -41,6 +44,8 @@ public class CustomerShippingAddressServiceImpl extends ServiceImpl<CustomerShip
 
     private final CustomerShippingAddressMapper baseMapper;
 
+    private final CustomerInfoMapper customerInfoMapper;
+
     /**
      * 查询客户收货地址
      *
@@ -110,14 +115,49 @@ public class CustomerShippingAddressServiceImpl extends ServiceImpl<CustomerShip
      * @return 是否新增成功
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean insertByBo(CustomerShippingAddressBo bo) {
-        bo.setShippingAddressNo(SequenceUtils.nextPaddedIdStr(SHOPPING_ADDRESS_NO_KEY, Duration.ofDays(3650), 4));
+        // 1. 获取并校验客户编号
+        String customerNo = null;
+        if (bo.getCustomerId() != null) {
+            CustomerInfoVo customerInfoVo = customerInfoMapper.selectVoById(bo.getCustomerId());
+
+            // 【关键修复】防御性编程:如果查不到客户信息,应直接抛错或返回失败,避免生成 "nullxxx" 数据
+            if (customerInfoVo == null) {
+                log.error("客户不存在,无法创建收货地址 (ID: " + bo.getCustomerId() + ")");
+                throw new RuntimeException("客户不存在,无法创建收货地址 (ID: " + bo.getCustomerId() + ")");
+            }
+
+            customerNo = customerInfoVo.getCustomerNo();
+
+            // 二次校验:防止 customerNo 本身为空
+            if (StringUtils.isBlank(customerNo)) {
+                log.error("客户编号为空,数据异常 (ID: " + bo.getCustomerId() + ")");
+                throw new RuntimeException("客户编号为空,数据异常 (ID: " + bo.getCustomerId() + ")");
+            }
+        }
+
+        // 2. 生成收货地址编号
+        String seqId = SequenceUtils.nextPaddedIdStr(SHOPPING_ADDRESS_NO_KEY, Duration.ofDays(3650), 3);
+        String shippingAddressNo = customerNo + seqId;
+
+        // 3. 对象转换
         CustomerShippingAddress add = MapstructUtils.convert(bo, CustomerShippingAddress.class);
+
+        // 4. 设置生成的业务字段
+        add.setShippingAddressNo(shippingAddressNo);
+
+        // 5. 业务校验
         validEntityBeforeSave(add);
+
+        // 6. 执行插入
         boolean flag = baseMapper.insert(add) > 0;
+
+        // 7. 回填 ID
         if (flag) {
             bo.setId(add.getId());
         }
+
         return flag;
     }
 
@@ -137,36 +177,42 @@ public class CustomerShippingAddressServiceImpl extends ServiceImpl<CustomerShip
     @Override
     @Transactional(rollbackFor = Exception.class)
     public int changeDefaultAddress(CustomerShippingAddressBo bo) {
-        if (bo == null || bo.getId() == null) {
-            return 0;
-        }
-        CustomerShippingAddressVo addressVo = baseMapper.selectVoById(bo.getId());
-        if (IsDefault.Yes.getCode().equals(bo.getDefaultAddress())) {
-            // 1. 清除该客户下所有默认地址
-            CustomerShippingAddress updateObj = new CustomerShippingAddress();
-            updateObj.setDefaultAddress(IsDefault.No.getCode()); //
-
-            baseMapper.update(
-                updateObj,
-                new LambdaUpdateWrapper<CustomerShippingAddress>()
-                    .eq(CustomerShippingAddress::getCustomerId, addressVo.getCustomerId()) //
-                    .eq(CustomerShippingAddress::getDefaultAddress, IsDefault.Yes.getCode())
-            );
-
-            // 2. 设置当前地址为默认
-            CustomerShippingAddress current = new CustomerShippingAddress();
-            current.setId(bo.getId());
-            current.setDefaultAddress(IsDefault.Yes.getCode());
-            baseMapper.updateById(current);
-
-            return 1;
-        } else {
-            // 取消默认
-            CustomerShippingAddress current = new CustomerShippingAddress();
-            current.setId(bo.getId());
-            current.setDefaultAddress(IsDefault.No.getCode());
-            baseMapper.updateById(current);
-            return 1;
+        try {
+            if (bo == null || bo.getId() == null) {
+                return 0;
+            }
+            CustomerShippingAddressVo addressVo = baseMapper.selectVoById(bo.getId());
+            log.info("修改默认地址:" + addressVo);
+            if (IsDefault.Yes.getCode().equals(bo.getDefaultAddress())) {
+                // 1. 清除该客户下所有默认地址
+                CustomerShippingAddress updateObj = new CustomerShippingAddress();
+                updateObj.setDefaultAddress(IsDefault.No.getCode()); //
+
+                baseMapper.update(
+                    updateObj,
+                    new LambdaUpdateWrapper<CustomerShippingAddress>()
+                        .eq(CustomerShippingAddress::getCustomerId, addressVo.getCustomerId()) //
+                        .eq(CustomerShippingAddress::getDefaultAddress, IsDefault.Yes.getCode())
+                );
+
+                // 2. 设置当前地址为默认
+                CustomerShippingAddress current = new CustomerShippingAddress();
+                current.setId(bo.getId());
+                current.setDefaultAddress(IsDefault.Yes.getCode());
+                baseMapper.updateById(current);
+
+                return 1;
+            } else {
+                // 取消默认
+                CustomerShippingAddress current = new CustomerShippingAddress();
+                current.setId(bo.getId());
+                current.setDefaultAddress(IsDefault.No.getCode());
+                baseMapper.updateById(current);
+                return 1;
+            }
+        } catch (Exception e) {
+            log.error("修改默认地址失败:" + e.getMessage(), e);
+            throw new RuntimeException(e);
         }
     }
 

+ 1 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/EnterpriseScaleServiceImpl.java

@@ -83,6 +83,7 @@ public class EnterpriseScaleServiceImpl extends ServiceImpl<EnterpriseScaleMappe
         lqw.orderByAsc(EnterpriseScale::getId);
         lqw.like(StringUtils.isNotBlank(bo.getEnterpriseScaleName()), EnterpriseScale::getEnterpriseScaleName, bo.getEnterpriseScaleName());
         lqw.eq(StringUtils.isNotBlank(bo.getStatus()), EnterpriseScale::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getDataSource()), EnterpriseScale::getDataSource, bo.getDataSource());
         lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), EnterpriseScale::getPlatformCode, bo.getPlatformCode());
         return lqw;
     }

+ 10 - 9
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/IndustryCategoryServiceImpl.java

@@ -1,27 +1,27 @@
 package org.dromara.customer.service.impl;
 
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.dromara.common.core.utils.MapstructUtils;
-import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.mybatis.core.page.TableDataInfo;
-import org.dromara.common.mybatis.core.page.PageQuery;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.redis.utils.SequenceUtils;
-import org.springframework.stereotype.Service;
+import org.dromara.customer.domain.IndustryCategory;
 import org.dromara.customer.domain.bo.IndustryCategoryBo;
 import org.dromara.customer.domain.vo.IndustryCategoryVo;
-import org.dromara.customer.domain.IndustryCategory;
 import org.dromara.customer.mapper.IndustryCategoryMapper;
 import org.dromara.customer.service.IIndustryCategoryService;
+import org.springframework.stereotype.Service;
 
 import java.time.Duration;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Collection;
 
 /**
  * 所属行业Service业务层处理
@@ -81,6 +81,7 @@ public class IndustryCategoryServiceImpl extends ServiceImpl<IndustryCategoryMap
         lqw.orderByAsc(IndustryCategory::getId);
         lqw.like(StringUtils.isNotBlank(bo.getIndustryCategoryName()), IndustryCategory::getIndustryCategoryName, bo.getIndustryCategoryName());
         lqw.eq(StringUtils.isNotBlank(bo.getStatus()), IndustryCategory::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getDataSource()), IndustryCategory::getDataSource, bo.getDataSource());
         lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), IndustryCategory::getPlatformCode, bo.getPlatformCode());
         return lqw;
     }

+ 147 - 72
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SupplierInfoServiceImpl.java

@@ -27,15 +27,10 @@ import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.redis.utils.RedisUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.customer.domain.*;
-import org.dromara.customer.domain.bo.CustomerRegisterBo;
-import org.dromara.customer.domain.bo.EnterpriseScaleBo;
-import org.dromara.customer.domain.bo.SupplierAuthorizeBo;
-import org.dromara.customer.domain.bo.SupplyAreaBo;
+import org.dromara.customer.domain.bo.*;
 import org.dromara.customer.domain.vo.*;
 import org.dromara.customer.enums.SupplierStatusEnum;
-import org.dromara.customer.mapper.SupplierAuthorizeMapper;
-import org.dromara.customer.mapper.SupplierBusinessInfoMapper;
-import org.dromara.customer.mapper.SupplierContactMapper;
+import org.dromara.customer.mapper.*;
 import org.dromara.customer.service.*;
 import org.dromara.customer.utils.qcc.QccUtils;
 import org.dromara.customer.utils.qcc.domain.CompanyInfoResponse;
@@ -49,10 +44,9 @@ import org.dromara.system.api.domain.bo.RemoteUserBo;
 import org.dromara.system.api.domain.vo.RemoteSupplierTypeVo;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
-import org.dromara.customer.domain.bo.SupplierInfoBo;
 import org.dromara.customer.domain.vo.SupplierInfoVo;
 import org.dromara.customer.domain.SupplierInfo;
-import org.dromara.customer.mapper.SupplierInfoMapper;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 
 import java.util.*;
@@ -67,7 +61,7 @@ import java.util.stream.Collectors;
 @Slf4j
 @RequiredArgsConstructor
 @Service
-public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, SupplierInfo> implements ISupplierInfoService {
+public class SupplierInfoServiceImpl extends ServiceImpl<SupplierInfoMapper, SupplierInfo> implements ISupplierInfoService {
 
     private final SupplierInfoMapper baseMapper;
 
@@ -85,10 +79,11 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
     private final ObjectMapper objectMapper;
 
+    private final SupplyAreaMapper supplyAreaMapper;
+
     /**
-     *
      * 供应商联系人信息
-     * */
+     */
     private final SupplierContactMapper supplierContactMapper;
 
     @DubboReference
@@ -107,8 +102,6 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     private final RemoteSupplierTypeService remoteSupplierTypeService;
 
 
-
-
     /**
      * 新增供应商信息
      *
@@ -138,11 +131,12 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         }
         return flag;
     }
+
     /**
      * 从JSON字符串解析工商信息并保存
      *
      * @param otherCustomersJson JSON格式的工商信息
-     * @param supplierId 供应商ID
+     * @param supplierId         供应商ID
      */
     private void saveSupplierBusinessInfoFromJson(String otherCustomersJson, Long supplierId) {
         if (StringUtils.isBlank(otherCustomersJson)) {
@@ -173,7 +167,6 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     }
 
 
-
     /**
      * 修改供应商信息
      *
@@ -198,7 +191,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
             supplierInfoVo.setSupplyStatus(SupplierStatusEnum.REVIEW_UPDATED.getCode());
             //先copy到临时表
             SupplierInfoTemporary supplierInfoTemporary = new SupplierInfoTemporary();
-            BeanUtils.copyProperties(bo, supplierInfoTemporary,"id");
+            BeanUtils.copyProperties(bo, supplierInfoTemporary, "id");
             //修改待审核
             //ID一对一
             supplierInfoTemporary.setSupplierId(bo.getId());
@@ -230,7 +223,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         }
 
         //停用 ->正式供应商
-        if (supplierInfoVo.getSupplyStatus() ==SupplierStatusEnum.DISABLED.getCode() && bo.getSupplyStatus() == SupplierStatusEnum.OFFICIAL_SUPPLIER.getCode()) {
+        if (supplierInfoVo.getSupplyStatus() == SupplierStatusEnum.DISABLED.getCode() && bo.getSupplyStatus() == SupplierStatusEnum.OFFICIAL_SUPPLIER.getCode()) {
             SupplierInfo update = MapstructUtils.convert(bo, SupplierInfo.class);
             update.setCooperative(1L);
             return baseMapper.updateById(update) > 0;
@@ -263,7 +256,8 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
                 // 注意:泛型解析需要用 TypeReference 明确类型,否则会解析为 LinkedHashMap
                 TableDataInfo<SupplyAreaVo> areaTableData = objectMapper.readValue(
                     areaListJson,
-                    new TypeReference<TableDataInfo<SupplyAreaVo>>() {}
+                    new TypeReference<TableDataInfo<SupplyAreaVo>>() {
+                    }
                 );
                 // 2. 提取 SupplyAreaVo 列表(判空避免空指针)
                 List<SupplyAreaVo> supplyAreaVoList = areaTableData.getRows();
@@ -291,7 +285,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
                 // 5. 更新主表 SupplierInfo
                 supplierInfoVo.setSupplyStatus(SupplierStatusEnum.OFFICIAL_SUPPLIER.getCode());
                 boolean updateMain = baseMapper.updateById(supplierInfoVo) > 0;
-                if (updateMain == false){
+                if (updateMain == false) {
                     throw new RuntimeException("更新供应商信息失败");
                 }
                 boolean remove = supplierInfoTemporaryService.remove(new LambdaQueryWrapper<SupplierInfoTemporary>().eq(SupplierInfoTemporary::getSupplierId, id));
@@ -357,16 +351,16 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         Long id = bo.getId();
         SupplierInfo supplierInfo = baseMapper.selectById(id);
         //如果是待审核和审核不通过 随便改
-        if (bo.getSupplyStatus() == SupplierStatusEnum.PENDING_REVIEW.getCode() || bo.getSupplyStatus() == SupplierStatusEnum.REVIEW_FAILED.getCode()){
+        if (bo.getSupplyStatus() == SupplierStatusEnum.PENDING_REVIEW.getCode() || bo.getSupplyStatus() == SupplierStatusEnum.REVIEW_FAILED.getCode()) {
             SupplierInfo update = MapstructUtils.convert(bo, SupplierInfo.class);
             return baseMapper.updateById(update) > 0;
         }
         //如果是启用状态,就变成待修改审核状态
-        if (supplierInfo.getSupplyStatus() == SupplierStatusEnum.OFFICIAL_SUPPLIER.getCode()){
+        if (supplierInfo.getSupplyStatus() == SupplierStatusEnum.OFFICIAL_SUPPLIER.getCode()) {
             SupplierInfoTemporary supplierInfoTemporary = supplierInfoTemporaryService.querBySupplierId(id);
-            if (supplierInfoTemporary == null){
+            if (supplierInfoTemporary == null) {
                 supplierInfoTemporary = new SupplierInfoTemporary();
-                BeanUtils.copyProperties(supplierInfo, supplierInfoTemporary,"id");
+                BeanUtils.copyProperties(supplierInfo, supplierInfoTemporary, "id");
                 supplierInfoTemporary.setSupplierId(id);
                 //通过传参在set临时表
                 //查询供应地址 存入  areaListJson  为了回显
@@ -378,57 +372,57 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
                 supplierInfoTemporary.setAreaListJson(areaListJson);
                 supplierInfoTemporary.setSupplyStatus(SupplierStatusEnum.REVIEW_UPDATED.getCode());
                 boolean save = supplierInfoTemporaryService.save(supplierInfoTemporary);
-                if (save == false){
+                if (save == false) {
                     throw new RuntimeException("保存供应商待审核数据失败");
                 }
 
             }
             //一共三个地方
-            boolean flag =false;
+            boolean flag = false;
             //1.简称
-            if (bo.getShortName() != null && StringUtils.isNotBlank(bo.getShortName())){
+            if (bo.getShortName() != null && StringUtils.isNotBlank(bo.getShortName())) {
                 supplierInfoTemporary.setShortName(bo.getShortName());
                 flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
             }
             //2.供货品牌
-            if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())){
+            if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())) {
                 supplierInfoTemporary.setOperatingBrand(bo.getOperatingBrand());
                 flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
             }
             //3.供货类目
-            if (bo.getOperatingCategory() != null && StringUtils.isNotBlank(bo.getOperatingCategory())){
+            if (bo.getOperatingCategory() != null && StringUtils.isNotBlank(bo.getOperatingCategory())) {
                 supplierInfoTemporary.setOperatingCategory(bo.getOperatingCategory());
                 flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
             }
 
             supplierInfo.setSupplyStatus(SupplierStatusEnum.REVIEW_UPDATED.getCode());
-            boolean update =baseMapper.updateById(supplierInfo) > 0;
+            boolean update = baseMapper.updateById(supplierInfo) > 0;
             return update && flag;
         }
         //如果是待审核状态,依旧是待修改审核状态
-        if (supplierInfo.getSupplyStatus() == SupplierStatusEnum.REVIEW_UPDATED.getCode()){
+        if (supplierInfo.getSupplyStatus() == SupplierStatusEnum.REVIEW_UPDATED.getCode()) {
             SupplierInfoTemporary supplierInfoTemporary = supplierInfoTemporaryService.querBySupplierId(id);
             //一共三个地方
-            boolean flag =false;
+            boolean flag = false;
             //1.简称
-            if (bo.getShortName() != null && StringUtils.isNotBlank(bo.getShortName())){
+            if (bo.getShortName() != null && StringUtils.isNotBlank(bo.getShortName())) {
                 supplierInfoTemporary.setShortName(bo.getShortName());
                 flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
             }
             //2.供货品牌
-            if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())){
+            if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())) {
                 supplierInfoTemporary.setOperatingBrand(bo.getOperatingBrand());
                 flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
             }
             //3.供货类目
-            if (bo.getOperatingCategory() != null && StringUtils.isNotBlank(bo.getOperatingCategory())){
+            if (bo.getOperatingCategory() != null && StringUtils.isNotBlank(bo.getOperatingCategory())) {
                 supplierInfoTemporary.setOperatingCategory(bo.getOperatingCategory());
                 flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
             }
             return flag;
         }
         // 停用呢  不能修改了
-        if (supplierInfo.getSupplyStatus() == SupplierStatusEnum.DISABLED.getCode()){
+        if (supplierInfo.getSupplyStatus() == SupplierStatusEnum.DISABLED.getCode()) {
             throw new RuntimeException("供应商已停用,不能修改");
         }
         return false;
@@ -459,11 +453,11 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     public SupplierInfoVo queryById(Long id) throws JsonProcessingException {
         SupplierInfoVo supplierInfoVo1 = baseMapper.selectVoOne(new LambdaQueryWrapper<SupplierInfo>().select(SupplierInfo::getSupplyStatus)
             .eq(SupplierInfo::getId, id));
-        SupplierInfoVo supplierInfoVo =new SupplierInfoVo();
+        SupplierInfoVo supplierInfoVo = new SupplierInfoVo();
         if (supplierInfoVo1.getSupplyStatus() == SupplierStatusEnum.REVIEW_UPDATED.getCode()) {
             SupplierInfoTemporaryVo supplierInfoTemporaryVo = supplierInfoTemporaryService.getVoOne(id);
             BeanUtils.copyProperties(supplierInfoTemporaryVo, supplierInfoVo);
-        }else {
+        } else {
             supplierInfoVo = baseMapper.selectVoById(id);
         }
         SupplierInfoVo supplierInformationVo = handleProcurementInfo(supplierInfoVo);
@@ -479,7 +473,6 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     }
 
 
-
     /**
      * 分页查询供应商信息列表
      *
@@ -498,18 +491,18 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     public TableDataInfo<SupplierInfoVo> getSupplierInformation(SupplierInfoBo bo, PageQuery pageQuery) {
         LambdaQueryWrapper<SupplierInfo> lqw = new LambdaQueryWrapper<>();
         // 1. 基础分页查询
-        if (bo.getSupplyStatus() == null){
+        if (bo.getSupplyStatus() == null) {
             //正式供应商
             lqw.eq(SupplierInfo::getSupplyStatus, 1L);
-        }else {
-            lqw.eq(SupplierInfo::getSupplyStatus,bo.getSupplyStatus());
+        } else {
+            lqw.eq(SupplierInfo::getSupplyStatus, bo.getSupplyStatus());
         }
 
         // 2. 添加品牌条件
-        if (bo.getOperatingBrand()!= null && StringUtils.isNotBlank(bo.getOperatingBrand())){
-            if (StringUtils.isNotBlank(bo.getOperatingBrand())){
+        if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())) {
+            if (StringUtils.isNotBlank(bo.getOperatingBrand())) {
                 Long productBrandIdByName = remoteProductService.getProductBrandIdByName(bo.getOperatingBrand());
-                if (productBrandIdByName != null){
+                if (productBrandIdByName != null) {
                     lqw.like(SupplierInfo::getOperatingBrand, productBrandIdByName);
                 }
             }
@@ -556,7 +549,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
     @Override
     public TableDataInfo<SupplierInfoVo> getSupplierApproveInformation(SupplierInfoBo bo, PageQuery pageQuery) {
-        if (bo.getSupplyStatus() == null){
+        if (bo.getSupplyStatus() == null) {
             // 步骤1:定义两个列表,存储主表(状态0)和临时表(状态4)的数据
             List<SupplierInfoVo> mainTableVoList = new ArrayList<>();
             List<SupplierInfoVo> tempTableVoList = new ArrayList<>();
@@ -664,13 +657,13 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
         // 1. 构建审批状态条件
         LambdaQueryWrapper<SupplierInfo> lqw = new LambdaQueryWrapper<>();
-        lqw.eq(SupplierInfo::getSupplyStatus,bo.getSupplyStatus());
+        lqw.eq(SupplierInfo::getSupplyStatus, bo.getSupplyStatus());
 
         // 2.添加品牌条件
-        if (bo.getOperatingBrand()!= null && StringUtils.isNotBlank(bo.getOperatingBrand())){
-            if (StringUtils.isNotBlank(bo.getOperatingBrand())){
+        if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())) {
+            if (StringUtils.isNotBlank(bo.getOperatingBrand())) {
                 Long productBrandIdByName = remoteProductService.getProductBrandIdByName(bo.getOperatingBrand());
-                if (productBrandIdByName != null){
+                if (productBrandIdByName != null) {
                     lqw.like(SupplierInfo::getOperatingBrand, productBrandIdByName);
                 }
             }
@@ -813,9 +806,9 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         lqw.eq(StringUtils.isNotBlank(bo.getSupplierName()), SupplierInfo::getEnterpriseName, bo.getSupplierName());
 
         //可供应品牌
-        if (StringUtils.isNotBlank(bo.getSupplyBrand())){
+        if (StringUtils.isNotBlank(bo.getSupplyBrand())) {
             Long productBrandIdByName = remoteProductService.getProductBrandIdByName(bo.getSupplyBrand());
-            if (productBrandIdByName != null){
+            if (productBrandIdByName != null) {
                 lqw.like(SupplierInfo::getOperatingBrand, productBrandIdByName);
             }
         }
@@ -823,10 +816,10 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         if (StringUtils.isNotBlank(bo.getProvince()) || StringUtils.isNotBlank(bo.getCity())) {
             // 查询符合条件的供应商ID列表
             List<Long> supplierIds = supplyAreaService.getSupplierIdsByArea(bo.getProvince(), bo.getCity());
-            if (supplierIds != null &&!supplierIds.isEmpty()) {
+            if (supplierIds != null && !supplierIds.isEmpty()) {
                 lqw.in(SupplierInfo::getId, supplierIds);
 
-            }else {
+            } else {
                 lqw.in(SupplierInfo::getId, -1L);
 
             }
@@ -836,9 +829,9 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         if (StringUtils.isNotBlank(bo.getBrandName())) {
             // 通过SupplierAuthorizeService查询符合条件的供应商ID
             List<Long> supplierIdsByBrand = commonSupplierAuthorizeMapper.getSupplierIdsByBrandName(bo.getBrandName());
-            if (supplierIdsByBrand!=null &&!supplierIdsByBrand.isEmpty()) {
+            if (supplierIdsByBrand != null && !supplierIdsByBrand.isEmpty()) {
                 lqw.in(SupplierInfo::getId, supplierIdsByBrand);
-            }else {
+            } else {
                 lqw.in(SupplierInfo::getId, -1L);
 
             }
@@ -889,7 +882,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     @Override
     public Long getSupplierStatus(Long id) {
         SupplierInfoVo supplierInfoVo1 = baseMapper.selectVoOne(new LambdaQueryWrapper<SupplierInfo>().select(SupplierInfo::getSupplyStatus)
-            .eq(SupplierInfo::getId,id));
+            .eq(SupplierInfo::getId, id));
 
         return supplierInfoVo1.getSupplyStatus();
     }
@@ -941,6 +934,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     }
 
     // ========== 3. 数据转换 - 映射构建方法 ==========
+
     /**
      * 构建品牌ID→名称映射表
      */
@@ -1421,7 +1415,6 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     }
 
 
-
     private SupplierInfoVo handleProcurementInfo(SupplierInfoVo records) {
 
         // 查询管理人员和采购人员
@@ -1565,7 +1558,6 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     }*/
 
 
-
     /**
      * 处理采购员和产品经理信息和品牌和供应商类型
      */
@@ -1951,12 +1943,18 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
      * @param bo
      */
     @Override
-    public Boolean register(CustomerRegisterBo bo) {
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean register(SupplierRegisterBo bo) {
         //先校验验证码是否正确
         String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + bo.getPurchasePhone());
-        code = "123456";
+
+        if (code == null) {
+            throw new ServiceException("验证码已过期");
+        }
+
         if (!code.equals(bo.getCode())) {
             throw new ServiceException("验证码错误");
+
         }
 
         //校验密码与确认密码是否一致
@@ -1966,23 +1964,47 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
         //查询数据库是否存在企业,已存在则提示客户已存在
         SupplierInfo one = baseMapper.selectOne(new LambdaQueryWrapper<SupplierInfo>()
-            .eq(SupplierInfo::getSupplierName, bo.getCustomerName())
+            .eq(SupplierInfo::getSupplierName, bo.getEnterpriseName())
         );
+
         if (one != null) {
-            throw new ServiceException("客户已存在");
+            throw new ServiceException("供应商已存在");
         }
 
         //查询企查查是否存在企业,如果企查查不存在则提示企业不存在
-        CompanyInfoResponse companyInfo = QccUtils.getCompanyInfo(bo.getCustomerName());
+        CompanyInfoResponse companyInfo = QccUtils.getCompanyInfo(bo.getEnterpriseName());
         if (companyInfo == null || companyInfo.getResult() == null) {
             throw new ServiceException("请检查企业名是否正确");
         }
 
         //构建客户信息实体
         SupplierInfo supplierInfo = new SupplierInfo();
-        supplierInfo.setSupplierName(bo.getCustomerName());
-        supplierInfo.setSupplierName(companyInfo.getResult().getName());
+        LambdaQueryWrapper<SupplierInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        // 按supplier_no降序排序,取第一条(最大值)
+        lambdaQueryWrapper.select(SupplierInfo::getSupplierNo)
+            .orderByDesc(SupplierInfo::getSupplierNo)
+            .last("LIMIT 1");
+        SupplierInfo maxSupplier = baseMapper.selectOne(lambdaQueryWrapper);
+        long l = Long.parseLong(maxSupplier.getSupplierNo()) + 1;
+        supplierInfo.setSupplierNo(String.valueOf(l));
+        // supplierInfo.setSupplierName(bo.getCustomerName()); //表中的该字段是表示 供应商联系人的名称
+        supplierInfo.setEnterpriseName(bo.getEnterpriseName());//表示供应商名称
+        supplierInfo.setBusinessName(companyInfo.getResult().getName());//表示供应商的工商名称
+        supplierInfo.setInvoiceHeader(companyInfo.getResult().getName());
         supplierInfo.setShortName(companyInfo.getResult().getName());
+        supplierInfo.setMembershipSize(bo.getMembershipSize());
+        supplierInfo.setFixedPhone(bo.getFixedPhone());
+        supplierInfo.setFax(bo.getFax());
+        supplierInfo.setUrl(bo.getUrl());
+        supplierInfo.setMailbox(bo.getMailbox());
+        supplierInfo.setOfficeProvince(bo.getOfficeProvince());
+        supplierInfo.setOfficeCity(bo.getOfficeCity());
+        supplierInfo.setOfficeCounty(bo.getOfficeCounty());
+        supplierInfo.setOfficeAddress(bo.getOfficeAddress());
+        supplierInfo.setSocialCreditCode(companyInfo.getResult().getCreditCode());
+        supplierInfo.setYearSales(bo.getYearSales());
+        supplierInfo.setOperatingCategory(bo.getOperatingCategory());
+        supplierInfo.setBusinessLicense(bo.getBusinessLicense());
 
         // 设置其他工商信息
         if (baseMapper.insert(supplierInfo) <= 0) {
@@ -1991,27 +2013,49 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
         Long supplierId = supplierInfo.getId();
 
+        String supplierNo = supplierInfo.getSupplierNo();
+
         // 添加客户工商信息
         SupplierBusinessInfo businessInfo = new SupplierBusinessInfo();
         businessInfo.setSupplierId(supplierId);
         businessInfo.setBusinessName(companyInfo.getResult().getName());
         businessInfo.setSocialCreditCode(companyInfo.getResult().getCreditCode());
         businessInfo.setLegalPersonName(companyInfo.getResult().getOperName());
-        businessInfo.setRegisteredCapital(companyInfo.getResult().getRegistCapi());
+//        businessInfo.setRegisteredCapital(companyInfo.getResult().getRegistCapi());
+        // 注册资本:拼接数值 + 单位(如 "101万元")
+        String regCap = companyInfo.getResult().getRegisteredCapital();
+        String regUnit = companyInfo.getResult().getRegisteredCapitalUnit();
+        businessInfo.setRegisteredCapital(
+            StringUtils.isNotBlank(regCap) && StringUtils.isNotBlank(regUnit)
+                ? regCap + regUnit
+                : regCap
+        );
+
+        // 实缴资本
+        String paidCap = companyInfo.getResult().getPaidUpCapital();
+        String paidUnit = companyInfo.getResult().getPaidUpCapitalUnit();
+        businessInfo.setPaidInCapital(
+            StringUtils.isNotBlank(paidCap) && StringUtils.isNotBlank(paidUnit)
+                ? paidCap + paidUnit
+                : paidCap
+        );
+
         businessInfo.setEstablishmentDate(DateUtils.parseDate((companyInfo.getResult().getStartDate())));
         businessInfo.setRegistrationStatus(companyInfo.getResult().getStatus());
+        businessInfo.setRegistrationAuthority(companyInfo.getResult().getBelongOrg());
         businessInfo.setBusinessAddress(companyInfo.getResult().getAddress());
         businessInfo.setBussinessRange(companyInfo.getResult().getScope());
         businessInfo.setStatus("0");
         businessInfo.setDelFlag("0");
 
         if (supplierBusinessInfoMapper.insert(businessInfo) <= 0) {
-            throw new ServiceException("客户工商信息新增失败");
+            throw new ServiceException("供应商工商信息新增失败");
         }
 
         // 添加客户联系人信息
         SupplierContact contact = new SupplierContact();
         contact.setSupplierId(supplierId);
+        contact.setSupplierNo(supplierNo);
         contact.setUserName(bo.getPurchaseName());
         contact.setPhone(bo.getPurchasePhone());
         contact.setPhone(bo.getPurchasePhone());
@@ -2021,13 +2065,44 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         contact.setDelFlag("0");
 
         if (supplierContactMapper.insert(contact) <= 0) {
-            throw new ServiceException("客户联系人信息新增失败");
+            throw new ServiceException("供应商联系人信息新增失败");
+        }
+
+        List<SupplyAreaBo> supplyAreaList = bo.getSupplyAreaList();
+        if (supplyAreaList == null || supplyAreaList.isEmpty()) {
+            throw new ServiceException("供应商供应区域为空");
+        }
+
+        // 1. 内存中转换对象
+        List<SupplyArea> insertList = new ArrayList<>(supplyAreaList.size());
+        Set<String> uniqueCodes = new HashSet<>();
+        for (SupplyAreaBo area : supplyAreaList) {
+            if (!uniqueCodes.add(area.getAreaCode())) {
+                throw new ServiceException("供应区域代码 [" + area.getAreaCode() + "] 重复");
+            }
+            SupplyArea supplyArea = new SupplyArea();
+            supplyArea.setSupplierId(supplierId);
+            supplyArea.setSupplyNo(supplierNo);
+            supplyArea.setAreaCode(area.getAreaCode());
+            supplyArea.setAreaName(area.getAreaName());
+            supplyArea.setParentCode(area.getParentCode());
+            supplyArea.setLevel(area.getLevel());
+            supplyArea.setDelFlag("0");
+            supplyArea.setStatus("0");
+            insertList.add(supplyArea);
+        }
+
+        // 2. 执行批量插入
+        boolean b = supplyAreaMapper.insertBatch(insertList);
+
+        if (!b) {
+            throw new ServiceException("供应商供应区域信息批量新增失败");
         }
 
         // 创建系统用户
         RemoteUserBo remoteUserBo = new RemoteUserBo();
         remoteUserBo.setUserName(bo.getPurchasePhone());
-        remoteUserBo.setNickName(bo.getCustomerName());
+        remoteUserBo.setNickName(bo.getPurchaseName());
         remoteUserBo.setPhonenumber(bo.getPurchasePhone());
         remoteUserBo.setPassword(BCrypt.hashpw(bo.getPassword()));
         remoteUserBo.setUserSonType("3"); // 商城用户
@@ -2036,13 +2111,13 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
         remoteUserService.addUser(remoteUserBo);
 
-        log.info("客户注册成功,客户ID:{},客户名称:{}", supplierId, bo.getCustomerName());
+        log.info("供应商注册成功,供应商ID:{},供应商名称:{}", supplierId, bo.getEnterpriseName());
         return true;
     }
 
     @Override
     public List<SupplierInfoVo> getNameList() {
-        List<SupplierInfoVo> supplierInfoVos = baseMapper.selectVoList(new LambdaQueryWrapper<SupplierInfo>().select(SupplierInfo::getId, SupplierInfo::getEnterpriseName,SupplierInfo::getSupplierNo));
+        List<SupplierInfoVo> supplierInfoVos = baseMapper.selectVoList(new LambdaQueryWrapper<SupplierInfo>().select(SupplierInfo::getId, SupplierInfo::getEnterpriseName, SupplierInfo::getSupplierNo));
         return supplierInfoVos;
     }
 

+ 45 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/config/CacheConstants.java

@@ -0,0 +1,45 @@
+package org.dromara.mall.config;
+
+/**
+ * 缓存常量类
+ *
+ * @author ruoyi
+ */
+public class CacheConstants {
+
+    /**
+     * 页面分类缓存键前缀
+     */
+    public static final String PAGE_CATEGORY_CACHE_KEY = "mall:page:category:";
+
+    /**
+     * 页面链接缓存键前缀
+     */
+    public static final String PAGE_LINK_CACHE_KEY = "mall:page:link:";
+
+    /**
+     * 分类树形结构缓存键
+     */
+    public static final String CATEGORY_TREE_CACHE_KEY = "mall:page:category:tree";
+
+    /**
+     * 分类列表缓存键前缀
+     */
+    public static final String CATEGORY_LIST_CACHE_KEY = "mall:page:category:list:";
+
+    /**
+     * 链接列表缓存键前缀
+     */
+    public static final String LINK_LIST_CACHE_KEY = "mall:page:link:list:";
+
+    /**
+     * 缓存过期时间(秒)
+     */
+    public static final long CACHE_EXPIRE_TIME = 3600;
+
+    /**
+     * 短缓存过期时间(秒)
+     */
+    public static final long SHORT_CACHE_EXPIRE_TIME = 600;
+}
+

+ 83 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/config/CacheWarmupRunner.java

@@ -0,0 +1,83 @@
+package org.dromara.mall.config;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.mall.service.IPageCategoryService;
+import org.dromara.mall.service.IPageLinkService;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+/**
+ * 缓存预热运行器
+ * 应用启动时预热页面分类和链接数据到Redis缓存
+ *
+ * @author system
+ */
+@Component
+@RequiredArgsConstructor
+@Slf4j
+@Order(100)
+public class CacheWarmupRunner implements ApplicationRunner {
+
+    private final IPageCategoryService pageCategoryService;
+    private final IPageLinkService pageLinkService;
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        log.info("开始执行页面分类与链接缓存预热...");
+
+        try {
+            // 预热分类树形结构缓存
+            warmupCategoryTree();
+
+            // 预热顶级分类列表缓存
+            warmupTopCategories();
+
+            // 预热热门分类缓存
+            warmupHotCategories();
+
+            // 预热自定义链接缓存
+            warmupCustomLinks();
+
+            log.info("页面分类与链接缓存预热完成");
+        } catch (Exception e) {
+            log.error("页面分类与链接缓存预热失败", e);
+        }
+    }
+
+    /**
+     * 预热分类树形结构缓存
+     */
+    private void warmupCategoryTree() {
+        log.info("预热分类树形结构缓存...");
+        pageCategoryService.selectCategoryTree(null);
+    }
+
+    /**
+     * 预热顶级分类列表缓存
+     */
+    private void warmupTopCategories() {
+        log.info("预热顶级分类列表缓存...");
+        pageCategoryService.selectByParentId(0L);
+    }
+
+    /**
+     * 预热热门分类缓存
+     */
+    private void warmupHotCategories() {
+        log.info("预热热门分类缓存...");
+        pageCategoryService.selectByType("1"); // 1表示热门分类
+    }
+
+    /**
+     * 预热自定义链接缓存
+     */
+    private void warmupCustomLinks() {
+        log.info("预热自定义链接缓存...");
+        pageLinkService.selectByIsCustom(1); // 1表示自定义链接
+    }
+}
+
+

+ 164 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/MallDiyPcPageController.java

@@ -0,0 +1,164 @@
+package org.dromara.mall.controller;
+
+import java.util.List;
+import java.util.Objects;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.mall.domain.MallDiyPcPage;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.mall.domain.vo.MallDiyPcPageVo;
+import org.dromara.mall.domain.bo.MallDiyPcPageBo;
+import org.dromara.mall.service.IMallDiyPcPageService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * pc装修页面
+ * 前端访问路由地址为:/mall/diyPcPage
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/diyPcPage")
+public class MallDiyPcPageController extends BaseController {
+
+    private final IMallDiyPcPageService mallDiyPcPageService;
+
+    /**
+     * 查询pc装修页面列表
+     */
+    //@SaCheckPermission("mall:diyPcPage:list")
+    @GetMapping("/list")
+    public TableDataInfo<MallDiyPcPageVo> list(MallDiyPcPageBo bo, PageQuery pageQuery) {
+        return mallDiyPcPageService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出pc装修页面列表
+     */
+    //@SaCheckPermission("mall:diyPcPage:export")
+    @Log(title = "pc装修页面", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(MallDiyPcPageBo bo, HttpServletResponse response) {
+        List<MallDiyPcPageVo> list = mallDiyPcPageService.queryList(bo);
+        ExcelUtil.exportExcel(list, "pc装修页面", MallDiyPcPageVo.class, response);
+    }
+
+    /**
+     * 获取pc装修页面详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("mall:diyPcPage:query")
+    @GetMapping("/{id}")
+    public R<MallDiyPcPageVo> getInfo(@NotNull(message = "主键不能为空")
+                                      @PathVariable("id") Long id) {
+        return R.ok(mallDiyPcPageService.queryById(id));
+    }
+
+    /**
+     * 新增pc装修页面
+     */
+    //@SaCheckPermission("mall:diyPcPage:add")
+    @Log(title = "pc装修页面", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody MallDiyPcPageBo bo) {
+        if(Objects.equals(bo.getIsHome(), 1)){
+            if(ObjectUtil.isEmpty(bo.getSiteId())){
+                mallDiyPcPageService.update(Wrappers.<MallDiyPcPage>lambdaUpdate()
+                    .eq(MallDiyPcPage::getType, bo.getType())
+                    .set(MallDiyPcPage::getIsHome, 0));
+            }else {
+                mallDiyPcPageService.update(Wrappers.<MallDiyPcPage>lambdaUpdate()
+                    .eq(MallDiyPcPage::getSiteId, bo.getSiteId())
+                    .set(MallDiyPcPage::getIsHome, 0));
+            }
+
+
+        }
+        return toAjax(mallDiyPcPageService.insertByBo(bo));
+    }
+
+    /**
+     * 修改pc装修页面
+     */
+    //@SaCheckPermission("mall:diyPcPage:edit")
+    @Log(title = "pc装修页面", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody MallDiyPcPageBo bo) {
+        if(Objects.equals(bo.getIsHome(), 1)){
+            if(ObjectUtil.isEmpty(bo.getSiteId())){
+                mallDiyPcPageService.update(Wrappers.<MallDiyPcPage>lambdaUpdate()
+                    .eq(MallDiyPcPage::getType, bo.getType())
+                    .set(MallDiyPcPage::getIsHome, 0));
+            }else {
+                mallDiyPcPageService.update(Wrappers.<MallDiyPcPage>lambdaUpdate()
+                    .eq(MallDiyPcPage::getSiteId, bo.getSiteId())
+                    .set(MallDiyPcPage::getIsHome, 0)
+                );
+            }
+        }
+        return toAjax(mallDiyPcPageService.updateByBo(bo));
+    }
+
+    /**
+     * 删除pc装修页面
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("mall:diyPcPage:remove")
+    @Log(title = "pc装修页面", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(mallDiyPcPageService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    /**
+    * 设为首页
+    * */
+    @Log(title = "pc装修页面", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping("/setHomePage/{id}")
+    public R<Void> setHomePage(@NotEmpty(message = "主键不能为空") @PathVariable("id") Long id){
+        MallDiyPcPage mallDiyPcPage = mallDiyPcPageService.getById(id);
+        //如果是客户站点
+        if (mallDiyPcPage.getSiteId() != null){
+            mallDiyPcPageService.update(Wrappers.<MallDiyPcPage>lambdaUpdate()
+                    .eq(MallDiyPcPage::getSiteId, mallDiyPcPage.getSiteId())
+                    .set(MallDiyPcPage::getIsHome, 0));
+            mallDiyPcPageService.update(Wrappers.<MallDiyPcPage>lambdaUpdate()
+                    .eq(MallDiyPcPage::getId, id)
+                    .set(MallDiyPcPage::getIsHome, 1));
+        }else{
+            mallDiyPcPageService.update(Wrappers.<MallDiyPcPage>lambdaUpdate()
+                    .eq(MallDiyPcPage::getType, mallDiyPcPage.getType())
+                    .set(MallDiyPcPage::getIsHome, 0));
+            mallDiyPcPageService.update(Wrappers.<MallDiyPcPage>lambdaUpdate()
+                    .eq(MallDiyPcPage::getId, id)
+                    .set(MallDiyPcPage::getIsHome, 1));
+        }
+         return R.ok();
+    }
+
+
+}

+ 106 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/MallDiyPcTemplatePageController.java

@@ -0,0 +1,106 @@
+package org.dromara.mall.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.mall.domain.vo.MallDiyPcTemplatePageVo;
+import org.dromara.mall.domain.bo.MallDiyPcTemplatePageBo;
+import org.dromara.mall.service.IMallDiyPcTemplatePageService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * pc装修页面模版
+ * 前端访问路由地址为:/mall/diyPcTemplatePage
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/diyPcTemplatePage")
+public class MallDiyPcTemplatePageController extends BaseController {
+
+    private final IMallDiyPcTemplatePageService mallDiyPcTemplatePageService;
+
+    /**
+     * 查询pc装修页面模版列表
+     */
+    //@SaCheckPermission("mall:diyPcTemplatePage:list")
+    @GetMapping("/list")
+    public TableDataInfo<MallDiyPcTemplatePageVo> list(MallDiyPcTemplatePageBo bo, PageQuery pageQuery) {
+        return mallDiyPcTemplatePageService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出pc装修页面模版列表
+     */
+    //@SaCheckPermission("mall:diyPcTemplatePage:export")
+    @Log(title = "pc装修页面模版", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(MallDiyPcTemplatePageBo bo, HttpServletResponse response) {
+        List<MallDiyPcTemplatePageVo> list = mallDiyPcTemplatePageService.queryList(bo);
+        ExcelUtil.exportExcel(list, "pc装修页面模版", MallDiyPcTemplatePageVo.class, response);
+    }
+
+    /**
+     * 获取pc装修页面模版详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("mall:diyPcTemplatePage:query")
+    @GetMapping("/{id}")
+    public R<MallDiyPcTemplatePageVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(mallDiyPcTemplatePageService.queryById(id));
+    }
+
+    /**
+     * 新增pc装修页面模版
+     */
+    //@SaCheckPermission("mall:diyPcTemplatePage:add")
+    @Log(title = "pc装修页面模版", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody MallDiyPcTemplatePageBo bo) {
+        return toAjax(mallDiyPcTemplatePageService.insertByBo(bo));
+    }
+
+    /**
+     * 修改pc装修页面模版
+     */
+    //@SaCheckPermission("mall:diyPcTemplatePage:edit")
+    @Log(title = "pc装修页面模版", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody MallDiyPcTemplatePageBo bo) {
+        return toAjax(mallDiyPcTemplatePageService.updateByBo(bo));
+    }
+
+    /**
+     * 删除pc装修页面模版
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("mall:diyPcTemplatePage:remove")
+    @Log(title = "pc装修页面模版", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(mallDiyPcTemplatePageService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 285 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/PageCategoryController.java

@@ -0,0 +1,285 @@
+package org.dromara.mall.controller;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.mall.mapper.PageCategoryMapper;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.mall.domain.vo.PageCategoryVo;
+import org.dromara.mall.domain.bo.PageCategoryBo;
+import org.dromara.mall.service.IPageCategoryService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 页面分类管理
+ * 前端访问路由地址为:/mall/pageCategory
+ *
+ * @author ruoyi
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pageCategory")
+public class PageCategoryController extends BaseController {
+
+    private final IPageCategoryService pageCategoryService;
+    private final PageCategoryMapper baseMapper;
+
+    /**
+     * 获取页面链接分类列表
+     */
+    //@SaCheckPermission("mall:pageCategory:list")
+    @PostMapping("/list")
+    public R<List<PageCategoryVo>> list(PageCategoryBo bo) {
+        List<PageCategoryVo> list = pageCategoryService.queryList(bo);
+        return R.ok(list);
+    }
+
+    /**
+     * 获取分类树形结构(用于前端树形选择器)
+     */
+    //@SaCheckPermission("mall:pageCategory:query")
+    @PostMapping("/treeList")
+    public R<List<PageCategoryVo>> treeList(@RequestBody PageCategoryBo bo) {
+        return R.ok(pageCategoryService.selectCategoryTree(bo));
+    }
+
+    /**
+     * 获取分类树形结构
+     */
+    //@SaCheckPermission("mall:pageCategory:query")
+    @GetMapping("/tree")
+    public R<List<PageCategoryVo>> getCategoryTree() {
+        // 创建一个顶级父节点(id=0,名称为"顶级父类")
+        PageCategoryVo rootNode = new PageCategoryVo();
+        rootNode.setId(0L);
+        rootNode.setName("顶级父类");
+        rootNode.setParentId(-1L); // 设置一个不存在的父ID,表示这是真正的根节点
+        rootNode.setLevel(0);
+
+        // 获取所有分类的扁平列表(不分层)
+        PageCategoryBo bo = new PageCategoryBo();
+        bo.setStatus(1);
+        List<PageCategoryVo> allCategories = pageCategoryService.queryList(bo);
+
+        // 将所有parentId=0的分类作为顶级父节点的直接子节点
+        List<PageCategoryVo> rootChildren = new ArrayList<>();
+
+        // 为每个顶级分类(parentId=0)构建其子树
+        for (PageCategoryVo category : allCategories) {
+            if (category.getParentId() == 0) {
+                // 递归构建子分类树
+                buildCategoryChildren(category, allCategories);
+                rootChildren.add(category);
+            }
+        }
+
+        rootNode.setChildren(rootChildren);
+
+        // 构建返回的树结构,只包含这个顶级父节点
+        List<PageCategoryVo> resultTree = new ArrayList<>();
+        resultTree.add(rootNode);
+
+        return R.ok(resultTree);
+    }
+
+    /**
+     * 递归构建分类的子树
+     */
+    private void buildCategoryChildren(PageCategoryVo parent, List<PageCategoryVo> allCategories) {
+        List<PageCategoryVo> children = new ArrayList<>();
+
+        for (PageCategoryVo category : allCategories) {
+            if (category.getParentId().equals(parent.getId())) {
+                // 递归构建子分类
+                buildCategoryChildren(category, allCategories);
+                children.add(category);
+            }
+        }
+
+        if (!children.isEmpty()) {
+            parent.setChildren(children);
+        }
+    }
+
+    /**
+     * 获取分类详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("mall:pageCategory:query")
+    @GetMapping("/{id}")
+    public R<PageCategoryVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(baseMapper.selectVoById(id));
+    }
+
+    /**
+     * 根据父级分类ID查询子分类列表
+     *
+     * @param parentId 父级分类ID
+     */
+    //@SaCheckPermission("mall:pageCategory:query")
+    @GetMapping("/children/{parentId}")
+    public R<List<PageCategoryVo>> getChildren(@NotNull(message = "父级分类ID不能为空")
+                                               @PathVariable("parentId") Long parentId) {
+        return R.ok(pageCategoryService.selectByParentId(parentId).stream()
+            .map(baseMapper::selectVoById)
+            .toList());
+    }
+
+    /**
+     * 检查分类是否可作为父分类(排除自身及子分类)
+     *
+     * @param excludeId 排除的分类ID
+     */
+    //@SaCheckPermission("mall:pageCategory:query")
+    @GetMapping("/checkParentValid/{excludeId}")
+    public R<List<PageCategoryVo>> checkParentValid(@PathVariable("excludeId") Long excludeId) {
+        PageCategoryBo bo = new PageCategoryBo();
+        // 这里可以添加排除逻辑,确保不会出现循环引用
+        List<PageCategoryVo> allCategories = pageCategoryService.queryList(bo);
+        // 构建树并排除当前分类及其子分类
+        return R.ok(buildValidParentTree(allCategories, excludeId));
+    }
+
+    /**
+     * 构建有效的父分类树(排除指定分类及其子分类)
+     */
+    private List<PageCategoryVo> buildValidParentTree(List<PageCategoryVo> allCategories, Long excludeId) {
+        // 查找需要排除的所有分类ID(包括子分类)
+        List<Long> excludeIds = new ArrayList<>();
+        if (excludeId != null) {
+            excludeIds.add(excludeId);
+            findAllChildrenIds(allCategories, excludeId, excludeIds);
+        }
+
+        // 过滤掉需要排除的分类
+        List<PageCategoryVo> filteredCategories = allCategories.stream()
+            .filter(cat -> !excludeIds.contains(cat.getId()))
+            .collect(Collectors.toList());
+
+        // 构建树形结构
+        List<PageCategoryVo> result = new ArrayList<>();
+        List<PageCategoryVo> rootCategories = filteredCategories.stream()
+            .filter(cat -> cat.getParentId() == 0)
+            .toList();
+
+        rootCategories.forEach(root -> {
+            buildTreeChildren(root, filteredCategories);
+            result.add(root);
+        });
+
+        return result;
+    }
+
+    /**
+     * 递归查找所有子分类ID
+     */
+    private void findAllChildrenIds(List<PageCategoryVo> allCategories, Long parentId, List<Long> ids) {
+        List<PageCategoryVo> children = allCategories.stream()
+            .filter(cat -> cat.getParentId().equals(parentId))
+            .toList();
+
+        for (PageCategoryVo child : children) {
+            ids.add(child.getId());
+            findAllChildrenIds(allCategories, child.getId(), ids);
+        }
+    }
+
+    /**
+     * 递归构建树的子节点
+     */
+    private void buildTreeChildren(PageCategoryVo parent, List<PageCategoryVo> allCategories) {
+        List<PageCategoryVo> children = allCategories.stream()
+            .filter(cat -> cat.getParentId().equals(parent.getId()))
+            .collect(Collectors.toList());
+
+        if (!children.isEmpty()) {
+            parent.setChildren(children);
+            children.forEach(child -> buildTreeChildren(child, allCategories));
+        }
+    }
+
+    /**
+     * 根据分类类型查询分类列表
+     *
+     * @param type 分类类型
+     */
+    //@SaCheckPermission("mall:pageCategory:query")
+    @GetMapping("/byType/{type}")
+    public R<List<PageCategoryVo>> getByType(@NotNull(message = "分类类型不能为空")
+                                             @PathVariable("type") String type) {
+        return R.ok(pageCategoryService.selectByType(type).stream()
+            .map(baseMapper::selectVoById)
+            .toList());
+    }
+
+    /**
+     * 新增分类
+     */
+    //@SaCheckPermission("mall:pageCategory:add")
+    @Log(title = "分类管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody PageCategoryBo bo) {
+        return toAjax(pageCategoryService.insertByBo(bo));
+    }
+
+    /**
+     * 修改分类
+     */
+    //@SaCheckPermission("mall:pageCategory:edit")
+    @Log(title = "分类管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PageCategoryBo bo) {
+        return toAjax(pageCategoryService.updateByBo(bo));
+    }
+
+    /**
+     * 删除分类
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("mall:pageCategory:remove")
+    @Log(title = "分类管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable List<Long> ids) {
+        return toAjax(pageCategoryService.deleteWithValidByIds(ids));
+    }
+
+    /**
+     * 检查分类是否有子分类
+     *
+     * @param categoryId 分类ID
+     */
+    //@SaCheckPermission("mall:pageCategory:query")
+    @GetMapping("/hasChild/{categoryId}")
+    public R<Boolean> hasChild(@NotNull(message = "分类ID不能为空")
+                               @PathVariable("categoryId") Long categoryId) {
+        return R.ok(pageCategoryService.hasChildByCategoryId(categoryId));
+    }
+
+    /**
+     * 检查分类是否关联链接
+     *
+     * @param categoryId 分类ID
+     */
+    //@SaCheckPermission("mall:pageCategory:query")
+    @GetMapping("/hasLink/{categoryId}")
+    public R<Boolean> hasLink(@NotNull(message = "分类ID不能为空")
+                              @PathVariable("categoryId") Long categoryId) {
+        return R.ok(pageCategoryService.checkCategoryExistLink(categoryId));
+    }
+}
+

+ 162 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/PageLinkController.java

@@ -0,0 +1,162 @@
+package org.dromara.mall.controller;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.mall.domain.PageLink;
+import org.dromara.mall.domain.bo.PageLinkBo;
+import org.dromara.mall.domain.vo.PageLinkVo;
+import org.dromara.mall.mapper.PageLinkMapper;
+import org.dromara.mall.service.IPageLinkService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 页面链接管理
+ * 前端访问路由地址为:/mall/pageLink
+ *
+ * @author ruoyi
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pageLink")
+public class PageLinkController extends BaseController {
+
+    private final IPageLinkService pageLinkService;
+    private final PageLinkMapper baseMapper;
+
+    /**
+     * 查询页面链接列表
+     */
+    //@SaCheckPermission("system:pageLink:list")
+    @GetMapping("/list")
+    public TableDataInfo<PageLinkVo> list(PageLinkBo bo, PageQuery pageQuery) {
+        return pageLinkService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 根据分类ID查询链接列表
+     *
+     * @param categoryId 分类ID
+     */
+    //@SaCheckPermission("mall:pageLink:query")
+    @GetMapping("/byCategory/{categoryId}")
+    public R<List<PageLinkVo>> getByCategory(@NotNull(message = "分类ID不能为空")
+                                             @PathVariable("categoryId") Long categoryId) {
+        return R.ok(pageLinkService.selectByCategoryId(categoryId).stream()
+            .map(baseMapper::selectVoById)
+            .toList());
+    }
+
+    /**
+     * 根据是否自定义查询链接列表
+     *
+     * @param isCustom 是否自定义:0-系统预设,1-自定义
+     */
+    //@SaCheckPermission("mall:pageLink:query")
+    @GetMapping("/byIsCustom/{isCustom}")
+    public R<List<PageLinkVo>> getByIsCustom(@NotNull(message = "是否自定义不能为空")
+                                             @PathVariable("isCustom") Integer isCustom) {
+        return R.ok(pageLinkService.selectByIsCustom(isCustom).stream()
+            .map(baseMapper::selectVoById)
+            .toList());
+    }
+
+    /**
+     * 获取链接详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("mall:pageLink:query")
+    @GetMapping("/detail/{id}")
+    public R<PageLinkVo> getDetail(@NotNull(message = "主键不能为空")
+                                   @PathVariable("id") Long id) {
+        return R.ok(pageLinkService.selectLinkDetail(id));
+    }
+
+    /**
+     * 根据链接关键字查询链接
+     *
+     * @param key 链接关键字
+     */
+    //@SaCheckPermission("mall:pageLink:query")
+    @GetMapping("/byKey/{key}")
+    public R<PageLinkVo> getByKey(@NotBlank(message = "链接关键字不能为空")
+                                  @PathVariable("key") String key) {
+        PageLink link = pageLinkService.selectByKey(key);
+        return link != null ? R.ok(baseMapper.selectVoById(link.getId())) : R.ok();
+    }
+
+    /**
+     * 新增链接
+     */
+    //@SaCheckPermission("mall:pageLink:add")
+    @Log(title = "链接管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody PageLinkBo bo) {
+        return toAjax(pageLinkService.insertByBo(bo));
+    }
+
+    /**
+     * 修改链接
+     */
+    //@SaCheckPermission("mall:pageLink:edit")
+    @Log(title = "链接管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PageLinkBo bo) {
+        return toAjax(pageLinkService.updateByBo(bo));
+    }
+
+    /**
+     * 删除链接
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("mall:pageLink:remove")
+    @Log(title = "链接管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable List<Long> ids) {
+        return toAjax(pageLinkService.deleteWithValidByIds(ids));
+    }
+
+    /**
+     * 批量更新链接状态
+     *
+     * @param ids 主键串
+     * @param status 状态:0-启用,1-禁用
+     */
+//    //@SaCheckPermission("mall:pageLink:edit")
+//    @Log(title = "链接管理", businessType = BusinessType.UPDATE)
+//    @PutMapping("/status/{ids}")
+//    public R<Void> updateStatus(@NotEmpty(message = "主键不能为空")
+//                               @PathVariable List<Long> ids,
+//                               @NotBlank(message = "状态不能为空")
+//                               @RequestParam String status) {
+//        return toAjax(pageLinkService.updateBatchStatus(ids, status));
+//    }
+
+    /**
+     * 批量更新链接排序
+     */
+//    //@SaCheckPermission("mall:pageLink:edit")
+//    @Log(title = "链接管理", businessType = BusinessType.UPDATE)
+//    @PutMapping("/sort")
+//    public R<Void> updateSort(@RequestBody List<PageLink> links) {
+//        return toAjax(pageLinkService.updateBatchSort(links));
+//    }
+}
+
+

+ 86 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/pc/DiyPcPageController.java

@@ -0,0 +1,86 @@
+package org.dromara.mall.controller.pc;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.mall.domain.MallDiyPcPage;
+import org.dromara.mall.service.IMallDiyPcPageService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 前端首页diy装修页面
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/indexDiyPcPage")
+public class DiyPcPageController {
+
+    private final IMallDiyPcPageService mallDiyPcPageService;
+
+    /**
+     * 获取平台首页diy装修页面
+     */
+    @GetMapping("/getPlatformIndexDiyPcPage")
+    public R<MallDiyPcPage> getPlatformIndexDiyPcPage() {
+        MallDiyPcPage one = mallDiyPcPageService.getOne(
+            Wrappers.lambdaQuery(MallDiyPcPage.class)
+                .eq(MallDiyPcPage::getIsHome, 1)
+                .eq(MallDiyPcPage::getType, 1)
+                .last("limit 1")
+        );
+        return R.ok(one);
+    }
+
+    /**
+    * 获取工业品商城首页diy装修页面
+    * */
+    @GetMapping("/getIndustryPlatformIndexDiyPcPage")
+    public R<MallDiyPcPage> getIndustryPlatformIndexDiyPcPage() {
+        MallDiyPcPage one = mallDiyPcPageService.getOne(
+            Wrappers.lambdaQuery(MallDiyPcPage.class)
+                .eq(MallDiyPcPage::getIsHome, 1)
+                .eq(MallDiyPcPage::getType, 2)
+                .last("limit 1")
+        );
+        return R.ok(one);
+    }
+    /**
+    * 获取福利商城首页diy装修页面
+    * */
+    @GetMapping("/getWelfarePlatformIndexDiyPcPage")
+    public R<MallDiyPcPage> getWelfarePlatformIndexDiyPcPage() {
+        MallDiyPcPage one = mallDiyPcPageService.getOne(
+            Wrappers.lambdaQuery(MallDiyPcPage.class)
+                .eq(MallDiyPcPage::getIsHome, 1)
+                .eq(MallDiyPcPage::getType, 3)
+                .last("limit 1")
+        );
+        return R.ok(one);
+    }
+
+    /**
+    * 获取大客户站点首页diy装修页面
+    * */
+    @GetMapping("/getBigCustomerPlatformIndexDiyPcPage")
+    public R<MallDiyPcPage> getBigCustomerPlatformIndexDiyPcPage(Long siteId) {
+        MallDiyPcPage one = mallDiyPcPageService.getOne(
+            Wrappers.lambdaQuery(MallDiyPcPage.class)
+                .eq(MallDiyPcPage::getIsHome, 1)
+                .eq(MallDiyPcPage::getType, 4)
+                .eq(MallDiyPcPage::getSiteId, siteId)
+                .last("limit 1")
+        );
+        return R.ok(one);
+    }
+
+
+
+
+}

+ 76 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/MallDiyPcPage.java

@@ -0,0 +1,76 @@
+package org.dromara.mall.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * pc装修页面对象 mall_diy_pc_page
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("mall_diy_pc_page")
+public class MallDiyPcPage extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 装修页面编号
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 装修模板编号
+     */
+    private Long templateId;
+
+    /**
+     * 页面名称
+     */
+    private String name;
+
+    /**
+     * 站点id
+     */
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    private Long clientId;
+
+    /**
+     * 页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城
+     */
+    private String type;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 预览图,多个逗号分隔
+     */
+    private String previewPicUrls;
+
+    /**
+     * 页面属性,JSON 格式
+     */
+    private String property;
+
+    /**
+     * 是否首页
+     */
+    private Long isHome;
+
+
+}

+ 71 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/MallDiyPcTemplatePage.java

@@ -0,0 +1,71 @@
+package org.dromara.mall.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * pc装修页面模版对象 mall_diy_pc_template_page
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("mall_diy_pc_template_page")
+public class MallDiyPcTemplatePage extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 装修页面编号
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 页面名称
+     */
+    private String name;
+
+    /**
+     * 站点id
+     */
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    private Long clientId;
+
+    /**
+     * 页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城
+     */
+    private String type;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 预览图,多个逗号分隔
+     */
+    private String previewPicUrls;
+
+    /**
+     * 页面属性,JSON 格式
+     */
+    private String property;
+
+    /**
+     * 是否首页
+     */
+    private Long isHome;
+
+
+}

+ 78 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/PageCategory.java

@@ -0,0 +1,78 @@
+package org.dromara.mall.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 页面分类对象 c_page_category
+ *
+ * @author ruoyi
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("mall_page_category")
+public class PageCategory extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 父分类ID,用于构建树形结构
+     */
+    @TableField(value = "pid")
+    private Long parentId;
+
+    /**
+     * 层级
+     */
+    private Integer level;
+
+    /**
+     * 类型标识
+     */
+    private String type;
+
+    /**
+     * 分类名称
+     */
+    private String name;
+
+    /**
+     * 显示标题
+     */
+    private String title;
+
+    /**
+     * 唯一键值,用于前端组件匹配
+     */
+    private String pageKey;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private Integer status;
+
+    /**
+     * 是否商户 0平台,1商户
+     */
+    private Integer isMer;
+
+}
+

+ 80 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/PageLink.java

@@ -0,0 +1,80 @@
+package org.dromara.mall.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 页面链接对象 c_page_link
+ *
+ * @author ruoyi
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("mall_page_link")
+public class PageLink extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 分类ID,关联c_page_category表
+     */
+    private Long cateId;
+
+    /**
+     * 类型标识
+     */
+    private Integer type;
+
+    /**
+     * 链接名称
+     */
+    private String name;
+
+    /**
+     * 唯一标识
+     */
+    private String linkKey;
+
+    /**
+     * 路由路径
+     */
+    private String url;
+
+    /**
+     * 是否自定义链接(0否 1是)
+     */
+    private Integer isCustom;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private Integer status;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+
+    /**
+     * 是否商户 0平台,1商户
+     */
+    private Integer isMer;
+
+}
+

+ 82 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/bo/MallDiyPcPageBo.java

@@ -0,0 +1,82 @@
+package org.dromara.mall.domain.bo;
+
+import org.dromara.mall.domain.MallDiyPcPage;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * pc装修页面业务对象 mall_diy_pc_page
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = MallDiyPcPage.class, reverseConvertGenerate = false)
+public class MallDiyPcPageBo extends BaseEntity {
+
+    /**
+     * 装修页面编号
+     */
+    private Long id;
+
+    /**
+     * 装修模板编号
+     */
+    //@NotNull(message = "装修模板编号不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long templateId;
+
+    /**
+     * 页面名称
+     */
+    private String name;
+
+    /**
+     * 站点id
+     */
+    //@NotNull(message = "站点id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    //@NotNull(message = "客户id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long clientId;
+
+    /**
+     * 页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城
+     */
+    //@NotBlank(message = "页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String type;
+
+    /**
+     * 备注
+     */
+    //@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String remark;
+
+    /**
+     * 预览图,多个逗号分隔
+     */
+    //@NotBlank(message = "预览图,多个逗号分隔不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String previewPicUrls;
+
+    /**
+     * 页面属性,JSON 格式
+     */
+    //@NotBlank(message = "页面属性,JSON 格式不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String property;
+
+    /**
+     * 是否首页
+     */
+    //@NotNull(message = "是否首页不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long isHome;
+
+
+}

+ 76 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/bo/MallDiyPcTemplatePageBo.java

@@ -0,0 +1,76 @@
+package org.dromara.mall.domain.bo;
+
+import org.dromara.mall.domain.MallDiyPcTemplatePage;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * pc装修页面模版业务对象 mall_diy_pc_template_page
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = MallDiyPcTemplatePage.class, reverseConvertGenerate = false)
+public class MallDiyPcTemplatePageBo extends BaseEntity {
+
+    /**
+     * 装修页面编号
+     */
+    private Long id;
+
+    /**
+     * 页面名称
+     */
+    private String name;
+
+    /**
+     * 站点id
+     */
+    //@NotNull(message = "站点id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    //@NotNull(message = "客户id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long clientId;
+
+    /**
+     * 页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城
+     */
+    //@NotBlank(message = "页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String type;
+
+    /**
+     * 备注
+     */
+    //@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String remark;
+
+    /**
+     * 预览图,多个逗号分隔
+     */
+    //@NotBlank(message = "预览图,多个逗号分隔不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String previewPicUrls;
+
+    /**
+     * 页面属性,JSON 格式
+     */
+    //@NotBlank(message = "页面属性,JSON 格式不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String property;
+
+    /**
+     * 是否首页
+     */
+    //@NotNull(message = "是否首页不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long isHome;
+
+
+}

+ 80 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/bo/PageCategoryBo.java

@@ -0,0 +1,80 @@
+package org.dromara.mall.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.mall.domain.PageCategory;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 页面分类业务对象 c_page_category
+ *
+ * @author ruoyi
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@AutoMapper(target = PageCategory.class, reverseConvertGenerate = false)
+public class PageCategoryBo extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 父级分类ID,0表示一级分类
+     */
+//    @NotNull(message = "父级分类ID不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Long parentId;
+
+    /**
+     * 分类级别:1-一级分类,2-二级分类
+     */
+//    @NotNull(message = "分类级别不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Integer level;
+
+    /**
+     * 分类类型:1-商城页面,2-商品页面,3-文章页面,4-自定义链接
+     */
+//    @NotNull(message = "分类类型不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Integer type;
+
+    /**
+     * 分类名称
+     */
+//    @NotBlank(message = "分类名称不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String name;
+
+    /**
+     * 分类标题(用于SEO)
+     */
+    private String title;
+
+    /**
+     * 分类关键字
+     */
+    private String pageKey;
+
+    /**
+     * 排序值
+     */
+    private Integer sort;
+
+    /**
+     * 状态:0-启用,1-禁用
+     */
+    private Integer status;
+
+    /**
+     * 是否商户 0平台,1商户
+     */
+    private Integer isMer;
+}
+

+ 82 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/bo/PageLinkBo.java

@@ -0,0 +1,82 @@
+package org.dromara.mall.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.mall.domain.PageLink;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 页面链接业务对象 c_page_link
+ *
+ * @author ruoyi
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@AutoMapper(target = PageLink.class, reverseConvertGenerate = false)
+public class PageLinkBo extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 分类ID
+     */
+//    @NotNull(message = "分类ID不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Long cateId;
+
+    /**
+     * 类型标识
+     */
+    private Integer type;
+
+    /**
+     * 链接名称
+     */
+//    @NotBlank(message = "链接名称不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String name;
+
+    /**
+     * 链接关键字
+     */
+//    @NotBlank(message = "链接关键字不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String linkKey;
+
+    /**
+     * 链接路径
+     */
+//    @NotBlank(message = "链接路径不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String url;
+
+    /**
+     * 是否自定义链接:0-系统预设,1-自定义
+     */
+//    @NotNull(message = "是否自定义链接不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Integer isCustom;
+
+    /**
+     * 排序值
+     */
+    private Integer sort;
+
+    /**
+     * 状态:0-启用,1-禁用
+     */
+    private Integer status;
+
+    /**
+     * 是否商户 0平台,1商户
+     */
+    private Integer isMer;
+}
+
+

+ 92 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/vo/MallDiyPcPageVo.java

@@ -0,0 +1,92 @@
+package org.dromara.mall.domain.vo;
+
+import org.dromara.mall.domain.MallDiyPcPage;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * pc装修页面视图对象 mall_diy_pc_page
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = MallDiyPcPage.class)
+public class MallDiyPcPageVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 装修页面编号
+     */
+    @ExcelProperty(value = "装修页面编号")
+    private Long id;
+
+    /**
+     * 装修模板编号
+     */
+    @ExcelProperty(value = "装修模板编号")
+    private Long templateId;
+
+    /**
+     * 页面名称
+     */
+    @ExcelProperty(value = "页面名称")
+    private String name;
+
+    /**
+     * 站点id
+     */
+    @ExcelProperty(value = "站点id")
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    @ExcelProperty(value = "客户id")
+    private Long clientId;
+
+    /**
+     * 页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城
+     */
+    @ExcelProperty(value = "页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城")
+    private String type;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 预览图,多个逗号分隔
+     */
+    @ExcelProperty(value = "预览图,多个逗号分隔")
+    private String previewPicUrls;
+
+    /**
+     * 页面属性,JSON 格式
+     */
+    @ExcelProperty(value = "页面属性,JSON 格式")
+    private String property;
+
+    /**
+     * 是否首页
+     */
+    @ExcelProperty(value = "是否首页")
+    private Long isHome;
+
+
+}

+ 86 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/vo/MallDiyPcTemplatePageVo.java

@@ -0,0 +1,86 @@
+package org.dromara.mall.domain.vo;
+
+import org.dromara.mall.domain.MallDiyPcTemplatePage;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * pc装修页面模版视图对象 mall_diy_pc_template_page
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = MallDiyPcTemplatePage.class)
+public class MallDiyPcTemplatePageVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 装修页面编号
+     */
+    @ExcelProperty(value = "装修页面编号")
+    private Long id;
+
+    /**
+     * 页面名称
+     */
+    @ExcelProperty(value = "页面名称")
+    private String name;
+
+    /**
+     * 站点id
+     */
+    @ExcelProperty(value = "站点id")
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    @ExcelProperty(value = "客户id")
+    private Long clientId;
+
+    /**
+     * 页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城
+     */
+    @ExcelProperty(value = "页面类型 1平台商城 2工业品商城 3福利商城 4客户站点商城")
+    private String type;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 预览图,多个逗号分隔
+     */
+    @ExcelProperty(value = "预览图,多个逗号分隔")
+    private String previewPicUrls;
+
+    /**
+     * 页面属性,JSON 格式
+     */
+    @ExcelProperty(value = "页面属性,JSON 格式")
+    private String property;
+
+    /**
+     * 是否首页
+     */
+    @ExcelProperty(value = "是否首页")
+    private Long isHome;
+
+
+}

+ 90 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/vo/PageCategoryVo.java

@@ -0,0 +1,90 @@
+package org.dromara.mall.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.mall.domain.PageCategory;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 页面分类视图对象
+ *
+ * @author ruoyi
+ */
+@Data
+@AutoMapper(target = PageCategory.class)
+public class PageCategoryVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 父分类ID,用于构建树形结构
+     */
+    private Long parentId;
+
+    /**
+     * 父部门名称
+     */
+    private String parentName;
+
+    /**
+     * 层级
+     */
+    private Integer level;
+
+    /**
+     * 类型标识
+     */
+    private String type;
+
+    /**
+     * 分类名称
+     */
+    private String name;
+
+    /**
+     * 显示标题
+     */
+    private String title;
+
+    /**
+     * 唯一键值,用于前端组件匹配
+     */
+    private String pageKey;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private Integer status;
+
+    /**
+     * 添加时间
+     */
+    private Date createTime;
+
+    /**
+     * 是否商户 0平台,1商户
+     */
+    private Integer isMer;
+
+    /**
+     * 子分类列表
+     */
+    private List<PageCategoryVo> children;
+
+}
+

+ 79 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/domain/vo/PageLinkVo.java

@@ -0,0 +1,79 @@
+package org.dromara.mall.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.mall.domain.PageLink;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 页面链接视图对象
+ *
+ * @author ruoyi
+ */
+@Data
+@AutoMapper(target = PageLink.class)
+public class PageLinkVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 分类ID,关联c_page_category表
+     */
+    private Long cateId;
+
+    /**
+     * 类型标识
+     */
+    private Integer type;
+
+    /**
+     * 链接名称
+     */
+    private String name;
+
+    /**
+     * 唯一标识
+     */
+    private String linkKey;
+
+    /**
+     * 路由路径
+     */
+    private String url;
+
+    /**
+     * 是否自定义链接(0否 1是)
+     */
+    private Integer isCustom;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private Integer status;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+
+    /**
+     * 增加时间
+     */
+    private Date createTime;
+
+    /**
+     * 是否商户 0平台,1商户
+     */
+    private Integer isMer;
+
+}
+

+ 61 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/event/DataSyncEvent.java

@@ -0,0 +1,61 @@
+package org.dromara.mall.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * 数据同步事件
+ * 用于在数据变更时通知相关组件进行同步操作
+ *
+ * @author system
+ * @date 2024-01-18
+ */
+@Getter
+public class DataSyncEvent extends ApplicationEvent {
+
+    /**
+     * 数据类型
+     */
+    private final String dataType;
+
+    /**
+     * 操作类型
+     */
+    private final String operation;
+
+    /**
+     * 数据ID
+     */
+    private final Object dataId;
+
+    /**
+     * 构造函数
+     *
+     * @param source    事件源
+     * @param dataType  数据类型
+     * @param operation 操作类型
+     * @param dataId    数据ID
+     */
+    public DataSyncEvent(Object source, String dataType, String operation, Object dataId) {
+        super(source);
+        this.dataType = dataType;
+        this.operation = operation;
+        this.dataId = dataId;
+    }
+
+    /**
+     * 数据类型常量
+     */
+    public static final String DATA_TYPE_PAGE_CATEGORY = "PAGE_CATEGORY";
+    public static final String DATA_TYPE_PAGE_LINK = "PAGE_LINK";
+
+    /**
+     * 操作类型常量
+     */
+    public static final String OPERATION_CREATE = "CREATE";
+    public static final String OPERATION_UPDATE = "UPDATE";
+    public static final String OPERATION_DELETE = "DELETE";
+    public static final String OPERATION_BATCH_DELETE = "BATCH_DELETE";
+    public static final String OPERATION_STATUS_CHANGE = "STATUS_CHANGE";
+    public static final String OPERATION_SORT_CHANGE = "SORT_CHANGE";
+}

+ 70 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/event/DataSyncListener.java

@@ -0,0 +1,70 @@
+package org.dromara.mall.event;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.mall.config.CacheConstants;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * 数据同步监听器
+ * 监听数据变更事件,清除相关缓存
+ *
+ * @author system
+ * @date 2024-01-18
+ */
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class DataSyncListener implements ApplicationListener<DataSyncEvent> {
+
+    @Override
+    public void onApplicationEvent(DataSyncEvent event) {
+        log.info("收到数据同步事件:dataType={}, operation={}, dataId={}",
+            event.getDataType(), event.getOperation(), event.getDataId());
+
+        // 根据数据类型和操作类型清除相关缓存
+        if (DataSyncEvent.DATA_TYPE_PAGE_CATEGORY.equals(event.getDataType())) {
+            clearCategoryCache();
+        } else if (DataSyncEvent.DATA_TYPE_PAGE_LINK.equals(event.getDataType())) {
+            clearLinkCache(event);
+        }
+    }
+
+    /**
+     * 清除分类相关缓存
+     */
+    private void clearCategoryCache() {
+        // 清除树形结构缓存
+        RedisUtils.deleteObject(CacheConstants.CATEGORY_TREE_CACHE_KEY);
+        // 清除分类列表缓存
+        RedisUtils.deleteObjectPattern(CacheConstants.CATEGORY_LIST_CACHE_KEY + "*");
+        log.info("分类缓存已清除");
+    }
+
+    /**
+     * 清除链接相关缓存
+     *
+     * @param event 数据同步事件
+     */
+    private void clearLinkCache(DataSyncEvent event) {
+        // 如果是批量操作或无法确定具体ID,清除所有链接缓存
+        if (DataSyncEvent.OPERATION_BATCH_DELETE.equals(event.getOperation()) ||
+            DataSyncEvent.OPERATION_STATUS_CHANGE.equals(event.getOperation()) ||
+            DataSyncEvent.OPERATION_SORT_CHANGE.equals(event.getOperation())) {
+            // 清除所有链接列表缓存
+            RedisUtils.deleteObjectPattern(CacheConstants.LINK_LIST_CACHE_KEY + "*");
+            // 清除所有链接详情缓存
+            RedisUtils.deleteObjectPattern(CacheConstants.PAGE_LINK_CACHE_KEY + "*");
+        } else if (event.getDataId() != null) {
+            // 清除特定链接的缓存
+            RedisUtils.deleteObject(CacheConstants.PAGE_LINK_CACHE_KEY + event.getDataId());
+            // 对于单个链接的修改,也需要清除相关分类的链接列表缓存
+            // 这里简化处理,清除所有分类的链接列表缓存
+            RedisUtils.deleteObjectPattern(CacheConstants.LINK_LIST_CACHE_KEY + "category:*");
+        }
+        log.info("链接缓存已清除");
+    }
+}
+

+ 15 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/mapper/MallDiyPcPageMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.mall.mapper;
+
+import org.dromara.mall.domain.MallDiyPcPage;
+import org.dromara.mall.domain.vo.MallDiyPcPageVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * pc装修页面Mapper接口
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+public interface MallDiyPcPageMapper extends BaseMapperPlus<MallDiyPcPage, MallDiyPcPageVo> {
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/mapper/MallDiyPcTemplatePageMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.mall.mapper;
+
+import org.dromara.mall.domain.MallDiyPcTemplatePage;
+import org.dromara.mall.domain.vo.MallDiyPcTemplatePageVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * pc装修页面模版Mapper接口
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+public interface MallDiyPcTemplatePageMapper extends BaseMapperPlus<MallDiyPcTemplatePage, MallDiyPcTemplatePageVo> {
+
+}

+ 43 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/mapper/PageCategoryMapper.java

@@ -0,0 +1,43 @@
+package org.dromara.mall.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.mall.domain.PageCategory;
+import org.dromara.mall.domain.vo.PageCategoryVo;
+
+import java.util.List;
+
+/**
+ * 页面分类Mapper接口
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface PageCategoryMapper extends BaseMapperPlus<PageCategory, PageCategoryVo> {
+
+    /**
+     * 查询分类树结构
+     *
+     * @return 分类树列表
+     */
+    List<PageCategoryVo> selectCategoryTree();
+
+    /**
+     * 根据父ID查询子分类
+     *
+     * @param parentId 父分类ID
+     * @return 子分类列表
+     */
+    List<PageCategory> selectByParentId(Long parentId);
+
+    /**
+     * 根据分类类型查询分类
+     *
+     * @param type 分类类型
+     * @return 分类列表
+     */
+    List<PageCategory> selectByType(String type);
+
+}
+
+

+ 51 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/mapper/PageLinkMapper.java

@@ -0,0 +1,51 @@
+package org.dromara.mall.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.mall.domain.PageLink;
+import org.dromara.mall.domain.vo.PageLinkVo;
+
+import java.util.List;
+
+/**
+ * 页面链接Mapper接口
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface PageLinkMapper extends BaseMapperPlus<PageLink, PageLinkVo> {
+
+    /**
+     * 根据分类ID查询链接列表
+     *
+     * @param categoryId 分类ID
+     * @return 链接列表
+     */
+    List<PageLink> selectByCategoryId(Long categoryId);
+
+    /**
+     * 根据链接类型查询链接列表
+     *
+     * @param isCustom 是否自定义链接
+     * @return 链接列表
+     */
+    List<PageLink> selectByIsCustom(String isCustom);
+
+    /**
+     * 查询链接详情,包含分类信息
+     *
+     * @param id 链接ID
+     * @return 链接详情
+     */
+    PageLinkVo selectLinkDetail(Long id);
+
+    /**
+     * 根据key查询链接
+     *
+     * @param key 唯一标识
+     * @return 链接对象
+     */
+    PageLink selectByKey(String key);
+
+}
+

+ 70 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/IMallDiyPcPageService.java

@@ -0,0 +1,70 @@
+package org.dromara.mall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.mall.domain.MallDiyPcPage;
+import org.dromara.mall.domain.vo.MallDiyPcPageVo;
+import org.dromara.mall.domain.bo.MallDiyPcPageBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * pc装修页面Service接口
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+public interface IMallDiyPcPageService extends IService<MallDiyPcPage>{
+
+    /**
+     * 查询pc装修页面
+     *
+     * @param id 主键
+     * @return pc装修页面
+     */
+    MallDiyPcPageVo queryById(Long id);
+
+    /**
+     * 分页查询pc装修页面列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return pc装修页面分页列表
+     */
+    TableDataInfo<MallDiyPcPageVo> queryPageList(MallDiyPcPageBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的pc装修页面列表
+     *
+     * @param bo 查询条件
+     * @return pc装修页面列表
+     */
+    List<MallDiyPcPageVo> queryList(MallDiyPcPageBo bo);
+
+    /**
+     * 新增pc装修页面
+     *
+     * @param bo pc装修页面
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(MallDiyPcPageBo bo);
+
+    /**
+     * 修改pc装修页面
+     *
+     * @param bo pc装修页面
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(MallDiyPcPageBo bo);
+
+    /**
+     * 校验并批量删除pc装修页面信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 70 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/IMallDiyPcTemplatePageService.java

@@ -0,0 +1,70 @@
+package org.dromara.mall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.mall.domain.MallDiyPcTemplatePage;
+import org.dromara.mall.domain.vo.MallDiyPcTemplatePageVo;
+import org.dromara.mall.domain.bo.MallDiyPcTemplatePageBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * pc装修页面模版Service接口
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+public interface IMallDiyPcTemplatePageService extends IService<MallDiyPcTemplatePage>{
+
+    /**
+     * 查询pc装修页面模版
+     *
+     * @param id 主键
+     * @return pc装修页面模版
+     */
+    MallDiyPcTemplatePageVo queryById(Long id);
+
+    /**
+     * 分页查询pc装修页面模版列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return pc装修页面模版分页列表
+     */
+    TableDataInfo<MallDiyPcTemplatePageVo> queryPageList(MallDiyPcTemplatePageBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的pc装修页面模版列表
+     *
+     * @param bo 查询条件
+     * @return pc装修页面模版列表
+     */
+    List<MallDiyPcTemplatePageVo> queryList(MallDiyPcTemplatePageBo bo);
+
+    /**
+     * 新增pc装修页面模版
+     *
+     * @param bo pc装修页面模版
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(MallDiyPcTemplatePageBo bo);
+
+    /**
+     * 修改pc装修页面模版
+     *
+     * @param bo pc装修页面模版
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(MallDiyPcTemplatePageBo bo);
+
+    /**
+     * 校验并批量删除pc装修页面模版信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 89 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/IPageCategoryService.java

@@ -0,0 +1,89 @@
+package org.dromara.mall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.mall.domain.PageCategory;
+import org.dromara.mall.domain.vo.PageCategoryVo;
+import org.dromara.mall.domain.bo.PageCategoryBo;
+
+import java.util.List;
+
+/**
+ * 页面分类Service接口
+ *
+ * @author ruoyi
+ */
+public interface IPageCategoryService extends IService<PageCategory> {
+
+    /**
+     * 查询分类列表
+     *
+     * @param bo 分类业务对象
+     * @return 分类列表
+     */
+    List<PageCategoryVo> queryList(PageCategoryBo bo);
+
+    /**
+     * 获取分类树形结构
+     *
+     * @return 分类树形结构
+     */
+    List<PageCategoryVo> selectCategoryTree(PageCategoryBo bo);
+
+    /**
+     * 根据父级分类ID查询子分类列表
+     *
+     * @param parentId 父级分类ID
+     * @return 子分类列表
+     */
+    List<PageCategory> selectByParentId(Long parentId);
+
+    /**
+     * 根据分类类型查询分类列表
+     *
+     * @param type 分类类型
+     * @return 分类列表
+     */
+    List<PageCategory> selectByType(String type);
+
+    /**
+     * 新增分类
+     *
+     * @param bo 分类业务对象
+     * @return 结果
+     */
+    Boolean insertByBo(PageCategoryBo bo);
+
+    /**
+     * 修改分类
+     *
+     * @param bo 分类业务对象
+     * @return 结果
+     */
+    Boolean updateByBo(PageCategoryBo bo);
+
+    /**
+     * 批量删除分类
+     *
+     * @param ids 分类ID列表
+     * @return 结果
+     */
+    Boolean deleteWithValidByIds(List<Long> ids);
+
+    /**
+     * 检查分类是否有子分类
+     *
+     * @param categoryId 分类ID
+     * @return 是否有子分类
+     */
+    Boolean hasChildByCategoryId(Long categoryId);
+
+    /**
+     * 检查分类是否关联链接
+     *
+     * @param categoryId 分类ID
+     * @return 是否关联链接
+     */
+    Boolean checkCategoryExistLink(Long categoryId);
+}
+
+

+ 110 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/IPageLinkService.java

@@ -0,0 +1,110 @@
+package org.dromara.mall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.mall.domain.PageLink;
+import org.dromara.mall.domain.vo.PageLinkVo;
+import org.dromara.mall.domain.bo.PageLinkBo;
+
+import java.util.List;
+
+/**
+ * 页面链接Service接口
+ *
+ * @author ruoyi
+ */
+public interface IPageLinkService extends IService<PageLink> {
+
+    /**
+     * 分页查询页面链接列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 页面链接分页列表
+     */
+    TableDataInfo<PageLinkVo> queryPageList(PageLinkBo bo, PageQuery pageQuery);
+
+    /**
+     * 根据分类ID查询链接列表
+     *
+     * @param categoryId 分类ID
+     * @return 链接列表
+     */
+    List<PageLink> selectByCategoryId(Long categoryId);
+
+    /**
+     * 根据是否自定义查询链接列表
+     *
+     * @param isCustom 是否自定义:0-系统预设,1-自定义
+     * @return 链接列表
+     */
+    List<PageLink> selectByIsCustom(Integer isCustom);
+
+    /**
+     * 根据ID查询链接详情
+     *
+     * @param id 链接ID
+     * @return 链接详情
+     */
+    PageLinkVo selectLinkDetail(Long id);
+
+    /**
+     * 根据链接关键字查询链接
+     *
+     * @param key 链接关键字
+     * @return 链接信息
+     */
+    PageLink selectByKey(String key);
+
+    /**
+     * 根据分类ID列表查询链接
+     *
+     * @param categoryIds 分类ID列表
+     * @return 链接列表
+     */
+    List<PageLink> selectLinksByCategoryIds(List<Long> categoryIds);
+
+    /**
+     * 新增链接
+     *
+     * @param bo 链接业务对象
+     * @return 结果
+     */
+    Boolean insertByBo(PageLinkBo bo);
+
+    /**
+     * 修改链接
+     *
+     * @param bo 链接业务对象
+     * @return 结果
+     */
+    Boolean updateByBo(PageLinkBo bo);
+
+    /**
+     * 批量删除链接
+     *
+     * @param ids 链接ID列表
+     * @return 结果
+     */
+    Boolean deleteWithValidByIds(List<Long> ids);
+
+    /**
+     * 批量更新链接状态
+     *
+     * @param ids 链接ID列表
+     * @param status 状态:0-启用,1-禁用
+     * @return 结果
+     */
+//    Boolean updateBatchStatus(List<Long> ids, String status);
+
+    /**
+     * 批量更新链接排序
+     *
+     * @param links 链接列表
+     * @return 结果
+     */
+//    Boolean updateBatchSort(List<PageLink> links);
+}
+
+

+ 140 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/impl/MallDiyPcPageServiceImpl.java

@@ -0,0 +1,140 @@
+package org.dromara.mall.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.mall.domain.bo.MallDiyPcPageBo;
+import org.dromara.mall.domain.vo.MallDiyPcPageVo;
+import org.dromara.mall.domain.MallDiyPcPage;
+import org.dromara.mall.mapper.MallDiyPcPageMapper;
+import org.dromara.mall.service.IMallDiyPcPageService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * pc装修页面Service业务层处理
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class MallDiyPcPageServiceImpl  extends ServiceImpl<MallDiyPcPageMapper, MallDiyPcPage> implements IMallDiyPcPageService {
+
+    private final MallDiyPcPageMapper baseMapper;
+
+    /**
+     * 查询pc装修页面
+     *
+     * @param id 主键
+     * @return pc装修页面
+     */
+    @Override
+    public MallDiyPcPageVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询pc装修页面列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return pc装修页面分页列表
+     */
+    @Override
+    public TableDataInfo<MallDiyPcPageVo> queryPageList(MallDiyPcPageBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<MallDiyPcPage> lqw = buildQueryWrapper(bo);
+        Page<MallDiyPcPageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的pc装修页面列表
+     *
+     * @param bo 查询条件
+     * @return pc装修页面列表
+     */
+    @Override
+    public List<MallDiyPcPageVo> queryList(MallDiyPcPageBo bo) {
+        LambdaQueryWrapper<MallDiyPcPage> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<MallDiyPcPage> buildQueryWrapper(MallDiyPcPageBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<MallDiyPcPage> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(MallDiyPcPage::getId);
+        lqw.eq(bo.getTemplateId() != null, MallDiyPcPage::getTemplateId, bo.getTemplateId());
+        lqw.like(StringUtils.isNotBlank(bo.getName()), MallDiyPcPage::getName, bo.getName());
+        lqw.eq(bo.getSiteId() != null, MallDiyPcPage::getSiteId, bo.getSiteId());
+        lqw.eq(bo.getClientId() != null, MallDiyPcPage::getClientId, bo.getClientId());
+        lqw.eq(StringUtils.isNotBlank(bo.getType()), MallDiyPcPage::getType, bo.getType());
+        lqw.eq(StringUtils.isNotBlank(bo.getPreviewPicUrls()), MallDiyPcPage::getPreviewPicUrls, bo.getPreviewPicUrls());
+        lqw.eq(StringUtils.isNotBlank(bo.getProperty()), MallDiyPcPage::getProperty, bo.getProperty());
+        lqw.eq(bo.getIsHome() != null, MallDiyPcPage::getIsHome, bo.getIsHome());
+        return lqw;
+    }
+
+    /**
+     * 新增pc装修页面
+     *
+     * @param bo pc装修页面
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(MallDiyPcPageBo bo) {
+        MallDiyPcPage add = MapstructUtils.convert(bo, MallDiyPcPage.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改pc装修页面
+     *
+     * @param bo pc装修页面
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(MallDiyPcPageBo bo) {
+        MallDiyPcPage update = MapstructUtils.convert(bo, MallDiyPcPage.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(MallDiyPcPage entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除pc装修页面信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 139 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/impl/MallDiyPcTemplatePageServiceImpl.java

@@ -0,0 +1,139 @@
+package org.dromara.mall.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.mall.domain.bo.MallDiyPcTemplatePageBo;
+import org.dromara.mall.domain.vo.MallDiyPcTemplatePageVo;
+import org.dromara.mall.domain.MallDiyPcTemplatePage;
+import org.dromara.mall.mapper.MallDiyPcTemplatePageMapper;
+import org.dromara.mall.service.IMallDiyPcTemplatePageService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * pc装修页面模版Service业务层处理
+ *
+ * @author LionLi
+ * @date 2026-03-07
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class MallDiyPcTemplatePageServiceImpl  extends ServiceImpl<MallDiyPcTemplatePageMapper, MallDiyPcTemplatePage> implements IMallDiyPcTemplatePageService {
+
+    private final MallDiyPcTemplatePageMapper baseMapper;
+
+    /**
+     * 查询pc装修页面模版
+     *
+     * @param id 主键
+     * @return pc装修页面模版
+     */
+    @Override
+    public MallDiyPcTemplatePageVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询pc装修页面模版列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return pc装修页面模版分页列表
+     */
+    @Override
+    public TableDataInfo<MallDiyPcTemplatePageVo> queryPageList(MallDiyPcTemplatePageBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<MallDiyPcTemplatePage> lqw = buildQueryWrapper(bo);
+        Page<MallDiyPcTemplatePageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的pc装修页面模版列表
+     *
+     * @param bo 查询条件
+     * @return pc装修页面模版列表
+     */
+    @Override
+    public List<MallDiyPcTemplatePageVo> queryList(MallDiyPcTemplatePageBo bo) {
+        LambdaQueryWrapper<MallDiyPcTemplatePage> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<MallDiyPcTemplatePage> buildQueryWrapper(MallDiyPcTemplatePageBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<MallDiyPcTemplatePage> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(MallDiyPcTemplatePage::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getName()), MallDiyPcTemplatePage::getName, bo.getName());
+        lqw.eq(bo.getSiteId() != null, MallDiyPcTemplatePage::getSiteId, bo.getSiteId());
+        lqw.eq(bo.getClientId() != null, MallDiyPcTemplatePage::getClientId, bo.getClientId());
+        lqw.eq(StringUtils.isNotBlank(bo.getType()), MallDiyPcTemplatePage::getType, bo.getType());
+        lqw.eq(StringUtils.isNotBlank(bo.getPreviewPicUrls()), MallDiyPcTemplatePage::getPreviewPicUrls, bo.getPreviewPicUrls());
+        lqw.eq(StringUtils.isNotBlank(bo.getProperty()), MallDiyPcTemplatePage::getProperty, bo.getProperty());
+        lqw.eq(bo.getIsHome() != null, MallDiyPcTemplatePage::getIsHome, bo.getIsHome());
+        return lqw;
+    }
+
+    /**
+     * 新增pc装修页面模版
+     *
+     * @param bo pc装修页面模版
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(MallDiyPcTemplatePageBo bo) {
+        MallDiyPcTemplatePage add = MapstructUtils.convert(bo, MallDiyPcTemplatePage.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改pc装修页面模版
+     *
+     * @param bo pc装修页面模版
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(MallDiyPcTemplatePageBo bo) {
+        MallDiyPcTemplatePage update = MapstructUtils.convert(bo, MallDiyPcTemplatePage.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(MallDiyPcTemplatePage entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除pc装修页面模版信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 329 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/impl/PageCategoryServiceImpl.java

@@ -0,0 +1,329 @@
+package org.dromara.mall.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.base.BaseException;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.mall.config.CacheConstants;
+import org.dromara.mall.domain.PageCategory;
+import org.dromara.mall.domain.PageLink;
+import org.dromara.mall.domain.bo.PageCategoryBo;
+import org.dromara.mall.domain.vo.PageCategoryVo;
+import org.dromara.mall.event.DataSyncEvent;
+import org.dromara.mall.mapper.PageCategoryMapper;
+import org.dromara.mall.mapper.PageLinkMapper;
+import org.dromara.mall.service.IPageCategoryService;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 页面分类Service业务层处理
+ *
+ * @author ruoyi
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class PageCategoryServiceImpl extends ServiceImpl<PageCategoryMapper, PageCategory> implements IPageCategoryService {
+
+    private final PageCategoryMapper baseMapper;
+    private final PageLinkMapper pageLinkMapper;
+    private final ApplicationEventPublisher eventPublisher;
+
+    @Override
+    public List<PageCategoryVo> queryList(PageCategoryBo bo) {
+        LambdaQueryWrapper<PageCategory> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<PageCategory> buildQueryWrapper(PageCategoryBo bo) {
+        // 添加空值检查,防止空指针异常
+        if (bo == null) {
+            return new LambdaQueryWrapper<PageCategory>().orderByAsc(PageCategory::getSort);
+        }
+
+        return new LambdaQueryWrapper<PageCategory>()
+            .like(StringUtils.isNotBlank(bo.getTitle()), PageCategory::getTitle, bo.getTitle())
+            .like(StringUtils.isNotBlank(bo.getPageKey()), PageCategory::getPageKey, bo.getPageKey())
+            .eq(bo.getCreateTime() != null, PageCategory::getCreateTime, bo.getCreateTime())
+            .eq(bo.getParentId() != null && bo.getParentId() > 0, PageCategory::getParentId, bo.getParentId())
+            .eq(bo.getStatus() != null, PageCategory::getStatus, bo.getStatus())
+            .eq(bo.getType() != null, PageCategory::getType, bo.getType())
+            .eq(bo.getIsMer() != null, PageCategory::getIsMer, bo.getIsMer())
+            .like(StringUtils.isNotBlank(bo.getName()), PageCategory::getName, bo.getName())
+            .orderByAsc(PageCategory::getSort);
+    }
+
+    /**
+     * 获取分类树形结构
+     *
+     * @return 分类树形结构
+     */
+    @Override
+    public List<PageCategoryVo> selectCategoryTree(PageCategoryBo bo) {
+        LambdaQueryWrapper<PageCategory> lqw = buildQueryWrapper(bo);
+        List<PageCategoryVo> categoryList = baseMapper.selectVoList(lqw);
+        return buildCategoryTree(categoryList);
+
+    }
+
+    /**
+     * 构建分类树
+     *
+     * @param categoryList 分类列表
+     * @return 分类树
+     */
+    private List<PageCategoryVo> buildCategoryTree(List<PageCategoryVo> categoryList) {
+        List<PageCategoryVo> result = new ArrayList<>();
+        List<PageCategoryVo> mainCategories = categoryList.stream()
+            .filter(category -> category.getParentId() == 0)
+            .toList();
+
+        mainCategories.forEach(category -> {
+            buildChildren(category, categoryList);
+            result.add(category);
+        });
+
+        return result;
+    }
+
+    /**
+     * 递归构建子分类
+     *
+     * @param parent       父分类
+     * @param categoryList 所有分类
+     */
+    private void buildChildren(PageCategoryVo parent, List<PageCategoryVo> categoryList) {
+        List<PageCategoryVo> children = categoryList.stream()
+            .filter(category -> category.getParentId().equals(parent.getId()))
+            .collect(Collectors.toList());
+
+        if (!children.isEmpty()) {
+            parent.setChildren(children);
+            children.forEach(child -> buildChildren(child, categoryList));
+        }
+    }
+
+    /**
+     * 根据父级分类ID查询子分类列表
+     *
+     * @param parentId 父级分类ID
+     * @return 子分类列表
+     */
+    @Override
+    public List<PageCategory> selectByParentId(Long parentId) {
+        String cacheKey = CacheConstants.CATEGORY_LIST_CACHE_KEY + "parent:" + parentId;
+        return RedisUtils.getCacheObject(cacheKey, () -> baseMapper.selectByParentId(parentId), CacheConstants.CACHE_EXPIRE_TIME);
+    }
+
+    /**
+     * 根据分类类型查询分类列表
+     *
+     * @param type 分类类型
+     * @return 分类列表
+     */
+    @Override
+    public List<PageCategory> selectByType(String type) {
+        String cacheKey = CacheConstants.CATEGORY_LIST_CACHE_KEY + "type:" + type;
+        return RedisUtils.getCacheObject(cacheKey, () -> baseMapper.selectByType(type), CacheConstants.CACHE_EXPIRE_TIME);
+    }
+
+    /**
+     * 新增分类
+     *
+     * @param bo 分类业务对象
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public Boolean insertByBo(PageCategoryBo bo) {
+        PageCategory category = MapstructUtils.convert(bo, PageCategory.class);
+        boolean result = false;
+        if (category != null) {
+            if (StringUtils.isEmpty(String.valueOf(category.getSort()))) {
+                category.setSort(50); //默认排序50
+            }
+            if (category.getStatus() == null) {
+                category.setStatus(0); //默认启用
+            }
+            result = save(category);
+            if (result) {
+                // 发布数据同步事件
+                eventPublisher.publishEvent(new DataSyncEvent(
+                    this,
+                    DataSyncEvent.DATA_TYPE_PAGE_CATEGORY,
+                    DataSyncEvent.OPERATION_CREATE,
+                    category.getId()
+                ));
+                // 清除缓存
+                clearCategoryCache();
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 修改分类
+     *
+     * @param bo 分类业务对象
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public Boolean updateByBo(PageCategoryBo bo) {
+        PageCategory category = MapstructUtils.convert(bo, PageCategory.class);
+        boolean result = false;
+        if (category != null) {
+            // 检查是否为子分类修改为自己的子分类
+            if (category.getId().equals(category.getParentId())) {
+                throw new BaseException("不能将分类设置为自己的子分类");
+            }
+            // 检查是否存在循环引用
+            if (checkCircularReference(category.getId(), category.getParentId())) {
+                throw new BaseException("存在循环引用,不能修改分类");
+            }
+            result = updateById(category);
+            if (result) {
+                // 发布数据同步事件
+                eventPublisher.publishEvent(new DataSyncEvent(
+                    this,
+                    DataSyncEvent.DATA_TYPE_PAGE_CATEGORY,
+                    DataSyncEvent.OPERATION_UPDATE,
+                    category.getId()
+                ));
+                // 清除缓存
+                clearCategoryCache();
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 检查循环引用
+     *
+     * @param categoryId 当前分类ID
+     * @param parentId   父分类ID
+     * @return 是否存在循环引用
+     */
+    private Boolean checkCircularReference(Long categoryId, Long parentId) {
+        if (parentId == 0) {
+            return false;
+        }
+        PageCategory parent = getById(parentId);
+        if (parent == null) {
+            return false;
+        }
+        if (parent.getId().equals(categoryId)) {
+            return true;
+        }
+        return checkCircularReference(categoryId, parent.getParentId());
+    }
+
+    /**
+     * 批量删除分类
+     *
+     * @param ids 分类ID列表
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public Boolean deleteWithValidByIds(List<Long> ids) {
+        for (Long id : ids) {
+            // 检查是否有子分类
+            if (hasChildByCategoryId(id)) {
+                throw new BaseException("分类下存在子分类,无法删除");
+            }
+            // 检查是否关联链接
+            if (checkCategoryExistLink(id)) {
+                throw new BaseException("分类下存在链接,无法删除");
+            }
+        }
+        boolean result = removeByIds(ids);
+        if (result) {
+            // 发布数据同步事件
+            eventPublisher.publishEvent(new DataSyncEvent(
+                this,
+                DataSyncEvent.DATA_TYPE_PAGE_CATEGORY,
+                DataSyncEvent.OPERATION_BATCH_DELETE,
+                ids
+            ));
+            // 清除缓存
+            clearCategoryCache();
+        }
+        return result;
+    }
+
+    /**
+     * 检查分类是否有子分类
+     *
+     * @param categoryId 分类ID
+     * @return 是否有子分类
+     */
+    @Override
+    public Boolean hasChildByCategoryId(Long categoryId) {
+        LambdaQueryWrapper<PageCategory> lqw = Wrappers.lambdaQuery();
+        lqw.eq(PageCategory::getParentId, categoryId);
+        return baseMapper.exists(lqw);
+    }
+
+    /**
+     * 检查分类是否关联链接
+     *
+     * @param categoryId 分类ID
+     * @return 是否关联链接
+     */
+    @Override
+    public Boolean checkCategoryExistLink(Long categoryId) {
+        LambdaQueryWrapper<PageLink> lqw = Wrappers.lambdaQuery();
+        lqw.eq(PageLink::getCateId, categoryId);
+        return pageLinkMapper.exists(lqw);
+    }
+
+    /**
+     * 清除分类缓存
+     *
+     * @param category 分类对象,为null时清除所有缓存
+     */
+    private void clearCategoryCache(PageCategory category) {
+        if (category == null) {
+            // 清除所有分类缓存
+            RedisUtils.deleteObject(CacheConstants.CATEGORY_TREE_CACHE_KEY);
+            RedisUtils.deleteObjectPattern(CacheConstants.PAGE_CATEGORY_CACHE_KEY + "*");
+            RedisUtils.deleteObjectPattern(CacheConstants.CATEGORY_LIST_CACHE_KEY + "*");
+        } else {
+            // 清除分类详情缓存
+            RedisUtils.deleteObject(CacheConstants.PAGE_CATEGORY_CACHE_KEY + category.getId());
+
+            // 清除分类树形结构缓存
+            RedisUtils.deleteObject(CacheConstants.CATEGORY_TREE_CACHE_KEY);
+
+            // 清除分类列表缓存
+            RedisUtils.deleteObject(CacheConstants.CATEGORY_LIST_CACHE_KEY + "*");
+
+            // 如果有父分类,清除父分类的缓存
+            if (category.getParentId() != null && category.getParentId() > 0) {
+                RedisUtils.deleteObject(CacheConstants.PAGE_CATEGORY_CACHE_KEY + category.getParentId());
+            }
+        }
+    }
+
+    /**
+     * 清除所有分类缓存
+     */
+    private void clearCategoryCache() {
+        clearCategoryCache(null);
+    }
+}
+

+ 365 - 0
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/service/impl/PageLinkServiceImpl.java

@@ -0,0 +1,365 @@
+package org.dromara.mall.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.base.BaseException;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.mall.config.CacheConstants;
+import org.dromara.mall.domain.PageLink;
+import org.dromara.mall.domain.vo.PageLinkVo;
+import org.dromara.mall.domain.bo.PageLinkBo;
+import org.dromara.mall.event.DataSyncEvent;
+import org.dromara.mall.mapper.PageLinkMapper;
+import org.dromara.mall.service.IPageLinkService;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 页面链接Service业务层处理
+ *
+ * @author ruoyi
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class PageLinkServiceImpl extends ServiceImpl<PageLinkMapper, PageLink> implements IPageLinkService {
+
+    private final PageLinkMapper baseMapper;
+    private final ApplicationEventPublisher eventPublisher;
+
+    /**
+     * 分页查询页面链接列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 页面链接分页列表
+     */
+    @Override
+    public TableDataInfo<PageLinkVo> queryPageList(PageLinkBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<PageLink> lqw = buildQueryWrapper(bo);
+        Page<PageLinkVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    private LambdaQueryWrapper<PageLink> buildQueryWrapper(PageLinkBo bo) {
+        LambdaQueryWrapper<PageLink> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(PageLink::getId);
+        lqw.eq(bo.getCateId() != null, PageLink::getCateId, bo.getCateId());
+        lqw.eq(bo.getType() != null, PageLink::getType, bo.getType());
+        lqw.like(StringUtils.isNotBlank(bo.getName()), PageLink::getName, bo.getName());
+        lqw.eq(StringUtils.isNotBlank(bo.getLinkKey()), PageLink::getLinkKey, bo.getLinkKey());
+        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), PageLink::getUrl, bo.getUrl());
+        lqw.eq(bo.getIsCustom() != null, PageLink::getIsCustom, bo.getIsCustom());
+        lqw.eq(bo.getStatus() != null, PageLink::getStatus, bo.getStatus());
+        lqw.eq(bo.getSort() != null, PageLink::getSort, bo.getSort());
+        lqw.eq(bo.getCreateTime() != null, PageLink::getCreateTime, bo.getCreateTime());
+        lqw.eq(bo.getIsMer() != null, PageLink::getIsMer, bo.getIsMer());
+        return lqw;
+    }
+
+    /**
+     * 根据分类ID查询链接列表
+     *
+     * @param categoryId 分类ID
+     * @return 链接列表
+     */
+    @Override
+    public List<PageLink> selectByCategoryId(Long categoryId) {
+        String cacheKey = CacheConstants.LINK_LIST_CACHE_KEY + "category:" + categoryId;
+        return RedisUtils.getCacheObject(cacheKey, () -> {
+            LambdaQueryWrapper<PageLink> lqw = Wrappers.lambdaQuery();
+            lqw.eq(PageLink::getCateId, categoryId);
+            lqw.orderByAsc(PageLink::getSort);
+            return baseMapper.selectList(lqw);
+        }, CacheConstants.CACHE_EXPIRE_TIME);
+    }
+
+    /**
+     * 根据是否自定义查询链接列表
+     *
+     * @param isCustom 是否自定义:0-系统预设,1-自定义
+     * @return 链接列表
+     */
+    @Override
+    public List<PageLink> selectByIsCustom(Integer isCustom) {
+        String cacheKey = CacheConstants.LINK_LIST_CACHE_KEY + "custom:" + isCustom;
+        return RedisUtils.getCacheObject(cacheKey, () -> {
+            LambdaQueryWrapper<PageLink> lqw = Wrappers.lambdaQuery();
+            lqw.eq(PageLink::getIsCustom, isCustom);
+            lqw.orderByAsc(PageLink::getSort);
+            return baseMapper.selectList(lqw);
+        }, CacheConstants.CACHE_EXPIRE_TIME);
+    }
+
+    /**
+     * 根据ID查询链接详情
+     *
+     * @param id 链接ID
+     * @return 链接详情
+     */
+    @Override
+    public PageLinkVo selectLinkDetail(Long id) {
+        String cacheKey = CacheConstants.PAGE_LINK_CACHE_KEY + id;
+        return baseMapper.selectLinkDetail(id);
+    }
+
+    /**
+     * 根据链接关键字查询链接
+     *
+     * @param key 链接关键字
+     * @return 链接信息
+     */
+    @Override
+    public PageLink selectByKey(String key) {
+        LambdaQueryWrapper<PageLink> lqw = Wrappers.lambdaQuery();
+        lqw.eq(PageLink::getLinkKey, key);
+        return baseMapper.selectOne(lqw);
+    }
+
+    /**
+     * 根据分类ID列表查询链接
+     *
+     * @param categoryIds 分类ID列表
+     * @return 链接列表
+     */
+    @Override
+    public List<PageLink> selectLinksByCategoryIds(List<Long> categoryIds) {
+        if (categoryIds == null || categoryIds.isEmpty()) {
+            return null;
+        }
+        // 简单实现,不缓存多ID查询
+        LambdaQueryWrapper<PageLink> lqw = Wrappers.lambdaQuery();
+        lqw.in(PageLink::getCateId, categoryIds);
+        lqw.orderByAsc(PageLink::getSort);
+        return baseMapper.selectList(lqw);
+    }
+
+    /**
+     * 新增链接
+     *
+     * @param bo 链接业务对象
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public Boolean insertByBo(PageLinkBo bo) {
+        boolean result = false;
+        // 检查链接关键字是否已存在
+        checkKeyUnique(bo.getLinkKey(), null);
+
+        PageLink link = MapstructUtils.convert(bo, PageLink.class);
+        if (link != null) {
+            if (link.getSort() == null) {
+                link.setSort(50);
+            }
+
+            result = save(link);
+            if (result) {
+                // 发布数据同步事件
+                eventPublisher.publishEvent(new DataSyncEvent(
+                    this,
+                    DataSyncEvent.DATA_TYPE_PAGE_LINK,
+                    DataSyncEvent.OPERATION_CREATE,
+                    link.getId()
+                ));
+                // 清除缓存
+                clearLinkCache(link);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 修改链接
+     *
+     * @param bo 链接业务对象
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public Boolean updateByBo(PageLinkBo bo) {
+        boolean result = false;
+        // 检查链接关键字是否已存在
+        checkKeyUnique(bo.getLinkKey(), bo.getId());
+
+        PageLink link = MapstructUtils.convert(bo, PageLink.class);
+        // 获取旧的链接信息,用于清除缓存
+        if (link != null) {
+            PageLink oldLink = getById(link.getId());
+            result = updateById(link);
+            if (result) {
+                // 发布数据同步事件
+                eventPublisher.publishEvent(new DataSyncEvent(
+                    this,
+                    DataSyncEvent.DATA_TYPE_PAGE_LINK,
+                    DataSyncEvent.OPERATION_UPDATE,
+                    link.getId()
+                ));
+                // 清除新旧链接的缓存
+                if (oldLink != null) {
+                    clearLinkCache(oldLink);
+                }
+                clearLinkCache(link);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 检查链接关键字是否唯一
+     *
+     * @param key 链接关键字
+     * @param id  链接ID,用于更新时排除自身
+     */
+    private void checkKeyUnique(String key, Long id) {
+        LambdaQueryWrapper<PageLink> lqw = Wrappers.lambdaQuery();
+        lqw.eq(PageLink::getLinkKey, key);
+        if (id != null) {
+            lqw.ne(PageLink::getId, id);
+        }
+        if (baseMapper.exists(lqw)) {
+            throw new BaseException("链接关键字已存在,请更换");
+        }
+    }
+
+    /**
+     * 批量删除链接
+     *
+     * @param ids 链接ID列表
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public Boolean deleteWithValidByIds(List<Long> ids) {
+        // 检查是否有系统预设的链接
+        LambdaQueryWrapper<PageLink> lqw = Wrappers.lambdaQuery();
+        lqw.in(PageLink::getId, ids);
+        lqw.eq(PageLink::getIsCustom, 0);
+        List<PageLink> systemLinks = baseMapper.selectList(lqw);
+        if (!systemLinks.isEmpty()) {
+            throw new BaseException("包含系统预设链接,无法删除");
+        }
+        // 获取要删除的链接信息,用于清除缓存
+        List<PageLink> links = listByIds(ids);
+        boolean result = removeByIds(ids);
+        if (result && links != null) {
+            // 发布数据同步事件
+            eventPublisher.publishEvent(new DataSyncEvent(
+                this,
+                DataSyncEvent.DATA_TYPE_PAGE_LINK,
+                DataSyncEvent.OPERATION_BATCH_DELETE,
+                ids
+            ));
+            // 清除缓存
+            for (PageLink link : links) {
+                clearLinkCache(link);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 批量更新链接状态
+     *
+     * @param ids 链接ID列表
+     * @param status 状态:0-启用,1-禁用
+     * @return 结果
+     */
+//    @Override
+//    @Transactional
+//    public Boolean updateBatchStatus(List<Long> ids, String status) {
+//        // 获取要更新的链接信息,用于清除缓存
+//        List<PageLink> links = listByIds(ids);
+//        boolean result = baseMapper.updateBatchStatus(ids, status) > 0;
+//        if (result && links != null) {
+//            // 发布数据同步事件
+//            eventPublisher.publishEvent(new DataSyncEvent(
+//                    this,
+//                    DataSyncEvent.DATA_TYPE_PAGE_LINK,
+//                    DataSyncEvent.OPERATION_STATUS_CHANGE,
+//                    ids
+//            ));
+//            // 清除缓存
+//            for (PageLink link : links) {
+//                clearLinkCache(link);
+//            }
+//        }
+//        return result;
+//    }
+
+    /**
+     * 批量更新链接排序
+     *
+     * @param links 链接列表
+     * @return 结果
+     */
+//    @Override
+//    @Transactional
+//    public Boolean updateBatchSort(List<PageLink> links) {
+//        if (links.isEmpty()) {
+//            return true;
+//        }
+//        // 验证分类一致性
+//        Map<Long, List<PageLink>> categoryMap = links.stream()
+//                .collect(Collectors.groupingBy(PageLink::getCategoryId));
+//        if (categoryMap.size() > 1) {
+//            throw new BaseException("批量排序只能处理同一分类下的链接");
+//        }
+//        boolean result = baseMapper.updateBatchSort(links) > 0;
+//        if (result) {
+//            // 发布数据同步事件
+//            eventPublisher.publishEvent(new DataSyncEvent(
+//                    this,
+//                    DataSyncEvent.DATA_TYPE_PAGE_LINK,
+//                    DataSyncEvent.OPERATION_SORT_CHANGE,
+//                    links.stream().map(PageLink::getId).collect(Collectors.toList())
+//            ));
+//            // 清除缓存
+//            for (PageLink link : links) {
+//                clearLinkCache(link);
+//            }
+//        }
+//        return result;
+//    }
+
+    /**
+     * 清除链接缓存
+     *
+     * @param link 链接对象
+     */
+    private void clearLinkCache(PageLink link) {
+        if (link == null) {
+            return;
+        }
+
+        // 清除链接详情缓存
+        RedisUtils.deleteObject(CacheConstants.PAGE_LINK_CACHE_KEY + link.getId());
+
+        // 清除分类链接列表缓存
+        RedisUtils.deleteObject(CacheConstants.LINK_LIST_CACHE_KEY + "category:" + link.getCateId());
+
+        // 清除自定义链接列表缓存
+        RedisUtils.deleteObject(CacheConstants.LINK_LIST_CACHE_KEY + "custom:" + link.getIsCustom());
+
+        // 清除关键字缓存
+        if (StringUtils.isNotEmpty(link.getLinkKey())) {
+            RedisUtils.deleteObject(CacheConstants.PAGE_LINK_CACHE_KEY + link.getLinkKey());
+        }
+    }
+}
+

+ 7 - 0
ruoyi-modules/ruoyi-mall/src/main/resources/mapper/mall/MallDiyPcPageMapper.xml

@@ -0,0 +1,7 @@
+<?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="org.dromara.mall.mapper.MallDiyPcPageMapper">
+
+</mapper>

+ 7 - 0
ruoyi-modules/ruoyi-mall/src/main/resources/mapper/mall/MallDiyPcTemplatePageMapper.xml

@@ -0,0 +1,7 @@
+<?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="org.dromara.mall.mapper.MallDiyPcTemplatePageMapper">
+
+</mapper>

+ 121 - 0
ruoyi-modules/ruoyi-mall/src/main/resources/mapper/mall/PageCategoryMapper.xml

@@ -0,0 +1,121 @@
+<?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="org.dromara.mall.mapper.PageCategoryMapper">
+
+    <resultMap type="org.dromara.mall.domain.vo.PageCategoryVo" id="PageCategoryVoResult">
+        <id property="id" column="id"/>
+        <result property="parentId" column="pid"/>
+        <result property="level" column="level"/>
+        <result property="type" column="type"/>
+        <result property="name" column="name"/>
+        <result property="title" column="title"/>
+        <result property="pageKey" column="page_key"/>
+        <result property="sort" column="sort"/>
+        <result property="status" column="status"/>
+        <result property="createTime" column="create_time"/>
+        <result property="isMer" column="is_mer"/>
+        <!-- 支持递归查询子分类 -->
+        <collection property="children" ofType="org.dromara.mall.domain.vo.PageCategoryVo"
+                    select="selectCategoryTreeByParentId" column="id"/>
+    </resultMap>
+
+    <!-- 查询分类树结构(顶级分类) -->
+    <select id="selectCategoryTree" resultMap="PageCategoryVoResult">
+        SELECT *
+        FROM mall_page_category
+        WHERE pid = 0
+          AND status = '0'
+        ORDER BY sort ASC
+    </select>
+
+    <!-- 根据父ID查询子分类树 -->
+    <select id="selectCategoryTreeByParentId" resultMap="PageCategoryVoResult">
+        SELECT *
+        FROM mall_page_category
+        WHERE pid = #{parentId}
+          AND status = '0'
+        ORDER BY sort ASC
+    </select>
+
+    <!-- 根据父ID查询子分类(不限制状态) -->
+    <select id="selectByParentId" resultType="org.dromara.mall.domain.PageCategory">
+        SELECT *
+        FROM mall_page_category
+        WHERE pid = #{parentId}
+        ORDER BY sort ASC
+    </select>
+
+    <!-- 根据分类类型查询分类 -->
+    <select id="selectByType" resultType="org.dromara.mall.domain.PageCategory">
+        SELECT *
+        FROM mall_page_category
+        WHERE type = #{type}
+        ORDER BY sort ASC
+    </select>
+
+    <!-- 批量更新 -->
+    <update id="updateBatch">
+        UPDATE mall_page_category
+        SET
+        name = CASE id
+        <foreach collection="list" item="item" separator="">
+            WHEN #{item.id} THEN #{item.name}
+        </foreach>
+        ELSE name
+        END,
+        title = CASE id
+        <foreach collection="list" item="item" separator="">
+            WHEN #{item.id} THEN #{item.title}
+        </foreach>
+        ELSE title
+        END,
+        sort = CASE id
+        <foreach collection="list" item="item" separator="">
+            WHEN #{item.id} THEN #{item.sort}
+        </foreach>
+        ELSE sort
+        END,
+        status = CASE id
+        <foreach collection="list" item="item" separator="">
+            WHEN #{item.id} THEN #{item.status}
+        </foreach>
+        ELSE status
+        END
+        WHERE id IN
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item.id}
+        </foreach>
+    </update>
+
+    <!-- 查询指定分类的所有子分类ID(递归查询) -->
+    <select id="selectAllChildrenIds" resultType="java.lang.Long">
+        WITH RECURSIVE cte AS (SELECT id
+                               FROM mall_page_category
+                               WHERE pid = #{categoryId}
+                               UNION ALL
+                               SELECT c.id
+                               FROM mall_page_category c
+                                        INNER JOIN cte ON c.pid = cte.id)
+        SELECT id
+        FROM cte
+    </select>
+
+    <!-- 检查分类是否可作为父分类(排除自身及子分类) -->
+    <select id="selectValidParentTree" resultMap="PageCategoryVoResult">
+        WITH RECURSIVE invalid_ids AS (SELECT id
+                                       FROM mall_page_category
+                                       WHERE id = #{excludeId}
+                                       UNION ALL
+                                       SELECT c.id
+                                       FROM mall_page_category c
+                                                INNER JOIN invalid_ids ON c.pid = invalid_ids.id)
+        SELECT *
+        FROM mall_page_category
+        WHERE id NOT IN (SELECT id FROM invalid_ids)
+          AND pid = 0
+          AND status = '0'
+        ORDER BY sort ASC
+    </select>
+</mapper>

+ 88 - 0
ruoyi-modules/ruoyi-mall/src/main/resources/mapper/mall/PageLinkMapper.xml

@@ -0,0 +1,88 @@
+<?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="org.dromara.mall.mapper.PageLinkMapper">
+
+    <resultMap type="org.dromara.mall.domain.vo.PageLinkVo" id="PageLinkVoResult">
+        <id property="id" column="id"/>
+        <result property="cateId" column="cate_id"/>
+        <result property="type" column="type"/>
+        <result property="name" column="name"/>
+        <result property="linkKey" column="link_key"/>
+        <result property="url" column="url"/>
+        <result property="isCustom" column="is_custom"/>
+        <result property="status" column="status"/>
+        <result property="sort" column="sort"/>
+        <result property="createTime" column="create_time"/>
+        <result property="isMer" column="is_mer"/>
+    </resultMap>
+
+    <select id="selectByCategoryId" resultType="org.dromara.mall.domain.PageLink">
+        SELECT *
+        FROM mall_page_link
+        WHERE cate_id = #{categoryId}
+          AND status = 0
+        ORDER BY sort ASC
+    </select>
+
+    <select id="selectByIsCustom" resultType="org.dromara.mall.domain.PageLink">
+        SELECT *
+        FROM mall_page_link
+        WHERE is_custom = #{isCustom}
+          AND status = 0
+        ORDER BY sort ASC
+    </select>
+
+    <select id="selectLinkDetail" resultMap="PageLinkVoResult">
+        SELECT l.*,
+               c.name AS category_name
+        FROM mall_page_link l
+                 LEFT JOIN mall_page_category c ON l.cate_id = c.id
+        WHERE l.id = #{id}
+    </select>
+
+    <select id="selectByKey" resultType="org.dromara.mall.domain.PageLink">
+        SELECT *
+        FROM mall_page_link
+        WHERE link_key = #{key}
+    </select>
+
+    <!-- 查询分类下的所有链接(包含子分类) -->
+    <select id="selectLinksByCategoryIds" resultType="org.dromara.mall.domain.PageLink">
+        SELECT * FROM mall_page_link
+        WHERE cate_id IN
+        <foreach collection="categoryIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+        AND status = 0
+        ORDER BY sort ASC
+    </select>
+
+    <!-- 批量更新链接状态 -->
+    <update id="updateBatchStatus">
+        UPDATE mall_page_link
+        SET status = #{status}
+        WHERE id IN
+        <foreach collection="ids" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+    <!-- 批量更新排序 -->
+    <update id="updateBatchSort">
+        UPDATE mall_page_link
+        SET
+        sort = CASE id
+        <foreach collection="list" item="item" separator="">
+            WHEN #{item.id} THEN #{item.sort}
+        </foreach>
+        ELSE sort
+        END
+        WHERE id IN
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item.id}
+        </foreach>
+    </update>
+</mapper>
+

+ 106 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteChannelPageController.java

@@ -0,0 +1,106 @@
+package org.dromara.product.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.product.domain.vo.ClientSiteChannelPageVo;
+import org.dromara.product.domain.bo.ClientSiteChannelPageBo;
+import org.dromara.product.service.IClientSiteChannelPageService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 频道页面配置
+ * 前端访问路由地址为:/product/siteChannelPage
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/siteChannelPage")
+public class ClientSiteChannelPageController extends BaseController {
+
+    private final IClientSiteChannelPageService clientSiteChannelPageService;
+
+    /**
+     * 查询频道页面配置列表
+     */
+    //@SaCheckPermission("product:siteChannelPage:list")
+    @GetMapping("/list")
+    public TableDataInfo<ClientSiteChannelPageVo> list(ClientSiteChannelPageBo bo, PageQuery pageQuery) {
+        return clientSiteChannelPageService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出频道页面配置列表
+     */
+    //@SaCheckPermission("product:siteChannelPage:export")
+    @Log(title = "频道页面配置", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(ClientSiteChannelPageBo bo, HttpServletResponse response) {
+        List<ClientSiteChannelPageVo> list = clientSiteChannelPageService.queryList(bo);
+        ExcelUtil.exportExcel(list, "频道页面配置", ClientSiteChannelPageVo.class, response);
+    }
+
+    /**
+     * 获取频道页面配置详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("product:siteChannelPage:query")
+    @GetMapping("/{id}")
+    public R<ClientSiteChannelPageVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(clientSiteChannelPageService.queryById(id));
+    }
+
+    /**
+     * 新增频道页面配置
+     */
+    //@SaCheckPermission("product:siteChannelPage:add")
+    @Log(title = "频道页面配置", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody ClientSiteChannelPageBo bo) {
+        return toAjax(clientSiteChannelPageService.insertByBo(bo));
+    }
+
+    /**
+     * 修改频道页面配置
+     */
+    //@SaCheckPermission("product:siteChannelPage:edit")
+    @Log(title = "频道页面配置", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody ClientSiteChannelPageBo bo) {
+        return toAjax(clientSiteChannelPageService.updateByBo(bo));
+    }
+
+    /**
+     * 删除频道页面配置
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("product:siteChannelPage:remove")
+    @Log(title = "频道页面配置", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(clientSiteChannelPageService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 1 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteController.java

@@ -27,7 +27,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
  * 前端访问路由地址为:/product/site
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Validated
 @RequiredArgsConstructor

+ 106 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteFloorController.java

@@ -0,0 +1,106 @@
+package org.dromara.product.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.product.domain.vo.ClientSiteFloorVo;
+import org.dromara.product.domain.bo.ClientSiteFloorBo;
+import org.dromara.product.service.IClientSiteFloorService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 客户站点楼层主
+ * 前端访问路由地址为:/product/siteFloor
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/siteFloor")
+public class ClientSiteFloorController extends BaseController {
+
+    private final IClientSiteFloorService clientSiteFloorService;
+
+    /**
+     * 查询客户站点楼层主列表
+     */
+    //@SaCheckPermission("product:siteFloor:list")
+    @GetMapping("/list")
+    public TableDataInfo<ClientSiteFloorVo> list(ClientSiteFloorBo bo, PageQuery pageQuery) {
+        return clientSiteFloorService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出客户站点楼层主列表
+     */
+    //@SaCheckPermission("product:siteFloor:export")
+    @Log(title = "客户站点楼层主", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(ClientSiteFloorBo bo, HttpServletResponse response) {
+        List<ClientSiteFloorVo> list = clientSiteFloorService.queryList(bo);
+        ExcelUtil.exportExcel(list, "客户站点楼层主", ClientSiteFloorVo.class, response);
+    }
+
+    /**
+     * 获取客户站点楼层主详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("product:siteFloor:query")
+    @GetMapping("/{id}")
+    public R<ClientSiteFloorVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(clientSiteFloorService.queryById(id));
+    }
+
+    /**
+     * 新增客户站点楼层主
+     */
+    //@SaCheckPermission("product:siteFloor:add")
+    @Log(title = "客户站点楼层主", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody ClientSiteFloorBo bo) {
+        return toAjax(clientSiteFloorService.insertByBo(bo));
+    }
+
+    /**
+     * 修改客户站点楼层主
+     */
+    //@SaCheckPermission("product:siteFloor:edit")
+    @Log(title = "客户站点楼层主", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody ClientSiteFloorBo bo) {
+        return toAjax(clientSiteFloorService.updateByBo(bo));
+    }
+
+    /**
+     * 删除客户站点楼层主
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("product:siteFloor:remove")
+    @Log(title = "客户站点楼层主", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(clientSiteFloorService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 106 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteFloorLinkController.java

@@ -0,0 +1,106 @@
+package org.dromara.product.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.product.domain.vo.ClientSiteFloorLinkVo;
+import org.dromara.product.domain.bo.ClientSiteFloorLinkBo;
+import org.dromara.product.service.IClientSiteFloorLinkService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 客户站点楼层关联
+ * 前端访问路由地址为:/product/siteFloorLink
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/siteFloorLink")
+public class ClientSiteFloorLinkController extends BaseController {
+
+    private final IClientSiteFloorLinkService clientSiteFloorLinkService;
+
+    /**
+     * 查询客户站点楼层关联列表
+     */
+    //@SaCheckPermission("product:siteFloorLink:list")
+    @GetMapping("/list")
+    public TableDataInfo<ClientSiteFloorLinkVo> list(ClientSiteFloorLinkBo bo, PageQuery pageQuery) {
+        return clientSiteFloorLinkService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出客户站点楼层关联列表
+     */
+    //@SaCheckPermission("product:siteFloorLink:export")
+    @Log(title = "客户站点楼层关联", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(ClientSiteFloorLinkBo bo, HttpServletResponse response) {
+        List<ClientSiteFloorLinkVo> list = clientSiteFloorLinkService.queryList(bo);
+        ExcelUtil.exportExcel(list, "客户站点楼层关联", ClientSiteFloorLinkVo.class, response);
+    }
+
+    /**
+     * 获取客户站点楼层关联详细信息
+     *
+     * @param id 主键
+     */
+    //@SaCheckPermission("product:siteFloorLink:query")
+    @GetMapping("/{id}")
+    public R<ClientSiteFloorLinkVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(clientSiteFloorLinkService.queryById(id));
+    }
+
+    /**
+     * 新增客户站点楼层关联
+     */
+    //@SaCheckPermission("product:siteFloorLink:add")
+    @Log(title = "客户站点楼层关联", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody ClientSiteFloorLinkBo bo) {
+        return toAjax(clientSiteFloorLinkService.insertByBo(bo));
+    }
+
+    /**
+     * 修改客户站点楼层关联
+     */
+    //@SaCheckPermission("product:siteFloorLink:edit")
+    @Log(title = "客户站点楼层关联", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody ClientSiteFloorLinkBo bo) {
+        return toAjax(clientSiteFloorLinkService.updateByBo(bo));
+    }
+
+    /**
+     * 删除客户站点楼层关联
+     *
+     * @param ids 主键串
+     */
+    //@SaCheckPermission("product:siteFloorLink:remove")
+    @Log(title = "客户站点楼层关联", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(clientSiteFloorLinkService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 141 - 2
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteProductController.java

@@ -1,13 +1,26 @@
 package org.dromara.product.controller;
 
 import java.util.List;
+import java.util.ArrayList;
 
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.easyes.core.biz.EsPageInfo;
+import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
+import org.dromara.product.domain.ClientSiteProduct;
+import org.dromara.product.domain.bo.ProductBaseBo;
+import org.dromara.product.domain.vo.PcProductVo;
+import org.dromara.product.domain.vo.ProductBaseVo;
+import org.dromara.product.esmapper.ProductEsMapper;
+import org.dromara.product.service.IProductBaseService;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.http.MediaType;
+import org.springframework.web.multipart.MultipartFile;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
 import org.dromara.common.log.annotation.Log;
 import org.dromara.common.web.core.BaseController;
@@ -17,9 +30,13 @@ import org.dromara.common.core.validate.AddGroup;
 import org.dromara.common.core.validate.EditGroup;
 import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.excel.core.ExcelResult;
 import org.dromara.product.domain.vo.ClientSiteProductVo;
 import org.dromara.product.domain.bo.ClientSiteProductBo;
 import org.dromara.product.service.IClientSiteProductService;
+import org.dromara.product.domain.vo.ClientSiteProductImportVo;
+import org.dromara.product.domain.vo.ClientSiteProductExportVo;
+import org.dromara.product.listener.ClientSiteProductImportListener;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 
 /**
@@ -27,7 +44,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
  * 前端访问路由地址为:/product/siteProduct
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Validated
 @RequiredArgsConstructor
@@ -37,6 +54,8 @@ public class ClientSiteProductController extends BaseController {
 
     private final IClientSiteProductService clientSiteProductService;
 
+    private final ProductEsMapper productEsMapper;
+
     /**
      * 查询客户站点产品配置列表
      */
@@ -65,7 +84,7 @@ public class ClientSiteProductController extends BaseController {
     //@SaCheckPermission("product:siteProduct:query")
     @GetMapping("/{id}")
     public R<ClientSiteProductVo> getInfo(@NotNull(message = "主键不能为空")
-                                     @PathVariable("id") Long id) {
+                                          @PathVariable("id") Long id) {
         return R.ok(clientSiteProductService.queryById(id));
     }
 
@@ -91,6 +110,21 @@ public class ClientSiteProductController extends BaseController {
         return toAjax(clientSiteProductService.updateByBo(bo));
     }
 
+    /**
+    * 更新协议价
+    * */
+    @Log(title = "客户站点产品配置", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping("/updateAgreementPrice")
+    public R<Void> updateAgreementPrice(@Validated @RequestBody ClientSiteProductBo bo) {
+        clientSiteProductService.update(Wrappers.lambdaUpdate(ClientSiteProduct.class)
+            .set(ClientSiteProduct::getAgreementPrice, bo.getAgreementPrice())
+            .eq(ClientSiteProduct::getSiteId, bo.getSiteId())
+            .eq(ClientSiteProduct::getProductId, bo.getProductId())
+        );
+        return R.ok();
+    }
+
     /**
      * 删除客户站点产品配置
      *
@@ -103,4 +137,109 @@ public class ClientSiteProductController extends BaseController {
                           @PathVariable("ids") Long[] ids) {
         return toAjax(clientSiteProductService.deleteWithValidByIds(List.of(ids), true));
     }
+
+    /**
+    * 获取站点下的产品数据
+    * */
+    @GetMapping("/getSiteProductPage")
+    public TableDataInfo<ProductBaseVo> getSiteProductPage(ProductBaseBo bo, PageQuery pageQuery) {
+        List<ClientSiteProduct> clientSiteProducts = clientSiteProductService.list(Wrappers.lambdaQuery(ClientSiteProduct.class)
+            .eq(ClientSiteProduct::getSiteId, bo.getSiteId()));
+        if (CollUtil.isEmpty(clientSiteProducts)) {
+            return TableDataInfo.build();
+        }
+        EsPageInfo<ProductBaseVo> esPageInfo = productEsMapper.pageQuery(new LambdaEsQueryWrapper<>(ProductBaseVo.class)
+                .in(ProductBaseVo::getId, clientSiteProducts.stream().map(ClientSiteProduct::getProductId).toList())
+            , pageQuery.getPageNum(), pageQuery.getPageSize()
+        );
+        if (CollUtil.isEmpty(esPageInfo.getList())) {
+            return TableDataInfo.build();
+        }
+        esPageInfo.getList().forEach(productBaseVo -> {
+            clientSiteProducts.stream()
+                .filter(clientSiteProduct -> clientSiteProduct.getProductId().equals(productBaseVo.getId()))
+                .findFirst()
+                .ifPresent(clientSiteProduct -> {
+                    productBaseVo.setAgreementPrice(clientSiteProduct.getAgreementPrice());
+                });
+        });
+        TableDataInfo tableDataInfo = new TableDataInfo();
+        tableDataInfo.setRows(esPageInfo.getList());
+        tableDataInfo.setTotal(esPageInfo.getTotal());
+        return tableDataInfo;
+    }
+
+    /**
+     * 导出客户站点产品数据
+     */
+    @Log(title = "客户站点产品配置", businessType = BusinessType.EXPORT)
+    @PostMapping("/exportData")
+    public void exportData(@RequestBody ClientSiteProductBo bo, HttpServletResponse response) {
+        // 站点下的产品
+        List<ClientSiteProduct> clientSiteProducts = clientSiteProductService.list(Wrappers.lambdaQuery(ClientSiteProduct.class)
+            .eq(ClientSiteProduct::getSiteId, bo.getSiteId()));
+
+        if (clientSiteProducts.isEmpty()) {
+            ExcelUtil.exportExcel(new ArrayList<>(), "客户站点产品配置", ClientSiteProductExportVo.class, response);
+            return;
+        }
+
+        // 产品数据
+        List<ProductBaseVo> productBaseVos = productEsMapper.selectList(new LambdaEsQueryWrapper<>(ProductBaseVo.class)
+            .in(ProductBaseVo::getId, clientSiteProducts.stream().map(ClientSiteProduct::getProductId).toList())
+        );
+
+        // 构建导出列表
+        List<ClientSiteProductExportVo> exportList = new ArrayList<>();
+        int serialNumber = 1;
+        for (ClientSiteProduct clientSiteProduct : clientSiteProducts) {
+            ProductBaseVo product = productBaseVos.stream()
+                .filter(p -> p.getId().equals(clientSiteProduct.getProductId()))
+                .findFirst()
+                .orElse(null);
+
+            if (product != null) {
+                ClientSiteProductExportVo exportVo = new ClientSiteProductExportVo();
+                exportVo.setSerialNumber(serialNumber++);
+                exportVo.setProductNo(product.getProductNo());
+                exportVo.setItemName(product.getItemName());
+                exportVo.setCategoryName(product.getCategoryName());
+                exportVo.setBrandName(product.getBrandName());
+                exportVo.setUnitName(product.getUnitName());
+                exportVo.setMarketPrice(product.getMarketPrice());
+                exportVo.setPlatformPrice(product.getMemberPrice());
+                exportVo.setMinSellingPrice(product.getMinSellingPrice());
+                exportVo.setPurchasingPrice(product.getPurchasingPrice());
+                exportVo.setAgreementPrice(clientSiteProduct.getAgreementPrice());
+                exportList.add(exportVo);
+            }
+        }
+
+        ExcelUtil.exportExcel(exportList, "客户站点产品配置", ClientSiteProductExportVo.class, response);
+    }
+
+
+    /**
+     * 导入客户站点产品配置数据
+     *
+     * @param file 导入文件
+     */
+    @Log(title = "客户站点产品配置", businessType = BusinessType.IMPORT)
+    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public R<Void> importData(@RequestPart("file") MultipartFile file) throws Exception {
+        ExcelResult<ClientSiteProductImportVo> result = ExcelUtil.importExcel(
+            file.getInputStream(),
+            ClientSiteProductImportVo.class,
+            new ClientSiteProductImportListener(clientSiteProductService)
+        );
+        return R.ok(result.getAnalysis());
+    }
+
+    /**
+     * 获取导入模板
+     */
+    @PostMapping("/importTemplate")
+    public void importTemplate(HttpServletResponse response) {
+        ExcelUtil.exportExcel(new ArrayList<>(), "客户站点产品配置模板", ClientSiteProductImportVo.class, response);
+    }
 }

+ 1 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ClientSiteSettingController.java

@@ -27,7 +27,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
  * 前端访问路由地址为:/product/siteSetting
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Validated
 @RequiredArgsConstructor

+ 1 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProtocolInfoController.java

@@ -32,7 +32,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
 @Validated
 @RequiredArgsConstructor
 @RestController
-@RequestMapping("/info")
+@RequestMapping("/protocolInfo")
 public class ProtocolInfoController extends BaseController {
 
     private final IProtocolInfoService protocolInfoService;

+ 14 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ProtocolProductsController.java

@@ -36,7 +36,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
 @Validated
 @RequiredArgsConstructor
 @RestController
-@RequestMapping("/products")
+@RequestMapping("/protocolProducts")
 public class ProtocolProductsController extends BaseController {
 
     private final IProtocolProductsService protocolProductsService;
@@ -123,4 +123,17 @@ public class ProtocolProductsController extends BaseController {
                           @PathVariable("ids") Long[] ids) {
         return toAjax(protocolProductsService.deleteWithValidByIds(List.of(ids), true));
     }
+
+    /**
+    * 更改协议价格
+    * */
+    @PutMapping("/updateProtocolPrice")
+    public R<Void> updateProtocolPrice(@Validated @RequestBody ProtocolProductsBo bo) {
+        protocolProductsService.update(Wrappers.lambdaUpdate(ProtocolProducts.class)
+            .set(ProtocolProducts::getAgreementPrice, bo.getAgreementPrice())
+            .eq(ProtocolProducts::getProductId, bo.getProductId())
+            .eq(ProtocolProducts::getProtocolId, bo.getProtocolId())
+        );
+        return R.ok();
+    }
 }

+ 53 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/DiyProductController.java

@@ -1,11 +1,29 @@
 package org.dromara.product.controller.pc;
 
+import cn.hutool.core.lang.tree.Tree;
 import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.product.domain.bo.ProductBaseBo;
+import org.dromara.product.domain.bo.ProductBrandBo;
+import org.dromara.product.domain.bo.ProductCategoryBo;
+import org.dromara.product.domain.vo.PcProductVo;
+import org.dromara.product.domain.vo.ProductBaseVo;
+import org.dromara.product.domain.vo.ProductBrandVo;
+import org.dromara.product.service.IProductBaseService;
+import org.dromara.product.service.IProductBrandService;
+import org.dromara.product.service.IProductCategoryService;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
+
 /**
+ * diy商品相关接口
  * @author
  * @date 2026/1/28 下午3:37
  */
@@ -14,4 +32,39 @@ import org.springframework.web.bind.annotation.RestController;
 @RestController
 @RequestMapping("/diyProduct")
 public class DiyProductController {
+
+    //商品
+    private final IProductBaseService productBaseService;
+    //商品品牌
+    private final IProductBrandService productBrandService;
+    //商品分类
+    private final IProductCategoryService productCategoryService;
+
+    /**
+     * 获取商品列表
+     * @param bo
+     * @param pageQuery
+     * @return
+     */
+    @GetMapping("/getDiyProductPage")
+    public TableDataInfo<ProductBaseVo> getDiyProductList(ProductBaseBo bo, PageQuery pageQuery){
+        return productBaseService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 查询商品分类树
+     * */
+    @GetMapping("/getProductCategoryTree")
+    public R<List<Tree<Long>>> selectCategoryTreeList(ProductCategoryBo bo) {
+        return R.ok(productCategoryService.selectCategoryTreeList(bo));
+    }
+
+    /**
+    * 获取商品品牌列表
+    * */
+    @GetMapping("/getProductBrandPage")
+    public TableDataInfo<ProductBrandVo> getProductBrandPage(ProductBrandBo bo, PageQuery pageQuery) {
+        return productBrandService.queryPageList(bo, pageQuery);
+    }
+
 }

+ 101 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/SiteProductController.java

@@ -0,0 +1,101 @@
+package org.dromara.product.controller.pc;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.product.domain.ClientSite;
+import org.dromara.product.domain.ClientSiteFloor;
+import org.dromara.product.domain.vo.ClientSiteVo;
+import org.dromara.product.domain.vo.PcProductVo;
+import org.dromara.product.domain.vo.ProductBrandVo;
+import org.dromara.product.domain.vo.ProductCategoryVo;
+import org.dromara.product.service.*;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 大客户站点
+ * @author
+ * @date 2026/3/6 下午1:57
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/siteProduct")
+public class SiteProductController {
+    //站点主体
+    private final IClientSiteService clientSiteService;
+    //站点配置
+    private final IClientSiteSettingService clientSiteSettingService;
+    //站点楼层
+    private final IClientSiteFloorService clientSiteFloorService;
+    //商品
+    private final IProductBaseService productBaseService;
+    //商品品牌
+    private final IProductBrandService productBrandService;
+
+    /**
+    * 当前客户下的站点地址
+    * */
+    @GetMapping("/getSiteAddress")
+    public R<ClientSite> getSiteAddress(){
+        ClientSite one = clientSiteService.getOne(Wrappers.lambdaQuery(ClientSite.class)
+            .eq(ClientSite::getClientId, LoginHelper.getLoginUser().getCustomerId())
+            .eq(ClientSite::getStatus, "0")
+            .last("limit 1")
+        );
+        return R.ok(one);
+    }
+
+    /**
+    * 获取站点的商品
+    * */
+    @GetMapping("/getSiteProductList")
+    public R<List<PcProductVo>> getSiteProductList(){
+        ClientSite one = clientSiteService.getOne(Wrappers.lambdaQuery(ClientSite.class)
+            .eq(ClientSite::getClientId, LoginHelper.getLoginUser().getCustomerId())
+            .eq(ClientSite::getStatus, "0")
+            .last("limit 1")
+        );
+        List<PcProductVo> pcProductVos= productBaseService.getSiteProductList(one.getId());
+        return R.ok(pcProductVos);
+    }
+
+    /**
+    * 获取站点楼层
+    * */
+    @GetMapping("/getSiteFloorList")
+    public R<List<ClientSiteFloor>> getSiteFloorList(){
+        ClientSite one = clientSiteService.getOne(Wrappers.lambdaQuery(ClientSite.class)
+            .eq(ClientSite::getClientId, LoginHelper.getLoginUser().getCustomerId())
+            .eq(ClientSite::getStatus, "0")
+            .last("limit 1")
+        );
+        List<ClientSiteFloor> clientSiteFloors = clientSiteFloorService.list(Wrappers.lambdaQuery(ClientSiteFloor.class)
+            .eq(ClientSiteFloor::getSiteId, one.getId())
+            .eq(ClientSiteFloor::getIsShow, "1")
+        );
+        return R.ok(clientSiteFloors);
+    }
+
+    /**
+    * 获取站点楼层的商品
+    * */
+    @GetMapping("/getSiteFloorProductList")
+    public R<List<PcProductVo>> getSiteFloorProductList(Long floorId){
+        return R.ok(productBaseService.getSiteFloorProductList(floorId));
+    }
+
+    /**
+    * 获取站点楼层的品牌
+    * */
+    @GetMapping("/getSiteFloorBrandList")
+    public R<List<ProductBrandVo>> getSiteFloorBrandList(Long floorId){
+        return R.ok(productBrandService.getSiteFloorBrandList(floorId));
+    }
+}

+ 6 - 3
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSite.java

@@ -13,7 +13,7 @@ import java.io.Serial;
  * 客户站点配置对象 client_site
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -117,7 +117,7 @@ public class ClientSite extends TenantEntity {
     /**
      * 协议文件路径
      */
-    private String protocolfile;
+    private String protocolFile;
 
     /**
      * 状态(0正常 1停用)
@@ -127,8 +127,11 @@ public class ClientSite extends TenantEntity {
     /**
      * 删除标志(0代表存在 2代表删除)
      */
-    @TableLogic
     private String delFlag;
 
+    /**
+     * 是否diy (0不diy,1diy)
+     * */
+    private Long isDiy;
 
 }

+ 76 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteChannelPage.java

@@ -0,0 +1,76 @@
+package org.dromara.product.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 频道页面配置对象 client_site_channel_page
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("client_site_channel_page")
+public class ClientSiteChannelPage extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID,自增
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    private String clientNo;
+
+    /**
+     * 客户id
+     */
+    private Long clientId;
+
+    /**
+     * 简称
+     */
+    private String shortName;
+
+    /**
+     * 频道名称
+     */
+    private String channelName;
+
+    /**
+     * 频道类型
+     */
+    private String channelType;
+
+    /**
+     * 排序序号
+     */
+    private Long serial;
+
+    /**
+     * 副标题
+     */
+    private String subTitle;
+
+    /**
+     * 图片路径或URL
+     */
+    private String pic;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+
+}

+ 87 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteFloor.java

@@ -0,0 +1,87 @@
+package org.dromara.product.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 客户站点楼层主对象 client_site_floor
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("client_site_floor")
+public class ClientSiteFloor extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 站点id
+     */
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    private Long customerId;
+
+    /**
+     * 楼层名称
+     */
+    private String name;
+
+    /**
+     * 楼层图片
+     */
+    private String imageUrl;
+
+    /**
+     * 链接地址
+     */
+    private String link;
+
+    /**
+     * 品牌编号集合
+     */
+    private String brandNos;
+
+    /**
+     * 排序
+     */
+    private Long sort;
+
+    /**
+     * 是否显示(0否 1是)
+     */
+    private String isShow;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 82 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteFloorLink.java

@@ -0,0 +1,82 @@
+package org.dromara.product.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 客户站点楼层关联对象 client_site_floor_link
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("client_site_floor_link")
+public class ClientSiteFloorLink extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 楼层ID
+     */
+    private Long floorId;
+
+    /**
+     * 类型(1商品 2品牌)
+     */
+    private Long type;
+
+    /**
+     * 商品编号
+     */
+    private String productNo;
+
+    /**
+     * 品牌编号
+     */
+    private String brandNo;
+
+    /**
+     * 商品ID
+     */
+    private Long productId;
+
+    /**
+     * 品牌id
+     */
+    private Long brandId;
+
+    /**
+     * 排序
+     */
+    private Long sort;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 8 - 2
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteProduct.java

@@ -6,12 +6,13 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 
 import java.io.Serial;
+import java.math.BigDecimal;
 
 /**
  * 客户站点产品配置对象 client_site_product
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -27,6 +28,11 @@ public class ClientSiteProduct extends TenantEntity {
     @TableId(value = "id")
     private Long id;
 
+    /**
+     * 站点Id
+     */
+    private Long siteId;
+
     /**
      * 客户编号
      */
@@ -75,7 +81,7 @@ public class ClientSiteProduct extends TenantEntity {
     /**
      * 协议价格(字符串形式,可能含格式或货币符号)
      */
-    private String agreementprice;
+    private BigDecimal agreementPrice;
 
     /**
      * 状态(0正常 1停用)

+ 5 - 10
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ClientSiteSetting.java

@@ -1,6 +1,6 @@
 package org.dromara.product.domain;
 
-import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.tenant.core.TenantEntity;
 import com.baomidou.mybatisplus.annotation.*;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -15,12 +15,12 @@ import java.io.Serial;
  * 客户站点设置对象 client_site_setting
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
 @TableName("client_site_setting")
-public class ClientSiteSetting extends BaseEntity {
+public class ClientSiteSetting extends TenantEntity {
 
     @Serial
     private static final long serialVersionUID = 1L;
@@ -117,14 +117,9 @@ public class ClientSiteSetting extends BaseEntity {
     private String announcementContent;
 
     /**
-     * 创建时间
+     * 状态(0正常 1停用)
      */
-    private Date created;
-
-    /**
-     * 最后修改时间
-     */
-    private Date modify;
+    private String status;
 
 
 }

+ 1 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProtocolInfo.java

@@ -122,7 +122,7 @@ public class ProtocolInfo extends TenantEntity {
     /**
      * 删除标志(0代表存在 2代表删除)
      */
-    @TableLogic
+//    @TableLogic
     private String delFlag;
 
 

+ 0 - 1
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProtocolProducts.java

@@ -83,7 +83,6 @@ public class ProtocolProducts extends TenantEntity {
     /**
      * 删除标志(0代表存在 2代表删除)
      */
-    @TableLogic
     private String delFlag;
 
 

+ 7 - 2
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteBo.java

@@ -15,7 +15,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
  * 客户站点配置业务对象 client_site
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -131,7 +131,7 @@ public class ClientSiteBo extends BaseEntity {
      * 协议文件路径
      */
     //@NotBlank(message = "协议文件路径不能为空", groups = { AddGroup.class, EditGroup.class })
-    private String protocolfile;
+    private String protocolFile;
 
     /**
      * 状态(0正常 1停用)
@@ -139,5 +139,10 @@ public class ClientSiteBo extends BaseEntity {
     //@NotBlank(message = "状态(0正常 1停用)不能为空", groups = { AddGroup.class, EditGroup.class })
     private String status;
 
+    /**
+    * 是否diy (0不diy,1diy)
+    * */
+    private Long isDiy;
+
 
 }

+ 82 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteChannelPageBo.java

@@ -0,0 +1,82 @@
+package org.dromara.product.domain.bo;
+
+import org.dromara.product.domain.ClientSiteChannelPage;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 频道页面配置业务对象 client_site_channel_page
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = ClientSiteChannelPage.class, reverseConvertGenerate = false)
+public class ClientSiteChannelPageBo extends BaseEntity {
+
+    /**
+     * 主键ID,自增
+     */
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    //@NotBlank(message = "客户编号不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String clientNo;
+
+    /**
+     * 客户id
+     */
+    //@NotNull(message = "客户id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long clientId;
+
+    /**
+     * 简称
+     */
+    //@NotBlank(message = "简称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String shortName;
+
+    /**
+     * 频道名称
+     */
+    //@NotBlank(message = "频道名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String channelName;
+
+    /**
+     * 频道类型
+     */
+    //@NotBlank(message = "频道类型不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String channelType;
+
+    /**
+     * 排序序号
+     */
+    private Long serial;
+
+    /**
+     * 副标题
+     */
+    //@NotBlank(message = "副标题不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String subTitle;
+
+    /**
+     * 图片路径或URL
+     */
+    //@NotBlank(message = "图片路径或URL不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String pic;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    //@NotBlank(message = "状态(0正常 1停用)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String status;
+
+
+}

+ 89 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteFloorBo.java

@@ -0,0 +1,89 @@
+package org.dromara.product.domain.bo;
+
+import org.dromara.product.domain.ClientSiteFloor;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 客户站点楼层主业务对象 client_site_floor
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = ClientSiteFloor.class, reverseConvertGenerate = false)
+public class ClientSiteFloorBo extends BaseEntity {
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 站点id
+     */
+    //@NotNull(message = "站点id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    //@NotNull(message = "客户id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long customerId;
+
+    /**
+     * 楼层名称
+     */
+    //@NotBlank(message = "楼层名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String name;
+
+    /**
+     * 楼层图片
+     */
+    //@NotBlank(message = "楼层图片不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String imageUrl;
+
+    /**
+     * 链接地址
+     */
+    //@NotBlank(message = "链接地址不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String link;
+
+    /**
+     * 品牌编号集合
+     */
+    //@NotBlank(message = "品牌编号集合不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String brandNos;
+
+    /**
+     * 排序
+     */
+    //@NotNull(message = "排序不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long sort;
+
+    /**
+     * 是否显示(0否 1是)
+     */
+    //@NotBlank(message = "是否显示(0否 1是)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String isShow;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    //@NotBlank(message = "状态(0正常 1停用)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String status;
+
+    /**
+     * 备注
+     */
+    //@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String remark;
+
+
+}

+ 82 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteFloorLinkBo.java

@@ -0,0 +1,82 @@
+package org.dromara.product.domain.bo;
+
+import org.dromara.product.domain.ClientSiteFloorLink;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 客户站点楼层关联业务对象 client_site_floor_link
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = ClientSiteFloorLink.class, reverseConvertGenerate = false)
+public class ClientSiteFloorLinkBo extends BaseEntity {
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 楼层ID
+     */
+    private Long floorId;
+
+    /**
+     * 类型(1商品 2品牌)
+     */
+    //@NotNull(message = "类型(1商品 2品牌)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long type;
+
+    /**
+     * 商品编号
+     */
+    //@NotBlank(message = "商品编号不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String productNo;
+
+    /**
+     * 品牌编号
+     */
+    //@NotBlank(message = "品牌编号不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String brandNo;
+
+    /**
+     * 商品ID
+     */
+    //@NotNull(message = "商品ID不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long productId;
+
+    /**
+     * 品牌id
+     */
+    //@NotNull(message = "品牌id不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long brandId;
+
+    /**
+     * 排序
+     */
+    //@NotNull(message = "排序不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long sort;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    //@NotBlank(message = "状态(0正常 1停用)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String status;
+
+    /**
+     * 备注
+     */
+    //@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String remark;
+
+
+}

+ 9 - 2
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteProductBo.java

@@ -9,11 +9,13 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import jakarta.validation.constraints.*;
 
+import java.math.BigDecimal;
+
 /**
  * 客户站点产品配置业务对象 client_site_product
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -25,6 +27,11 @@ public class ClientSiteProductBo extends BaseEntity {
      */
     private Long id;
 
+    /**
+     * 站点Id
+     */
+    private Long siteId;
+
     /**
      * 客户编号
      */
@@ -78,7 +85,7 @@ public class ClientSiteProductBo extends BaseEntity {
      * 协议价格(字符串形式,可能含格式或货币符号)
      */
     //@NotBlank(message = "协议价格(字符串形式,可能含格式或货币符号)不能为空", groups = { AddGroup.class, EditGroup.class })
-    private String agreementprice;
+    private BigDecimal agreementPrice;
 
     /**
      * 状态(0正常 1停用)

+ 4 - 9
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ClientSiteSettingBo.java

@@ -17,7 +17,7 @@ import org.dromara.common.translation.constant.TransConstant;
  * 客户站点设置业务对象 client_site_setting
  *
  * @author LionLi
- * @date 2026-03-04
+ * @date 2026-03-06
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -128,15 +128,10 @@ public class ClientSiteSettingBo extends BaseEntity {
     private String announcementContent;
 
     /**
-     * 创建时间
+     * 状态(0正常 1停用)
      */
-    private Date created;
-
-    /**
-     * 最后修改时间
-     */
-    //@NotNull(message = "最后修改时间不能为空", groups = { AddGroup.class, EditGroup.class })
-    private Date modify;
+    //@NotBlank(message = "状态(0正常 1停用)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String status;
 
 
 }

+ 5 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductBaseBo.java

@@ -348,5 +348,10 @@ public class ProductBaseBo extends BaseEntity {
      * */
     private String imageUrl;
 
+    /**
+    * 站点id
+    * */
+    private Long siteId;
+
 
 }

+ 3 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ServiceCaseBo.java

@@ -25,6 +25,9 @@ public class ServiceCaseBo extends BaseEntity {
      */
     private Long id;
 
+    private String ids;
+
+
     /**
      * 案例编号
      */

+ 93 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteChannelPageVo.java

@@ -0,0 +1,93 @@
+package org.dromara.product.domain.vo;
+
+import org.dromara.product.domain.ClientSiteChannelPage;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * 频道页面配置视图对象 client_site_channel_page
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = ClientSiteChannelPage.class)
+public class ClientSiteChannelPageVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID,自增
+     */
+    @ExcelProperty(value = "主键ID,自增")
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    @ExcelProperty(value = "客户编号")
+    private String clientNo;
+
+    /**
+     * 客户id
+     */
+    @ExcelProperty(value = "客户id")
+    private Long clientId;
+
+    /**
+     * 简称
+     */
+    @ExcelProperty(value = "简称")
+    private String shortName;
+
+    /**
+     * 频道名称
+     */
+    @ExcelProperty(value = "频道名称")
+    private String channelName;
+
+    /**
+     * 频道类型
+     */
+    @ExcelProperty(value = "频道类型")
+    private String channelType;
+
+    /**
+     * 排序序号
+     */
+    @ExcelProperty(value = "排序序号")
+    private Long serial;
+
+    /**
+     * 副标题
+     */
+    @ExcelProperty(value = "副标题")
+    private String subTitle;
+
+    /**
+     * 图片路径或URL
+     */
+    @ExcelProperty(value = "图片路径或URL")
+    private String pic;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+
+}

+ 94 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteFloorLinkVo.java

@@ -0,0 +1,94 @@
+package org.dromara.product.domain.vo;
+
+import org.dromara.product.domain.ClientSiteFloorLink;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * 客户站点楼层关联视图对象 client_site_floor_link
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = ClientSiteFloorLink.class)
+public class ClientSiteFloorLinkVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 楼层ID
+     */
+    @ExcelProperty(value = "楼层ID")
+    private Long floorId;
+
+    /**
+     * 类型(1商品 2品牌)
+     */
+    @ExcelProperty(value = "类型", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "1=商品,2=品牌")
+    private Long type;
+
+    /**
+     * 商品编号
+     */
+    @ExcelProperty(value = "商品编号")
+    private String productNo;
+
+    /**
+     * 品牌编号
+     */
+    @ExcelProperty(value = "品牌编号")
+    private String brandNo;
+
+    /**
+     * 商品ID
+     */
+    @ExcelProperty(value = "商品ID")
+    private Long productId;
+
+    /**
+     * 品牌id
+     */
+    @ExcelProperty(value = "品牌id")
+    private Long brandId;
+
+    /**
+     * 排序
+     */
+    @ExcelProperty(value = "排序")
+    private Long sort;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 106 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteFloorVo.java

@@ -0,0 +1,106 @@
+package org.dromara.product.domain.vo;
+
+import org.dromara.product.domain.ClientSiteFloor;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * 客户站点楼层主视图对象 client_site_floor
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = ClientSiteFloor.class)
+public class ClientSiteFloorVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 站点id
+     */
+    @ExcelProperty(value = "站点id")
+    private Long siteId;
+
+    /**
+     * 客户id
+     */
+    @ExcelProperty(value = "客户id")
+    private Long customerId;
+
+    /**
+     * 楼层名称
+     */
+    @ExcelProperty(value = "楼层名称")
+    private String name;
+
+    /**
+     * 楼层图片
+     */
+    @ExcelProperty(value = "楼层图片")
+    private String imageUrl;
+
+    /**
+     * 链接地址
+     */
+    @ExcelProperty(value = "链接地址")
+    private String link;
+
+    /**
+     * 品牌编号集合
+     */
+    @ExcelProperty(value = "品牌编号集合")
+    private String brandNos;
+
+    /**
+     * 排序
+     */
+    @ExcelProperty(value = "排序")
+    private Long sort;
+
+    /**
+     * 是否显示(0否 1是)
+     */
+    @ExcelProperty(value = "是否显示", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=否,1=是")
+    private String isShow;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+    * 更新时间
+    * */
+    @ExcelProperty(value = "更新时间")
+    private Date updateTime;
+
+
+}

+ 89 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteProductExportVo.java

@@ -0,0 +1,89 @@
+package org.dromara.product.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 客户站点产品导出对象
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class ClientSiteProductExportVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 序号
+     */
+    @ExcelProperty(value = "序号", index = 0)
+    private Integer serialNumber;
+
+    /**
+     * 商品编号
+     */
+    @ExcelProperty(value = "商品编号", index = 1)
+    private String productNo;
+
+    /**
+     * 商品名称
+     */
+    @ExcelProperty(value = "商品名称", index = 2)
+    private String itemName;
+
+    /**
+     * 商品类别
+     */
+    @ExcelProperty(value = "商品类别", index = 3)
+    private String categoryName;
+
+    /**
+     * 商品品牌
+     */
+    @ExcelProperty(value = "商品品牌", index = 4)
+    private String brandName;
+
+    /**
+     * 商品单位
+     */
+    @ExcelProperty(value = "商品单位", index = 5)
+    private String unitName;
+
+    /**
+     * 市场价
+     */
+    @ExcelProperty(value = "市场价", index = 6)
+    private BigDecimal marketPrice;
+
+    /**
+     * 平台售价
+     */
+    @ExcelProperty(value = "平台售价", index = 7)
+    private BigDecimal platformPrice;
+
+    /**
+     * 最低售价
+     */
+    @ExcelProperty(value = "最低售价", index = 8)
+    private BigDecimal minSellingPrice;
+
+    /**
+     * 采购价
+     */
+    @ExcelProperty(value = "采购价", index = 9)
+    private BigDecimal purchasingPrice;
+
+    /**
+     * 协议价
+     */
+    @ExcelProperty(value = "协议价", index = 10)
+    private BigDecimal agreementPrice;
+}

+ 33 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ClientSiteProductImportVo.java

@@ -0,0 +1,33 @@
+package org.dromara.product.domain.vo;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 客户站点产品配置导入 VO
+ *
+ * @author LionLi
+ * @date 2026-03-06
+ */
+@Data
+public class ClientSiteProductImportVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 商品编号
+     */
+    @ExcelProperty(value = "商品编号")
+    private String productNo;
+
+    /**
+     * 协议价格
+     */
+    @ExcelProperty(value = "协议价")
+    private BigDecimal agreementPrice;
+}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.