Explorar el Código

feat(customer): 完善客户联系人及客户信息功能

- 在 CustomerContact 类中新增自定义登录名、部门ID、邮箱和生日字段
- 更新 CustomerContactBo 和 CustomerContactVo 类以支持新字段
- 修改 CustomerInfo 类中的客户名称字段为 customerName 并调整相关类型
- 在 CustomerInfoBo 中同步更新字段定义和验证规则
- 新增 ContractVo 类用于合同管理视图展示
- 在 CustomerInfoController 中增加合同列表查询、状态修改、临时额度更新和设置客户标签接口
- 实现 CustomerInfoServiceImpl 中的合同分页查询、状态更新、信用额度更新和标签设置逻辑
- 调整 MaintainInfoServiceImpl 中的服务内容显示与维保次数统计逻辑
- 添加对新表 maintenance_server_item 等的忽略租户过滤配置
- 引入 spring-data-redis 依赖支持 Redis 功能
- 将 ruoyi-customer 模块加入 modules pom.xml 中进行构建管理
hurx hace 1 semana
padre
commit
80dcf83491
Se han modificado 71 ficheros con 4343 adiciones y 57 borrados
  1. 4 0
      ruoyi-common/ruoyi-common-core/pom.xml
  2. 25 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/IsDefault.java
  3. 5 1
      ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlatformDataScopeInterceptor.java
  4. 5 1
      ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlatformTenantLineHandler.java
  5. 1 0
      ruoyi-modules/pom.xml
  6. 106 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerContractController.java
  7. 42 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerInfoController.java
  8. 118 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerShippingAddressController.java
  9. 105 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/MaintenanceServerItemController.java
  10. 105 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/MaintenanceServerTimeController.java
  11. 105 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/MaintenanceTypeController.java
  12. 112 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/PurchaseHabitController.java
  13. 10 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerContact.java
  14. 123 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerContract.java
  15. 8 3
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerInfo.java
  16. 42 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerInfoTag.java
  17. 127 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerShippingAddress.java
  18. 52 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/MaintenanceServerItem.java
  19. 52 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/MaintenanceServerTime.java
  20. 52 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/MaintenanceType.java
  21. 113 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/PurchaseHabit.java
  22. 13 3
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContactBo.java
  23. 121 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContractBo.java
  24. 16 14
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerInfoBo.java
  25. 7 4
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerSalesInfoBo.java
  26. 120 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerShippingAddressBo.java
  27. 45 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/MaintenanceServerItemBo.java
  28. 45 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/MaintenanceServerTimeBo.java
  29. 45 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/MaintenanceTypeBo.java
  30. 108 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/PurchaseHabitBo.java
  31. 13 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/dto/SetCustomerInfoTagDto.java
  32. 41 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/ContractVo.java
  33. 9 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContactVo.java
  34. 140 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContractVo.java
  35. 10 3
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerInfoVo.java
  36. 149 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerShippingAddressVo.java
  37. 2 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/MaintainInfoVo.java
  38. 57 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/MaintenanceServerItemVo.java
  39. 57 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/MaintenanceServerTimeVo.java
  40. 57 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/MaintenanceTypeVo.java
  41. 139 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/PurchaseHabitVo.java
  42. 21 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerContractMapper.java
  43. 16 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerInfoTagMapper.java
  44. 22 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerShippingAddressMapper.java
  45. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/MaintenanceServerItemMapper.java
  46. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/MaintenanceServerTimeMapper.java
  47. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/MaintenanceTypeMapper.java
  48. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/PurchaseHabitMapper.java
  49. 70 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerContractService.java
  50. 24 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerInfoService.java
  51. 75 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerShippingAddressService.java
  52. 70 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IMaintenanceServerItemService.java
  53. 70 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IMaintenanceServerTimeService.java
  54. 70 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IMaintenanceTypeService.java
  55. 72 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IPurchaseHabitService.java
  56. 34 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/RedisContractNoGenerator.java
  57. 152 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerContractServiceImpl.java
  58. 220 20
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java
  59. 189 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerShippingAddressServiceImpl.java
  60. 45 6
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/MaintainInfoServiceImpl.java
  61. 135 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/MaintenanceServerItemServiceImpl.java
  62. 135 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/MaintenanceServerTimeServiceImpl.java
  63. 135 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/MaintenanceTypeServiceImpl.java
  64. 154 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/PurchaseHabitServiceImpl.java
  65. 21 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CustomerContractMapper.xml
  66. 12 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CustomerInfoTagMapper.xml
  67. 7 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CustomerShippingAddressMapper.xml
  68. 7 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/MaintenanceServerItemMapper.xml
  69. 7 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/MaintenanceServerTimeMapper.xml
  70. 7 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/MaintenanceTypeMapper.xml
  71. 7 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/PurchaseHabitMapper.xml

+ 4 - 0
ruoyi-common/ruoyi-common-core/pom.xml

@@ -98,6 +98,10 @@
             <groupId>org.lionsoul</groupId>
             <artifactId>ip2region</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-redis</artifactId>
+        </dependency>
 
     </dependencies>
 

+ 25 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/IsDefault.java

@@ -0,0 +1,25 @@
+package org.dromara.common.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum IsDefault {
+
+    /**
+     * 默认
+     */
+    Yes("0", "是"),
+
+    /**
+     * 否
+     */
+    No("1", "否");
+
+
+
+    private final String code;
+    private final String info;
+
+}

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

@@ -49,7 +49,11 @@ public class PlatformDataScopeInterceptor implements Interceptor {
         "sys_oss_config",
         "sys_oper_log",
         "sys_logininfor",
-        "qrtz_"
+        "qrtz_",
+        "maintenance_server_item",
+        "maintenance_server_time",
+        "maintenance_type",
+        "customer_info_tag"
         // 注意:前缀匹配需特殊处理(如 qrtz_),见 isIgnoreTable 方法
     ));
 

+ 5 - 1
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlatformTenantLineHandler.java

@@ -35,7 +35,11 @@ public class PlatformTenantLineHandler implements TenantLineHandler {
         "sys_oss_config",
         "gen_table",
         "gen_table_column",
-        "qrtz_"
+        "qrtz_",
+        "maintenance_server_item",
+        "maintenance_server_time",
+        "maintenance_type"
+
         // 注意:前缀匹配(如 qrtz_)在 ignoreTable 中单独处理
     ));
 

+ 1 - 0
ruoyi-modules/pom.xml

@@ -16,6 +16,7 @@
         <module>ruoyi-workflow</module>
         <module>ruoyi-order</module>
 	    <module>ruoyi-product</module>
+        <module>ruoyi-customer</module>
     </modules>
 
     <artifactId>ruoyi-modules</artifactId>

+ 106 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerContractController.java

@@ -0,0 +1,106 @@
+package org.dromara.customer.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.customer.domain.vo.CustomerContractVo;
+import org.dromara.customer.domain.bo.CustomerContractBo;
+import org.dromara.customer.service.ICustomerContractService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 客户合同
+ * 前端访问路由地址为:/customer/contract
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/contract")
+public class CustomerContractController extends BaseController {
+
+    private final ICustomerContractService customerContractService;
+
+    /**
+     * 查询客户合同列表
+     */
+    @SaCheckPermission("customer:contract:list")
+    @GetMapping("/list")
+    public TableDataInfo<CustomerContractVo> list(CustomerContractBo bo, PageQuery pageQuery) {
+        return customerContractService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出客户合同列表
+     */
+    @SaCheckPermission("customer:contract:export")
+    @Log(title = "客户合同", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(CustomerContractBo bo, HttpServletResponse response) {
+        List<CustomerContractVo> list = customerContractService.queryList(bo);
+        ExcelUtil.exportExcel(list, "客户合同", CustomerContractVo.class, response);
+    }
+
+    /**
+     * 获取客户合同详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("customer:contract:query")
+    @GetMapping("/{id}")
+    public R<CustomerContractVo> getInfo(@NotNull(message = "主键不能为空")
+                                         @PathVariable("id") Long id) {
+        return R.ok(customerContractService.queryById(id));
+    }
+
+    /**
+     * 新增客户合同
+     */
+    @SaCheckPermission("customer:contract:add")
+    @Log(title = "客户合同", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody CustomerContractBo bo) {
+        return toAjax(customerContractService.insertByBo(bo));
+    }
+
+    /**
+     * 修改客户合同
+     */
+    @SaCheckPermission("customer:contract:edit")
+    @Log(title = "客户合同", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody CustomerContractBo bo) {
+        return toAjax(customerContractService.updateByBo(bo));
+    }
+
+    /**
+     * 删除客户合同
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("customer:contract:remove")
+    @Log(title = "客户合同", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(customerContractService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 42 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerInfoController.java

@@ -6,6 +6,10 @@ import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.customer.domain.CustomerSalesInfo;
+import org.dromara.customer.domain.bo.CustomerSalesInfoBo;
+import org.dromara.customer.domain.dto.SetCustomerInfoTagDto;
+import org.dromara.customer.domain.vo.ContractVo;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -46,6 +50,14 @@ public class CustomerInfoController extends BaseController {
         return customerInfoService.queryPageList(bo, pageQuery);
     }
 
+    /**
+     * 查询合同管理
+     */
+    @GetMapping("/contractList")
+    public TableDataInfo<ContractVo> contractList(CustomerInfoBo bo, PageQuery pageQuery) {
+        return customerInfoService.queryContractPageList(bo, pageQuery);
+    }
+
     /**
      * 导出客户信息列表
      */
@@ -103,4 +115,34 @@ public class CustomerInfoController extends BaseController {
                           @PathVariable("ids") Long[] ids) {
         return toAjax(customerInfoService.deleteWithValidByIds(List.of(ids), true));
     }
+
+    /**
+     * 状态修改
+     */
+    @SaCheckPermission("customer:customerInfo:edit")
+    @Log(title = "客户信息", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestBody CustomerInfoBo bo) {
+        return toAjax(customerInfoService.updateCustomerCheckStatus(bo.getId(), bo.getStatus()));
+    }
+
+    /**
+     * 状态临时额度
+     */
+    @SaCheckPermission("customer:customerInfo:edit")
+    @Log(title = "客户信息", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateCreditAmount")
+    public R<Void> updateCreditAmount(@RequestBody CustomerSalesInfoBo bo) {
+        return toAjax(customerInfoService.updateCreditAmount(bo.getCustomerIds(), bo.getCreditAmount()));
+    }
+
+    /**
+     * 设置客户标签
+     */
+    @SaCheckPermission("customer:customerInfo:edit")
+    @Log(title = "客户信息", businessType = BusinessType.UPDATE)
+    @PutMapping("/setCustomerInfoTag")
+    public R<Void> setCustomerInfoTag(@RequestBody SetCustomerInfoTagDto bo) {
+        return toAjax(customerInfoService.setCustomerInfoTag(bo.getCustomerIds(), bo.getTagIds()));
+    }
 }

+ 118 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerShippingAddressController.java

@@ -0,0 +1,118 @@
+package org.dromara.customer.controller;
+
+import java.util.List;
+
+import cn.dev33.satoken.annotation.SaCheckRole;
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.common.core.constant.TenantConstants;
+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.customer.domain.vo.CustomerShippingAddressVo;
+import org.dromara.customer.domain.bo.CustomerShippingAddressBo;
+import org.dromara.customer.service.ICustomerShippingAddressService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 客户收货地址
+ * 前端访问路由地址为:/customer/shippingAddress
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/shippingAddress")
+public class CustomerShippingAddressController extends BaseController {
+
+    private final ICustomerShippingAddressService customerShippingAddressService;
+
+    /**
+     * 查询客户收货地址列表
+     */
+    @SaCheckPermission("customer:shippingAddress:list")
+    @GetMapping("/list")
+    public TableDataInfo<CustomerShippingAddressVo> list(CustomerShippingAddressBo bo, PageQuery pageQuery) {
+        return customerShippingAddressService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出客户收货地址列表
+     */
+    @SaCheckPermission("customer:shippingAddress:export")
+    @Log(title = "客户收货地址", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(CustomerShippingAddressBo bo, HttpServletResponse response) {
+        List<CustomerShippingAddressVo> list = customerShippingAddressService.queryList(bo);
+        ExcelUtil.exportExcel(list, "客户收货地址", CustomerShippingAddressVo.class, response);
+    }
+
+    /**
+     * 获取客户收货地址详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("customer:shippingAddress:query")
+    @GetMapping("/{id}")
+    public R<CustomerShippingAddressVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(customerShippingAddressService.queryById(id));
+    }
+
+    /**
+     * 新增客户收货地址
+     */
+    @SaCheckPermission("customer:shippingAddress:add")
+    @Log(title = "客户收货地址", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody CustomerShippingAddressBo bo) {
+        return toAjax(customerShippingAddressService.insertByBo(bo));
+    }
+
+    /**
+     * 修改客户收货地址
+     */
+    @SaCheckPermission("customer:shippingAddress:edit")
+    @Log(title = "客户收货地址", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody CustomerShippingAddressBo bo) {
+        return toAjax(customerShippingAddressService.updateByBo(bo));
+    }
+
+    /**
+     * 删除客户收货地址
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("customer:shippingAddress:remove")
+    @Log(title = "客户收货地址", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(customerShippingAddressService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    /**
+     * 设置默认地址
+     */
+    @SaCheckPermission("customer:shippingAddress:edit")
+    @Log(title = "客户收货地址", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeDefaultAddress")
+    public R<Void> changeDefaultAddress(@RequestBody CustomerShippingAddressBo bo) {
+        return toAjax(customerShippingAddressService.changeDefaultAddress(bo));
+    }
+}

+ 105 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/MaintenanceServerItemController.java

@@ -0,0 +1,105 @@
+package org.dromara.customer.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.customer.domain.vo.MaintenanceServerItemVo;
+import org.dromara.customer.domain.bo.MaintenanceServerItemBo;
+import org.dromara.customer.service.IMaintenanceServerItemService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 维保服务内容
+ * 前端访问路由地址为:/customer/serverItem
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/serverItem")
+public class MaintenanceServerItemController extends BaseController {
+
+    private final IMaintenanceServerItemService maintenanceServerItemService;
+
+    /**
+     * 查询维保服务内容列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<MaintenanceServerItemVo> list(MaintenanceServerItemBo bo, PageQuery pageQuery) {
+        return maintenanceServerItemService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出维保服务内容列表
+     */
+    @SaCheckPermission("customer:serverItem:export")
+    @Log(title = "维保服务内容", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(MaintenanceServerItemBo bo, HttpServletResponse response) {
+        List<MaintenanceServerItemVo> list = maintenanceServerItemService.queryList(bo);
+        ExcelUtil.exportExcel(list, "维保服务内容", MaintenanceServerItemVo.class, response);
+    }
+
+    /**
+     * 获取维保服务内容详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("customer:serverItem:query")
+    @GetMapping("/{id}")
+    public R<MaintenanceServerItemVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(maintenanceServerItemService.queryById(id));
+    }
+
+    /**
+     * 新增维保服务内容
+     */
+    @SaCheckPermission("customer:serverItem:add")
+    @Log(title = "维保服务内容", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody MaintenanceServerItemBo bo) {
+        return toAjax(maintenanceServerItemService.insertByBo(bo));
+    }
+
+    /**
+     * 修改维保服务内容
+     */
+    @SaCheckPermission("customer:serverItem:edit")
+    @Log(title = "维保服务内容", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody MaintenanceServerItemBo bo) {
+        return toAjax(maintenanceServerItemService.updateByBo(bo));
+    }
+
+    /**
+     * 删除维保服务内容
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("customer:serverItem:remove")
+    @Log(title = "维保服务内容", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(maintenanceServerItemService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 105 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/MaintenanceServerTimeController.java

@@ -0,0 +1,105 @@
+package org.dromara.customer.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.customer.domain.vo.MaintenanceServerTimeVo;
+import org.dromara.customer.domain.bo.MaintenanceServerTimeBo;
+import org.dromara.customer.service.IMaintenanceServerTimeService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 维保服务时间
+ * 前端访问路由地址为:/customer/serverTime
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/serverTime")
+public class MaintenanceServerTimeController extends BaseController {
+
+    private final IMaintenanceServerTimeService maintenanceServerTimeService;
+
+    /**
+     * 查询维保服务时间列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<MaintenanceServerTimeVo> list(MaintenanceServerTimeBo bo, PageQuery pageQuery) {
+        return maintenanceServerTimeService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出维保服务时间列表
+     */
+    @SaCheckPermission("customer:serverTime:export")
+    @Log(title = "维保服务时间", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(MaintenanceServerTimeBo bo, HttpServletResponse response) {
+        List<MaintenanceServerTimeVo> list = maintenanceServerTimeService.queryList(bo);
+        ExcelUtil.exportExcel(list, "维保服务时间", MaintenanceServerTimeVo.class, response);
+    }
+
+    /**
+     * 获取维保服务时间详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("customer:serverTime:query")
+    @GetMapping("/{id}")
+    public R<MaintenanceServerTimeVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(maintenanceServerTimeService.queryById(id));
+    }
+
+    /**
+     * 新增维保服务时间
+     */
+    @SaCheckPermission("customer:serverTime:add")
+    @Log(title = "维保服务时间", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody MaintenanceServerTimeBo bo) {
+        return toAjax(maintenanceServerTimeService.insertByBo(bo));
+    }
+
+    /**
+     * 修改维保服务时间
+     */
+    @SaCheckPermission("customer:serverTime:edit")
+    @Log(title = "维保服务时间", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody MaintenanceServerTimeBo bo) {
+        return toAjax(maintenanceServerTimeService.updateByBo(bo));
+    }
+
+    /**
+     * 删除维保服务时间
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("customer:serverTime:remove")
+    @Log(title = "维保服务时间", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(maintenanceServerTimeService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 105 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/MaintenanceTypeController.java

@@ -0,0 +1,105 @@
+package org.dromara.customer.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.customer.domain.vo.MaintenanceTypeVo;
+import org.dromara.customer.domain.bo.MaintenanceTypeBo;
+import org.dromara.customer.service.IMaintenanceTypeService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 维保类型
+ * 前端访问路由地址为:/customer/maintenanceType
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/maintenanceType")
+public class MaintenanceTypeController extends BaseController {
+
+    private final IMaintenanceTypeService maintenanceTypeService;
+
+    /**
+     * 查询维保类型列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<MaintenanceTypeVo> list(MaintenanceTypeBo bo, PageQuery pageQuery) {
+        return maintenanceTypeService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出维保类型列表
+     */
+    @SaCheckPermission("customer:maintenanceType:export")
+    @Log(title = "维保类型", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(MaintenanceTypeBo bo, HttpServletResponse response) {
+        List<MaintenanceTypeVo> list = maintenanceTypeService.queryList(bo);
+        ExcelUtil.exportExcel(list, "维保类型", MaintenanceTypeVo.class, response);
+    }
+
+    /**
+     * 获取维保类型详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("customer:maintenanceType:query")
+    @GetMapping("/{id}")
+    public R<MaintenanceTypeVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable("id") Long id) {
+        return R.ok(maintenanceTypeService.queryById(id));
+    }
+
+    /**
+     * 新增维保类型
+     */
+    @SaCheckPermission("customer:maintenanceType:add")
+    @Log(title = "维保类型", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody MaintenanceTypeBo bo) {
+        return toAjax(maintenanceTypeService.insertByBo(bo));
+    }
+
+    /**
+     * 修改维保类型
+     */
+    @SaCheckPermission("customer:maintenanceType:edit")
+    @Log(title = "维保类型", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody MaintenanceTypeBo bo) {
+        return toAjax(maintenanceTypeService.updateByBo(bo));
+    }
+
+    /**
+     * 删除维保类型
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("customer:maintenanceType:remove")
+    @Log(title = "维保类型", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(maintenanceTypeService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 112 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/PurchaseHabitController.java

@@ -0,0 +1,112 @@
+package org.dromara.customer.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.customer.domain.vo.PurchaseHabitVo;
+import org.dromara.customer.domain.bo.PurchaseHabitBo;
+import org.dromara.customer.service.IPurchaseHabitService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 客户采购习惯
+ * 前端访问路由地址为:/customer/purchaseHabit
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/purchaseHabit")
+public class PurchaseHabitController extends BaseController {
+
+    private final IPurchaseHabitService purchaseHabitService;
+
+    /**
+     * 查询客户采购习惯列表
+     */
+    @SaCheckPermission("customer:purchaseHabit:list")
+    @GetMapping("/list")
+    public TableDataInfo<PurchaseHabitVo> list(PurchaseHabitBo bo, PageQuery pageQuery) {
+        return purchaseHabitService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出客户采购习惯列表
+     */
+    @SaCheckPermission("customer:purchaseHabit:export")
+    @Log(title = "客户采购习惯", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(PurchaseHabitBo bo, HttpServletResponse response) {
+        List<PurchaseHabitVo> list = purchaseHabitService.queryList(bo);
+        ExcelUtil.exportExcel(list, "客户采购习惯", PurchaseHabitVo.class, response);
+    }
+
+    /**
+     * 获取客户采购习惯详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("customer:purchaseHabit:query")
+    @GetMapping("/{id}")
+    public R<PurchaseHabitVo> getInfo(@NotNull(message = "主键不能为空")
+                                      @PathVariable("id") Long id) {
+        return R.ok(purchaseHabitService.queryById(id));
+    }
+
+    /**
+     * 新增客户采购习惯
+     */
+    @SaCheckPermission("customer:purchaseHabit:add")
+    @Log(title = "客户采购习惯", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody PurchaseHabitBo bo) {
+        return toAjax(purchaseHabitService.insertByBo(bo));
+    }
+
+    /**
+     * 修改客户采购习惯
+     */
+    @SaCheckPermission("customer:purchaseHabit:edit")
+    @Log(title = "客户采购习惯", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PurchaseHabitBo bo) {
+        return toAjax(purchaseHabitService.updateByBo(bo));
+    }
+
+    /**
+     * 删除客户采购习惯
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("customer:purchaseHabit:remove")
+    @Log(title = "客户采购习惯", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(purchaseHabitService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    @SaCheckPermission("customer:purchaseHabit:list")
+    @GetMapping("/getCustomerPurchaseHabitData")
+    public R<PurchaseHabitVo> getOpenPlatformSettingData(@RequestParam("customerNo") String customerNo) {
+        return R.ok(purchaseHabitService.getCustomerPurchaseHabitData(customerNo));
+    }
+}

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

@@ -6,6 +6,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 
 import java.io.Serial;
+import java.util.Date;
 
 /**
  * 客户联系人信息对象 customer_contact
@@ -37,6 +38,9 @@ public class CustomerContact extends TenantEntity {
      */
     private String contactName;
 
+    /*自定义登录名*/
+    private String customLoginName;
+
     /**
      * 手机号码
      */
@@ -62,6 +66,12 @@ public class CustomerContact extends TenantEntity {
      */
     private Long roleId;
 
+    private Long deptId;
+
+    private String email;
+
+    private Date birthday;
+
     /**
      * 是否主联系人:0=是,1=否
      */

+ 123 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerContract.java

@@ -0,0 +1,123 @@
+package org.dromara.customer.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.io.Serial;
+
+/**
+ * 客户合同对象 customer_contract
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("customer_contract")
+public class CustomerContract extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    private String customerNo;
+
+    /**
+     * 客户id
+     */
+    private Long customerId;
+
+    private String customerName;
+
+    /**
+     * 合同编号
+     */
+    private String contractNo;
+
+    /**
+     * 合同名称
+     */
+    private String contractName;
+
+    /**
+     * 合同金额
+     */
+    private String contractAmount;
+
+    /**
+     * 客服负责人
+     */
+    private String customerLeader;
+
+    /**
+     * 项目负责人
+     */
+    private String projectLeader;
+
+    /**
+     * 合同类型
+     */
+    private String contractType;
+
+    /**
+     * 合同开始时间
+     */
+    private Date startTime;
+
+    /**
+     * 合同结束时间
+     */
+    private Date endTime;
+
+    /**
+     * 合同上传时间
+     */
+    private Date uploadTime;
+
+    /**
+     * 结算方式
+     */
+    private String settlementType;
+
+    /**
+     * 合同状态
+     */
+    private String contractStatus;
+
+    /**
+     * 审批状态(如:0-待审批,1-已通过,2-已驳回等)
+     */
+    private String approvalStatus;
+
+    /**
+     * 附件路径或信息
+     */
+    private String annex;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 8 - 3
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerInfo.java

@@ -4,7 +4,9 @@ import org.dromara.common.tenant.core.TenantEntity;
 import com.baomidou.mybatisplus.annotation.*;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+
 import java.util.Date;
+
 import com.fasterxml.jackson.annotation.JsonFormat;
 
 import java.io.Serial;
@@ -39,10 +41,13 @@ public class CustomerInfo extends TenantEntity {
      */
     private Long belongCompanyId;
 
+
+    private String companyName;
+
     /**
      * 客户名称
      */
-    private String companyName;
+    private String customerName;
 
     /**
      * 工商名称
@@ -67,7 +72,7 @@ public class CustomerInfo extends TenantEntity {
     /**
      * 客户类别
      */
-    private Long customerTypeId;
+    private String customerTypeId;
 
     /**
      * 行业类别
@@ -77,7 +82,7 @@ public class CustomerInfo extends TenantEntity {
     /**
      * 客户等级
      */
-    private Long customerLevelId;
+    private String customerLevelId;
 
     /**
      * 固定电话

+ 42 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerInfoTag.java

@@ -0,0 +1,42 @@
+package org.dromara.customer.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.PlatformEntity;
+
+import java.io.Serial;
+
+/**
+ * 客户标签关联对象 customer_info_tag
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("customer_info_tag")
+public class CustomerInfoTag extends PlatformEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 客户id
+     */
+    private Long customerId;
+
+    /**
+     * 标签id
+     */
+    private Long tagId;
+
+
+}

+ 127 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerShippingAddress.java

@@ -0,0 +1,127 @@
+package org.dromara.customer.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 客户收货地址对象 customer_shipping_address
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("customer_shipping_address")
+public class CustomerShippingAddress extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 客户id
+     */
+    private Long customerId;
+
+    /**
+     * 收货地址编号
+     */
+    private String shippingAddressNo;
+
+    /**
+     * 收货人姓名
+     */
+    private String consignee;
+
+    /**
+     * 部门编号
+     */
+    private Long deptId;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 详细地址
+     */
+    private String address;
+
+    /**
+     * 邮政编码
+     */
+    private String postal;
+
+    /**
+     * 是否默认地址(0-是,1-否)
+     */
+    private String defaultAddress;
+
+    /**
+     * 地址标签
+     */
+    private String addressLabel;
+
+    /**
+     * 省份编码
+     */
+    private String provincialNo;
+
+    /**
+     * 城市编码
+     */
+    private String cityNo;
+
+    /**
+     * 区县编码
+     */
+    private String countryNo;
+
+    /**
+     * 区县编码
+     */
+    private String provincialCityCountry;
+
+    /**
+     * 推送状态(0-已推送,1-未推送等)
+     */
+    private String pushStatus;
+
+    /**
+     * 序号/排序号
+     */
+    private Long num;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 52 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/MaintenanceServerItem.java

@@ -0,0 +1,52 @@
+package org.dromara.customer.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 维保服务内容对象 maintenance_server_item
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("maintenance_server_item")
+public class MaintenanceServerItem extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 服务内容
+     */
+    private String itemName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 52 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/MaintenanceServerTime.java

@@ -0,0 +1,52 @@
+package org.dromara.customer.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 维保服务时间对象 maintenance_server_time
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("maintenance_server_time")
+public class MaintenanceServerTime extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 时间类型
+     */
+    private String timeName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 52 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/MaintenanceType.java

@@ -0,0 +1,52 @@
+package org.dromara.customer.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 维保类型对象 maintenance_type
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("maintenance_type")
+public class MaintenanceType extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 类型名称
+     */
+    private String typeName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 113 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/PurchaseHabit.java

@@ -0,0 +1,113 @@
+package org.dromara.customer.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import java.math.BigDecimal;
+
+import java.io.Serial;
+
+/**
+ * 客户采购习惯对象 purchase_habit
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("purchase_habit")
+public class PurchaseHabit extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    private String customerNo;
+
+    /**
+     * 月度采购量
+     */
+    private BigDecimal monthPurchase;
+
+    /**
+     * 年度采购量
+     */
+    private BigDecimal yearPurchase;
+
+    /**
+     * 常驻负责人/对接人
+     */
+    private String permanentOfficer;
+
+    /**
+     * 选择的机型/型号
+     */
+    private String choiceModel;
+
+    /**
+     * 打印量
+     */
+    private String printAmount;
+
+    /**
+     * 是否购买原装耗材(0-是,1-否)
+     */
+    private String buyOriginal;
+
+    /**
+     * 是否购买技术服务(0-是,1-否)
+     */
+    private String technologyService;
+
+    /**
+     * 采购品类(主选项)
+     */
+    private String purchaseCategory;
+
+    /**
+     * 采购品类(其他补充)
+     */
+    private String otherCategory;
+
+    /**
+     * 适用场景(主选项)
+     */
+    private String adaptScene;
+
+    /**
+     * 适用场景(其他补充)
+     */
+    private String otherScene;
+
+    /**
+     * 定制化需求(主选项)
+     */
+    private String customizeDemand;
+
+    /**
+     * 定制化需求(其他补充)
+     */
+    private String otherCustomize;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

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

@@ -9,6 +9,8 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import jakarta.validation.constraints.*;
 
+import java.util.Date;
+
 /**
  * 客户联系人信息业务对象 customer_contact
  *
@@ -33,13 +35,13 @@ public class CustomerContactBo extends BaseEntity {
     /**
      * 联系人姓名
      */
-    @NotBlank(message = "联系人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotBlank(message = "联系人姓名不能为空", groups = {AddGroup.class, EditGroup.class})
     private String contactName;
 
     /**
      * 手机号码
      */
-    @NotBlank(message = "手机号码不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotBlank(message = "手机号码不能为空", groups = {AddGroup.class, EditGroup.class})
     private String phone;
 
     /**
@@ -52,6 +54,9 @@ public class CustomerContactBo extends BaseEntity {
      */
     private String officePhoneTwo;
 
+    /*自定义登录名*/
+    private String customLoginName;
+
     /**
      * 性别
      */
@@ -62,6 +67,12 @@ public class CustomerContactBo extends BaseEntity {
      */
     private Long roleId;
 
+    private Long deptId;
+
+    private String email;
+
+    private Date birthday;
+
     /**
      * 是否主联系人:0=是,1=否
      */
@@ -100,7 +111,6 @@ public class CustomerContactBo extends BaseEntity {
     /**
      * 备注
      */
-    @NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
     private String remark;
 
 

+ 121 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContractBo.java

@@ -0,0 +1,121 @@
+package org.dromara.customer.domain.bo;
+
+import org.dromara.customer.domain.CustomerContract;
+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.*;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 客户合同业务对象 customer_contract
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = CustomerContract.class, reverseConvertGenerate = false)
+public class CustomerContractBo extends BaseEntity {
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    @NotBlank(message = "客户编号不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String customerNo;
+
+    /**
+     * 客户id
+     */
+    private Long customerId;
+
+    /**
+     * 合同编号
+     */
+    private String contractNo;
+
+    /**
+     * 合同名称
+     */
+    private String contractName;
+
+    private String customerName;
+
+
+    /**
+     * 合同金额
+     */
+    @NotBlank(message = "合同金额不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String contractAmount;
+
+    /**
+     * 客服负责人
+     */
+    private String customerLeader;
+
+    /**
+     * 项目负责人
+     */
+    private String projectLeader;
+
+    /**
+     * 合同类型
+     */
+    @NotBlank(message = "合同类型不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String contractType;
+
+    /**
+     * 合同开始时间
+     */
+    @NotNull(message = "合同开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Date startTime;
+
+    /**
+     * 合同结束时间
+     */
+    @NotNull(message = "合同结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Date endTime;
+
+    /**
+     * 合同上传时间
+     */
+    private Date uploadTime;
+
+    /**
+     * 结算方式
+     */
+    private String settlementType;
+
+    /**
+     * 合同状态
+     */
+    private String contractStatus;
+
+    /**
+     * 审批状态
+     */
+    private String approvalStatus;
+
+    /**
+     * 附件路径或信息
+     */
+    private String annex;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 16 - 14
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerInfoBo.java

@@ -8,6 +8,7 @@ import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import jakarta.validation.constraints.*;
+
 import java.util.Date;
 import java.util.List;
 
@@ -32,62 +33,64 @@ public class CustomerInfoBo extends BaseEntity {
     /**
      * 客户编号
      */
-    @NotBlank(message = "客户编号不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotBlank(message = "客户编号不能为空", groups = {AddGroup.class, EditGroup.class})
     private String customerNo;
 
     /**
      * 所属公司
      */
-    @NotNull(message = "所属公司不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "所属公司不能为空", groups = {AddGroup.class, EditGroup.class})
     private Long belongCompanyId;
 
     /**
-     * 客户名称
+     * 公司名称
      */
-    @NotBlank(message = "客户名称不能为空", groups = { AddGroup.class, EditGroup.class })
     private String companyName;
 
+    /**
+     * 客户名称
+     */
+    private String customerName;
+
     /**
      * 工商名称
      */
-    @NotBlank(message = "工商名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotBlank(message = "工商名称不能为空", groups = {AddGroup.class, EditGroup.class})
     private String businessCustomerName;
 
     /**
      * 企业简称
      */
-    @NotBlank(message = "企业简称不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotBlank(message = "企业简称不能为空", groups = {AddGroup.class, EditGroup.class})
     private String shortName;
 
     /**
      * 开票类型
      */
-    @NotNull(message = "开票类型不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "开票类型不能为空", groups = {AddGroup.class, EditGroup.class})
     private Long invoiceTypeId;
 
     /**
      * 企业规模
      */
-    @NotNull(message = "企业规模不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "企业规模不能为空", groups = {AddGroup.class, EditGroup.class})
     private Long enterpriseScaleId;
 
     /**
      * 客户类别
      */
-    @NotNull(message = "客户类别不能为空", groups = { AddGroup.class, EditGroup.class })
-    private Long customerTypeId;
+    private String customerTypeId;
 
     /**
      * 行业类别
      */
-    @NotNull(message = "行业类别不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "行业类别不能为空", groups = {AddGroup.class, EditGroup.class})
     private Long industryCategoryId;
 
     /**
      * 客户等级
      */
-    @NotNull(message = "客户等级不能为空", groups = { AddGroup.class, EditGroup.class })
-    private Long customerLevelId;
+    private String customerLevelId;
 
     /**
      * 固定电话
@@ -167,5 +170,4 @@ public class CustomerInfoBo extends BaseEntity {
 
     private List<CustomerInvoiceInfoBo> customerInvoiceInfoBoList;
 
-
 }

+ 7 - 4
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerSalesInfoBo.java

@@ -8,7 +8,9 @@ import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import jakarta.validation.constraints.*;
+
 import java.math.BigDecimal;
+import java.util.List;
 
 /**
  * 客户销售信息业务对象 customer_sales_info
@@ -44,13 +46,13 @@ public class CustomerSalesInfoBo extends BaseEntity {
     /**
      * 临时额度
      */
-    @NotNull(message = "临时额度不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "临时额度不能为空", groups = {AddGroup.class, EditGroup.class})
     private BigDecimal temporaryQuota;
 
     /**
      * 账期(如:30天)
      */
-    @NotBlank(message = "账期(如:30天)不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotBlank(message = "账期(如:30天)不能为空", groups = {AddGroup.class, EditGroup.class})
     private String accountPeriod;
 
     /**
@@ -86,13 +88,13 @@ public class CustomerSalesInfoBo extends BaseEntity {
     /**
      * 销售人员
      */
-    @NotNull(message = "销售人员不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "销售人员不能为空", groups = {AddGroup.class, EditGroup.class})
     private Long salesPersonId;
 
     /**
      * 服务人员
      */
-    @NotNull(message = "服务人员不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "服务人员不能为空", groups = {AddGroup.class, EditGroup.class})
     private Long serviceStaffId;
 
     /**
@@ -115,5 +117,6 @@ public class CustomerSalesInfoBo extends BaseEntity {
      */
     private String remark;
 
+    private List<Long> customerIds;
 
 }

+ 120 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerShippingAddressBo.java

@@ -0,0 +1,120 @@
+package org.dromara.customer.domain.bo;
+
+import org.dromara.customer.domain.CustomerShippingAddress;
+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.*;
+
+/**
+ * 客户收货地址业务对象 customer_shipping_address
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = CustomerShippingAddress.class, reverseConvertGenerate = false)
+public class CustomerShippingAddressBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 客户id
+     */
+    private Long customerId;
+
+    /**
+     * 收货地址编号
+     */
+    private String shippingAddressNo;
+
+    /**
+     * 收货人姓名
+     */
+    @NotBlank(message = "收货人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String consignee;
+
+    /**
+     * 部门编号
+     */
+    private Long deptId;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 详细地址
+     */
+    private String address;
+
+    /**
+     * 邮政编码
+     */
+    private String postal;
+
+    /**
+     * 是否默认地址(0-是,1-否)
+     */
+    private String defaultAddress;
+
+    /**
+     * 地址标签
+     */
+    private String addressLabel;
+
+    /**
+     * 省份编码
+     */
+    private String provincialNo;
+
+    /**
+     * 城市编码
+     */
+    private String cityNo;
+
+    /**
+     * 区县编码
+     */
+    private String countryNo;
+
+    /**
+     * 区县编码
+     */
+    private String provincialCityCountry;
+
+    /**
+     * 推送状态(0-已推送,1-未推送等)
+     */
+    private String pushStatus;
+
+    /**
+     * 序号/排序号
+     */
+    private Long num;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 45 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/MaintenanceServerItemBo.java

@@ -0,0 +1,45 @@
+package org.dromara.customer.domain.bo;
+
+import org.dromara.customer.domain.MaintenanceServerItem;
+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.*;
+
+/**
+ * 维保服务内容业务对象 maintenance_server_item
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = MaintenanceServerItem.class, reverseConvertGenerate = false)
+public class MaintenanceServerItemBo extends BaseEntity {
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 服务内容
+     */
+    @NotBlank(message = "服务内容不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String itemName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 45 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/MaintenanceServerTimeBo.java

@@ -0,0 +1,45 @@
+package org.dromara.customer.domain.bo;
+
+import org.dromara.customer.domain.MaintenanceServerTime;
+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.*;
+
+/**
+ * 维保服务时间业务对象 maintenance_server_time
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = MaintenanceServerTime.class, reverseConvertGenerate = false)
+public class MaintenanceServerTimeBo extends BaseEntity {
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 时间类型
+     */
+    @NotBlank(message = "时间类型不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String timeName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 45 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/MaintenanceTypeBo.java

@@ -0,0 +1,45 @@
+package org.dromara.customer.domain.bo;
+
+import org.dromara.customer.domain.MaintenanceType;
+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.*;
+
+/**
+ * 维保类型业务对象 maintenance_type
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = MaintenanceType.class, reverseConvertGenerate = false)
+public class MaintenanceTypeBo extends BaseEntity {
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 类型名称
+     */
+    @NotBlank(message = "类型名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String typeName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 108 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/PurchaseHabitBo.java

@@ -0,0 +1,108 @@
+package org.dromara.customer.domain.bo;
+
+import org.dromara.customer.domain.PurchaseHabit;
+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.*;
+
+import java.math.BigDecimal;
+
+/**
+ * 客户采购习惯业务对象 purchase_habit
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = PurchaseHabit.class, reverseConvertGenerate = false)
+public class PurchaseHabitBo extends BaseEntity {
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    private String customerNo;
+
+    /**
+     * 月度采购量
+     */
+    private BigDecimal monthPurchase;
+
+    /**
+     * 年度采购量
+     */
+    private BigDecimal yearPurchase;
+
+    /**
+     * 常驻负责人/对接人
+     */
+    private String permanentOfficer;
+
+    /**
+     * 选择的机型/型号
+     */
+    private String choiceModel;
+
+    /**
+     * 打印量
+     */
+    private String printAmount;
+
+    /**
+     * 是否购买原装耗材(0-是,1-否)
+     */
+    @NotBlank(message = "是否购买原装耗材(0-是,1-否)不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String buyOriginal;
+
+    /**
+     * 是否购买技术服务(0-是,1-否)
+     */
+    @NotBlank(message = "是否购买技术服务(0-是,1-否)不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String technologyService;
+
+    /**
+     * 采购品类(主选项)
+     */
+    private String purchaseCategory;
+
+    /**
+     * 采购品类(其他补充)
+     */
+    private String otherCategory;
+
+    /**
+     * 适用场景(主选项)
+     */
+    private String adaptScene;
+
+    /**
+     * 适用场景(其他补充)
+     */
+    private String otherScene;
+
+    /**
+     * 定制化需求(主选项)
+     */
+    private String customizeDemand;
+
+    /**
+     * 定制化需求(其他补充)
+     */
+    private String otherCustomize;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 13 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/dto/SetCustomerInfoTagDto.java

@@ -0,0 +1,13 @@
+package org.dromara.customer.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class SetCustomerInfoTagDto implements Serializable {
+    private List<Long> customerIds;
+
+    private List<Long> tagIds;
+}

+ 41 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/ContractVo.java

@@ -0,0 +1,41 @@
+package org.dromara.customer.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class ContractVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    // 客户id
+    private Long customerId;
+    //客户编号
+    private String customerNo;
+
+    //客户名称
+    private String customerName;
+
+    private Long deptId;
+
+    //部门
+    private String deptName;
+
+    private Long industryCategoryId;
+
+    //行业类别
+    private String industryCategory;
+
+    //合同数量
+    private Integer contractCount;
+
+    //有效合同数量
+    private Integer effectiveContract;
+
+    //无效合同数量
+    private Integer invalidContract;
+
+    //作废合同数量
+    private Integer voidedContract;
+
+
+}

+ 9 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContactVo.java

@@ -13,7 +13,6 @@ import java.io.Serializable;
 import java.util.Date;
 
 
-
 /**
  * 客户联系人信息视图对象 customer_contact
  *
@@ -46,6 +45,9 @@ public class CustomerContactVo implements Serializable {
     @ExcelProperty(value = "联系人姓名")
     private String contactName;
 
+    /*自定义登录名*/
+    private String customLoginName;
+
     /**
      * 手机号码
      */
@@ -78,6 +80,8 @@ public class CustomerContactVo implements Serializable {
     @ExcelDictFormat(readConverterExp = "如=:采购经理、财务")
     private Long roleId;
 
+    private Long deptId;
+
     /**
      * 是否主联系人:0=是,1=否
      */
@@ -91,6 +95,10 @@ public class CustomerContactVo implements Serializable {
     @ExcelProperty(value = "详细地址")
     private String addressDetail;
 
+    private String email;
+
+    private Date birthday;
+
     /**
      * 所在省编码
      */

+ 140 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContractVo.java

@@ -0,0 +1,140 @@
+package org.dromara.customer.domain.vo;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.dromara.customer.domain.CustomerContract;
+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;
+
+
+/**
+ * 客户合同视图对象 customer_contract
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = CustomerContract.class)
+public class CustomerContractVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    @ExcelProperty(value = "客户编号")
+    private String customerNo;
+
+    /**
+     * 客户id
+     */
+    @ExcelProperty(value = "客户id")
+    private Long customerId;
+
+    private String customerName;
+
+    /**
+     * 合同编号
+     */
+    @ExcelProperty(value = "合同编号")
+    private String contractNo;
+
+    /**
+     * 合同名称
+     */
+    @ExcelProperty(value = "合同名称")
+    private String contractName;
+
+    /**
+     * 合同金额
+     */
+    @ExcelProperty(value = "合同金额")
+    private String contractAmount;
+
+    /**
+     * 客服负责人
+     */
+    @ExcelProperty(value = "客服负责人")
+    private String customerLeader;
+
+    /**
+     * 项目负责人
+     */
+    @ExcelProperty(value = "项目负责人")
+    private String projectLeader;
+
+    /**
+     * 合同类型
+     */
+    @ExcelProperty(value = "合同类型", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "contract_type")
+    private String contractType;
+
+    /**
+     * 合同开始时间
+     */
+    @ExcelProperty(value = "合同开始时间")
+    private Date startTime;
+
+    /**
+     * 合同结束时间
+     */
+    @ExcelProperty(value = "合同结束时间")
+    private Date endTime;
+
+    /**
+     * 合同上传时间
+     */
+    @ExcelProperty(value = "合同上传时间")
+    private Date uploadTime;
+
+    /**
+     * 结算方式
+     */
+    @ExcelProperty(value = "结算方式")
+    private String settlementType;
+
+    /**
+     * 合同状态
+     */
+    @ExcelProperty(value = "合同状态")
+    private String contractStatus;
+
+    /**
+     * 审批状态(如:0-待审批,1-已通过,2-已驳回等)
+     */
+    @ExcelProperty(value = "审批状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "如=:0-待审批,1-已通过,2-已驳回等")
+    private String approvalStatus;
+
+    /**
+     * 附件路径或信息
+     */
+    @ExcelProperty(value = "附件路径或信息")
+    private String annex;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 10 - 3
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerInfoVo.java

@@ -1,6 +1,7 @@
 package org.dromara.customer.domain.vo;
 
 import java.util.Date;
+
 import com.fasterxml.jackson.annotation.JsonFormat;
 import org.dromara.customer.domain.CustomerInfo;
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
@@ -51,11 +52,17 @@ public class CustomerInfoVo implements Serializable {
     @ExcelProperty(value = "所属公司")
     private Long belongCompanyId;
 
+    /**
+     * 公司名称
+     */
+    @ExcelProperty(value = "公司名称")
+    private String companyName;
+
     /**
      * 客户名称
      */
     @ExcelProperty(value = "客户名称")
-    private String companyName;
+    private String customerName;
 
     /**
      * 工商名称
@@ -85,7 +92,7 @@ public class CustomerInfoVo implements Serializable {
      * 客户类别
      */
     @ExcelProperty(value = "客户类别")
-    private Long customerTypeId;
+    private String customerTypeId;
 
     /**
      * 行业类别
@@ -97,7 +104,7 @@ public class CustomerInfoVo implements Serializable {
      * 客户等级
      */
     @ExcelProperty(value = "客户等级")
-    private Long customerLevelId;
+    private String customerLevelId;
 
     /**
      * 固定电话

+ 149 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerShippingAddressVo.java

@@ -0,0 +1,149 @@
+package org.dromara.customer.domain.vo;
+
+import org.dromara.customer.domain.CustomerShippingAddress;
+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;
+
+
+
+/**
+ * 客户收货地址视图对象 customer_shipping_address
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = CustomerShippingAddress.class)
+public class CustomerShippingAddressVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 客户id
+     */
+    @ExcelProperty(value = "客户id")
+    private Long customerId;
+
+    /**
+     * 收货地址编号
+     */
+    @ExcelProperty(value = "收货地址编号")
+    private String shippingAddressNo;
+
+    /**
+     * 收货人姓名
+     */
+    @ExcelProperty(value = "收货人姓名")
+    private String consignee;
+
+    /**
+     * 部门编号
+     */
+    @ExcelProperty(value = "部门编号")
+    private Long deptId;
+
+    /**
+     * 联系电话
+     */
+    @ExcelProperty(value = "联系电话")
+    private String phone;
+
+    /**
+     * 详细地址
+     */
+    @ExcelProperty(value = "详细地址")
+    private String address;
+
+    /**
+     * 邮政编码
+     */
+    @ExcelProperty(value = "邮政编码")
+    private String postal;
+
+    /**
+     * 是否默认地址(0-是,1-否)
+     */
+    @ExcelProperty(value = "是否默认地址", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=-是,1-否")
+    private String defaultAddress;
+
+    /**
+     * 地址标签
+     */
+    @ExcelProperty(value = "地址标签")
+    private String addressLabel;
+
+    /**
+     * 省份编码
+     */
+    @ExcelProperty(value = "省份编码")
+    private String provincialNo;
+
+    /**
+     * 城市编码
+     */
+    @ExcelProperty(value = "城市编码")
+    private String cityNo;
+
+    /**
+     * 区县编码
+     */
+    @ExcelProperty(value = "区县编码")
+    private String countryNo;
+
+    /**
+     * 区县编码
+     */
+    @ExcelProperty(value = "区县编码")
+    private String provincialCityCountry;
+
+    /**
+     * 推送状态(0-已推送,1-未推送等)
+     */
+    @ExcelProperty(value = "推送状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=-已推送,1-未推送等")
+    private String pushStatus;
+
+    /**
+     * 序号/排序号
+     */
+    @ExcelProperty(value = "序号/排序号")
+    private Long num;
+
+    /**
+     * 部门名称
+     */
+    @ExcelProperty(value = "部门名称")
+    private String deptName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 2 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/MaintainInfoVo.java

@@ -188,5 +188,7 @@ public class MaintainInfoVo implements Serializable {
 
     private Integer maintainCount;
 
+    private String serviceContentStr;
+
 
 }

+ 57 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/MaintenanceServerItemVo.java

@@ -0,0 +1,57 @@
+package org.dromara.customer.domain.vo;
+
+import org.dromara.customer.domain.MaintenanceServerItem;
+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;
+
+
+
+/**
+ * 维保服务内容视图对象 maintenance_server_item
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = MaintenanceServerItem.class)
+public class MaintenanceServerItemVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 服务内容
+     */
+    @ExcelProperty(value = "服务内容")
+    private String itemName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 57 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/MaintenanceServerTimeVo.java

@@ -0,0 +1,57 @@
+package org.dromara.customer.domain.vo;
+
+import org.dromara.customer.domain.MaintenanceServerTime;
+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;
+
+
+
+/**
+ * 维保服务时间视图对象 maintenance_server_time
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = MaintenanceServerTime.class)
+public class MaintenanceServerTimeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 时间类型
+     */
+    @ExcelProperty(value = "时间类型")
+    private String timeName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 57 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/MaintenanceTypeVo.java

@@ -0,0 +1,57 @@
+package org.dromara.customer.domain.vo;
+
+import org.dromara.customer.domain.MaintenanceType;
+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;
+
+
+
+/**
+ * 维保类型视图对象 maintenance_type
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = MaintenanceType.class)
+public class MaintenanceTypeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 类型名称
+     */
+    @ExcelProperty(value = "类型名称")
+    private String typeName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 139 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/PurchaseHabitVo.java

@@ -0,0 +1,139 @@
+package org.dromara.customer.domain.vo;
+
+import java.math.BigDecimal;
+import org.dromara.customer.domain.PurchaseHabit;
+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;
+
+
+
+/**
+ * 客户采购习惯视图对象 purchase_habit
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = PurchaseHabit.class)
+public class PurchaseHabitVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 客户编号
+     */
+    @ExcelProperty(value = "客户编号")
+    private String customerNo;
+
+    /**
+     * 月度采购量
+     */
+    @ExcelProperty(value = "月度采购量")
+    private BigDecimal monthPurchase;
+
+    /**
+     * 年度采购量
+     */
+    @ExcelProperty(value = "年度采购量")
+    private BigDecimal yearPurchase;
+
+    /**
+     * 常驻负责人/对接人
+     */
+    @ExcelProperty(value = "常驻负责人/对接人")
+    private String permanentOfficer;
+
+    /**
+     * 选择的机型/型号
+     */
+    @ExcelProperty(value = "选择的机型/型号", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "product_types_choosing")
+    private String choiceModel;
+
+    /**
+     * 打印量
+     */
+    @ExcelProperty(value = "打印量", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "daily_print_volume")
+    private String printAmount;
+
+    /**
+     * 是否购买原装耗材(0-是,1-否)
+     */
+    @ExcelProperty(value = "是否购买原装耗材", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_platform_yes_no")
+    private String buyOriginal;
+
+    /**
+     * 是否购买技术服务(0-是,1-否)
+     */
+    @ExcelProperty(value = "是否购买技术服务", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_platform_yes_no")
+    private String technologyService;
+
+    /**
+     * 采购品类(主选项)
+     */
+    @ExcelProperty(value = "采购品类", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "purchase_item")
+    private String purchaseCategory;
+
+    /**
+     * 采购品类(其他补充)
+     */
+    @ExcelProperty(value = "采购品类", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "其=他补充")
+    private String otherCategory;
+
+    /**
+     * 适用场景(主选项)
+     */
+    @ExcelProperty(value = "适用场景", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "welfare_item")
+    private String adaptScene;
+
+    /**
+     * 适用场景(其他补充)
+     */
+    @ExcelProperty(value = "适用场景", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "其=他补充")
+    private String otherScene;
+
+    /**
+     * 定制化需求(主选项)
+     */
+    @ExcelProperty(value = "定制化需求", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "product_customization")
+    private String customizeDemand;
+
+    /**
+     * 定制化需求(其他补充)
+     */
+    @ExcelProperty(value = "定制化需求", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "其=他补充")
+    private String otherCustomize;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 21 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerContractMapper.java

@@ -0,0 +1,21 @@
+package org.dromara.customer.mapper;
+
+import org.apache.dubbo.remoting.http12.rest.Param;
+import org.dromara.customer.domain.CustomerContract;
+import org.dromara.customer.domain.vo.CustomerContractVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 客户合同Mapper接口
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+public interface CustomerContractMapper extends BaseMapperPlus<CustomerContract, CustomerContractVo> {
+
+    List<Map<String, Object>> selectContractStatsByCustomerIds(@Param("customerIdList") List<Long> customerIdList);
+
+}

+ 16 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerInfoTagMapper.java

@@ -0,0 +1,16 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.customer.domain.CustomerInfoTag;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+import java.util.List;
+
+/**
+ * 客户标签关联Mapper接口
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+public interface CustomerInfoTagMapper extends BaseMapperPlus<CustomerInfoTag, CustomerInfoTag> {
+    List<Long> selectCustomerIdsByTagId(Long tagId);
+}

+ 22 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerShippingAddressMapper.java

@@ -0,0 +1,22 @@
+package org.dromara.customer.mapper;
+
+import org.apache.dubbo.remoting.http12.rest.Param;
+import org.apache.ibatis.annotations.Update;
+import org.dromara.customer.domain.CustomerShippingAddress;
+import org.dromara.customer.domain.vo.CustomerShippingAddressVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 客户收货地址Mapper接口
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+public interface CustomerShippingAddressMapper extends BaseMapperPlus<CustomerShippingAddress, CustomerShippingAddressVo> {
+
+    @Update("UPDATE customer_shipping_address SET default_address = #{noCode} WHERE customer_id = #{customerId} AND default_address = #{yesCode}")
+    int clearDefaultAddressByCustomerId(@Param("customerId") Long customerId,
+                                        @Param("yesCode") String yesCode,
+                                        @Param("noCode") String noCode);
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/MaintenanceServerItemMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.customer.domain.MaintenanceServerItem;
+import org.dromara.customer.domain.vo.MaintenanceServerItemVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 维保服务内容Mapper接口
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+public interface MaintenanceServerItemMapper extends BaseMapperPlus<MaintenanceServerItem, MaintenanceServerItemVo> {
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/MaintenanceServerTimeMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.customer.domain.MaintenanceServerTime;
+import org.dromara.customer.domain.vo.MaintenanceServerTimeVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 维保服务时间Mapper接口
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+public interface MaintenanceServerTimeMapper extends BaseMapperPlus<MaintenanceServerTime, MaintenanceServerTimeVo> {
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/MaintenanceTypeMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.customer.domain.MaintenanceType;
+import org.dromara.customer.domain.vo.MaintenanceTypeVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 维保类型Mapper接口
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+public interface MaintenanceTypeMapper extends BaseMapperPlus<MaintenanceType, MaintenanceTypeVo> {
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/PurchaseHabitMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.customer.domain.PurchaseHabit;
+import org.dromara.customer.domain.vo.PurchaseHabitVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 客户采购习惯Mapper接口
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+public interface PurchaseHabitMapper extends BaseMapperPlus<PurchaseHabit, PurchaseHabitVo> {
+
+}

+ 70 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerContractService.java

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

+ 24 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerInfoService.java

@@ -2,11 +2,13 @@ package org.dromara.customer.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.dromara.customer.domain.CustomerInfo;
+import org.dromara.customer.domain.vo.ContractVo;
 import org.dromara.customer.domain.vo.CustomerInfoVo;
 import org.dromara.customer.domain.bo.CustomerInfoBo;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
 
+import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.List;
 
@@ -16,7 +18,7 @@ import java.util.List;
  * @author LionLi
  * @date 2025-12-11
  */
-public interface ICustomerInfoService extends IService<CustomerInfo>{
+public interface ICustomerInfoService extends IService<CustomerInfo> {
 
     /**
      * 查询客户信息
@@ -35,6 +37,15 @@ public interface ICustomerInfoService extends IService<CustomerInfo>{
      */
     TableDataInfo<CustomerInfoVo> queryPageList(CustomerInfoBo bo, PageQuery pageQuery);
 
+    /**
+     * 分页查询合同管理列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 客户信息分页列表
+     */
+    TableDataInfo<ContractVo> queryContractPageList(CustomerInfoBo bo, PageQuery pageQuery);
+
     /**
      * 查询符合条件的客户信息列表
      *
@@ -43,6 +54,7 @@ public interface ICustomerInfoService extends IService<CustomerInfo>{
      */
     List<CustomerInfoVo> queryList(CustomerInfoBo bo);
 
+
     /**
      * 新增客户信息
      *
@@ -59,6 +71,17 @@ public interface ICustomerInfoService extends IService<CustomerInfo>{
      */
     Boolean updateByBo(CustomerInfoBo bo);
 
+    /**
+     * 修改状态
+     */
+    int updateCustomerCheckStatus(Long customerId, String status);
+
+    /*修改临时额度*/
+    int updateCreditAmount(List<Long> customerIds, BigDecimal creditAmount);
+
+    /*设置客户标签*/
+    int setCustomerInfoTag(List<Long> customerIds, List<Long> tagIds);
+
     /**
      * 校验并批量删除客户信息信息
      *

+ 75 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerShippingAddressService.java

@@ -0,0 +1,75 @@
+package org.dromara.customer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.customer.domain.CustomerShippingAddress;
+import org.dromara.customer.domain.vo.CustomerShippingAddressVo;
+import org.dromara.customer.domain.bo.CustomerShippingAddressBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 客户收货地址Service接口
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+public interface ICustomerShippingAddressService extends IService<CustomerShippingAddress>{
+
+    /**
+     * 查询客户收货地址
+     *
+     * @param id 主键
+     * @return 客户收货地址
+     */
+    CustomerShippingAddressVo queryById(Long id);
+
+    /**
+     * 分页查询客户收货地址列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 客户收货地址分页列表
+     */
+    TableDataInfo<CustomerShippingAddressVo> queryPageList(CustomerShippingAddressBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的客户收货地址列表
+     *
+     * @param bo 查询条件
+     * @return 客户收货地址列表
+     */
+    List<CustomerShippingAddressVo> queryList(CustomerShippingAddressBo bo);
+
+    /**
+     * 新增客户收货地址
+     *
+     * @param bo 客户收货地址
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(CustomerShippingAddressBo bo);
+
+    /**
+     * 修改客户收货地址
+     *
+     * @param bo 客户收货地址
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(CustomerShippingAddressBo bo);
+
+    /**
+     * 设置默认地址
+     */
+    int changeDefaultAddress(CustomerShippingAddressBo bo);
+
+    /**
+     * 校验并批量删除客户收货地址信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 70 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IMaintenanceServerItemService.java

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

+ 70 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IMaintenanceServerTimeService.java

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

+ 70 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IMaintenanceTypeService.java

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

+ 72 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IPurchaseHabitService.java

@@ -0,0 +1,72 @@
+package org.dromara.customer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.customer.domain.PurchaseHabit;
+import org.dromara.customer.domain.vo.PurchaseHabitVo;
+import org.dromara.customer.domain.bo.PurchaseHabitBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 客户采购习惯Service接口
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+public interface IPurchaseHabitService extends IService<PurchaseHabit> {
+
+    /**
+     * 查询客户采购习惯
+     *
+     * @param id 主键
+     * @return 客户采购习惯
+     */
+    PurchaseHabitVo queryById(Long id);
+
+    /**
+     * 分页查询客户采购习惯列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 客户采购习惯分页列表
+     */
+    TableDataInfo<PurchaseHabitVo> queryPageList(PurchaseHabitBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的客户采购习惯列表
+     *
+     * @param bo 查询条件
+     * @return 客户采购习惯列表
+     */
+    List<PurchaseHabitVo> queryList(PurchaseHabitBo bo);
+
+    /**
+     * 新增客户采购习惯
+     *
+     * @param bo 客户采购习惯
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(PurchaseHabitBo bo);
+
+    /**
+     * 修改客户采购习惯
+     *
+     * @param bo 客户采购习惯
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(PurchaseHabitBo bo);
+
+    PurchaseHabitVo getCustomerPurchaseHabitData(String customerNo);
+
+    /**
+     * 校验并批量删除客户采购习惯信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 34 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/RedisContractNoGenerator.java

@@ -0,0 +1,34 @@
+package org.dromara.customer.service;
+
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+@Component
+public class RedisContractNoGenerator {
+
+    private final StringRedisTemplate redisTemplate;
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
+    private static final int MAX_SEQ = 999; // 最大序号
+
+    public RedisContractNoGenerator(StringRedisTemplate redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    public String generateContractNo(String prefix) {
+        String timestamp = LocalDateTime.now().format(FORMATTER);
+        String key = prefix + "CONTRACT_NO_SEQ:" + timestamp;
+
+        Long seq = redisTemplate.opsForValue().increment(key);
+        if (seq == null || seq > MAX_SEQ) {
+            throw new RuntimeException("同一秒内合同编号超过上限:" + MAX_SEQ);
+        }
+
+        // 设置过期时间(2秒)
+        redisTemplate.expire(key, 2, java.util.concurrent.TimeUnit.SECONDS);
+
+        return timestamp + String.format("%04d", seq);
+    }
+}

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

@@ -0,0 +1,152 @@
+package org.dromara.customer.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.customer.service.RedisContractNoGenerator;
+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.customer.domain.bo.CustomerContractBo;
+import org.dromara.customer.domain.vo.CustomerContractVo;
+import org.dromara.customer.domain.CustomerContract;
+import org.dromara.customer.mapper.CustomerContractMapper;
+import org.dromara.customer.service.ICustomerContractService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 客户合同Service业务层处理
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class CustomerContractServiceImpl extends ServiceImpl<CustomerContractMapper, CustomerContract> implements ICustomerContractService {
+
+    private final CustomerContractMapper baseMapper;
+
+    private final RedisContractNoGenerator redisContractNoGenerator;
+
+    /**
+     * 查询客户合同
+     *
+     * @param id 主键
+     * @return 客户合同
+     */
+    @Override
+    public CustomerContractVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询客户合同列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 客户合同分页列表
+     */
+    @Override
+    public TableDataInfo<CustomerContractVo> queryPageList(CustomerContractBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<CustomerContract> lqw = buildQueryWrapper(bo);
+        Page<CustomerContractVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的客户合同列表
+     *
+     * @param bo 查询条件
+     * @return 客户合同列表
+     */
+    @Override
+    public List<CustomerContractVo> queryList(CustomerContractBo bo) {
+        LambdaQueryWrapper<CustomerContract> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<CustomerContract> buildQueryWrapper(CustomerContractBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<CustomerContract> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(CustomerContract::getId);
+        lqw.eq(StringUtils.isNotBlank(bo.getCustomerNo()), CustomerContract::getCustomerNo, bo.getCustomerNo());
+        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());
+        lqw.eq(bo.getStartTime() != null, CustomerContract::getStartTime, bo.getStartTime());
+        lqw.eq(bo.getEndTime() != null, CustomerContract::getEndTime, bo.getEndTime());
+        lqw.eq(bo.getUploadTime() != null, CustomerContract::getUploadTime, bo.getUploadTime());
+        lqw.eq(StringUtils.isNotBlank(bo.getSettlementType()), CustomerContract::getSettlementType, bo.getSettlementType());
+        lqw.eq(StringUtils.isNotBlank(bo.getContractStatus()), CustomerContract::getContractStatus, bo.getContractStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getApprovalStatus()), CustomerContract::getApprovalStatus, bo.getApprovalStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getAnnex()), CustomerContract::getAnnex, bo.getAnnex());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), CustomerContract::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增客户合同
+     *
+     * @param bo 客户合同
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(CustomerContractBo bo) {
+        CustomerContract add = MapstructUtils.convert(bo, CustomerContract.class);
+        add.setContractNo(redisContractNoGenerator.generateContractNo(""));
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改客户合同
+     *
+     * @param bo 客户合同
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(CustomerContractBo bo) {
+        CustomerContract update = MapstructUtils.convert(bo, CustomerContract.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(CustomerContract entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除客户合同信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 220 - 20
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java

@@ -1,26 +1,28 @@
 package org.dromara.customer.service.impl;
 
 import cn.hutool.core.collection.CollUtil;
-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.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.dromara.common.core.context.PlatformContext;
+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.customer.domain.*;
 import org.dromara.customer.domain.bo.*;
 import org.dromara.customer.domain.vo.*;
 import org.dromara.customer.mapper.*;
-import org.springframework.stereotype.Service;
 import org.dromara.customer.service.ICustomerInfoService;
+import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
 import java.util.*;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -32,7 +34,7 @@ import java.util.stream.Collectors;
 @Slf4j
 @RequiredArgsConstructor
 @Service
-public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, CustomerInfo> implements ICustomerInfoService {
+public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, CustomerInfo> implements ICustomerInfoService {
 
     private final CustomerInfoMapper baseMapper;
     private final CustomerBusinessInfoMapper customerBusinessInfoMapper;
@@ -41,6 +43,8 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
     private final CustomerSalesInfoMapper customerSalesInfoMapper;
     private final IndustryCategoryMapper industryCategoryMapper;
     private final EnterpriseScaleMapper enterpriseScaleMapper;
+    private final CustomerInfoTagMapper customerInfoTagMapper;
+    private final CustomerContractMapper customerContractMapper;
 
 
     /**
@@ -50,7 +54,7 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
      * @return 客户信息
      */
     @Override
-    public CustomerInfoVo queryById(Long id){
+    public CustomerInfoVo queryById(Long id) {
         if (id == null || id <= 0) {
             return null;
         }
@@ -61,12 +65,12 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
             return null;
         }
         EnterpriseScaleVo enterpriseScaleVo = enterpriseScaleMapper.selectVoById(vo.getEnterpriseScaleId());
-        if (enterpriseScaleVo != null){
+        if (enterpriseScaleVo != null) {
             vo.setEnterpriseScale(enterpriseScaleVo.getEnterpriseScaleName());
         }
 
         IndustryCategoryVo industryCategoryVo = industryCategoryMapper.selectVoById(vo.getIndustryCategoryId());
-        if (industryCategoryVo != null){
+        if (industryCategoryVo != null) {
             vo.setIndustryCategory(industryCategoryVo.getIndustryCategoryName());
         }
         // 2. 查询关联信息
@@ -115,8 +119,8 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
         LambdaQueryWrapper<CustomerInfo> lqw = buildQueryWrapper(bo);
         Page<CustomerInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
         List<CustomerInfoVo> records = result.getRecords();
-        if (CollUtil.isNotEmpty( records)){
-            Set<Long>industryIds = records.stream()
+        if (CollUtil.isNotEmpty(records)) {
+            Set<Long> industryIds = records.stream()
                 .map(CustomerInfoVo::getIndustryCategoryId)
                 .filter(Objects::nonNull)
                 .collect(Collectors.toSet());
@@ -137,6 +141,124 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
         return TableDataInfo.build(result);
     }
 
+    @Override
+    public TableDataInfo<ContractVo> queryContractPageList(CustomerInfoBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<CustomerInfo> lqw = buildQueryWrapper(bo);
+
+        Page<CustomerInfo> customerPage = baseMapper.selectPage(pageQuery.build(), lqw);
+        List<CustomerInfo> customerList = customerPage.getRecords();
+
+        if (CollUtil.isEmpty(customerList)) {
+            return new TableDataInfo<>(new ArrayList<>(), 0);
+        }
+
+        List<Long> customerIdList = customerList.stream()
+            .map(CustomerInfo::getId)
+            .collect(Collectors.toList());
+
+        List<Long> industryIdList = customerList.stream()
+            .map(CustomerInfo::getIndustryCategoryId)
+            .filter(Objects::nonNull) // 防止空指针
+            .distinct() // 去重,减少调用数据量
+            .collect(Collectors.toList());
+
+        final Map<Long, String> industryCategoryMap = new HashMap<>();
+
+        if (!industryIdList.isEmpty()) {
+            List<IndustryCategoryVo> industryList = industryCategoryMapper.selectVoByIds(industryIdList);
+            industryCategoryMap.putAll(industryList.stream()
+                .collect(Collectors.toMap(
+                    IndustryCategoryVo::getId,
+                    vo -> Optional.ofNullable(vo.getIndustryCategoryName()).orElse(""),
+                    (v1, v2) -> v1
+                ))
+            );
+        }
+
+        // 获取合同统计
+        Map<Long, ContractVo> contractStatMap = getContractStatsByCustomerIds(customerIdList);
+
+        // 获取部门名称映射
+        // Map<Long, String> deptNameMap = getDeptNameMapByCustomerIds(customerIdList);
+
+
+        // 构造最终返回数据
+        // 构造 VO
+        List<ContractVo> voList = customerList.stream().map(customer -> {
+            ContractVo vo = new ContractVo();
+            vo.setCustomerId(customer.getId());
+            vo.setCustomerNo(customer.getCustomerNo());
+
+            // ⚠️ 确认这里字段是否存在!
+            vo.setCustomerName(customer.getBusinessCustomerName()); // 或 customer.getShortName()
+
+            vo.setIndustryCategoryId(customer.getIndustryCategoryId());
+            vo.setIndustryCategory(industryCategoryMap.getOrDefault(customer.getIndustryCategoryId(), ""));
+
+            ContractVo stat = contractStatMap.getOrDefault(customer.getId(), new ContractVo());
+            vo.setContractCount(Optional.ofNullable(stat.getContractCount()).orElse(0));
+            vo.setEffectiveContract(Optional.ofNullable(stat.getEffectiveContract()).orElse(0));
+            vo.setInvalidContract(Optional.ofNullable(stat.getInvalidContract()).orElse(0));
+            vo.setVoidedContract(Optional.ofNullable(stat.getVoidedContract()).orElse(0));
+
+            return vo;
+        }).collect(Collectors.toList());
+
+        return new TableDataInfo<>(voList, customerPage.getTotal());
+    }
+
+    private Map<Long, ContractVo> getContractStatsByCustomerIds(List<Long> customerIdList) {
+        if (CollUtil.isEmpty(customerIdList)) {
+            return new HashMap<>();
+        }
+
+        // 调用自定义 SQL
+        List<Map<String, Object>> stats = customerContractMapper.selectContractStatsByCustomerIds(customerIdList);
+
+        Map<Long, ContractVo> result = new HashMap<>();
+
+        for (Map<String, Object> row : stats) {
+            Long customerId = (Long) row.get("customer_id");
+            ContractVo vo = new ContractVo();
+
+            // 安全获取数值:防止 null
+            vo.setContractCount(getIntValue(row.get("total")));
+            vo.setEffectiveContract(getIntValue(row.get("effective")));
+            vo.setInvalidContract(getIntValue(row.get("invalid")));
+            vo.setVoidedContract(getIntValue(row.get("voided")));
+
+            result.put(customerId, vo);
+        }
+
+        // 补全缺失的客户(无合同的客户)
+        for (Long id : customerIdList) {
+            result.putIfAbsent(id, createEmptyContractVo());
+        }
+
+        return result;
+    }
+
+    // 工具方法:安全转换 Object -> Integer
+    private int getIntValue(Object value) {
+        if (value == null) return 0;
+        if (value instanceof BigDecimal) {
+            return ((BigDecimal) value).intValue();
+        }
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        return 0;
+    }
+
+    private ContractVo createEmptyContractVo() {
+        ContractVo vo = new ContractVo();
+        vo.setContractCount(0);
+        vo.setEffectiveContract(0);
+        vo.setInvalidContract(0);
+        vo.setVoidedContract(0);
+        return vo;
+    }
+
     /**
      * 查询符合条件的客户信息列表
      *
@@ -156,6 +278,7 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
         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());
+        lqw.like(StringUtils.isNotBlank(bo.getCustomerName()), CustomerInfo::getCustomerName, bo.getCustomerName());
         lqw.like(StringUtils.isNotBlank(bo.getBusinessCustomerName()), CustomerInfo::getBusinessCustomerName, bo.getBusinessCustomerName());
         lqw.like(StringUtils.isNotBlank(bo.getShortName()), CustomerInfo::getShortName, bo.getShortName());
         lqw.eq(bo.getInvoiceTypeId() != null, CustomerInfo::getInvoiceTypeId, bo.getInvoiceTypeId());
@@ -227,8 +350,6 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
             customerBusinessInfoMapper.deleteByCustomerId(customerId);
             // 删除销售信息(一对一)
             customerSalesInfoMapper.deleteByCustomerId(customerId);
-            // 删除联系人(一对多)
-            customerContactMapper.deleteByCustomerId(customerId);
             // 删除发票信息(一对多)
             customerInvoiceInfoMapper.deleteByCustomerId(customerId);
         }
@@ -295,7 +416,7 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
         // 2. 删除旧的关联数据
         customerBusinessInfoMapper.deleteByCustomerId(customerId);
         customerSalesInfoMapper.deleteByCustomerId(customerId);
-        customerContactMapper.deleteByCustomerId(customerId);
+//        customerContactMapper.deleteByCustomerId(customerId);
         customerInvoiceInfoMapper.deleteByCustomerId(customerId);
 
         // 3. 插入新的关联数据
@@ -305,7 +426,7 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
     }
 
     /**
-     * 保存客户的关联信息(不包含删除逻辑,仅插入)
+     * 保存客户的关联信息
      */
     private void saveAssociatedData(Long customerId, CustomerInfoBo bo) {
         // 业务信息
@@ -341,10 +462,89 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
         }
     }
 
+    @Override
+    public int updateCustomerCheckStatus(Long customerId, String status) {
+        return baseMapper.update(null,
+            new LambdaUpdateWrapper<CustomerInfo>()
+                .set(CustomerInfo::getStatus, status)
+                .eq(CustomerInfo::getId, customerId));
+    }
+
+    @Override
+    public int updateCreditAmount(List<Long> customerIds, BigDecimal creditAmount) {
+        if (customerIds == null || customerIds.isEmpty()) {
+            return 0; // 避免无效更新
+        }
+        return customerSalesInfoMapper.update(
+            null,
+            new LambdaUpdateWrapper<CustomerSalesInfo>()
+                .set(CustomerSalesInfo::getCreditAmount, creditAmount)
+                .in(CustomerSalesInfo::getCustomerId, customerIds)
+        );
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int setCustomerInfoTag(List<Long> customerIds, List<Long> tagIds) {
+        if (CollUtil.isEmpty(customerIds) || CollUtil.isEmpty(tagIds)) {
+            return 0;
+        }
+        String platform = PlatformContext.getPlatform();
+        this.insertCustomerInfoTag(customerIds, tagIds, false, platform);
+        return 1;
+    }
+
+    private void insertCustomerInfoTag(List<Long> customerIdList, List<Long> tagIdList, boolean clear, String platformCode) {
+
+
+        // 1. 如果 clear = true,则删除这些客户在该平台下的所有标签
+        if (clear) {
+            customerInfoTagMapper.delete(
+                new LambdaQueryWrapper<CustomerInfoTag>()
+                    .in(CustomerInfoTag::getCustomerId, customerIdList)
+                    .eq(CustomerInfoTag::getPlatformCode, platformCode)
+            );
+        }
+
+        // 2. 查询这些客户当前已有的(客户ID, 标签ID)组合,避免重复插入
+        List<CustomerInfoTag> existingList = customerInfoTagMapper.selectList(
+            new LambdaQueryWrapper<CustomerInfoTag>()
+                .in(CustomerInfoTag::getCustomerId, customerIdList)
+                .in(CustomerInfoTag::getTagId, tagIdList)
+                .eq(CustomerInfoTag::getPlatformCode, platformCode)
+        );
+
+        // 构建已存在的 (customerId, tagId) 集合,用于去重
+        Set<String> existingPairs = existingList.stream()
+            .map(cit -> cit.getCustomerId() + "_" + cit.getTagId())
+            .collect(Collectors.toSet());
+
+        // 3. 构造需要新增的记录(笛卡尔积:每个客户 × 每个标签)
+        List<CustomerInfoTag> toInsertList = new ArrayList<>();
+        for (Long customerId : customerIdList) {
+            for (Long tagId : tagIdList) {
+                String pair = customerId + "_" + tagId;
+                if (!existingPairs.contains(pair)) {
+                    CustomerInfoTag cit = new CustomerInfoTag();
+                    cit.setCustomerId(customerId);
+                    cit.setTagId(tagId);
+                    cit.setPlatformCode(platformCode);
+                    toInsertList.add(cit);
+                }
+            }
+        }
+
+        // 4. 批量插入
+        if (!toInsertList.isEmpty()) {
+            customerInfoTagMapper.insertBatch(toInsertList);
+        }
+    }
+
+
     /**
      * 保存前的数据校验
      */
-    private void validEntityBeforeSave(CustomerInfo entity){
+    private void validEntityBeforeSave(CustomerInfo entity) {
         //TODO 做一些数据校验,如唯一约束
     }
 
@@ -357,7 +557,7 @@ public class CustomerInfoServiceImpl  extends ServiceImpl<CustomerInfoMapper, Cu
      */
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
+        if (isValid) {
             //TODO 做一些业务上的校验,判断是否需要校验
         }
         return baseMapper.deleteByIds(ids) > 0;

+ 189 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerShippingAddressServiceImpl.java

@@ -0,0 +1,189 @@
+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.dromara.common.core.enums.IsDefault;
+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.customer.domain.CustomerShippingAddress;
+import org.dromara.customer.domain.bo.CustomerShippingAddressBo;
+import org.dromara.customer.domain.vo.CustomerShippingAddressVo;
+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.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 客户收货地址Service业务层处理
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class CustomerShippingAddressServiceImpl extends ServiceImpl<CustomerShippingAddressMapper, CustomerShippingAddress> implements ICustomerShippingAddressService {
+
+    private final CustomerShippingAddressMapper baseMapper;
+
+    /**
+     * 查询客户收货地址
+     *
+     * @param id 主键
+     * @return 客户收货地址
+     */
+    @Override
+    public CustomerShippingAddressVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询客户收货地址列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 客户收货地址分页列表
+     */
+    @Override
+    public TableDataInfo<CustomerShippingAddressVo> queryPageList(CustomerShippingAddressBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<CustomerShippingAddress> lqw = buildQueryWrapper(bo);
+        Page<CustomerShippingAddressVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的客户收货地址列表
+     *
+     * @param bo 查询条件
+     * @return 客户收货地址列表
+     */
+    @Override
+    public List<CustomerShippingAddressVo> queryList(CustomerShippingAddressBo bo) {
+        LambdaQueryWrapper<CustomerShippingAddress> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<CustomerShippingAddress> buildQueryWrapper(CustomerShippingAddressBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<CustomerShippingAddress> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(CustomerShippingAddress::getId);
+        lqw.eq(bo.getCustomerId() != null, CustomerShippingAddress::getCustomerId, bo.getCustomerId());
+        lqw.eq(StringUtils.isNotBlank(bo.getShippingAddressNo()), CustomerShippingAddress::getShippingAddressNo, bo.getShippingAddressNo());
+        lqw.eq(StringUtils.isNotBlank(bo.getConsignee()), CustomerShippingAddress::getConsignee, bo.getConsignee());
+        lqw.eq(bo.getDeptId() != null, CustomerShippingAddress::getDeptId, bo.getDeptId());
+        lqw.eq(StringUtils.isNotBlank(bo.getPhone()), CustomerShippingAddress::getPhone, bo.getPhone());
+        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), CustomerShippingAddress::getAddress, bo.getAddress());
+        lqw.eq(StringUtils.isNotBlank(bo.getPostal()), CustomerShippingAddress::getPostal, bo.getPostal());
+        lqw.eq(StringUtils.isNotBlank(bo.getDefaultAddress()), CustomerShippingAddress::getDefaultAddress, bo.getDefaultAddress());
+        lqw.eq(StringUtils.isNotBlank(bo.getAddressLabel()), CustomerShippingAddress::getAddressLabel, bo.getAddressLabel());
+        lqw.eq(StringUtils.isNotBlank(bo.getProvincialNo()), CustomerShippingAddress::getProvincialNo, bo.getProvincialNo());
+        lqw.eq(StringUtils.isNotBlank(bo.getCityNo()), CustomerShippingAddress::getCityNo, bo.getCityNo());
+        lqw.eq(StringUtils.isNotBlank(bo.getCountryNo()), CustomerShippingAddress::getCountryNo, bo.getCountryNo());
+        lqw.eq(StringUtils.isNotBlank(bo.getProvincialCityCountry()), CustomerShippingAddress::getProvincialCityCountry, bo.getProvincialCityCountry());
+        lqw.eq(StringUtils.isNotBlank(bo.getPushStatus()), CustomerShippingAddress::getPushStatus, bo.getPushStatus());
+        lqw.eq(bo.getNum() != null, CustomerShippingAddress::getNum, bo.getNum());
+        lqw.like(StringUtils.isNotBlank(bo.getDeptName()), CustomerShippingAddress::getDeptName, bo.getDeptName());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), CustomerShippingAddress::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), CustomerShippingAddress::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增客户收货地址
+     *
+     * @param bo 客户收货地址
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(CustomerShippingAddressBo bo) {
+        CustomerShippingAddress add = MapstructUtils.convert(bo, CustomerShippingAddress.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改客户收货地址
+     *
+     * @param bo 客户收货地址
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(CustomerShippingAddressBo bo) {
+        CustomerShippingAddress update = MapstructUtils.convert(bo, CustomerShippingAddress.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    @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;
+        }
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(CustomerShippingAddress entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除客户收货地址信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 45 - 6
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/MaintainInfoServiceImpl.java

@@ -16,15 +16,15 @@ import org.dromara.customer.domain.bo.MaintainInfoBo;
 import org.dromara.customer.domain.dto.TechnicalAdviserDto;
 import org.dromara.customer.domain.vo.CustomerInfoVo;
 import org.dromara.customer.domain.vo.MaintainInfoVo;
+import org.dromara.customer.domain.vo.MaintenanceServerItemVo;
 import org.dromara.customer.mapper.CustomerInfoMapper;
 import org.dromara.customer.mapper.MaintainInfoMapper;
+import org.dromara.customer.mapper.MaintenanceServerItemMapper;
 import org.dromara.customer.service.IMaintainInfoService;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -42,6 +42,8 @@ public class MaintainInfoServiceImpl  extends ServiceImpl<MaintainInfoMapper, Ma
 
     private final CustomerInfoMapper customerInfoMapper;
 
+    private final MaintenanceServerItemMapper maintenanceServerItemMapper;
+
     /**
      * 查询维保记录
      *
@@ -71,7 +73,7 @@ public class MaintainInfoServiceImpl  extends ServiceImpl<MaintainInfoMapper, Ma
         Page<MaintainInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
         List<MaintainInfoVo> records = result.getRecords();
         if (CollUtil.isNotEmpty(records)) {
-            Set<Long>customerIds = records.stream().map(MaintainInfoVo::getCustomerId).collect(Collectors.toSet());
+            Set<Long> customerIds = records.stream().map(MaintainInfoVo::getCustomerId).collect(Collectors.toSet());
             List<CustomerInfoVo> industryList = customerInfoMapper.selectVoByIds(customerIds);
 
             Map<Long, String> customerMap = industryList.stream()
@@ -80,8 +82,44 @@ public class MaintainInfoServiceImpl  extends ServiceImpl<MaintainInfoMapper, Ma
                     CustomerInfoVo::getCompanyName,
                     (existing, replacement) -> existing
                 ));
+            Set<Long> itemIds = records.stream()
+                .map(MaintainInfoVo::getServiceContent)
+                .filter(content -> content != null && !content.trim().isEmpty())
+                .flatMap(content -> Arrays.stream(content.split(",")))
+                .map(String::trim)
+                .filter(idStr -> !idStr.isEmpty())
+                .map(Long::parseLong)
+                .collect(Collectors.toSet());
+
+            List<MaintenanceServerItemVo> itemList = maintenanceServerItemMapper.selectVoByIds(itemIds);
+
+            Map<Long, String> itemMap = itemList.stream()
+                .collect(Collectors.toMap(
+                    MaintenanceServerItemVo::getId,
+                    MaintenanceServerItemVo::getItemName,
+                    (existing, replacement) -> existing // 重复 key 保留第一个
+                ));
+
             records.forEach(vo -> {
+                // 设置客户名称
                 vo.setCustomerName(customerMap.get(vo.getCustomerId()));
+
+                String serviceContent = vo.getServiceContent();
+                if (serviceContent != null && !serviceContent.trim().isEmpty()) {
+                    String names = Arrays.stream(serviceContent.split(","))
+                        .map(String::trim)
+                        .filter(idStr -> !idStr.isEmpty())
+                        .map(idStr -> {
+                            Long id = Long.parseLong(idStr);
+                            return itemMap.getOrDefault(id, "未知项(ID:" + id + ")");
+                        })
+                        .collect(Collectors.joining(","));
+                    vo.setServiceContentStr(names);
+                } else {
+                    vo.setServiceContentStr("");
+                }
+
+                // 设置维保次数
                 vo.setMaintainCount(baseMapper.selectMaintainCountByCustomerId(vo.getCustomerId()));
             });
         }
@@ -138,6 +176,7 @@ public class MaintainInfoServiceImpl  extends ServiceImpl<MaintainInfoMapper, Ma
      * @return 是否新增成功
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean insertByBo(MaintainInfoBo bo) {
         MaintainInfo add = MapstructUtils.convert(bo, MaintainInfo.class);
         validEntityBeforeSave(add);
@@ -155,7 +194,6 @@ public class MaintainInfoServiceImpl  extends ServiceImpl<MaintainInfoMapper, Ma
         maintainInfo.setTechnicalAdviser(bo.getTechnicalAdviser());
         maintainInfo.setTechnicalAdviserPhone(bo.getTechnicalAdviserPhone());
 
-
         return baseMapper.updateById(maintainInfo)>0;
     }
 
@@ -166,6 +204,7 @@ public class MaintainInfoServiceImpl  extends ServiceImpl<MaintainInfoMapper, Ma
      * @return 是否修改成功
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean updateByBo(MaintainInfoBo bo) {
         MaintainInfo update = MapstructUtils.convert(bo, MaintainInfo.class);
         validEntityBeforeSave(update);

+ 135 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/MaintenanceServerItemServiceImpl.java

@@ -0,0 +1,135 @@
+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 lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.customer.domain.bo.MaintenanceServerItemBo;
+import org.dromara.customer.domain.vo.MaintenanceServerItemVo;
+import org.dromara.customer.domain.MaintenanceServerItem;
+import org.dromara.customer.mapper.MaintenanceServerItemMapper;
+import org.dromara.customer.service.IMaintenanceServerItemService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 维保服务内容Service业务层处理
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class MaintenanceServerItemServiceImpl  extends ServiceImpl<MaintenanceServerItemMapper, MaintenanceServerItem> implements IMaintenanceServerItemService {
+
+    private final MaintenanceServerItemMapper baseMapper;
+
+    /**
+     * 查询维保服务内容
+     *
+     * @param id 主键
+     * @return 维保服务内容
+     */
+    @Override
+    public MaintenanceServerItemVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询维保服务内容列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 维保服务内容分页列表
+     */
+    @Override
+    public TableDataInfo<MaintenanceServerItemVo> queryPageList(MaintenanceServerItemBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<MaintenanceServerItem> lqw = buildQueryWrapper(bo);
+        Page<MaintenanceServerItemVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的维保服务内容列表
+     *
+     * @param bo 查询条件
+     * @return 维保服务内容列表
+     */
+    @Override
+    public List<MaintenanceServerItemVo> queryList(MaintenanceServerItemBo bo) {
+        LambdaQueryWrapper<MaintenanceServerItem> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<MaintenanceServerItem> buildQueryWrapper(MaintenanceServerItemBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<MaintenanceServerItem> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(MaintenanceServerItem::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getItemName()), MaintenanceServerItem::getItemName, bo.getItemName());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), MaintenanceServerItem::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), MaintenanceServerItem::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增维保服务内容
+     *
+     * @param bo 维保服务内容
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(MaintenanceServerItemBo bo) {
+        MaintenanceServerItem add = MapstructUtils.convert(bo, MaintenanceServerItem.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改维保服务内容
+     *
+     * @param bo 维保服务内容
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(MaintenanceServerItemBo bo) {
+        MaintenanceServerItem update = MapstructUtils.convert(bo, MaintenanceServerItem.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(MaintenanceServerItem entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除维保服务内容信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 135 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/MaintenanceServerTimeServiceImpl.java

@@ -0,0 +1,135 @@
+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 lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.customer.domain.bo.MaintenanceServerTimeBo;
+import org.dromara.customer.domain.vo.MaintenanceServerTimeVo;
+import org.dromara.customer.domain.MaintenanceServerTime;
+import org.dromara.customer.mapper.MaintenanceServerTimeMapper;
+import org.dromara.customer.service.IMaintenanceServerTimeService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 维保服务时间Service业务层处理
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class MaintenanceServerTimeServiceImpl  extends ServiceImpl<MaintenanceServerTimeMapper, MaintenanceServerTime> implements IMaintenanceServerTimeService {
+
+    private final MaintenanceServerTimeMapper baseMapper;
+
+    /**
+     * 查询维保服务时间
+     *
+     * @param id 主键
+     * @return 维保服务时间
+     */
+    @Override
+    public MaintenanceServerTimeVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询维保服务时间列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 维保服务时间分页列表
+     */
+    @Override
+    public TableDataInfo<MaintenanceServerTimeVo> queryPageList(MaintenanceServerTimeBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<MaintenanceServerTime> lqw = buildQueryWrapper(bo);
+        Page<MaintenanceServerTimeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的维保服务时间列表
+     *
+     * @param bo 查询条件
+     * @return 维保服务时间列表
+     */
+    @Override
+    public List<MaintenanceServerTimeVo> queryList(MaintenanceServerTimeBo bo) {
+        LambdaQueryWrapper<MaintenanceServerTime> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<MaintenanceServerTime> buildQueryWrapper(MaintenanceServerTimeBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<MaintenanceServerTime> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(MaintenanceServerTime::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getTimeName()), MaintenanceServerTime::getTimeName, bo.getTimeName());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), MaintenanceServerTime::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), MaintenanceServerTime::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增维保服务时间
+     *
+     * @param bo 维保服务时间
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(MaintenanceServerTimeBo bo) {
+        MaintenanceServerTime add = MapstructUtils.convert(bo, MaintenanceServerTime.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改维保服务时间
+     *
+     * @param bo 维保服务时间
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(MaintenanceServerTimeBo bo) {
+        MaintenanceServerTime update = MapstructUtils.convert(bo, MaintenanceServerTime.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(MaintenanceServerTime entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除维保服务时间信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 135 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/MaintenanceTypeServiceImpl.java

@@ -0,0 +1,135 @@
+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 lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.customer.domain.bo.MaintenanceTypeBo;
+import org.dromara.customer.domain.vo.MaintenanceTypeVo;
+import org.dromara.customer.domain.MaintenanceType;
+import org.dromara.customer.mapper.MaintenanceTypeMapper;
+import org.dromara.customer.service.IMaintenanceTypeService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 维保类型Service业务层处理
+ *
+ * @author LionLi
+ * @date 2025-12-15
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class MaintenanceTypeServiceImpl  extends ServiceImpl<MaintenanceTypeMapper, MaintenanceType> implements IMaintenanceTypeService {
+
+    private final MaintenanceTypeMapper baseMapper;
+
+    /**
+     * 查询维保类型
+     *
+     * @param id 主键
+     * @return 维保类型
+     */
+    @Override
+    public MaintenanceTypeVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询维保类型列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 维保类型分页列表
+     */
+    @Override
+    public TableDataInfo<MaintenanceTypeVo> queryPageList(MaintenanceTypeBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<MaintenanceType> lqw = buildQueryWrapper(bo);
+        Page<MaintenanceTypeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的维保类型列表
+     *
+     * @param bo 查询条件
+     * @return 维保类型列表
+     */
+    @Override
+    public List<MaintenanceTypeVo> queryList(MaintenanceTypeBo bo) {
+        LambdaQueryWrapper<MaintenanceType> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<MaintenanceType> buildQueryWrapper(MaintenanceTypeBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<MaintenanceType> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(MaintenanceType::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getTypeName()), MaintenanceType::getTypeName, bo.getTypeName());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), MaintenanceType::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), MaintenanceType::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增维保类型
+     *
+     * @param bo 维保类型
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(MaintenanceTypeBo bo) {
+        MaintenanceType add = MapstructUtils.convert(bo, MaintenanceType.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改维保类型
+     *
+     * @param bo 维保类型
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(MaintenanceTypeBo bo) {
+        MaintenanceType update = MapstructUtils.convert(bo, MaintenanceType.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(MaintenanceType entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除维保类型信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 154 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/PurchaseHabitServiceImpl.java

@@ -0,0 +1,154 @@
+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 lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.customer.domain.bo.PurchaseHabitBo;
+import org.dromara.customer.domain.vo.PurchaseHabitVo;
+import org.dromara.customer.domain.PurchaseHabit;
+import org.dromara.customer.mapper.PurchaseHabitMapper;
+import org.dromara.customer.service.IPurchaseHabitService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 客户采购习惯Service业务层处理
+ *
+ * @author LionLi
+ * @date 2025-12-16
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class PurchaseHabitServiceImpl extends ServiceImpl<PurchaseHabitMapper, PurchaseHabit> implements IPurchaseHabitService {
+
+    private final PurchaseHabitMapper baseMapper;
+
+    /**
+     * 查询客户采购习惯
+     *
+     * @param id 主键
+     * @return 客户采购习惯
+     */
+    @Override
+    public PurchaseHabitVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询客户采购习惯列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 客户采购习惯分页列表
+     */
+    @Override
+    public TableDataInfo<PurchaseHabitVo> queryPageList(PurchaseHabitBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<PurchaseHabit> lqw = buildQueryWrapper(bo);
+        Page<PurchaseHabitVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的客户采购习惯列表
+     *
+     * @param bo 查询条件
+     * @return 客户采购习惯列表
+     */
+    @Override
+    public List<PurchaseHabitVo> queryList(PurchaseHabitBo bo) {
+        LambdaQueryWrapper<PurchaseHabit> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<PurchaseHabit> buildQueryWrapper(PurchaseHabitBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<PurchaseHabit> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(PurchaseHabit::getId);
+        lqw.eq(StringUtils.isNotBlank(bo.getCustomerNo()), PurchaseHabit::getCustomerNo, bo.getCustomerNo());
+        lqw.eq(bo.getMonthPurchase() != null, PurchaseHabit::getMonthPurchase, bo.getMonthPurchase());
+        lqw.eq(bo.getYearPurchase() != null, PurchaseHabit::getYearPurchase, bo.getYearPurchase());
+        lqw.eq(StringUtils.isNotBlank(bo.getPermanentOfficer()), PurchaseHabit::getPermanentOfficer, bo.getPermanentOfficer());
+        lqw.eq(StringUtils.isNotBlank(bo.getChoiceModel()), PurchaseHabit::getChoiceModel, bo.getChoiceModel());
+        lqw.eq(StringUtils.isNotBlank(bo.getPrintAmount()), PurchaseHabit::getPrintAmount, bo.getPrintAmount());
+        lqw.eq(StringUtils.isNotBlank(bo.getBuyOriginal()), PurchaseHabit::getBuyOriginal, bo.getBuyOriginal());
+        lqw.eq(StringUtils.isNotBlank(bo.getTechnologyService()), PurchaseHabit::getTechnologyService, bo.getTechnologyService());
+        lqw.eq(StringUtils.isNotBlank(bo.getPurchaseCategory()), PurchaseHabit::getPurchaseCategory, bo.getPurchaseCategory());
+        lqw.eq(StringUtils.isNotBlank(bo.getOtherCategory()), PurchaseHabit::getOtherCategory, bo.getOtherCategory());
+        lqw.eq(StringUtils.isNotBlank(bo.getAdaptScene()), PurchaseHabit::getAdaptScene, bo.getAdaptScene());
+        lqw.eq(StringUtils.isNotBlank(bo.getOtherScene()), PurchaseHabit::getOtherScene, bo.getOtherScene());
+        lqw.eq(StringUtils.isNotBlank(bo.getCustomizeDemand()), PurchaseHabit::getCustomizeDemand, bo.getCustomizeDemand());
+        lqw.eq(StringUtils.isNotBlank(bo.getOtherCustomize()), PurchaseHabit::getOtherCustomize, bo.getOtherCustomize());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), PurchaseHabit::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增客户采购习惯
+     *
+     * @param bo 客户采购习惯
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(PurchaseHabitBo bo) {
+        PurchaseHabit add = MapstructUtils.convert(bo, PurchaseHabit.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改客户采购习惯
+     *
+     * @param bo 客户采购习惯
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(PurchaseHabitBo bo) {
+        PurchaseHabit update = MapstructUtils.convert(bo, PurchaseHabit.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    public PurchaseHabitVo getCustomerPurchaseHabitData(String customerNo) {
+        return baseMapper.selectVoOne(Wrappers.lambdaQuery(PurchaseHabit.class)
+            .eq(PurchaseHabit::getCustomerNo, customerNo)
+            .orderByDesc(PurchaseHabit::getCreateTime)
+            .last("LIMIT 1"));
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(PurchaseHabit entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除客户采购习惯信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 21 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CustomerContractMapper.xml

@@ -0,0 +1,21 @@
+<?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.customer.mapper.CustomerContractMapper">
+    <select id="selectContractStatsByCustomerIds" resultType="map">
+        SELECT
+        customer_id,
+        COUNT(*) AS total,
+        SUM(CASE WHEN contract_status = '1' THEN 1 ELSE 0 END) AS effective,
+        SUM(CASE WHEN contract_status = '2' THEN 1 ELSE 0 END) AS invalid,
+        SUM(CASE WHEN contract_status = '3' THEN 1 ELSE 0 END) AS voided
+        FROM customer_contract
+        WHERE del_flag = '0'
+        AND customer_id IN
+        <foreach collection="customerIdList" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+        GROUP BY customer_id
+    </select>
+</mapper>

+ 12 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CustomerInfoTagMapper.xml

@@ -0,0 +1,12 @@
+<?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.customer.mapper.CustomerInfoTagMapper">
+    <select id="selectCustomerIdsByTagId" resultType="Long">
+        select ci.id
+        from customer_info ci
+                 inner join customer_info_tag cit
+                            on ci.id = cit.customer_id and cit.tag_id = #{tagId}
+    </select>
+</mapper>

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CustomerShippingAddressMapper.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.customer.mapper.CustomerShippingAddressMapper">
+
+</mapper>

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/MaintenanceServerItemMapper.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.customer.mapper.MaintenanceServerItemMapper">
+
+</mapper>

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/MaintenanceServerTimeMapper.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.customer.mapper.MaintenanceServerTimeMapper">
+
+</mapper>

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/MaintenanceTypeMapper.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.customer.mapper.MaintenanceTypeMapper">
+
+</mapper>

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/PurchaseHabitMapper.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.customer.mapper.PurchaseHabitMapper">
+
+</mapper>