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 c31153fae..e8846bdad 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 @@ -182,6 +182,34 @@ public class ErrorCodeConstants { public static final ErrorCode VENEER_RESELECT_RECORD_REQUIRED = new ErrorCode(1_003_009_020, "veneer.reselect.record.required"); public static final ErrorCode VENEER_RESELECT_SOURCE_REQUIRED = new ErrorCode(1_003_009_021, "veneer.reselect.source.required"); public static final ErrorCode VENEER_RESELECT_RECORD_STATUS_CHANGED = new ErrorCode(1_003_009_022, "veneer.reselect.record.status.changed"); + public static final ErrorCode VENEER_REMOVE_RECORD_REQUIRED = new ErrorCode(1_003_009_023, "veneer.remove.record.required"); + public static final ErrorCode VENEER_REMOVE_RECORD_STATUS_CHANGED = new ErrorCode(1_003_009_024, "veneer.remove.record.status.changed"); + public static final ErrorCode VENEER_UNPLANNED_SCOPE_INVALID = new ErrorCode(1_003_009_025, "veneer.unplanned.scope.invalid"); + public static final ErrorCode VENEER_PLAN_RECORD_REQUIRED = new ErrorCode(1_003_009_026, "veneer.plan.record.required"); + public static final ErrorCode VENEER_PLAN_RECORD_STATUS_CHANGED = new ErrorCode(1_003_009_027, "veneer.plan.record.status.changed"); + public static final ErrorCode VENEER_PLAN_AREA_DATA_INVALID = new ErrorCode(1_003_009_028, "veneer.plan.area.data.invalid"); + public static final ErrorCode VENEER_PLAN_CREATE_FAILED = new ErrorCode(1_003_009_029, "veneer.plan.create.failed"); + public static final ErrorCode VENEER_PLAN_GROUP_REQUIRED = new ErrorCode(1_003_009_030, "veneer.plan.group.required"); + public static final ErrorCode VENEER_PLAN_GROUP_SOURCE_NO_REQUIRED = new ErrorCode(1_003_009_031, "veneer.plan.group.source.no.required"); + public static final ErrorCode VENEER_PLAN_GROUP_DUPLICATE = new ErrorCode(1_003_009_032, "veneer.plan.group.duplicate"); + public static final ErrorCode VENEER_PLAN_RECORD_DUPLICATE = new ErrorCode(1_003_009_033, "veneer.plan.record.duplicate"); + public static final ErrorCode VENEER_PLAN_SOURCE_NO_MISMATCH = new ErrorCode(1_003_009_034, "veneer.plan.source.no.mismatch"); + public static final ErrorCode VENEER_PLAN_SOURCE_NO_NOT_EXIST = new ErrorCode(1_003_009_035, "veneer.plan.source.no.not.exist"); + public static final ErrorCode VENEER_PLAN_SOURCE_NO_DUPLICATE = new ErrorCode(1_003_009_036, "veneer.plan.source.no.duplicate"); + public static final ErrorCode VENEER_PLAN_DELETE_RECORD_REQUIRED = new ErrorCode(1_003_009_037, "veneer.plan.delete.record.required"); + public static final ErrorCode VENEER_PLAN_DELETE_LIMIT_EXCEEDED = new ErrorCode(1_003_009_038, "veneer.plan.delete.limit.exceeded"); + public static final ErrorCode VENEER_PLAN_DELETE_NOT_EXIST = new ErrorCode(1_003_009_039, "veneer.plan.delete.not.exist"); + public static final ErrorCode VENEER_PLAN_DELETE_NOT_ALLOWED = new ErrorCode(1_003_009_040, "veneer.plan.delete.not.allowed"); + public static final ErrorCode VENEER_PLAN_DELETE_STATUS_CHANGED = new ErrorCode(1_003_009_041, "veneer.plan.delete.status.changed"); + public static final ErrorCode VENEER_PLAN_SORT_RECORD_REQUIRED = new ErrorCode(1_003_009_042, "veneer.plan.sort.record.required"); + public static final ErrorCode VENEER_PLAN_SORT_RECORD_DUPLICATE = new ErrorCode(1_003_009_043, "veneer.plan.sort.record.duplicate"); + public static final ErrorCode VENEER_PLAN_SORT_STATUS_CHANGED = new ErrorCode(1_003_009_044, "veneer.plan.sort.status.changed"); + public static final ErrorCode VENEER_PLAN_NOT_EXIST = new ErrorCode(1_003_009_045, "veneer.plan.not.exist"); + public static final ErrorCode VENEER_PLAN_PROCESS_REQUIRED = new ErrorCode(1_003_009_046, "veneer.plan.process.required"); + public static final ErrorCode VENEER_PLAN_OPERATION_STATUS_CHANGED = new ErrorCode(1_003_009_047, "veneer.plan.operation.status.changed"); + public static final ErrorCode VENEER_PLAN_PLATE_RECORD_REQUIRED = new ErrorCode(1_003_009_048, "veneer.plan.plate.record.required"); + public static final ErrorCode VENEER_PLAN_PLATE_SOURCE_MISMATCH = new ErrorCode(1_003_009_049, "veneer.plan.plate.source.mismatch"); + public static final ErrorCode VENEER_PLAN_PLATE_STATUS_CHANGED = new ErrorCode(1_003_009_050, "veneer.plan.plate.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-api/src/main/java/com/cf/imes/module/executor/enums/VeneerPlanStatusEnum.java b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/VeneerPlanStatusEnum.java new file mode 100644 index 000000000..ac5b81098 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/VeneerPlanStatusEnum.java @@ -0,0 +1,40 @@ +package com.cf.imes.module.executor.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 贴皮排单加工状态枚举。 + * + *

状态值与普通排单 {@code PlanStatusEnum} 保持一致,投入生产状态由 + * {@code order_veneer_plan.is_scheduled} 单独记录。

+ */ +@Getter +@AllArgsConstructor +public enum VeneerPlanStatusEnum { + + NOCUTTING(0, "未开料/新单"), + OPTIMIZED(1, "已优化"), + OPENING(2, "开料中"), + OPENED(3, "已开料"); + + /** + * 状态值。 + */ + private final Integer status; + + /** + * 状态说明。 + */ + private final String description; + + /** + * 判断传入状态是否等于当前枚举值。 + * + * @param status 状态值 + * @return 是否相等 + */ + public boolean equals(Integer status) { + return this.status.equals(status); + } +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/VeneerPlateSurfaceEnum.java b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/VeneerPlateSurfaceEnum.java new file mode 100644 index 000000000..5afd67a76 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/VeneerPlateSurfaceEnum.java @@ -0,0 +1,25 @@ +package com.cf.imes.module.executor.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 贴皮对应的实际板件面枚举。 + */ +@Getter +@AllArgsConstructor +public enum VeneerPlateSurfaceEnum { + + FRONT(0, "正面"), + BACK(1, "反面"); + + /** + * 实际板件面存储值。 + */ + private final Integer surface; + + /** + * 板件面说明。 + */ + private final String description; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/VeneerSideEnum.java b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/VeneerSideEnum.java new file mode 100644 index 000000000..2595d0bcd --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/VeneerSideEnum.java @@ -0,0 +1,25 @@ +package com.cf.imes.module.executor.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 贴皮逻辑面枚举。 + */ +@Getter +@AllArgsConstructor +public enum VeneerSideEnum { + + A("A", "A面"), + B("B", "B面"); + + /** + * 存储值,同时作为贴皮编号后缀。 + */ + private final String side; + + /** + * 逻辑面说明。 + */ + private final String description; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/UnplannedVeneerController.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/UnplannedVeneerController.java new file mode 100644 index 000000000..7039246ff --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/UnplannedVeneerController.java @@ -0,0 +1,78 @@ +package com.cf.imes.module.executor.controller.admin.veneer; + +import com.cf.imes.framework.common.pojo.CommonResult; +import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsPageReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderPageReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO; +import com.cf.imes.module.executor.service.veneer.VeneerManageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static com.cf.imes.framework.common.pojo.CommonResult.success; + +/** + * 未排贴皮接口。 + */ +@Tag(name = "生产管理 - 未排贴皮") +@RestController +@RequestMapping("/executor/unplanned-veneer") +@Validated +public class UnplannedVeneerController { + + @Resource + private VeneerManageService veneerManageService; + + /** + * 分页查询存在未排贴皮的订单。 + * + * @param req 分页及筛选条件 + * @return 订单分页列表 + */ + @PostMapping("/order/page") + @Operation(summary = "获取未排贴皮订单分页列表") + @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") + public CommonResult> getUnplannedVeneerOrderPage( + @Valid @RequestBody VeneerManageOrderPageReqVO req) { + return success(veneerManageService.getUnplannedOrderPage(req)); + } + + /** + * 分页查询存在未排贴皮的板材。 + * + * @param req 分页及筛选条件 + * @return 板材分页列表 + */ + @PostMapping("/goods/page") + @Operation(summary = "获取未排贴皮板材分页列表") + @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") + public CommonResult> getUnplannedVeneerGoodsPage( + @Valid @RequestBody VeneerManageGoodsPageReqVO req) { + return success(veneerManageService.getUnplannedGoodsPage(req)); + } + + /** + * 按订单或板材查询未排贴皮、原板件及板件造型数据。 + * + * @param req 互斥查询范围及可选房间、柜体条件 + * @return 未排贴皮排单基础数据 + */ + @PostMapping("/plate/data") + @Operation(summary = "获取未排贴皮板件数据") + @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") + public CommonResult getUnplannedVeneerPlateData( + @Valid @RequestBody UnplannedVeneerPlateDataReqVO req) { + return success(veneerManageService.getUnplannedPlateData(req)); + } +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerDecomposeController.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerDecomposeController.java index 04d75f6ad..47e626854 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerDecomposeController.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerDecomposeController.java @@ -7,8 +7,8 @@ import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeGoo import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeGoodsRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateDataRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlatePageReqVO; -import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO; import com.cf.imes.module.executor.service.veneer.VeneerDecomposeService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -21,8 +21,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.List; - import static com.cf.imes.framework.common.pojo.CommonResult.success; /** @@ -46,7 +44,7 @@ public class VeneerDecomposeController { @PostMapping("/order/page") @Operation(summary = "获取贴皮分解订单分页列表") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult> getOrderPage( + public CommonResult> getVeneerDecomposeOrderPage( @Valid @RequestBody VeneerDecomposeOrderPageReqVO req) { return success(veneerDecomposeService.getOrderPage(req)); } @@ -60,7 +58,7 @@ public class VeneerDecomposeController { @PostMapping("/goods/page") @Operation(summary = "获取贴皮分解板材分页列表") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult> getGoodsPage( + public CommonResult> getVeneerDecomposeGoodsPage( @Valid @RequestBody VeneerDecomposeGoodsPageReqVO req) { return success(veneerDecomposeService.getGoodsPage(req)); } @@ -69,12 +67,12 @@ public class VeneerDecomposeController { * 按订单查询未贴皮分解板件列表。 * * @param req 查询条件,必须包含订单ID列表 - * @return 板件列表 + * @return 板件及板件造型数据 */ @PostMapping("/order/plate/page") @Operation(summary = "获取订单下待贴皮分解板件列表") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult> getOrderPlateList( + public CommonResult getVeneerDecomposeOrderPlateData( @Valid @RequestBody VeneerDecomposePlatePageReqVO req) { return success(veneerDecomposeService.getOrderPlateList(req)); } @@ -83,12 +81,12 @@ public class VeneerDecomposeController { * 按板材查询未贴皮分解板件列表。 * * @param req 查询条件,必须包含商品编码列表 - * @return 板件列表 + * @return 板件及板件造型数据 */ @PostMapping("/goods/plate/page") @Operation(summary = "获取材质下待贴皮分解板件列表") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult> getGoodsPlateList( + public CommonResult getVeneerDecomposeGoodsPlateData( @Valid @RequestBody VeneerDecomposePlatePageReqVO req) { return success(veneerDecomposeService.getGoodsPlateList(req)); } @@ -102,7 +100,8 @@ public class VeneerDecomposeController { @PostMapping("/decompose") @Operation(summary = "执行贴皮分解") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult decompose(@Valid @RequestBody VeneerDecomposeCreateReqVO req) { + public CommonResult createVeneerDecompose( + @Valid @RequestBody VeneerDecomposeCreateReqVO req) { veneerDecomposeService.decompose(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/VeneerManageController.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerManageController.java index 069332947..33c40fdcb 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerManageController.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerManageController.java @@ -8,6 +8,7 @@ import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderP import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateListReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageRemoveReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageReselectReqVO; import com.cf.imes.module.executor.service.veneer.VeneerManageService; import io.swagger.v3.oas.annotations.Operation; @@ -46,7 +47,7 @@ public class VeneerManageController { @PostMapping("/order/page") @Operation(summary = "获取贴皮管理订单分页列表") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult> getOrderPage( + public CommonResult> getVeneerManageOrderPage( @Valid @RequestBody VeneerManageOrderPageReqVO req) { return success(veneerManageService.getOrderPage(req)); } @@ -60,7 +61,7 @@ public class VeneerManageController { @PostMapping("/goods/page") @Operation(summary = "获取贴皮管理板材分页列表") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult> getGoodsPage( + public CommonResult> getVeneerManageGoodsPage( @Valid @RequestBody VeneerManageGoodsPageReqVO req) { return success(veneerManageService.getGoodsPage(req)); } @@ -68,13 +69,13 @@ public class VeneerManageController { /** * 按订单查询已分解板件贴皮列表。 * - * @param req 查询条件,必须包含订单ID列表 + * @param req 查询条件,必须包含订单ID列表,可按房间和柜体筛选 * @return 已分解板件贴皮列表 */ @PostMapping("/order/plate/list") @Operation(summary = "按订单获取已分解板件贴皮列表") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult> getOrderPlateList( + public CommonResult> getVeneerManageOrderPlateList( @Valid @RequestBody VeneerManagePlateListReqVO req) { return success(veneerManageService.getOrderPlateList(req)); } @@ -88,7 +89,7 @@ public class VeneerManageController { @PostMapping("/goods/plate/list") @Operation(summary = "按板材获取已分解板件贴皮列表") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult> getGoodsPlateList( + public CommonResult> getVeneerManageGoodsPlateList( @Valid @RequestBody VeneerManagePlateListReqVO req) { return success(veneerManageService.getGoodsPlateList(req)); } @@ -102,9 +103,24 @@ public class VeneerManageController { @PostMapping("/plate/reselect") @Operation(summary = "批量重选贴皮") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") - public CommonResult reselectVeneer( + public CommonResult reselectVeneerManageSource( @Valid @RequestBody VeneerManageReselectReqVO req) { veneerManageService.reselectVeneer(req); return success(true); } + + /** + * 批量移除待排单板件贴皮。 + * + * @param req 待移除的板件贴皮记录ID列表 + * @return 是否成功 + */ + @PostMapping("/plate/remove") + @Operation(summary = "批量移除待排单贴皮") + @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") + public CommonResult removeVeneerManageRecord( + @Valid @RequestBody VeneerManageRemoveReqVO req) { + veneerManageService.removeVeneer(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/VeneerPlanController.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerPlanController.java new file mode 100644 index 000000000..821d66dfc --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerPlanController.java @@ -0,0 +1,212 @@ +package com.cf.imes.module.executor.controller.admin.veneer; + +import com.cf.imes.framework.common.pojo.CommonResult; +import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanAddPlateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanCreateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPlateRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanProduceReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanRemovePlateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanUpdateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanWithdrawProduceReqVO; +import com.cf.imes.module.executor.service.veneer.VeneerPlanService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Size; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static com.cf.imes.framework.common.pojo.CommonResult.success; + +/** + * 贴皮排单接口。 + */ +@Tag(name = "生产管理 - 贴皮排单") +@RestController +@RequestMapping("/executor/veneer-plan") +@Validated +public class VeneerPlanController { + + @Resource + private VeneerPlanService veneerPlanService; + + /** + * 按前端提交的贴皮商品编号分组批量创建贴皮排单,每个分组生成一个排单。 + * + * @param req 前端整理的贴皮商品编号分组 + * @return 是否创建成功 + */ + @PostMapping("/create") + @Operation(summary = "批量创建贴皮排单") + @PreAuthorize("@ss.hasPermission('unplannedOrders:query')") + public CommonResult createVeneerPlan( + @Valid @RequestBody VeneerPlanCreateReqVO req) { + veneerPlanService.createPlan(req); + return success(true); + } + + /** + * 更新未进入开料阶段的贴皮排单加工方案。 + * + * @param req 排单ID及加工方案 + * @return 是否更新成功 + */ + @PostMapping("/process-scheme/update") + @Operation(summary = "更新贴皮排单加工方案") + @PreAuthorize("@ss.hasPermission('placeorder:update')") + public CommonResult updateVeneerPlanProcessScheme( + @Valid @RequestBody VeneerPlanUpdateReqVO req) { + veneerPlanService.updatePlan(req); + return success(true); + } + + /** + * 将贴皮排单投入生产。 + * + * @param req 待投入生产的排单ID + * @return 是否操作成功 + */ + @PostMapping("/produce") + @Operation(summary = "贴皮排单投入生产") + @PreAuthorize("@ss.hasPermission('placeorder:update')") + public CommonResult produceVeneerPlan( + @Valid @RequestBody VeneerPlanProduceReqVO req) { + veneerPlanService.producePlan(req); + return success(true); + } + + /** + * 撤回贴皮排单的投入生产状态。 + * + * @param req 待撤回生产的排单ID + * @return 是否操作成功 + */ + @PostMapping("/withdraw-produce") + @Operation(summary = "撤回贴皮排单生产") + @PreAuthorize("@ss.hasPermission('placeorder:update')") + public CommonResult withdrawVeneerPlanProduction( + @Valid @RequestBody VeneerPlanWithdrawProduceReqVO req) { + veneerPlanService.withdrawProduce(req); + return success(true); + } + + /** + * 向未进入开料阶段的贴皮排单新增贴皮记录。 + * + * @param req 目标排单及待加入贴皮记录 + * @return 是否操作成功 + */ + @PostMapping("/plate/add") + @Operation(summary = "向贴皮排单新增贴皮") + @PreAuthorize("@ss.hasPermission('manage:order-plan:insert-plate')") + public CommonResult addVeneerPlanPlate( + @Valid @RequestBody VeneerPlanAddPlateReqVO req) { + veneerPlanService.addPlanPlate(req); + return success(true); + } + + /** + * 从未进入开料阶段的贴皮排单移除贴皮记录。 + * + * @param req 目标排单及待移除贴皮记录 + * @return 是否操作成功 + */ + @PostMapping("/plate/remove") + @Operation(summary = "从贴皮排单移除贴皮") + @PreAuthorize("@ss.hasPermission('manage:order-plan:remove-plate')") + public CommonResult removeVeneerPlanPlate( + @Valid @RequestBody VeneerPlanRemovePlateReqVO req) { + veneerPlanService.removePlanPlate(req); + return success(true); + } + + /** + * 批量删除未进入开料阶段的贴皮排单,并将排单内贴皮恢复为待排单状态。 + * + * @param ids 贴皮排单ID列表 + * @return 是否删除成功 + */ + @DeleteMapping("/delete") + @Operation(summary = "批量删除贴皮排单") + @Parameter(name = "ids", description = "贴皮排单ID列表", required = true) + @PreAuthorize("@ss.hasPermission('placeorder:delete')") + public CommonResult deleteVeneerPlan( + @RequestParam("ids") @Size(max = 20, message = "最多可同时删除20个贴皮排单") + List ids) { + veneerPlanService.deletePlan(ids); + return success(true); + } + + /** + * 分页查询已创建的贴皮排单。 + * + * @param req 分页及筛选条件 + * @return 贴皮排单分页列表 + */ + @PostMapping("/page") + @Operation(summary = "获取已排贴皮排单分页列表") + @PreAuthorize("@ss.hasAnyPermissions('placeorder:query','placeorder:priority','producePlan:query')") + public CommonResult> getVeneerPlanPage( + @Valid @RequestBody VeneerPlanPageReqVO req) { + return success(veneerPlanService.getPlanPage(req)); + } + + /** + * 查询指定贴皮排单下的全部贴皮明细。 + * + * @param planId 贴皮排单ID + * @return 排单贴皮明细列表 + */ + @GetMapping("/plate/list") + @Operation(summary = "获取贴皮排单明细列表") + @Parameter(name = "planId", description = "贴皮排单ID", required = true) + @PreAuthorize("@ss.hasAnyPermissions('placeorder:query','producePlan:query')") + public CommonResult> getVeneerPlanPlateList( + @RequestParam("planId") Long planId) { + return success(veneerPlanService.getPlanPlateList(planId)); + } + + /** + * 查询未完成开料的贴皮排单优先级列表。 + * + * @return 贴皮排单优先级列表 + */ + @GetMapping("/sort/page") + @Operation(summary = "获取贴皮排单优先级列表") + @PreAuthorize("@ss.hasAnyPermissions('placeorder:query','placeorder:priority','producePlan:query')") + public CommonResult> getVeneerPlanSortPage() { + return success(veneerPlanService.getPlanSortPage()); + } + + /** + * 批量设置未完成开料贴皮排单的优先级。 + * + * @param req 排单ID及排序值列表 + * @return 是否设置成功 + */ + @PutMapping("/setSort") + @Operation(summary = "设置贴皮排单优先级") + @PreAuthorize("@ss.hasPermission('placeorder:priority')") + public CommonResult setVeneerPlanSort( + @Valid @RequestBody List req) { + veneerPlanService.setPlanSort(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/UnplannedVeneerPlateDataReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/UnplannedVeneerPlateDataReqVO.java new file mode 100644 index 000000000..b510981e2 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/UnplannedVeneerPlateDataReqVO.java @@ -0,0 +1,25 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 未排贴皮板件数据查询参数。 + */ +@Data +public class UnplannedVeneerPlateDataReqVO { + + @Schema(description = "订单ID列表,与板材商品编码列表互斥") + private List orderIds; + + @Schema(description = "板材商品编码列表,与订单ID列表互斥") + private List goodsIds; + + @Schema(description = "房间ID列表") + private List roomIds; + + @Schema(description = "柜体ID列表") + private List bodyIds; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/UnplannedVeneerPlateDataRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/UnplannedVeneerPlateDataRespVO.java new file mode 100644 index 000000000..a0b8f905b --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/UnplannedVeneerPlateDataRespVO.java @@ -0,0 +1,30 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import com.cf.imes.module.executor.dal.dataobject.order.OrderDO; +import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderModelDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 未排贴皮排单基础数据响应参数。 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UnplannedVeneerPlateDataRespVO { + + @Schema(description = "订单信息") + private List orderList; + + @Schema(description = "未排贴皮及其原板件信息") + private List plateList; + + @Schema(description = "原板件造型信息") + private List plateModels; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/UnplannedVeneerPlateRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/UnplannedVeneerPlateRespVO.java new file mode 100644 index 000000000..c8d776e45 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/UnplannedVeneerPlateRespVO.java @@ -0,0 +1,54 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +/** + * 未排贴皮及其原板件一体化响应参数。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class UnplannedVeneerPlateRespVO extends VeneerDecomposePlateRespVO { + + @Schema(description = "板件贴皮记录ID") + private Long id; + + @Schema(description = "订单ID") + private Long orderId; + + @Schema(description = "订单板材ID") + private Long orderGoodsId; + + @Schema(description = "贴皮逻辑面:A-A面,B-B面") + private String veneerSide; + + @Schema(description = "实际板件面:0-正面,1-反面") + private Integer plateSurface; + + @Schema(description = "贴皮编号") + private String veneerNo; + + @Schema(description = "外扩余量(mm)") + private BigDecimal outerMargin; + + @Schema(description = "来源贴皮库记录ID") + private Long sourceVeneerId; + + @Schema(description = "贴皮商品编号") + private String sourceVeneerNo; + + @Schema(description = "贴皮名称") + private String veneerName; + + @Schema(description = "产品等级") + private Integer productLevel; + + @Schema(description = "贴皮颜色") + private String veneerColor; + + @Schema(description = "贴皮材质") + private String veneerMaterial; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDecomposePlateDataRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDecomposePlateDataRespVO.java new file mode 100644 index 000000000..8e4f828b9 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDecomposePlateDataRespVO.java @@ -0,0 +1,26 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderModelDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 待贴皮分解板件及板件造型响应参数。 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VeneerDecomposePlateDataRespVO { + + @Schema(description = "待贴皮分解板件列表") + private List plateList; + + @Schema(description = "板件造型列表") + private List plateModels; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDecomposePlateRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDecomposePlateRespVO.java index b88abc06b..256e5c6eb 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDecomposePlateRespVO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDecomposePlateRespVO.java @@ -1,6 +1,7 @@ package com.cf.imes.module.executor.controller.admin.veneer.vo; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -8,43 +9,190 @@ import java.math.BigDecimal; /** * 待贴皮分解板件响应参数。 + * 板件筛选字段参考未排单 {@code getPlanPlateData.plateList}, + * 仅保留贴皮分解业务实际使用的数据。 */ @Data +@JsonInclude(JsonInclude.Include.NON_NULL) public class VeneerDecomposePlateRespVO { - /** - * 板件主键,仅用于 Service 层按板件去重,不返回前端。 - */ - @JsonIgnore + @Schema(description = "板件ID") private Long plateId; - @Schema(description = "房名") - private String roomName; + @Schema(description = "板材商品编码") + private String goodsId; - @Schema(description = "柜名") - private String bodyName; + @Schema(description = "板材名称") + private String goodsName; @Schema(description = "板件编号") private String plateNo; - @Schema(description = "材质") - private String material; + @Schema(description = "自定义板件编号") + private String customPlateNo; + + @Schema(description = "板件名称") + private String name; + + @Schema(description = "板件名称,兼容原贴皮分解字段") + private String plateName; @Schema(description = "颜色") private String color; - @Schema(description = "板名称") - private String plateName; + @Schema(description = "品牌") + private String brand; - @Schema(description = "长度") + @Schema(description = "材质") + private String material; + + @Schema(description = "板件高度") + private BigDecimal height; + + @Schema(description = "板件高度,兼容原贴皮分解长度字段") private BigDecimal length; - @Schema(description = "宽度") + @Schema(description = "板件宽度") private BigDecimal width; - @Schema(description = "厚度") + @Schema(description = "板件厚度") private BigDecimal thickness; + @Schema(description = "拆单宽度") + private BigDecimal splitWidth; + + @Schema(description = "拆单高度") + private BigDecimal splitHeight; + + @Schema(description = "拆单厚度") + private BigDecimal splitThickness; + + @Schema(description = "是否异形") + private Boolean isSpecialShaped; + + @Schema(description = "左封边厚度") + private BigDecimal sealLeft; + + @Schema(description = "右封边厚度") + private BigDecimal sealRight; + + @Schema(description = "上封边厚度") + private BigDecimal sealUp; + + @Schema(description = "下封边厚度") + private BigDecimal sealDown; + + @Schema(description = "左预留边") + private BigDecimal reservedEdgeLeft; + + @Schema(description = "右预留边") + private BigDecimal reservedEdgeRight; + + @Schema(description = "上预留边") + private BigDecimal reservedEdgeUp; + + @Schema(description = "下预留边") + private BigDecimal reservedEdgeDown; + @Schema(description = "面积") private BigDecimal area; + + @Schema(description = "纹路类型:0正纹,1可翻转,2反纹") + private Integer texture; + + @Schema(description = "孔面类型:0正面,1反面,2侧面") + private Integer holeFace; + + @Schema(description = "排版面类型:0正面,1反面,2随意面") + private Integer holeArrange; + + @Schema(description = "异型孔数量") + private Integer unregularPointCount; + + @Schema(description = "正面孔数量") + private Integer frontHoleCount; + + @Schema(description = "背面孔数量") + private Integer backHoleCount; + + @Schema(description = "侧面孔数量") + private Integer sideHoleCount; + + @Schema(description = "正面造型数量") + private Integer frontModelCount; + + @Schema(description = "背面造型数量") + private Integer backModelCount; + + @Schema(description = "是否门板") + private Boolean isDoor; + + @Schema(description = "是否弧形对角") + private Boolean isArcAcross; + + @Schema(description = "模块类型ID") + private Long moduleTypeId; + + @Schema(description = "异形偏移X") + private BigDecimal offsetX; + + @Schema(description = "异形偏移Y") + private BigDecimal offsetY; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "板件类型:0层板,1立板,2背板,3自增板") + private Integer type; + + @Schema(description = "加工组名称") + private String processGroupName; + + @Schema(description = "开门类型:0无,1左,2右,3上,4下") + private Integer openDoorType; + + @Schema(description = "是否有孔") + private Boolean isRowHole; + + @Schema(description = "是否正反面造型") + private Boolean isSculpt; + + @Schema(description = "是否侧面造型") + private Boolean isSideSculpt; + + @Schema(description = "是否二维刀路") + private Boolean is2v; + + @Schema(description = "是否侧面二维刀路") + private Boolean isSide2v; + + @Schema(description = "是否双面造型") + private Boolean doubleSideModel; + + @Schema(description = "是否双面排孔") + private Boolean doubleSideHole; + + @Schema(description = "是否正面造型") + private Boolean frontModel; + + @Schema(description = "是否背面造型") + private Boolean backModel; + + @Schema(description = "是否正面排孔") + private Boolean frontHole; + + @Schema(description = "是否反面排孔") + private Boolean backHole; + + @Schema(description = "是否侧面造型") + private Boolean sideModel; + + @Schema(description = "是否侧面排孔") + private Boolean sideHole; + + /** + * 解压后的板件造型 JSON,仅用于组装 {@code plateModels},不直接返回前端。 + */ + @JsonIgnore + private String modelData; } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManagePlateListReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManagePlateListReqVO.java index e9694595f..823e9d88c 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManagePlateListReqVO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManagePlateListReqVO.java @@ -14,6 +14,12 @@ public class VeneerManagePlateListReqVO { @Schema(description = "订单ID列表") private List orderIds; + @Schema(description = "房间ID列表,仅按订单查询时生效") + private List roomIds; + + @Schema(description = "柜体ID列表,仅按订单查询时生效") + private List bodyIds; + @Schema(description = "板材商品编码列表") private List goodsIds; } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManagePlateRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManagePlateRespVO.java index 5aa43b68a..2fbc58270 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManagePlateRespVO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManagePlateRespVO.java @@ -32,6 +32,9 @@ public class VeneerManagePlateRespVO { @Schema(description = "原板件厚(mm)") private BigDecimal thickness; + @Schema(description = "贴皮排单ID,0表示未排单") + private Long planId; + @Schema(description = "贴皮商品号") private String sourceVeneerNo; diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManageRemoveReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManageRemoveReqVO.java new file mode 100644 index 000000000..e9cef30d6 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerManageRemoveReqVO.java @@ -0,0 +1,19 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 批量移除板件贴皮请求参数。 + */ +@Data +public class VeneerManageRemoveReqVO { + + @Schema(description = "板件贴皮记录ID列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "{veneer.remove.record.required}") + private List<@NotNull(message = "{veneer.remove.record.required}") Long> ids; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanAddPlateReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanAddPlateReqVO.java new file mode 100644 index 000000000..6b7808d41 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanAddPlateReqVO.java @@ -0,0 +1,24 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 贴皮排单新增贴皮参数。 + */ +@Schema(description = "管理后台 - 贴皮排单新增贴皮 Request VO") +@Data +public class VeneerPlanAddPlateReqVO { + + @Schema(description = "目标贴皮排单ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{veneer.plan.id.required}") + private Long planId; + + @Schema(description = "待加入的板件贴皮记录ID列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "{veneer.plan.plate.record.required}") + private List veneerIds; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanCreateReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanCreateReqVO.java new file mode 100644 index 000000000..5b8f14648 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanCreateReqVO.java @@ -0,0 +1,17 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 创建贴皮排单请求参数。 + */ +@Data +public class VeneerPlanCreateReqVO { + + @Schema(description = "按贴皮商品编号整理的排单分组列表;每个分组生成一个贴皮排单", + requiredMode = Schema.RequiredMode.REQUIRED) + private List groups; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanGroupReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanGroupReqVO.java new file mode 100644 index 000000000..234d3a27a --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanGroupReqVO.java @@ -0,0 +1,20 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 贴皮排单分组请求参数。 + */ +@Data +public class VeneerPlanGroupReqVO { + + @Schema(description = "贴皮商品编号", requiredMode = Schema.RequiredMode.REQUIRED) + private String sourceVeneerNo; + + @Schema(description = "当前贴皮商品编号对应的板件贴皮记录ID列表", + requiredMode = Schema.RequiredMode.REQUIRED) + private List veneerIds; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPageReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPageReqVO.java new file mode 100644 index 000000000..eddd2b5bb --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPageReqVO.java @@ -0,0 +1,26 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import com.cf.imes.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 已排贴皮排单分页查询参数。 + */ +@Schema(description = "管理后台 - 已排贴皮排单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class VeneerPlanPageReqVO extends PageParam { + + @Schema(description = "排单号,支持模糊查询") + private String id; + + @Schema(description = "加工方案组名称,支持模糊查询") + private String processName; + + @Schema(description = "排单状态:0-未开料/新单,1-已优化,2-开料中,3-已开料") + private List status; +} 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 new file mode 100644 index 000000000..7d9e86f2c --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPageRespVO.java @@ -0,0 +1,48 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 已排贴皮排单分页响应参数。 + */ +@Schema(description = "管理后台 - 已排贴皮排单分页 Response VO") +@Data +public class VeneerPlanPageRespVO { + + @Schema(description = "贴皮排单号,即贴皮排单ID") + private Long id; + + @Schema(description = "排序优先级") + private Long sort; + + @Schema(description = "加工方案ID,预留给后续加工方案组件") + private Long processId; + + @Schema(description = "加工方案名称,预留给后续加工方案组件") + private String processName; + + @Schema(description = "贴皮信息,格式为材质-颜色-厚度mm") + private String veneerInfo; + + @Schema(description = "排单状态:0-未开料/新单,1-已优化,2-开料中,3-已开料") + private Integer status; + + @Schema(description = "是否已投入生产") + private Boolean isScheduled; + + @Schema(description = "贴皮总数,A/B面分别计数") + private Integer veneerNum; + + @Schema(description = "贴皮总面积,单位:平方米") + private BigDecimal veneerArea; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "加工时间") + private LocalDateTime produceTime; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPlateRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPlateRespVO.java new file mode 100644 index 000000000..55bc0fb5a --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanPlateRespVO.java @@ -0,0 +1,47 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 已排贴皮排单明细响应参数。 + */ +@Schema(description = "管理后台 - 已排贴皮排单明细 Response VO") +@Data +public class VeneerPlanPlateRespVO { + + @Schema(description = "贴皮编号") + private String veneerNo; + + @Schema(description = "板件编号") + private String plateNo; + + @Schema(description = "板件名称") + private String plateName; + + @Schema(description = "板件长度,单位:mm") + private BigDecimal length; + + @Schema(description = "板件宽度,单位:mm") + private BigDecimal width; + + @Schema(description = "板件厚度,单位:mm") + private BigDecimal thickness; + + @Schema(description = "贴皮商品编号") + private String sourceVeneerNo; + + @Schema(description = "贴皮名称") + private String veneerName; + + @Schema(description = "产品等级") + private Integer productLevel; + + @Schema(description = "颜色") + private String color; + + @Schema(description = "材质") + private String material; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanProduceReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanProduceReqVO.java new file mode 100644 index 000000000..bba5496fd --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanProduceReqVO.java @@ -0,0 +1,17 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 贴皮排单投入生产参数。 + */ +@Schema(description = "管理后台 - 贴皮排单投入生产 Request VO") +@Data +public class VeneerPlanProduceReqVO { + + @Schema(description = "贴皮排单ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{veneer.plan.id.required}") + private Long id; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanRemovePlateReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanRemovePlateReqVO.java new file mode 100644 index 000000000..17df505e5 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanRemovePlateReqVO.java @@ -0,0 +1,24 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 贴皮排单移除贴皮参数。 + */ +@Schema(description = "管理后台 - 贴皮排单移除贴皮 Request VO") +@Data +public class VeneerPlanRemovePlateReqVO { + + @Schema(description = "目标贴皮排单ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{veneer.plan.id.required}") + private Long planId; + + @Schema(description = "待移除的板件贴皮记录ID列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "{veneer.plan.plate.record.required}") + private List veneerIds; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanSortReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanSortReqVO.java new file mode 100644 index 000000000..b5c4d85c0 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanSortReqVO.java @@ -0,0 +1,21 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 贴皮排单优先级设置参数。 + */ +@Schema(description = "管理后台 - 贴皮排单优先级设置 Request VO") +@Data +public class VeneerPlanSortReqVO { + + @Schema(description = "贴皮排单ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "贴皮排单ID不能为空") + private Long planId; + + @Schema(description = "排序优先级", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "排序优先级不能为空") + private Long sort; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanSortRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanSortRespVO.java new file mode 100644 index 000000000..43df09d24 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanSortRespVO.java @@ -0,0 +1,44 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 贴皮排单优先级列表响应参数。 + */ +@Schema(description = "管理后台 - 贴皮排单优先级列表 Response VO") +@Data +public class VeneerPlanSortRespVO { + + @Schema(description = "贴皮排单号,即贴皮排单ID") + private Long id; + + @Schema(description = "排序优先级") + private Long sort; + + @Schema(description = "加工方案ID,预留给后续加工方案组件") + private Long processId; + + @Schema(description = "加工方案名称,预留给后续加工方案组件") + private String processName; + + @Schema(description = "贴皮信息,格式为材质-颜色-厚度mm") + private String veneerInfo; + + @Schema(description = "排单状态:0-未开料/新单,1-已优化,2-开料中,3-已开料") + private Integer status; + + @Schema(description = "是否已投入生产") + private Boolean isScheduled; + + @Schema(description = "贴皮总数,A/B面分别计数") + private Integer veneerNum; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "加工时间") + private LocalDateTime produceTime; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanUpdateReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanUpdateReqVO.java new file mode 100644 index 000000000..49567cbba --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanUpdateReqVO.java @@ -0,0 +1,24 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 贴皮排单加工方案更新参数。 + */ +@Schema(description = "管理后台 - 贴皮排单加工方案更新 Request VO") +@Data +public class VeneerPlanUpdateReqVO { + + @Schema(description = "贴皮排单ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{veneer.plan.id.required}") + private Long id; + + @Schema(description = "加工方案ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{veneer.plan.process.required}") + private Long processId; + + @Schema(description = "加工方案名称") + private String processName; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanWithdrawProduceReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanWithdrawProduceReqVO.java new file mode 100644 index 000000000..0a2449f63 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerPlanWithdrawProduceReqVO.java @@ -0,0 +1,17 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 贴皮排单撤回生产参数。 + */ +@Schema(description = "管理后台 - 贴皮排单撤回生产 Request VO") +@Data +public class VeneerPlanWithdrawProduceReqVO { + + @Schema(description = "贴皮排单ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{veneer.plan.id.required}") + private Long id; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/goods/GoodsDO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/goods/GoodsDO.java index d5a3347d5..35c01e77c 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/goods/GoodsDO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/goods/GoodsDO.java @@ -113,6 +113,11 @@ public class GoodsDO extends BaseDO { */ private Integer decomposedVeneerPlateNum; + /** + * 已排贴皮板件数量,所有有效贴皮面均已排单时计数 + */ + private Integer plannedVeneerPlateNum; + /** * 板件总面积 */ diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/order/OrderDO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/order/OrderDO.java index 739590999..ecc96f418 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/order/OrderDO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/order/OrderDO.java @@ -147,6 +147,11 @@ public class OrderDO extends BaseDO { */ private Integer decomposedVeneerPlateNum; + /** + * 已排贴皮板件数量,所有有效贴皮面均已排单时计数 + */ + private Integer plannedVeneerPlateNum; + /** * 板件总面积 */ diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderPlateVeneerDO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderPlateVeneerDO.java index 8a6925b62..396543021 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderPlateVeneerDO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderPlateVeneerDO.java @@ -98,6 +98,11 @@ public class OrderPlateVeneerDO extends BaseDO { */ private BigDecimal outerMargin; + /** + * 贴皮排单ID,0表示未排单。 + */ + private Long planId; + /** * 来源贴皮库记录主键,对应 {@code veneers.id}。 */ 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 new file mode 100644 index 000000000..80c7ef61d --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/OrderVeneerPlanDO.java @@ -0,0 +1,113 @@ +package com.cf.imes.module.executor.dal.dataobject.veneer; + +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.cf.imes.framework.mybatis.core.dataobject.BaseDO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 贴皮排单 DO。 + */ +@TableName("order_veneer_plan") +@KeySequence("order_veneer_plan_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrderVeneerPlanDO extends BaseDO { + + /** + * 贴皮排单ID。 + */ + @TableId + private Long id; + + /** + * 组织ID,也是 MyCat 分片键。 + */ + private Long organId; + + /** + * 排单状态:0-未开料/新单,1-已优化,2-开料中,3-已开料。 + */ + private Integer status; + + /** + * 是否已投入生产。 + */ + private Boolean isScheduled; + + /** + * 排序优先级。 + */ + private Long sort; + + /** + * 计划生产时间。 + */ + private LocalDateTime planTime; + + /** + * 当前加工方案ID。 + */ + private Long processId; + + /** + * 当前加工方案名称。 + */ + private String processName; + + /** + * 上一次加工方案ID。 + */ + private Long previousProcessId; + + /** + * 来源贴皮库记录主键,对应 {@code veneers.id}。 + */ + private Long sourceVeneerId; + + /** + * 贴皮数量,A/B面分别计数。 + */ + private Integer veneerNum; + + /** + * 贴皮总面积,单位:平方米。 + */ + private BigDecimal veneerArea; + + /** + * 备注。 + */ + private String remark; + + /** + * 生产操作人。 + */ + private String operator; + + /** + * 开始生产时间。 + */ + private LocalDateTime produceTime; + + /** + * 完成时间。 + */ + private LocalDateTime finishTime; + + /** + * 乐观锁版本号。 + */ + private Integer version; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerPlanStatisticsDO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerPlanStatisticsDO.java new file mode 100644 index 000000000..391ef4dd6 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerPlanStatisticsDO.java @@ -0,0 +1,22 @@ +package com.cf.imes.module.executor.dal.dataobject.veneer; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 贴皮排单数量及面积聚合结果。 + */ +@Data +public class VeneerPlanStatisticsDO { + + /** + * 贴皮数量,A/B面分别计数。 + */ + private Integer veneerNum; + + /** + * 贴皮总面积,单位:平方米。 + */ + private BigDecimal veneerArea; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerPlannedPlateCountDO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerPlannedPlateCountDO.java new file mode 100644 index 000000000..53dcde68a --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerPlannedPlateCountDO.java @@ -0,0 +1,24 @@ +package com.cf.imes.module.executor.dal.dataobject.veneer; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 已完成贴皮排单的板件数量统计结果。 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class VeneerPlannedPlateCountDO { + + /** + * 订单ID或 {@code order_goods} 主键。 + */ + private Long targetId; + + /** + * 所有有效贴皮面均已排单的原始板件数量。 + */ + private Integer plannedVeneerPlateNum; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerDecomposeMapper.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerDecomposeMapper.java index b3f42ed42..ffe3b7917 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerDecomposeMapper.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerDecomposeMapper.java @@ -84,14 +84,6 @@ public interface VeneerDecomposeMapper { List selectUndecomposedPlateList(@Param("req") VeneerDecomposeCreateReqVO req, @Param("plateNos") List plateNos); - /** - * 按固定顺序锁定受影响订单,串行化同一订单的分解与计数重算。 - * - * @param orderIds 订单ID列表 - * @return 已锁定订单ID列表 - */ - List selectOrderIdForUpdate(@Param("orderIds") List orderIds); - /** * 将指定原板件标记为已分解。 * @@ -117,4 +109,5 @@ public interface VeneerDecomposeMapper { */ List selectGoodsDecomposedPlateCount( @Param("orderGoodsIds") List orderGoodsIds); + } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerManageMapper.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerManageMapper.java index c93ff0973..ce91f8a74 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerManageMapper.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerManageMapper.java @@ -1,11 +1,15 @@ package com.cf.imes.module.executor.dal.mysql.veneer; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateListReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateRespVO; +import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -50,13 +54,37 @@ public interface VeneerManageMapper { IPage selectGoodsPage(@Param("page") IPage page, @Param("req") VeneerManageGoodsPageReqVO req); + /** + * 分页查询存在待排单贴皮的根订单ID。 + * + * @param page 分页参数 + * @param req 订单查询条件 + * @param orderDateTime 完整订单日期时间范围 + * @return 根订单ID分页结果 + */ + IPage selectUnplannedRootOrderIdList( + @Param("page") IPage page, + @Param("req") VeneerManageOrderPageReqVO req, + @Param("orderDateTime") LocalDateTime[] orderDateTime); + + /** + * 分页查询存在待排单贴皮的板材。 + * + * @param page 分页参数 + * @param req 板材查询条件 + * @return 板材分页结果 + */ + IPage selectUnplannedGoodsPage( + @Param("page") IPage page, + @Param("req") VeneerManageGoodsPageReqVO req); + /** * 按订单查询已分解板件贴皮列表。 * - * @param orderIds 订单ID列表 + * @param req 订单、房间及柜体查询条件 * @return 已分解板件贴皮列表 */ - List selectOrderPlateList(@Param("orderIds") List orderIds); + List selectOrderPlateList(@Param("req") VeneerManagePlateListReqVO req); /** * 按板材商品编码查询已分解板件贴皮列表。 @@ -65,4 +93,46 @@ public interface VeneerManageMapper { * @return 已分解板件贴皮列表 */ List selectGoodsPlateList(@Param("goodsIds") List goodsIds); + + /** + * 按互斥业务范围查询待排单贴皮及其原板件信息。 + * 同一贴皮可能因多个加工组返回多条,由 Service 层合并。 + * + * @param req 订单或板材范围及房间、柜体筛选条件 + * @return 待排单贴皮及原板件原始列表 + */ + List selectUnplannedPlateList( + @Param("req") UnplannedVeneerPlateDataReqVO req); + + /** + * 查询本次已移除的板件贴皮记录。 + * + * @param ids 板件贴皮记录ID列表 + * @return 已移除板件贴皮记录 + */ + List selectRemovedPlateVeneerList(@Param("ids") List ids); + + /** + * 批量逻辑删除待排单板件贴皮记录。 + * + * @param ids 板件贴皮记录ID列表 + * @return 更新行数 + */ + int softDeletePendingPlateVeneer(@Param("ids") List ids); + + /** + * 查询原板件下仍然有效的贴皮记录。 + * + * @param plateIds 原板件ID列表 + * @return 仍存在有效贴皮记录的原板件ID列表 + */ + List selectRemainingPlateIdList(@Param("plateIds") List plateIds); + + /** + * 将已无有效贴皮记录的原板件恢复为未分解状态。 + * + * @param plateIds 原板件ID列表 + * @return 更新行数 + */ + int restorePlateUndecomposed(@Param("plateIds") List plateIds); } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerPlanMapper.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerPlanMapper.java new file mode 100644 index 000000000..dabf4fdda --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerPlanMapper.java @@ -0,0 +1,169 @@ +package com.cf.imes.module.executor.dal.mysql.veneer; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPlateRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortRespVO; +import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO; +import com.cf.imes.module.executor.dal.dataobject.veneer.OrderVeneerPlanDO; +import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlannedPlateCountDO; +import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlanStatisticsDO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 贴皮排单 Mapper。 + */ +@Mapper +public interface VeneerPlanMapper extends BaseMapperX { + + /** + * 分页查询贴皮排单及其关联的贴皮库信息。 + * + * @param page 分页参数 + * @param req 筛选条件 + * @return 贴皮排单分页数据 + */ + IPage selectPlanPage( + @Param("page") IPage page, + @Param("req") VeneerPlanPageReqVO req); + + /** + * 查询指定排单下的贴皮明细。 + * + * @param planId 贴皮排单ID + * @return 排单贴皮明细列表 + */ + List selectPlanPlateList(@Param("planId") Long planId); + + /** + * 查询尚未完成开料的贴皮排单优先级列表。 + * + * @param openedStatus 已开料状态 + * @return 按优先级升序排列的贴皮排单 + */ + List selectPlanSortList( + @Param("openedStatus") Integer openedStatus); + + /** + * 批量更新尚未完成开料的贴皮排单优先级。 + * + * @param sortList 排单ID及排序值列表 + * @param openedStatus 已开料状态 + * @return 更新行数 + */ + int batchUpdatePlanSort(@Param("sortList") List sortList, + @Param("openedStatus") Integer openedStatus); + + /** + * 查询属于指定排单且仍为已排单状态的贴皮记录。 + * + * @param ids 板件贴皮记录ID列表 + * @param planId 贴皮排单ID + * @return 符合条件的贴皮记录 + */ + List selectPlannedVeneerList( + @Param("ids") List ids, + @Param("planId") Long planId); + + /** + * 将指定贴皮记录从排单中恢复为待排单状态。 + * + * @param ids 板件贴皮记录ID列表 + * @param planId 贴皮排单ID + * @return 更新行数 + */ + int resetPlanVeneer(@Param("ids") List ids, + @Param("planId") Long planId); + + /** + * 根据排单当前关联贴皮重新统计数量和面积。 + * + * @param planId 贴皮排单ID + * @return 排单贴皮数量及面积 + */ + VeneerPlanStatisticsDO selectPlanStatistics(@Param("planId") Long planId); + + /** + * 按版本物理删除已无关联贴皮且未进入开料阶段的排单。 + * + * @param planId 贴皮排单ID + * @param openingStatus 开料中状态 + * @param openedStatus 已开料状态 + * @param version 当前版本号 + * @return 删除行数 + */ + int physicalDeleteEmptyPlan(@Param("planId") Long planId, + @Param("openingStatus") Integer openingStatus, + @Param("openedStatus") Integer openedStatus, + @Param("version") Integer version); + + /** + * 查询仍处于待排单状态的贴皮记录。 + * + * @param ids 板件贴皮记录ID列表 + * @return 可创建排单的贴皮记录 + */ + List selectPendingVeneerList(@Param("ids") List ids); + + /** + * 将一批贴皮记录关联到指定贴皮排单。 + * + * @param ids 板件贴皮记录ID列表 + * @param planId 贴皮排单ID + * @return 更新行数 + */ + int updateVeneerPlanned(@Param("ids") List ids, + @Param("planId") Long planId); + + /** + * 按订单重新统计所有有效贴皮面均已排单的原始板件数量。 + * + * @param orderIds 订单ID列表 + * @return 订单及其所有贴皮面均已排单的板件数量 + */ + List selectOrderPlannedVeneerPlateCount( + @Param("orderIds") List orderIds); + + /** + * 按订单材质重新统计所有有效贴皮面均已排单的原始板件数量。 + * + * @param orderGoodsIds {@code order_goods} 主键列表 + * @return 订单材质及其所有贴皮面均已排单的板件数量 + */ + List selectGoodsPlannedVeneerPlateCount( + @Param("orderGoodsIds") List orderGoodsIds); + + /** + * 查询待删除排单影响的订单和订单材质范围。 + * + * @param planIds 贴皮排单ID列表 + * @return 受影响的订单及订单材质 + */ + List selectPlanAffectedScope(@Param("planIds") List planIds); + + /** + * 将指定排单内的板件贴皮恢复为待排单状态。 + * + * @param planIds 贴皮排单ID列表 + * @return 更新行数 + */ + int resetVeneerPlan(@Param("planIds") List planIds); + + /** + * 物理删除指定状态的贴皮排单。 + * + * @param planIds 贴皮排单ID列表 + * @param openingStatus 开料中状态 + * @param openedStatus 已开料状态 + * @return 删除行数 + */ + int physicalDeletePlan(@Param("planIds") List planIds, + @Param("openingStatus") Integer openingStatus, + @Param("openedStatus") Integer openedStatus); +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanServiceImpl.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanServiceImpl.java index 1ac3a24d7..c94a046df 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanServiceImpl.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/plan/PlanServiceImpl.java @@ -523,7 +523,51 @@ public class PlanServiceImpl implements PlanService { @Override public List getPlanSortPage() { - return BeanUtil.copyToList(planMapper.selectPlanSortList(), PlanRespVO.class); + List plans = planMapper.selectPlanSortList(); + if (CollUtil.isEmpty(plans)) { + return List.of(); + } + + List planIds = plans.stream() + .map(PlanDO::getId) + .toList(); + Set orderIds = plans.stream() + .map(PlanDO::getOrderNos) + .filter(StringUtils::isNotBlank) + .flatMap(orderNos -> JSON.parseArray(orderNos) + .toJavaList(Long.class) + .stream()) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + // 与已排订单分页保持一致,一次查询全部排单的大板信息,避免逐排单查询产生N+1。 + List plateInfoList = CollUtil.isEmpty(orderIds) + ? List.of() + : planItemMapper.selectOrderPlanGoodsList(planIds, orderIds, getUserOrganId()); + + // 混单的大板来自实际选择板材,按商品编号批量加载后再映射回排单。 + Map planGoodsMap = new HashMap<>(); + List mixedPlans = plans.stream() + .filter(plan -> ObjectUtil.equals( + PlanTypeEnum.MIXEDORDERPLAN.getType(), plan.getType())) + .toList(); + if (CollUtil.isNotEmpty(mixedPlans)) { + List goodsList = plateGoodMapper.selectGoodsListByGoodsId( + mixedPlans.stream() + .map(PlanDO::getGoodsId) + .distinct() + .toList()); + Map goodsMap = goodsList.stream() + .collect(Collectors.toMap(PlateGoodDO::getGoodsId, Function.identity())); + mixedPlans.forEach(plan -> planGoodsMap.put( + plan.getId(), goodsMap.get(plan.getGoodsId()))); + } + + List result = BeanUtil.copyToList(plans, PlanRespVO.class); + result.forEach(plan -> { + plan.setPlateInfoList(getPlateInfoList(plan.getId(), plateInfoList)); + getMixedPlanGoodsInfo(plan, planGoodsMap); + }); + return result; } @@ -1947,4 +1991,4 @@ public class PlanServiceImpl implements PlanService { stringRedisTemplate.delete(lockKey); } } -} \ No newline at end of file +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerDecomposeService.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerDecomposeService.java index 2914a00f2..4b987ee9c 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerDecomposeService.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerDecomposeService.java @@ -6,8 +6,8 @@ import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeGoo import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeGoodsRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateDataRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlatePageReqVO; -import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO; import java.util.List; @@ -36,17 +36,17 @@ public interface VeneerDecomposeService { * 按订单查询未贴皮分解板件列表。 * * @param req 查询条件,必须包含订单 ID 列表 - * @return 去重后的板件列表 + * @return 去重后的板件及板件造型数据 */ - List getOrderPlateList(VeneerDecomposePlatePageReqVO req); + VeneerDecomposePlateDataRespVO getOrderPlateList(VeneerDecomposePlatePageReqVO req); /** * 按板材查询未贴皮分解板件列表。 * * @param req 查询条件,必须包含商品编码列表 - * @return 去重后的板件列表 + * @return 去重后的板件及板件造型数据 */ - List getGoodsPlateList(VeneerDecomposePlatePageReqVO req); + VeneerDecomposePlateDataRespVO getGoodsPlateList(VeneerDecomposePlatePageReqVO req); /** * 将原板件分解为 A 面和/或 B 面贴皮记录。 @@ -54,4 +54,12 @@ public interface VeneerDecomposeService { * @param req 分解参数 */ void decompose(VeneerDecomposeCreateReqVO req); + + /** + * 根据原板件当前分解状态重新统计订单和订单板材的已分解板件数量。 + * + * @param orderIds 订单ID列表 + * @param orderGoodsIds {@code order_goods} 主键列表 + */ + void refreshDecomposedPlateCount(List orderIds, List orderGoodsIds); } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerDecomposeServiceImpl.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerDecomposeServiceImpl.java index 4ead987b4..e56503be2 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerDecomposeServiceImpl.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerDecomposeServiceImpl.java @@ -2,21 +2,26 @@ package com.cf.imes.module.executor.service.veneer; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; +import com.cf.imes.framework.common.enums.PlatePlaceHoleEnum; import com.cf.imes.framework.common.exception.ServiceException; import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.util.date.LocalDateTimeUtils; +import com.cf.imes.framework.common.util.json.JsonUtils; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeCreateReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeGoodsPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeGoodsRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateDataRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlatePageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO; import com.cf.imes.module.executor.dal.dataobject.goods.GoodsDO; import com.cf.imes.module.executor.dal.dataobject.order.OrderDO; +import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderModelDO; import com.cf.imes.module.executor.dal.dataobject.plate.PlateDO; import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO; import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDecomposedPlateCountDO; @@ -26,6 +31,8 @@ import com.cf.imes.module.executor.dal.mysql.order.OrderMapper; import com.cf.imes.module.executor.dal.mysql.veneer.OrderPlateVeneerMapper; import com.cf.imes.module.executor.dal.mysql.veneer.VeneerDecomposeMapper; import com.cf.imes.module.executor.dal.mysql.veneer.VeneerMapper; +import com.cf.imes.module.executor.enums.VeneerPlateSurfaceEnum; +import com.cf.imes.module.executor.enums.VeneerSideEnum; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,6 +42,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -63,31 +71,6 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { */ private static final int DECOMPOSE_STAT_BATCH_SIZE = 100; - /** - * 贴皮逻辑 A 面。 - */ - private static final String VENEER_SIDE_A = "A"; - - /** - * 贴皮逻辑 B 面。 - */ - private static final String VENEER_SIDE_B = "B"; - - /** - * WebCAD 反面排版标识。 - */ - private static final int HOLE_ARRANGE_REVERSE = 1; - - /** - * 板件正面标识。 - */ - private static final int PLATE_SURFACE_FRONT = 0; - - /** - * 板件反面标识。 - */ - private static final int PLATE_SURFACE_BACK = 1; - @Resource private VeneerDecomposeMapper veneerDecomposeMapper; @@ -125,39 +108,91 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { } @Override - public List getOrderPlateList(VeneerDecomposePlatePageReqVO req) { + public VeneerDecomposePlateDataRespVO getOrderPlateList(VeneerDecomposePlatePageReqVO req) { if (CollUtil.isEmpty(req.getOrderIds())) { - return List.of(); + return buildPlateData(List.of()); } - return distinctPlate(veneerDecomposeMapper.selectOrderPlateList(req)); + return buildPlateData(veneerDecomposeMapper.selectOrderPlateList(req)); } @Override - public List getGoodsPlateList(VeneerDecomposePlatePageReqVO req) { + public VeneerDecomposePlateDataRespVO getGoodsPlateList(VeneerDecomposePlatePageReqVO req) { if (CollUtil.isEmpty(req.getGoodsIds())) { - return List.of(); + return buildPlateData(List.of()); } - return distinctPlate(veneerDecomposeMapper.selectGoodsPlateList(req)); + return buildPlateData(veneerDecomposeMapper.selectGoodsPlateList(req)); } /** - * 一块板可关联多个加工组,查询结果按板件主键保留第一条。 - * 房间、柜体是板件的唯一归属,因此同一板的多条加工组记录展示信息一致。 + * 合并板件重复记录,并从解压后的造型 JSON 生成板件造型列表。 + * + * @param rawPlates 数据库查询得到的板件原始列表 + * @return 板件及板件造型数据 + */ + private VeneerDecomposePlateDataRespVO buildPlateData(List rawPlates) { + List plates = mergePlateDetails(rawPlates); + List plateModels = plates.stream() + .map(VeneerDecomposePlateRespVO::getModelData) + .filter(StrUtil::isNotBlank) + .map(modelData -> JsonUtils.parseObject(modelData, OrderModelDO.class)) + .toList(); + return VeneerDecomposePlateDataRespVO.builder() + .plateList(plates) + .plateModels(plateModels) + .build(); + } + + /** + * 一块板可关联多个加工组,查询结果按板件主键合并。 + * 加工组名称去重后拼接,既满足前端板件筛选字段, + * 又保证一块板只返回一条记录。 * * @param plates 数据库查询得到的板件原始列表 - * @return 按板件主键去重后的列表 + * @return 按板件主键合并后的列表 */ - private List distinctPlate(List plates) { + private List mergePlateDetails(List plates) { if (CollUtil.isEmpty(plates)) { return plates; } Map plateMap = new LinkedHashMap<>(); + Map> groupNameMap = new HashMap<>(); for (VeneerDecomposePlateRespVO plate : plates) { plateMap.putIfAbsent(plate.getPlateId(), plate); + addName(groupNameMap, plate.getPlateId(), plate.getProcessGroupName()); + } + + for (Map.Entry entry : plateMap.entrySet()) { + Long plateId = entry.getKey(); + VeneerDecomposePlateRespVO plate = entry.getValue(); + plate.setProcessGroupName(joinNames(groupNameMap.get(plateId))); } return new ArrayList<>(plateMap.values()); } + /** + * 将非空名称加入指定板件的有序去重集合。 + * + * @param nameMap 板件名称集合映射 + * @param plateId 板件ID + * @param name 待合并名称 + */ + private void addName(Map> nameMap, Long plateId, String name) { + if (plateId == null || name == null || name.isBlank()) { + return; + } + nameMap.computeIfAbsent(plateId, key -> new LinkedHashSet<>()).add(name); + } + + /** + * 将有序名称集合拼接为前端展示和筛选值。 + * + * @param names 名称集合 + * @return 逗号分隔名称;无名称时返回 {@code null} + */ + private String joinNames(Set names) { + return CollUtil.isEmpty(names) ? null : String.join(",", names); + } + /** * 执行贴皮分解:一块原板按所选 A/B 面生成一条或两条贴皮记录,原板计数只增加一次。 * @@ -186,11 +221,8 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { throw new ServiceException(VENEER_DECOMPOSE_PLATE_STATUS_CHANGED); } - // 先锁定受影响订单,避免同一订单的并发分解互相覆盖重新统计的结果。 + // 汇总受影响订单,分解完成后以数据库当前状态重新统计已分解板件数量。 List affectedOrderIds = plates.stream().map(PlateDO::getOrderId).distinct().sorted().toList(); - for (List orderIdBatch : CollUtil.split(affectedOrderIds, DECOMPOSE_BATCH_SIZE)) { - veneerDecomposeMapper.selectOrderIdForUpdate(orderIdBatch); - } // 从贴皮库加载 A/B 面贴皮,并将贴皮属性以快照形式写入板件贴皮表。 Map sourceVeneerMap = loadSourceVeneers(req); @@ -198,11 +230,13 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { for (PlateDO plate : plates) { if (req.getAVeneerId() != null) { plateVeneers.add(buildPlateVeneer( - plate, sourceVeneerMap.get(req.getAVeneerId()), VENEER_SIDE_A, req.getOuterMargin())); + plate, sourceVeneerMap.get(req.getAVeneerId()), + VeneerSideEnum.A, req.getOuterMargin())); } if (req.getBVeneerId() != null) { plateVeneers.add(buildPlateVeneer( - plate, sourceVeneerMap.get(req.getBVeneerId()), VENEER_SIDE_B, req.getOuterMargin())); + plate, sourceVeneerMap.get(req.getBVeneerId()), + VeneerSideEnum.B, req.getOuterMargin())); } } @@ -222,11 +256,22 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { orderPlateVeneerMapper.insertBatch(plateVeneerBatch); } - // 以 order_plate 当前状态为准重新统计,不使用本次请求数量做增量计算。 + // 以 order_plate 当前状态重新统计,不使用本次请求数量做增量计算。 List affectedOrderGoodsIds = plates.stream() .map(PlateDO::getGoodsId).distinct().sorted().toList(); - refreshOrderDecomposedPlateCount(affectedOrderIds); - refreshGoodsDecomposedPlateCount(affectedOrderGoodsIds); + refreshDecomposedPlateCount(affectedOrderIds, affectedOrderGoodsIds); + } + + /** + * 根据原板件当前分解状态重新统计订单和订单板材的已分解板件数量。 + * + * @param orderIds 订单ID列表 + * @param orderGoodsIds {@code order_goods} 主键列表 + */ + @Override + public void refreshDecomposedPlateCount(List orderIds, List orderGoodsIds) { + refreshOrderDecomposedPlateCount(orderIds); + refreshGoodsDecomposedPlateCount(orderGoodsIds); } /** @@ -245,7 +290,6 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { List databaseCounts = veneerDecomposeMapper.selectOrderDecomposedPlateCount(orderIdBatch); Map countMap = convertDecomposedPlateCount(databaseCounts); - for (Long orderId : orderIdBatch) { OrderDO order = orderMap.get(orderId); if (order == null) { @@ -280,7 +324,6 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { List databaseCounts = veneerDecomposeMapper.selectGoodsDecomposedPlateCount(orderGoodsIdBatch); Map countMap = convertDecomposedPlateCount(databaseCounts); - for (Long orderGoodsId : orderGoodsIdBatch) { GoodsDO goods = goodsMap.get(orderGoodsId); if (goods == null) { @@ -347,12 +390,12 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { * * @param plate 原板件 * @param sourceVeneer 选中的贴皮库数据 - * @param veneerSide 贴皮逻辑面 + * @param veneerSide 贴皮逻辑面枚举 * @param outerMargin 外扩余量 * @return 待保存的板件贴皮记录 */ private OrderPlateVeneerDO buildPlateVeneer(PlateDO plate, VeneerDO sourceVeneer, - String veneerSide, BigDecimal outerMargin) { + VeneerSideEnum veneerSide, BigDecimal outerMargin) { int plateSurface = resolvePlateSurface(plate.getHoleArrange(), veneerSide); return OrderPlateVeneerDO.builder() .organId(plate.getOrganId()) @@ -365,10 +408,11 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { .plateWidth(plate.getWidth()) .plateThickness(plate.getThickness()) .holeArrange(plate.getHoleArrange()) - .veneerSide(veneerSide) + .veneerSide(veneerSide.getSide()) .plateSurface(plateSurface) - .veneerNo(plate.getPlateNo() + veneerSide) + .veneerNo(plate.getPlateNo() + veneerSide.getSide()) .outerMargin(outerMargin) + .planId(0L) .sourceVeneerId(sourceVeneer.getId()) .sourceVeneerNo(sourceVeneer.getVeneerNo()) .veneerName(sourceVeneer.getVeneerName()) @@ -383,14 +427,18 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService { * 反面排版时 A 面对应反面、B 面对应正面;正面或随意面排版时关系相反。 * * @param holeArrange WebCAD 排版面 - * @param veneerSide 贴皮逻辑面 + * @param veneerSide 贴皮逻辑面枚举 * @return 实际板件面 */ - private int resolvePlateSurface(Integer holeArrange, String veneerSide) { - boolean reverseArrange = Integer.valueOf(HOLE_ARRANGE_REVERSE).equals(holeArrange); - if (VENEER_SIDE_A.equals(veneerSide)) { - return reverseArrange ? PLATE_SURFACE_BACK : PLATE_SURFACE_FRONT; + private int resolvePlateSurface(Integer holeArrange, VeneerSideEnum veneerSide) { + boolean reverseArrange = PlatePlaceHoleEnum.BACK.equals(holeArrange); + if (VeneerSideEnum.A == veneerSide) { + return reverseArrange + ? VeneerPlateSurfaceEnum.BACK.getSurface() + : VeneerPlateSurfaceEnum.FRONT.getSurface(); } - return reverseArrange ? PLATE_SURFACE_FRONT : PLATE_SURFACE_BACK; + return reverseArrange + ? VeneerPlateSurfaceEnum.FRONT.getSurface() + : VeneerPlateSurfaceEnum.BACK.getSurface(); } } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerManageService.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerManageService.java index 003971bbd..0f4193128 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerManageService.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerManageService.java @@ -1,12 +1,15 @@ package com.cf.imes.module.executor.service.veneer; import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateListReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageRemoveReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageReselectReqVO; import java.util.List; @@ -32,10 +35,26 @@ public interface VeneerManageService { */ PageResult getGoodsPage(VeneerManageGoodsPageReqVO req); + /** + * 分页查询存在待排单贴皮的订单。 + * + * @param req 订单查询条件 + * @return 未排贴皮订单分页结果 + */ + PageResult getUnplannedOrderPage(VeneerManageOrderPageReqVO req); + + /** + * 分页查询存在待排单贴皮的板材。 + * + * @param req 板材查询条件 + * @return 未排贴皮板材分页结果 + */ + PageResult getUnplannedGoodsPage(VeneerManageGoodsPageReqVO req); + /** * 按订单查询已分解板件贴皮列表。 * - * @param req 查询条件,必须包含订单ID列表 + * @param req 查询条件,必须包含订单ID列表,可按房间和柜体筛选 * @return 已分解板件贴皮列表 */ List getOrderPlateList(VeneerManagePlateListReqVO req); @@ -48,10 +67,25 @@ public interface VeneerManageService { */ List getGoodsPlateList(VeneerManagePlateListReqVO req); + /** + * 按订单或板材查询未排贴皮、原板件及板件造型数据。 + * + * @param req 互斥查询范围及可选房间、柜体条件 + * @return 未排贴皮排单基础数据 + */ + UnplannedVeneerPlateDataRespVO getUnplannedPlateData(UnplannedVeneerPlateDataReqVO req); + /** * 批量为已分解板件贴皮重新选择贴皮库数据。 * * @param req 板件贴皮记录ID及新贴皮库ID */ void reselectVeneer(VeneerManageReselectReqVO req); + + /** + * 批量移除待排单板件贴皮,并在原板件无剩余有效贴皮时恢复其分解状态。 + * + * @param req 待移除的板件贴皮记录ID列表 + */ + void removeVeneer(VeneerManageRemoveReqVO req); } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerManageServiceImpl.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerManageServiceImpl.java index 1163548ea..509b30b3f 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerManageServiceImpl.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerManageServiceImpl.java @@ -2,6 +2,7 @@ package com.cf.imes.module.executor.service.veneer; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -9,15 +10,23 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; import com.cf.imes.framework.common.exception.ServiceException; import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.util.date.LocalDateTimeUtils; +import com.cf.imes.framework.common.util.json.JsonUtils; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateListReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageRemoveReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageReselectReqVO; +import com.cf.imes.module.executor.dal.dataobject.order.OrderDO; +import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderModelDO; import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO; import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO; +import com.cf.imes.module.executor.dal.mysql.order.OrderMapper; import com.cf.imes.module.executor.dal.mysql.veneer.OrderPlateVeneerMapper; import com.cf.imes.module.executor.dal.mysql.veneer.VeneerManageMapper; import com.cf.imes.module.executor.dal.mysql.veneer.VeneerMapper; @@ -25,14 +34,24 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; import static com.cf.imes.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_NOT_EXIST; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_REMOVE_RECORD_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_REMOVE_RECORD_STATUS_CHANGED; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_RESELECT_RECORD_REQUIRED; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_RESELECT_RECORD_STATUS_CHANGED; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_RESELECT_SOURCE_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_UNPLANNED_SCOPE_INVALID; /** * 贴皮管理 Service 实现。 @@ -45,6 +64,16 @@ public class VeneerManageServiceImpl implements VeneerManageService { */ private static final int RESELECT_BATCH_SIZE = 1000; + /** + * 移除贴皮查询及更新的单批处理数量。 + */ + private static final int REMOVE_BATCH_SIZE = 1000; + + /** + * 未排贴皮关联订单的单批查询数量。 + */ + private static final int UNPLANNED_ORDER_BATCH_SIZE = 100; + @Resource private VeneerManageMapper veneerManageMapper; @@ -54,6 +83,12 @@ public class VeneerManageServiceImpl implements VeneerManageService { @Resource private VeneerMapper veneerMapper; + @Resource + private VeneerDecomposeService veneerDecomposeService; + + @Resource + private OrderMapper orderMapper; + @Override public PageResult getOrderPage(VeneerManageOrderPageReqVO req) { PageDTO page = new PageDTO<>(req.getPageNo(), req.getPageSize()); @@ -75,12 +110,33 @@ public class VeneerManageServiceImpl implements VeneerManageService { return new PageResult<>(result.getRecords(), result.getTotal()); } + @Override + public PageResult getUnplannedOrderPage(VeneerManageOrderPageReqVO req) { + PageDTO page = new PageDTO<>(req.getPageNo(), req.getPageSize()); + IPage rootPage = veneerManageMapper.selectUnplannedRootOrderIdList( + page, req, LocalDateTimeUtils.generateTimeSection(req.getOrderDate())); + List rootIds = rootPage.getRecords(); + if (CollUtil.isEmpty(rootIds)) { + return new PageResult<>(); + } + IPage result = veneerManageMapper.selectOrderList( + new PageDTO<>(req.getPageNo(), PAGE_SIZE_NONE), rootIds); + return new PageResult<>(result.getRecords(), rootPage.getTotal()); + } + + @Override + public PageResult getUnplannedGoodsPage(VeneerManageGoodsPageReqVO req) { + IPage result = veneerManageMapper.selectUnplannedGoodsPage( + new PageDTO<>(req.getPageNo(), req.getPageSize()), req); + return new PageResult<>(result.getRecords(), result.getTotal()); + } + @Override public List getOrderPlateList(VeneerManagePlateListReqVO req) { if (CollUtil.isEmpty(req.getOrderIds())) { return List.of(); } - return veneerManageMapper.selectOrderPlateList(req.getOrderIds()); + return veneerManageMapper.selectOrderPlateList(req); } @Override @@ -91,6 +147,128 @@ public class VeneerManageServiceImpl implements VeneerManageService { return veneerManageMapper.selectGoodsPlateList(req.getGoodsIds()); } + @Override + public UnplannedVeneerPlateDataRespVO getUnplannedPlateData(UnplannedVeneerPlateDataReqVO req) { + boolean byOrder = CollUtil.isNotEmpty(req.getOrderIds()); + boolean byGoods = CollUtil.isNotEmpty(req.getGoodsIds()); + if (byOrder == byGoods) { + throw new ServiceException(VENEER_UNPLANNED_SCOPE_INVALID); + } + + // 一次查询贴皮和原板件信息;A/B 面分别保留,加工组重复行在内存中合并。 + List plates = mergeUnplannedPlateDetails( + veneerManageMapper.selectUnplannedPlateList(req)); + if (CollUtil.isEmpty(plates)) { + return buildEmptyUnplannedPlateData(); + } + + // A/B 面共享同一份原板造型,按原板件ID只解析并返回一次。 + Map modelMap = new LinkedHashMap<>(); + for (UnplannedVeneerPlateRespVO plate : plates) { + if (plate.getPlateId() != null && StrUtil.isNotBlank(plate.getModelData()) + && !modelMap.containsKey(plate.getPlateId())) { + modelMap.put(plate.getPlateId(), + JsonUtils.parseObject(plate.getModelData(), OrderModelDO.class)); + } + } + List plateModels = new ArrayList<>(modelMap.values()); + + // 订单数量远小于板件数量,按小批次加载,避免一次生成过长的 IN 条件。 + List orderIds = plates.stream() + .map(UnplannedVeneerPlateRespVO::getOrderId) + .distinct() + .sorted() + .toList(); + List orders = new ArrayList<>(); + for (List orderIdBatch : CollUtil.split(orderIds, UNPLANNED_ORDER_BATCH_SIZE)) { + orders.addAll(orderMapper.selectBatchIds(orderIdBatch)); + } + + return UnplannedVeneerPlateDataRespVO.builder() + .orderList(buildOrderTree(orders)) + .plateList(plates) + .plateModels(plateModels) + .build(); + } + + /** + * 构造各数据段均为空集合的未排贴皮响应。 + * + * @return 空的未排贴皮排单基础数据 + */ + private UnplannedVeneerPlateDataRespVO buildEmptyUnplannedPlateData() { + return UnplannedVeneerPlateDataRespVO.builder() + .orderList(List.of()) + .plateList(List.of()) + .plateModels(List.of()) + .build(); + } + + /** + * 合并同一贴皮因多个加工组产生的重复记录。 + * + * @param rawPlates 贴皮及原板件原始查询结果 + * @return 按板件贴皮记录ID合并后的列表 + */ + private List mergeUnplannedPlateDetails( + List rawPlates) { + if (CollUtil.isEmpty(rawPlates)) { + return rawPlates; + } + Map plateMap = new LinkedHashMap<>(); + Map> groupNameMap = new HashMap<>(); + for (UnplannedVeneerPlateRespVO plate : rawPlates) { + plateMap.putIfAbsent(plate.getId(), plate); + if (plate.getId() != null && StrUtil.isNotBlank(plate.getProcessGroupName())) { + groupNameMap.computeIfAbsent(plate.getId(), key -> new LinkedHashSet<>()) + .add(plate.getProcessGroupName()); + } + } + plateMap.forEach((id, plate) -> { + Set groupNames = groupNameMap.get(id); + plate.setProcessGroupName(CollUtil.isEmpty(groupNames) ? null : String.join(",", groupNames)); + }); + return new ArrayList<>(plateMap.values()); + } + + /** + * 将所选订单整理为父子结构;缺少父订单的子单作为顶层数据保留。 + * + * @param orders 所选贴皮关联的订单 + * @return 订单父子结构 + */ + private List buildOrderTree(List orders) { + if (CollUtil.isEmpty(orders)) { + return List.of(); + } + Map orderMap = new LinkedHashMap<>(); + for (OrderDO order : orders) { + orderMap.put(order.getId(), order); + } + List result = new ArrayList<>(); + for (OrderDO order : orders) { + if (OrderDO.PARENT_ID_ROOT.equals(order.getParentId())) { + order.setChild(new ArrayList<>()); + result.add(order); + } + } + for (OrderDO order : orders) { + if (OrderDO.PARENT_ID_ROOT.equals(order.getParentId())) { + continue; + } + OrderDO parent = orderMap.get(order.getParentId()); + if (parent == null) { + result.add(order); + continue; + } + if (parent.getChild() == null) { + parent.setChild(new ArrayList<>()); + } + parent.getChild().add(order); + } + return result; + } + /** * 批量重选贴皮,仅替换所选 A/B 面记录的贴皮库关联及贴皮属性快照。 * @@ -122,12 +300,11 @@ public class VeneerManageServiceImpl implements VeneerManageService { } for (List idBatch : CollUtil.split(ids, RESELECT_BATCH_SIZE)) { - // 锁定本批有效记录,避免重选过程中记录被移除。 + // 查询本批有效记录,提前校验所选数据是否已删除或发生变化。 List records = orderPlateVeneerMapper.selectList( new LambdaQueryWrapper() .select(OrderPlateVeneerDO::getId) - .in(OrderPlateVeneerDO::getId, idBatch) - .last("FOR UPDATE")); + .in(OrderPlateVeneerDO::getId, idBatch)); if (records.size() != idBatch.size()) { throw new ServiceException(VENEER_RESELECT_RECORD_STATUS_CHANGED); } @@ -149,4 +326,85 @@ public class VeneerManageServiceImpl implements VeneerManageService { } } } + + /** + * 批量移除待排单板件贴皮。 + * 关键更新均校验影响行数,任意一批数量不一致时整笔事务回滚。 + * + * @param req 批量移除参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void removeVeneer(VeneerManageRemoveReqVO req) { + List ids = normalizeRemoveIds(req); + + // 删除条件包含待排单及未删除状态;更新数量不一致表示数据状态已发生变化。 + for (List idBatch : CollUtil.split(ids, REMOVE_BATCH_SIZE)) { + int deleted = veneerManageMapper.softDeletePendingPlateVeneer(idBatch); + if (deleted != idBatch.size()) { + throw new ServiceException(VENEER_REMOVE_RECORD_STATUS_CHANGED); + } + } + + // 更新成功后批量读取本次移除记录的归属信息,用于判断原板是否需要恢复。 + List selectedRecords = new ArrayList<>(); + for (List idBatch : CollUtil.split(ids, REMOVE_BATCH_SIZE)) { + selectedRecords.addAll(veneerManageMapper.selectRemovedPlateVeneerList(idBatch)); + } + + List plateIds = selectedRecords.stream() + .map(OrderPlateVeneerDO::getOrderPlateId) + .distinct() + .sorted() + .toList(); + + // 批量查询删除后仍存在有效 A/B 面的原板件,不逐板执行数据库查询。 + Set remainingPlateIds = new HashSet<>(); + for (List plateIdBatch : CollUtil.split(plateIds, REMOVE_BATCH_SIZE)) { + remainingPlateIds.addAll(veneerManageMapper.selectRemainingPlateIdList(plateIdBatch)); + } + List restoredPlateIds = plateIds.stream() + .filter(plateId -> !remainingPlateIds.contains(plateId)) + .toList(); + if (CollUtil.isEmpty(restoredPlateIds)) { + return; + } + + // 仅当原板已无任何有效贴皮面时恢复为未分解,并校验每批更新数量。 + for (List plateIdBatch : CollUtil.split(restoredPlateIds, REMOVE_BATCH_SIZE)) { + int restored = veneerManageMapper.restorePlateUndecomposed(plateIdBatch); + if (restored != plateIdBatch.size()) { + throw new ServiceException(VENEER_REMOVE_RECORD_STATUS_CHANGED); + } + } + + List affectedOrderIds = selectedRecords.stream() + .map(OrderPlateVeneerDO::getOrderId) + .distinct() + .sorted() + .toList(); + List affectedOrderGoodsIds = selectedRecords.stream() + .map(OrderPlateVeneerDO::getOrderGoodsId) + .distinct() + .sorted() + .toList(); + + // 以 order_plate 当前状态重新统计,不使用删除贴皮记录数进行增量扣减。 + veneerDecomposeService.refreshDecomposedPlateCount( + affectedOrderIds, affectedOrderGoodsIds); + } + + /** + * 校验并标准化待移除记录ID。 + * + * @param req 批量移除参数 + * @return 去重并升序排列的记录ID列表 + */ + private List normalizeRemoveIds(VeneerManageRemoveReqVO req) { + if (req == null || CollUtil.isEmpty(req.getIds()) + || req.getIds().stream().anyMatch(Objects::isNull)) { + throw new ServiceException(VENEER_REMOVE_RECORD_REQUIRED); + } + return req.getIds().stream().distinct().sorted().toList(); + } } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerPlanService.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerPlanService.java new file mode 100644 index 000000000..df25e3f78 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerPlanService.java @@ -0,0 +1,101 @@ +package com.cf.imes.module.executor.service.veneer; + +import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanAddPlateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanCreateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPlateRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanProduceReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanRemovePlateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanUpdateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanWithdrawProduceReqVO; + +import java.util.List; + +/** + * 贴皮排单 Service。 + */ +public interface VeneerPlanService { + + /** + * 按前端提交的贴皮商品编号分组批量创建贴皮排单,每个分组生成一个排单。 + * + * @param req 创建贴皮排单参数 + */ + void createPlan(VeneerPlanCreateReqVO req); + + /** + * 更新未进入开料阶段的贴皮排单加工方案。 + * + * @param req 排单ID及加工方案 + */ + void updatePlan(VeneerPlanUpdateReqVO req); + + /** + * 将贴皮排单投入生产。 + * + * @param req 待投入生产的排单ID + */ + void producePlan(VeneerPlanProduceReqVO req); + + /** + * 撤回贴皮排单的投入生产状态。 + * + * @param req 待撤回生产的排单ID + */ + void withdrawProduce(VeneerPlanWithdrawProduceReqVO req); + + /** + * 向未进入开料阶段的贴皮排单新增贴皮记录。 + * + * @param req 目标排单及待加入贴皮记录 + */ + void addPlanPlate(VeneerPlanAddPlateReqVO req); + + /** + * 从未进入开料阶段的贴皮排单移除贴皮记录。 + * + * @param req 目标排单及待移除贴皮记录 + */ + void removePlanPlate(VeneerPlanRemovePlateReqVO req); + + /** + * 批量删除未进入开料阶段的贴皮排单,并将排单内贴皮恢复为待排单状态。 + * + * @param planIds 贴皮排单ID列表 + */ + void deletePlan(List planIds); + + /** + * 分页查询已创建的贴皮排单。 + * + * @param req 分页及筛选条件 + * @return 贴皮排单分页结果 + */ + PageResult getPlanPage(VeneerPlanPageReqVO req); + + /** + * 查询指定贴皮排单下的全部贴皮明细。 + * + * @param planId 贴皮排单ID + * @return 排单贴皮明细列表 + */ + List getPlanPlateList(Long planId); + + /** + * 查询未完成开料的贴皮排单优先级列表。 + * + * @return 贴皮排单优先级列表 + */ + List getPlanSortPage(); + + /** + * 批量设置未完成开料贴皮排单的优先级。 + * + * @param req 排单ID及排序值列表 + */ + void setPlanSort(List req); +} 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 new file mode 100644 index 000000000..671ea96b2 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerPlanServiceImpl.java @@ -0,0 +1,820 @@ +package com.cf.imes.module.executor.service.veneer; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; +import com.cf.imes.framework.common.exception.ServiceException; +import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.framework.security.core.LoginUser; +import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanAddPlateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanCreateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanGroupReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPlateRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanProduceReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanRemovePlateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortRespVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanUpdateReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanWithdrawProduceReqVO; +import com.cf.imes.module.executor.dal.dataobject.goods.GoodsDO; +import com.cf.imes.module.executor.dal.dataobject.order.OrderDO; +import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO; +import com.cf.imes.module.executor.dal.dataobject.veneer.OrderVeneerPlanDO; +import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO; +import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlannedPlateCountDO; +import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlanStatisticsDO; +import com.cf.imes.module.executor.dal.mysql.goods.GoodsMapper; +import com.cf.imes.module.executor.dal.mysql.order.OrderMapper; +import com.cf.imes.module.executor.dal.mysql.veneer.VeneerPlanMapper; +import com.cf.imes.module.executor.dal.mysql.veneer.VeneerMapper; +import com.cf.imes.module.executor.enums.VeneerPlanStatusEnum; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.GOODS_PLATE_UPDATE_CONCURRENCY_ERROR; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_PLATE_UPDATE_CONCURRENCY_ERROR; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_AREA_DATA_INVALID; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_CREATE_FAILED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_DELETE_LIMIT_EXCEEDED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_DELETE_NOT_ALLOWED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_DELETE_NOT_EXIST; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_DELETE_RECORD_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_DELETE_STATUS_CHANGED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_GROUP_DUPLICATE; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_GROUP_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_GROUP_SOURCE_NO_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_NOT_EXIST; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_OPERATION_STATUS_CHANGED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_PLATE_RECORD_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_PLATE_SOURCE_MISMATCH; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_PLATE_STATUS_CHANGED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_PROCESS_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_RECORD_DUPLICATE; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_RECORD_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_RECORD_STATUS_CHANGED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_SORT_RECORD_DUPLICATE; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_SORT_RECORD_REQUIRED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_SORT_STATUS_CHANGED; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_SOURCE_NO_DUPLICATE; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_SOURCE_NO_MISMATCH; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PLAN_SOURCE_NO_NOT_EXIST; + +/** + * 贴皮排单 Service 实现。 + */ +@Service +public class VeneerPlanServiceImpl implements VeneerPlanService { + + /** + * 贴皮记录查询和更新的单批处理数量。 + */ + private static final int PLAN_BATCH_SIZE = 1000; + + /** + * 贴皮排单批量插入数量。 + */ + private static final int PLAN_INSERT_BATCH_SIZE = 100; + + /** + * 订单及订单材质贴皮数量统计的单批处理数量。 + */ + private static final int PLAN_STAT_BATCH_SIZE = 100; + + /** + * 单次允许删除的贴皮排单最大数量。 + */ + private static final int PLAN_DELETE_LIMIT = 20; + + /** + * 贴皮排单优先级单批更新数量。 + */ + private static final int PLAN_SORT_BATCH_SIZE = 500; + + /** + * 允许修改加工方案及贴皮明细的排单状态。 + */ + private static final List PLAN_EDITABLE_STATUSES = List.of( + VeneerPlanStatusEnum.NOCUTTING.getStatus(), + VeneerPlanStatusEnum.OPTIMIZED.getStatus()); + + /** + * 平方毫米转换为平方米的除数。 + */ + private static final BigDecimal SQUARE_MILLIMETER_PER_SQUARE_METER = new BigDecimal("1000000"); + + /** + * 外扩余量需要向长、宽的两侧分别增加。 + */ + private static final BigDecimal OUTER_MARGIN_DOUBLE = new BigDecimal("2"); + + @Resource + private VeneerPlanMapper veneerPlanMapper; + + @Resource + private IdentifierGenerator identifierGenerator; + + @Resource + private OrderMapper orderMapper; + + @Resource + private GoodsMapper goodsMapper; + + @Resource + private VeneerMapper veneerMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createPlan(VeneerPlanCreateReqVO req) { + Map> groupIdMap = normalizeGroups(req.getGroups()); + Map sourceVeneerIdMap = loadSourceVeneerIdMap(groupIdMap.keySet()); + List veneerIds = groupIdMap.values().stream().flatMap(List::stream).toList(); + + // 将所有分组ID统一分批查询,避免逐组查询产生N+1,同时控制IN条件长度。 + List veneers = new ArrayList<>(); + for (List idBatch : CollUtil.split(veneerIds, PLAN_BATCH_SIZE)) { + veneers.addAll(veneerPlanMapper.selectPendingVeneerList(idBatch)); + } + if (veneers.size() != veneerIds.size()) { + throw new ServiceException(VENEER_PLAN_RECORD_STATUS_CHANGED); + } + + Map veneerMap = new HashMap<>(veneers.size()); + for (OrderPlateVeneerDO veneer : veneers) { + veneerMap.put(veneer.getId(), veneer); + } + + LoginUser loginUser = Objects.requireNonNull(SecurityFrameworkUtils.getLoginUser()); + LocalDateTime planTime = LocalDateTime.now(); + long sort = System.currentTimeMillis(); + List plans = new ArrayList<>(groupIdMap.size()); + + // 前端已按贴皮商品编号分组;后端逐组校验归属,并为每个分组创建一个排单。 + for (Map.Entry> entry : groupIdMap.entrySet()) { + String sourceVeneerNo = entry.getKey(); + Long sourceVeneerId = sourceVeneerIdMap.get(sourceVeneerNo); + List groupIds = entry.getValue(); + List group = new ArrayList<>(groupIds.size()); + for (Long veneerId : groupIds) { + OrderPlateVeneerDO veneer = veneerMap.get(veneerId); + if (veneer == null) { + throw new ServiceException(VENEER_PLAN_RECORD_STATUS_CHANGED); + } + if (!StrUtil.equals(sourceVeneerNo, veneer.getSourceVeneerNo())) { + throw new ServiceException( + VENEER_PLAN_SOURCE_NO_MISMATCH, veneerId, sourceVeneerNo); + } + group.add(veneer); + } + + Long planId = (Long) identifierGenerator.nextId(null); + + // 更新时再次限制 plan_id=0,并校验影响行数,阻止并发重复排单。 + for (List idBatch : CollUtil.split(groupIds, PLAN_BATCH_SIZE)) { + int updated = veneerPlanMapper.updateVeneerPlanned(idBatch, planId); + if (updated != idBatch.size()) { + throw new ServiceException(VENEER_PLAN_RECORD_STATUS_CHANGED); + } + } + + plans.add(OrderVeneerPlanDO.builder() + .id(planId) + .organId(loginUser.getOrganId()) + .status(VeneerPlanStatusEnum.NOCUTTING.getStatus()) + .isScheduled(false) + .sort(++sort) + .planTime(planTime) + .sourceVeneerId(sourceVeneerId) + .veneerNum(group.size()) + .veneerArea(calculateVeneerArea(group)) + .operator(loginUser.getNickname()) + .version(0) + .build()); + } + + if (Boolean.FALSE.equals(veneerPlanMapper.insertBatch(plans, PLAN_INSERT_BATCH_SIZE))) { + throw new ServiceException(VENEER_PLAN_CREATE_FAILED); + } + + // 排单完成后以数据库当前状态重新统计,避免使用本次请求数量增量更新导致计数漂移。 + List affectedOrderIds = veneers.stream() + .map(OrderPlateVeneerDO::getOrderId) + .distinct() + .sorted() + .toList(); + List affectedOrderGoodsIds = veneers.stream() + .map(OrderPlateVeneerDO::getOrderGoodsId) + .distinct() + .sorted() + .toList(); + refreshPlannedVeneerPlateCount(affectedOrderIds, affectedOrderGoodsIds); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePlan(VeneerPlanUpdateReqVO req) { + if (req == null || req.getProcessId() == null) { + throw new ServiceException(VENEER_PLAN_PROCESS_REQUIRED); + } + OrderVeneerPlanDO plan = validatePlanStatuses(req.getId(), PLAN_EDITABLE_STATUSES); + int version = plan.getVersion(); + boolean processChanged = !Objects.equals(plan.getProcessId(), req.getProcessId()); + + int updated = veneerPlanMapper.update(new LambdaUpdateWrapper() + .eq(OrderVeneerPlanDO::getId, plan.getId()) + .in(OrderVeneerPlanDO::getStatus, PLAN_EDITABLE_STATUSES) + .eq(OrderVeneerPlanDO::getVersion, version) + .set(processChanged, OrderVeneerPlanDO::getPreviousProcessId, + plan.getProcessId()) + .set(OrderVeneerPlanDO::getProcessId, req.getProcessId()) + .set(OrderVeneerPlanDO::getProcessName, req.getProcessName()) + .set(OrderVeneerPlanDO::getVersion, version + 1)); + if (updated != 1) { + throw new ServiceException(VENEER_PLAN_OPERATION_STATUS_CHANGED, plan.getId()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void producePlan(VeneerPlanProduceReqVO req) { + Long planId = req == null ? null : req.getId(); + OrderVeneerPlanDO plan = validatePlanExists(planId); + if (Boolean.TRUE.equals(plan.getIsScheduled())) { + throw new ServiceException(VENEER_PLAN_OPERATION_STATUS_CHANGED, planId); + } + int version = plan.getVersion(); + + int updated = veneerPlanMapper.update(new LambdaUpdateWrapper() + .eq(OrderVeneerPlanDO::getId, planId) + .eq(OrderVeneerPlanDO::getIsScheduled, false) + .eq(OrderVeneerPlanDO::getVersion, version) + .set(OrderVeneerPlanDO::getIsScheduled, true) + .set(OrderVeneerPlanDO::getVersion, version + 1)); + if (updated != 1) { + throw new ServiceException(VENEER_PLAN_OPERATION_STATUS_CHANGED, planId); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void withdrawProduce(VeneerPlanWithdrawProduceReqVO req) { + Long planId = req == null ? null : req.getId(); + OrderVeneerPlanDO plan = validatePlanExists(planId); + if (!Boolean.TRUE.equals(plan.getIsScheduled()) + || VeneerPlanStatusEnum.OPENING.equals(plan.getStatus()) + || VeneerPlanStatusEnum.OPENED.equals(plan.getStatus())) { + throw new ServiceException(VENEER_PLAN_OPERATION_STATUS_CHANGED, planId); + } + int version = plan.getVersion(); + + int updated = veneerPlanMapper.update(new LambdaUpdateWrapper() + .eq(OrderVeneerPlanDO::getId, planId) + .eq(OrderVeneerPlanDO::getIsScheduled, true) + .notIn(OrderVeneerPlanDO::getStatus, + VeneerPlanStatusEnum.OPENING.getStatus(), + VeneerPlanStatusEnum.OPENED.getStatus()) + .eq(OrderVeneerPlanDO::getVersion, version) + .set(OrderVeneerPlanDO::getIsScheduled, false) + .set(OrderVeneerPlanDO::getVersion, version + 1)); + if (updated != 1) { + throw new ServiceException(VENEER_PLAN_OPERATION_STATUS_CHANGED, planId); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void addPlanPlate(VeneerPlanAddPlateReqVO req) { + Long planId = req == null ? null : req.getPlanId(); + OrderVeneerPlanDO plan = validatePlanStatuses(planId, PLAN_EDITABLE_STATUSES); + List veneerIds = normalizePlanVeneerIds( + req == null ? null : req.getVeneerIds()); + + // 分批加载全部待排贴皮,避免大IN条件,并校验贴皮商品与目标排单一致。 + List veneers = new ArrayList<>(); + for (List idBatch : CollUtil.split(veneerIds, PLAN_BATCH_SIZE)) { + veneers.addAll(veneerPlanMapper.selectPendingVeneerList(idBatch)); + } + if (veneers.size() != veneerIds.size()) { + throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED); + } + for (OrderPlateVeneerDO veneer : veneers) { + if (!Objects.equals(plan.getSourceVeneerId(), veneer.getSourceVeneerId())) { + throw new ServiceException(VENEER_PLAN_PLATE_SOURCE_MISMATCH, veneer.getId()); + } + } + + // 更新时再次限制待排状态并校验数量,防止并发重复加入排单。 + for (List idBatch : CollUtil.split(veneerIds, PLAN_BATCH_SIZE)) { + int updated = veneerPlanMapper.updateVeneerPlanned(idBatch, planId); + if (updated != idBatch.size()) { + throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED); + } + } + + refreshPlanStatistics(plan); + refreshAffectedPlannedVeneerPlateCount(veneers); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void removePlanPlate(VeneerPlanRemovePlateReqVO req) { + Long planId = req == null ? null : req.getPlanId(); + OrderVeneerPlanDO plan = validatePlanStatuses(planId, PLAN_EDITABLE_STATUSES); + List veneerIds = normalizePlanVeneerIds( + req == null ? null : req.getVeneerIds()); + + // 仅允许移除当前排单中的有效贴皮,并保留归属范围用于后续重新统计。 + List veneers = new ArrayList<>(); + for (List idBatch : CollUtil.split(veneerIds, PLAN_BATCH_SIZE)) { + veneers.addAll(veneerPlanMapper.selectPlannedVeneerList(idBatch, planId)); + } + if (veneers.size() != veneerIds.size()) { + throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED); + } + + for (List idBatch : CollUtil.split(veneerIds, PLAN_BATCH_SIZE)) { + int updated = veneerPlanMapper.resetPlanVeneer(idBatch, planId); + if (updated != idBatch.size()) { + throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED); + } + } + + VeneerPlanStatisticsDO statistics = veneerPlanMapper.selectPlanStatistics(planId); + if (statistics == null || statistics.getVeneerNum() == null) { + throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED); + } + if (statistics.getVeneerNum() == 0) { + int deleted = veneerPlanMapper.physicalDeleteEmptyPlan( + planId, + VeneerPlanStatusEnum.OPENING.getStatus(), + VeneerPlanStatusEnum.OPENED.getStatus(), + plan.getVersion()); + if (deleted != 1) { + throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED); + } + } else { + updatePlanStatistics(plan, statistics); + } + refreshAffectedPlannedVeneerPlateCount(veneers); + } + + @Override + public PageResult getPlanPage(VeneerPlanPageReqVO req) { + IPage page = veneerPlanMapper.selectPlanPage( + new PageDTO<>(req.getPageNo(), req.getPageSize()), + req); + return new PageResult<>(page.getRecords(), page.getTotal()); + } + + @Override + public List getPlanPlateList(Long planId) { + if (planId == null) { + return List.of(); + } + return veneerPlanMapper.selectPlanPlateList(planId); + } + + @Override + public List getPlanSortPage() { + return veneerPlanMapper.selectPlanSortList( + VeneerPlanStatusEnum.OPENED.getStatus()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void setPlanSort(List req) { + List normalizedSortList = normalizePlanSortList(req); + + // 更新条件排除已开料状态并递增version;任意排单状态发生变化时整次排序回滚。 + for (List batch + : CollUtil.split(normalizedSortList, PLAN_SORT_BATCH_SIZE)) { + int updated = veneerPlanMapper.batchUpdatePlanSort( + batch, VeneerPlanStatusEnum.OPENED.getStatus()); + if (updated != batch.size()) { + throw new ServiceException(VENEER_PLAN_SORT_STATUS_CHANGED); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deletePlan(List planIds) { + List normalizedPlanIds = normalizeDeletePlanIds(planIds); + List plans = veneerPlanMapper.selectByIds(normalizedPlanIds); + if (plans.size() != normalizedPlanIds.size()) { + throw new ServiceException(VENEER_PLAN_DELETE_NOT_EXIST); + } + for (OrderVeneerPlanDO plan : plans) { + if (VeneerPlanStatusEnum.OPENING.equals(plan.getStatus()) + || VeneerPlanStatusEnum.OPENED.equals(plan.getStatus())) { + throw new ServiceException(VENEER_PLAN_DELETE_NOT_ALLOWED, plan.getId()); + } + } + + // 删除前读取受影响范围,恢复贴皮后仍可据此重新统计订单及订单材质数量。 + List affectedScopes = + veneerPlanMapper.selectPlanAffectedScope(normalizedPlanIds); + int expectedVeneerCount = 0; + for (OrderVeneerPlanDO plan : plans) { + if (plan.getVeneerNum() == null || plan.getVeneerNum() <= 0) { + throw new ServiceException(VENEER_PLAN_DELETE_STATUS_CHANGED); + } + expectedVeneerCount += plan.getVeneerNum(); + } + + // 直接按排单ID恢复关联贴皮,避免将大量板件贴皮ID拼接到IN条件中。 + int resetCount = veneerPlanMapper.resetVeneerPlan(normalizedPlanIds); + if (resetCount != expectedVeneerCount) { + throw new ServiceException(VENEER_PLAN_DELETE_STATUS_CHANGED); + } + + int deletedCount = veneerPlanMapper.physicalDeletePlan( + normalizedPlanIds, + VeneerPlanStatusEnum.OPENING.getStatus(), + VeneerPlanStatusEnum.OPENED.getStatus()); + if (deletedCount != normalizedPlanIds.size()) { + throw new ServiceException(VENEER_PLAN_DELETE_STATUS_CHANGED); + } + + List affectedOrderIds = affectedScopes.stream() + .map(OrderPlateVeneerDO::getOrderId) + .distinct() + .sorted() + .toList(); + List affectedOrderGoodsIds = affectedScopes.stream() + .map(OrderPlateVeneerDO::getOrderGoodsId) + .distinct() + .sorted() + .toList(); + refreshPlannedVeneerPlateCount(affectedOrderIds, affectedOrderGoodsIds); + } + + /** + * 清洗并校验待删除贴皮排单ID列表。 + * + * @param planIds 原始贴皮排单ID列表 + * @return 去空、去重并排序后的贴皮排单ID列表 + */ + private List normalizeDeletePlanIds(List planIds) { + if (CollUtil.isEmpty(planIds) || planIds.stream().anyMatch(Objects::isNull)) { + throw new ServiceException(VENEER_PLAN_DELETE_RECORD_REQUIRED); + } + List normalizedPlanIds = planIds.stream().distinct().sorted().toList(); + if (normalizedPlanIds.size() > PLAN_DELETE_LIMIT) { + throw new ServiceException( + VENEER_PLAN_DELETE_LIMIT_EXCEEDED, PLAN_DELETE_LIMIT); + } + return normalizedPlanIds; + } + + /** + * 校验并整理贴皮排单优先级设置参数。 + * + * @param req 原始优先级设置列表 + * @return 参数校验通过后的优先级设置列表 + */ + private List normalizePlanSortList(List req) { + if (CollUtil.isEmpty(req)) { + throw new ServiceException(VENEER_PLAN_SORT_RECORD_REQUIRED); + } + Set planIds = new HashSet<>(req.size()); + for (VeneerPlanSortReqVO sort : req) { + if (sort == null || sort.getPlanId() == null || sort.getSort() == null) { + throw new ServiceException(VENEER_PLAN_SORT_RECORD_REQUIRED); + } + if (!planIds.add(sort.getPlanId())) { + throw new ServiceException(VENEER_PLAN_SORT_RECORD_DUPLICATE, sort.getPlanId()); + } + } + return req; + } + + /** + * 查询并校验贴皮排单存在。 + * + * @param planId 贴皮排单ID + * @return 当前贴皮排单 + */ + private OrderVeneerPlanDO validatePlanExists(Long planId) { + if (planId == null) { + throw new ServiceException(VENEER_PLAN_NOT_EXIST, planId); + } + OrderVeneerPlanDO plan = veneerPlanMapper.selectById(planId); + if (plan == null) { + throw new ServiceException(VENEER_PLAN_NOT_EXIST, planId); + } + return plan; + } + + /** + * 查询贴皮排单并校验其状态属于允许范围。 + * + * @param planId 贴皮排单ID + * @param expectedStatuses 当前操作允许的状态 + * @return 当前贴皮排单 + */ + private OrderVeneerPlanDO validatePlanStatuses( + Long planId, List expectedStatuses) { + OrderVeneerPlanDO plan = validatePlanExists(planId); + if (!expectedStatuses.contains(plan.getStatus())) { + throw new ServiceException(VENEER_PLAN_OPERATION_STATUS_CHANGED, planId); + } + return plan; + } + + /** + * 清洗新增或移除排单的贴皮记录ID。 + * + * @param veneerIds 原始板件贴皮记录ID列表 + * @return 去重并升序排列后的记录ID列表 + */ + private List normalizePlanVeneerIds(List veneerIds) { + if (CollUtil.isEmpty(veneerIds) || veneerIds.stream().anyMatch(Objects::isNull)) { + throw new ServiceException(VENEER_PLAN_PLATE_RECORD_REQUIRED); + } + List normalizedIds = veneerIds.stream() + .distinct() + .sorted() + .toList(); + if (CollUtil.isEmpty(normalizedIds)) { + throw new ServiceException(VENEER_PLAN_PLATE_RECORD_REQUIRED); + } + return normalizedIds; + } + + /** + * 从数据库重新统计并更新贴皮排单汇总信息。 + * + * @param plan 更新前读取的贴皮排单 + */ + private void refreshPlanStatistics(OrderVeneerPlanDO plan) { + VeneerPlanStatisticsDO statistics = veneerPlanMapper.selectPlanStatistics(plan.getId()); + if (statistics == null || statistics.getVeneerNum() == null + || statistics.getVeneerArea() == null || statistics.getVeneerNum() <= 0) { + throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED); + } + updatePlanStatistics(plan, statistics); + } + + /** + * 使用状态及version乐观锁覆盖贴皮排单汇总信息。 + * + * @param plan 更新前读取的贴皮排单 + * @param statistics 数据库当前聚合结果 + */ + private void updatePlanStatistics( + OrderVeneerPlanDO plan, VeneerPlanStatisticsDO statistics) { + int version = plan.getVersion(); + int updated = veneerPlanMapper.update(new LambdaUpdateWrapper() + .eq(OrderVeneerPlanDO::getId, plan.getId()) + .in(OrderVeneerPlanDO::getStatus, PLAN_EDITABLE_STATUSES) + .eq(OrderVeneerPlanDO::getVersion, version) + .set(OrderVeneerPlanDO::getVeneerNum, statistics.getVeneerNum()) + .set(OrderVeneerPlanDO::getVeneerArea, statistics.getVeneerArea()) + .set(OrderVeneerPlanDO::getVersion, version + 1)); + if (updated != 1) { + throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED); + } + } + + /** + * 根据本次新增或移除贴皮的归属范围重新统计订单及订单材质数量。 + * + * @param veneers 本次操作的板件贴皮记录 + */ + private void refreshAffectedPlannedVeneerPlateCount(List veneers) { + List orderIds = veneers.stream() + .map(OrderPlateVeneerDO::getOrderId) + .distinct() + .sorted() + .toList(); + List orderGoodsIds = veneers.stream() + .map(OrderPlateVeneerDO::getOrderGoodsId) + .distinct() + .sorted() + .toList(); + refreshPlannedVeneerPlateCount(orderIds, orderGoodsIds); + } + + /** + * 按贴皮商品编号批量查询组织内贴皮库主键,并校验编号唯一性和完整性。 + * + * @param sourceVeneerNos 本次排单涉及的贴皮商品编号集合 + * @return 贴皮商品编号与贴皮库主键的映射 + */ + private Map loadSourceVeneerIdMap(Set sourceVeneerNos) { + List sourceVeneers = new ArrayList<>(); + List sourceVeneerNoList = new ArrayList<>(sourceVeneerNos); + for (List sourceVeneerNoBatch + : CollUtil.split(sourceVeneerNoList, PLAN_BATCH_SIZE)) { + sourceVeneers.addAll(veneerMapper.selectList( + new LambdaQueryWrapper() + .select(VeneerDO::getId, VeneerDO::getVeneerNo) + .in(VeneerDO::getVeneerNo, sourceVeneerNoBatch))); + } + Map sourceVeneerIdMap = new HashMap<>(sourceVeneers.size()); + for (VeneerDO sourceVeneer : sourceVeneers) { + Long previousId = sourceVeneerIdMap.putIfAbsent( + sourceVeneer.getVeneerNo(), sourceVeneer.getId()); + if (previousId != null) { + throw new ServiceException( + VENEER_PLAN_SOURCE_NO_DUPLICATE, sourceVeneer.getVeneerNo()); + } + } + for (String sourceVeneerNo : sourceVeneerNos) { + if (!sourceVeneerIdMap.containsKey(sourceVeneerNo)) { + throw new ServiceException(VENEER_PLAN_SOURCE_NO_NOT_EXIST, sourceVeneerNo); + } + } + return sourceVeneerIdMap; + } + + /** + * 根据板件贴皮当前排单状态,重新统计订单和订单材质的已排贴皮板件数量。 + * + * @param orderIds 订单ID列表 + * @param orderGoodsIds {@code order_goods} 主键列表 + */ + private void refreshPlannedVeneerPlateCount(List orderIds, List orderGoodsIds) { + refreshOrderPlannedVeneerPlateCount(orderIds); + refreshGoodsPlannedVeneerPlateCount(orderGoodsIds); + } + + /** + * 分批重新统计并覆盖订单的已排贴皮板件数量,更新时使用 version 进行乐观锁校验。 + * + * @param orderIds 订单ID列表 + */ + private void refreshOrderPlannedVeneerPlateCount(List orderIds) { + for (List orderIdBatch : CollUtil.split(orderIds, PLAN_STAT_BATCH_SIZE)) { + Map orderMap = new HashMap<>(); + for (OrderDO order : orderMapper.selectByIds(orderIdBatch)) { + orderMap.put(order.getId(), order); + } + Map countMap = convertPlannedVeneerPlateCount( + veneerPlanMapper.selectOrderPlannedVeneerPlateCount(orderIdBatch)); + + for (Long orderId : orderIdBatch) { + OrderDO order = orderMap.get(orderId); + if (order == null) { + throw new ServiceException(ORDER_PLATE_UPDATE_CONCURRENCY_ERROR, orderId); + } + int version = order.getVersion(); + int updated = orderMapper.update(new LambdaUpdateWrapper() + .eq(OrderDO::getId, orderId) + .eq(OrderDO::getVersion, version) + .set(OrderDO::getPlannedVeneerPlateNum, + countMap.getOrDefault(orderId, 0)) + .set(OrderDO::getVersion, version + 1)); + if (updated == 0) { + throw new ServiceException(ORDER_PLATE_UPDATE_CONCURRENCY_ERROR, orderId); + } + } + } + } + + /** + * 分批重新统计并覆盖订单材质的已排贴皮板件数量,更新时使用 version 进行乐观锁校验。 + * + * @param orderGoodsIds {@code order_goods} 主键列表 + */ + private void refreshGoodsPlannedVeneerPlateCount(List orderGoodsIds) { + for (List orderGoodsIdBatch : CollUtil.split(orderGoodsIds, PLAN_STAT_BATCH_SIZE)) { + Map goodsMap = new HashMap<>(); + for (GoodsDO goods : goodsMapper.selectByIds(orderGoodsIdBatch)) { + goodsMap.put(goods.getId(), goods); + } + Map countMap = convertPlannedVeneerPlateCount( + veneerPlanMapper.selectGoodsPlannedVeneerPlateCount(orderGoodsIdBatch)); + + for (Long orderGoodsId : orderGoodsIdBatch) { + GoodsDO goods = goodsMap.get(orderGoodsId); + if (goods == null) { + throw new ServiceException(GOODS_PLATE_UPDATE_CONCURRENCY_ERROR, orderGoodsId); + } + int version = goods.getVersion(); + int updated = goodsMapper.update(new LambdaUpdateWrapper() + .eq(GoodsDO::getId, orderGoodsId) + .eq(GoodsDO::getVersion, version) + .set(GoodsDO::getPlannedVeneerPlateNum, + countMap.getOrDefault(orderGoodsId, 0)) + .set(GoodsDO::getVersion, version + 1)); + if (updated == 0) { + throw new ServiceException(GOODS_PLATE_UPDATE_CONCURRENCY_ERROR, orderGoodsId); + } + } + } + } + + /** + * 将数据库聚合结果转换为目标ID与已排贴皮板件数量的映射。 + * + * @param databaseCounts 数据库聚合统计结果 + * @return 目标ID与已排贴皮板件数量的映射 + */ + private Map convertPlannedVeneerPlateCount( + List databaseCounts) { + Map countMap = new HashMap<>(); + for (VeneerPlannedPlateCountDO databaseCount : databaseCounts) { + countMap.put(databaseCount.getTargetId(), databaseCount.getPlannedVeneerPlateNum()); + } + return countMap; + } + + /** + * 校验并整理前端提交的贴皮商品编号分组。 + * 同一贴皮商品编号只能出现一个分组,同一板件贴皮记录也只能属于一个分组。 + * + * @param groups 前端按贴皮商品编号整理的分组列表 + * @return 以贴皮商品编号为键、板件贴皮记录ID列表为值的有序分组 + */ + private Map> normalizeGroups(List groups) { + if (CollUtil.isEmpty(groups)) { + throw new ServiceException(VENEER_PLAN_GROUP_REQUIRED); + } + Map> groupIdMap = new LinkedHashMap<>(groups.size()); + Set allVeneerIds = new HashSet<>(); + for (VeneerPlanGroupReqVO group : groups) { + if (group == null || StrUtil.isBlank(group.getSourceVeneerNo())) { + throw new ServiceException(VENEER_PLAN_GROUP_SOURCE_NO_REQUIRED); + } + String sourceVeneerNo = StrUtil.trim(group.getSourceVeneerNo()); + if (groupIdMap.containsKey(sourceVeneerNo)) { + throw new ServiceException(VENEER_PLAN_GROUP_DUPLICATE, sourceVeneerNo); + } + List groupIds = normalizeVeneerIds(group.getVeneerIds()); + for (Long veneerId : groupIds) { + if (!allVeneerIds.add(veneerId)) { + throw new ServiceException(VENEER_PLAN_RECORD_DUPLICATE, veneerId); + } + } + groupIdMap.put(sourceVeneerNo, groupIds); + } + return groupIdMap; + } + + /** + * 清洗并校验贴皮记录ID列表。 + * + * @param veneerIds 原始贴皮记录ID列表 + * @return 去空、去重并排序后的ID列表 + */ + private List normalizeVeneerIds(List veneerIds) { + if (CollUtil.isEmpty(veneerIds)) { + throw new ServiceException(VENEER_PLAN_RECORD_REQUIRED); + } + List normalizedIds = veneerIds.stream() + .filter(Objects::nonNull) + .distinct() + .sorted() + .toList(); + if (CollUtil.isEmpty(normalizedIds)) { + throw new ServiceException(VENEER_PLAN_RECORD_REQUIRED); + } + return normalizedIds; + } + + /** + * 根据原板尺寸和外扩余量计算当前贴皮排单的总面积。 + * A/B面作为两条贴皮记录分别累计,结果单位为平方米。 + * + * @param veneers 同一贴皮商品编号下的贴皮记录 + * @return 保留四位小数的贴皮总面积 + */ + private BigDecimal calculateVeneerArea(List veneers) { + BigDecimal totalSquareMillimeter = BigDecimal.ZERO; + for (OrderPlateVeneerDO veneer : veneers) { + BigDecimal length = veneer.getPlateLength(); + BigDecimal width = veneer.getPlateWidth(); + BigDecimal outerMargin = Objects.requireNonNullElse(veneer.getOuterMargin(), BigDecimal.ZERO); + if (length == null || width == null || length.signum() <= 0 || width.signum() <= 0 + || outerMargin.signum() < 0) { + throw new ServiceException(VENEER_PLAN_AREA_DATA_INVALID, veneer.getId()); + } + BigDecimal finalLength = length.add(outerMargin.multiply(OUTER_MARGIN_DOUBLE)); + BigDecimal finalWidth = width.add(outerMargin.multiply(OUTER_MARGIN_DOUBLE)); + totalSquareMillimeter = totalSquareMillimeter.add(finalLength.multiply(finalWidth)); + } + return totalSquareMillimeter.divide( + SQUARE_MILLIMETER_PER_SQUARE_METER, 4, RoundingMode.HALF_UP); + } +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerDecomposeMapper.xml b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerDecomposeMapper.xml index c8baa1b6f..e5347ed8e 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerDecomposeMapper.xml +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerDecomposeMapper.xml @@ -101,40 +101,88 @@ + type="com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO" + autoMapping="true"> - - - - - - - - - - + + + op.id AS plateId, + og.goods_id AS goodsId, + og.goods_name AS goodsName, + op.plate_no AS plateNo, + op.custom_plate_no AS customPlateNo, + op.name, + op.name AS plateName, + og.color, + og.brand, + og.material, + op.height, + op.height AS length, + op.width, + op.thickness, + op.split_width AS splitWidth, + op.split_height AS splitHeight, + op.split_thickness AS splitThickness, + op.is_special_shaped AS isSpecialShaped, + op.seal_left AS sealLeft, + op.seal_right AS sealRight, + op.seal_up AS sealUp, + op.seal_down AS sealDown, + op.reserved_edge_left AS reservedEdgeLeft, + op.reserved_edge_right AS reservedEdgeRight, + op.reserved_edge_up AS reservedEdgeUp, + op.reserved_edge_down AS reservedEdgeDown, + op.area, + op.texture, + op.hole_face AS holeFace, + op.hole_arrange AS holeArrange, + op.unregular_point_count AS unregularPointCount, + op.front_hole_count AS frontHoleCount, + op.back_hole_count AS backHoleCount, + op.side_hole_count AS sideHoleCount, + op.front_model_count AS frontModelCount, + op.back_model_count AS backModelCount, + op.is_door AS isDoor, + op.is_arc_across AS isArcAcross, + op.module_type_id AS moduleTypeId, + op.offset_x AS offsetX, + op.offset_y AS offsetY, + op.remark, + op.type, + opg.name AS processGroupName, + op.open_door_type AS openDoorType, + op.is_row_hole AS isRowHole, + op.is_sculpt AS isSculpt, + op.is_side_sculpt AS isSideSculpt, + op.is_2v AS is2v, + op.is_side_2v AS isSide2v, + CASE WHEN op.front_model_count > 0 AND op.back_model_count > 0 + THEN 1 ELSE 0 END AS doubleSideModel, + CASE WHEN op.front_hole_count > 0 AND op.back_hole_count > 0 + THEN 1 ELSE 0 END AS doubleSideHole, + CASE WHEN op.front_model_count > 0 THEN 1 ELSE 0 END AS frontModel, + CASE WHEN op.back_model_count > 0 THEN 1 ELSE 0 END AS backModel, + CASE WHEN op.front_hole_count > 0 THEN 1 ELSE 0 END AS frontHole, + CASE WHEN op.back_hole_count > 0 THEN 1 ELSE 0 END AS backHole, + op.is_side_sculpt AS sideModel, + CASE WHEN op.side_hole_count > 0 THEN 1 ELSE 0 END AS sideHole, + op.model_data AS modelData + + - - @@ -275,4 +302,5 @@ GROUP BY op.goods_id + diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerManageMapper.xml b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerManageMapper.xml index 11f6ecb5f..673b92487 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerManageMapper.xml +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerManageMapper.xml @@ -34,6 +34,7 @@ + @@ -41,6 +42,15 @@ + + + + + + + + + + opv.id, opv.veneer_no AS veneerNo, @@ -124,6 +206,7 @@ opv.plate_length AS length, opv.plate_width AS width, opv.plate_thickness AS thickness, + opv.plan_id AS planId, opv.source_veneer_no AS sourceVeneerNo, opv.veneer_name AS veneerName, opv.product_level AS productLevel, @@ -137,9 +220,31 @@ FROM order_plate_veneer opv WHERE opv.deleted = false AND opv.order_id IN - + #{orderId} + + AND EXISTS ( + SELECT 1 + FROM order_item oi + WHERE oi.order_id = opv.order_id + AND oi.plate_id = opv.order_plate_id + AND oi.plate_id != 0 + + AND oi.room_id IN + + #{roomId} + + + + AND oi.body_id IN + + #{bodyId} + + + ) + ORDER BY opv.order_plate_id DESC, opv.veneer_side ASC @@ -157,4 +262,176 @@ ORDER BY opv.order_plate_id DESC, opv.veneer_side ASC + + + opv.id, + opv.order_id AS orderId, + opv.order_goods_id AS orderGoodsId, + opv.veneer_side AS veneerSide, + opv.plate_surface AS plateSurface, + opv.veneer_no AS veneerNo, + opv.outer_margin AS outerMargin, + opv.source_veneer_id AS sourceVeneerId, + opv.source_veneer_no AS sourceVeneerNo, + opv.veneer_name AS veneerName, + opv.product_level AS productLevel, + opv.color AS veneerColor, + opv.material AS veneerMaterial, + op.id AS plateId, + og.goods_id AS goodsId, + og.goods_name AS goodsName, + op.plate_no AS plateNo, + op.custom_plate_no AS customPlateNo, + op.name, + op.name AS plateName, + og.color, + og.brand, + og.material, + op.height, + op.height AS length, + op.width, + op.thickness, + op.split_width AS splitWidth, + op.split_height AS splitHeight, + op.split_thickness AS splitThickness, + op.is_special_shaped AS isSpecialShaped, + op.seal_left AS sealLeft, + op.seal_right AS sealRight, + op.seal_up AS sealUp, + op.seal_down AS sealDown, + op.reserved_edge_left AS reservedEdgeLeft, + op.reserved_edge_right AS reservedEdgeRight, + op.reserved_edge_up AS reservedEdgeUp, + op.reserved_edge_down AS reservedEdgeDown, + op.area, + op.texture, + op.hole_face AS holeFace, + op.hole_arrange AS holeArrange, + op.unregular_point_count AS unregularPointCount, + op.front_hole_count AS frontHoleCount, + op.back_hole_count AS backHoleCount, + op.side_hole_count AS sideHoleCount, + op.front_model_count AS frontModelCount, + op.back_model_count AS backModelCount, + op.is_door AS isDoor, + op.is_arc_across AS isArcAcross, + op.module_type_id AS moduleTypeId, + op.offset_x AS offsetX, + op.offset_y AS offsetY, + op.remark, + op.type, + opg.name AS processGroupName, + op.open_door_type AS openDoorType, + op.is_row_hole AS isRowHole, + op.is_sculpt AS isSculpt, + op.is_side_sculpt AS isSideSculpt, + op.is_2v AS is2v, + op.is_side_2v AS isSide2v, + CASE WHEN op.front_model_count > 0 AND op.back_model_count > 0 + THEN 1 ELSE 0 END AS doubleSideModel, + CASE WHEN op.front_hole_count > 0 AND op.back_hole_count > 0 + THEN 1 ELSE 0 END AS doubleSideHole, + CASE WHEN op.front_model_count > 0 THEN 1 ELSE 0 END AS frontModel, + CASE WHEN op.back_model_count > 0 THEN 1 ELSE 0 END AS backModel, + CASE WHEN op.front_hole_count > 0 THEN 1 ELSE 0 END AS frontHole, + CASE WHEN op.back_hole_count > 0 THEN 1 ELSE 0 END AS backHole, + op.is_side_sculpt AS sideModel, + CASE WHEN op.side_hole_count > 0 THEN 1 ELSE 0 END AS sideHole, + op.model_data AS modelData + + + + + + + + UPDATE order_plate_veneer + SET deleted = true, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND plan_id = 0 + AND id IN + + #{id} + + + + + + + UPDATE order_plate + SET is_decomposed = false, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND is_decomposed = true + AND id IN + + #{plateId} + + 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 new file mode 100644 index 000000000..024ed3e55 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/mapper/veneer/VeneerPlanMapper.xml @@ -0,0 +1,259 @@ + + + + + + + + + + UPDATE order_veneer_plan + SET sort = CASE id + + WHEN #{item.planId} THEN #{item.sort} + + END, + version = version + 1, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND status != #{openedStatus} + AND id IN + + #{item.planId} + + + + + + + UPDATE order_plate_veneer + SET plan_id = 0, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND plan_id = #{planId} + AND id IN + + #{id} + + + + + + + DELETE FROM order_veneer_plan + WHERE deleted = false + AND id = #{planId} + AND status NOT IN (#{openingStatus}, #{openedStatus}) + AND version = #{version} + + + + + + + + UPDATE order_plate_veneer + SET plan_id = #{planId}, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND plan_id = 0 + AND id IN + + #{id} + + + + + + + + + + + UPDATE order_plate_veneer + SET plan_id = 0, + update_time = CURRENT_TIMESTAMP + WHERE deleted = false + AND plan_id IN + + #{planId} + + + + + DELETE FROM order_veneer_plan + WHERE deleted = false + AND status NOT IN (#{openingStatus}, #{openedStatus}) + AND id IN + + #{planId} + + + + 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 85c790d9f..93fc67c16 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 @@ -78,6 +78,35 @@ veneer.decompose.outer.margin.min=外擴餘量不能小於0 veneer.reselect.record.required=請選擇需要重選貼皮的資料 veneer.reselect.source.required=請選擇新的貼皮 veneer.reselect.record.status.changed=板件貼皮資料狀態已變更,請重新整理後再試 +veneer.remove.record.required=請選擇需要移除的貼皮資料 +veneer.remove.record.status.changed=所選貼皮資料已刪除、已排單或狀態發生變化,請重新整理後再試 +veneer.unplanned.scope.invalid=訂單ID列表和板材商品編碼列表必須且只能傳入一項 +veneer.plan.record.required=請選擇需要排單的貼皮資料 +veneer.plan.record.status.changed=所選貼皮資料已排單、已刪除或狀態發生變化,請重新整理後再試 +veneer.plan.area.data.invalid=貼皮記錄{0}的板件尺寸或外擴餘量異常,無法計算貼皮面積 +veneer.plan.create.failed=貼皮排單建立失敗,請稍後再試 +veneer.plan.group.required=請提交需要建立的貼皮排單分組 +veneer.plan.group.source.no.required=貼皮排單分組的貼皮商品編號不能為空 +veneer.plan.group.duplicate=貼皮商品編號{0}存在重複分組 +veneer.plan.record.duplicate=貼皮記錄{0}被重複提交到多個排單分組 +veneer.plan.source.no.mismatch=貼皮記錄{0}與分組貼皮商品編號{1}不一致,請重新整理後再試 +veneer.plan.source.no.not.exist=貼皮商品編號{0}在貼皮庫中不存在,請重新整理後再試 +veneer.plan.source.no.duplicate=貼皮商品編號{0}在目前組織內存在重複資料,無法建立排單 +veneer.plan.delete.record.required=請選擇需要刪除的貼皮排單 +veneer.plan.delete.limit.exceeded=單次最多刪除{0}個貼皮排單 +veneer.plan.delete.not.exist=所選貼皮排單不存在或已刪除,請重新整理後再試 +veneer.plan.delete.not.allowed=貼皮排單{0}已進入生產流程,不允許刪除 +veneer.plan.delete.status.changed=貼皮排單或關聯貼皮狀態發生變化,請重新整理後再試 +veneer.plan.sort.record.required=請提交需要設定優先級的貼皮排單 +veneer.plan.sort.record.duplicate=貼皮排單{0}被重複提交,請檢查後再試 +veneer.plan.sort.status.changed=貼皮排單不存在或狀態發生變化,請重新整理後再試 +veneer.plan.not.exist=貼皮排單{0}不存在或已刪除,請重新整理後再試 +veneer.plan.id.required=貼皮排單ID不能為空 +veneer.plan.process.required=請選擇貼皮排單的加工方案 +veneer.plan.operation.status.changed=貼皮排單{0}的狀態發生變化,目前操作無法執行,請重新整理後再試 +veneer.plan.plate.record.required=請選擇需要新增或移除的貼皮記錄 +veneer.plan.plate.source.mismatch=貼皮記錄{0}與目標排單的貼皮商品不一致,無法加入排單 +veneer.plan.plate.status.changed=所選貼皮記錄或目標排單狀態發生變化,請重新整理後再試 veneer.no.exist=貼皮編號已存在 veneer.import.required.lack=第{0}行存在帶*必填項為空,請檢查檔案 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 f8da0e3fb..68bb66a3e 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 @@ -78,6 +78,35 @@ veneer.decompose.outer.margin.min=Outer margin cannot be less than 0 veneer.reselect.record.required=Please select records for veneer reselection veneer.reselect.source.required=Please select a new veneer veneer.reselect.record.status.changed=Plate veneer data has changed. Please refresh and retry +veneer.remove.record.required=Please select veneer records to remove +veneer.remove.record.status.changed=The selected veneer data has been removed, scheduled, or changed. Please refresh and retry +veneer.unplanned.scope.invalid=Exactly one of the order ID list and goods code list must be provided +veneer.plan.record.required=Please select veneer records to schedule +veneer.plan.record.status.changed=The selected veneer data has been scheduled, removed, or changed. Please refresh and retry +veneer.plan.area.data.invalid=Veneer record {0} has invalid plate dimensions or outer margin +veneer.plan.create.failed=Failed to create the veneer plan. Please retry later +veneer.plan.group.required=Please submit at least one veneer plan group +veneer.plan.group.source.no.required=The veneer product number of a plan group is required +veneer.plan.group.duplicate=Duplicate plan groups exist for veneer product number {0} +veneer.plan.record.duplicate=Veneer record {0} was submitted in multiple plan groups +veneer.plan.source.no.mismatch=Veneer record {0} does not belong to veneer product number {1}. Please refresh and retry +veneer.plan.source.no.not.exist=Veneer product number {0} does not exist in the veneer library. Please refresh and retry +veneer.plan.source.no.duplicate=Veneer product number {0} is duplicated in the current organization and cannot be scheduled +veneer.plan.delete.record.required=Please select veneer plans to delete +veneer.plan.delete.limit.exceeded=At most {0} veneer plans can be deleted at a time +veneer.plan.delete.not.exist=The selected veneer plan does not exist or has been deleted. Please refresh and retry +veneer.plan.delete.not.allowed=Veneer plan {0} has entered production and cannot be deleted +veneer.plan.delete.status.changed=The veneer plan or its associated veneer status has changed. Please refresh and retry +veneer.plan.sort.record.required=Please submit veneer plans whose priority needs to be updated +veneer.plan.sort.record.duplicate=Veneer plan {0} was submitted more than once. Please check and retry +veneer.plan.sort.status.changed=The veneer plan does not exist or its status has changed. Please refresh and retry +veneer.plan.not.exist=Veneer plan {0} does not exist or has been deleted. Please refresh and retry +veneer.plan.id.required=Veneer plan ID is required +veneer.plan.process.required=Please select a process scheme for the veneer plan +veneer.plan.operation.status.changed=The status of veneer plan {0} has changed, so the operation cannot be performed. Please refresh and retry +veneer.plan.plate.record.required=Please select veneer records to add or remove +veneer.plan.plate.source.mismatch=Veneer record {0} does not match the veneer product of the target plan +veneer.plan.plate.status.changed=The selected veneer record or target plan status has changed. Please refresh and retry veneer.no.exist=veneer no already exists veneer.import.required.lack=The required field is empty in line {0}, please check the file 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 ec8d0c24f..7c89d0ac6 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 @@ -78,6 +78,35 @@ veneer.decompose.outer.margin.min=外扩余量不能小于0 veneer.reselect.record.required=请选择需要重选贴皮的数据 veneer.reselect.source.required=请选择新的贴皮 veneer.reselect.record.status.changed=板件贴皮数据状态已变更,请刷新后重试 +veneer.remove.record.required=请选择需要移除的贴皮数据 +veneer.remove.record.status.changed=所选贴皮数据已删除、已排单或状态发生变化,请刷新后重试 +veneer.unplanned.scope.invalid=订单ID列表和板材商品编码列表必须且只能传入一项 +veneer.plan.record.required=请选择需要排单的贴皮数据 +veneer.plan.record.status.changed=所选贴皮数据已排单、已删除或状态发生变化,请刷新后重试 +veneer.plan.area.data.invalid=贴皮记录{0}的板件尺寸或外扩余量异常,无法计算贴皮面积 +veneer.plan.create.failed=贴皮排单创建失败,请稍后重试 +veneer.plan.group.required=请提交需要创建的贴皮排单分组 +veneer.plan.group.source.no.required=贴皮排单分组的贴皮商品编号不能为空 +veneer.plan.group.duplicate=贴皮商品编号{0}存在重复分组 +veneer.plan.record.duplicate=贴皮记录{0}被重复提交到多个排单分组 +veneer.plan.source.no.mismatch=贴皮记录{0}与分组贴皮商品编号{1}不一致,请刷新后重试 +veneer.plan.source.no.not.exist=贴皮商品编号{0}在贴皮库中不存在,请刷新后重试 +veneer.plan.source.no.duplicate=贴皮商品编号{0}在当前组织内存在重复数据,无法创建排单 +veneer.plan.delete.record.required=请选择需要删除的贴皮排单 +veneer.plan.delete.limit.exceeded=单次最多删除{0}个贴皮排单 +veneer.plan.delete.not.exist=所选贴皮排单不存在或已删除,请刷新后重试 +veneer.plan.delete.not.allowed=贴皮排单{0}已进入生产流程,不允许删除 +veneer.plan.delete.status.changed=贴皮排单或关联贴皮状态发生变化,请刷新后重试 +veneer.plan.sort.record.required=请提交需要设置优先级的贴皮排单 +veneer.plan.sort.record.duplicate=贴皮排单{0}被重复提交,请检查后重试 +veneer.plan.sort.status.changed=贴皮排单不存在或状态发生变化,请刷新后重试 +veneer.plan.not.exist=贴皮排单{0}不存在或已删除,请刷新后重试 +veneer.plan.id.required=贴皮排单ID不能为空 +veneer.plan.process.required=请选择贴皮排单的加工方案 +veneer.plan.operation.status.changed=贴皮排单{0}的状态发生变化,当前操作无法执行,请刷新后重试 +veneer.plan.plate.record.required=请选择需要新增或移除的贴皮记录 +veneer.plan.plate.source.mismatch=贴皮记录{0}与目标排单的贴皮商品不一致,无法加入排单 +veneer.plan.plate.status.changed=所选贴皮记录或目标排单状态发生变化,请刷新后重试 veneer.no.exist=贴皮编号已存在 veneer.import.required.lack=第{0}行存在带*必填项为空,请检查文件