Bläddra i källkod

feat(order): 增加营销平台分配类型并完善订单拆分分配逻辑

- 新增MKT营销平台分配类型常量定义
- 实现多层级订单拆分限制校验功能
- 完善分配对象类型的校验规则和assigneeId必填验证
- 实现订单商品层级关系和追溯字段管理
- 优化子订单创建逻辑和父子订单关联关系处理
- 支持根据分配类型动态设置订单状态和分配状态
- 实现多级订单的发货单号生成机制
- 完善订单查询中的层级筛选功能
- 优化平台分配策略和条件判断逻辑
hurx 1 vecka sedan
förälder
incheckning
8a36842a9c

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

@@ -17,6 +17,11 @@ public enum AssigneeTypeConstants {
      */
     PARTNER("bp", "伙伴商"),
 
+    /**
+     * 营销平台
+     */
+    MKT("mkt" ,"营销平台"),
+
     /**
      * 伙伴商
      */

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

@@ -47,6 +47,16 @@ public class OrderProduct extends TenantEntity {
     /*被分配到的子订单ID*/
     private Long assignedChildOrderId;
 
+    /**
+     * 根商品ID(始终指向1级订单的原始商品ID)
+     */
+    private Long rootItemId;
+
+    /**
+     * 父商品ID(指向上一级订单的商品ID)
+     */
+    private Long parentItemId;
+
     /**
      * 订单编号
      */

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

@@ -11,5 +11,6 @@ public class OrderProductAssignRule implements Serializable {
 
     private Long assigneeId;     // 供应商ID 或 伙伴商ID
 
-    private String assigneeType; // "srm" 或 "bp"或者"zy"
+    /*mkt 表示平台 zy表示自营发货*/
+    private String assigneeType; // "srm" 或 "bp"或者"zy"或者"mkt"
 }

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

@@ -76,6 +76,11 @@ public class OrderMainBo extends BaseEntity {
      */
     private Integer currentLevel;
 
+    /**
+     * 当前级数字符串(支持单个值"1"或多个值"1,2")
+     */
+    private String currentLevelStr;
+
     /**
      * 所属公司
      */

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

@@ -46,6 +46,16 @@ public class OrderProductBo extends BaseEntity {
     /*被分配到的子订单ID*/
     private Long assignedChildOrderId;
 
+    /**
+     * 根商品ID(始终指向1级订单的原始商品ID)
+     */
+    private Long rootItemId;
+
+    /**
+     * 父商品ID(指向上一级订单的商品ID)
+     */
+    private Long parentItemId;
+
     /**
      * 订单编号
      */

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

@@ -53,6 +53,16 @@ public class OrderProductVo implements Serializable {
     /*被分配到的子订单ID*/
     private Long assignedChildOrderId;
 
+    /**
+     * 根商品ID(始终指向1级订单的原始商品ID)
+     */
+    private Long rootItemId;
+
+    /**
+     * 父商品ID(指向上一级订单的商品ID)
+     */
+    private Long parentItemId;
+
     /**
      * 订单编号
      */

+ 130 - 17
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderAssignmentServiceImpl.java

@@ -258,6 +258,7 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
     @Transactional(rollbackFor = Exception.class)
     public Boolean splitAssign(OrderSplitAssignBo bo) {
         Long parentId = bo.getOrderId();
+        String platform = PlatformContext.getPlatform(); //当前平台标识
         List<OrderProductAssignRule> rules = bo.getItemRules();
 
         if (CollUtil.isEmpty(rules)) {
@@ -272,11 +273,14 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
         if (parentOrder == null) {
             throw new ServiceException("父订单不存在");
         }
-      /*  if (OrderSplitStatus.SPLITED.getCode().equals(parentOrder.getSplitStatus())) {
-            throw new ServiceException("该订单已被拆分,不可重复操作");
-        }*/
 
-        // 2. 验证商品行(必须属于该主订单且未分配)
+        // 1.1 校验订单层级:只有1级和2级订单可以继续拆分,3级订单不允许再拆分
+        Integer parentLevel = parentOrder.getCurrentLevel() != null ? parentOrder.getCurrentLevel() : 1;
+        if (parentLevel >= 3) {
+            throw new ServiceException("3级订单不允许再次拆分分配");
+        }
+
+        // 2. 验证商品行(必须属于该订单且未分配)
         List<Long> itemIds = rules.stream().map(OrderProductAssignRule::getItemId).collect(Collectors.toList());
         List<OrderProduct> items = orderProductMapper.selectBatchIds(itemIds);
         Map<Long, OrderProduct> itemMap = items.stream()
@@ -291,11 +295,20 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
                 throw new ServiceException("商品 [" + item.getProductName() + "] 已分配,不可重复操作");
             }
             String type = rule.getAssigneeType();
+            // 支持多种分配类型:srm(供应商)、bp(伙伴商)、zy(自营)、mkt(平台)
             if (!AssigneeTypeConstants.SUPPLIER.getCode().equals(type) &&
                 !AssigneeTypeConstants.PARTNER.getCode().equals(type) &&
-                !AssigneeTypeConstants.CUSTOMER.getCode().equals(type)) {
+                !AssigneeTypeConstants.CUSTOMER.getCode().equals(type) &&
+                !AssigneeTypeConstants.MKT.getCode().equals(type)) {
                 throw new ServiceException("分配对象类型不支持: " + type);
             }
+            // 校验:srm和bp必须有assigneeId,zy和mkt不能有assigneeId
+            if (("srm".equals(type) || "bp".equals(type)) && rule.getAssigneeId() == null) {
+                throw new ServiceException("分配类型为 [" + type + "] 时,必须指定分配对象ID");
+            }
+            if (("zy".equals(type) || "mkt".equals(type)) && rule.getAssigneeId() != null) {
+                throw new ServiceException("分配类型为 [" + type + "] 时,不能指定分配对象ID");
+            }
         }
 
         // 3. 按 (assigneeId, assigneeType) 分组
@@ -333,8 +346,8 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
                 }
             }
 
-            // 创建子订单(从主订单拷贝必要字段
-            OrderMain childOrder = copyParentToChild(parentOrder, key.id(), key.type());
+            // 创建子订单(从父订单拷贝必要字段,并设置正确的层级关系
+            OrderMain childOrder = copyParentToChild(parentOrder, key.id(), key.type(), parentLevel);
 
             // 用刚才累加的正确数量,覆盖掉从父订单继承的数量
             childOrder.setProductQuantity(childTotalQuantity);
@@ -351,7 +364,7 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
             for (OrderProductAssignRule rule : groupRules) {
                 OrderProduct parentItem = itemMap.get(rule.getItemId());
 
-                // 1. 收集订单商品ID,用于后续批量更新分配状态
+                // 1. 收集订单商品ID,用于后续批量更新分配状态
                 parentItemIdsToUpdate.add(parentItem.getId());
 
                 // 2. 创建子订单商品副本
@@ -362,7 +375,15 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
 
                 // 关联信息
                 childProduct.setOrderId(childOrderId);           // 归属子订单
-                childProduct.setOriginalItemId(parentItem.getId()); // 指向主订单商品
+                childProduct.setParentItemId(parentItem.getId()); // 指向上一级商品ID
+
+                // 设置根商品ID:如果父商品已有rootItemId则继承,否则父商品本身就是根商品
+                if (parentItem.getRootItemId() != null) {
+                    childProduct.setRootItemId(parentItem.getRootItemId());
+                } else {
+                    // 父商品是1级商品,它自己就是根
+                    childProduct.setRootItemId(parentItem.getId());
+                }
 
                 // 订单基础信息
                 childProduct.setOrderNo(childOrder.getOrderNo()); // 子订单编号
@@ -395,7 +416,17 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
                 childProduct.setReturnAmount(BigDecimal.ZERO);
 
                 // 状态与标志
-                childProduct.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode()); // 已分配
+                childProduct.setCurrentLevel(parentLevel + 1); // 子订单商品层级 = 父层级 + 1
+
+                // 根据分配对象类型设置商品分配状态
+                String assigneeType = key.type();
+                if ("mkt".equals(assigneeType)) {
+                    // 分配给平台(mkt):商品保持待分配状态,以便在market平台再次分配
+                    childProduct.setAssignmentStatus(OrderAssignStatus.WAIT_ASSIGN.getCode());
+                } else {
+                    // 分配给其他对象(srm/bp/zy):商品标记为已分配
+                    childProduct.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode());
+                }
 
                 // 时间 & 其他
                 childProduct.setPreDeliveryDate(parentItem.getPreDeliveryDate());
@@ -409,7 +440,7 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
                 orderProductMapper.insertBatch(childProducts);
             }
 
-            // 批量更新订单商品:标记已分配 + 记录分配到的子订单ID
+            // 批量更新订单商品:标记已分配 + 记录分配到的子订单ID
             if (!parentItemIdsToUpdate.isEmpty()) {
                 orderProductMapper.updateAssignmentInfo(
                     parentItemIdsToUpdate,
@@ -492,12 +523,48 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
     }
 
 
-    private OrderMain copyParentToChild(OrderMain parent, Long assigneeId, String assigneeType) {
+    private OrderMain copyParentToChild(OrderMain parent, Long assigneeId, String assigneeType, Integer parentLevel) {
         OrderMain child = new OrderMain();
 
-        child.setParentOrderId(parent.getId());
-        child.setIsSplitChild("0"); // 0=是子订单
+        // 设置子订单编号
         child.setOrderNo(SequenceUtils.generateOrderCode("ZD"));
+
+        // 计算子订单层级和订单类型
+        Integer childLevel;
+        String orderType = parent.getOrderType(); // 继承父订单的订单类型
+
+        // 根据订单类型和分配对象决定子订单层级
+        if ("1".equals(orderType)) {
+            // 项目订单 (orderType=1) - DMS平台分配
+            // 分配给 mkt 或 bp 都生成2级订单
+            childLevel = parentLevel + 1; // 通常是 1 -> 2
+        } else {
+            // 自营订单 (orderType=0) - Market平台分配
+            // 分配给 zy 或 srm 都生成3级订单(如果是从1级直接分配)或下一级
+            childLevel = parentLevel + 1; // 通常是 2 -> 3
+        }
+
+        child.setCurrentLevel(childLevel);
+
+        // 根据父订单层级设置不同的父子关系字段
+        if (parentLevel == 1) {
+            // 父订单是1级,子订单是2级
+            // 2级订单:parentOrderId/parentOrderNo 指向1级订单,subOrderId/subOrderNo 为空
+            child.setParentOrderId(parent.getId());
+            child.setParentOrderNo(parent.getOrderNo());
+            child.setSubOrderId(null);
+            child.setSubOrderNo(null);
+        } else if (parentLevel == 2) {
+            // 父订单是2级,子订单是3级
+            // 3级订单:parentOrderId/parentOrderNo 指向1级订单,subOrderId/subOrderNo 指向2级订单
+            child.setParentOrderId(parent.getParentOrderId()); // 继承1级订单ID
+            child.setParentOrderNo(parent.getParentOrderNo()); // 继承1级订单编号
+            child.setSubOrderId(parent.getId()); // 指向2级订单(当前父订单)
+            child.setSubOrderNo(parent.getOrderNo()); // 指向2级订单编号
+        }
+
+        // 复制其他基础字段
+        child.setIsSplitChild("0"); // 0=是子订单
         child.setCompanyId(parent.getCompanyId());
         child.setCustomerId(parent.getCustomerId());
         child.setCustomerCode(parent.getCustomerCode());
@@ -520,7 +587,16 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
         child.setOrderSource(parent.getOrderSource());
         child.setProductQuantity(parent.getProductQuantity());
         child.setQuantityShipped(parent.getQuantityShipped());
-        child.setOrderStatus(OrderStatus.PENDING_CONFIRMATION.getCode()); // 子订单状态初始化为待确认  被分配者需要重新确认
+
+        // 根据分配对象类型设置订单状态
+        if ("zy".equals(assigneeType) || "mkt".equals(assigneeType)) {
+            child.setOrderStatus(OrderStatus.PENDING_SHIPMENT.getCode()); // 自营:待发货,不需要确认
+
+        } else {
+            // srm 或 bp:待确认
+            child.setOrderStatus(OrderStatus.PENDING_CONFIRMATION.getCode());
+        }
+
         child.setOrderTime(parent.getOrderTime());
         child.setConfirmTime(parent.getConfirmTime());
         child.setDeliveryType(parent.getDeliveryType());
@@ -532,13 +608,50 @@ public class OrderAssignmentServiceImpl extends ServiceImpl<OrderAssignmentMappe
         child.setCreateBy(parent.getCreateBy());
         child.setCreateDept(parent.getCreateDept());
         child.setPaymentStatus(parent.getPaymentStatus());
+        child.setOrderType(parent.getOrderType());
         // 新增分配字段
         child.setAssigneeId(assigneeId);
         child.setAssigneeType(assigneeType);
 
         // 子订单状态初始化
-        child.setSplitStatus(OrderSplitStatus.WAIT_SPLIT.getCode()); // 子订单默认未拆(通常不再拆)
-        child.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode()); // 已分配
+        child.setSplitStatus(OrderSplitStatus.WAIT_SPLIT.getCode()); // 子订单默认未拆
+
+        // 根据订单类型、分配对象和层级设置分配状态
+        if ("1".equals(orderType)) {
+            // 项目订单 (DMS平台分配)
+            if ("mkt".equals(assigneeType)) {
+                // 分配给平台(mkt):2级订单,待分配状态,可在market再次分配
+                child.setAssignmentStatus(OrderAssignStatus.WAIT_ASSIGN.getCode());
+            } else if ("bp".equals(assigneeType)) {
+                // 分配给伙伴商(bp):2级订单,已分配状态
+                child.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode());
+                /*
+                 * 生成发货单号*/
+                child.setShipmentNo(SequenceUtils.generateOrderCode("YO"));
+            } else if ("zy".equals(assigneeType) || "srm".equals(assigneeType)) {
+                // 3级订单:已分配状态,不可再分配
+                child.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode());
+                /*
+                 * 生成发货单号*/
+                child.setShipmentNo(SequenceUtils.generateOrderCode("YO"));
+            } else {
+                // 其他情况:2级订单默认待分配
+                child.setAssignmentStatus(OrderAssignStatus.WAIT_ASSIGN.getCode());
+
+            }
+        } else {
+            // 自营订单 (Market平台分配)
+            if ("zy".equals(assigneeType) || "srm".equals(assigneeType)) {
+                // 2级订单:已分配状态,不可再分配
+                child.setAssignmentStatus(OrderAssignStatus.ASSIGNED.getCode());
+                /*
+                 * 生成发货单号*/
+                child.setShipmentNo(SequenceUtils.generateOrderCode("YO"));
+            } else {
+                // 其他层级:待分配
+                child.setAssignmentStatus(OrderAssignStatus.WAIT_ASSIGN.getCode());
+            }
+        }
         return child;
     }
 

+ 59 - 12
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderMainServiceImpl.java

@@ -339,17 +339,21 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
             // 获取前端传过来的 assigneeType
             String assigneeType = bo.getAssigneeType();
             if ("market".equals(platformCode)) {
-                // 判断当前是否为 zy 类型
-                if ("zy".equals(assigneeType)) {
-                    // 使用 and 嵌套查询,实现 (assignee_type = 'zy' OR assignee_type IS NULL)
-                    lqw.and(wrapper -> wrapper
-                        .eq(OrderMain::getAssigneeType, "zy")
-                        .or()
-                        .isNull(OrderMain::getAssigneeType)
-                        .or()
-                        .eq(OrderMain::getAssigneeType, "")
-                    );
-                }
+//                // 判断当前是否为 zy 类型
+//                if ("zy".equals(assigneeType)) {
+//                    // 使用 and 嵌套查询,实现 (assignee_type = 'zy' OR assignee_type IS NULL)
+//                    lqw.and(wrapper -> wrapper
+//                        .eq(OrderMain::getAssigneeType, "zy")
+//                        .or()
+//                        .isNull(OrderMain::getAssigneeType)
+//                        .or()
+//                        .eq(OrderMain::getAssigneeType, "")
+//                    );
+//                }else if ("mkt".equals(assigneeType)){
+//                    lqw.eq(StringUtils.isNotBlank(bo.getAssigneeType()), OrderMain::getAssigneeType, bo.getAssigneeType());
+//                }
+                lqw.eq(StringUtils.isNotBlank(bo.getAssigneeType()), OrderMain::getAssigneeType, bo.getAssigneeType());
+
             } else if ("scm".equals(platformCode)) {
                 lqw.eq(StringUtils.isNotBlank(bo.getAssigneeType()), OrderMain::getAssigneeType, bo.getAssigneeType());
             } else if ("dms".equals(platformCode)) {
@@ -412,6 +416,32 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
         lqw.eq(StringUtils.isNotBlank(bo.getStatus()), OrderMain::getStatus, bo.getStatus());
         lqw.eq(StringUtils.isNotBlank(bo.getIsNeedCheck()), OrderMain::getIsNeedCheck, bo.getIsNeedCheck());
         lqw.eq(StringUtils.isNotBlank(bo.getReturnedStatus()), OrderMain::getReturnedStatus, bo.getReturnedStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getOrderType()), OrderMain::getOrderType, bo.getOrderType());
+
+        // 处理 currentLevelStr:支持单个值"1"或多个值"1,2"
+        if (StringUtils.isNotBlank(bo.getCurrentLevelStr())) {
+            String currentLevelStr = bo.getCurrentLevelStr().trim();
+            if (currentLevelStr.contains(",")) {
+                // 多个值,使用 IN 查询
+                String[] levels = currentLevelStr.split(",");
+                List<Integer> levelList = Arrays.stream(levels)
+                    .map(String::trim)
+                    .filter(StringUtils::isNotBlank)
+                    .map(Integer::parseInt)
+                    .collect(Collectors.toList());
+                if (!levelList.isEmpty()) {
+                    lqw.in(OrderMain::getCurrentLevel, levelList);
+                }
+            } else {
+                // 单个值,使用 EQ 查询
+                try {
+                    Integer level = Integer.parseInt(currentLevelStr);
+                    lqw.eq(OrderMain::getCurrentLevel, level);
+                } catch (NumberFormatException e) {
+                    // 忽略无效的层级值
+                }
+            }
+        }
         // 关键词搜索:精确匹配订单号
         if (StringUtils.isNotBlank(bo.getSearchValue())) {
             lqw.eq(OrderMain::getOrderNo, bo.getSearchValue().trim());
@@ -496,9 +526,17 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
                         orderTimeToUse = new Date();
                     }
                     bo.setOrderTime(orderTimeToUse);
-
+                    bo.setOrderType("0");
+                    bo.setAssigneeType(AssigneeTypeConstants.MKT.getCode());
                     // --- 步骤 C: 转换对象 ---
                     orderMain = MapstructUtils.convert(bo, OrderMain.class);
+
+                    // 初始化1级订单的层级信息
+                    if (orderMain.getCurrentLevel() == null) {
+                        orderMain.setCurrentLevel(1); // 新创建的订单默认为1级
+                    }
+                    // 1级订单的 parentOrderId/parentOrderNo/subOrderId/subOrderNo 都为空(默认就是null)
+
                     validEntityBeforeSave(orderMain);
 
                     // --- 步骤 D: 尝试插入 (关键兜底点) ---
@@ -576,6 +614,12 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
                     product.setOrderId(orderId);
                     product.setOrderNo(orderNum);
 
+                    // 初始化1级订单商品的层级和追溯字段
+                    if (product.getCurrentLevel() == null) {
+                        product.setCurrentLevel(1); // 1级订单商品
+                    }
+                    // rootItemId 和 parentItemId 在1级订单商品中都为空(自己就是根)
+
                     // 小计 = 单价 × 数量
                     BigDecimal subtotal = unitPrice.multiply(BigDecimal.valueOf(quantity));
                     product.setSubtotal(subtotal);
@@ -656,6 +700,9 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
             mainBo.setBusinessDept(deptMap.get(belongingDepartmentId));
         }
         mainBo.setOrderNoPrefix("MO");
+        mainBo.setOrderType("0");
+        mainBo.setAssigneeType(AssigneeTypeConstants.MKT.getCode());
+
         // 1. 插入主订单,获取生成的 ID
         Long orderId = this.insertByBo(mainBo);
         // 2. 校验插入是否成功