Bläddra i källkod

feat(order): 优化订单分配功能支持供应商和伙伴商分配

- 新增分配对象ID和类型字段用于区分供应商(srm)和伙伴商(bp)
- 实现按分配对象分组创建子订单的逻辑替代原有的按平台分组
- 添加分配状态枚举包括待分配、已分配和部分分配三种状态
- 重构订单分配规则实体将targetPlatform替换为assigneeId和assigneeType
- 更新订单主表和商品表结构增加分配相关字段和统计功能
- 实现订单分配统计功能显示已分配和未分配商品数量
- 添加分配对象类型常量枚举确保类型校验的合法性
- 优化子订单复制逻辑继承父订单更多业务字段
- 实现客户部门与系统部门的数据同步更新机制
- 移除文件分类业务对象的部分验证注解以支持灵活配置
hurx 2 månader sedan
förälder
incheckning
6c0f5d0390
27 ändrade filer med 522 tillägg och 68 borttagningar
  1. 10 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemoteCustomerDeptService.java
  2. 63 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/domain/vo/RemoteCustomerDeptVo.java
  3. 2 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDeptService.java
  4. 23 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/AssigneeTypeConstants.java
  5. 7 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/OrderAssignStatus.java
  6. 28 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemoteCustomerDeptServiceImpl.java
  7. 3 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerDeptService.java
  8. 67 21
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerDeptServiceImpl.java
  9. 10 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderAssignment.java
  10. 10 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderMain.java
  11. 2 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderProduct.java
  12. 4 2
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderProductAssignRule.java
  13. 8 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderAssignmentBo.java
  14. 10 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderMainBo.java
  15. 2 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderProductBo.java
  16. 10 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderAssignmentVo.java
  17. 19 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderMainVo.java
  18. 2 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderProductVo.java
  19. 6 1
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/mapper/OrderProductMapper.java
  20. 76 37
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderAssignmentServiceImpl.java
  21. 13 1
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderMainServiceImpl.java
  22. 21 4
      ruoyi-modules/ruoyi-order/src/main/resources/mapper/order/OrderProductMapper.xml
  23. 2 2
      ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/domain/bo/FileCategoryBo.java
  24. 4 0
      ruoyi-modules/ruoyi-system/pom.xml
  25. 10 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java
  26. 8 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java
  27. 102 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java

+ 10 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemoteCustomerDeptService.java

@@ -0,0 +1,10 @@
+package org.dromara.customer.api;
+
+import org.dromara.customer.api.domain.vo.RemoteCustomerDeptVo;
+
+import java.util.List;
+
+public interface RemoteCustomerDeptService {
+
+    List<RemoteCustomerDeptVo> selectCustomerDeptListByCustomerId(Long customerId);
+}

+ 63 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/domain/vo/RemoteCustomerDeptVo.java

@@ -0,0 +1,63 @@
+package org.dromara.customer.api.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+public class RemoteCustomerDeptVo implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    private Long deptId;
+
+    /**
+     * 客户编号
+     */
+    private Long customerId;
+
+    /**
+     * 年度预算
+     */
+    private BigDecimal yearlyBudget;
+
+    /**
+     * 已使用预算
+     */
+    private BigDecimal usedBudget;
+
+    /**
+     * 月度限额
+     */
+    private BigDecimal monthLimit;
+
+    /**
+     * 月度已用预算
+     */
+    private BigDecimal monthUsedBudget;
+
+    /**
+     * 绑定状态
+     */
+    private String bindStatus;
+
+    /**
+     * 绑定地址
+     */
+    private String bindAddress;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+}

+ 2 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDeptService.java

@@ -51,6 +51,8 @@ public interface RemoteDeptService {
      */
     RemoteDeptVo updateDept(RemoteDeptVo vo);
 
+    RemoteDeptVo updateDept(RemoteDeptVo vo);
+
     List<RemoteDeptVo> selectDeptByIds(List<Long> deptIds);
 
     /**

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

@@ -0,0 +1,23 @@
+package org.dromara.common.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum AssigneeTypeConstants {
+
+    /**
+     * 供应商
+     */
+    SUPPLIER("srm", "供应商"),
+
+    /**
+     * 伙伴商
+     */
+    PARTNER("bp", "伙伴商");
+
+
+    private final String code;
+    private final String info;
+}

+ 7 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/OrderAssignStatus.java

@@ -15,6 +15,13 @@ public enum OrderAssignStatus {
      * 待分配
      */
     WAIT_ASSIGN("0", "待分配"),
+
+    /**
+     * 部分分配
+     */
+    PARTIALLY_ASSIGNED("2", "部分分配"),
+
+
     /**
      * 已分配
      */

+ 28 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemoteCustomerDeptServiceImpl.java

@@ -0,0 +1,28 @@
+package org.dromara.customer.dubbo;
+
+import cn.hutool.core.bean.BeanUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.customer.api.RemoteCustomerDeptService;
+import org.dromara.customer.api.domain.vo.RemoteCustomerDeptVo;
+import org.dromara.customer.domain.vo.CustomerDeptVo;
+import org.dromara.customer.service.ICustomerDeptService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@DubboService
+public class RemoteCustomerDeptServiceImpl implements RemoteCustomerDeptService {
+
+    private final ICustomerDeptService customerDeptService;
+
+    @Override
+    public List<RemoteCustomerDeptVo> selectCustomerDeptListByCustomerId(Long customerId) {
+        List<CustomerDeptVo> customerDeptVos = customerDeptService.selectCustomerDeptListByCustomerId(customerId);
+        return BeanUtil.copyToList(customerDeptVos, RemoteCustomerDeptVo.class);
+    }
+}

+ 3 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerDeptService.java

@@ -1,6 +1,7 @@
 package org.dromara.customer.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.customer.api.domain.vo.RemoteCustomerDeptVo;
 import org.dromara.customer.domain.CustomerDept;
 import org.dromara.customer.domain.vo.CustomerDeptTreeVo;
 import org.dromara.customer.domain.vo.CustomerDeptVo;
@@ -51,6 +52,8 @@ public interface ICustomerDeptService extends IService<CustomerDept> {
      */
     Boolean updateByBo(CustomerDeptBo bo);
 
+    List<CustomerDeptVo> selectCustomerDeptListByCustomerId(Long customerId);
+
     /**
      * 校验并批量删除客户部门信息信息
      *

+ 67 - 21
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerDeptServiceImpl.java

@@ -55,7 +55,13 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
      */
     @Override
     public CustomerDeptVo queryById(Long id) {
-        return baseMapper.selectVoById(id);
+        RemoteDeptVo remoteDeptVo = remoteDeptService.selectDeptById(id);
+        CustomerDeptVo customerDeptVo = baseMapper.selectVoById(id);
+        if (null != remoteDeptVo) {
+            customerDeptVo.setDeptName(remoteDeptVo.getDeptName());
+            customerDeptVo.setParentId(remoteDeptVo.getParentId());
+        }
+        return customerDeptVo;
     }
 
 
@@ -69,25 +75,25 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
     public List<CustomerDeptVo> queryList(CustomerDeptBo bo) {
         LambdaQueryWrapper<CustomerDept> lqw = buildQueryWrapper(bo);
         List<CustomerDeptVo> list = baseMapper.selectVoList(lqw);
-        
+
         if (CollUtil.isEmpty(list)) {
             return list;
         }
-        
+
         // 关联查询 sys_dept 获取部门名称等信息
         List<Long> deptIds = list.stream()
             .map(CustomerDeptVo::getDeptId)
             .filter(id -> id != null)
             .collect(Collectors.toList());
-        
+
         if (CollUtil.isEmpty(deptIds)) {
             return list;
         }
-        
+
         List<RemoteDeptVo> sysDepts = remoteDeptService.selectDeptByIds(deptIds);
         Map<Long, RemoteDeptVo> deptMap = sysDepts.stream()
             .collect(Collectors.toMap(RemoteDeptVo::getDeptId, d -> d, (e, r) -> e));
-        
+
         // 补充部门名称等信息
         for (CustomerDeptVo vo : list) {
             RemoteDeptVo sysDept = deptMap.get(vo.getDeptId());
@@ -97,7 +103,7 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
                 vo.setStatus(sysDept.getStatus());
             }
         }
-        
+
         return list;
     }
 
@@ -168,7 +174,7 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
         lqw.eq(StringUtils.isNotBlank(bo.getDeptManage()), CustomerDept::getDeptManage, bo.getDeptManage());
         lqw.eq(StringUtils.isNotBlank(bo.getIsLimit()), CustomerDept::getIsLimit, bo.getIsLimit());
         lqw.eq(StringUtils.isNotBlank(bo.getSelectYear()), CustomerDept::getSelectYear, bo.getSelectYear());
-        // lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), CustomerDept::getPlatformCode, bo.getPlatformCode());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), CustomerDept::getPlatformCode, bo.getPlatformCode());
         return lqw;
     }
 
@@ -218,23 +224,63 @@ public class CustomerDeptServiceImpl extends ServiceImpl<CustomerDeptMapper, Cus
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean updateByBo(CustomerDeptBo bo) {
+        // 1. 参数校验
         if (bo == null || bo.getDeptId() == null || bo.getDeptId() <= 0) {
+            log.warn("参数校验失败: bo={}", bo);
+            return false;
+        }
+
+        Long deptId = bo.getDeptId();
+
+        // 2. 查询并更新远程部门信息
+        RemoteDeptVo remoteDeptVo = remoteDeptService.selectDeptById(deptId);
+        if (remoteDeptVo == null) {
+            log.warn("远程部门不存在,deptId={}", deptId);
+            return false;
+        }
+
+        // 更新远程部门字段
+        remoteDeptVo.setDeptName(bo.getDeptName());
+        remoteDeptVo.setParentId(bo.getParentId());
+
+        RemoteDeptVo updatedRemoteDept = remoteDeptService.updateDept(remoteDeptVo);
+        if (updatedRemoteDept == null) {
+            log.error("远程部门更新失败,deptId={}", deptId);
+            return false;
+        }
+
+        // 3. 更新本地客户部门信息
+        CustomerDeptVo customerDeptVo = baseMapper.selectVoById(deptId);
+        if (customerDeptVo == null) {
+            log.warn("本地客户部门不存在,deptId={}", deptId);
+            return false;
+        }
+
+        // 设置更新字段(可封装为方法)
+        customerDeptVo.setBindStatus(bo.getBindStatus());
+        customerDeptVo.setBindAddress(bo.getBindAddress());
+        customerDeptVo.setStatus(bo.getStatus());
+        customerDeptVo.setYearlyBudget(bo.getYearlyBudget());
+        customerDeptVo.setUsedBudget(bo.getUsedBudget());
+        customerDeptVo.setMonthLimit(bo.getMonthLimit());
+
+        // 转换为实体类并更新
+        CustomerDept entity = MapstructUtils.convert(customerDeptVo, CustomerDept.class);
+        int result = baseMapper.updateById(entity);
+
+        if (result <= 0) {
+            log.warn("本地客户部门更新未生效,deptId={}", deptId);
             return false;
         }
-        
-        // 更新 customer_dept 表(业务扩展字段)
-        // 注意:部门名称、上级部门等基础信息存在 sys_dept 表,暂不支持修改
-        CustomerDept dept = new CustomerDept();
-        dept.setDeptId(bo.getDeptId());
-        dept.setCustomerId(bo.getCustomerId());
-        dept.setYearlyBudget(bo.getYearlyBudget());
-        dept.setMonthLimit(bo.getMonthLimit());
-        dept.setUsedBudget(bo.getUsedBudget());
-        dept.setBindStatus(bo.getBindStatus());
-        dept.setBindAddress(bo.getBindAddress());
-        
-        return baseMapper.updateById(dept) > 0;
+
+        return true;
+    }
+
+    @Override
+    public List<CustomerDeptVo> selectCustomerDeptListByCustomerId(Long customerId) {
+        return baseMapper.selectVoList(new LambdaQueryWrapper<CustomerDept>().eq(CustomerDept::getCustomerId, customerId));
     }
 
     @Override

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

@@ -79,5 +79,15 @@ public class OrderAssignment extends TenantEntity {
      */
     private String remark;
 
+    /**
+     * 分配对象ID(供应商ID 或 伙伴商ID)
+     */
+    private Long assigneeId;
+
+    /**
+     * 分配对象类型(srm=供应商, bp=伙伴商)
+     */
+    private String assigneeType;
+
 
 }

+ 10 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderMain.java

@@ -280,4 +280,14 @@ public class OrderMain extends TenantEntity {
      */
     private String remark;
 
+    /**
+     * 分配对象ID(供应商ID 或 伙伴商ID)
+     */
+    private Long assigneeId;
+
+    /**
+     * 分配对象类型(srm=供应商, bp=伙伴商)
+     */
+    private String assigneeType;
+
 }

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

@@ -66,6 +66,8 @@ public class OrderProduct extends TenantEntity {
      */
     private String productName;
 
+    private Long productUnitId;
+
     /**
      * 产品单位
      */

+ 4 - 2
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/OrderProductAssignRule.java

@@ -7,7 +7,9 @@ import java.io.Serializable;
 @Data
 public class OrderProductAssignRule implements Serializable {
 
-    private Long itemId;          // order_product.id
+    private Long itemId;
 
-    private String targetPlatform; // 目标平台
+    private Long assigneeId;     // 供应商ID 或 伙伴商ID
+
+    private String assigneeType; // "srm" 或 "bp"
 }

+ 8 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderAssignmentBo.java

@@ -74,5 +74,13 @@ public class OrderAssignmentBo extends BaseEntity {
      */
     private String remark;
 
+    /**
+     * 分配对象ID(供应商ID 或 伙伴商ID)
+     */
+    private Long assigneeId;
 
+    /**
+     * 分配对象类型(srm=供应商, bp=伙伴商)
+     */
+    private String assigneeType;
 }

+ 10 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderMainBo.java

@@ -280,6 +280,16 @@ public class OrderMainBo extends BaseEntity {
     /*分配状态(0待分配 1已分配 )*/
     private String assignmentStatus;
 
+    /**
+     * 分配对象ID(供应商ID 或 伙伴商ID)
+     */
+    private Long assigneeId;
+
+    /**
+     * 分配对象类型(srm=供应商, bp=伙伴商)
+     */
+    private String assigneeType;
+
     /**
      * 订单状态
      */

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

@@ -65,6 +65,8 @@ public class OrderProductBo extends BaseEntity {
      */
     private String productName;
 
+    private Long productUnitId;
+
     /**
      * 产品单位
      */

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

@@ -87,5 +87,15 @@ public class OrderAssignmentVo implements Serializable {
     @ExcelProperty(value = "分配原因")
     private String remark;
 
+    /**
+     * 分配对象ID(供应商ID 或 伙伴商ID)
+     */
+    private Long assigneeId;
+
+    /**
+     * 分配对象类型(srm=供应商, bp=伙伴商)
+     */
+    private String assigneeType;
+
 
 }

+ 19 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/vo/OrderMainVo.java

@@ -348,10 +348,29 @@ public class OrderMainVo implements Serializable {
 
     private String platformCode;
 
+    /**
+     * 分配对象ID(供应商ID 或 伙伴商ID)
+     */
+    private Long assigneeId;
+
+    /**
+     * 分配对象类型(srm=供应商, bp=伙伴商)
+     */
+    private String assigneeType;
+
     private String createTime;
 
     private String customerName;
 
+    /*订单商品种数*/
+    private Long productTotal;
+
+    /*已分配数*/
+    private Long assigned;
+
+    private Long unassigned;
+
+
     private List<OrderProductVo> orderProductList;
 
     private List<OrderDeliverProductVo> deliverProductList;

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

@@ -78,6 +78,8 @@ public class OrderProductVo implements Serializable {
     @ExcelProperty(value = "产品名称")
     private String productName;
 
+    private Long productUnitId;
+
     /**
      * 产品单位
      */

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

@@ -7,6 +7,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 import org.dromara.order.domain.vo.OrderQuantitySummary;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 订单商品明细Mapper接口
@@ -16,6 +17,7 @@ import java.util.List;
  */
 public interface OrderProductMapper extends BaseMapperPlus<OrderProduct, OrderProductVo> {
 
+    /*查询订单关联的商品列表--并且查询商品已发货数量与未发货数量*/
     List<OrderProductVo> selectProductsWithDelivered(@Param("orderId") Long orderId);
 
     OrderQuantitySummary selectOrderAndDeliveredQuantity(@Param("orderId") Long orderId);
@@ -29,7 +31,10 @@ public interface OrderProductMapper extends BaseMapperPlus<OrderProduct, OrderPr
     void updateForSplitAssign(
         @Param("itemIds") List<Long> itemIds,
         @Param("newOrderId") Long newOrderId,
-        @Param("platformCode") String platformCode,
         @Param("assignmentStatus") String assignmentStatus
     );
+
+
+    /*查询  订单商品种数与已分配商品种数*/
+    Map getAssignmentStats(@Param("orderId") Long orderId);
 }

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

@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.context.PlatformContext;
+import org.dromara.common.core.enums.AssigneeTypeConstants;
 import org.dromara.common.core.enums.OrderAssignStatus;
 import org.dromara.common.core.enums.OrderSplitStatus;
 import org.dromara.common.core.enums.SysPlatformCode;
@@ -207,72 +208,92 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
         if (parentOrder == null) {
             throw new ServiceException("父订单不存在");
         }
-        if (OrderSplitStatus.SPLITED.getCode().equals(parentOrder.getSplitStatus())) {
+        if ("1".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()
             .collect(Collectors.toMap(OrderProduct::getId, Function.identity()));
 
-        // 验证:所有商品属于该父订单,且未分配
         for (OrderProductAssignRule rule : rules) {
             OrderProduct item = itemMap.get(rule.getItemId());
             if (item == null || !parentId.equals(item.getOrderId())) {
                 throw new ServiceException("商品行不属于该订单或不存在");
             }
-            if (OrderAssignStatus.ASSIGNED.getCode().equals(item.getAssignmentStatus())) {
+            if ("1".equals(item.getAssignmentStatus())) { // 已分配
                 throw new ServiceException("商品 [" + item.getProductName() + "] 已分配,不可重复操作");
             }
+            // 校验 assigneeType 合法性
+            String type = rule.getAssigneeType();
+            if (!AssigneeTypeConstants.SUPPLIER.getCode().equals(type) && !AssigneeTypeConstants.PARTNER.getCode().equals(type)) {
+                throw new ServiceException("分配对象类型不支持: " + type);
+            }
+        }
+
+        // 3. 按 (assigneeId, assigneeType) 分组
+        record AssigneeKey(Long id, String type) {
         }
+        Map<AssigneeKey, List<OrderProductAssignRule>> assigneeGroups = rules.stream()
+            .collect(Collectors.groupingBy(rule -> new AssigneeKey(rule.getAssigneeId(), rule.getAssigneeType())));
 
-        // 3. 按目标平台分组
-        Map<String, List<OrderProductAssignRule>> platformToRules = rules.stream()
-            .collect(Collectors.groupingBy(OrderProductAssignRule::getTargetPlatform));
-        OrderMain childOrder = null;
-        // 4. 为每个平台创建子订单,并迁移商品
-        for (Map.Entry<String, List<OrderProductAssignRule>> entry : platformToRules.entrySet()) {
-            String targetPlatform = entry.getKey();
+        // 4. 为每个分配对象创建子订单
+        for (Map.Entry<AssigneeKey, List<OrderProductAssignRule>> entry : assigneeGroups.entrySet()) {
+            AssigneeKey key = entry.getKey();
             List<OrderProductAssignRule> groupRules = entry.getValue();
-            childOrder = copyParentToChild(parentOrder, targetPlatform);
+
+            // 创建子订单
+            OrderMain childOrder = copyParentToChild(parentOrder, key.id(), key.type());
             orderMainMapper.insert(childOrder);
 
-            // 迁移商品行:更新 orderId + platform_code + assignment_status
+            // 更新商品:指向子订单 + 标记已分配
             List<Long> groupItemIds = groupRules.stream().map(OrderProductAssignRule::getItemId).collect(Collectors.toList());
             orderProductMapper.updateForSplitAssign(
                 groupItemIds,
                 childOrder.getId(),
-                targetPlatform,
-                OrderAssignStatus.ASSIGNED.getCode()
+                OrderAssignStatus.ASSIGNED.getCode() // assignmentStatus = 已分配
             );
 
-            // 插入分配记录(子订单维度)
+            // 插入分配记录
             OrderAssignment assignment = new OrderAssignment();
             assignment.setOrderId(childOrder.getId());
             assignment.setOrderNo(childOrder.getOrderNo());
             assignment.setPlatformBefore(parentOrder.getPlatformCode());
-            assignment.setPlatformAfter(targetPlatform);
             assignment.setAssignedBy(operatorId);
             assignment.setAssignTime(now);
-            assignment.setAssignType("0"); // 拆单类型
+            assignment.setAssignType("0");
             assignment.setRemark(bo.getRemark());
-            baseMapper.insert(assignment);
+            assignment.setAssigneeId(key.id());
+            assignment.setAssigneeType(key.type());
+            baseMapper.insert(assignment); // 假设 baseMapper 是 OrderAssignmentMapper
         }
 
-        // 5. 更新父订单状态:标记为已拆分
+        Map<String, Object> stats = orderProductMapper.getAssignmentStats(parentId);
+        Long total = (Long) stats.getOrDefault("total", 0L);
+        Long assigned = (Long) stats.getOrDefault("assigned", 0L);
+
+        String parentAssignmentStatus;
+        if (assigned == 0) {
+            parentAssignmentStatus = OrderAssignStatus.WAIT_ASSIGN.getCode(); // 待分配
+        } else if (assigned.equals(total)) {
+            parentAssignmentStatus = OrderAssignStatus.ASSIGNED.getCode(); // 已分配
+        } else {
+            parentAssignmentStatus = OrderAssignStatus.PARTIALLY_ASSIGNED.getCode();// 部分分配
+        }
+
+        // 5. 更新父订单状态
         OrderMain updateParent = new OrderMain();
         updateParent.setId(parentId);
-        updateParent.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode());
         updateParent.setSplitStatus(OrderSplitStatus.SPLITED.getCode()); // 已拆分
-        // 注意:父订单 platformCode 保持不变(作为入口)
+        updateParent.setAssignmentStatus(parentAssignmentStatus);
+        updateParent.setUpdateTime(now);
         orderMainMapper.updateById(updateParent);
 
         return true;
     }
 
-
     private List<Long> parseOrderIds(String orderIdsStr) {
         if (StringUtils.contains(orderIdsStr, ",")) {
             return Arrays.stream(orderIdsStr.split(","))
@@ -301,38 +322,56 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
     }
 
 
-    private OrderMain copyParentToChild(OrderMain parent, String targetPlatform) {
+    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");
-        child.setPlatformCode(targetPlatform);
+        child.setIsSplitChild("0"); // 0=是子订单
         child.setOrderNo(parent.getOrderNo() + paddedSeq);
         child.setCompanyId(parent.getCompanyId());
         child.setCustomerId(parent.getCustomerId());
-        child.setExpectedDeliveryTime(parent.getExpectedDeliveryTime());
+        child.setCustomerCode(parent.getCustomerCode());
+        child.setUserId(parent.getUserId());
+        child.setUserNo(parent.getUserNo());
         child.setShippingAddressId(parent.getShippingAddressId());
         child.setWarehouseId(parent.getWarehouseId());
+        child.setExpectedDeliveryTime(parent.getExpectedDeliveryTime());
+        child.setPurchaseReason(parent.getPurchaseReason());
         child.setInvoiceType(parent.getInvoiceType());
         child.setPayType(parent.getPayType());
+        child.setCreditLimit(parent.getCreditLimit());
+        child.setBusinessStaff(parent.getBusinessStaff());
+        child.setCustomerService(parent.getCustomerService());
+        child.setBusinessDept(parent.getBusinessDept());
+        child.setUserDept(parent.getUserDept());
         child.setTotalAmount(parent.getTotalAmount());
         child.setPayableAmount(parent.getPayableAmount());
         child.setShippingFee(parent.getShippingFee());
         child.setOrderSource(parent.getOrderSource());
-        child.setBusinessStaff(parent.getBusinessStaff());
-        child.setBusinessDept(parent.getBusinessDept());
-        child.setPurchaseReason(parent.getPurchaseReason());
-        child.setCreditLimit(parent.getCreditLimit());
-        child.setOrderStatus(parent.getOrderStatus());
-        child.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode());
-        child.setSplitStatus("0");
-        child.setRemark(parent.getRemark());
+        child.setOrderStatus(parent.getOrderStatus()); // 继承状态,后续由子订单独立流转
+        child.setOrderTime(parent.getOrderTime());
+        child.setConfirmTime(parent.getConfirmTime());
+        child.setDeliveryType(parent.getDeliveryType());
+        child.setExpenseType(parent.getExpenseType());
         child.setCheckStatus(parent.getCheckStatus());
+        child.setDataSource(parent.getDataSource());
+        child.setRemark(parent.getRemark());
+        child.setAttachmentPath(parent.getAttachmentPath());
+
+        // 新增分配字段
+        child.setAssigneeId(assigneeId);
+        child.setAssigneeType(assigneeType);
+
+        // 子订单状态初始化
+        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());
-        child.setDataSource(parent.getDataSource());
-        child.setDeliveryType(parent.getDeliveryType());
+
         return child;
     }
 

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

@@ -68,15 +68,27 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
             return null;
         }
 
-        // 2. 查询关联的商品列表
+        // 2. 查询关联的商品列表--并且查询商品已发货数量与未发货数量
         List<OrderProductVo> orderProductVoList = orderProductMapper.selectProductsWithDelivered(orderMainVo.getId());
 
         // 3. 组装数据
         orderMainVo.setOrderProductList(orderProductVoList);
 
+        Set<Long> customerIds = new HashSet<>();
+        customerIds.add(orderMainVo.getCustomerId());
+        Map<Long, String> map = remoteCustomerService.selectCustomerNameByIds(customerIds);
+        orderMainVo.setCustomerName(map.get(orderMainVo.getCustomerId()));
+
         orderMainVo.setDeliverProductList(
             orderDeliverProductMapper.selectDeliverProductsByOrderId(orderMainVo.getId())
         );
+
+        Map<String, Object> stats = orderProductMapper.getAssignmentStats(orderMainVo.getId());
+        Long total = (Long) stats.getOrDefault("total", 0L);
+        Long assigned = (Long) stats.getOrDefault("assigned", 0L);
+        orderMainVo.setProductTotal(total);
+        orderMainVo.setUnassigned(total - assigned);
+        orderMainVo.setAssigned(assigned);
         // 4. 返回已组装好的对象
         return orderMainVo;
     }

+ 21 - 4
ruoyi-modules/ruoyi-order/src/main/resources/mapper/order/OrderProductMapper.xml

@@ -27,6 +27,7 @@
                op.after_sale_quantity                                AS afterSaleQuantity,
                op.return_amount                                      AS returnAmount,
                op.pre_delivery_date                                  AS preDeliveryDate,
+               op.assignment_status                                  AS assignmentStatus,
                op.status                                             AS status,
                op.del_flag                                           AS delFlag,
                op.remark                                             AS remark
@@ -63,14 +64,30 @@
 
     <update id="updateForSplitAssign">
         UPDATE order_product
-        SET
-        order_id = #{newOrderId},
-        platform_code = #{platformCode},
-        assignment_status = #{assignmentStatus}
+        SET order_id = #{newOrderId},
+        assignment_status = #{assignmentStatus},
+        update_time = NOW()
         WHERE id IN
         <foreach collection="itemIds" item="id" open="(" separator="," close=")">
             #{id}
         </foreach>
+        AND del_flag = '0'
     </update>
 
+    <select id="countUnassignedProducts" resultType="long">
+        SELECT COUNT(1)
+        FROM order_product
+        WHERE order_id = #{orderId}
+          AND assignment_status != '1'
+      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
+        FROM order_product
+        WHERE order_id = #{orderId}
+          AND del_flag = '0'
+    </select>
+
 </mapper>

+ 2 - 2
ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/domain/bo/FileCategoryBo.java

@@ -36,7 +36,7 @@ public class FileCategoryBo extends BaseEntityWithoutPlatform {
     /**
      * 分类编码
      */
-    @NotBlank(message = "分类编码不能为空", groups = {AddGroup.class, EditGroup.class})
+//    @NotBlank(message = "分类编码不能为空", groups = {AddGroup.class, EditGroup.class})
     private String code;
 
     /**
@@ -47,7 +47,7 @@ public class FileCategoryBo extends BaseEntityWithoutPlatform {
     /**
      * 分类类型(1图片 2视频 3音频 4文档 5其他)
      */
-    @NotNull(message = "分类类型不能为空", groups = {AddGroup.class, EditGroup.class})
+//    @NotNull(message = "分类类型不能为空", groups = {AddGroup.class, EditGroup.class})
     private Integer type;
 
     /**

+ 4 - 0
ruoyi-modules/ruoyi-system/pom.xml

@@ -114,6 +114,10 @@
             <groupId>org.dromara</groupId>
             <artifactId>ruoyi-api-workflow</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-api-customer</artifactId>
+        </dependency>
 
     </dependencies>
 

+ 10 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java

@@ -56,6 +56,16 @@ public class SysDeptController extends BaseController {
         return R.ok(depts);
     }
 
+    /**
+     * 获取部门列表
+     */
+    @SaCheckPermission("system:dept:list")
+    @GetMapping("/customerDeptList/{customerId}")
+    public R<List<SysDeptVo>> customerDeptList(@PathVariable(value = "customerId", required = false) Long customerId) {
+        List<SysDeptVo> depts = deptService.selectCustomerDeptList(customerId);
+        return R.ok(depts);
+    }
+
     /**
      * 根据部门编号获取详细信息
      *

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

@@ -155,5 +155,13 @@ public interface ISysDeptService {
      */
     List<SysDeptVo> selectDeptsSimple();
 
+    /**
+     * 查询客户部门管理数据
+     *
+     * @param customerId
+     * @return 部门信息集合
+     */
+    List<SysDeptVo> selectCustomerDeptList(Long customerId);
+
     Map<Long, String> selectDeptNameByIds(Set<Long> ids);
 }

+ 102 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java

@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import lombok.RequiredArgsConstructor;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.constant.CacheNames;
 import org.dromara.common.core.constant.SystemConstants;
 import org.dromara.common.core.context.PlatformContext;
@@ -20,6 +21,8 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.helper.DataBaseHelper;
 import org.dromara.common.redis.utils.CacheUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.customer.api.RemoteCustomerDeptService;
+import org.dromara.customer.api.domain.vo.RemoteCustomerDeptVo;
 import org.dromara.system.domain.SysDept;
 import org.dromara.system.domain.SysRole;
 import org.dromara.system.domain.SysUser;
@@ -36,6 +39,8 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * 部门管理 服务实现
@@ -46,6 +51,9 @@ import java.util.*;
 @Service
 public class SysDeptServiceImpl implements ISysDeptService {
 
+    @DubboReference
+    private final RemoteCustomerDeptService remoteCustomerDeptService;
+
     private final SysDeptMapper baseMapper;
     private final SysRoleMapper roleMapper;
     private final SysUserMapper userMapper;
@@ -457,4 +465,98 @@ public class SysDeptServiceImpl implements ISysDeptService {
         return resultMap;
     }
 
+    @Override
+    public List<SysDeptVo> selectCustomerDeptList(Long customerId) {
+        if (customerId == null) {
+            return Collections.emptyList();
+        }
+
+        // 1. 获取客户绑定的 deptId 集合
+        List<RemoteCustomerDeptVo> remoteDepts = remoteCustomerDeptService.selectCustomerDeptListByCustomerId(customerId);
+        if (CollUtil.isEmpty(remoteDepts)) {
+            return Collections.emptyList();
+        }
+
+        Set<Long> customerDeptIds = remoteDepts.stream()
+            .map(RemoteCustomerDeptVo::getDeptId)
+            .filter(Objects::nonNull)
+            .collect(Collectors.toSet());
+
+        if (customerDeptIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        // 2. 查询这些 deptId 对应的 SysDept(DO)
+        List<SysDept> customerDepts = baseMapper.selectList(new LambdaQueryWrapper<SysDept>()
+            .in(SysDept::getDeptId, customerDeptIds)
+            .eq(SysDept::getStatus, SystemConstants.NORMAL));
+
+        if (CollUtil.isEmpty(customerDepts)) {
+            return Collections.emptyList();
+        }
+
+        // 3. 找出其中 parentId == 100 的部门(即 100 的直接子部门)
+        List<SysDept> directChildrenOf100 = customerDepts.stream()
+            .filter(dept -> 100L == dept.getParentId())
+            .collect(Collectors.toList());
+
+        if (directChildrenOf100.isEmpty()) {
+            // 如果客户绑定的部门都不在 100 下,返回空
+            return Collections.emptyList();
+        }
+
+        // 4. 收集这些 direct children 及其所有后代 deptId
+        Set<Long> allRelevantDeptIds = new HashSet<>();
+        for (SysDept root : directChildrenOf100) {
+            collectAllDescendantIds(root.getDeptId(), allRelevantDeptIds); // 递归或迭代收集
+        }
+
+        // 5. 查询所有相关部门(包括后代)
+        List<SysDept> allDepts = baseMapper.selectList(new LambdaQueryWrapper<SysDept>()
+            .in(SysDept::getDeptId, allRelevantDeptIds)
+            .eq(SysDept::getStatus, SystemConstants.NORMAL));
+
+        // 6. 构建树形结构(只基于这些相关节点)
+        Map<Long, SysDept> deptMap = allDepts.stream()
+            .collect(Collectors.toMap(SysDept::getDeptId, Function.identity()));
+
+        // 初始化 children
+        allDepts.forEach(dept -> dept.setChildren(new ArrayList<>()));
+
+        // 建立父子关系
+        List<SysDept> resultTree = new ArrayList<>();
+        for (SysDept dept : allDepts) {
+            if (directChildrenOf100.stream().anyMatch(d -> d.getDeptId().equals(dept.getDeptId()))) {
+                // 是我们要的根节点(100 的 direct child)
+                resultTree.add(dept);
+            } else {
+                // 非根节点,挂到父节点下
+                SysDept parent = deptMap.get(dept.getParentId());
+                if (parent != null) {
+                    parent.getChildren().add(dept);
+                }
+            }
+        }
+
+        // 7. 转 VO
+        return resultTree.stream()
+            .map(dept -> {
+                SysDeptVo vo = MapstructUtils.convert(dept, SysDeptVo.class);
+                vo.setChildren(dept.getChildren());
+                return vo;
+            })
+            .collect(Collectors.toList());
+    }
+
+    private void collectAllDescendantIds(Long rootDeptId, Set<Long> result) {
+        result.add(rootDeptId);
+        List<SysDept> children = baseMapper.selectList(
+            new LambdaQueryWrapper<SysDept>()
+                .eq(SysDept::getParentId, rootDeptId)
+                .eq(SysDept::getStatus, SystemConstants.NORMAL)
+        );
+        for (SysDept child : children) {
+            collectAllDescendantIds(child.getDeptId(), result);
+        }
+    }
 }