1、订单新增加工状态字段;2、拆单、删除部件、部件加工增加订单加工状态计算能力;

This commit is contained in:
gaoqr
2026-08-14 15:10:17 +08:00
parent a459eebde4
commit eb87c7e7b2
18 changed files with 423 additions and 86 deletions
@@ -246,6 +246,9 @@ public class ErrorCodeConstants {
public static final ErrorCode ORDER_COMPONENT_ASSEMBLY_CONFIG_QUERY_ERROR = new ErrorCode(1_003_010_005, "order.component.assembly.config.query.error"); public static final ErrorCode ORDER_COMPONENT_ASSEMBLY_CONFIG_QUERY_ERROR = new ErrorCode(1_003_010_005, "order.component.assembly.config.query.error");
public static final ErrorCode ORDER_COMPONENT_NOT_PROCESS = new ErrorCode(1_003_010_006, "order.component.not.process"); public static final ErrorCode ORDER_COMPONENT_NOT_PROCESS = new ErrorCode(1_003_010_006, "order.component.not.process");
public static final ErrorCode ORDER_COMPONENT_RESTORE_REPEAT = new ErrorCode(1_003_010_007, "order.component.restore.repeat"); public static final ErrorCode ORDER_COMPONENT_RESTORE_REPEAT = new ErrorCode(1_003_010_007, "order.component.restore.repeat");
public static final ErrorCode ORDER_COMPONENT_PROCESS_STATUS_INVALID = new ErrorCode(1_003_010_008, "order.component.process.status.invalid");
public static final ErrorCode ORDER_COMPONENT_PROCESSED_CAN_NOT_DELETE = new ErrorCode(1_003_010_009, "order.component.processed.can.not.delete");
public static final ErrorCode ORDER_ASSEMBLY_STATUS_UPDATE_CONCURRENCY_ERROR = new ErrorCode(1_003_010_010, "order.assembly.status.update.concurrency.error");
//=========== 分堆打包 1-003-011-000 ============ //=========== 分堆打包 1-003-011-000 ============
public static final ErrorCode PARTS_PACK_NUM_NOT_ENOUGH = new ErrorCode(1_003_010_000, "parts.pack.num.not.enough"); public static final ErrorCode PARTS_PACK_NUM_NOT_ENOUGH = new ErrorCode(1_003_010_000, "parts.pack.num.not.enough");
@@ -7,8 +7,8 @@ import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.module.executor.controller.admin.assemblylist.vo.AssemblyListComponentPageReqVO; import com.cf.imes.module.executor.controller.admin.assemblylist.vo.AssemblyListComponentPageReqVO;
import com.cf.imes.module.executor.controller.admin.assemblylist.vo.AssemblyListComponentFlatReqVO; import com.cf.imes.module.executor.controller.admin.assemblylist.vo.AssemblyListComponentFlatReqVO;
import com.cf.imes.module.executor.controller.admin.assemblylist.vo.AssemblyListComponentFlatRespVO; import com.cf.imes.module.executor.controller.admin.assemblylist.vo.AssemblyListComponentFlatRespVO;
import com.cf.imes.module.executor.controller.admin.assemblylist.vo.AssemblyOrderRespVO;
import com.cf.imes.module.executor.controller.admin.order.vo.order.OrderPageReqVO; import com.cf.imes.module.executor.controller.admin.order.vo.order.OrderPageReqVO;
import com.cf.imes.module.executor.controller.admin.order.vo.order.OrderRespVO;
import com.cf.imes.module.executor.controller.admin.order.vo.order.OrdersViewCadViewInfoRespVO; import com.cf.imes.module.executor.controller.admin.order.vo.order.OrdersViewCadViewInfoRespVO;
import com.cf.imes.module.executor.controller.admin.ordercomponent.vo.AssemblyComponentChildPrintReqVO; import com.cf.imes.module.executor.controller.admin.ordercomponent.vo.AssemblyComponentChildPrintReqVO;
import com.cf.imes.module.executor.controller.admin.ordercomponent.vo.AssemblyComponentChildPrintRespVO; import com.cf.imes.module.executor.controller.admin.ordercomponent.vo.AssemblyComponentChildPrintRespVO;
@@ -56,8 +56,9 @@ public class AssemblyListController {
@GetMapping("/order/page") @GetMapping("/order/page")
@Operation(summary = "获得装配清单订单分页") @Operation(summary = "获得装配清单订单分页")
@PreAuthorize("@ss.hasPermission('productManager:List')") @PreAuthorize("@ss.hasPermission('productManager:List')")
public CommonResult<PageResult<OrderRespVO>> getMainOrderPage(@Valid OrderPageReqVO pageReqVO) { public CommonResult<PageResult<AssemblyOrderRespVO>> getMainOrderPage(@Valid OrderPageReqVO pageReqVO) {
return success(BeanUtils.toBean(assemblyListService.getMainOrderPage(pageReqVO), OrderRespVO.class)); return success(BeanUtils.toBean(
assemblyListService.getMainOrderPage(pageReqVO), AssemblyOrderRespVO.class));
} }
@PostMapping("/list") @PostMapping("/list")
@@ -82,6 +82,13 @@ public class OrderDO extends BaseDO {
* 订单状态,0空订单1新单2已排单3生产中4生产完成 * 订单状态,0空订单1新单2已排单3生产中4生产完成
*/ */
private Integer status; private Integer status;
/**
* 订单装配加工状态:0不加工、1未加工、2已加工、3部分加工。
*
* <p>订单没有有效部件时保持为空。</p>
*/
private Integer assemblyProcessStatus;
/** /**
* 打包状态:0未打包1打包中2已打包 * 打包状态:0未打包1打包中2已打包
*/ */
@@ -5,9 +5,12 @@ import com.cf.imes.module.executor.controller.admin.ordercomponent.vo.OrderDetai
import com.cf.imes.module.executor.dal.dataobject.ordercomponent.OrderComponentDO; import com.cf.imes.module.executor.dal.dataobject.ordercomponent.OrderComponentDO;
import com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentPrintDataDO; import com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentPrintDataDO;
import com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentPrintQuantityDO; import com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentPrintQuantityDO;
import com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentProcessStatisticsDO;
import com.cf.imes.module.executor.enums.ordercomponent.OrderComponentProcessStatusEnum;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -19,6 +22,32 @@ import java.util.Set;
*/ */
@Mapper @Mapper
public interface OrderComponentMapper extends BaseMapperX<OrderComponentDO> { public interface OrderComponentMapper extends BaseMapperX<OrderComponentDO> {
/**
* 汇总订单下全部未删除部件的加工状态。
*
* @param organId 组织 ID,用于分片路由和数据隔离
* @param orderId 订单 ID
* @return 状态聚合结果
*/
ComponentProcessStatisticsDO selectProcessStatistics(@Param("organId") Long organId,
@Param("orderId") Long orderId);
/**
* 判断待删除子树中是否存在已加工部件。
*/
default boolean existsProcessedComponent(Long organId, Long orderId,
Collection<Long> componentIds) {
if (componentIds == null || componentIds.isEmpty()) {
return false;
}
return selectCount(new com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX<OrderComponentDO>()
.eq(OrderComponentDO::getOrganId, organId)
.eq(OrderComponentDO::getOrderId, orderId)
.in(OrderComponentDO::getId, componentIds)
.eq(OrderComponentDO::getDeleted, false)
.eq(OrderComponentDO::getProcessStatus,
OrderComponentProcessStatusEnum.PROCESSED.getStatus())) > 0;
}
/** /**
* 查询部件标签打印数据,包括当前部件、直接父级、层级及同树同名数量。 * 查询部件标签打印数据,包括当前部件、直接父级、层级及同树同名数量。
* *
@@ -44,6 +44,7 @@ import com.cf.imes.module.system.api.assemblyconfig.dto.AssemblyConfigRespDTO;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@@ -90,6 +91,9 @@ public class AssemblyListServiceImpl implements AssemblyListService {
@Resource @Resource
private OrderItemMapper orderItemMapper; private OrderItemMapper orderItemMapper;
@Resource
private OrderAssemblyProcessStatusService orderAssemblyProcessStatusService;
@Override @Override
public PageResult<OrderDO> getMainOrderPage(OrderPageReqVO pageReqVO) { public PageResult<OrderDO> getMainOrderPage(OrderPageReqVO pageReqVO) {
pageReqVO.setAssembly(true); pageReqVO.setAssembly(true);
@@ -771,12 +775,14 @@ public class AssemblyListServiceImpl implements AssemblyListService {
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void processComponent(Long id) { public void processComponent(Long id) {
// 未加工 -> 加工 // 未加工 -> 加工
handleComponentProcessStatus(id, OrderComponentProcessStatusEnum.UNPROCESSED.getStatus(), OrderComponentProcessStatusEnum.PROCESSED.getStatus()); handleComponentProcessStatus(id, OrderComponentProcessStatusEnum.UNPROCESSED.getStatus(), OrderComponentProcessStatusEnum.PROCESSED.getStatus());
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void restoreComponent(Long id) { public void restoreComponent(Long id) {
// 加工 -> 未加工 // 加工 -> 未加工
handleComponentProcessStatus(id, OrderComponentProcessStatusEnum.PROCESSED.getStatus(), OrderComponentProcessStatusEnum.UNPROCESSED.getStatus()); handleComponentProcessStatus(id, OrderComponentProcessStatusEnum.PROCESSED.getStatus(), OrderComponentProcessStatusEnum.UNPROCESSED.getStatus());
@@ -894,81 +900,13 @@ public class AssemblyListServiceImpl implements AssemblyListService {
return; return;
} }
// ========================= // 完整沿用原有规则重新计算顶级部件状态,不改变顶级节点判定口径。
// 5、重新查询整棵树(拿最新状态) orderAssemblyProcessStatusService.refreshTopComponentProcessStatus(
// ========================= organId, orderId, topId);
List<OrderComponentDO> topComponentList =
orderComponentMapper.selectList(
new LambdaQueryWrapperX<OrderComponentDO>()
.eq(OrderComponentDO::getTopId, topId)
.eq(OrderComponentDO::getDeleted,
DeletedCodeEnum.NOT_DELETED.getStatus())
.select(
OrderComponentDO::getId,
OrderComponentDO::getTopId,
OrderComponentDO::getLevel,
OrderComponentDO::getProcessStatus
)
);
if (CollUtil.isEmpty(topComponentList)) { // 订单状态是新增的独立聚合结果,按订单下全部未删除部件重新计算。
return; orderAssemblyProcessStatusService.refreshOrderAssemblyProcessStatus(
} organId, orderId);
// =========================
// 6、统计:
// 是否存在已加工子级
// =========================
boolean hasProcessedChild =
topComponentList.stream()
// 排除顶级自己
.filter(item ->
!Objects.equals(
item.getId(),
topId
))
.anyMatch(item ->
Objects.equals(
item.getProcessStatus(),
OrderComponentProcessStatusEnum.PROCESSED.getStatus()
));
// =========================
// 7、计算顶级 extra 状态
// =========================
Integer extraProcessStatus = null;
// 顶级自身未加工
boolean topUnProcessed =
Objects.equals(
topComponentDO.getProcessStatus(),
OrderComponentProcessStatusEnum.UNPROCESSED.getStatus()
);
// 只要:
// 顶级未加工
// 且存在已加工子级
// => 部分加工
if (topUnProcessed && hasProcessedChild) {
extraProcessStatus =
OrderComponentProcessStatusEnum.PART_PROCESSED.getStatus();
}
// =========================
// 8、更新顶级 extra 状态
// =========================
orderComponentMapper.update(
null,
new LambdaUpdateWrapperX<OrderComponentDO>()
.eq(OrderComponentDO::getId, topId)
.eq(OrderComponentDO::getOrderId, orderId)
.eq(OrderComponentDO::getOrganId, organId)
.eq(OrderComponentDO::getDeleted,
DeletedCodeEnum.NOT_DELETED.getStatus())
.set(OrderComponentDO::getExtraProcessStatus,
extraProcessStatus)
);
} }
@@ -0,0 +1,24 @@
package com.cf.imes.module.executor.service.assemblylist;
/**
* 订单装配加工状态服务。
*
* <p>顶级部件状态继续沿用原规则;订单状态在此基础上独立聚合。</p>
*/
public interface OrderAssemblyProcessStatusService {
/**
* 按现有顶级部件规则重新计算 extraProcessStatus。
*/
void refreshTopComponentProcessStatus(Long organId, Long orderId, Long topId);
/**
* 计算订单下全部未删除部件对应的订单装配加工状态。
*/
Integer calculateOrderAssemblyProcessStatus(Long organId, Long orderId);
/**
* 计算并通过 orders.version 乐观锁更新订单装配加工状态。
*/
void refreshOrderAssemblyProcessStatus(Long organId, Long orderId);
}
@@ -0,0 +1,141 @@
package com.cf.imes.module.executor.service.assemblylist;
import cn.hutool.core.collection.CollUtil;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.mybatis.core.query.LambdaUpdateWrapperX;
import com.cf.imes.module.executor.dal.dataobject.order.OrderDO;
import com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentProcessStatisticsDO;
import com.cf.imes.module.executor.dal.dataobject.ordercomponent.OrderComponentDO;
import com.cf.imes.module.executor.dal.mysql.order.OrderMapper;
import com.cf.imes.module.executor.dal.mysql.ordercomponent.OrderComponentMapper;
import com.cf.imes.module.executor.enums.ordercomponent.OrderComponentProcessStatusEnum;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_ASSEMBLY_STATUS_UPDATE_CONCURRENCY_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_PROCESS_STATUS_INVALID;
/**
* 订单装配加工状态服务实现。
*/
@Service
public class OrderAssemblyProcessStatusServiceImpl implements OrderAssemblyProcessStatusService {
@Resource
private OrderComponentMapper orderComponentMapper;
@Resource
private OrderMapper orderMapper;
@Override
public void refreshTopComponentProcessStatus(Long organId, Long orderId, Long topId) {
OrderComponentDO topComponent = orderComponentMapper.selectOne(
new LambdaQueryWrapperX<OrderComponentDO>()
.eq(OrderComponentDO::getId, topId)
.eq(OrderComponentDO::getOrderId, orderId)
.eq(OrderComponentDO::getOrganId, organId)
.eq(OrderComponentDO::getDeleted, false));
// 删除整个顶级部件时,该顶级节点已不再参与任何状态计算。
if (topComponent == null) {
return;
}
List<OrderComponentDO> topComponentList = orderComponentMapper.selectList(
new LambdaQueryWrapperX<OrderComponentDO>()
.eq(OrderComponentDO::getTopId, topId)
.eq(OrderComponentDO::getOrderId, orderId)
.eq(OrderComponentDO::getOrganId, organId)
.eq(OrderComponentDO::getDeleted, false)
.select(OrderComponentDO::getId,
OrderComponentDO::getProcessStatus));
if (CollUtil.isEmpty(topComponentList)) {
return;
}
/*
* 以下判断完整沿用原有顶级部件规则:
* 顶级自身未加工,并且任一子级已加工时,顶级才标记为部分加工;
* 其他情况清空附加状态,不新增或改变顶级部件业务规则。
*/
boolean hasProcessedChild = topComponentList.stream()
.filter(item -> !Objects.equals(item.getId(), topId))
.anyMatch(item -> Objects.equals(item.getProcessStatus(),
OrderComponentProcessStatusEnum.PROCESSED.getStatus()));
boolean topUnprocessed = Objects.equals(topComponent.getProcessStatus(),
OrderComponentProcessStatusEnum.UNPROCESSED.getStatus());
Integer extraProcessStatus = topUnprocessed && hasProcessedChild
? OrderComponentProcessStatusEnum.PART_PROCESSED.getStatus()
: null;
orderComponentMapper.update(null,
new LambdaUpdateWrapperX<OrderComponentDO>()
.eq(OrderComponentDO::getId, topId)
.eq(OrderComponentDO::getOrderId, orderId)
.eq(OrderComponentDO::getOrganId, organId)
.eq(OrderComponentDO::getDeleted, false)
.set(OrderComponentDO::getExtraProcessStatus, extraProcessStatus));
}
@Override
public Integer calculateOrderAssemblyProcessStatus(Long organId, Long orderId) {
ComponentProcessStatisticsDO stats =
orderComponentMapper.selectProcessStatistics(organId, orderId);
if (stats == null || value(stats.getTotalCount()) == 0) {
return null;
}
// 状态字段出现空值或非法值时终止事务,不能静默归类为未加工。
if (value(stats.getInvalidProcessStatusCount()) > 0
|| value(stats.getInvalidExtraProcessStatusCount()) > 0) {
throw new ServiceException(ORDER_COMPONENT_PROCESS_STATUS_INVALID);
}
// 部分加工只由顶级部件产生;任一顶级为部分加工,订单即为部分加工。
if (value(stats.getPartProcessedCount()) > 0) {
return OrderComponentProcessStatusEnum.PART_PROCESSED.getStatus();
}
// 不加工只排除节点自身,不排除该节点下仍需加工的子部件。
long processableCount = value(stats.getTotalCount())
- value(stats.getNotProcessCount());
if (processableCount == 0) {
return OrderComponentProcessStatusEnum.NOT_PROCESS.getStatus();
}
if (value(stats.getProcessedCount()) == 0) {
return OrderComponentProcessStatusEnum.UNPROCESSED.getStatus();
}
if (value(stats.getUnprocessedCount()) == 0) {
return OrderComponentProcessStatusEnum.PROCESSED.getStatus();
}
return OrderComponentProcessStatusEnum.PART_PROCESSED.getStatus();
}
@Override
public void refreshOrderAssemblyProcessStatus(Long organId, Long orderId) {
OrderDO order = orderMapper.selectOrderOne(orderId, organId);
if (order == null) {
return;
}
Integer status = calculateOrderAssemblyProcessStatus(organId, orderId);
int version = order.getVersion();
int updated = orderMapper.update(null,
new LambdaUpdateWrapperX<OrderDO>()
.eq(OrderDO::getId, orderId)
.eq(OrderDO::getOrganId, organId)
.eq(OrderDO::getVersion, version)
.set(OrderDO::getAssemblyProcessStatus, status)
.set(OrderDO::getVersion, version + 1));
if (updated != 1) {
throw new ServiceException(ORDER_ASSEMBLY_STATUS_UPDATE_CONCURRENCY_ERROR, orderId);
}
}
private long value(Long count) {
return count == null ? 0L : count;
}
}
@@ -20,6 +20,14 @@ public interface OrderInfoVersionControlService {
*/ */
void updateOrderPlateNumAndArea(OrderDO orderDO, Boolean delete); void updateOrderPlateNumAndArea(OrderDO orderDO, Boolean delete);
/**
* 在同一次订单乐观锁更新中写入板件统计和装配加工状态。
*
* <p>部件删除、恢复会同时影响两类数据,合并更新可避免使用已经过期的 version。</p>
*/
void updateOrderPlateNumAreaAndAssemblyStatus(OrderDO orderDO, Boolean delete,
Integer assemblyProcessStatus);
/** /**
* 更新订单柜体板件数量 * 更新订单柜体板件数量
* *
@@ -47,6 +47,18 @@ public class OrderInfoVersionControlServiceImpl implements OrderInfoVersionContr
@Override @Override
public void updateOrderPlateNumAndArea(OrderDO orderDO, Boolean delete) { public void updateOrderPlateNumAndArea(OrderDO orderDO, Boolean delete) {
updateOrderPlateNumAndArea(orderDO, delete, null, false);
}
@Override
public void updateOrderPlateNumAreaAndAssemblyStatus(OrderDO orderDO, Boolean delete,
Integer assemblyProcessStatus) {
updateOrderPlateNumAndArea(orderDO, delete, assemblyProcessStatus, true);
}
private void updateOrderPlateNumAndArea(OrderDO orderDO, Boolean delete,
Integer assemblyProcessStatus,
boolean updateAssemblyProcessStatus) {
Long orderId = orderDO.getId(); Long orderId = orderDO.getId();
// 重新计算订单数量 // 重新计算订单数量
// 统计每个orders下的板件数量和面积,更新到orders中 // 统计每个orders下的板件数量和面积,更新到orders中
@@ -63,6 +75,10 @@ public class OrderInfoVersionControlServiceImpl implements OrderInfoVersionContr
.set(OrderDO::getArea, orderPlateNumAndAreaDO.getArea()) .set(OrderDO::getArea, orderPlateNumAndAreaDO.getArea())
.set(OrderDO::getPlateNum, num) .set(OrderDO::getPlateNum, num)
.set(OrderDO::getVersion, version + 1); .set(OrderDO::getVersion, version + 1);
if (updateAssemblyProcessStatus) {
// 删除或恢复部件后,订单装配状态与板件统计使用同一个 version 提交。
updateWrapper.set(OrderDO::getAssemblyProcessStatus, assemblyProcessStatus);
}
int update = orderMapper.update(updateWrapper); int update = orderMapper.update(updateWrapper);
if (update == 0) { if (update == 0) {
throw new ServiceException(com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_PLATE_UPDATE_CONCURRENCY_ERROR, orderId); throw new ServiceException(com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_PLATE_UPDATE_CONCURRENCY_ERROR, orderId);
@@ -52,6 +52,7 @@ import com.cf.imes.module.executor.dal.mysql.orderItem.OrderItemTempMapper;
import com.cf.imes.module.executor.dal.mysql.ordercomponent.OrderComponentMapper; import com.cf.imes.module.executor.dal.mysql.ordercomponent.OrderComponentMapper;
import com.cf.imes.module.executor.dal.mysql.plate.PlateMapper; import com.cf.imes.module.executor.dal.mysql.plate.PlateMapper;
import com.cf.imes.module.executor.service.manage.pack.PackService; import com.cf.imes.module.executor.service.manage.pack.PackService;
import com.cf.imes.module.executor.service.assemblylist.OrderAssemblyProcessStatusService;
import com.cf.imes.module.executor.service.order.OrderInfoVersionControlService; import com.cf.imes.module.executor.service.order.OrderInfoVersionControlService;
import com.cf.imes.module.executor.service.order.OrderService; import com.cf.imes.module.executor.service.order.OrderService;
import com.cf.imes.module.infra.api.file.FileApi; import com.cf.imes.module.infra.api.file.FileApi;
@@ -82,6 +83,7 @@ import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_BODY_NO
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_DELETE_PLATE_IN_PLAN; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_DELETE_PLATE_IN_PLAN;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_NOT_EXIST; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_NOT_EXIST;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_NO_NEED_TO_RESTORE; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_NO_NEED_TO_RESTORE;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_PROCESSED_CAN_NOT_DELETE;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_RESTORE_PARENT_IS_DELETED; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_RESTORE_PARENT_IS_DELETED;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_NOT_EXISTS; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_NOT_EXISTS;
@@ -129,6 +131,9 @@ public class OrderComponentServiceImpl implements OrderComponentService {
@Resource @Resource
private OrderInfoVersionControlService orderInfoVersionControlService; private OrderInfoVersionControlService orderInfoVersionControlService;
@Resource
private OrderAssemblyProcessStatusService orderAssemblyProcessStatusService;
@Resource @Resource
private PackService packService; private PackService packService;
@@ -600,6 +605,14 @@ public class OrderComponentServiceImpl implements OrderComponentService {
// 整理出所属的下级,整理成id集合 // 整理出所属的下级,整理成id集合
Set<Long> deleteIds = getChildrenIds(componentDO, componentOnTop); Set<Long> deleteIds = getChildrenIds(componentDO, componentOnTop);
/*
* 删除会作用于目标节点的整棵子树。只要其中任一有效部件已经加工,
* 就必须拒绝整个删除操作,避免删除已经产生加工进度的业务数据。
*/
if (orderComponentMapper.existsProcessedComponent(organId, orderId, deleteIds)) {
throw new ServiceException(ORDER_COMPONENT_PROCESSED_CAN_NOT_DELETE);
}
// 板件id Set // 板件id Set
Set<Long> plateIdSet = new HashSet<>(); Set<Long> plateIdSet = new HashSet<>();
// 需要重新累计数量的order_goods id // 需要重新累计数量的order_goods id
@@ -668,8 +681,15 @@ public class OrderComponentServiceImpl implements OrderComponentService {
if (CollUtil.isNotEmpty(deleteGoodsIds)) { if (CollUtil.isNotEmpty(deleteGoodsIds)) {
orderInfoVersionControlService.updateOrderGoodsPlateNumAndArea(deleteGoodsIds, true); orderInfoVersionControlService.updateOrderGoodsPlateNumAndArea(deleteGoodsIds, true);
} }
// 更新订单的板件数量和面积 // 子树删除后先沿用原规则刷新仍然存在的顶级部件,再聚合订单状态。
orderInfoVersionControlService.updateOrderPlateNumAndArea(orderDO, true); orderAssemblyProcessStatusService.refreshTopComponentProcessStatus(
organId, orderId, componentDO.getTopId());
Integer assemblyProcessStatus = orderAssemblyProcessStatusService
.calculateOrderAssemblyProcessStatus(organId, orderId);
// 板件统计和订单装配状态在同一次 version 乐观锁更新中提交。
orderInfoVersionControlService.updateOrderPlateNumAreaAndAssemblyStatus(
orderDO, true, assemblyProcessStatus);
} }
/** /**
@@ -825,8 +845,15 @@ public class OrderComponentServiceImpl implements OrderComponentService {
if (CollUtil.isNotEmpty(restoreGoodsIds)) { if (CollUtil.isNotEmpty(restoreGoodsIds)) {
orderInfoVersionControlService.updateOrderGoodsPlateNumAndArea(restoreGoodsIds, false); orderInfoVersionControlService.updateOrderGoodsPlateNumAndArea(restoreGoodsIds, false);
} }
// 更新订单 // 子树恢复后先沿用原规则刷新顶级部件,再聚合订单状态。
orderInfoVersionControlService.updateOrderPlateNumAndArea(orderDO, false); orderAssemblyProcessStatusService.refreshTopComponentProcessStatus(
organId, orderId, componentDO.getTopId());
Integer assemblyProcessStatus = orderAssemblyProcessStatusService
.calculateOrderAssemblyProcessStatus(organId, orderId);
// 板件统计和订单装配状态在同一次 version 乐观锁更新中提交。
orderInfoVersionControlService.updateOrderPlateNumAreaAndAssemblyStatus(
orderDO, false, assemblyProcessStatus);
} }
@@ -34,7 +34,19 @@
t.root_id t.root_id
FROM orders t FROM orders t
<if test="reqVO.assembly or reqVO.processStatus != null"> <!--
装配清单沿用原关联方式,只用于保证订单存在有效顶级部件;
加工状态不再通过 order_component 过滤。
-->
<if test="reqVO.assembly">
JOIN order_component assembly_oc
ON assembly_oc.organ_id = t.organ_id
AND assembly_oc.order_id = t.id
AND assembly_oc.deleted = 0
AND assembly_oc.pid = 0
</if>
<!-- 非装配接口继续保留原部件状态关联逻辑。 -->
<if test="!reqVO.assembly and reqVO.processStatus != null">
JOIN order_component oc JOIN order_component oc
ON oc.organ_id = t.organ_id ON oc.organ_id = t.organ_id
AND oc.order_id = t.id AND oc.order_id = t.id
@@ -60,6 +72,11 @@
</if> </if>
where 1=1 where 1=1
<!-- 装配加工状态直接过滤订单冗余字段,不再依赖部件状态计算。 -->
<if test="reqVO.assembly and reqVO.processStatus != null">
AND t.assembly_process_status = #{reqVO.processStatus}
</if>
<if test="reqVO.id != null and reqVO.id != ''"> <if test="reqVO.id != null and reqVO.id != ''">
AND CAST(t.id AS CHAR) LIKE CONCAT('%', #{reqVO.id}, '%') AND CAST(t.id AS CHAR) LIKE CONCAT('%', #{reqVO.id}, '%')
</if> </if>
@@ -174,7 +191,16 @@
SELECT DISTINCT SELECT DISTINCT
t.id t.id
FROM orders t FROM orders t
<if test="reqVO.assembly or reqVO.processStatus != null"> <!-- 子订单同样只用原关联方式保证存在有效顶级部件。 -->
<if test="reqVO.assembly">
JOIN order_component assembly_oc
ON assembly_oc.organ_id = t.organ_id
AND assembly_oc.order_id = t.id
AND assembly_oc.deleted = 0
AND assembly_oc.pid = 0
</if>
<!-- 非装配接口继续保留原部件状态关联逻辑。 -->
<if test="!reqVO.assembly and reqVO.processStatus != null">
JOIN order_component oc JOIN order_component oc
ON oc.organ_id = t.organ_id ON oc.organ_id = t.organ_id
AND oc.order_id = t.id AND oc.order_id = t.id
@@ -208,6 +234,11 @@
#{parentId} #{parentId}
</foreach> </foreach>
<!-- 子订单同样按自身的订单装配状态过滤。 -->
<if test="reqVO.assembly and reqVO.processStatus != null">
AND t.assembly_process_status = #{reqVO.processStatus}
</if>
<if test="reqVO.id != null and reqVO.id != ''"> <if test="reqVO.id != null and reqVO.id != ''">
AND CAST(t.id AS CHAR) LIKE CONCAT('%', #{reqVO.id}, '%') AND CAST(t.id AS CHAR) LIKE CONCAT('%', #{reqVO.id}, '%')
</if> </if>
@@ -1,6 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?> <?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" > <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.cf.imes.module.executor.dal.mysql.ordercomponent.OrderComponentMapper"> <mapper namespace="com.cf.imes.module.executor.dal.mysql.ordercomponent.OrderComponentMapper">
<!--
订单装配状态使用单行聚合结果,避免部件状态变化时加载整棵树。
extra_process_status = 0 只排除当前节点,不会排除该节点的子级。
-->
<select id="selectProcessStatistics"
resultType="com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentProcessStatisticsDO">
SELECT COUNT(*) AS totalCount,
COALESCE(SUM(CASE
WHEN process_status IS NULL OR process_status NOT IN (1, 2)
THEN 1 ELSE 0 END), 0) AS invalidProcessStatusCount,
COALESCE(SUM(CASE
WHEN extra_process_status IS NOT NULL
AND extra_process_status NOT IN (0, 3)
THEN 1 ELSE 0 END), 0) AS invalidExtraProcessStatusCount,
COALESCE(SUM(CASE WHEN extra_process_status = 0 THEN 1 ELSE 0 END), 0)
AS notProcessCount,
COALESCE(SUM(CASE WHEN extra_process_status = 3 THEN 1 ELSE 0 END), 0)
AS partProcessedCount,
COALESCE(SUM(CASE
WHEN extra_process_status IS NULL AND process_status = 1
THEN 1 ELSE 0 END), 0) AS unprocessedCount,
COALESCE(SUM(CASE
WHEN extra_process_status IS NULL AND process_status = 2
THEN 1 ELSE 0 END), 0) AS processedCount
FROM order_component
WHERE organ_id = #{organId}
AND order_id = #{orderId}
AND deleted = 0
</select>
<select id="selectComponentPrintData" <select id="selectComponentPrintData"
resultType="com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentPrintDataDO"> resultType="com.cf.imes.module.executor.dal.dataobject.ordercomponent.ComponentPrintDataDO">
SELECT oc.order_id AS orderId, SELECT oc.order_id AS orderId,
@@ -150,6 +150,9 @@ order.component.processed.repeat=該部件已加工勿重複操作,請重繪
order.component.assembly.config.query.error=裝配設定査詢失敗,請檢查服務狀態 order.component.assembly.config.query.error=裝配設定査詢失敗,請檢查服務狀態
order.component.not.process=當前部件無需加工,請重繪清單後檢查狀態 order.component.not.process=當前部件無需加工,請重繪清單後檢查狀態
order.component.restore.repeat=該部件已恢復勿重複操作,請重繪清單後檢查狀態 order.component.restore.repeat=該部件已恢復勿重複操作,請重繪清單後檢查狀態
order.component.process.status.invalid=部件加工狀態異常,請檢查資料後重試
order.component.processed.can.not.delete=已加工部件不能刪除,請先恢復為未加工狀態
order.assembly.status.update.concurrency.error=訂單裝配狀態已發生變化,請重新整理後重試,訂單號:{0}
heap.split.task.not.exist=分堆任務不存在 heap.split.task.not.exist=分堆任務不存在
heap.split.task.name.exist=分堆任務名已存在,請重新命名 heap.split.task.name.exist=分堆任務名已存在,請重新命名
@@ -150,6 +150,9 @@ order.component.processed.repeat=This component has been processed and should no
order.component.assembly.config.query.error=Assembly settings query failed, please check service status order.component.assembly.config.query.error=Assembly settings query failed, please check service status
order.component.not.process=The current component does not require processing. Please refresh the list and check the status order.component.not.process=The current component does not require processing. Please refresh the list and check the status
order.component.restore.repeat=The component has been restored. Do not repeat the operation. Please refresh the list and check the status order.component.restore.repeat=The component has been restored. Do not repeat the operation. Please refresh the list and check the status
order.component.process.status.invalid=The component processing status is invalid. Check the data and try again
order.component.processed.can.not.delete=Processed components cannot be deleted. Restore them to unprocessed first
order.assembly.status.update.concurrency.error=The order assembly status has changed. Refresh and try again. Order ID: {0}
heap.split.task.not.exist=The heap split task does not exist heap.split.task.not.exist=The heap split task does not exist
heap.split.task.name.exist=The task name for heap splitting already exists, please rename it heap.split.task.name.exist=The task name for heap splitting already exists, please rename it
@@ -150,6 +150,9 @@ order.component.processed.repeat=该部件已加工勿重复操作,请刷新
order.component.assembly.config.query.error=装配设置查询失败,请检查服务状态 order.component.assembly.config.query.error=装配设置查询失败,请检查服务状态
order.component.not.process=当前部件无需加工,请刷新列表后检查状态 order.component.not.process=当前部件无需加工,请刷新列表后检查状态
order.component.restore.repeat=该部件已恢复勿重复操作,请刷新列表后检查状态 order.component.restore.repeat=该部件已恢复勿重复操作,请刷新列表后检查状态
order.component.process.status.invalid=部件加工状态异常,请检查数据后重试
order.component.processed.can.not.delete=已加工部件不能删除,请先恢复为未加工状态
order.assembly.status.update.concurrency.error=订单装配状态已发生变化,请刷新后重试,订单号:{0}
heap.split.task.not.exist=分堆任务不存在 heap.split.task.not.exist=分堆任务不存在
heap.split.task.name.exist=分堆任务名已存在,请重新命名 heap.split.task.name.exist=分堆任务名已存在,请重新命名
@@ -93,6 +93,13 @@ public class OrderDO extends BaseDO {
* 订单状态,0空订单1新单2已排单3生产中4生产完成 * 订单状态,0空订单1新单2已排单3生产中4生产完成
*/ */
private Integer status; private Integer status;
/**
* 订单装配加工状态:0不加工、1未加工、2已加工、3部分加工。
*
* <p>订单没有有效部件时保持为空。</p>
*/
private Integer assemblyProcessStatus;
/** /**
* 打包状态:0未打包1打包中2已打包 * 打包状态:0未打包1打包中2已打包
*/ */
@@ -17,7 +17,8 @@ import java.util.Arrays;
public enum OrderComponentProcessStatusEnum implements IntArrayValuable { public enum OrderComponentProcessStatusEnum implements IntArrayValuable {
NOT_PROCESS(0), NOT_PROCESS(0),
UNPROCESSED(1), UNPROCESSED(1),
PROCESSED(2); PROCESSED(2),
PART_PROCESSED(3);
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(OrderComponentProcessStatusEnum::getStatus).toArray(); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(OrderComponentProcessStatusEnum::getStatus).toArray();
@@ -88,6 +88,7 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_ASSEMBLY_CONFIG_QUERY_ERROR; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_ASSEMBLY_CONFIG_QUERY_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_PROCESS_STATUS_INVALID;
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_GET_ORG_SEALEDGE_ERROR; import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_GET_ORG_SEALEDGE_ERROR;
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORDER_PLATENUM_CHECK_ERROR; import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORDER_PLATENUM_CHECK_ERROR;
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORDER_PLATENUM_REACH_THRESHOLD_ERROR; import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORDER_PLATENUM_REACH_THRESHOLD_ERROR;
@@ -3119,23 +3120,87 @@ public class ProducingOrderImportFactory {
} }
/** /**
* 更新订单板件总数 * 更新订单板件统计和装配加工状态。
*
* <p>组件生产单允许向已有订单重复导入,因此装配状态必须查询数据库中的
* 全部未删除部件,不能只计算本次导入缓存中的部件。</p>
*/ */
private void updateOrderPlateNum() { private void updateOrderPlateNum() {
int version = orderDO.getVersion(); int version = orderDO.getVersion();
Long orderId = orderDO.getId(); Long orderId = orderDO.getId();
Integer assemblyProcessStatus = calculateOrderAssemblyProcessStatus();
int updated = orderMapper.update(new LambdaUpdateWrapper<OrderDO>() int updated = orderMapper.update(new LambdaUpdateWrapper<OrderDO>()
.eq(OrderDO::getId, orderId) .eq(OrderDO::getId, orderId)
.eq(OrderDO::getOrganId, organId) .eq(OrderDO::getOrganId, organId)
.eq(OrderDO::getVersion, version) .eq(OrderDO::getVersion, version)
.set(OrderDO::getPlateNum, currentPlateNum) .set(OrderDO::getPlateNum, currentPlateNum)
.set(OrderDO::getArea, currentPlateArea) .set(OrderDO::getArea, currentPlateArea)
.set(OrderDO::getAssemblyProcessStatus, assemblyProcessStatus)
.set(OrderDO::getVersion, version + 1)); .set(OrderDO::getVersion, version + 1));
if (updated == 0) { if (updated == 0) {
throw new ServiceException(ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORDER_UPDATE_CONCURRENCY_ERROR, orderId); throw new ServiceException(ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORDER_UPDATE_CONCURRENCY_ERROR, orderId);
} }
} }
/**
* 根据订单下全部未删除部件计算订单装配加工状态。
*
* <p>不加工状态只排除节点自身,不排除该节点的子树;出现空值或非法状态时
* 直接终止导入事务,避免将未知数据归类为未加工。</p>
*/
private Integer calculateOrderAssemblyProcessStatus() {
String sql = """
SELECT COUNT(*),
COALESCE(SUM(CASE
WHEN process_status IS NULL OR process_status NOT IN (1, 2)
THEN 1 ELSE 0 END), 0),
COALESCE(SUM(CASE
WHEN extra_process_status IS NOT NULL
AND extra_process_status NOT IN (0, 3)
THEN 1 ELSE 0 END), 0),
COALESCE(SUM(CASE WHEN extra_process_status = 0 THEN 1 ELSE 0 END), 0),
COALESCE(SUM(CASE WHEN extra_process_status = 3 THEN 1 ELSE 0 END), 0),
COALESCE(SUM(CASE
WHEN extra_process_status IS NULL AND process_status = 1
THEN 1 ELSE 0 END), 0),
COALESCE(SUM(CASE
WHEN extra_process_status IS NULL AND process_status = 2
THEN 1 ELSE 0 END), 0)
FROM order_component
WHERE organ_id = ?
AND order_id = ?
AND deleted = 0
""";
long[] stats = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> new long[]{
rs.getLong(1), rs.getLong(2), rs.getLong(3), rs.getLong(4),
rs.getLong(5), rs.getLong(6), rs.getLong(7)
}, organId, orderId);
if (stats == null || stats[0] == 0) {
return null;
}
if (stats[1] > 0 || stats[2] > 0) {
throw new ServiceException(ORDER_COMPONENT_PROCESS_STATUS_INVALID);
}
// 任一顶级部件为部分加工,订单直接为部分加工。
if (stats[4] > 0) {
return OrderComponentProcessStatusEnum.PART_PROCESSED.getStatus();
}
long processableCount = stats[0] - stats[3];
if (processableCount == 0) {
return OrderComponentProcessStatusEnum.NOT_PROCESS.getStatus();
}
if (stats[6] == 0) {
return OrderComponentProcessStatusEnum.UNPROCESSED.getStatus();
}
if (stats[5] == 0) {
return OrderComponentProcessStatusEnum.PROCESSED.getStatus();
}
return OrderComponentProcessStatusEnum.PART_PROCESSED.getStatus();
}
/** /**
* 新增order_goods/更新已有order_goods的板件总数 * 新增order_goods/更新已有order_goods的板件总数
*/ */