diff --git a/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/ErrorCodeConstants.java b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/ErrorCodeConstants.java index b0a5a65ba..92e0fa8b8 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/ErrorCodeConstants.java +++ b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/ErrorCodeConstants.java @@ -231,6 +231,11 @@ public class ErrorCodeConstants { public static final ErrorCode VENEER_OPTIMIZE_GOODS_EMPTY = new ErrorCode(1_003_009_062, "veneer.optimize.goods.empty"); public static final ErrorCode VENEER_OPTIMIZE_ORDERS_EMPTY = new ErrorCode(1_003_009_063, "veneer.optimize.orders.empty"); public static final ErrorCode VENEER_OPTIMIZE_PLATE_MODEL_EMPTY = new ErrorCode(1_003_009_064, "veneer.optimize.plate.model.empty"); + public static final ErrorCode VENEER_OPTIMIZE_RESULT_REQUIRED = new ErrorCode(1_003_009_065, "veneer.optimize.result.required"); + public static final ErrorCode VENEER_OPTIMIZE_PROCESS_STATUS_INVALID = new ErrorCode(1_003_009_066, "veneer.optimize.process.status.invalid"); + public static final ErrorCode VENEER_OPTIMIZE_VENEER_NO_REQUIRED = new ErrorCode(1_003_009_067, "veneer.optimize.veneer.no.required"); + public static final ErrorCode VENEER_OPTIMIZE_MATERIAL_MULTIPLE = new ErrorCode(1_003_009_068, "veneer.optimize.material.multiple"); + public static final ErrorCode VENEER_OPTIMIZE_RESULT_STATUS_CHANGED = new ErrorCode(1_003_009_069, "veneer.optimize.result.status.changed"); //=========== 部件管理 1-003-010-000 ============ public static final ErrorCode ORDER_COMPONENT_NOT_EXIST = new ErrorCode(1_003_010_000, "order.component.not.exist"); diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerOptimizePlanController.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerOptimizePlanController.java index 00895b969..3666d0cc6 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerOptimizePlanController.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerOptimizePlanController.java @@ -3,6 +3,7 @@ package com.cf.imes.module.executor.controller.admin.veneer; import com.cf.imes.framework.common.pojo.CommonResult; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOptimizePlanLockReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOrderSource; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOptimizationResultsReqVO; import com.cf.imes.module.executor.service.veneer.VeneerOptimizePlanService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -79,4 +80,19 @@ public class VeneerOptimizePlanController { veneerOptimizePlanService.releasePlanLock(req); return success(true); } + + /** + * 保存组件回传的贴皮优化、开始开料或结束开料结果。 + * + * @param req 贴皮排单及组件流程结果 + * @return 是否保存成功 + */ + @PostMapping("/optimize") + @Operation(summary = "保存贴皮优化及开料结果") + @PreAuthorize("@ss.hasPermission('veneerPlan:optimize')") + public CommonResult saveVeneerOptimizationResults( + @Valid @RequestBody VeneerOptimizationResultsReqVO req) { + veneerOptimizePlanService.saveOptimizationResults(req); + return success(true); + } } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerOptimizationResultsReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerOptimizationResultsReqVO.java new file mode 100644 index 000000000..ce51ac36e --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerOptimizationResultsReqVO.java @@ -0,0 +1,24 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import com.cf.imes.module.executor.controller.admin.plan.component.FlowSessionSavedContent; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 贴皮优化及开料结果保存参数。 + */ +@Data +@Schema(description = "管理后台 - 贴皮优化及开料结果保存 Request VO") +public class VeneerOptimizationResultsReqVO { + + @Schema(description = "贴皮排单ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{veneer.plan.id.required}") + private Long planId; + + @Valid + @Schema(description = "组件优化流程保存结果", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{veneer.optimize.result.required}") + private FlowSessionSavedContent flowSessionSavedContent; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPageRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPageRespVO.java index ec9c77a99..9aaf6b579 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPageRespVO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPageRespVO.java @@ -48,6 +48,27 @@ public class VeneerPlanPageRespVO { @Schema(description = "贴皮总面积,单位:平方米") private BigDecimal veneerArea; + @Schema(description = "实际优化贴皮商品编号") + private String optimizeGoodsId; + + @Schema(description = "最近优化时间") + private LocalDateTime optimizeTime; + + @Schema(description = "已优化板件贴皮数量") + private Integer optimizedPlateNum; + + @Schema(description = "未优化板件贴皮数量") + private Integer unOptimizedPlateNum; + + @Schema(description = "已排贴皮库原料张数") + private Integer scheduledBoardNum; + + @Schema(description = "已开料贴皮库原料张数") + private Integer cuttedBoardNum; + + @Schema(description = "已切板件贴皮数量") + private Integer cuttedPlateNum; + @Schema(description = "创建时间") private LocalDateTime createTime; diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderVeneerPlanDO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderVeneerPlanDO.java index c4ec5c0f8..d70faf0c4 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderVeneerPlanDO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderVeneerPlanDO.java @@ -76,6 +76,24 @@ public class OrderVeneerPlanDO extends BaseDO { */ private Long sourceVeneerId; + /** 实际优化贴皮商品编号。 */ + private String optimizeGoodsId; + + /** 最近一次优化时间。 */ + private LocalDateTime optimizeTime; + + /** 已优化板件贴皮数量,A/B面分别计数。 */ + private Integer optimizedPlateNum; + + /** 已排贴皮库原料张数。 */ + private Integer scheduledBoardNum; + + /** 已开料贴皮库原料张数。 */ + private Integer cuttedBoardNum; + + /** 已切板件贴皮数量,A/B面分别计数。 */ + private Integer cuttedPlateNum; + /** * 排单关联的订单主键ID集合,使用 JSON 数组格式存储。 */ diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerOptimizePlanMapper.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerOptimizePlanMapper.java index 88b17a94e..b7ab7c577 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerOptimizePlanMapper.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerOptimizePlanMapper.java @@ -23,6 +23,14 @@ public interface VeneerOptimizePlanMapper extends BaseMapperX */ List selectGoodsList(@Param("planId") Long planId); + /** + * 查询排单涉及的全部订单ID。 + * + * @param planId 贴皮排单ID + * @return 订单ID列表 + */ + List selectOrderIds(@Param("planId") Long planId); + /** * 查询排单贴皮及来源板件完整数据。 * 同一贴皮可能因来源板件属于多个加工组返回多条记录,由 Service 层合并。 @@ -30,7 +38,8 @@ public interface VeneerOptimizePlanMapper extends BaseMapperX * @param planId 贴皮排单ID * @return 贴皮及来源板件原始列表 */ - List selectPlateList(@Param("planId") Long planId); + List selectPlateList(@Param("planId") Long planId, + @Param("optimized") boolean optimized); /** * 查询排单内已标记待删除的贴皮编号。 @@ -39,4 +48,35 @@ public interface VeneerOptimizePlanMapper extends BaseMapperX * @return 待删除贴皮编号列表 */ List selectPendingDeleteVeneerNos(@Param("planId") Long planId); + + /** 重置当前排单中非待删除贴皮的已优化状态。 */ + int resetOptimizedVeneerStatus(@Param("planId") Long planId, + @Param("optimizedStatus") Integer optimizedStatus, + @Param("notOptimizedStatus") Integer notOptimizedStatus); + + /** 按贴皮短号批量更新加工状态。 */ + int updateVeneerProcessStatus(@Param("planId") Long planId, + @Param("veneerNos") List veneerNos, + @Param("sourceStatuses") List sourceStatuses, + @Param("targetStatus") Integer targetStatus); + + /** 保存优化排版统计并递增排单版本。 */ + int updatePlanOptimized(@Param("planId") Long planId, + @Param("version") Integer version, + @Param("status") Integer status, + @Param("optimizeGoodsId") String optimizeGoodsId, + @Param("optimizedPlateNum") Integer optimizedPlateNum, + @Param("scheduledBoardNum") Integer scheduledBoardNum); + + /** 更新排单为开料中并递增版本。 */ + int updatePlanCutting(@Param("planId") Long planId, + @Param("version") Integer version, + @Param("status") Integer status); + + /** 更新排单开料完成统计并递增版本。 */ + int updatePlanCut(@Param("planId") Long planId, + @Param("version") Integer version, + @Param("status") Integer status, + @Param("cuttedBoardNum") Integer cuttedBoardNum, + @Param("cuttedPlateNum") Integer cuttedPlateNum); } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerOptimizePlanService.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerOptimizePlanService.java index 83af83143..35b1416e9 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerOptimizePlanService.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerOptimizePlanService.java @@ -2,6 +2,7 @@ package com.cf.imes.module.executor.service.veneer; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOptimizePlanLockReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOrderSource; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOptimizationResultsReqVO; /** * 贴皮排单优化 Service。 @@ -30,4 +31,11 @@ public interface VeneerOptimizePlanService { * @param req 排单ID及锁持有者标识 */ void releasePlanLock(VeneerOptimizePlanLockReqVO req); + + /** + * 保存组件回传的贴皮优化及开料结果。 + * + * @param req 贴皮排单及组件流程结果 + */ + void saveOptimizationResults(VeneerOptimizationResultsReqVO req); } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerOptimizePlanServiceImpl.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerOptimizePlanServiceImpl.java index 69a51b1f3..a4b289c36 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerOptimizePlanServiceImpl.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerOptimizePlanServiceImpl.java @@ -9,15 +9,21 @@ import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOptimizePlan import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOptimizePlateRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOrderModelRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOrderSource; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerOptimizationResultsReqVO; +import com.cf.imes.module.executor.controller.admin.plan.component.BoardLayoutResultSummary; import com.cf.imes.module.executor.dal.dataobject.order.OrderDO; import com.cf.imes.module.executor.dal.dataobject.veneer.OrderVeneerPlanDO; import com.cf.imes.module.executor.dal.mysql.order.OrderMapper; import com.cf.imes.module.executor.dal.mysql.veneer.VeneerOptimizePlanMapper; +import com.cf.imes.module.executor.enums.VeneerPlanStatusEnum; +import com.cf.imes.module.executor.enums.VeneerPlateProcessStatusEnum; +import com.cf.imes.module.executor.enums.plan.FlowProcessStatus; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.Duration; import java.util.ArrayList; @@ -28,6 +34,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Objects; import static com.cf.imes.framework.redis.constants.RedisKeyConstants.ORDER_VENEER_PLAN_OPTIMIZE_KEY; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_GOODS_EMPTY; @@ -36,6 +43,11 @@ import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMI import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_PLAN_LOCKED; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_PLAN_NOT_EXIST; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_PLATE_EMPTY; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_MATERIAL_MULTIPLE; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_PROCESS_STATUS_INVALID; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_RESULT_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_RESULT_STATUS_CHANGED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_OPTIMIZE_VENEER_NO_REQUIRED; /** * 贴皮排单优化 Service 实现。 @@ -50,6 +62,9 @@ public class VeneerOptimizePlanServiceImpl implements VeneerOptimizePlanService /** 订单批量查询大小,避免生成过长的 IN 条件。 */ private static final int ORDER_QUERY_BATCH_SIZE = 100; + /** 贴皮短号批量更新大小,避免生成过长的 IN 条件。 */ + private static final int VENEER_UPDATE_BATCH_SIZE = 1000; + /** 原子校验锁归属并删除,避免误删其他请求重新获取的锁。 */ private static final DefaultRedisScript RELEASE_PLAN_LOCK_SCRIPT; @@ -78,33 +93,37 @@ public class VeneerOptimizePlanServiceImpl implements VeneerOptimizePlanService VeneerOptimizePlanLockReqVO lockReq = new VeneerOptimizePlanLockReqVO(); lockReq.setPlanId(planId); lockReq.setVal(val); - acquirePlanLock(lockReq); + validateLockRequest(lockReq); + OrderVeneerPlanDO plan = validatePlanExists(planId); + acquirePlanLockValue(lockReq); - // 待删除贴皮仅向优化端下发短号,不重复传输此前已经发送的完整板件数据。 - List pendingDeleteVeneerNos = - veneerOptimizePlanMapper.selectPendingDeleteVeneerNos(planId); + // optimize_time 是首次优化是否已经完成的持久标志,避免用可重算的数量字段判断。 + boolean optimized = plan.getOptimizeTime() != null; - // 只加载本次尚未优化且未标记待删除的贴皮,并合并多加工组造成的重复记录。 + // 首次优化尚不存在历史排版,不下发待删除列表;再次优化才通知组件移除历史贴皮。 + List pendingDeleteVeneerNos = optimized + ? veneerOptimizePlanMapper.selectPendingDeleteVeneerNos(planId) : List.of(); + + // 首次优化加载全部正常贴皮;再次优化只加载新增未优化贴皮,并合并多加工组重复记录。 List plates = mergePlateDetails( - veneerOptimizePlanMapper.selectPlateList(planId)); + veneerOptimizePlanMapper.selectPlateList(planId, optimized)); if (CollUtil.isEmpty(plates) && CollUtil.isEmpty(pendingDeleteVeneerNos)) { throw new ServiceException(VENEER_OPTIMIZE_PLATE_EMPTY); } - List goods = CollUtil.isEmpty(plates) - ? List.of() : veneerOptimizePlanMapper.selectGoodsList(planId); - if (CollUtil.isNotEmpty(plates) && CollUtil.isEmpty(goods)) { + // 订单和贴皮商品属于排单基础上下文,始终返回全量,不随增量贴皮列表变空。 + List goods = veneerOptimizePlanMapper.selectGoodsList(planId); + if (CollUtil.isEmpty(goods)) { throw new ServiceException(VENEER_OPTIMIZE_GOODS_EMPTY); } - // 订单范围以当前排单有效贴皮明细为准,不依赖排单冗余的 order_nos。 - List orderIds = plates.stream().map(VeneerOptimizePlateRespVO::getOrderId) - .filter(java.util.Objects::nonNull).distinct().sorted().toList(); + // 订单范围以整个排单有效贴皮明细为准,不依赖本次增量 plateList 或冗余 order_nos。 + List orderIds = veneerOptimizePlanMapper.selectOrderIds(planId); List orders = new ArrayList<>(); for (List batch : CollUtil.split(orderIds, ORDER_QUERY_BATCH_SIZE)) { orders.addAll(orderMapper.selectBatchIds(batch)); } - if (CollUtil.isNotEmpty(plates) && CollUtil.isEmpty(orders)) { + if (CollUtil.isEmpty(orders)) { throw new ServiceException(VENEER_OPTIMIZE_ORDERS_EMPTY); } @@ -129,11 +148,153 @@ public class VeneerOptimizePlanServiceImpl implements VeneerOptimizePlanService .build(); } + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOptimizationResults(VeneerOptimizationResultsReqVO req) { + if (req == null || req.getFlowSessionSavedContent() == null + || CollUtil.isEmpty(req.getFlowSessionSavedContent().getLayoutResultSummary())) { + throw new ServiceException(VENEER_OPTIMIZE_RESULT_REQUIRED); + } + OrderVeneerPlanDO plan = validatePlanExists(req.getPlanId()); + Integer flowStatus = req.getFlowSessionSavedContent().getProcessStatus(); + List summaries = + req.getFlowSessionSavedContent().getLayoutResultSummary(); + + if (FlowProcessStatus.NOT_STARTED.getStatus().equals(flowStatus)) { + saveOptimizedResult(plan, summaries); + } else if (FlowProcessStatus.IN_PROGRESS.getStatus().equals(flowStatus)) { + saveCuttingResult(plan, summaries); + } else if (FlowProcessStatus.COMPLETED.getStatus().equals(flowStatus)) { + saveCutResult(plan, summaries); + } else { + throw new ServiceException(VENEER_OPTIMIZE_PROCESS_STATUS_INVALID); + } + } + + /** 保存全量优化排版结果。 */ + private void saveOptimizedResult(OrderVeneerPlanDO plan, + List summaries) { + List veneerNos = collectVeneerNos(summaries, false); + String optimizeGoodsId = resolveOptimizeGoodsId(summaries); + + // 与普通板件排单一致:先重置当前正常贴皮,再按本次全量结果重新标记已优化。 + veneerOptimizePlanMapper.resetOptimizedVeneerStatus( + plan.getId(), + VeneerPlateProcessStatusEnum.OPTIMIZED.getStatus(), + VeneerPlateProcessStatusEnum.NOT_OPTIMIZED.getStatus()); + updateVeneerStatusInBatches( + plan.getId(), veneerNos, + List.of(VeneerPlateProcessStatusEnum.NOT_OPTIMIZED.getStatus()), + VeneerPlateProcessStatusEnum.OPTIMIZED.getStatus()); + + int scheduledBoardNum = summaries.stream() + .map(BoardLayoutResultSummary::getBoardCount) + .filter(Objects::nonNull) + .mapToInt(Integer::intValue) + .sum(); + int updated = veneerOptimizePlanMapper.updatePlanOptimized( + plan.getId(), plan.getVersion(), VeneerPlanStatusEnum.OPTIMIZED.getStatus(), + optimizeGoodsId, veneerNos.size(), scheduledBoardNum); + validateSinglePlanUpdated(updated); + } + + /** 保存开始开料结果。 */ + private void saveCuttingResult(OrderVeneerPlanDO plan, + List summaries) { + List veneerNos = collectVeneerNos(summaries, false); + updateVeneerStatusInBatches( + plan.getId(), veneerNos, + List.of(VeneerPlateProcessStatusEnum.OPTIMIZED.getStatus()), + VeneerPlateProcessStatusEnum.CUTTING.getStatus()); + int updated = veneerOptimizePlanMapper.updatePlanCutting( + plan.getId(), plan.getVersion(), VeneerPlanStatusEnum.OPENING.getStatus()); + validateSinglePlanUpdated(updated); + } + + /** 保存结束开料结果。 */ + private void saveCutResult(OrderVeneerPlanDO plan, + List summaries) { + List veneerNos = collectVeneerNos(summaries, true); + updateVeneerStatusInBatches( + plan.getId(), veneerNos, + List.of(VeneerPlateProcessStatusEnum.OPTIMIZED.getStatus(), + VeneerPlateProcessStatusEnum.CUTTING.getStatus()), + VeneerPlateProcessStatusEnum.CUT.getStatus()); + + int cuttedBoardNum = summaries.stream() + .map(BoardLayoutResultSummary::getEndCutBoardCount) + .filter(Objects::nonNull) + .mapToInt(Integer::intValue) + .sum(); + int updated = veneerOptimizePlanMapper.updatePlanCut( + plan.getId(), plan.getVersion(), VeneerPlanStatusEnum.OPENED.getStatus(), + cuttedBoardNum, veneerNos.size()); + validateSinglePlanUpdated(updated); + } + + /** 汇总组件回传的贴皮短号并去重。 */ + private List collectVeneerNos(List summaries, + boolean completed) { + List veneerNos = summaries.stream() + .map(completed + ? BoardLayoutResultSummary::getCustomEndCutBlockNos + : BoardLayoutResultSummary::getCustomBlockNos) + .filter(CollUtil::isNotEmpty) + .flatMap(List::stream) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + if (CollUtil.isEmpty(veneerNos)) { + throw new ServiceException(VENEER_OPTIMIZE_VENEER_NO_REQUIRED); + } + return veneerNos; + } + + /** 获取本次优化唯一的贴皮商品编号。 */ + private String resolveOptimizeGoodsId(List summaries) { + List goodsIds = summaries.stream() + .map(BoardLayoutResultSummary::getCustomMaterialNos) + .filter(CollUtil::isNotEmpty) + .flatMap(List::stream) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + if (goodsIds.size() > 1) { + throw new ServiceException(VENEER_OPTIMIZE_MATERIAL_MULTIPLE); + } + return CollUtil.getFirst(goodsIds); + } + + /** 分批更新贴皮加工状态,并以影响行数校验组件结果与数据库状态一致。 */ + private void updateVeneerStatusInBatches(Long planId, List veneerNos, + List sourceStatuses, + Integer targetStatus) { + int updatedCount = 0; + for (List batch : CollUtil.split(veneerNos, VENEER_UPDATE_BATCH_SIZE)) { + updatedCount += veneerOptimizePlanMapper.updateVeneerProcessStatus( + planId, batch, sourceStatuses, targetStatus); + } + if (updatedCount != veneerNos.size()) { + throw new ServiceException(VENEER_OPTIMIZE_RESULT_STATUS_CHANGED); + } + } + + /** 校验排单版本及状态更新结果。 */ + private void validateSinglePlanUpdated(int updated) { + if (updated != 1) { + throw new ServiceException(VENEER_OPTIMIZE_RESULT_STATUS_CHANGED); + } + } + @Override public void acquirePlanLock(VeneerOptimizePlanLockReqVO req) { validateLockRequest(req); validatePlanExists(req.getPlanId()); + acquirePlanLockValue(req); + } + /** 获取锁值;调用前必须完成请求参数和排单存在性校验。 */ + private void acquirePlanLockValue(VeneerOptimizePlanLockReqVO req) { String lockKey = String.format(ORDER_VENEER_PLAN_OPTIMIZE_KEY, req.getPlanId()); String lockVal = stringRedisTemplate.opsForValue().get(lockKey); if (lockVal == null) { diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerPlanServiceImpl.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerPlanServiceImpl.java index 3d7f8aab9..0ec7f37ae 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerPlanServiceImpl.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerPlanServiceImpl.java @@ -225,6 +225,10 @@ public class VeneerPlanServiceImpl implements VeneerPlanService { .orderNos(buildOrderNos(group)) .veneerNum(group.size()) .veneerArea(calculateVeneerArea(group)) + .optimizedPlateNum(0) + .scheduledBoardNum(0) + .cuttedBoardNum(0) + .cuttedPlateNum(0) .operator(loginUser.getNickname()) .version(0) .build(); diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerOptimizePlanMapper.xml b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerOptimizePlanMapper.xml index b422b7e91..6dc498efa 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerOptimizePlanMapper.xml +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerOptimizePlanMapper.xml @@ -31,11 +31,17 @@ AND ovg.deleted = false WHERE opv.deleted = false AND opv.plan_id = #{planId} - AND opv.process_status = 0 - AND opv.pending_delete = false ORDER BY ovg.goods_id + + @@ -134,4 +142,73 @@ AND opv.pending_delete = true ORDER BY opv.id + + + UPDATE order_plate_veneer + SET process_status = #{notOptimizedStatus}, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND plan_id = #{planId} + AND pending_delete = false + AND process_status = #{optimizedStatus} + + + + UPDATE order_plate_veneer + SET process_status = #{targetStatus}, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND plan_id = #{planId} + AND pending_delete = false + AND process_status IN + + #{status} + + AND veneer_no IN + + #{veneerNo} + + + + + UPDATE order_veneer_plan + SET status = #{status}, + optimize_goods_id = #{optimizeGoodsId}, + optimize_time = CURRENT_TIMESTAMP, + optimized_plate_num = #{optimizedPlateNum}, + scheduled_board_num = #{scheduledBoardNum}, + version = version + 1, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND id = #{planId} + AND version = #{version} + AND status IN (0, 1) + + + + UPDATE order_veneer_plan + SET status = #{status}, + produce_time = NULL, + version = version + 1, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND id = #{planId} + AND version = #{version} + AND status IN (0, 1) + + + + UPDATE order_veneer_plan + SET status = #{status}, + cutted_board_num = #{cuttedBoardNum}, + cutted_plate_num = #{cuttedPlateNum}, + produce_time = CURRENT_TIMESTAMP, + finish_time = CURRENT_TIMESTAMP, + version = version + 1, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND id = #{planId} + AND version = #{version} + AND status != 3 + diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerPlanMapper.xml b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerPlanMapper.xml index 5a3f2c030..2eb0eefaa 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerPlanMapper.xml +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerPlanMapper.xml @@ -24,6 +24,13 @@ ovp.is_scheduled AS isScheduled, ovp.veneer_num AS veneerNum, ovp.veneer_area AS veneerArea, + ovp.optimize_goods_id AS optimizeGoodsId, + ovp.optimize_time AS optimizeTime, + ovp.optimized_plate_num AS optimizedPlateNum, + GREATEST(ovp.veneer_num - ovp.optimized_plate_num, 0) AS unOptimizedPlateNum, + ovp.scheduled_board_num AS scheduledBoardNum, + ovp.cutted_board_num AS cuttedBoardNum, + ovp.cutted_plate_num AS cuttedPlateNum, ovp.create_time AS createTime, ovp.produce_time AS produceTime FROM order_veneer_plan ovp diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_cht.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_cht.properties index 11fd14cae..46c3220de 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_cht.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_cht.properties @@ -173,3 +173,8 @@ veneer.optimize.plate.empty=貼皮排單下沒有有效貼皮數據 veneer.optimize.goods.empty=貼皮排單商品數據異常 veneer.optimize.orders.empty=貼皮排單關聯訂單為空 veneer.optimize.plate.model.empty=貼皮排單板件造型數據異常 +veneer.optimize.result.required=貼皮優化結果不能為空 +veneer.optimize.process.status.invalid=貼皮優化流程狀態不正確 +veneer.optimize.veneer.no.required=貼皮優化結果中的貼皮編號不能為空 +veneer.optimize.material.multiple=同一貼皮排單只能包含一個貼皮商品編號 +veneer.optimize.result.status.changed=貼皮排單或貼皮加工狀態已發生變化,請刷新後重試 diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_en.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_en.properties index fedbebeb2..93fbfc586 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_en.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_en.properties @@ -174,3 +174,8 @@ veneer.optimize.plate.empty=No valid veneer records exist in the veneer plan veneer.optimize.goods.empty=The veneer plan goods data is invalid veneer.optimize.orders.empty=The veneer plan has no associated orders veneer.optimize.plate.model.empty=The veneer plan plate model data is invalid +veneer.optimize.result.required=The veneer optimization result is required +veneer.optimize.process.status.invalid=The veneer optimization process status is invalid +veneer.optimize.veneer.no.required=The veneer number in the optimization result is required +veneer.optimize.material.multiple=Only one veneer goods number is allowed in a veneer plan +veneer.optimize.result.status.changed=The veneer plan or veneer process status has changed. Please refresh and try again diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_zh.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_zh.properties index c87ac53c4..823f63181 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_zh.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_zh.properties @@ -174,3 +174,8 @@ veneer.optimize.plate.empty=贴皮排单下没有有效贴皮数据 veneer.optimize.goods.empty=贴皮排单商品数据异常 veneer.optimize.orders.empty=贴皮排单关联订单为空 veneer.optimize.plate.model.empty=贴皮排单板件造型数据异常 +veneer.optimize.result.required=贴皮优化结果不能为空 +veneer.optimize.process.status.invalid=贴皮优化流程状态不正确 +veneer.optimize.veneer.no.required=贴皮优化结果中的贴皮编号不能为空 +veneer.optimize.material.multiple=同一贴皮排单只能包含一个贴皮商品编号 +veneer.optimize.result.status.changed=贴皮排单或贴皮加工状态已发生变化,请刷新后重试