Преглед на файлове

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISupplierInfoService.java
#	ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SupplierInfoServiceImpl.java
Lijingyang преди 2 месеца
родител
ревизия
5dc26569a0
променени са 95 файла, в които са добавени 3395 реда и са изтрити 258 реда
  1. 12 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemotePartnerInfoService.java
  2. 8 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemoteSupplierInfoService.java
  3. 10 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteComStaffService.java
  4. 64 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteComStaffVo.java
  5. 26 0
      ruoyi-auth/src/main/java/org/dromara/auth/controller/TokenController.java
  6. 19 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/IAuthStrategy.java
  7. 12 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/EmailAuthStrategy.java
  8. 12 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/PasswordAuthStrategy.java
  9. 12 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SmsAuthStrategy.java
  10. 12 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SocialAuthStrategy.java
  11. 12 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/XcxAuthStrategy.java
  12. 23 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/InvoiceStatus.java
  13. 39 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/StatementStatus.java
  14. 6 1
      ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
  15. 0 1
      ruoyi-modules/pom.xml
  16. 7 2
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/domain/StatementDetail.java
  17. 2 0
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/domain/bo/StatementDetailBo.java
  18. 2 0
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/domain/vo/StatementDetailVo.java
  19. 69 0
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/pc/controller/PcStatementDetailController.java
  20. 58 0
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/pc/controller/PcStatementInvoiceController.java
  21. 147 0
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/pc/controller/PcStatementOrderController.java
  22. 103 0
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/pc/controller/PcStatementProductController.java
  23. 10 0
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/service/IStatementOrderService.java
  24. 6 2
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/service/impl/StatementInvoiceServiceImpl.java
  25. 24 6
      ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/service/impl/StatementOrderServiceImpl.java
  26. 31 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/pc/CustomerRegisterController.java
  27. 4 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerContact.java
  28. 1 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerDept.java
  29. 6 2
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContactBo.java
  30. 1 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerDeptBo.java
  31. 35 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerRegisterBo.java
  32. 4 2
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContactVo.java
  33. 2 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerDeptTreeVo.java
  34. 3 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerDeptVo.java
  35. 25 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemotePartnerInfoServiceImpl.java
  36. 9 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemoteSupplierInfoServiceImpl.java
  37. 118 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/pc/controller/PcServicePersonController.java
  38. 6 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerInfoService.java
  39. 6 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IPartnerInfoService.java
  40. 7 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISupplierInfoService.java
  41. 53 26
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerDeptServiceImpl.java
  42. 125 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java
  43. 5 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerShippingAddressServiceImpl.java
  44. 32 7
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/PartnerInfoServiceImpl.java
  45. 84 116
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SupplierInfoServiceImpl.java
  46. 31 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/OrderDeliverController.java
  47. 12 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/OrderMainController.java
  48. 106 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/OrderOperateLogController.java
  49. 5 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderAssignment.java
  50. 12 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderDeliverProduct.java
  51. 57 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderOperateLog.java
  52. 6 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderProduct.java
  53. 12 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderDeliverProductBo.java
  54. 49 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderOperateLogBo.java
  55. 6 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderProductBo.java
  56. 12 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/dto/AssignmentStatsDto.java
  57. 13 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderAssignmentVo.java
  58. 13 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderDeliverProductVo.java
  59. 15 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderDeliverVo.java
  60. 62 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderOperateLogVo.java
  61. 6 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderProductVo.java
  62. 1 1
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/mapper/OrderDeliverMapper.java
  63. 15 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/mapper/OrderOperateLogMapper.java
  64. 19 1
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/mapper/OrderProductMapper.java
  65. 171 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/pc/controller/PcOrderController.java
  66. 144 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/pc/controller/PcOrderReturnController.java
  67. 14 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderDeliverService.java
  68. 2 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderMainService.java
  69. 70 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderOperateLogService.java
  70. 154 37
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderAssignmentServiceImpl.java
  71. 132 5
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderDeliverServiceImpl.java
  72. 37 18
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderMainServiceImpl.java
  73. 136 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderOperateLogServiceImpl.java
  74. 1 4
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderReturnServiceImpl.java
  75. 1 1
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderRevenueHeaderServiceImpl.java
  76. 2 2
      ruoyi-modules/ruoyi-order/src/main/resources/mapper/order/OrderDeliverMapper.xml
  77. 7 0
      ruoyi-modules/ruoyi-order/src/main/resources/mapper/order/OrderOperateLogMapper.xml
  78. 15 5
      ruoyi-modules/ruoyi-order/src/main/resources/mapper/order/OrderProductMapper.xml
  79. 104 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ServiceCaseController.java
  80. 17 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/pc/DiyProductController.java
  81. 20 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductRecommendLink.java
  82. 106 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ServiceCase.java
  83. 20 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductRecommendLinkBo.java
  84. 98 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ServiceCaseBo.java
  85. 24 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductRecommendLinkVo.java
  86. 104 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ServiceCaseVo.java
  87. 15 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/mapper/ServiceCaseMapper.java
  88. 70 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IServiceCaseService.java
  89. 137 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ServiceCaseServiceImpl.java
  90. 18 7
      ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/controller/SysSmsController.java
  91. 6 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ComStaffVo.java
  92. 9 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteComStaffServiceImpl.java
  93. 8 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/IComStaffService.java
  94. 44 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/ComStaffServiceImpl.java
  95. 5 5
      ruoyi-modules/ruoyi-system/src/main/resources/application.yml

+ 12 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemotePartnerInfoService.java

@@ -0,0 +1,12 @@
+package org.dromara.customer.api;
+
+import java.util.Map;
+import java.util.Set;
+
+public interface RemotePartnerInfoService {
+
+    /**
+     * 根据ids查询伙伴商名称
+     */
+    Map<Long, String> selectPartnerNameByIds(Set<Long> ids);
+}

+ 8 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemoteSupplierInfoService.java

@@ -1,5 +1,8 @@
 package org.dromara.customer.api;
 
+import java.util.Map;
+import java.util.Set;
+
 /**
  * author
  * 时间:2026/1/23,9:43
@@ -9,4 +12,9 @@ public interface RemoteSupplierInfoService {
      * 通过生效状态下判断有效期来更新是否停用
      */
     int updateIsDisable();
+
+    /**
+     * 根据ids查询供应商名称
+     */
+    Map<Long, String> selectSupplierNameByIds(Set<Long> ids);
 }

+ 10 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteComStaffService.java

@@ -1,5 +1,8 @@
 package org.dromara.system.api;
 
+import org.dromara.system.api.domain.vo.RemoteComStaffVo;
+
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -9,4 +12,11 @@ public interface RemoteComStaffService {
 
     Map<String,String> selectStaffNameAndCode();
 
+    /**
+     * 根据人员ID批量查询人员完整信息
+     * @param ids 人员ID集合
+     * @return 人员信息列表
+     */
+    List<RemoteComStaffVo> selectStaffByIds(Set<Long> ids);
+
 }

+ 64 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteComStaffVo.java

@@ -0,0 +1,64 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 远程调用 - 人员信息视图对象
+ *
+ * @author Claude
+ * @date 2026-01-28
+ */
+@Data
+public class RemoteComStaffVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 人员ID
+     */
+    private Long staffId;
+
+    /**
+     * 人员编码
+     */
+    private String staffCode;
+
+    /**
+     * 姓名
+     */
+    private String staffName;
+
+    /**
+     * 所属部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 所属部门名称
+     */
+    private String deptName;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 岗位ID
+     */
+    private Long postId;
+
+    /**
+     * 岗位名称
+     */
+    private String postName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+}

+ 26 - 0
ruoyi-auth/src/main/java/org/dromara/auth/controller/TokenController.java

@@ -228,4 +228,30 @@ public class TokenController {
         return R.ok(result);
     }
 
+    /**
+    * 客户登录
+    * */
+    @PostMapping("/client/login")
+    public R<LoginVo> clientLogin(@RequestBody String body) {
+        LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        // 授权类型和客户端id
+        String clientId = loginBody.getClientId();
+        String grantType = loginBody.getGrantType();
+        RemoteClientVo clientVo = remoteClientService.queryByClientId(clientId);
+
+        // 查询不到 client 或 client 内不包含 grantType
+        if (ObjectUtil.isNull(clientVo) || !StringUtils.contains(clientVo.getGrantType(), grantType)) {
+            log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
+            return R.fail(MessageUtils.message("auth.grant.type.error"));
+        } else if (!SystemConstants.NORMAL.equals(clientVo.getStatus())) {
+            return R.fail(MessageUtils.message("auth.grant.type.blocked"));
+        }
+        // 登录
+        LoginVo loginVo = IAuthStrategy.clientLogin(body, clientVo, grantType);
+
+        Long userId = LoginHelper.getUserId();
+        return R.ok(loginVo);
+    }
+
 }

+ 19 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/IAuthStrategy.java

@@ -37,6 +37,16 @@ public interface IAuthStrategy {
         return instance.getToken(username,password);
     }
 
+    static LoginVo clientLogin(String body, RemoteClientVo client, String grantType) {
+        // 授权类型和客户端id
+        String beanName = grantType + BASE_NAME;
+        if (!SpringUtils.containsBean(beanName)) {
+            throw new ServiceException("授权类型不正确!");
+        }
+        IAuthStrategy instance = SpringUtils.getBean(beanName);
+        return instance.login(body, client);
+    }
+
     /**
      * 登录
      *
@@ -46,6 +56,15 @@ public interface IAuthStrategy {
      */
     LoginVo login(String body, RemoteClientVo client);
 
+    /**
+     * 客户登录
+     *
+     * @param body   登录对象
+     * @param client 授权管理视图对象
+     * @return 登录验证信息
+     */
+    LoginVo clientLogin(String body, RemoteClientVo client);
+
     LoginVo getToken(String username, String password);
 
 }

+ 12 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/EmailAuthStrategy.java

@@ -71,6 +71,18 @@ public class EmailAuthStrategy implements IAuthStrategy {
         return loginVo;
     }
 
+    /**
+     * 客户登录
+     *
+     * @param body   登录对象
+     * @param client 授权管理视图对象
+     * @return 登录验证信息
+     */
+    @Override
+    public LoginVo clientLogin(String body, RemoteClientVo client) {
+        return null;
+    }
+
     @Override
     public LoginVo getToken(String username, String password) {
         return null;

+ 12 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/PasswordAuthStrategy.java

@@ -90,6 +90,18 @@ public class PasswordAuthStrategy implements IAuthStrategy {
         return loginVo;
     }
 
+    /**
+     * 客户登录
+     *
+     * @param body   登录对象
+     * @param client 授权管理视图对象
+     * @return 登录验证信息
+     */
+    @Override
+    public LoginVo clientLogin(String body, RemoteClientVo client) {
+        return null;
+    }
+
     /**
      * 校验验证码
      *

+ 12 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SmsAuthStrategy.java

@@ -71,6 +71,18 @@ public class SmsAuthStrategy implements IAuthStrategy {
         return loginVo;
     }
 
+    /**
+     * 客户登录
+     *
+     * @param body   登录对象
+     * @param client 授权管理视图对象
+     * @return 登录验证信息
+     */
+    @Override
+    public LoginVo clientLogin(String body, RemoteClientVo client) {
+        return null;
+    }
+
     @Override
     public LoginVo getToken(String username, String password) {
         return null;

+ 12 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SocialAuthStrategy.java

@@ -99,6 +99,18 @@ public class SocialAuthStrategy implements IAuthStrategy {
         return loginVo;
     }
 
+    /**
+     * 客户登录
+     *
+     * @param body   登录对象
+     * @param client 授权管理视图对象
+     * @return 登录验证信息
+     */
+    @Override
+    public LoginVo clientLogin(String body, RemoteClientVo client) {
+        return null;
+    }
+
     @Override
     public LoginVo getToken(String username, String password) {
         return null;

+ 12 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/XcxAuthStrategy.java

@@ -88,6 +88,18 @@ public class XcxAuthStrategy implements IAuthStrategy {
         return loginVo;
     }
 
+    /**
+     * 客户登录
+     *
+     * @param body   登录对象
+     * @param client 授权管理视图对象
+     * @return 登录验证信息
+     */
+    @Override
+    public LoginVo clientLogin(String body, RemoteClientVo client) {
+        return null;
+    }
+
     @Override
     public LoginVo getToken(String username, String password) {
         return null;

+ 23 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/InvoiceStatus.java

@@ -0,0 +1,23 @@
+package org.dromara.common.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum InvoiceStatus {
+
+    /**
+     * 未开票
+     */
+    NOT_INVOICED("0", "未开票"),
+
+    /**
+     * 已开票
+     */
+    INVOICED("1", "已开票");
+
+
+    private final String code;
+    private final String info;
+}

+ 39 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/StatementStatus.java

@@ -0,0 +1,39 @@
+package org.dromara.common.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum StatementStatus {
+
+    /**
+     * 待确认
+     */
+    PENDING_CONFIRMATION("0", "待确认"),
+
+    /**
+     * 待对账
+     */
+    PENDING_RECONCILIATION("1", "待对账"),
+
+    /**
+     * 已对账
+     */
+    RECONCILED("2", "已对账"),
+
+    /**
+     * 驳回
+     */
+    REJECTED("3", "驳回"),
+
+    /**
+     * 作废
+     */
+    CANCELLED("4", "作废");
+
+
+    private final String code;
+    private final String info;
+
+}

+ 6 - 1
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java

@@ -87,7 +87,12 @@ public class LoginHelper {
      * 获取用户id
      */
     public static Long getUserId() {
-        return Convert.toLong(getExtra(USER_KEY));
+        // ⚠️⚠️⚠️ 临时测试代码,登录功能完成后必须删除 ⚠️⚠️⚠️
+        // TODO: 临时返回固定的客户ID用于测试PC端接口,登录功能完成后删除此行
+//         return 2009201047173152769L;
+
+        // 原来的代码(登录功能完成后恢复)
+         return Convert.toLong(getExtra(USER_KEY));
     }
 
     /**

+ 0 - 1
ruoyi-modules/pom.xml

@@ -19,7 +19,6 @@
         <module>ruoyi-customer</module>
 	    <module>ruoyi-external</module>
 	    <module>ruoyi-mall</module>
-        <module>ruoyi-external</module>
         <module>ruoyi-bill</module>
     </modules>
 

+ 7 - 2
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/domain/StatementDetail.java

@@ -48,15 +48,20 @@ public class StatementDetail extends TenantEntity {
     private String type;
 
     /**
-     * 关联的订单id
+     * 关联的订单id----关联的发货单的订单id
      */
     private Long orderId;
 
     /**
-     * 关联的订单编号
+     * 关联的订单编号----订单主表的订单编号
      */
     private String orderNo;
 
+    /**
+     * 订单发货单编号
+     */
+    private String deliverCode;
+
     /**
      * 该明细金额
      */

+ 2 - 0
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/domain/bo/StatementDetailBo.java

@@ -51,6 +51,8 @@ public class StatementDetailBo extends BaseEntity {
      */
     private String orderNo;
 
+    private String deliverCode;
+
     /**
      * 该明细金额
      */

+ 2 - 0
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/domain/vo/StatementDetailVo.java

@@ -70,6 +70,8 @@ public class StatementDetailVo implements Serializable {
     @ExcelProperty(value = "关联的订单编号")
     private String orderNo;
 
+    private String deliverCode;
+
     /**
      * 该明细金额
      */

+ 69 - 0
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/pc/controller/PcStatementDetailController.java

@@ -0,0 +1,69 @@
+package org.dromara.bill.pc.controller;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.validation.constraints.*;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.bill.domain.bo.StatementDetailBo;
+import org.dromara.bill.domain.vo.StatementDetailVo;
+import org.dromara.bill.domain.vo.StatementOrderVo;
+import org.dromara.bill.service.IStatementDetailService;
+import org.dromara.bill.service.IStatementOrderService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * PC端 - 对账单明细管理
+ * 前端访问路由地址为:/pc/enterprise/statementDetail
+ *
+ * @author Claude
+ * @date 2026-01-29
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pc/enterprise/statementDetail")
+public class PcStatementDetailController extends BaseController {
+
+    private final IStatementDetailService statementDetailService;
+    private final IStatementOrderService statementOrderService;
+
+    /**
+     * 查询对账单明细列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<StatementDetailVo> list(StatementDetailBo bo, PageQuery pageQuery) {
+        if (bo.getStatementOrderId() != null) {
+            StatementOrderVo statement = statementOrderService.queryById(bo.getStatementOrderId());
+            if (statement != null) {
+                Long customerId = LoginHelper.getUserId();
+                if (!customerId.equals(statement.getCustomerId())) {
+                    throw new IllegalArgumentException("无权访问该对账单明细");
+                }
+            }
+        }
+        return statementDetailService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取对账单明细详细信息
+     */
+    @GetMapping("/{id}")
+    public R<StatementDetailVo> getInfo(@NotNull(message = "主键不能为空")
+                                        @PathVariable("id") Long id) {
+        StatementDetailVo vo = statementDetailService.queryById(id);
+        if (vo != null && vo.getStatementOrderId() != null) {
+            StatementOrderVo statement = statementOrderService.queryById(vo.getStatementOrderId());
+            if (statement != null) {
+                Long customerId = LoginHelper.getUserId();
+                if (!customerId.equals(statement.getCustomerId())) {
+                    return R.fail("无权访问该对账单明细");
+                }
+            }
+        }
+        return R.ok(vo);
+    }
+}

+ 58 - 0
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/pc/controller/PcStatementInvoiceController.java

@@ -0,0 +1,58 @@
+package org.dromara.bill.pc.controller;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.validation.constraints.*;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.bill.domain.bo.StatementInvoiceBo;
+import org.dromara.bill.domain.vo.StatementInvoiceVo;
+import org.dromara.bill.service.IStatementInvoiceService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * PC端 - 对账单发票管理
+ * 前端访问路由地址为:/pc/enterprise/statementInvoice
+ *
+ * @author Claude
+ * @date 2026-01-29
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pc/enterprise/statementInvoice")
+public class PcStatementInvoiceController extends BaseController {
+
+    private final IStatementInvoiceService statementInvoiceService;
+
+    /**
+     * 查询对账单发票列表
+     * PC端用户只能查询自己企业的对账单发票
+     */
+    @GetMapping("/list")
+    public TableDataInfo<StatementInvoiceVo> list(StatementInvoiceBo bo, PageQuery pageQuery) {
+        Long customerId = LoginHelper.getUserId();
+        bo.setCustomerId(customerId);
+        return statementInvoiceService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取对账单发票详细信息
+     * PC端用户只能查询自己企业的对账单发票
+     */
+    @GetMapping("/{id}")
+    public R<StatementInvoiceVo> getInfo(@NotNull(message = "主键不能为空")
+                                         @PathVariable("id") Long id) {
+        StatementInvoiceVo vo = statementInvoiceService.queryById(id);
+        if (vo != null) {
+            Long customerId = LoginHelper.getUserId();
+            if (!customerId.equals(vo.getCustomerId())) {
+                return R.fail("无权访问该对账单发票");
+            }
+        }
+        return R.ok(vo);
+    }
+}

+ 147 - 0
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/pc/controller/PcStatementOrderController.java

@@ -0,0 +1,147 @@
+package org.dromara.bill.pc.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import jakarta.validation.constraints.*;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.bill.domain.StatementOrder;
+import org.dromara.bill.domain.bo.StatementOrderBo;
+import org.dromara.bill.domain.vo.StatementOrderVo;
+import org.dromara.bill.domain.vo.StatementDetailVo;
+import org.dromara.bill.service.IStatementOrderService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * PC端 - 对账单管理
+ * 前端访问路由地址为:/pc/enterprise/statement
+ *
+ * @author Claude
+ * @date 2026-01-29
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pc/enterprise/statement")
+public class PcStatementOrderController extends BaseController {
+
+    private final IStatementOrderService statementOrderService;
+
+    /**
+     * 查询当前企业的对账单列表
+     * PC端用户只能查询自己企业的对账单
+     * 只允许查询状态为 1(待对账)、2(已对账)、3(驳回) 的对账单
+     * 不允许查询状态为 0(待确认)、4(作废) 的对账单
+     */
+    @GetMapping("/list")
+    public TableDataInfo<StatementOrderVo> list(StatementOrderBo bo, PageQuery pageQuery) {
+        Long customerId = LoginHelper.getUserId();
+
+        // PC端权限控制:强制只查询状态为 1、2、3 的对账单
+        LambdaQueryWrapper<StatementOrder> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(StatementOrder::getCustomerId, customerId);
+        wrapper.in(StatementOrder::getStatementStatus, Arrays.asList("1", "2", "3"));
+
+        // 如果用户指定了状态,且状态在允许范围内,则使用用户指定的状态
+        String statementStatus = bo.getStatementStatus();
+        if (statementStatus != null && !statementStatus.isEmpty()) {
+            if ("1".equals(statementStatus) || "2".equals(statementStatus) || "3".equals(statementStatus)) {
+                // 用户指定的状态在允许范围内,覆盖 IN 条件
+                wrapper.clear();
+                wrapper.eq(StatementOrder::getCustomerId, customerId);
+                wrapper.eq(StatementOrder::getStatementStatus, statementStatus);
+            }
+            // 如果用户指定了不允许的状态(0或4),保持 IN 条件,会返回空结果
+        }
+
+        // 添加其他查询条件
+        if (bo.getStatementOrderNo() != null && !bo.getStatementOrderNo().isEmpty()) {
+            wrapper.like(StatementOrder::getStatementOrderNo, bo.getStatementOrderNo());
+        }
+        if (bo.getIsInvoiceStatus() != null && !bo.getIsInvoiceStatus().isEmpty()) {
+            wrapper.eq(StatementOrder::getIsInvoiceStatus, bo.getIsInvoiceStatus());
+        }
+        if (bo.getIsPaymentStatus() != null && !bo.getIsPaymentStatus().isEmpty()) {
+            wrapper.eq(StatementOrder::getIsPaymentStatus, bo.getIsPaymentStatus());
+        }
+
+        // 使用新添加的 PC 端专用方法
+        return statementOrderService.queryPageListByWrapper(wrapper, pageQuery);
+    }
+
+    /**
+     * 获取对账单详细信息
+     * PC端用户只能查询自己企业的对账单
+     */
+    @GetMapping("/{id}")
+    public R<StatementOrderVo> getInfo(@NotNull(message = "主键不能为空")
+                                       @PathVariable("id") Long id) {
+        StatementOrderVo vo = statementOrderService.queryById(id);
+        if (vo != null) {
+            Long customerId = LoginHelper.getUserId();
+            if (!customerId.equals(vo.getCustomerId())) {
+                return R.fail("无权访问该对账单");
+            }
+        }
+        return R.ok(vo);
+    }
+
+    /**
+     * 查询当前企业的对账单明细列表
+     */
+    @GetMapping("/details")
+    public TableDataInfo<StatementDetailVo> getDetails(PageQuery pageQuery) {
+        Long customerId = LoginHelper.getUserId();
+        return statementOrderService.listDetailsByCustomerIdPage(customerId, pageQuery);
+    }
+
+    /**
+     * 确认对账单
+     */
+    @Log(title = "PC端-对账单确认", businessType = BusinessType.UPDATE)
+    @PutMapping("/confirm")
+    public R<Void> confirm(@RequestBody StatementOrderBo bo) {
+        Long customerId = LoginHelper.getUserId();
+        StatementOrderVo existingStatement = statementOrderService.queryById(bo.getId());
+        if (existingStatement == null) {
+            return R.fail("对账单不存在");
+        }
+        if (!customerId.equals(existingStatement.getCustomerId())) {
+            return R.fail("无权确认该对账单");
+        }
+        bo.setCustomerId(customerId);
+        bo.setStatementStatus("2");
+        return toAjax(statementOrderService.updateStatus(bo));
+    }
+
+    /**
+     * 驳回对账单
+     */
+    @Log(title = "PC端-对账单驳回", businessType = BusinessType.UPDATE)
+    @PutMapping("/reject")
+    public R<Void> reject(@RequestBody StatementOrderBo bo) {
+        Long customerId = LoginHelper.getUserId();
+        StatementOrderVo existingStatement = statementOrderService.queryById(bo.getId());
+        if (existingStatement == null) {
+            return R.fail("对账单不存在");
+        }
+        if (!customerId.equals(existingStatement.getCustomerId())) {
+            return R.fail("无权驳回该对账单");
+        }
+        bo.setCustomerId(customerId);
+        bo.setStatementStatus("2");
+        return toAjax(statementOrderService.updateStatus(bo));
+    }
+}

+ 103 - 0
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/pc/controller/PcStatementProductController.java

@@ -0,0 +1,103 @@
+package org.dromara.bill.pc.controller;
+
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import lombok.RequiredArgsConstructor;
+import jakarta.validation.constraints.*;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.bill.domain.bo.StatementProductBo;
+import org.dromara.bill.domain.dto.StatementOrderItem;
+import org.dromara.bill.domain.vo.StatementProductVo;
+import org.dromara.bill.domain.vo.StatementOrderVo;
+import org.dromara.bill.service.IStatementProductService;
+import org.dromara.bill.service.IStatementOrderService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * PC端 - 对账单商品管理
+ * 前端访问路由地址为:/pc/enterprise/statementProduct
+ *
+ * @author Claude
+ * @date 2026-01-29
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pc/enterprise/statementProduct")
+public class PcStatementProductController extends BaseController {
+
+    private final IStatementProductService statementProductService;
+    private final IStatementOrderService statementOrderService;
+
+    /**
+     * 查询对账单商品列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<StatementProductVo> list(StatementProductBo bo, PageQuery pageQuery) {
+        if (bo.getStatementOrderId() != null) {
+            StatementOrderVo statement = statementOrderService.queryById(bo.getStatementOrderId());
+            if (statement != null) {
+                Long customerId = LoginHelper.getUserId();
+                if (!customerId.equals(statement.getCustomerId())) {
+                    throw new IllegalArgumentException("无权访问该对账单商品");
+                }
+            }
+        }
+        return statementProductService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 根据订单ID查询对账单商品列表
+     */
+    @PostMapping("/getStatementProductList")
+    public TableDataInfo<StatementProductVo> getStatementProductList(
+        @RequestBody List<StatementOrderItem> items) {
+
+        if (CollectionUtils.isEmpty(items)) {
+            throw new IllegalArgumentException("至少选择一个订单");
+        }
+
+        if (items.stream().anyMatch(item -> item.getStatementOrderId() == null || item.getOrderId() == null)) {
+            throw new IllegalArgumentException("statementOrderId 和 orderId 不能为空");
+        }
+
+        Long customerId = LoginHelper.getUserId();
+        for (StatementOrderItem item : items) {
+            Long statementOrderId = Long.parseLong(item.getStatementOrderId());
+            StatementOrderVo statement = statementOrderService.queryById(statementOrderId);
+            if (statement == null) {
+                throw new IllegalArgumentException("对账单不存在");
+            }
+            if (!customerId.equals(statement.getCustomerId())) {
+                throw new IllegalArgumentException("无权访问该对账单商品");
+            }
+        }
+
+        return statementOrderService.getStatementProductList(items);
+    }
+
+    /**
+     * 获取对账单商品详细信息
+     */
+    @GetMapping("/{id}")
+    public R<StatementProductVo> getInfo(@NotNull(message = "主键不能为空")
+                                         @PathVariable("id") Long id) {
+        StatementProductVo vo = statementProductService.queryById(id);
+        if (vo != null && vo.getStatementOrderId() != null) {
+            StatementOrderVo statement = statementOrderService.queryById(vo.getStatementOrderId());
+            if (statement != null) {
+                Long customerId = LoginHelper.getUserId();
+                if (!customerId.equals(statement.getCustomerId())) {
+                    return R.fail("无权访问该对账单商品");
+                }
+            }
+        }
+        return R.ok(vo);
+    }
+}

+ 10 - 0
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/service/IStatementOrderService.java

@@ -56,6 +56,16 @@ public interface IStatementOrderService extends IService<StatementOrder> {
      */
     TableDataInfo<StatementOrderVo> queryPageList(StatementOrderBo bo, PageQuery pageQuery);
 
+    /**
+     * 使用自定义 Wrapper 分页查询对账单列表
+     * 用于 PC 端权限控制,支持自定义查询条件
+     *
+     * @param wrapper   查询条件包装器
+     * @param pageQuery 分页参数
+     * @return 对账单主分页列表
+     */
+    TableDataInfo<StatementOrderVo> queryPageListByWrapper(com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<StatementOrder> wrapper, PageQuery pageQuery);
+
 
     TableDataInfo<StatementProductVo> getStatementProductList(List<StatementOrderItem> items);
 

+ 6 - 2
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/service/impl/StatementInvoiceServiceImpl.java

@@ -121,7 +121,7 @@ public class StatementInvoiceServiceImpl extends ServiceImpl<StatementInvoiceMap
     private LambdaQueryWrapper<StatementInvoice> buildQueryWrapper(StatementInvoiceBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<StatementInvoice> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(StatementInvoice::getId);
+        lqw.orderByDesc(StatementInvoice::getId);
         lqw.eq(StringUtils.isNotBlank(bo.getStatementInvoiceNo()), StatementInvoice::getStatementInvoiceNo, bo.getStatementInvoiceNo());
         lqw.eq(bo.getCustomerId() != null, StatementInvoice::getCustomerId, bo.getCustomerId());
         lqw.eq(StringUtils.isNotBlank(bo.getCustomerNo()), StatementInvoice::getCustomerNo, bo.getCustomerNo());
@@ -224,12 +224,16 @@ public class StatementInvoiceServiceImpl extends ServiceImpl<StatementInvoiceMap
     }
 
     /**
-     * 公共方法:删除明细和商品
+     * 公共方法:删除明细,商品和发票明细
      */
     private void deleteDetailsAndProductsAndInvoiceInfos(Long statementInvoiceId) {
+
         statementInvoiceDetailMapper.deleteByStatementInvoiceId(statementInvoiceId);
+
         statementInvoiceProductMapper.deleteByStatementInvoiceId(statementInvoiceId);
+
         invoiceInfoMapper.deleteByStatementInvoiceId(statementInvoiceId);
+
     }
 
     /**

+ 24 - 6
ruoyi-modules/ruoyi-bill/src/main/java/org/dromara/bill/service/impl/StatementOrderServiceImpl.java

@@ -127,7 +127,7 @@ public class StatementOrderServiceImpl extends ServiceImpl<StatementOrderMapper,
 
         // 查询客户的所有有效对账单
         LambdaQueryWrapper<StatementOrder> orderWrapper = new LambdaQueryWrapper<>();
-        orderWrapper.eq(StatementOrder::getCustomerId, customerId)
+        orderWrapper.eq(StatementOrder::getCustomerId, customerId)//.ne(StatementOrder::getIsInvoiceStatus, InvoiceStatus.INVOICED.getCode())
             .eq(StatementOrder::getDelFlag, "0");
 
         List<StatementOrder> statementOrders = baseMapper.selectList(orderWrapper);
@@ -150,7 +150,7 @@ public class StatementOrderServiceImpl extends ServiceImpl<StatementOrderMapper,
         // 查询明细(分页)
         LambdaQueryWrapper<StatementDetail> detailWrapper = new LambdaQueryWrapper<>();
         detailWrapper.in(StatementDetail::getStatementOrderId, statementOrderIds)
-            .eq(StatementDetail::getDelFlag, "0");
+            .eq(StatementDetail::getDelFlag, "0").orderByDesc(StatementDetail::getId);
 
         Page<StatementDetailVo> pageResult = statementDetailMapper.selectVoPage(pageQuery.build(), detailWrapper);
 
@@ -190,6 +190,20 @@ public class StatementOrderServiceImpl extends ServiceImpl<StatementOrderMapper,
         return TableDataInfo.build(result);
     }
 
+    /**
+     * 使用自定义 Wrapper 分页查询对账单列表
+     * 用于 PC 端权限控制,支持自定义查询条件
+     *
+     * @param wrapper   查询条件包装器
+     * @param pageQuery 分页参数
+     * @return 对账单主分页列表
+     */
+    @Override
+    public TableDataInfo<StatementOrderVo> queryPageListByWrapper(LambdaQueryWrapper<StatementOrder> wrapper, PageQuery pageQuery) {
+        Page<StatementOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), wrapper);
+        return TableDataInfo.build(result);
+    }
+
     /**
      * 查询符合条件的对账单主列表
      *
@@ -205,7 +219,7 @@ public class StatementOrderServiceImpl extends ServiceImpl<StatementOrderMapper,
     private LambdaQueryWrapper<StatementOrder> buildQueryWrapper(StatementOrderBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<StatementOrder> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(StatementOrder::getId);
+        lqw.orderByDesc(StatementOrder::getId);
         lqw.eq(StringUtils.isNotBlank(bo.getStatementOrderNo()), StatementOrder::getStatementOrderNo, bo.getStatementOrderNo());
         lqw.eq(bo.getCustomerId() != null, StatementOrder::getCustomerId, bo.getCustomerId());
         lqw.eq(StringUtils.isNotBlank(bo.getCustomerNo()), StatementOrder::getCustomerNo, bo.getCustomerNo());
@@ -307,14 +321,18 @@ public class StatementOrderServiceImpl extends ServiceImpl<StatementOrderMapper,
         Map<Long, List<StatementProductBo>> productGroupByOrderId =
             CollectionUtil.isNotEmpty(bo.getProductList())
                 ? bo.getProductList().stream()
+                .filter(product -> product.getOrderId() != null)
                 .collect(Collectors.groupingBy(StatementProductBo::getOrderId))
                 : Collections.emptyMap();
 
-        // 3. 为每个明细,找到对应的商品
+        // 3. 处理每个明细
         for (StatementDetailBo detailBo : bo.getDetailList()) {
-            Long originalOrderId = detailBo.getOrderId();
-            List<StatementProductBo> productListForThisOrder = productGroupByOrderId.get(originalOrderId);
+            Long originalOrderId = detailBo.getOrderId(); // 可能为 null
+            if (originalOrderId == null) {
+                continue; // 跳过无效明细
+            }
 
+            List<StatementProductBo> productListForThisOrder = productGroupByOrderId.get(originalOrderId);
             if (CollectionUtil.isNotEmpty(productListForThisOrder)) {
                 for (StatementProductBo productBo : productListForThisOrder) {
                     StatementProduct product = MapstructUtils.convert(productBo, StatementProduct.class);

+ 31 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/pc/CustomerRegisterController.java

@@ -0,0 +1,31 @@
+package org.dromara.customer.controller.pc;
+
+/**
+ * @author
+ * @date 2026/1/29 上午10:15
+ */
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.customer.domain.bo.CustomerRegisterBo;
+import org.dromara.customer.service.ICustomerInfoService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/customerContact")
+public class CustomerRegisterController {
+
+    private final ICustomerInfoService customerInfoService;
+
+    /**
+    * 企业注册
+    * */
+    public R<Void> register(CustomerRegisterBo bo) {
+        customerInfoService.register(bo);
+        return R.ok();
+    }
+}

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

@@ -66,8 +66,12 @@ public class CustomerContact extends TenantEntity {
      */
     private Long roleId;
 
+    private String roleName;
+
     private Long deptId;
 
+    private String deptName;
+
     private String email;
 
     private Date birthday;

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

@@ -61,7 +61,7 @@ public class CustomerDept extends TenantEntity {
     /**
      * 绑定地址
      */
-    private String bindAddress;
+    private Long bindAddress;
 
     /**
      * 部门负责人

+ 6 - 2
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerContactBo.java

@@ -35,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;
 
     /**
@@ -67,8 +67,12 @@ public class CustomerContactBo extends BaseEntity {
      */
     private Long roleId;
 
+    private String roleName;
+
     private Long deptId;
 
+    private String deptName;
+
     private String email;
 
     private Date birthday;

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

@@ -85,7 +85,7 @@ public class CustomerDeptBo extends BaseEntity {
     /**
      * 绑定地址
      */
-    private String bindAddress;
+    private Long bindAddress;
 
     /**
      * 部门负责人

+ 35 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerRegisterBo.java

@@ -0,0 +1,35 @@
+package org.dromara.customer.domain.bo;
+
+import lombok.Data;
+
+/**
+ * @author
+ * @date 2026/1/29 上午10:24
+ */
+@Data
+public class CustomerRegisterBo {
+    /**
+    * 客户名称
+    * */
+    private String customerName;
+    /**
+    * 采购人姓名
+    * */
+    private String purchaseName;
+    /**
+    * 采购人手机号
+    * */
+    private String purchasePhone;
+    /**
+    * 验证码
+    * */
+    private String code;
+    /**
+    * 密码
+    * */
+    private String password;
+    /**
+    * 确认密码
+    * */
+    private String confirmPassword;
+}

+ 4 - 2
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerContactVo.java

@@ -40,7 +40,6 @@ public class CustomerContactVo implements Serializable {
     private Long customerId;
 
 
-
     /**
      * 联系人姓名
      */
@@ -79,11 +78,14 @@ public class CustomerContactVo implements Serializable {
      * 采购角色(如:采购经理、财务)
      */
     @ExcelProperty(value = "采购角色", converter = ExcelDictConvert.class)
-    @ExcelDictFormat(readConverterExp = "如=:采购经理、财务")
     private Long roleId;
 
+    private String roleName;
+
     private Long deptId;
 
+    private String deptName;
+
     /**
      * 是否主联系人:0=是,1=否
      */

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

@@ -17,7 +17,8 @@ public class CustomerDeptTreeVo implements Serializable {
     private BigDecimal yearlyBudget;
     private BigDecimal usedBudget;
     private BigDecimal monthLimit;
-    private String bindAddress;
+    private Long bindAddress;
+    private String bindAddressStr;
     private String bindStatus;
     private String status;      // 来自 sys_dept(启用/停用)
 

+ 3 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerDeptVo.java

@@ -98,7 +98,9 @@ public class CustomerDeptVo implements Serializable {
      * 绑定地址
      */
     @ExcelProperty(value = "绑定地址")
-    private String bindAddress;
+    private Long bindAddress;
+
+    private String bindAddressStr;
 
     /**
      * 部门负责人

+ 25 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemotePartnerInfoServiceImpl.java

@@ -0,0 +1,25 @@
+package org.dromara.customer.dubbo;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.customer.api.RemotePartnerInfoService;
+import org.dromara.customer.service.IPartnerInfoService;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+import java.util.Set;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@DubboService
+public class RemotePartnerInfoServiceImpl implements RemotePartnerInfoService {
+
+    private final IPartnerInfoService partnerInfoService;
+
+    @Override
+    public Map<Long, String> selectPartnerNameByIds(Set<Long> ids) {
+        return partnerInfoService.selectPartnerNameByIds(ids);
+    }
+}

+ 9 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemoteSupplierInfoServiceImpl.java

@@ -7,6 +7,9 @@ import org.dromara.customer.api.RemoteSupplierInfoService;
 import org.dromara.customer.service.ISupplierInfoService;
 import org.springframework.stereotype.Service;
 
+import java.util.Map;
+import java.util.Set;
+
 /**
  * author
  * 时间:2026/1/23,9:42
@@ -18,8 +21,14 @@ import org.springframework.stereotype.Service;
 public class RemoteSupplierInfoServiceImpl implements RemoteSupplierInfoService {
 
     private final ISupplierInfoService supplierInfoService;
+
     @Override
     public int updateIsDisable() {
         return supplierInfoService.updateIsDisable();
     }
+
+    @Override
+    public Map<Long, String> selectSupplierNameByIds(Set<Long> ids) {
+        return supplierInfoService.selectSupplierNameByIds(ids);
+    }
 }

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

@@ -0,0 +1,118 @@
+package org.dromara.customer.pc.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.vo.CustomerSalesInfoVo;
+import org.dromara.customer.service.ICustomerSalesInfoService;
+import org.dromara.system.api.RemoteComStaffService;
+import org.dromara.system.api.domain.vo.RemoteComStaffVo;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+/**
+ * PC端 - 专属服务人员查询
+ * 前端访问路由地址为:/pc/enterprise/servicePerson
+ *
+ * @author Claude
+ * @date 2026-01-28
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pc/enterprise/servicePerson")
+public class PcServicePersonController extends BaseController {
+
+    private final ICustomerSalesInfoService customerSalesInfoService;
+
+    @DubboReference
+    private RemoteComStaffService remoteComStaffService;
+
+    /**
+     * 查询当前企业的专属服务人员
+     * 返回:销售人员(专属采购顾问)、服务人员(客服人员)
+     */
+    @GetMapping("/list")
+    public R<List<ServicePersonVO>> getServicePersons() {
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+
+        // 查询客户销售信息
+        CustomerSalesInfoVo salesInfo = customerSalesInfoService.queryByCustomerId(customerId);
+        if (salesInfo == null) {
+            return R.ok(Collections.emptyList());
+        }
+
+        // 收集服务人员ID
+        Set<Long> staffIds = new HashSet<>();
+        if (salesInfo.getSalesPersonId() != null) {
+            staffIds.add(salesInfo.getSalesPersonId());
+        }
+        if (salesInfo.getServiceStaffId() != null) {
+            staffIds.add(salesInfo.getServiceStaffId());
+        }
+
+        if (staffIds.isEmpty()) {
+            return R.ok(Collections.emptyList());
+        }
+
+        // 通过Dubbo远程调用获取人员详细信息
+        List<RemoteComStaffVo> staffList = remoteComStaffService.selectStaffByIds(staffIds);
+        if (staffList == null || staffList.isEmpty()) {
+            return R.ok(Collections.emptyList());
+        }
+
+        // 组装返回数据
+        List<ServicePersonVO> result = new ArrayList<>();
+
+        // 添加销售人员(专属采购顾问)
+        if (salesInfo.getSalesPersonId() != null) {
+            staffList.stream()
+                .filter(staff -> salesInfo.getSalesPersonId().equals(staff.getStaffId()))
+                .findFirst()
+                .ifPresent(staff -> {
+                    ServicePersonVO vo = new ServicePersonVO();
+                    vo.setName(staff.getStaffName());
+                    vo.setType("专属采购顾问");
+                    vo.setDepartment(staff.getDeptName());
+                    vo.setPhone(staff.getPhone());
+                    vo.setAvatar("");
+                    result.add(vo);
+                });
+        }
+
+        // 添加服务人员(客服人员)
+        if (salesInfo.getServiceStaffId() != null) {
+            staffList.stream()
+                .filter(staff -> salesInfo.getServiceStaffId().equals(staff.getStaffId()))
+                .findFirst()
+                .ifPresent(staff -> {
+                    ServicePersonVO vo = new ServicePersonVO();
+                    vo.setName(staff.getStaffName());
+                    vo.setType("客服人员");
+                    vo.setDepartment(staff.getDeptName());
+                    vo.setPhone(staff.getPhone());
+                    vo.setAvatar("");
+                    result.add(vo);
+                });
+        }
+
+        return R.ok(result);
+    }
+
+    /**
+     * 服务人员视图对象
+     */
+    @lombok.Data
+    public static class ServicePersonVO {
+        private String name;
+        private String type;
+        private String department;
+        private String phone;
+        private String avatar;
+    }
+}

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

@@ -3,6 +3,7 @@ package org.dromara.customer.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.dromara.customer.domain.CustomerInfo;
 import org.dromara.customer.domain.bo.CustomerListBo;
+import org.dromara.customer.domain.bo.CustomerRegisterBo;
 import org.dromara.customer.domain.bo.MessagePublishCustomerBo;
 import org.dromara.customer.domain.vo.*;
 import org.dromara.customer.domain.bo.CustomerInfoBo;
@@ -121,4 +122,9 @@ public interface ICustomerInfoService extends IService<CustomerInfo> {
      * @return 是否删除成功
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+    * 客户注册
+    * */
+    Boolean register(CustomerRegisterBo bo);
 }

+ 6 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IPartnerInfoService.java

@@ -9,6 +9,8 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * 伙伴商基本信息Service接口
@@ -16,7 +18,7 @@ import java.util.List;
  * @author LionLi
  * @date 2026-01-21
  */
-public interface IPartnerInfoService extends IService<PartnerInfo>{
+public interface IPartnerInfoService extends IService<PartnerInfo> {
 
     /**
      * 查询伙伴商基本信息
@@ -74,4 +76,7 @@ public interface IPartnerInfoService extends IService<PartnerInfo>{
      * @return 伙伴商基本信息
      */
     PartnerInfoVo queryCurrentPartnerInfo();
+
+    /*根据ids查询伙伴商名称*/
+    Map<Long, String> selectPartnerNameByIds(Set<Long> ids);
 }

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISupplierInfoService.java

@@ -13,6 +13,8 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * 供应商信息Service接口
@@ -38,6 +40,7 @@ public interface ISupplierInfoService extends IService<SupplierInfo>{
      * @return 供应商信息分页列表
      */
     TableDataInfo<SupplierInfoVo> queryPageList(SupplierInfoBo bo, PageQuery pageQuery);
+
     /**
      * 获取供应商信息列表
      *
@@ -50,6 +53,7 @@ public interface ISupplierInfoService extends IService<SupplierInfo>{
 
     /**
      * 获取供应商审核列表
+     *
      * @param bo
      * @param pageQuery
      * @return
@@ -99,4 +103,7 @@ public interface ISupplierInfoService extends IService<SupplierInfo>{
     Long getSupplierStatus(Long id);
 
     boolean srmUpdateByBo(SupplierInfoBo bo);
+
+    /*根据ids查询供应商名称*/
+    Map<Long, String> selectSupplierNameByIds(Set<Long> ids);
 }

+ 53 - 26
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerDeptServiceImpl.java

@@ -1,8 +1,6 @@
 package org.dromara.customer.service.impl;
 
-import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.tree.Tree;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -10,23 +8,23 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.context.PlatformContext;
-import org.dromara.common.core.enums.IsDefault;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.customer.domain.CustomerDept;
-import org.dromara.customer.domain.CustomerInfo;
 import org.dromara.customer.domain.bo.CustomerDeptBo;
 import org.dromara.customer.domain.vo.CustomerDeptTreeVo;
 import org.dromara.customer.domain.vo.CustomerDeptVo;
+import org.dromara.customer.domain.vo.CustomerShippingAddressVo;
 import org.dromara.customer.mapper.CustomerDeptMapper;
+import org.dromara.customer.mapper.CustomerShippingAddressMapper;
 import org.dromara.customer.service.ICustomerDeptService;
 import org.dromara.system.api.RemoteDeptService;
 import org.dromara.system.api.domain.vo.RemoteDeptVo;
 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;
 
 /**
@@ -47,6 +45,8 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
 
     private final CustomerDeptMapper baseMapper;
 
+    private final CustomerShippingAddressMapper customerShippingAddressMapper;
+
     /**
      * 查询客户部门信息
      *
@@ -133,33 +133,61 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
         Map<Long, RemoteDeptVo> deptMap = sysDepts.stream()
             .collect(Collectors.toMap(RemoteDeptVo::getDeptId, d -> d));
 
-        // 3. 合并 + 安全排序
+        // 1. 提前批量获取所有需要的地址信息
+        Set<Long> bindAddressIds = customerDepts.stream()
+            .map(CustomerDept::getBindAddress)
+            .filter(Objects::nonNull)
+            .collect(Collectors.toSet());
+
+        final Map<Long, CustomerShippingAddressVo> addressVoMap = !bindAddressIds.isEmpty()
+            ? customerShippingAddressMapper.selectVoByIds(new ArrayList<>(bindAddressIds))
+            .stream()
+            .collect(Collectors.toMap(CustomerShippingAddressVo::getId, Function.identity(), (a, b) -> a))
+            : new HashMap<>();
+
+        Map<Long, CustomerDept> customerDeptsMap = customerDepts.stream()
+            .collect(Collectors.toMap(CustomerDept::getDeptId, Function.identity()));
+
+        // 2. 构建结果并排序
         return customerDepts.stream()
-            .filter(cd -> deptMap.containsKey(cd.getDeptId()))
-            .map(cd -> {
-                RemoteDeptVo sys = deptMap.get(cd.getDeptId());
-                CustomerDeptTreeVo vo = new CustomerDeptTreeVo();
-                vo.setDeptId(sys.getDeptId());
-                vo.setParentId(sys.getParentId());
-                vo.setDeptName(sys.getDeptName());
-                vo.setOrderNum(sys.getOrderNum()); // 可能为 null!
-                vo.setStatus(sys.getStatus());
-
-                vo.setYearlyBudget(cd.getYearlyBudget());
-                vo.setUsedBudget(cd.getUsedBudget());
-                vo.setMonthLimit(cd.getMonthLimit());
-                vo.setBindAddress(cd.getBindAddress());
-                vo.setBindStatus(cd.getBindStatus());
-
-                return vo;
-            })
+            .map(cd -> deptMap.get(cd.getDeptId())) // 直接 get,null 表示不匹配
+            .filter(Objects::nonNull) // 过滤掉 deptMap 中不存在的
+            .map(sys -> buildCustomerDeptTreeVo(sys, customerDeptsMap.get(sys.getDeptId()), addressVoMap)) // 假设你有 customerDepts 按 deptId 的 map
             .sorted(Comparator.comparing(
                 CustomerDeptTreeVo::getOrderNum,
-                Comparator.nullsLast(Comparator.naturalOrder()) // ✅ 安全处理 null
+                Comparator.nullsLast(Comparator.naturalOrder())
             ))
             .collect(Collectors.toList());
     }
 
+    private CustomerDeptTreeVo buildCustomerDeptTreeVo(RemoteDeptVo sys, CustomerDept cd, Map<Long, CustomerShippingAddressVo> addressVoMap) {
+        CustomerDeptTreeVo vo = new CustomerDeptTreeVo();
+
+        // 部门信息
+        vo.setDeptId(sys.getDeptId());
+        vo.setParentId(sys.getParentId());
+        vo.setDeptName(sys.getDeptName());
+        vo.setOrderNum(sys.getOrderNum());
+        vo.setStatus(sys.getStatus());
+
+        // 客户部门绑定信息
+        vo.setYearlyBudget(cd.getYearlyBudget());
+        vo.setUsedBudget(cd.getUsedBudget());
+        vo.setMonthLimit(cd.getMonthLimit());
+        vo.setBindAddress(cd.getBindAddress());
+        vo.setBindStatus(cd.getBindStatus());
+
+        // 地址拼接(安全处理 null)
+        CustomerShippingAddressVo addressVo = addressVoMap.get(cd.getBindAddress());
+        if (addressVo != null) {
+            String provinceCity = Optional.ofNullable(addressVo.getProvincialCityCountry()).orElse("");
+            String detailAddr = Optional.ofNullable(addressVo.getAddress()).orElse("");
+            vo.setBindAddressStr((provinceCity + " " + detailAddr).trim());
+        }
+
+        return vo;
+    }
+
     private LambdaQueryWrapper<CustomerDept> buildQueryWrapper(CustomerDeptBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<CustomerDept> lqw = Wrappers.lambdaQuery();
@@ -170,7 +198,6 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
         lqw.eq(bo.getMonthLimit() != null, CustomerDept::getMonthLimit, bo.getMonthLimit());
         lqw.eq(bo.getMonthUsedBudget() != null, CustomerDept::getMonthUsedBudget, bo.getMonthUsedBudget());
         lqw.eq(StringUtils.isNotBlank(bo.getBindStatus()), CustomerDept::getBindStatus, bo.getBindStatus());
-        lqw.eq(StringUtils.isNotBlank(bo.getBindAddress()), CustomerDept::getBindAddress, bo.getBindAddress());
         lqw.eq(StringUtils.isNotBlank(bo.getDeptManage()), CustomerDept::getDeptManage, bo.getDeptManage());
         lqw.eq(StringUtils.isNotBlank(bo.getIsLimit()), CustomerDept::getIsLimit, bo.getIsLimit());
         lqw.eq(StringUtils.isNotBlank(bo.getSelectYear()), CustomerDept::getSelectYear, bo.getSelectYear());

+ 125 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java

@@ -1,6 +1,7 @@
 package org.dromara.customer.service.impl;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.crypto.digest.BCrypt;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -9,18 +10,26 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.core.constant.GlobalConstants;
 import org.dromara.common.core.context.PlatformContext;
 import org.dromara.common.core.enums.IsDefault;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.DateUtils;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.customer.domain.*;
 import org.dromara.customer.domain.bo.*;
 import org.dromara.customer.domain.vo.*;
 import org.dromara.customer.mapper.*;
 import org.dromara.customer.service.ICustomerInfoService;
+import org.dromara.customer.utils.qcc.QccUtils;
+import org.dromara.customer.utils.qcc.domain.CompanyInfoResponse;
 import org.dromara.system.api.*;
+import org.dromara.system.api.domain.bo.RemoteUserBo;
 import org.dromara.system.api.domain.vo.RemoteDeptVo;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -43,6 +52,8 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
     @DubboReference
     private RemoteDeptService remoteDeptService;
     @DubboReference
+    private RemoteUserService remoteUserService;
+    @DubboReference
     private RemoteComPostService remoteComPostService;
     @DubboReference
     private RemoteComStaffService remoteComStaffService;
@@ -593,6 +604,23 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
 
             // 联系人列表
             if (contactList != null && !contactList.isEmpty()) {
+                CustomerContactBo primaryContact = contactList.stream()
+                    .filter(contact -> contact.getIsPrimary().equals("0"))
+                    .findFirst()
+                    .orElse(null);
+                RemoteUserBo remoteUserBo = new RemoteUserBo();
+                remoteUserBo.setNickName(bo.getCustomerName());
+                if (null != primaryContact) {
+                    remoteUserBo.setUserName(primaryContact.getPhone());
+                } else {
+                    remoteUserBo.setUserName(contactList.get(0).getPhone());
+                }
+
+                String password = "123456";
+                remoteUserBo.setPassword(BCrypt.hashpw(password));
+                remoteUserBo.setUserSonType("3");
+                remoteUserBo.setTenantId(LoginHelper.getTenantId());
+                remoteUserService.addUser(remoteUserBo);
                 for (CustomerContactBo contactBo : contactList) {
                     CustomerContact contact = MapstructUtils.convert(contactBo, CustomerContact.class);
                     contact.setCustomerId(customerId);
@@ -812,4 +840,101 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    /**
+     * 客户注册
+     *
+     * @param bo
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean register(CustomerRegisterBo bo) {
+        //先校验验证码是否正确
+        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + bo.getPurchasePhone());
+        code = "123456";
+        if (!code.equals(bo.getCode())) {
+            throw new ServiceException("验证码错误");
+        }
+
+        //校验密码与确认密码是否一致
+        if (!bo.getPassword().equals(bo.getConfirmPassword())) {
+            throw new ServiceException("密码与确认密码不一致");
+        }
+
+        //查询数据库是否存在企业,已存在则提示客户已存在
+        CustomerInfo customerInfo = baseMapper.selectOne(new LambdaQueryWrapper<CustomerInfo>()
+            .eq(CustomerInfo::getCustomerName, bo.getCustomerName())
+        );
+        if (customerInfo != null) {
+            throw new ServiceException("客户已存在");
+        }
+
+        //查询企查查是否存在企业,如果企查查不存在则提示企业不存在
+        CompanyInfoResponse companyInfo = QccUtils.getCompanyInfo(bo.getCustomerName());
+        if (companyInfo == null || companyInfo.getResult() == null) {
+            throw new ServiceException("请检查企业名是否正确");
+        }
+
+        //构建客户信息实体
+        CustomerInfo customerEntity = new CustomerInfo();
+        customerEntity.setCustomerName(bo.getCustomerName());
+        customerEntity.setBusinessCustomerName(companyInfo.getResult().getName());
+        customerEntity.setShortName(companyInfo.getResult().getName());
+        customerEntity.setStatus("0"); // 正常状态
+        customerEntity.setDelFlag("0"); // 未删除
+
+        // 设置其他工商信息
+        if (baseMapper.insert(customerEntity) <= 0) {
+            throw new ServiceException("客户信息新增失败");
+        }
+
+        Long customerId = customerEntity.getId();
+
+        // 添加客户工商信息
+        CustomerBusinessInfo businessInfo = new CustomerBusinessInfo();
+        businessInfo.setCustomerId(customerId);
+        businessInfo.setBusinessCustomerName(companyInfo.getResult().getName());
+        businessInfo.setSocialCreditCode(companyInfo.getResult().getCreditCode());
+        businessInfo.setLegalPersonName(companyInfo.getResult().getOperName());
+        businessInfo.setRegisteredCapital(companyInfo.getResult().getRegistCapi());
+        businessInfo.setEstablishmentDate(DateUtils.parseDate((companyInfo.getResult().getStartDate())));
+        businessInfo.setRegistrationStatus(companyInfo.getResult().getStatus());
+        businessInfo.setBusinessAddress(companyInfo.getResult().getAddress());
+        businessInfo.setBussinessRange(companyInfo.getResult().getScope());
+        businessInfo.setStatus("0");
+        businessInfo.setDelFlag("0");
+
+        if (customerBusinessInfoMapper.insert(businessInfo) <= 0) {
+            throw new ServiceException("客户工商信息新增失败");
+        }
+
+        // 添加客户联系人信息
+        CustomerContact contact = new CustomerContact();
+        contact.setCustomerId(customerId);
+        contact.setContactName(bo.getPurchaseName());
+        contact.setPhone(bo.getPurchasePhone());
+        contact.setCustomLoginName(bo.getPurchasePhone());
+        contact.setIsPrimary("0"); // 设为主联系人
+        contact.setStatus("0");
+        contact.setDelFlag("0");
+
+        if (customerContactMapper.insert(contact) <= 0) {
+            throw new ServiceException("客户联系人信息新增失败");
+        }
+
+        // 创建系统用户
+        RemoteUserBo remoteUserBo = new RemoteUserBo();
+        remoteUserBo.setUserName(bo.getPurchasePhone());
+        remoteUserBo.setNickName(bo.getCustomerName());
+        remoteUserBo.setPhonenumber(bo.getPurchasePhone());
+        remoteUserBo.setPassword(BCrypt.hashpw(bo.getPassword()));
+        remoteUserBo.setUserSonType("3"); // 商城用户
+        remoteUserBo.setTenantId(LoginHelper.getTenantId());
+        remoteUserBo.setStatus("0"); // 正常状态
+
+        remoteUserService.addUser(remoteUserBo);
+
+        log.info("客户注册成功,客户ID:{},客户名称:{}", customerId, bo.getCustomerName());
+        return true;
+    }
 }

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

@@ -12,6 +12,7 @@ import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.redis.utils.SequenceUtils;
 import org.dromara.customer.domain.CustomerShippingAddress;
 import org.dromara.customer.domain.bo.CustomerShippingAddressBo;
 import org.dromara.customer.domain.vo.CustomerShippingAddressVo;
@@ -20,6 +21,7 @@ import org.dromara.customer.service.ICustomerShippingAddressService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.time.Duration;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -35,6 +37,8 @@ import java.util.Map;
 @Service
 public class CustomerShippingAddressServiceImpl extends ServiceImpl<CustomerShippingAddressMapper, CustomerShippingAddress> implements ICustomerShippingAddressService {
 
+    private static final String SHOPPING_ADDRESS_NO_KEY = "customer_shipping_address:shipping_address_no";
+
     private final CustomerShippingAddressMapper baseMapper;
 
     /**
@@ -107,6 +111,7 @@ public class CustomerShippingAddressServiceImpl extends ServiceImpl<CustomerShip
      */
     @Override
     public Boolean insertByBo(CustomerShippingAddressBo bo) {
+        bo.setShippingAddressNo(SequenceUtils.nextPaddedIdStr(SHOPPING_ADDRESS_NO_KEY, Duration.ofDays(3650), 4));
         CustomerShippingAddress add = MapstructUtils.convert(bo, CustomerShippingAddress.class);
         validEntityBeforeSave(add);
         boolean flag = baseMapper.insert(add) > 0;

+ 32 - 7
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/PartnerInfoServiceImpl.java

@@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.customer.domain.SupplierInfo;
 import org.springframework.stereotype.Service;
 import org.dromara.customer.domain.bo.PartnerInfoBo;
 import org.dromara.customer.domain.vo.PartnerInfoVo;
@@ -20,9 +21,7 @@ import org.dromara.customer.mapper.PartnerUserMapper;
 import org.dromara.customer.service.IPartnerInfoService;
 import org.dromara.common.satoken.utils.LoginHelper;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Collection;
+import java.util.*;
 
 /**
  * 伙伴商基本信息Service业务层处理
@@ -33,7 +32,7 @@ import java.util.Collection;
 @Slf4j
 @RequiredArgsConstructor
 @Service
-public class PartnerInfoServiceImpl  extends ServiceImpl<PartnerInfoMapper, PartnerInfo> implements IPartnerInfoService {
+public class PartnerInfoServiceImpl extends ServiceImpl<PartnerInfoMapper, PartnerInfo> implements IPartnerInfoService {
 
     private final PartnerInfoMapper baseMapper;
     private final PartnerUserMapper partnerUserMapper;
@@ -45,7 +44,7 @@ public class PartnerInfoServiceImpl  extends ServiceImpl<PartnerInfoMapper, Part
      * @return 伙伴商基本信息
      */
     @Override
-    public PartnerInfoVo queryById(Long id){
+    public PartnerInfoVo queryById(Long id) {
         return baseMapper.selectVoById(id);
     }
 
@@ -126,7 +125,7 @@ public class PartnerInfoServiceImpl  extends ServiceImpl<PartnerInfoMapper, Part
     /**
      * 保存前的数据校验
      */
-    private void validEntityBeforeSave(PartnerInfo entity){
+    private void validEntityBeforeSave(PartnerInfo entity) {
         //TODO 做一些数据校验,如唯一约束
     }
 
@@ -139,7 +138,7 @@ public class PartnerInfoServiceImpl  extends ServiceImpl<PartnerInfoMapper, Part
      */
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
+        if (isValid) {
             //TODO 做一些业务上的校验,判断是否需要校验
         }
         return baseMapper.deleteByIds(ids) > 0;
@@ -161,4 +160,30 @@ public class PartnerInfoServiceImpl  extends ServiceImpl<PartnerInfoMapper, Part
         }
         return baseMapper.selectVoById(partnerUser.getPartnerId());
     }
+
+    @Override
+    public Map<Long, String> selectPartnerNameByIds(Set<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        // 限制批量大小
+        if (ids.size() > 1000) {
+            throw new IllegalArgumentException("Batch size exceeds limit: " + ids.size());
+        }
+
+        List<PartnerInfo> partnerInfoList = baseMapper.selectByIds(ids);
+        Map<Long, String> resultMap = new HashMap<>(ids.size());
+
+        // 初始化所有请求的 ID 为 null
+        ids.forEach(id -> resultMap.put(id, null));
+
+        if (partnerInfoList != null) {
+            partnerInfoList.stream()
+                .filter(partner -> partner.getId() != null && partner.getPartnerName() != null)
+                .forEach(partner -> resultMap.put(partner.getId(), partner.getPartnerName()));
+        }
+
+        return resultMap;
+    }
 }

+ 84 - 116
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SupplierInfoServiceImpl.java

@@ -37,6 +37,7 @@ import org.dromara.system.api.domain.vo.RemoteSupplierTypeVo;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.dromara.customer.domain.bo.SupplierInfoBo;
+import org.dromara.customer.domain.vo.SupplierInfoVo;
 import org.dromara.customer.mapper.SupplierInfoMapper;
 import org.springframework.util.CollectionUtils;
 
@@ -81,8 +82,6 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     private final RemoteSupplierTypeService remoteSupplierTypeService;
 
 
-
-
     /**
      * 新增供应商信息
      *
@@ -97,7 +96,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
             .orderByDesc(SupplierInfo::getSupplierNo)
             .last("LIMIT 1");
         SupplierInfo maxSupplier = baseMapper.selectOne(lambdaQueryWrapper);
-        long l = Long.parseLong(maxSupplier.getSupplierNo())+1;
+        long l = Long.parseLong(maxSupplier.getSupplierNo()) + 1;
         String supplierNo = String.valueOf(l);
         SupplierInfo add = MapstructUtils.convert(bo, SupplierInfo.class);
         add.setSupplierNo(supplierNo);
@@ -328,74 +327,6 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         return baseMapper.updateById(update) > 0;
     }
 
-    @Override
-    public boolean srmUpdateByBo(SupplierInfoBo bo) {
-        Long id = bo.getId();
-        SupplierInfo supplierInfo = baseMapper.selectById(id);
-        //如果是待审核 随便改
-        if (bo.getSupplyStatus() == SupplierStatusEnum.PENDING_REVIEW.getCode()){
-            SupplierInfo update = MapstructUtils.convert(bo, SupplierInfo.class);
-            return baseMapper.updateById(update) > 0;
-        }
-        //如果是启用状态,就变成待修改审核状态
-        if (supplierInfo.getSupplyStatus() == SupplierStatusEnum.OFFICIAL_SUPPLIER.getCode()){
-            SupplierInfoTemporary supplierInfoTemporary = supplierInfoTemporaryService.querBySupplierId(id);
-            if (supplierInfoTemporary == null){
-                BeanUtils.copyProperties(bo, supplierInfoTemporary,"id");
-                supplierInfoTemporary.setSupplierId(id);
-                supplierInfoTemporary.setSupplyStatus(SupplierStatusEnum.REVIEW_UPDATED.getCode());
-                boolean save = supplierInfoTemporaryService.save(supplierInfoTemporary);
-                if (save == false){
-                    throw new RuntimeException("保存供应商待审核数据失败");
-                }
-            }
-            //一共三个地方
-            boolean flag =false;
-            //1.简称
-            if (bo.getShortName() != null && StringUtils.isNotBlank(bo.getShortName())){
-                supplierInfoTemporary.setShortName(bo.getShortName());
-                flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
-            }
-            //2.供货品牌
-            if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())){
-                supplierInfoTemporary.setOperatingBrand(bo.getOperatingBrand());
-                flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
-            }
-            //3.供货类目
-            if (bo.getOperatingCategory() != null && StringUtils.isNotBlank(bo.getOperatingCategory())){
-                supplierInfoTemporary.setOperatingCategory(bo.getOperatingCategory());
-                flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
-            }
-
-            supplierInfo.setSupplyStatus(SupplierStatusEnum.REVIEW_UPDATED.getCode());
-            boolean update =baseMapper.updateById(supplierInfo) > 0;
-            return update && flag;
-        }
-        //如果是待审核状态,就变成待修改审核状态
-        if (supplierInfo.getSupplyStatus() == SupplierStatusEnum.REVIEW_UPDATED.getCode()){
-            SupplierInfoTemporary supplierInfoTemporary = supplierInfoTemporaryService.querBySupplierId(id);
-            //一共三个地方
-            boolean flag =false;
-            //1.简称
-            if (bo.getShortName() != null && StringUtils.isNotBlank(bo.getShortName())){
-                supplierInfoTemporary.setShortName(bo.getShortName());
-                flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
-            }
-            //2.供货品牌
-            if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())){
-                supplierInfoTemporary.setOperatingBrand(bo.getOperatingBrand());
-                flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
-            }
-            //3.供货类目
-            if (bo.getOperatingCategory() != null && StringUtils.isNotBlank(bo.getOperatingCategory())){
-                supplierInfoTemporary.setOperatingCategory(bo.getOperatingCategory());
-                flag = supplierInfoTemporaryService.updateById(supplierInfoTemporary);
-            }
-            return flag;
-        }
-        return false;
-    }
-
     /**
      * 校验并批量删除供应商信息信息
      *
@@ -405,7 +336,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
      */
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
+        if (isValid) {
             //TODO 做一些业务上的校验,判断是否需要校验
         }
         return baseMapper.deleteByIds(ids) > 0;
@@ -436,6 +367,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
             // 将工商信息复制到供应商信息对象中
             copyBusinessInfoToSupplierInfoVo(supplierBusinessInfoVo, supplierInformationVo);
         }
+
         return supplierInformationVo;
     }
 
@@ -459,18 +391,19 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     public TableDataInfo<SupplierInfoVo> getSupplierInformation(SupplierInfoBo bo, PageQuery pageQuery) {
         LambdaQueryWrapper<SupplierInfo> lqw = new LambdaQueryWrapper<>();
         // 1. 基础分页查询
-        if (bo.getSupplyStatus() == null){
+        if (bo.getSupplyStatus() == null) {
             //正式供应商
             lqw.eq(SupplierInfo::getSupplyStatus, 1L);
-        }else {
-            lqw.eq(SupplierInfo::getSupplyStatus,bo.getSupplyStatus());
+        } else {
+            lqw.eq(SupplierInfo::getSupplyStatus, bo.getSupplyStatus());
         }
 
+
         // 2. 添加品牌条件
-        if (bo.getOperatingBrand()!= null && StringUtils.isNotBlank(bo.getOperatingBrand())){
-            if (StringUtils.isNotBlank(bo.getOperatingBrand())){
+        if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())) {
+            if (StringUtils.isNotBlank(bo.getOperatingBrand())) {
                 Long productBrandIdByName = remoteProductService.getProductBrandIdByName(bo.getOperatingBrand());
-                if (productBrandIdByName != null){
+                if (productBrandIdByName != null) {
                     lqw.like(SupplierInfo::getOperatingBrand, productBrandIdByName);
                 }
             }
@@ -680,14 +613,15 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         if (bo.getSupplyStatus() == null) {
             List<Long> status = Arrays.asList(0L, 4L);
             lqw.in(SupplierInfo::getSupplyStatus, status);
-        }else {
-            lqw.eq(SupplierInfo::getSupplyStatus,bo.getSupplyStatus());
+        } else {
+            lqw.eq(SupplierInfo::getSupplyStatus, bo.getSupplyStatus());
         }
+
         // 2.添加品牌条件
-        if (bo.getOperatingBrand()!= null && StringUtils.isNotBlank(bo.getOperatingBrand())){
-            if (StringUtils.isNotBlank(bo.getOperatingBrand())){
+        if (bo.getOperatingBrand() != null && StringUtils.isNotBlank(bo.getOperatingBrand())) {
+            if (StringUtils.isNotBlank(bo.getOperatingBrand())) {
                 Long productBrandIdByName = remoteProductService.getProductBrandIdByName(bo.getOperatingBrand());
-                if (productBrandIdByName != null){
+                if (productBrandIdByName != null) {
                     lqw.like(SupplierInfo::getOperatingBrand, productBrandIdByName);
                 }
             }
@@ -704,6 +638,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
             }
         }
+
         //产品经理和采购人员
         if (bo.getProductManager() != null || bo.getBuyer() != null) {
             // 查询符合条件的供应商ID列表
@@ -714,6 +649,8 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
                 lqw.in(SupplierInfo::getId, -1L);
             }
         }
+
+
         // 2. 基础分页查询
         Page<SupplierInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
         List<SupplierInfoVo> supplierVos = result.getRecords();
@@ -774,9 +711,9 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         lqw.eq(StringUtils.isNotBlank(bo.getSupplierName()), SupplierInfo::getEnterpriseName, bo.getSupplierName());
 
         //可供应品牌
-        if (StringUtils.isNotBlank(bo.getSupplyBrand())){
+        if (StringUtils.isNotBlank(bo.getSupplyBrand())) {
             Long productBrandIdByName = remoteProductService.getProductBrandIdByName(bo.getSupplyBrand());
-            if (productBrandIdByName != null){
+            if (productBrandIdByName != null) {
                 lqw.like(SupplierInfo::getOperatingBrand, productBrandIdByName);
             }
         }
@@ -784,10 +721,10 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         if (StringUtils.isNotBlank(bo.getProvince()) || StringUtils.isNotBlank(bo.getCity())) {
             // 查询符合条件的供应商ID列表
             List<Long> supplierIds = supplyAreaService.getSupplierIdsByArea(bo.getProvince(), bo.getCity());
-            if (supplierIds != null &&!supplierIds.isEmpty()) {
+            if (supplierIds != null && !supplierIds.isEmpty()) {
                 lqw.in(SupplierInfo::getId, supplierIds);
 
-            }else {
+            } else {
                 lqw.in(SupplierInfo::getId, -1L);
 
             }
@@ -797,9 +734,9 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         if (StringUtils.isNotBlank(bo.getBrandName())) {
             // 通过SupplierAuthorizeService查询符合条件的供应商ID
             List<Long> supplierIdsByBrand = commonSupplierAuthorizeMapper.getSupplierIdsByBrandName(bo.getBrandName());
-            if (supplierIdsByBrand!=null &&!supplierIdsByBrand.isEmpty()) {
+            if (supplierIdsByBrand != null && !supplierIdsByBrand.isEmpty()) {
                 lqw.in(SupplierInfo::getId, supplierIdsByBrand);
-            }else {
+            } else {
                 lqw.in(SupplierInfo::getId, -1L);
 
             }
@@ -857,6 +794,32 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
 
 
+    @Override
+    public Map<Long, String> selectSupplierNameByIds(Set<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        // 限制批量大小
+        if (ids.size() > 1000) {
+            throw new IllegalArgumentException("Batch size exceeds limit: " + ids.size());
+        }
+
+        List<SupplierInfo> supplierInfoList = baseMapper.selectByIds(ids);
+        Map<Long, String> resultMap = new HashMap<>(ids.size());
+
+        // 初始化所有请求的 ID 为 null
+        ids.forEach(id -> resultMap.put(id, null));
+
+        if (supplierInfoList != null) {
+            supplierInfoList.stream()
+                .filter(supplier -> supplier.getId() != null && supplier.getSupplierName() != null)
+                .forEach(supplier -> resultMap.put(supplier.getId(), supplier.getSupplierName()));
+        }
+
+        return resultMap;
+    }
+
     /**
      * 将工商信息复制到供应商信息VO中
      *
@@ -878,6 +841,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     }
 
     // ========== 3. 数据转换 - 映射构建方法 ==========
+
     /**
      * 构建品牌ID→名称映射表
      */
@@ -959,6 +923,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
                 Collectors.mapping(SupplyArea::getAreaName, Collectors.toList())
             ));
     }
+
     /**
      * 构建企业规模ID→名称映射表
      */
@@ -981,6 +946,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
         }
         return scaleMap;
     }
+
     /**
      * 构建供应商品类ID→名称映射表
      */
@@ -1296,6 +1262,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     }
 
     // ========== 5. 工具方法 ==========
+
     /**
      * 收集供应商列表中的所有品牌ID
      */
@@ -1358,30 +1325,30 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
     private SupplierInfoVo handleProcurementInfo(SupplierInfoVo records) {
 
-            // 查询管理人员和采购人员
-            Long id = records.getId();
-            //查询供应地区
-            LambdaQueryWrapper<SupplyArea> supplyProvinceAreaLqw = Wrappers.lambdaQuery();
-            supplyProvinceAreaLqw.eq(SupplyArea::getSupplierId, id)
-                .eq(SupplyArea::getLevel,"1");
-            List<SupplyArea> supplyProvinceArealist = supplyAreaService.list(supplyProvinceAreaLqw);
-
-            LambdaQueryWrapper<SupplyArea> supplyCityAreaLqw = Wrappers.lambdaQuery();
-            supplyCityAreaLqw.eq(SupplyArea::getSupplierId, id)
-                .eq(SupplyArea::getLevel,"2");
-            List<SupplyArea> supplyCityArealist = supplyAreaService.list(supplyCityAreaLqw);
-
-            // 拼接省份名称
-            String provinceNames = supplyProvinceArealist.stream()
-                .map(SupplyArea::getAreaName)
-                .filter(Objects::nonNull)
-                .collect(Collectors.joining(","));
+        // 查询管理人员和采购人员
+        Long id = records.getId();
+        //查询供应地区
+        LambdaQueryWrapper<SupplyArea> supplyProvinceAreaLqw = Wrappers.lambdaQuery();
+        supplyProvinceAreaLqw.eq(SupplyArea::getSupplierId, id)
+            .eq(SupplyArea::getLevel, "1");
+        List<SupplyArea> supplyProvinceArealist = supplyAreaService.list(supplyProvinceAreaLqw);
+
+        LambdaQueryWrapper<SupplyArea> supplyCityAreaLqw = Wrappers.lambdaQuery();
+        supplyCityAreaLqw.eq(SupplyArea::getSupplierId, id)
+            .eq(SupplyArea::getLevel, "2");
+        List<SupplyArea> supplyCityArealist = supplyAreaService.list(supplyCityAreaLqw);
+
+        // 拼接省份名称
+        String provinceNames = supplyProvinceArealist.stream()
+            .map(SupplyArea::getAreaName)
+            .filter(Objects::nonNull)
+            .collect(Collectors.joining(","));
 
-            // 拼接城市名称
-            String cityNames = supplyCityArealist.stream()
-                .map(SupplyArea::getAreaName)
-                .filter(Objects::nonNull)
-                .collect(Collectors.joining(","));
+        // 拼接城市名称
+        String cityNames = supplyCityArealist.stream()
+            .map(SupplyArea::getAreaName)
+            .filter(Objects::nonNull)
+            .collect(Collectors.joining(","));
 
         records.setProvince(provinceNames);
         records.setCity(cityNames);
@@ -1499,7 +1466,6 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
     }*/
 
 
-
     /**
      * 处理采购员和产品经理信息和品牌和供应商类型
      */
@@ -1586,12 +1552,12 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
             // 2. 查询供应地区(原有逻辑)
             LambdaQueryWrapper<SupplyArea> supplyProvinceAreaLqw = Wrappers.lambdaQuery();
             supplyProvinceAreaLqw.eq(SupplyArea::getSupplierId, id)
-                .eq(SupplyArea::getLevel,"1");
+                .eq(SupplyArea::getLevel, "1");
             List<SupplyArea> supplyProvinceArealist = supplyAreaService.list(supplyProvinceAreaLqw);
 
             LambdaQueryWrapper<SupplyArea> supplyCityAreaLqw = Wrappers.lambdaQuery();
             supplyCityAreaLqw.eq(SupplyArea::getSupplierId, id)
-                .eq(SupplyArea::getLevel,"2");
+                .eq(SupplyArea::getLevel, "2");
             List<SupplyArea> supplyCityArealist = supplyAreaService.list(supplyCityAreaLqw);
 
             if (!supplyProvinceArealist.isEmpty()) {
@@ -1795,8 +1761,9 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
     /**
      * 复制分页结果(保持原分页的总数/页码/页大小,仅替换数据列表)
+     *
      * @param originalPage 原分页对象(包含总数、页码等元数据)
-     * @param newRecords 新的数据集
+     * @param newRecords   新的数据集
      * @return 新的分页对象
      */
     private Page<SupplierInfoVo> copyPageResult(Page<SupplierInfoVo> originalPage, List<SupplierInfoVo> newRecords) {
@@ -1814,6 +1781,7 @@ public class SupplierInfoServiceImpl  extends ServiceImpl<SupplierInfoMapper, Su
 
         return newPage;
     }
+
     private LambdaQueryWrapper<SupplierInfo> buildQueryWrapper(SupplierInfoBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<SupplierInfo> lqw = Wrappers.lambdaQuery();

+ 31 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/OrderDeliverController.java

@@ -1,11 +1,17 @@
 package org.dromara.order.controller;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.order.domain.OrderDeliver;
+import org.dromara.order.domain.vo.OrderDeliverProductVo;
+import org.dromara.order.domain.vo.OrderProductVo;
 import org.dromara.order.utils.kd100.domain.TrackVO;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
@@ -113,4 +119,29 @@ public class OrderDeliverController extends BaseController {
                           @PathVariable("ids") Long[] ids) {
         return toAjax(orderDeliverService.deleteWithValidByIds(List.of(ids), true));
     }
+
+    /*根据客户id查询客户对账单对应的订单*/
+    @GetMapping("/getCustomerDeliverOrders/{customerId}")
+    public TableDataInfo<OrderDeliverVo> getCustomerDeliverOrders(@PathVariable Long customerId,
+                                                                  PageQuery pageQuery) {
+        return orderDeliverService.listDeliverOrderByCustomerIdPage(customerId, pageQuery);
+    }
+
+    /**
+     * 根据订单id查询订单商品
+     */
+    @GetMapping("/getCustomerOrderProductList")
+    public TableDataInfo<OrderDeliverProductVo> getCustomerOrderProductList(
+        @RequestParam(value = "orderId", required = false) List<Long> orderIdList) {
+
+        if (CollectionUtils.isEmpty(orderIdList)) {
+            throw new IllegalArgumentException("订单ID列表不能为空");
+        }
+        if (orderIdList.size() > 1000) {
+            throw new IllegalArgumentException("订单ID数量不能超过1000个");
+        }
+
+        Set<Long> uniqueOrderIds = new HashSet<>(orderIdList);
+        return orderDeliverService.getCustomerOrderProductList(uniqueOrderIds);
+    }
 }

+ 12 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/OrderMainController.java

@@ -143,6 +143,18 @@ public class OrderMainController extends BaseController {
         return toAjax(orderMainService.deleteWithValidByIds(List.of(ids), true));
     }
 
+    /**
+     * 关闭订单
+     *
+     * @param ids 主键串
+     */
+    @Log(title = "订单主信息", businessType = BusinessType.UPDATE)
+    @PostMapping("closeOrder")
+    public R<Void> closeOrder(@NotEmpty(message = "主键不能为空")
+                              @RequestParam Long[] ids) {
+        return toAjax(orderMainService.closeOrderByIds(List.of(ids), true));
+    }
+
     /**
      * 状态修改
      */

+ 106 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/OrderOperateLogController.java

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

+ 5 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderAssignment.java

@@ -38,6 +38,11 @@ public class OrderAssignment extends TenantEntity {
 
     private String orderNo;
 
+    /**
+     * 子订单ID 分配后新的订单的ID
+     */
+    private Long childOrderId;
+
     /**
      * 分配前平台
      */

+ 12 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderDeliverProduct.java

@@ -6,6 +6,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 
 import java.io.Serial;
+import java.math.BigDecimal;
 
 /**
  * 订单发货商品明细对象 order_deliver_product
@@ -62,6 +63,17 @@ public class OrderDeliverProduct extends TenantEntity {
      */
     private Long deliverNum;
 
+
+    /**
+     * 订单单价(元)
+     */
+    private BigDecimal orderPrice;
+
+    /**
+     * 行小计金额(元)
+     */
+    private BigDecimal subtotal;
+
     private String dataSource;
 
     /**

+ 57 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderOperateLog.java

@@ -0,0 +1,57 @@
+package org.dromara.order.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 订单操作记录对象 order_operate_log
+ *
+ * @author LionLi
+ * @date 2026-01-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("order_operate_log")
+public class OrderOperateLog extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 订单id
+     */
+    private Long orderId;
+
+    /**
+     * 操作
+     */
+    private String action;
+
+    /**
+     * 用户名
+     */
+    private String userName;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 6 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderProduct.java

@@ -41,6 +41,12 @@ public class OrderProduct extends TenantEntity {
      */
     private Long orderId;
 
+    /*原始商品ID(用于子订单商品指向主订单)*/
+    private Long originalItemId;
+
+    /*被分配到的子订单ID*/
+    private Long assignedChildOrderId;
+
     /**
      * 订单编号
      */

+ 12 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderDeliverProductBo.java

@@ -9,6 +9,8 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import jakarta.validation.constraints.*;
 
+import java.math.BigDecimal;
+
 /**
  * 订单发货商品明细业务对象 order_deliver_product
  *
@@ -60,6 +62,16 @@ public class OrderDeliverProductBo extends BaseEntity {
      */
     private Long deliverNum;
 
+    /**
+     * 订单单价(元)
+     */
+    private BigDecimal orderPrice;
+
+    /**
+     * 行小计金额(元)
+     */
+    private BigDecimal subtotal;
+
     private String dataSource;
 
     /**

+ 49 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderOperateLogBo.java

@@ -0,0 +1,49 @@
+package org.dromara.order.domain.bo;
+
+import org.dromara.order.domain.OrderOperateLog;
+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.*;
+
+/**
+ * 订单操作记录业务对象 order_operate_log
+ *
+ * @author LionLi
+ * @date 2026-01-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = OrderOperateLog.class, reverseConvertGenerate = false)
+public class OrderOperateLogBo extends BaseEntity {
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 订单id
+     */
+    private Long orderId;
+
+    /**
+     * 操作
+     */
+    private String action;
+
+    /**
+     * 用户名
+     */
+    private String userName;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 6 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderProductBo.java

@@ -40,6 +40,12 @@ public class OrderProductBo extends BaseEntity {
     @NotNull(message = "订单ID不能为空", groups = {AddGroup.class, EditGroup.class})
     private Long orderId;
 
+    /*原始商品ID(用于子订单商品指向主订单)*/
+    private Long originalItemId;
+
+    /*被分配到的子订单ID*/
+    private Long assignedChildOrderId;
+
     /**
      * 订单编号
      */

+ 12 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/dto/AssignmentStatsDto.java

@@ -0,0 +1,12 @@
+package org.dromara.order.domain.dto;
+
+import lombok.Data;
+
+@Data
+public class AssignmentStatsDto {
+
+    private Long total;
+
+    private Long assigned;
+
+}

+ 13 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderAssignmentVo.java

@@ -44,6 +44,11 @@ public class OrderAssignmentVo implements Serializable {
 
     private String orderNo;
 
+    /**
+     * 子订单ID 分配后新的订单的ID
+     */
+    private Long childOrderId;
+
     /**
      * 分配前平台
      */
@@ -92,10 +97,18 @@ public class OrderAssignmentVo implements Serializable {
      */
     private Long assigneeId;
 
+    private String assigneeName;
+
     /**
      * 分配对象类型(srm=供应商, bp=伙伴商)
      */
     private String assigneeType;
 
+    private Long createBy;
+
+    private String createName;
+
+    private Date createTime;
+
 
 }

+ 13 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderDeliverProductVo.java

@@ -10,6 +10,7 @@ import lombok.Data;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.util.Date;
 
 
@@ -74,6 +75,16 @@ public class OrderDeliverProductVo implements Serializable {
     @ExcelProperty(value = "本次发货数量")
     private Long deliverNum;
 
+    /**
+     * 订单单价(元)
+     */
+    private BigDecimal orderPrice;
+
+    /**
+     * 行小计金额(元)
+     */
+    private BigDecimal subtotal;
+
     /**
      * 备注
      */
@@ -82,5 +93,7 @@ public class OrderDeliverProductVo implements Serializable {
 
     private String dataSource;
 
+    private String orderNo;
+
 
 }

+ 15 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderDeliverVo.java

@@ -10,6 +10,7 @@ import lombok.Data;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
 
@@ -131,5 +132,19 @@ public class OrderDeliverVo implements Serializable {
 
     private Date createTime;
 
+    private String customerName;
+
+    private BigDecimal totalAmount;
+
+    private Date orderTime;
+
+    private Long createBy;
+
+    private String createName;
+
+    private Long createDept;
+
+    private String createDeptName;
+
     private List<OrderDeliverProductVo> deliverProductList;
 }

+ 62 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderOperateLogVo.java

@@ -0,0 +1,62 @@
+package org.dromara.order.domain.vo;
+
+import org.dromara.order.domain.OrderOperateLog;
+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;
+
+
+
+/**
+ * 订单操作记录视图对象 order_operate_log
+ *
+ * @author LionLi
+ * @date 2026-01-26
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = OrderOperateLog.class)
+public class OrderOperateLogVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 订单id
+     */
+    @ExcelProperty(value = "订单id")
+    private Long orderId;
+
+    /**
+     * 操作
+     */
+    @ExcelProperty(value = "操作")
+    private String action;
+
+    /**
+     * 用户名
+     */
+    @ExcelProperty(value = "用户名")
+    private String userName;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 6 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderProductVo.java

@@ -47,6 +47,12 @@ public class OrderProductVo implements Serializable {
     @ExcelProperty(value = "订单ID")
     private Long orderId;
 
+    /*原始商品ID(用于子订单商品指向主订单)*/
+    private Long originalItemId;
+
+    /*被分配到的子订单ID*/
+    private Long assignedChildOrderId;
+
     /**
      * 订单编号
      */

+ 1 - 1
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/mapper/OrderDeliverMapper.java

@@ -1,8 +1,8 @@
 package org.dromara.order.mapper;
 
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 import org.dromara.order.domain.OrderDeliver;
 import org.dromara.order.domain.vo.OrderDeliverVo;
-import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 
 /**
  * 订单发货主Mapper接口

+ 15 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/mapper/OrderOperateLogMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.order.mapper;
+
+import org.dromara.order.domain.OrderOperateLog;
+import org.dromara.order.domain.vo.OrderOperateLogVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 订单操作记录Mapper接口
+ *
+ * @author LionLi
+ * @date 2026-01-26
+ */
+public interface OrderOperateLogMapper extends BaseMapperPlus<OrderOperateLog, OrderOperateLogVo> {
+
+}

+ 19 - 1
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/mapper/OrderProductMapper.java

@@ -1,11 +1,14 @@
 package org.dromara.order.mapper;
 
 import org.apache.ibatis.annotations.Param;
+import org.dromara.common.core.enums.OrderAssignStatus;
 import org.dromara.order.domain.OrderProduct;
+import org.dromara.order.domain.dto.AssignmentStatsDto;
 import org.dromara.order.domain.vo.OrderProductVo;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 import org.dromara.order.domain.vo.OrderQuantitySummary;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -36,5 +39,20 @@ public interface OrderProductMapper extends BaseMapperPlus<OrderProduct, OrderPr
 
 
     /*查询  订单商品种数与已分配商品种数*/
-    Map getAssignmentStats(@Param("orderId") Long orderId);
+    AssignmentStatsDto getAssignmentStats(@Param("parentId") Long parentId);
+
+    /**
+     * 批量更新商品的分配状态和关联的子订单ID
+     *
+     * @param itemIds          要更新的商品ID列表
+     * @param assignmentStatus 分配状态(如 "1" 表示已分配)
+     * @param childOrderId     被分配到的子订单ID
+     * @param updateTime       更新时间
+     */
+    void updateAssignmentInfo(
+        @Param("itemIds") List<Long> itemIds,
+        @Param("assignmentStatus") String assignmentStatus,
+        @Param("childOrderId") Long childOrderId,
+        @Param("updateTime") Date updateTime
+    );
 }

+ 171 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/pc/controller/PcOrderController.java

@@ -0,0 +1,171 @@
+package org.dromara.order.pc.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.order.domain.bo.OrderMainBo;
+import org.dromara.order.domain.vo.OrderMainVo;
+import org.dromara.order.domain.vo.OrderProductVo;
+import org.dromara.order.domain.vo.OrderStatusStats;
+import org.dromara.order.service.IOrderMainService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * PC端 - 订单管理
+ * 前端访问路由地址为:/pc/enterprise/order
+ *
+ * @author Claude
+ * @date 2026-01-28
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pc/enterprise/order")
+public class PcOrderController extends BaseController {
+
+    private final IOrderMainService orderMainService;
+
+    /**
+     * 查询当前企业的订单列表
+     * PC端用户只能查询自己企业的订单
+     */
+    @GetMapping("/list")
+    public TableDataInfo<OrderMainVo> list(OrderMainBo bo, PageQuery pageQuery) {
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+        // 强制设置企业ID,防止越权访问
+        bo.setCustomerId(customerId);
+
+        return orderMainService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 查询当前企业的订单状态统计
+     * 返回各状态订单数量
+     */
+    @GetMapping("/statusStats")
+    public R<OrderStatusStats> getStatusStats() {
+        return R.ok(orderMainService.queryOrderStatusStats());
+    }
+
+    /**
+     * 获取订单详细信息
+     * PC端用户只能查询自己企业的订单
+     *
+     * @param id 订单主键
+     */
+    @GetMapping("/{id}")
+    public R<OrderMainVo> getInfo(@NotNull(message = "主键不能为空")
+                                  @PathVariable("id") Long id) {
+        // 查询订单信息
+        OrderMainVo vo = orderMainService.queryById(id);
+
+        // 验证订单是否属于当前用户的企业
+        if (vo != null) {
+            Long customerId = LoginHelper.getUserId();
+            if (!customerId.equals(vo.getCustomerId())) {
+                return R.fail("无权访问该订单");
+            }
+        }
+
+        return R.ok(vo);
+    }
+
+    /**
+     * 根据订单ID查询订单商品明细
+     * PC端用户只能查询自己企业的订单商品
+     *
+     * @param orderIds 订单ID列表
+     */
+    @GetMapping("/products")
+    public TableDataInfo<OrderProductVo> getOrderProducts(@RequestParam("orderIds") List<Long> orderIds) {
+        if (orderIds == null || orderIds.isEmpty()) {
+            throw new IllegalArgumentException("订单ID列表不能为空");
+        }
+        if (orderIds.size() > 1000) {
+            throw new IllegalArgumentException("订单ID数量不能超过1000个");
+        }
+
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+
+        // 验证所有订单是否都属于当前用户的企业
+        for (Long orderId : orderIds) {
+            OrderMainVo order = orderMainService.queryById(orderId);
+            if (order == null) {
+                throw new IllegalArgumentException("订单ID " + orderId + " 不存在");
+            }
+            if (!customerId.equals(order.getCustomerId())) {
+                throw new IllegalArgumentException("无权访问订单ID " + orderId);
+            }
+        }
+
+        Set<Long> uniqueOrderIds = new HashSet<>(orderIds);
+        return orderMainService.getCustomerOrderProductList(uniqueOrderIds);
+    }
+
+    /**
+     * 取消订单
+     * PC端用户只能取消自己企业的订单
+     *
+     * @param bo 订单业务对象
+     */
+    @Log(title = "PC端-订单管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/cancel")
+    public R<Void> cancelOrder(@RequestBody OrderMainBo bo) {
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+
+        // 验证订单是否属于当前用户的企业
+        OrderMainVo existingOrder = orderMainService.queryById(bo.getId());
+        if (existingOrder == null) {
+            return R.fail("订单不存在");
+        }
+        if (!customerId.equals(existingOrder.getCustomerId())) {
+            return R.fail("无权取消该订单");
+        }
+
+        // 强制设置企业ID
+        bo.setCustomerId(customerId);
+
+        return toAjax(orderMainService.updateStatus(bo));
+    }
+
+    /**
+     * 审核订单
+     * PC端企业客户审核自己企业的订单
+     *
+     * @param bo 订单业务对象
+     */
+    @Log(title = "PC端-订单审核", businessType = BusinessType.UPDATE)
+    @PutMapping("/checkStatus")
+    public R<Void> checkStatus(@RequestBody OrderMainBo bo) {
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+
+        // 验证订单是否属于当前用户的企业
+        OrderMainVo existingOrder = orderMainService.queryById(bo.getId());
+        if (existingOrder == null) {
+            return R.fail("订单不存在");
+        }
+        if (!customerId.equals(existingOrder.getCustomerId())) {
+            return R.fail("无权审核该订单");
+        }
+
+        // 强制设置企业ID
+        bo.setCustomerId(customerId);
+
+        return toAjax(orderMainService.updateCheckStatus(bo));
+    }
+}

+ 144 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/pc/controller/PcOrderReturnController.java

@@ -0,0 +1,144 @@
+package org.dromara.order.pc.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.order.domain.bo.OrderReturnBo;
+import org.dromara.order.domain.vo.OrderReturnVo;
+import org.dromara.order.service.IOrderReturnService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * PC端 - 售后服务管理
+ * 前端访问路由地址为:/pc/enterprise/orderReturn
+ *
+ * @author Claude
+ * @date 2026-01-29
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pc/enterprise/orderReturn")
+public class PcOrderReturnController extends BaseController {
+
+    private final IOrderReturnService orderReturnService;
+
+    /**
+     * 查询当前企业的售后申请列表
+     * PC端用户只能查询自己企业的售后记录
+     */
+    @GetMapping("/list")
+    public TableDataInfo<OrderReturnVo> list(OrderReturnBo bo, PageQuery pageQuery) {
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+        // 强制设置企业ID,防止越权访问
+        bo.setCustomerId(customerId);
+
+        return orderReturnService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取售后申请详细信息
+     * PC端用户只能查询自己企业的售后记录
+     *
+     * @param id 主键
+     */
+    @GetMapping("/{id}")
+    public R<OrderReturnVo> getInfo(@NotNull(message = "主键不能为空")
+                                    @PathVariable("id") Long id) {
+        // 查询售后信息
+        OrderReturnVo vo = orderReturnService.queryById(id);
+
+        // 验证售后记录是否属于当前用户的企业
+        if (vo != null) {
+            Long customerId = LoginHelper.getUserId();
+            if (!customerId.equals(vo.getCustomerId())) {
+                return R.fail("无权访问该售后记录");
+            }
+        }
+
+        return R.ok(vo);
+    }
+
+    /**
+     * 新增售后申请
+     * 企业客户申请退货/售后服务
+     */
+    @Log(title = "PC端-售后服务", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody OrderReturnBo bo) {
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+        // 强制设置企业ID,防止为其他企业创建售后申请
+        bo.setCustomerId(customerId);
+
+        return toAjax(orderReturnService.insertByBo(bo));
+    }
+
+    /**
+     * 修改售后申请
+     * 企业客户修改自己的售后申请(仅限未审核状态)
+     */
+    @Log(title = "PC端-售后服务", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody OrderReturnBo bo) {
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+
+        // 验证售后记录是否属于当前用户的企业
+        OrderReturnVo existingReturn = orderReturnService.queryById(bo.getId());
+        if (existingReturn == null) {
+            return R.fail("售后记录不存在");
+        }
+        if (!customerId.equals(existingReturn.getCustomerId())) {
+            return R.fail("无权修改该售后记录");
+        }
+
+        // 强制设置企业ID
+        bo.setCustomerId(customerId);
+
+        return toAjax(orderReturnService.updateByBo(bo));
+    }
+
+    /**
+     * 删除售后申请
+     * 企业客户删除自己的售后申请(仅限未审核状态)
+     *
+     * @param ids 主键串
+     */
+    @Log(title = "PC端-售后服务", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        // 获取当前登录用户的企业ID
+        Long customerId = LoginHelper.getUserId();
+
+        // 验证所有售后记录是否都属于当前用户的企业
+        for (Long id : ids) {
+            OrderReturnVo orderReturn = orderReturnService.queryById(id);
+            if (orderReturn == null) {
+                return R.fail("售后记录ID " + id + " 不存在");
+            }
+            if (!customerId.equals(orderReturn.getCustomerId())) {
+                return R.fail("无权删除售后记录ID " + id);
+            }
+        }
+
+        return toAjax(orderReturnService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 14 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderDeliverService.java

@@ -2,14 +2,17 @@ package org.dromara.order.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.dromara.order.domain.OrderDeliver;
+import org.dromara.order.domain.vo.OrderDeliverProductVo;
 import org.dromara.order.domain.vo.OrderDeliverVo;
 import org.dromara.order.domain.bo.OrderDeliverBo;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.order.domain.vo.OrderProductVo;
 import org.dromara.order.utils.kd100.domain.TrackVO;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 /**
  * 订单发货主Service接口
@@ -77,4 +80,15 @@ public interface IOrderDeliverService extends IService<OrderDeliver> {
      * @return 是否删除成功
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 分页查询客户发货单
+     *
+     * @param customerId 客户ID
+     * @param pageQuery  分页参数
+     * @return 分页结果
+     */
+    TableDataInfo<OrderDeliverVo> listDeliverOrderByCustomerIdPage(Long customerId, PageQuery pageQuery);
+
+    TableDataInfo<OrderDeliverProductVo> getCustomerOrderProductList(Set<Long> orderIdList);
 }

+ 2 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderMainService.java

@@ -86,4 +86,6 @@ public interface IOrderMainService extends IService<OrderMain> {
      * @return 是否删除成功
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    Boolean closeOrderByIds(Collection<Long> ids, Boolean isValid);
 }

+ 70 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderOperateLogService.java

@@ -0,0 +1,70 @@
+package org.dromara.order.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.order.domain.OrderOperateLog;
+import org.dromara.order.domain.vo.OrderOperateLogVo;
+import org.dromara.order.domain.bo.OrderOperateLogBo;
+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 2026-01-26
+ */
+public interface IOrderOperateLogService extends IService<OrderOperateLog>{
+
+    /**
+     * 查询订单操作记录
+     *
+     * @param id 主键
+     * @return 订单操作记录
+     */
+    OrderOperateLogVo queryById(Long id);
+
+    /**
+     * 分页查询订单操作记录列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 订单操作记录分页列表
+     */
+    TableDataInfo<OrderOperateLogVo> queryPageList(OrderOperateLogBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的订单操作记录列表
+     *
+     * @param bo 查询条件
+     * @return 订单操作记录列表
+     */
+    List<OrderOperateLogVo> queryList(OrderOperateLogBo bo);
+
+    /**
+     * 新增订单操作记录
+     *
+     * @param bo 订单操作记录
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(OrderOperateLogBo bo);
+
+    /**
+     * 修改订单操作记录
+     *
+     * @param bo 订单操作记录
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(OrderOperateLogBo bo);
+
+    /**
+     * 校验并批量删除订单操作记录信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 154 - 37
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderAssignmentServiceImpl.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.context.PlatformContext;
 import org.dromara.common.core.enums.AssigneeTypeConstants;
 import org.dromara.common.core.enums.OrderAssignStatus;
@@ -20,21 +21,26 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.redis.utils.SequenceUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.customer.api.RemotePartnerInfoService;
+import org.dromara.customer.api.RemoteSupplierInfoService;
 import org.dromara.order.domain.OrderAssignment;
 import org.dromara.order.domain.OrderMain;
 import org.dromara.order.domain.OrderProduct;
 import org.dromara.order.domain.OrderProductAssignRule;
 import org.dromara.order.domain.bo.OrderAssignmentBo;
 import org.dromara.order.domain.bo.OrderSplitAssignBo;
+import org.dromara.order.domain.dto.AssignmentStatsDto;
 import org.dromara.order.domain.vo.OrderAssignmentVo;
 import org.dromara.order.domain.vo.OrderMainVo;
 import org.dromara.order.mapper.OrderAssignmentMapper;
 import org.dromara.order.mapper.OrderMainMapper;
 import org.dromara.order.mapper.OrderProductMapper;
 import org.dromara.order.service.IOrderAssignmentService;
+import org.dromara.system.api.RemoteUserService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
 import java.time.Duration;
 import java.util.*;
 import java.util.function.Function;
@@ -53,6 +59,15 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
 
     private static final String CHILD_ORDER_NO_KEY = "order_main:child_order_no";
 
+    @DubboReference
+    private RemotePartnerInfoService remotePartnerInfoService;
+
+    @DubboReference
+    private RemoteSupplierInfoService remoteSupplierInfoService;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
     private final OrderAssignmentMapper baseMapper;
 
     private final OrderMainMapper orderMainMapper;
@@ -81,6 +96,47 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
     public TableDataInfo<OrderAssignmentVo> queryPageList(OrderAssignmentBo bo, PageQuery pageQuery) {
         LambdaQueryWrapper<OrderAssignment> lqw = buildQueryWrapper(bo);
         Page<OrderAssignmentVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        List<OrderAssignmentVo> records = result.getRecords();
+        if (CollUtil.isNotEmpty(records)) {
+            Set<Long> createUserIds = records.stream().map(OrderAssignmentVo::getCreateBy).collect(Collectors.toSet());
+
+
+            Set<Long> partnerIds = records.stream()
+                .filter(r -> AssigneeTypeConstants.PARTNER.getCode().equals(r.getAssignType()))
+                .map(OrderAssignmentVo::getAssigneeId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+            Set<Long> supplierIds = records.stream()
+                .filter(r -> AssigneeTypeConstants.SUPPLIER.getCode().equals(r.getAssignType()))
+                .map(OrderAssignmentVo::getAssigneeId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+            // 批量查询名称
+            Map<Long, String> createUserNameMap = remoteUserService.selectUserNamesByIds(createUserIds.stream().toList());
+            Map<Long, String> partnerNameMap = remotePartnerInfoService.selectPartnerNameByIds(partnerIds);
+            Map<Long, String> supplierNameMap = remoteSupplierInfoService.selectSupplierNameByIds(supplierIds);
+
+            // 设置 assigneeName
+            for (OrderAssignmentVo record : records) {
+                Long assigneeId = record.getAssigneeId();
+                String assignType = record.getAssignType();
+
+                if (assigneeId == null || assignType == null) {
+                    record.setAssigneeName(null);
+                    continue;
+                }
+
+                String name = null;
+                if (AssigneeTypeConstants.PARTNER.getCode().equals(assignType)) {
+                    name = partnerNameMap.get(assigneeId);
+                } else if (AssigneeTypeConstants.SUPPLIER.getCode().equals(assignType)) {
+                    name = supplierNameMap.get(assigneeId);
+                }
+                record.setAssigneeName(name);
+                record.setCreateName(createUserNameMap.get(record.getCreateBy()));
+            }
+        }
         return TableDataInfo.build(result);
     }
 
@@ -99,7 +155,7 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
     private LambdaQueryWrapper<OrderAssignment> buildQueryWrapper(OrderAssignmentBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<OrderAssignment> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(OrderAssignment::getId);
+        lqw.orderByDesc(OrderAssignment::getId);
         lqw.eq(bo.getOrderId() != null, OrderAssignment::getOrderId, bo.getOrderId());
         lqw.eq(StringUtils.isNotBlank(bo.getPlatformBefore()), OrderAssignment::getPlatformBefore, bo.getPlatformBefore());
         lqw.eq(StringUtils.isNotBlank(bo.getPlatformAfter()), OrderAssignment::getPlatformAfter, bo.getPlatformAfter());
@@ -208,11 +264,11 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
         if (parentOrder == null) {
             throw new ServiceException("父订单不存在");
         }
-        if ("1".equals(parentOrder.getSplitStatus())) { // 已拆分
+      /*  if (OrderSplitStatus.SPLITED.getCode().equals(parentOrder.getSplitStatus())) {
             throw new ServiceException("该订单已被拆分,不可重复操作");
-        }
+        }*/
 
-        // 2. 验证商品行
+        // 2. 验证商品行(必须属于该主订单且未分配)
         List<Long> itemIds = rules.stream().map(OrderProductAssignRule::getItemId).collect(Collectors.toList());
         List<OrderProduct> items = orderProductMapper.selectBatchIds(itemIds);
         Map<Long, OrderProduct> itemMap = items.stream()
@@ -223,12 +279,12 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
             if (item == null || !parentId.equals(item.getOrderId())) {
                 throw new ServiceException("商品行不属于该订单或不存在");
             }
-            if ("1".equals(item.getAssignmentStatus())) { // 已分配
+            if (OrderAssignStatus.ASSIGNED.getCode().equals(item.getAssignmentStatus())) {
                 throw new ServiceException("商品 [" + item.getProductName() + "] 已分配,不可重复操作");
             }
-            // 校验 assigneeType 合法性
             String type = rule.getAssigneeType();
-            if (!AssigneeTypeConstants.SUPPLIER.getCode().equals(type) && !AssigneeTypeConstants.PARTNER.getCode().equals(type)) {
+            if (!AssigneeTypeConstants.SUPPLIER.getCode().equals(type) &&
+                !AssigneeTypeConstants.PARTNER.getCode().equals(type)) {
                 throw new ServiceException("分配对象类型不支持: " + type);
             }
         }
@@ -239,51 +295,119 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
         Map<AssigneeKey, List<OrderProductAssignRule>> assigneeGroups = rules.stream()
             .collect(Collectors.groupingBy(rule -> new AssigneeKey(rule.getAssigneeId(), rule.getAssigneeType())));
 
-        // 4. 为每个分配对象创建子订单
+        // 4. 为每个分配对象创建子订单 + 商品副本
         for (Map.Entry<AssigneeKey, List<OrderProductAssignRule>> entry : assigneeGroups.entrySet()) {
             AssigneeKey key = entry.getKey();
             List<OrderProductAssignRule> groupRules = entry.getValue();
 
-            // 创建子订单
+            // 创建子订单(从主订单拷贝必要字段)
             OrderMain childOrder = copyParentToChild(parentOrder, key.id(), key.type());
             orderMainMapper.insert(childOrder);
+            Long childOrderId = childOrder.getId();
+
+            // 准备子订单商品列表(副本)
+            List<OrderProduct> childProducts = new ArrayList<>();
+            List<Long> parentItemIdsToUpdate = new ArrayList<>();
+
+            for (OrderProductAssignRule rule : groupRules) {
+                OrderProduct parentItem = itemMap.get(rule.getItemId());
+
+                // 1. 收集主订单商品ID,用于后续批量更新分配状态
+                parentItemIdsToUpdate.add(parentItem.getId());
+
+                // 2. 创建子订单商品副本
+                OrderProduct childProduct = new OrderProduct();
+
+                // 继承租户信息
+                childProduct.setTenantId(parentItem.getTenantId());
+
+                // 关联信息
+                childProduct.setOrderId(childOrderId);           // 归属子订单
+                childProduct.setOriginalItemId(parentItem.getId()); // 指向主订单商品
+
+                // 订单基础信息
+                childProduct.setOrderNo(childOrder.getOrderNo()); // 子订单编号
+                childProduct.setDataSource(parentItem.getDataSource());
+
+                // 商品核心信息
+                childProduct.setProductId(parentItem.getProductId());
+                childProduct.setProductNo(parentItem.getProductNo());
+                childProduct.setProductName(parentItem.getProductName());
+                childProduct.setProductUnitId(parentItem.getProductUnitId());
+                childProduct.setProductUnit(parentItem.getProductUnit());
+                childProduct.setProductImage(parentItem.getProductImage());
+
+                // 价格信息
+                childProduct.setPlatformPrice(parentItem.getPlatformPrice());
+                childProduct.setMarketPrice(parentItem.getMarketPrice());
+                childProduct.setMemberPrice(parentItem.getMemberPrice());
+                childProduct.setPurchasingPrice(parentItem.getPurchasingPrice());
+                childProduct.setMaxPurchasePrice(parentItem.getMaxPurchasePrice());
+                childProduct.setMinSellingPrice(parentItem.getMinSellingPrice());
+                childProduct.setTaxRate(parentItem.getTaxRate());
+                childProduct.setOrderPrice(parentItem.getOrderPrice());
+                childProduct.setSubtotal(parentItem.getSubtotal());
+
+                // 数量信息(注意:履约数量应重置)
+                childProduct.setOrderQuantity(parentItem.getOrderQuantity());
+                childProduct.setMinOrderQuantity(parentItem.getMinOrderQuantity());
+                childProduct.setUnsentQuantity(parentItem.getOrderQuantity()); // 未发货 = 总数
+                childProduct.setReturnAmount(BigDecimal.ZERO);
+
+                // 状态与标志
+                childProduct.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode()); // 已分配
+
+                // 时间 & 其他
+                childProduct.setPreDeliveryDate(parentItem.getPreDeliveryDate());
+                childProduct.setRemark(parentItem.getRemark());
+
+                childProducts.add(childProduct);
+            }
 
-            // 更新商品:指向子订单 + 标记已分配
-            List<Long> groupItemIds = groupRules.stream().map(OrderProductAssignRule::getItemId).collect(Collectors.toList());
-            orderProductMapper.updateForSplitAssign(
-                groupItemIds,
-                childOrder.getId(),
-                OrderAssignStatus.ASSIGNED.getCode() // assignmentStatus = 已分配
-            );
+            // 批量插入子订单商品
+            if (!childProducts.isEmpty()) {
+                orderProductMapper.insertBatch(childProducts);
+            }
 
-            // 插入分配记录
+            // 批量更新主订单商品:标记已分配 + 记录分配到的子订单ID
+            if (!parentItemIdsToUpdate.isEmpty()) {
+                orderProductMapper.updateAssignmentInfo(
+                    parentItemIdsToUpdate,
+                    OrderAssignStatus.ASSIGNED.getCode(),
+                    childOrderId,
+                    now
+                );
+            }
+
+            // 插入分配记录(分配动作日志)
             OrderAssignment assignment = new OrderAssignment();
-            assignment.setOrderId(childOrder.getId());
-            assignment.setOrderNo(childOrder.getOrderNo());
+            assignment.setOrderId(parentOrder.getId());
+            assignment.setOrderNo(parentOrder.getOrderNo());
+            assignment.setChildOrderId(childOrderId);
             assignment.setPlatformBefore(parentOrder.getPlatformCode());
             assignment.setAssignedBy(operatorId);
             assignment.setAssignTime(now);
-            assignment.setAssignType("0");
+            assignment.setAssignType("0"); // 可按需定义
             assignment.setRemark(bo.getRemark());
             assignment.setAssigneeId(key.id());
             assignment.setAssigneeType(key.type());
-            baseMapper.insert(assignment); // 假设 baseMapper 是 OrderAssignmentMapper
+            baseMapper.insert(assignment);
         }
 
-        Map<String, Object> stats = orderProductMapper.getAssignmentStats(parentId);
-        Long total = (Long) stats.getOrDefault("total", 0L);
-        Long assigned = (Long) stats.getOrDefault("assigned", 0L);
+        // 5. 更新主订单的拆分状态和分配状态
+        AssignmentStatsDto stats = orderProductMapper.getAssignmentStats(parentId);
+        Long total = stats.getTotal();
+        Long assigned = stats.getAssigned();
 
         String parentAssignmentStatus;
         if (assigned == 0) {
-            parentAssignmentStatus = OrderAssignStatus.WAIT_ASSIGN.getCode(); // 待分配
-        } else if (assigned.equals(total)) {
-            parentAssignmentStatus = OrderAssignStatus.ASSIGNED.getCode(); // 已分配
+            parentAssignmentStatus = OrderAssignStatus.WAIT_ASSIGN.getCode();
+        } else if (assigned == total) {
+            parentAssignmentStatus = OrderAssignStatus.ASSIGNED.getCode();
         } else {
-            parentAssignmentStatus = OrderAssignStatus.PARTIALLY_ASSIGNED.getCode();// 部分分配
+            parentAssignmentStatus = OrderAssignStatus.PARTIALLY_ASSIGNED.getCode();
         }
 
-        // 5. 更新父订单状态
         OrderMain updateParent = new OrderMain();
         updateParent.setId(parentId);
         updateParent.setSplitStatus(OrderSplitStatus.SPLITED.getCode()); // 已拆分
@@ -324,12 +448,10 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
 
     private OrderMain copyParentToChild(OrderMain parent, Long assigneeId, String assigneeType) {
         OrderMain child = new OrderMain();
-        String childSeqKey = "child_order_seq:" + parent.getOrderNo();
-        String paddedSeq = SequenceUtils.nextPaddedIdStr(childSeqKey, Duration.ofDays(90), 2);
 
         child.setParentOrderId(parent.getId());
         child.setIsSplitChild("0"); // 0=是子订单
-        child.setOrderNo(parent.getOrderNo() + paddedSeq);
+        child.setOrderNo(SequenceUtils.generateOrderCode("ZD"));
         child.setCompanyId(parent.getCompanyId());
         child.setCustomerId(parent.getCustomerId());
         child.setCustomerCode(parent.getCustomerCode());
@@ -367,11 +489,6 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
         // 子订单状态初始化
         child.setSplitStatus(OrderSplitStatus.WAIT_SPLIT.getCode()); // 子订单默认未拆(通常不再拆)
         child.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode()); // 已分配
-        child.setStatus("0"); // 正常
-        child.setDelFlag("0");
-        child.setCreateTime(new Date());
-        child.setUpdateTime(new Date());
-
         return child;
     }
 

+ 132 - 5
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderDeliverServiceImpl.java

@@ -1,12 +1,15 @@
 package org.dromara.order.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.enums.OrderStatus;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.MapstructUtils;
@@ -14,11 +17,13 @@ import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.redis.utils.SequenceUtils;
+import org.dromara.customer.api.RemoteCustomerService;
 import org.dromara.order.domain.OrderDeliver;
 import org.dromara.order.domain.OrderDeliverProduct;
 import org.dromara.order.domain.OrderMain;
 import org.dromara.order.domain.bo.OrderDeliverBo;
 import org.dromara.order.domain.bo.OrderDeliverProductBo;
+import org.dromara.order.domain.vo.OrderDeliverProductVo;
 import org.dromara.order.domain.vo.OrderDeliverVo;
 import org.dromara.order.domain.vo.OrderQuantitySummary;
 import org.dromara.order.mapper.OrderDeliverMapper;
@@ -29,13 +34,13 @@ import org.dromara.order.service.IOrderDeliverService;
 import org.dromara.order.utils.kd100.Kd100Util;
 import org.dromara.order.utils.kd100.domain.QueryTrackDTO;
 import org.dromara.order.utils.kd100.domain.TrackVO;
+import org.dromara.system.api.RemoteDeptService;
+import org.dromara.system.api.RemoteUserService;
 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.Optional;
+import java.math.BigDecimal;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -49,6 +54,15 @@ import java.util.stream.Collectors;
 @Service
 public class OrderDeliverServiceImpl extends ServiceImpl<OrderDeliverMapper, OrderDeliver> implements IOrderDeliverService {
 
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
+    @DubboReference
+    private RemoteDeptService remoteDeptService;
+
+    @DubboReference
+    private RemoteCustomerService remoteCustomerService;
+
     private final OrderDeliverMapper baseMapper;
 
     private final OrderMainMapper orderMainMapper;
@@ -128,7 +142,7 @@ public class OrderDeliverServiceImpl extends ServiceImpl<OrderDeliverMapper, Ord
     private LambdaQueryWrapper<OrderDeliver> buildQueryWrapper(OrderDeliverBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<OrderDeliver> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(OrderDeliver::getId);
+        lqw.orderByDesc(OrderDeliver::getId);
         lqw.eq(bo.getOrderId() != null, OrderDeliver::getOrderId, bo.getOrderId());
         lqw.eq(StringUtils.isNotBlank(bo.getOrderCode()), OrderDeliver::getOrderCode, bo.getOrderCode());
         lqw.eq(StringUtils.isNotBlank(bo.getLogisticPackNo()), OrderDeliver::getLogisticPackNo, bo.getLogisticPackNo());
@@ -219,6 +233,38 @@ public class OrderDeliverServiceImpl extends ServiceImpl<OrderDeliverMapper, Ord
         }
     }
 
+    @Override
+    public TableDataInfo<OrderDeliverProductVo> getCustomerOrderProductList(Set<Long> orderIdList) {
+        // 1. 空值与空集合校验
+        if (CollectionUtils.isEmpty(orderIdList)) {
+            return TableDataInfo.build(Collections.emptyList());
+        }
+
+        List<OrderDeliverVo> orderDeliverVoList = baseMapper.selectVoList(
+            new LambdaQueryWrapper<OrderDeliver>()
+                .in(OrderDeliver::getId, orderIdList)
+        );
+
+        /*构建发货id与订单编号对应关系的map*/
+        Map<Long, String> deliverIdToOrderNoMap = orderDeliverVoList.stream()
+            .collect(Collectors.toMap(
+                OrderDeliverVo::getId,
+                OrderDeliverVo::getOrderCode,
+                (v1, v2) -> v1
+            ));
+
+        List<OrderDeliverProductVo> orderProductVoList = orderDeliverProductMapper.selectVoList(
+            new LambdaQueryWrapper<OrderDeliverProduct>()
+                .in(OrderDeliverProduct::getDeliverId, orderIdList)
+        );
+
+        orderProductVoList.forEach(v -> {
+            v.setOrderNo(deliverIdToOrderNoMap.get(v.getDeliverId()));
+        });
+
+        return TableDataInfo.build(orderProductVoList);
+    }
+
     /**
      * 修改订单发货主
      *
@@ -267,6 +313,7 @@ public class OrderDeliverServiceImpl extends ServiceImpl<OrderDeliverMapper, Ord
             .map(bo -> {
                 OrderDeliverProduct product = MapstructUtils.convert(bo, OrderDeliverProduct.class);
                 product.setDeliverId(deliverId);
+                product.setSubtotal(product.getOrderPrice().multiply(BigDecimal.valueOf(product.getDeliverNum())));
                 return product;
             })
             .collect(Collectors.toList());
@@ -276,6 +323,86 @@ public class OrderDeliverServiceImpl extends ServiceImpl<OrderDeliverMapper, Ord
         }
     }
 
+    @Override
+    public TableDataInfo<OrderDeliverVo> listDeliverOrderByCustomerIdPage(Long customerId, PageQuery pageQuery) {
+        if (customerId == null) {
+            return TableDataInfo.build(new Page<>(0, 0));
+        }
+
+        // 查询客户的所有有效订单
+        LambdaQueryWrapper<OrderMain> orderWrapper = new LambdaQueryWrapper<>();
+        orderWrapper.eq(OrderMain::getCustomerId, customerId)
+            .eq(OrderMain::getDelFlag, "0");
+
+        List<OrderMain> orderMainList = orderMainMapper.selectList(orderWrapper);
+        if (orderMainList.isEmpty()) {
+            return TableDataInfo.build(new Page<>(0, 0));
+        }
+
+        List<Long> orderIds = orderMainList.stream()
+            .map(OrderMain::getId)
+            .collect(Collectors.toList());
+
+        Map<Long, Date> orderIdToorderTimeMap = new HashMap<>();
+        for (OrderMain order : orderMainList) {
+            orderIdToorderTimeMap.put(order.getId(), order.getOrderTime());
+        }
+
+        Set<Long> createUserIds = orderMainList.stream().map(OrderMain::getCreateBy).collect(Collectors.toSet());
+        Set<Long> createDeptIds = orderMainList.stream().map(OrderMain::getCreateDept).collect(Collectors.toSet());
+
+        final Map<Long, String> userMap = new HashMap<>();
+        if (!createUserIds.isEmpty()) {
+            userMap.putAll(remoteUserService.selectUserNamesByIds(new ArrayList<>(createUserIds)));
+        }
+
+        final Map<Long, String> deptMap = new HashMap<>();
+        if (!createDeptIds.isEmpty()) {
+            deptMap.putAll(remoteDeptService.selectDeptNameByIds(new HashSet<>(createDeptIds)));
+        }
+
+        // 查询明细(分页)
+        LambdaQueryWrapper<OrderDeliver> deliverWrapper = new LambdaQueryWrapper<>();
+        deliverWrapper.in(OrderDeliver::getOrderId, orderIds)
+            .eq(OrderDeliver::getDelFlag, "0").orderByDesc(OrderDeliver::getId);
+
+        Page<OrderDeliverVo> pageResult = baseMapper.selectVoPage(pageQuery.build(), deliverWrapper);
+        List<OrderDeliverVo> records = pageResult.getRecords();
+        if (CollUtil.isNotEmpty(records)) {
+            // 1. 提取所有 deliverId
+            List<Long> deliverIds = records.stream()
+                .map(OrderDeliverVo::getId)
+                .collect(Collectors.toList());
+
+            // 批量查询所有商品明细
+            List<OrderDeliverProductVo> allProducts = orderDeliverProductMapper.selectVoList(
+                new LambdaQueryWrapper<OrderDeliverProduct>()
+                    .in(OrderDeliverProduct::getDeliverId, deliverIds)
+            );
+
+            // 安全聚合:处理 null subtotal
+            Map<Long, BigDecimal> deliverIdToTotalAmount = allProducts.stream()
+                .collect(Collectors.groupingBy(
+                    OrderDeliverProductVo::getDeliverId,
+                    Collectors.reducing(
+                        BigDecimal.ZERO,
+                        product -> Optional.ofNullable(product.getSubtotal()).orElse(BigDecimal.ZERO),
+                        BigDecimal::add
+                    )
+                ));
+
+            // 4. 回填 totalAmount 和其他字段
+            for (OrderDeliverVo record : records) {
+                record.setTotalAmount(deliverIdToTotalAmount.getOrDefault(record.getId(), BigDecimal.ZERO));
+                record.setCreateName(userMap.getOrDefault(record.getCreateBy(), "未知用户"));
+                record.setCreateDeptName(deptMap.getOrDefault(record.getCreateDept(), "未知部门"));
+                record.setOrderTime(orderIdToorderTimeMap.get(record.getOrderId()));
+            }
+        }
+
+        return TableDataInfo.build(pageResult);
+    }
+
     /**
      * 保存前的数据校验
      */

+ 37 - 18
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderMainServiceImpl.java

@@ -2,6 +2,7 @@ package org.dromara.order.service.impl;
 
 import cn.hutool.core.collection.CollUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -9,6 +10,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.core.enums.OrderStatus;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
@@ -19,6 +21,7 @@ import org.dromara.order.domain.OrderMain;
 import org.dromara.order.domain.OrderProduct;
 import org.dromara.order.domain.bo.OrderMainBo;
 import org.dromara.order.domain.bo.OrderProductBo;
+import org.dromara.order.domain.dto.AssignmentStatsDto;
 import org.dromara.order.domain.vo.OrderMainVo;
 import org.dromara.order.domain.vo.OrderProductVo;
 import org.dromara.order.domain.vo.OrderStatusStats;
@@ -32,7 +35,6 @@ import org.dromara.system.api.RemoteUserService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.math.BigDecimal;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -93,20 +95,11 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
             orderDeliverProductMapper.selectDeliverProductsByOrderId(orderMainVo.getId())
         );
 
-        Map<String, Object> stats = orderProductMapper.getAssignmentStats(orderMainVo.getId());
-        Object totalObj = stats.get("total");
-        Long total = (totalObj instanceof BigDecimal)
-            ? ((BigDecimal) totalObj).longValue()
-            : (totalObj instanceof Long)
-            ? (Long) totalObj
-            : 0L;
-
-        Object assignedObj = stats.get("assigned");
-        Long assigned = (assignedObj instanceof BigDecimal)
-            ? ((BigDecimal) assignedObj).longValue()
-            : (assignedObj instanceof Long)
-            ? (Long) assignedObj
-            : 0L;
+        // 查询主订单原始商品的总行数与已分配行数
+        AssignmentStatsDto stats = orderProductMapper.getAssignmentStats(orderMainVo.getId());
+        Long total = stats.getTotal();
+        Long assigned = stats.getAssigned();
+
         orderMainVo.setProductTotal(total);
         orderMainVo.setUnassigned(total - assigned);
         orderMainVo.setAssigned(assigned);
@@ -178,7 +171,7 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
     private LambdaQueryWrapper<OrderMain> buildQueryWrapper(OrderMainBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<OrderMain> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(OrderMain::getId);
+        lqw.orderByDesc(OrderMain::getId);
         lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), OrderMain::getOrderNo, bo.getOrderNo());
         lqw.eq(StringUtils.isNotBlank(bo.getCustomerCode()), OrderMain::getCustomerCode, bo.getCustomerCode());
         lqw.eq(StringUtils.isNotBlank(bo.getShipmentNo()), OrderMain::getShipmentNo, bo.getShipmentNo());
@@ -220,12 +213,20 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
         lqw.eq(StringUtils.isNotBlank(bo.getOrderCategory()), OrderMain::getOrderCategory, bo.getOrderCategory());
         lqw.eq(StringUtils.isNotBlank(bo.getExpenseType()), OrderMain::getExpenseType, bo.getExpenseType());
         lqw.eq(StringUtils.isNotBlank(bo.getStatus()), OrderMain::getStatus, bo.getStatus());
+        // 关键词搜索:精确匹配订单号
+        if (StringUtils.isNotBlank(bo.getSearchValue())) {
+            lqw.eq(OrderMain::getOrderNo, bo.getSearchValue().trim());
+        }
         if (params != null) {
             lqw.between(params.get("beginTime") != null && params.get("endTime") != null,
                 OrderMain::getOrderTime, params.get("beginTime"), params.get("endTime"));
         }
-        if (null != bo.getOrderStatuses() && bo.getOrderStatuses().contains(",")) {
-            lqw.in(OrderMain::getOrderStatus, bo.getOrderStatuses().split(","));
+        if (StringUtils.isNotBlank(bo.getOrderStatuses())) {
+            if (bo.getOrderStatuses().contains(",")) {
+                lqw.in(OrderMain::getOrderStatus, bo.getOrderStatuses().split(","));
+            } else {
+                lqw.eq(OrderMain::getOrderStatus, bo.getOrderStatuses());
+            }
         }
         return lqw;
     }
@@ -372,4 +373,22 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    @Override
+    public Boolean closeOrderByIds(Collection<Long> ids, Boolean isValid) {
+        if (ids == null || ids.isEmpty()) {
+            return true;
+        }
+
+        if (isValid) {
+            // TODO 业务校验
+        }
+
+        LambdaUpdateWrapper<OrderMain> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.in(OrderMain::getId, ids)
+            .set(OrderMain::getOrderStatus, OrderStatus.CLOSED.getCode());
+
+        int affectedRows = baseMapper.update(null, wrapper);
+        return affectedRows > 0;
+    }
 }

+ 136 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderOperateLogServiceImpl.java

@@ -0,0 +1,136 @@
+package org.dromara.order.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.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.order.domain.OrderOperateLog;
+import org.dromara.order.domain.bo.OrderOperateLogBo;
+import org.dromara.order.domain.vo.OrderOperateLogVo;
+import org.dromara.order.mapper.OrderOperateLogMapper;
+import org.dromara.order.service.IOrderOperateLogService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 订单操作记录Service业务层处理
+ *
+ * @author LionLi
+ * @date 2026-01-26
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class OrderOperateLogServiceImpl extends ServiceImpl<OrderOperateLogMapper, OrderOperateLog> implements IOrderOperateLogService {
+
+    private final OrderOperateLogMapper baseMapper;
+
+    /**
+     * 查询订单操作记录
+     *
+     * @param id 主键
+     * @return 订单操作记录
+     */
+    @Override
+    public OrderOperateLogVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询订单操作记录列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 订单操作记录分页列表
+     */
+    @Override
+    public TableDataInfo<OrderOperateLogVo> queryPageList(OrderOperateLogBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<OrderOperateLog> lqw = buildQueryWrapper(bo);
+        Page<OrderOperateLogVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的订单操作记录列表
+     *
+     * @param bo 查询条件
+     * @return 订单操作记录列表
+     */
+    @Override
+    public List<OrderOperateLogVo> queryList(OrderOperateLogBo bo) {
+        LambdaQueryWrapper<OrderOperateLog> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<OrderOperateLog> buildQueryWrapper(OrderOperateLogBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<OrderOperateLog> lqw = Wrappers.lambdaQuery();
+        lqw.orderByDesc(OrderOperateLog::getId);
+        lqw.eq(bo.getOrderId() != null, OrderOperateLog::getOrderId, bo.getOrderId());
+        lqw.eq(StringUtils.isNotBlank(bo.getAction()), OrderOperateLog::getAction, bo.getAction());
+        lqw.like(StringUtils.isNotBlank(bo.getUserName()), OrderOperateLog::getUserName, bo.getUserName());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), OrderOperateLog::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增订单操作记录
+     *
+     * @param bo 订单操作记录
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(OrderOperateLogBo bo) {
+        OrderOperateLog add = MapstructUtils.convert(bo, OrderOperateLog.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改订单操作记录
+     *
+     * @param bo 订单操作记录
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(OrderOperateLogBo bo) {
+        OrderOperateLog update = MapstructUtils.convert(bo, OrderOperateLog.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(OrderOperateLog entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除订单操作记录信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 1 - 4
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderReturnServiceImpl.java

@@ -11,17 +11,14 @@ import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.redis.utils.SequenceUtils;
-import org.dromara.order.domain.OrderMain;
 import org.dromara.order.domain.OrderReturn;
 import org.dromara.order.domain.OrderReturnItem;
-import org.dromara.order.domain.OrderRevenueHeader;
 import org.dromara.order.domain.bo.OrderReturnBo;
 import org.dromara.order.domain.bo.OrderReturnItemBo;
 import org.dromara.order.domain.vo.OrderReturnVo;
 import org.dromara.order.mapper.OrderReturnItemMapper;
 import org.dromara.order.mapper.OrderReturnMapper;
 import org.dromara.order.service.IOrderReturnService;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -89,7 +86,7 @@ public class OrderReturnServiceImpl extends ServiceImpl<OrderReturnMapper, Order
     private LambdaQueryWrapper<OrderReturn> buildQueryWrapper(OrderReturnBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<OrderReturn> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(OrderReturn::getId);
+        lqw.orderByDesc(OrderReturn::getId);
         lqw.eq(bo.getOrderId() != null, OrderReturn::getOrderId, bo.getOrderId());
         lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), OrderReturn::getOrderNo, bo.getOrderNo());
         lqw.eq(StringUtils.isNotBlank(bo.getServiceType()), OrderReturn::getServiceType, bo.getServiceType());

+ 1 - 1
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderRevenueHeaderServiceImpl.java

@@ -125,7 +125,7 @@ public class OrderRevenueHeaderServiceImpl extends ServiceImpl<OrderRevenueHeade
     private LambdaQueryWrapper<OrderRevenueHeader> buildQueryWrapper(OrderRevenueHeaderBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<OrderRevenueHeader> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(OrderRevenueHeader::getId);
+        lqw.orderByDesc(OrderRevenueHeader::getId);
         lqw.eq(StringUtils.isNotBlank(bo.getRevenueType()), OrderRevenueHeader::getRevenueType, bo.getRevenueType());
         lqw.eq(StringUtils.isNotBlank(bo.getOrderRevenueCode()), OrderRevenueHeader::getOrderRevenueCode, bo.getOrderRevenueCode());
         lqw.eq(StringUtils.isNotBlank(bo.getIncomeOrderCode()), OrderRevenueHeader::getIncomeOrderCode, bo.getIncomeOrderCode());

+ 2 - 2
ruoyi-modules/ruoyi-order/src/main/resources/mapper/order/OrderDeliverMapper.xml

@@ -1,7 +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">
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="org.dromara.order.mapper.OrderDeliverMapper">
 
 </mapper>

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

+ 15 - 5
ruoyi-modules/ruoyi-order/src/main/resources/mapper/order/OrderProductMapper.xml

@@ -82,12 +82,22 @@
       AND del_flag = '0'
     </select>
 
-    <select id="getAssignmentStats" resultType="map">
-        SELECT COUNT(*)                                                 AS total,
-               SUM(CASE WHEN assignment_status = '1' THEN 1 ELSE 0 END) AS assigned
+    <select id="getAssignmentStats" resultType="org.dromara.order.domain.dto.AssignmentStatsDto">
+        SELECT COUNT(*)                                                              AS total,
+               COALESCE(SUM(CASE WHEN assignment_status = '1' THEN 1 ELSE 0 END), 0) AS assigned
         FROM order_product
-        WHERE order_id = #{orderId}
-          AND del_flag = '0'
+        WHERE order_id = #{parentId}
+          AND original_item_id IS NULL
     </select>
+    <update id="updateAssignmentInfo">
+        UPDATE order_product
+        SET assignment_status = #{assignmentStatus},
+        assigned_child_order_id = #{childOrderId},
+        update_time = #{updateTime}
+        WHERE id IN
+        <foreach collection="itemIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
 
 </mapper>

+ 104 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/controller/ServiceCaseController.java

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

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

@@ -0,0 +1,17 @@
+package org.dromara.product.controller.pc;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author
+ * @date 2026/1/28 下午3:37
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/diyProduct")
+public class DiyProductController {
+}

+ 20 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/ProductRecommendLink.java

@@ -37,6 +37,26 @@ public class ProductRecommendLink extends TenantEntity {
      */
     private Long productId;
 
+    /**
+     * 方案编号
+     */
+    private Long programId;
+
+    /**
+     * 服务案例编号
+     */
+    private Long serviceCaseId;
+
+    /**
+     * 品牌编号
+     */
+    private Long brandId;
+
+    /**
+     * 排序值
+     */
+    private Integer sort;
+
     /**
      * 删除标志(0代表存在 2代表删除)
      */

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

@@ -0,0 +1,106 @@
+package org.dromara.product.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 服务案例对象 service_case
+ *
+ * @author LionLi
+ * @date 2026-01-30
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("service_case")
+public class ServiceCase extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 案例编号
+     */
+    private String serviceCaseNo;
+
+    /**
+     * 案例标题
+     */
+    private String caseTitle;
+
+    /**
+     * 项目简介
+     */
+    private String projectBrief;
+
+    /**
+     * 客户行业
+     */
+    private String clientIndustry;
+
+    /**
+     * 项目类型
+     */
+    private String projectType;
+
+    /**
+     * 上传方案
+     */
+    private String uploadProgram;
+
+    /**
+     * 案例图片URL
+     */
+    private String caseImage;
+
+    /**
+     * 项目详情
+     */
+    private String projectDetails;
+
+    /**
+     * 是否显示(0=不显示 1=显示)
+     */
+    private String isShow;
+
+    /**
+     * 是否推荐(0=不推荐 1=推荐)
+     */
+    private String isRecommend;
+
+    /**
+     * 点赞数
+     */
+    private Integer praise;
+
+    /**
+     * 是否相关(0=不相关 1=相关)
+     */
+    private String isRelevant;
+
+    /**
+     * 平台标识
+     */
+    private String platformCode;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 20 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/ProductRecommendLinkBo.java

@@ -37,6 +37,26 @@ public class ProductRecommendLinkBo extends BaseEntity {
     //@NotNull(message = "产品编号不能为空", groups = { AddGroup.class, EditGroup.class })
     private Long productId;
 
+    /**
+     * 方案编号
+     */
+    private Long programId;
+
+    /**
+     * 服务案例编号
+     */
+    private Long serviceCaseId;
+
+    /**
+     * 品牌编号
+     */
+    private Long brandId;
+
+    /**
+     * 排序值
+     */
+    private Integer sort;
+
     /**
      * 备注
      */

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

@@ -0,0 +1,98 @@
+package org.dromara.product.domain.bo;
+
+import org.dromara.product.domain.ServiceCase;
+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.*;
+
+/**
+ * 服务案例业务对象 service_case
+ *
+ * @author LionLi
+ * @date 2026-01-30
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = ServiceCase.class, reverseConvertGenerate = false)
+public class ServiceCaseBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 案例编号
+     */
+    private String serviceCaseNo;
+
+    /**
+     * 案例标题
+     */
+    private String caseTitle;
+
+    /**
+     * 项目简介
+     */
+    private String projectBrief;
+
+    /**
+     * 客户行业
+     */
+    private String clientIndustry;
+
+    /**
+     * 项目类型
+     */
+    private String projectType;
+
+    /**
+     * 上传方案
+     */
+    private String uploadProgram;
+
+    /**
+     * 案例图片URL
+     */
+    private String caseImage;
+
+    /**
+     * 项目详情
+     */
+    private String projectDetails;
+
+    /**
+     * 是否显示(0=不显示 1=显示)
+     */
+    private String isShow;
+
+    /**
+     * 是否推荐(0=不推荐 1=推荐)
+     */
+    private String isRecommend;
+
+    /**
+     * 点赞数
+     */
+    private Integer praise;
+
+    /**
+     * 是否相关(0=不相关 1=相关)
+     */
+    private String isRelevant;
+
+    /**
+     * 平台标识
+     */
+    private String platformCode;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 24 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ProductRecommendLinkVo.java

@@ -46,6 +46,30 @@ public class ProductRecommendLinkVo implements Serializable {
     @ExcelProperty(value = "产品编号")
     private Long productId;
 
+    /**
+     * 方案编号
+     */
+    @ExcelProperty(value = "方案编号")
+    private Long programId;
+
+    /**
+     * 服务案例编号
+     */
+    @ExcelProperty(value = "服务案例编号")
+    private Long serviceCaseId;
+
+    /**
+     * 品牌编号
+     */
+    @ExcelProperty(value = "品牌编号")
+    private Long brandId;
+
+    /**
+     * 排序值
+     */
+    @ExcelProperty(value = "排序值")
+    private Integer sort;
+
     /**
      * 备注
      */

+ 104 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/ServiceCaseVo.java

@@ -0,0 +1,104 @@
+package org.dromara.product.domain.vo;
+
+import org.dromara.product.domain.ServiceCase;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 服务案例视图对象 service_case
+ *
+ * @author LionLi
+ * @date 2026-01-30
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = ServiceCase.class)
+public class ServiceCaseVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 案例编号
+     */
+    @ExcelProperty(value = "案例编号")
+    private String serviceCaseNo;
+
+    /**
+     * 案例标题
+     */
+    @ExcelProperty(value = "案例标题")
+    private String caseTitle;
+
+    /**
+     * 项目简介
+     */
+    @ExcelProperty(value = "项目简介")
+    private String projectBrief;
+
+    /**
+     * 客户行业
+     */
+    @ExcelProperty(value = "客户行业")
+    private String clientIndustry;
+
+    /**
+     * 项目类型
+     */
+    @ExcelProperty(value = "项目类型")
+    private String projectType;
+
+    /**
+     * 上传方案
+     */
+    @ExcelProperty(value = "上传方案")
+    private String uploadProgram;
+
+    /**
+     * 案例图片URL
+     */
+    @ExcelProperty(value = "案例图片")
+    private String caseImage;
+
+    /**
+     * 是否显示
+     */
+    @ExcelProperty(value = "是否显示")
+    private String isShow;
+
+    /**
+     * 是否推荐
+     */
+    @ExcelProperty(value = "是否推荐")
+    private String isRecommend;
+
+    /**
+     * 点赞数
+     */
+    @ExcelProperty(value = "点赞数")
+    private Integer praise;
+
+    /**
+     * 平台标识
+     */
+    @ExcelProperty(value = "平台标识")
+    private String platformCode;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/mapper/ServiceCaseMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.product.mapper;
+
+import org.dromara.product.domain.ServiceCase;
+import org.dromara.product.domain.vo.ServiceCaseVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 服务案例Mapper接口
+ *
+ * @author LionLi
+ * @date 2026-01-30
+ */
+public interface ServiceCaseMapper extends BaseMapperPlus<ServiceCase, ServiceCaseVo> {
+
+}

+ 70 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/IServiceCaseService.java

@@ -0,0 +1,70 @@
+package org.dromara.product.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.product.domain.ServiceCase;
+import org.dromara.product.domain.vo.ServiceCaseVo;
+import org.dromara.product.domain.bo.ServiceCaseBo;
+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 2026-01-30
+ */
+public interface IServiceCaseService extends IService<ServiceCase>{
+
+    /**
+     * 查询服务案例
+     *
+     * @param id 主键
+     * @return 服务案例
+     */
+    ServiceCaseVo queryById(Long id);
+
+    /**
+     * 分页查询服务案例列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 服务案例分页列表
+     */
+    TableDataInfo<ServiceCaseVo> queryPageList(ServiceCaseBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的服务案例列表
+     *
+     * @param bo 查询条件
+     * @return 服务案例列表
+     */
+    List<ServiceCaseVo> queryList(ServiceCaseBo bo);
+
+    /**
+     * 新增服务案例
+     *
+     * @param bo 服务案例
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(ServiceCaseBo bo);
+
+    /**
+     * 修改服务案例
+     *
+     * @param bo 服务案例
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(ServiceCaseBo bo);
+
+    /**
+     * 校验并批量删除服务案例信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 137 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ServiceCaseServiceImpl.java

@@ -0,0 +1,137 @@
+package org.dromara.product.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.product.domain.bo.ServiceCaseBo;
+import org.dromara.product.domain.vo.ServiceCaseVo;
+import org.dromara.product.domain.ServiceCase;
+import org.dromara.product.mapper.ServiceCaseMapper;
+import org.dromara.product.service.IServiceCaseService;
+
+import java.util.List;
+import java.util.Collection;
+
+/**
+ * 服务案例Service业务层处理
+ *
+ * @author LionLi
+ * @date 2026-01-30
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class ServiceCaseServiceImpl extends ServiceImpl<ServiceCaseMapper, ServiceCase> implements IServiceCaseService {
+
+    private final ServiceCaseMapper baseMapper;
+
+    /**
+     * 查询服务案例
+     *
+     * @param id 主键
+     * @return 服务案例
+     */
+    @Override
+    public ServiceCaseVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询服务案例列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 服务案例分页列表
+     */
+    @Override
+    public TableDataInfo<ServiceCaseVo> queryPageList(ServiceCaseBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<ServiceCase> lqw = buildQueryWrapper(bo);
+        Page<ServiceCaseVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的服务案例列表
+     *
+     * @param bo 查询条件
+     * @return 服务案例列表
+     */
+    @Override
+    public List<ServiceCaseVo> queryList(ServiceCaseBo bo) {
+        LambdaQueryWrapper<ServiceCase> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<ServiceCase> buildQueryWrapper(ServiceCaseBo bo) {
+        LambdaQueryWrapper<ServiceCase> lqw = Wrappers.lambdaQuery();
+        lqw.orderByDesc(ServiceCase::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getServiceCaseNo()), ServiceCase::getServiceCaseNo, bo.getServiceCaseNo());
+        lqw.like(StringUtils.isNotBlank(bo.getCaseTitle()), ServiceCase::getCaseTitle, bo.getCaseTitle());
+        lqw.eq(StringUtils.isNotBlank(bo.getClientIndustry()), ServiceCase::getClientIndustry, bo.getClientIndustry());
+        lqw.eq(StringUtils.isNotBlank(bo.getProjectType()), ServiceCase::getProjectType, bo.getProjectType());
+        lqw.eq(StringUtils.isNotBlank(bo.getIsShow()), ServiceCase::getIsShow, bo.getIsShow());
+        lqw.eq(StringUtils.isNotBlank(bo.getIsRecommend()), ServiceCase::getIsRecommend, bo.getIsRecommend());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), ServiceCase::getPlatformCode, bo.getPlatformCode());
+        return lqw;
+    }
+
+    /**
+     * 新增服务案例
+     *
+     * @param bo 服务案例
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(ServiceCaseBo bo) {
+        ServiceCase add = MapstructUtils.convert(bo, ServiceCase.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改服务案例
+     *
+     * @param bo 服务案例
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(ServiceCaseBo bo) {
+        ServiceCase update = MapstructUtils.convert(bo, ServiceCase.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(ServiceCase entity){
+        // 可以在此添加业务校验逻辑
+    }
+
+    /**
+     * 校验并批量删除服务案例信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            // 可以在此添加删除前的校验逻辑
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 18 - 7
ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/controller/SysSmsController.java

@@ -2,6 +2,7 @@ package org.dromara.resource.controller;
 
 
 import cn.hutool.core.util.RandomUtil;
+import cn.hutool.http.HttpUtil;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
@@ -20,8 +21,10 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import java.time.Duration;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 短信功能
@@ -40,17 +43,17 @@ public class SysSmsController extends BaseController {
      *
      * @param phonenumber 用户手机号
      */
-    @RateLimiter(key = "#phonenumber", time = 60, count = 1)
+//    @RateLimiter(key = "#phonenumber", time = 60, count = 1)
     @GetMapping("/code")
     public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
         String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
-        String code = RandomUtil.randomNumbers(4);
+        String code = RandomUtil.randomNumbers(6);
         RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
         // 验证码模板id 自行处理 (查数据库或写死均可)
-        String templateId = "";
+        String templateId = "SMS_482795341";
         LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
         map.put("code", code);
-        SmsBlend smsBlend = SmsFactory.getSmsBlend("tx1");
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
         SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
         if (!smsResponse.isSuccess()) {
             log.error("验证码短信发送异常 => {}", smsResponse);
@@ -59,6 +62,14 @@ public class SysSmsController extends BaseController {
         return R.ok();
     }
 
+    public static void main(String[] args) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("phonenumber", "17683851082");
+        String string = HttpUtil.get("http://127.0.0.1:8080/resource/sms/code",map);
+        System.out.println(string);
+
+    }
+
     /**
      * 群发短信
      *
@@ -68,16 +79,16 @@ public class SysSmsController extends BaseController {
     public R<Void> batchSend(@Validated @RequestBody SmsBatchRequest request) {
         List<String> phones = request.getPhones();
         String message = request.getMessage();
-        
+
         if (phones.isEmpty()) {
             return R.fail("手机号列表不能为空");
         }
         if (phones.size() > 1000) {
             return R.fail("单次群发不能超过1000个手机号");
         }
-        
+
         log.info("群发短信 => 手机号数量: {}, 内容: {}", phones.size(), message);
-        
+
         try {
             SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
             SmsResponse smsResponse = smsBlend.massTexting(phones, message);

+ 6 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ComStaffVo.java

@@ -9,7 +9,9 @@ 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 io.github.linpeilie.annotations.AutoMappers;
 import lombok.Data;
+import org.dromara.system.api.domain.vo.RemoteComStaffVo;
 
 import java.io.Serial;
 import java.io.Serializable;
@@ -24,7 +26,10 @@ import java.util.Date;
  */
 @Data
 @ExcelIgnoreUnannotated
-@AutoMapper(target = ComStaff.class)
+@AutoMappers({
+    @AutoMapper(target = ComStaff.class),
+    @AutoMapper(target = RemoteComStaffVo.class)
+})
 public class ComStaffVo implements Serializable {
 
     @Serial

+ 9 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteComStaffServiceImpl.java

@@ -2,8 +2,10 @@ package org.dromara.system.dubbo;
 
 import lombok.RequiredArgsConstructor;
 import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.system.api.RemoteComStaffService;
-import org.dromara.system.domain.ComStaff;
+import org.dromara.system.api.domain.vo.RemoteComStaffVo;
+import org.dromara.system.domain.vo.ComStaffVo;
 import org.dromara.system.service.IComStaffService;
 import org.springframework.stereotype.Service;
 
@@ -27,4 +29,10 @@ public class RemoteComStaffServiceImpl implements RemoteComStaffService {
     public Map<String, String> selectStaffNameAndCode() {
         return comStaffService.selectStaffNameAndCode();
     }
+
+    @Override
+    public List<RemoteComStaffVo> selectStaffByIds(Set<Long> ids) {
+        List<ComStaffVo> staffList = comStaffService.queryByIds(ids);
+        return MapstructUtils.convert(staffList, RemoteComStaffVo.class);
+    }
 }

+ 8 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/IComStaffService.java

@@ -46,6 +46,14 @@ public interface IComStaffService extends IService<ComStaff> {
      */
     List<ComStaffVo> queryList(ComStaffBo bo);
 
+    /**
+     * 根据人员ID批量查询人员信息
+     *
+     * @param ids 人员ID集合
+     * @return 人员信息列表
+     */
+    List<ComStaffVo> queryByIds(Set<Long> ids);
+
     /**
      * 新增人员信息
      *

+ 44 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/ComStaffServiceImpl.java

@@ -90,6 +90,50 @@ public class ComStaffServiceImpl extends ServiceImpl<ComStaffMapper, ComStaff> i
         return baseMapper.selectVoList(lqw);
     }
 
+    /**
+     * 根据人员ID批量查询人员信息
+     *
+     * @param ids 人员ID集合
+     * @return 人员信息列表
+     */
+    @Override
+    public List<ComStaffVo> queryByIds(Set<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        LambdaQueryWrapper<ComStaff> lqw = Wrappers.lambdaQuery();
+        lqw.in(ComStaff::getStaffId, ids);
+        List<ComStaffVo> records = baseMapper.selectVoList(lqw);
+
+        if (CollUtil.isNotEmpty(records)) {
+            Set<Long> deptIds = records.stream().map(ComStaffVo::getDeptId).filter(Objects::nonNull).collect(Collectors.toSet());
+            Map<Long, String> deptMap = new HashMap<>();
+            if (CollUtil.isNotEmpty(deptIds)) {
+                deptMap = sysDeptMapper.selectList(new LambdaQueryWrapper<SysDept>()
+                        .select(SysDept::getDeptId, SysDept::getDeptName).in(SysDept::getDeptId, deptIds)).stream()
+                    .collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName));
+            }
+
+            Set<Long> postIds = records.stream().map(ComStaffVo::getPostId).filter(Objects::nonNull).collect(Collectors.toSet());
+            Map<Long, String> postMap = new HashMap<>();
+            if (CollUtil.isNotEmpty(postIds)) {
+                postMap = sysPostMapper.selectList(new LambdaQueryWrapper<SysPost>()
+                        .select(SysPost::getPostId, SysPost::getPostName).in(SysPost::getPostId, postIds)).stream()
+                    .collect(Collectors.toMap(SysPost::getPostId, SysPost::getPostName));
+            }
+
+            Map<Long, String> finalDeptMap = deptMap;
+            Map<Long, String> finalPostMap = postMap;
+            records.forEach(item -> {
+                item.setDeptName(finalDeptMap.get(item.getDeptId()));
+                item.setPostName(finalPostMap.get(item.getPostId()));
+            });
+        }
+
+        return records;
+    }
+
     private LambdaQueryWrapper<ComStaff> buildQueryWrapper(ComStaffBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<ComStaff> lqw = Wrappers.lambdaQuery();

+ 5 - 5
ruoyi-modules/ruoyi-system/src/main/resources/application.yml

@@ -13,6 +13,11 @@ spring:
 # 👇 新增:允许 Bean 定义覆盖(解决 Filter/Bean 冲突)
   main:
     allow-bean-definition-overriding: true
+  config:
+    import:
+      - optional:nacos:application-common.yml
+      - optional:nacos:datasource.yml
+      - optional:nacos:${spring.application.name}.yml
 --- # nacos 配置
 spring:
   cloud:
@@ -29,8 +34,3 @@ spring:
         # 配置组
         group: @nacos.config.group@
         namespace: ${spring.profiles.active}
-  config:
-    import:
-      - optional:nacos:application-common.yml
-      - optional:nacos:datasource.yml
-      - optional:nacos:${spring.application.name}.yml