1、已排订单排单优先级补充板件信息;2、新增贴皮管理批量移除接口;3、新增贴皮排单系列接口;4、新增未排贴皮系列接口;

This commit is contained in:
gaoqr
2026-07-21 15:09:39 +08:00
parent 396a9222cc
commit 0a425032bc
50 changed files with 3594 additions and 161 deletions
@@ -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_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_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_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 ============ //=========== 部件管理 1-003-010-000 ============
public static final ErrorCode ORDER_COMPONENT_NOT_EXIST = new ErrorCode(1_003_010_000, "order.component.not.exist"); public static final ErrorCode ORDER_COMPONENT_NOT_EXIST = new ErrorCode(1_003_010_000, "order.component.not.exist");
@@ -0,0 +1,40 @@
package com.cf.imes.module.executor.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 贴皮排单加工状态枚举。
*
* <p>状态值与普通排单 {@code PlanStatusEnum} 保持一致,投入生产状态由
* {@code order_veneer_plan.is_scheduled} 单独记录。</p>
*/
@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);
}
}
@@ -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;
}
@@ -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;
}
@@ -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<PageResult<VeneerManageOrderRespVO>> 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<PageResult<VeneerManageGoodsRespVO>> 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<UnplannedVeneerPlateDataRespVO> getUnplannedVeneerPlateData(
@Valid @RequestBody UnplannedVeneerPlateDataReqVO req) {
return success(veneerManageService.getUnplannedPlateData(req));
}
}
@@ -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.VeneerDecomposeGoodsRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderPageReqVO; 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.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.VeneerDecomposePlatePageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO;
import com.cf.imes.module.executor.service.veneer.VeneerDecomposeService; import com.cf.imes.module.executor.service.veneer.VeneerDecomposeService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; 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.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.cf.imes.framework.common.pojo.CommonResult.success; import static com.cf.imes.framework.common.pojo.CommonResult.success;
/** /**
@@ -46,7 +44,7 @@ public class VeneerDecomposeController {
@PostMapping("/order/page") @PostMapping("/order/page")
@Operation(summary = "获取贴皮分解订单分页列表") @Operation(summary = "获取贴皮分解订单分页列表")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<PageResult<VeneerDecomposeOrderRespVO>> getOrderPage( public CommonResult<PageResult<VeneerDecomposeOrderRespVO>> getVeneerDecomposeOrderPage(
@Valid @RequestBody VeneerDecomposeOrderPageReqVO req) { @Valid @RequestBody VeneerDecomposeOrderPageReqVO req) {
return success(veneerDecomposeService.getOrderPage(req)); return success(veneerDecomposeService.getOrderPage(req));
} }
@@ -60,7 +58,7 @@ public class VeneerDecomposeController {
@PostMapping("/goods/page") @PostMapping("/goods/page")
@Operation(summary = "获取贴皮分解板材分页列表") @Operation(summary = "获取贴皮分解板材分页列表")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<PageResult<VeneerDecomposeGoodsRespVO>> getGoodsPage( public CommonResult<PageResult<VeneerDecomposeGoodsRespVO>> getVeneerDecomposeGoodsPage(
@Valid @RequestBody VeneerDecomposeGoodsPageReqVO req) { @Valid @RequestBody VeneerDecomposeGoodsPageReqVO req) {
return success(veneerDecomposeService.getGoodsPage(req)); return success(veneerDecomposeService.getGoodsPage(req));
} }
@@ -69,12 +67,12 @@ public class VeneerDecomposeController {
* 按订单查询未贴皮分解板件列表。 * 按订单查询未贴皮分解板件列表。
* *
* @param req 查询条件,必须包含订单ID列表 * @param req 查询条件,必须包含订单ID列表
* @return 板件列表 * @return 板件及板件造型数据
*/ */
@PostMapping("/order/plate/page") @PostMapping("/order/plate/page")
@Operation(summary = "获取订单下待贴皮分解板件列表") @Operation(summary = "获取订单下待贴皮分解板件列表")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<List<VeneerDecomposePlateRespVO>> getOrderPlateList( public CommonResult<VeneerDecomposePlateDataRespVO> getVeneerDecomposeOrderPlateData(
@Valid @RequestBody VeneerDecomposePlatePageReqVO req) { @Valid @RequestBody VeneerDecomposePlatePageReqVO req) {
return success(veneerDecomposeService.getOrderPlateList(req)); return success(veneerDecomposeService.getOrderPlateList(req));
} }
@@ -83,12 +81,12 @@ public class VeneerDecomposeController {
* 按板材查询未贴皮分解板件列表。 * 按板材查询未贴皮分解板件列表。
* *
* @param req 查询条件,必须包含商品编码列表 * @param req 查询条件,必须包含商品编码列表
* @return 板件列表 * @return 板件及板件造型数据
*/ */
@PostMapping("/goods/plate/page") @PostMapping("/goods/plate/page")
@Operation(summary = "获取材质下待贴皮分解板件列表") @Operation(summary = "获取材质下待贴皮分解板件列表")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<List<VeneerDecomposePlateRespVO>> getGoodsPlateList( public CommonResult<VeneerDecomposePlateDataRespVO> getVeneerDecomposeGoodsPlateData(
@Valid @RequestBody VeneerDecomposePlatePageReqVO req) { @Valid @RequestBody VeneerDecomposePlatePageReqVO req) {
return success(veneerDecomposeService.getGoodsPlateList(req)); return success(veneerDecomposeService.getGoodsPlateList(req));
} }
@@ -102,7 +100,8 @@ public class VeneerDecomposeController {
@PostMapping("/decompose") @PostMapping("/decompose")
@Operation(summary = "执行贴皮分解") @Operation(summary = "执行贴皮分解")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<Boolean> decompose(@Valid @RequestBody VeneerDecomposeCreateReqVO req) { public CommonResult<Boolean> createVeneerDecompose(
@Valid @RequestBody VeneerDecomposeCreateReqVO req) {
veneerDecomposeService.decompose(req); veneerDecomposeService.decompose(req);
return success(true); return success(true);
} }
@@ -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.VeneerManageOrderRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateListReqVO; 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.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.controller.admin.veneer.vo.VeneerManageReselectReqVO;
import com.cf.imes.module.executor.service.veneer.VeneerManageService; import com.cf.imes.module.executor.service.veneer.VeneerManageService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@@ -46,7 +47,7 @@ public class VeneerManageController {
@PostMapping("/order/page") @PostMapping("/order/page")
@Operation(summary = "获取贴皮管理订单分页列表") @Operation(summary = "获取贴皮管理订单分页列表")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<PageResult<VeneerManageOrderRespVO>> getOrderPage( public CommonResult<PageResult<VeneerManageOrderRespVO>> getVeneerManageOrderPage(
@Valid @RequestBody VeneerManageOrderPageReqVO req) { @Valid @RequestBody VeneerManageOrderPageReqVO req) {
return success(veneerManageService.getOrderPage(req)); return success(veneerManageService.getOrderPage(req));
} }
@@ -60,7 +61,7 @@ public class VeneerManageController {
@PostMapping("/goods/page") @PostMapping("/goods/page")
@Operation(summary = "获取贴皮管理板材分页列表") @Operation(summary = "获取贴皮管理板材分页列表")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<PageResult<VeneerManageGoodsRespVO>> getGoodsPage( public CommonResult<PageResult<VeneerManageGoodsRespVO>> getVeneerManageGoodsPage(
@Valid @RequestBody VeneerManageGoodsPageReqVO req) { @Valid @RequestBody VeneerManageGoodsPageReqVO req) {
return success(veneerManageService.getGoodsPage(req)); return success(veneerManageService.getGoodsPage(req));
} }
@@ -68,13 +69,13 @@ public class VeneerManageController {
/** /**
* 按订单查询已分解板件贴皮列表。 * 按订单查询已分解板件贴皮列表。
* *
* @param req 查询条件,必须包含订单ID列表 * @param req 查询条件,必须包含订单ID列表,可按房间和柜体筛选
* @return 已分解板件贴皮列表 * @return 已分解板件贴皮列表
*/ */
@PostMapping("/order/plate/list") @PostMapping("/order/plate/list")
@Operation(summary = "按订单获取已分解板件贴皮列表") @Operation(summary = "按订单获取已分解板件贴皮列表")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<List<VeneerManagePlateRespVO>> getOrderPlateList( public CommonResult<List<VeneerManagePlateRespVO>> getVeneerManageOrderPlateList(
@Valid @RequestBody VeneerManagePlateListReqVO req) { @Valid @RequestBody VeneerManagePlateListReqVO req) {
return success(veneerManageService.getOrderPlateList(req)); return success(veneerManageService.getOrderPlateList(req));
} }
@@ -88,7 +89,7 @@ public class VeneerManageController {
@PostMapping("/goods/plate/list") @PostMapping("/goods/plate/list")
@Operation(summary = "按板材获取已分解板件贴皮列表") @Operation(summary = "按板材获取已分解板件贴皮列表")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<List<VeneerManagePlateRespVO>> getGoodsPlateList( public CommonResult<List<VeneerManagePlateRespVO>> getVeneerManageGoodsPlateList(
@Valid @RequestBody VeneerManagePlateListReqVO req) { @Valid @RequestBody VeneerManagePlateListReqVO req) {
return success(veneerManageService.getGoodsPlateList(req)); return success(veneerManageService.getGoodsPlateList(req));
} }
@@ -102,9 +103,24 @@ public class VeneerManageController {
@PostMapping("/plate/reselect") @PostMapping("/plate/reselect")
@Operation(summary = "批量重选贴皮") @Operation(summary = "批量重选贴皮")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')") @PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<Boolean> reselectVeneer( public CommonResult<Boolean> reselectVeneerManageSource(
@Valid @RequestBody VeneerManageReselectReqVO req) { @Valid @RequestBody VeneerManageReselectReqVO req) {
veneerManageService.reselectVeneer(req); veneerManageService.reselectVeneer(req);
return success(true); return success(true);
} }
/**
* 批量移除待排单板件贴皮。
*
* @param req 待移除的板件贴皮记录ID列表
* @return 是否成功
*/
@PostMapping("/plate/remove")
@Operation(summary = "批量移除待排单贴皮")
@PreAuthorize("@ss.hasPermission('unplannedOrders:query')")
public CommonResult<Boolean> removeVeneerManageRecord(
@Valid @RequestBody VeneerManageRemoveReqVO req) {
veneerManageService.removeVeneer(req);
return success(true);
}
} }
@@ -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<Boolean> 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<Boolean> 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<Boolean> 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<Boolean> 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<Boolean> 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<Boolean> 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<Boolean> deleteVeneerPlan(
@RequestParam("ids") @Size(max = 20, message = "最多可同时删除20个贴皮排单")
List<Long> 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<PageResult<VeneerPlanPageRespVO>> 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<List<VeneerPlanPlateRespVO>> 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<List<VeneerPlanSortRespVO>> getVeneerPlanSortPage() {
return success(veneerPlanService.getPlanSortPage());
}
/**
* 批量设置未完成开料贴皮排单的优先级。
*
* @param req 排单ID及排序值列表
* @return 是否设置成功
*/
@PutMapping("/setSort")
@Operation(summary = "设置贴皮排单优先级")
@PreAuthorize("@ss.hasPermission('placeorder:priority')")
public CommonResult<Boolean> setVeneerPlanSort(
@Valid @RequestBody List<VeneerPlanSortReqVO> req) {
veneerPlanService.setPlanSort(req);
return success(true);
}
}
@@ -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<Long> orderIds;
@Schema(description = "板材商品编码列表,与订单ID列表互斥")
private List<String> goodsIds;
@Schema(description = "房间ID列表")
private List<Long> roomIds;
@Schema(description = "柜体ID列表")
private List<Long> bodyIds;
}
@@ -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<OrderDO> orderList;
@Schema(description = "未排贴皮及其原板件信息")
private List<UnplannedVeneerPlateRespVO> plateList;
@Schema(description = "原板件造型信息")
private List<OrderModelDO> plateModels;
}
@@ -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;
}
@@ -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<VeneerDecomposePlateRespVO> plateList;
@Schema(description = "板件造型列表")
private List<OrderModelDO> plateModels;
}
@@ -1,6 +1,7 @@
package com.cf.imes.module.executor.controller.admin.veneer.vo; package com.cf.imes.module.executor.controller.admin.veneer.vo;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@@ -8,43 +9,190 @@ import java.math.BigDecimal;
/** /**
* 待贴皮分解板件响应参数。 * 待贴皮分解板件响应参数。
* 板件筛选字段参考未排单 {@code getPlanPlateData.plateList}
* 仅保留贴皮分解业务实际使用的数据。
*/ */
@Data @Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class VeneerDecomposePlateRespVO { public class VeneerDecomposePlateRespVO {
/** @Schema(description = "板件ID")
* 板件主键,仅用于 Service 层按板件去重,不返回前端。
*/
@JsonIgnore
private Long plateId; private Long plateId;
@Schema(description = "房名") @Schema(description = "板材商品编码")
private String roomName; private String goodsId;
@Schema(description = "柜名") @Schema(description = "板材名称")
private String bodyName; private String goodsName;
@Schema(description = "板件编号") @Schema(description = "板件编号")
private String plateNo; private String plateNo;
@Schema(description = "材质") @Schema(description = "自定义板件编号")
private String material; private String customPlateNo;
@Schema(description = "板件名称")
private String name;
@Schema(description = "板件名称,兼容原贴皮分解字段")
private String plateName;
@Schema(description = "颜色") @Schema(description = "颜色")
private String color; private String color;
@Schema(description = "板名称") @Schema(description = "品牌")
private String plateName; private String brand;
@Schema(description = "长度") @Schema(description = "材质")
private String material;
@Schema(description = "板件高度")
private BigDecimal height;
@Schema(description = "板件高度,兼容原贴皮分解长度字段")
private BigDecimal length; private BigDecimal length;
@Schema(description = "宽度") @Schema(description = "板件宽度")
private BigDecimal width; private BigDecimal width;
@Schema(description = "厚度") @Schema(description = "板件厚度")
private BigDecimal thickness; 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 = "面积") @Schema(description = "面积")
private BigDecimal area; 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;
} }
@@ -14,6 +14,12 @@ public class VeneerManagePlateListReqVO {
@Schema(description = "订单ID列表") @Schema(description = "订单ID列表")
private List<Long> orderIds; private List<Long> orderIds;
@Schema(description = "房间ID列表,仅按订单查询时生效")
private List<Long> roomIds;
@Schema(description = "柜体ID列表,仅按订单查询时生效")
private List<Long> bodyIds;
@Schema(description = "板材商品编码列表") @Schema(description = "板材商品编码列表")
private List<String> goodsIds; private List<String> goodsIds;
} }
@@ -32,6 +32,9 @@ public class VeneerManagePlateRespVO {
@Schema(description = "原板件厚(mm)") @Schema(description = "原板件厚(mm)")
private BigDecimal thickness; private BigDecimal thickness;
@Schema(description = "贴皮排单ID0表示未排单")
private Long planId;
@Schema(description = "贴皮商品号") @Schema(description = "贴皮商品号")
private String sourceVeneerNo; private String sourceVeneerNo;
@@ -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;
}
@@ -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<Long> veneerIds;
}
@@ -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<VeneerPlanGroupReqVO> groups;
}
@@ -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<Long> veneerIds;
}
@@ -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<Integer> status;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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<Long> veneerIds;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -113,6 +113,11 @@ public class GoodsDO extends BaseDO {
*/ */
private Integer decomposedVeneerPlateNum; private Integer decomposedVeneerPlateNum;
/**
* 已排贴皮板件数量,所有有效贴皮面均已排单时计数
*/
private Integer plannedVeneerPlateNum;
/** /**
* 板件总面积 * 板件总面积
*/ */
@@ -147,6 +147,11 @@ public class OrderDO extends BaseDO {
*/ */
private Integer decomposedVeneerPlateNum; private Integer decomposedVeneerPlateNum;
/**
* 已排贴皮板件数量,所有有效贴皮面均已排单时计数
*/
private Integer plannedVeneerPlateNum;
/** /**
* 板件总面积 * 板件总面积
*/ */
@@ -98,6 +98,11 @@ public class OrderPlateVeneerDO extends BaseDO {
*/ */
private BigDecimal outerMargin; private BigDecimal outerMargin;
/**
* 贴皮排单ID,0表示未排单。
*/
private Long planId;
/** /**
* 来源贴皮库记录主键,对应 {@code veneers.id}。 * 来源贴皮库记录主键,对应 {@code veneers.id}。
*/ */
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -84,14 +84,6 @@ public interface VeneerDecomposeMapper {
List<PlateDO> selectUndecomposedPlateList(@Param("req") VeneerDecomposeCreateReqVO req, List<PlateDO> selectUndecomposedPlateList(@Param("req") VeneerDecomposeCreateReqVO req,
@Param("plateNos") List<String> plateNos); @Param("plateNos") List<String> plateNos);
/**
* 按固定顺序锁定受影响订单,串行化同一订单的分解与计数重算。
*
* @param orderIds 订单ID列表
* @return 已锁定订单ID列表
*/
List<Long> selectOrderIdForUpdate(@Param("orderIds") List<Long> orderIds);
/** /**
* 将指定原板件标记为已分解。 * 将指定原板件标记为已分解。
* *
@@ -117,4 +109,5 @@ public interface VeneerDecomposeMapper {
*/ */
List<VeneerDecomposedPlateCountDO> selectGoodsDecomposedPlateCount( List<VeneerDecomposedPlateCountDO> selectGoodsDecomposedPlateCount(
@Param("orderGoodsIds") List<Long> orderGoodsIds); @Param("orderGoodsIds") List<Long> orderGoodsIds);
} }
@@ -1,11 +1,15 @@
package com.cf.imes.module.executor.dal.mysql.veneer; package com.cf.imes.module.executor.dal.mysql.veneer;
import com.baomidou.mybatisplus.core.metadata.IPage; 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.VeneerManageGoodsPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsRespVO; 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.VeneerManageOrderPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO; 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.VeneerManagePlateRespVO;
import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -50,13 +54,37 @@ public interface VeneerManageMapper {
IPage<VeneerManageGoodsRespVO> selectGoodsPage(@Param("page") IPage<VeneerManageGoodsRespVO> page, IPage<VeneerManageGoodsRespVO> selectGoodsPage(@Param("page") IPage<VeneerManageGoodsRespVO> page,
@Param("req") VeneerManageGoodsPageReqVO req); @Param("req") VeneerManageGoodsPageReqVO req);
/**
* 分页查询存在待排单贴皮的根订单ID。
*
* @param page 分页参数
* @param req 订单查询条件
* @param orderDateTime 完整订单日期时间范围
* @return 根订单ID分页结果
*/
IPage<Long> selectUnplannedRootOrderIdList(
@Param("page") IPage<VeneerManageOrderRespVO> page,
@Param("req") VeneerManageOrderPageReqVO req,
@Param("orderDateTime") LocalDateTime[] orderDateTime);
/**
* 分页查询存在待排单贴皮的板材。
*
* @param page 分页参数
* @param req 板材查询条件
* @return 板材分页结果
*/
IPage<VeneerManageGoodsRespVO> selectUnplannedGoodsPage(
@Param("page") IPage<VeneerManageGoodsRespVO> page,
@Param("req") VeneerManageGoodsPageReqVO req);
/** /**
* 按订单查询已分解板件贴皮列表。 * 按订单查询已分解板件贴皮列表。
* *
* @param orderIds 订单ID列表 * @param req 订单、房间及柜体查询条件
* @return 已分解板件贴皮列表 * @return 已分解板件贴皮列表
*/ */
List<VeneerManagePlateRespVO> selectOrderPlateList(@Param("orderIds") List<Long> orderIds); List<VeneerManagePlateRespVO> selectOrderPlateList(@Param("req") VeneerManagePlateListReqVO req);
/** /**
* 按板材商品编码查询已分解板件贴皮列表。 * 按板材商品编码查询已分解板件贴皮列表。
@@ -65,4 +93,46 @@ public interface VeneerManageMapper {
* @return 已分解板件贴皮列表 * @return 已分解板件贴皮列表
*/ */
List<VeneerManagePlateRespVO> selectGoodsPlateList(@Param("goodsIds") List<String> goodsIds); List<VeneerManagePlateRespVO> selectGoodsPlateList(@Param("goodsIds") List<String> goodsIds);
/**
* 按互斥业务范围查询待排单贴皮及其原板件信息。
* 同一贴皮可能因多个加工组返回多条,由 Service 层合并。
*
* @param req 订单或板材范围及房间、柜体筛选条件
* @return 待排单贴皮及原板件原始列表
*/
List<UnplannedVeneerPlateRespVO> selectUnplannedPlateList(
@Param("req") UnplannedVeneerPlateDataReqVO req);
/**
* 查询本次已移除的板件贴皮记录。
*
* @param ids 板件贴皮记录ID列表
* @return 已移除板件贴皮记录
*/
List<OrderPlateVeneerDO> selectRemovedPlateVeneerList(@Param("ids") List<Long> ids);
/**
* 批量逻辑删除待排单板件贴皮记录。
*
* @param ids 板件贴皮记录ID列表
* @return 更新行数
*/
int softDeletePendingPlateVeneer(@Param("ids") List<Long> ids);
/**
* 查询原板件下仍然有效的贴皮记录。
*
* @param plateIds 原板件ID列表
* @return 仍存在有效贴皮记录的原板件ID列表
*/
List<Long> selectRemainingPlateIdList(@Param("plateIds") List<Long> plateIds);
/**
* 将已无有效贴皮记录的原板件恢复为未分解状态。
*
* @param plateIds 原板件ID列表
* @return 更新行数
*/
int restorePlateUndecomposed(@Param("plateIds") List<Long> plateIds);
} }
@@ -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<OrderVeneerPlanDO> {
/**
* 分页查询贴皮排单及其关联的贴皮库信息。
*
* @param page 分页参数
* @param req 筛选条件
* @return 贴皮排单分页数据
*/
IPage<VeneerPlanPageRespVO> selectPlanPage(
@Param("page") IPage<VeneerPlanPageRespVO> page,
@Param("req") VeneerPlanPageReqVO req);
/**
* 查询指定排单下的贴皮明细。
*
* @param planId 贴皮排单ID
* @return 排单贴皮明细列表
*/
List<VeneerPlanPlateRespVO> selectPlanPlateList(@Param("planId") Long planId);
/**
* 查询尚未完成开料的贴皮排单优先级列表。
*
* @param openedStatus 已开料状态
* @return 按优先级升序排列的贴皮排单
*/
List<VeneerPlanSortRespVO> selectPlanSortList(
@Param("openedStatus") Integer openedStatus);
/**
* 批量更新尚未完成开料的贴皮排单优先级。
*
* @param sortList 排单ID及排序值列表
* @param openedStatus 已开料状态
* @return 更新行数
*/
int batchUpdatePlanSort(@Param("sortList") List<VeneerPlanSortReqVO> sortList,
@Param("openedStatus") Integer openedStatus);
/**
* 查询属于指定排单且仍为已排单状态的贴皮记录。
*
* @param ids 板件贴皮记录ID列表
* @param planId 贴皮排单ID
* @return 符合条件的贴皮记录
*/
List<OrderPlateVeneerDO> selectPlannedVeneerList(
@Param("ids") List<Long> ids,
@Param("planId") Long planId);
/**
* 将指定贴皮记录从排单中恢复为待排单状态。
*
* @param ids 板件贴皮记录ID列表
* @param planId 贴皮排单ID
* @return 更新行数
*/
int resetPlanVeneer(@Param("ids") List<Long> 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<OrderPlateVeneerDO> selectPendingVeneerList(@Param("ids") List<Long> ids);
/**
* 将一批贴皮记录关联到指定贴皮排单。
*
* @param ids 板件贴皮记录ID列表
* @param planId 贴皮排单ID
* @return 更新行数
*/
int updateVeneerPlanned(@Param("ids") List<Long> ids,
@Param("planId") Long planId);
/**
* 按订单重新统计所有有效贴皮面均已排单的原始板件数量。
*
* @param orderIds 订单ID列表
* @return 订单及其所有贴皮面均已排单的板件数量
*/
List<VeneerPlannedPlateCountDO> selectOrderPlannedVeneerPlateCount(
@Param("orderIds") List<Long> orderIds);
/**
* 按订单材质重新统计所有有效贴皮面均已排单的原始板件数量。
*
* @param orderGoodsIds {@code order_goods} 主键列表
* @return 订单材质及其所有贴皮面均已排单的板件数量
*/
List<VeneerPlannedPlateCountDO> selectGoodsPlannedVeneerPlateCount(
@Param("orderGoodsIds") List<Long> orderGoodsIds);
/**
* 查询待删除排单影响的订单和订单材质范围。
*
* @param planIds 贴皮排单ID列表
* @return 受影响的订单及订单材质
*/
List<OrderPlateVeneerDO> selectPlanAffectedScope(@Param("planIds") List<Long> planIds);
/**
* 将指定排单内的板件贴皮恢复为待排单状态。
*
* @param planIds 贴皮排单ID列表
* @return 更新行数
*/
int resetVeneerPlan(@Param("planIds") List<Long> planIds);
/**
* 物理删除指定状态的贴皮排单。
*
* @param planIds 贴皮排单ID列表
* @param openingStatus 开料中状态
* @param openedStatus 已开料状态
* @return 删除行数
*/
int physicalDeletePlan(@Param("planIds") List<Long> planIds,
@Param("openingStatus") Integer openingStatus,
@Param("openedStatus") Integer openedStatus);
}
@@ -523,7 +523,51 @@ public class PlanServiceImpl implements PlanService {
@Override @Override
public List<PlanRespVO> getPlanSortPage() { public List<PlanRespVO> getPlanSortPage() {
return BeanUtil.copyToList(planMapper.selectPlanSortList(), PlanRespVO.class); List<PlanDO> plans = planMapper.selectPlanSortList();
if (CollUtil.isEmpty(plans)) {
return List.of();
}
List<Long> planIds = plans.stream()
.map(PlanDO::getId)
.toList();
Set<Long> 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<PlateInfoVO> plateInfoList = CollUtil.isEmpty(orderIds)
? List.of()
: planItemMapper.selectOrderPlanGoodsList(planIds, orderIds, getUserOrganId());
// 混单的大板来自实际选择板材,按商品编号批量加载后再映射回排单。
Map<Long, PlateGoodDO> planGoodsMap = new HashMap<>();
List<PlanDO> mixedPlans = plans.stream()
.filter(plan -> ObjectUtil.equals(
PlanTypeEnum.MIXEDORDERPLAN.getType(), plan.getType()))
.toList();
if (CollUtil.isNotEmpty(mixedPlans)) {
List<PlateGoodDO> goodsList = plateGoodMapper.selectGoodsListByGoodsId(
mixedPlans.stream()
.map(PlanDO::getGoodsId)
.distinct()
.toList());
Map<String, PlateGoodDO> goodsMap = goodsList.stream()
.collect(Collectors.toMap(PlateGoodDO::getGoodsId, Function.identity()));
mixedPlans.forEach(plan -> planGoodsMap.put(
plan.getId(), goodsMap.get(plan.getGoodsId())));
}
List<PlanRespVO> 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); stringRedisTemplate.delete(lockKey);
} }
} }
} }
@@ -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.VeneerDecomposeGoodsRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderPageReqVO; 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.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.VeneerDecomposePlatePageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO;
import java.util.List; import java.util.List;
@@ -36,17 +36,17 @@ public interface VeneerDecomposeService {
* 按订单查询未贴皮分解板件列表。 * 按订单查询未贴皮分解板件列表。
* *
* @param req 查询条件,必须包含订单 ID 列表 * @param req 查询条件,必须包含订单 ID 列表
* @return 去重后的板件列表 * @return 去重后的板件及板件造型数据
*/ */
List<VeneerDecomposePlateRespVO> getOrderPlateList(VeneerDecomposePlatePageReqVO req); VeneerDecomposePlateDataRespVO getOrderPlateList(VeneerDecomposePlatePageReqVO req);
/** /**
* 按板材查询未贴皮分解板件列表。 * 按板材查询未贴皮分解板件列表。
* *
* @param req 查询条件,必须包含商品编码列表 * @param req 查询条件,必须包含商品编码列表
* @return 去重后的板件列表 * @return 去重后的板件及板件造型数据
*/ */
List<VeneerDecomposePlateRespVO> getGoodsPlateList(VeneerDecomposePlatePageReqVO req); VeneerDecomposePlateDataRespVO getGoodsPlateList(VeneerDecomposePlatePageReqVO req);
/** /**
* 将原板件分解为 A 面和/或 B 面贴皮记录。 * 将原板件分解为 A 面和/或 B 面贴皮记录。
@@ -54,4 +54,12 @@ public interface VeneerDecomposeService {
* @param req 分解参数 * @param req 分解参数
*/ */
void decompose(VeneerDecomposeCreateReqVO req); void decompose(VeneerDecomposeCreateReqVO req);
/**
* 根据原板件当前分解状态重新统计订单和订单板材的已分解板件数量。
*
* @param orderIds 订单ID列表
* @param orderGoodsIds {@code order_goods} 主键列表
*/
void refreshDecomposedPlateCount(List<Long> orderIds, List<Long> orderGoodsIds);
} }
@@ -2,21 +2,26 @@ package com.cf.imes.module.executor.service.veneer;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; 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.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; 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.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.date.LocalDateTimeUtils; 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.VeneerDecomposeCreateReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeGoodsPageReqVO; 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.VeneerDecomposeGoodsRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposeOrderPageReqVO; 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.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.VeneerDecomposePlatePageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO; 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.goods.GoodsDO;
import com.cf.imes.module.executor.dal.dataobject.order.OrderDO; 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.plate.PlateDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO; import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDecomposedPlateCountDO; 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.OrderPlateVeneerMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerDecomposeMapper; 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.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 jakarta.annotation.Resource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -35,6 +42,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -63,31 +71,6 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
*/ */
private static final int DECOMPOSE_STAT_BATCH_SIZE = 100; 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 @Resource
private VeneerDecomposeMapper veneerDecomposeMapper; private VeneerDecomposeMapper veneerDecomposeMapper;
@@ -125,39 +108,91 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
} }
@Override @Override
public List<VeneerDecomposePlateRespVO> getOrderPlateList(VeneerDecomposePlatePageReqVO req) { public VeneerDecomposePlateDataRespVO getOrderPlateList(VeneerDecomposePlatePageReqVO req) {
if (CollUtil.isEmpty(req.getOrderIds())) { if (CollUtil.isEmpty(req.getOrderIds())) {
return List.of(); return buildPlateData(List.of());
} }
return distinctPlate(veneerDecomposeMapper.selectOrderPlateList(req)); return buildPlateData(veneerDecomposeMapper.selectOrderPlateList(req));
} }
@Override @Override
public List<VeneerDecomposePlateRespVO> getGoodsPlateList(VeneerDecomposePlatePageReqVO req) { public VeneerDecomposePlateDataRespVO getGoodsPlateList(VeneerDecomposePlatePageReqVO req) {
if (CollUtil.isEmpty(req.getGoodsIds())) { 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<VeneerDecomposePlateRespVO> rawPlates) {
List<VeneerDecomposePlateRespVO> plates = mergePlateDetails(rawPlates);
List<OrderModelDO> 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 数据库查询得到的板件原始列表 * @param plates 数据库查询得到的板件原始列表
* @return 按板件主键去重后的列表 * @return 按板件主键合并后的列表
*/ */
private List<VeneerDecomposePlateRespVO> distinctPlate(List<VeneerDecomposePlateRespVO> plates) { private List<VeneerDecomposePlateRespVO> mergePlateDetails(List<VeneerDecomposePlateRespVO> plates) {
if (CollUtil.isEmpty(plates)) { if (CollUtil.isEmpty(plates)) {
return plates; return plates;
} }
Map<Long, VeneerDecomposePlateRespVO> plateMap = new LinkedHashMap<>(); Map<Long, VeneerDecomposePlateRespVO> plateMap = new LinkedHashMap<>();
Map<Long, Set<String>> groupNameMap = new HashMap<>();
for (VeneerDecomposePlateRespVO plate : plates) { for (VeneerDecomposePlateRespVO plate : plates) {
plateMap.putIfAbsent(plate.getPlateId(), plate); plateMap.putIfAbsent(plate.getPlateId(), plate);
addName(groupNameMap, plate.getPlateId(), plate.getProcessGroupName());
}
for (Map.Entry<Long, VeneerDecomposePlateRespVO> entry : plateMap.entrySet()) {
Long plateId = entry.getKey();
VeneerDecomposePlateRespVO plate = entry.getValue();
plate.setProcessGroupName(joinNames(groupNameMap.get(plateId)));
} }
return new ArrayList<>(plateMap.values()); return new ArrayList<>(plateMap.values());
} }
/**
* 将非空名称加入指定板件的有序去重集合。
*
* @param nameMap 板件名称集合映射
* @param plateId 板件ID
* @param name 待合并名称
*/
private void addName(Map<Long, Set<String>> 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<String> names) {
return CollUtil.isEmpty(names) ? null : String.join(",", names);
}
/** /**
* 执行贴皮分解:一块原板按所选 A/B 面生成一条或两条贴皮记录,原板计数只增加一次。 * 执行贴皮分解:一块原板按所选 A/B 面生成一条或两条贴皮记录,原板计数只增加一次。
* *
@@ -186,11 +221,8 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
throw new ServiceException(VENEER_DECOMPOSE_PLATE_STATUS_CHANGED); throw new ServiceException(VENEER_DECOMPOSE_PLATE_STATUS_CHANGED);
} }
// 先锁定受影响订单,避免同一订单的并发分解互相覆盖重新统计的结果 // 汇总受影响订单,分解完成后以数据库当前状态重新统计已分解板件数量
List<Long> affectedOrderIds = plates.stream().map(PlateDO::getOrderId).distinct().sorted().toList(); List<Long> affectedOrderIds = plates.stream().map(PlateDO::getOrderId).distinct().sorted().toList();
for (List<Long> orderIdBatch : CollUtil.split(affectedOrderIds, DECOMPOSE_BATCH_SIZE)) {
veneerDecomposeMapper.selectOrderIdForUpdate(orderIdBatch);
}
// 从贴皮库加载 A/B 面贴皮,并将贴皮属性以快照形式写入板件贴皮表。 // 从贴皮库加载 A/B 面贴皮,并将贴皮属性以快照形式写入板件贴皮表。
Map<Long, VeneerDO> sourceVeneerMap = loadSourceVeneers(req); Map<Long, VeneerDO> sourceVeneerMap = loadSourceVeneers(req);
@@ -198,11 +230,13 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
for (PlateDO plate : plates) { for (PlateDO plate : plates) {
if (req.getAVeneerId() != null) { if (req.getAVeneerId() != null) {
plateVeneers.add(buildPlateVeneer( 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) { if (req.getBVeneerId() != null) {
plateVeneers.add(buildPlateVeneer( 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); orderPlateVeneerMapper.insertBatch(plateVeneerBatch);
} }
// 以 order_plate 当前状态为准重新统计,不使用本次请求数量做增量计算。 // 以 order_plate 当前状态重新统计,不使用本次请求数量做增量计算。
List<Long> affectedOrderGoodsIds = plates.stream() List<Long> affectedOrderGoodsIds = plates.stream()
.map(PlateDO::getGoodsId).distinct().sorted().toList(); .map(PlateDO::getGoodsId).distinct().sorted().toList();
refreshOrderDecomposedPlateCount(affectedOrderIds); refreshDecomposedPlateCount(affectedOrderIds, affectedOrderGoodsIds);
refreshGoodsDecomposedPlateCount(affectedOrderGoodsIds); }
/**
* 根据原板件当前分解状态重新统计订单和订单板材的已分解板件数量。
*
* @param orderIds 订单ID列表
* @param orderGoodsIds {@code order_goods} 主键列表
*/
@Override
public void refreshDecomposedPlateCount(List<Long> orderIds, List<Long> orderGoodsIds) {
refreshOrderDecomposedPlateCount(orderIds);
refreshGoodsDecomposedPlateCount(orderGoodsIds);
} }
/** /**
@@ -245,7 +290,6 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
List<VeneerDecomposedPlateCountDO> databaseCounts = List<VeneerDecomposedPlateCountDO> databaseCounts =
veneerDecomposeMapper.selectOrderDecomposedPlateCount(orderIdBatch); veneerDecomposeMapper.selectOrderDecomposedPlateCount(orderIdBatch);
Map<Long, Integer> countMap = convertDecomposedPlateCount(databaseCounts); Map<Long, Integer> countMap = convertDecomposedPlateCount(databaseCounts);
for (Long orderId : orderIdBatch) { for (Long orderId : orderIdBatch) {
OrderDO order = orderMap.get(orderId); OrderDO order = orderMap.get(orderId);
if (order == null) { if (order == null) {
@@ -280,7 +324,6 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
List<VeneerDecomposedPlateCountDO> databaseCounts = List<VeneerDecomposedPlateCountDO> databaseCounts =
veneerDecomposeMapper.selectGoodsDecomposedPlateCount(orderGoodsIdBatch); veneerDecomposeMapper.selectGoodsDecomposedPlateCount(orderGoodsIdBatch);
Map<Long, Integer> countMap = convertDecomposedPlateCount(databaseCounts); Map<Long, Integer> countMap = convertDecomposedPlateCount(databaseCounts);
for (Long orderGoodsId : orderGoodsIdBatch) { for (Long orderGoodsId : orderGoodsIdBatch) {
GoodsDO goods = goodsMap.get(orderGoodsId); GoodsDO goods = goodsMap.get(orderGoodsId);
if (goods == null) { if (goods == null) {
@@ -347,12 +390,12 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
* *
* @param plate 原板件 * @param plate 原板件
* @param sourceVeneer 选中的贴皮库数据 * @param sourceVeneer 选中的贴皮库数据
* @param veneerSide 贴皮逻辑面 * @param veneerSide 贴皮逻辑面枚举
* @param outerMargin 外扩余量 * @param outerMargin 外扩余量
* @return 待保存的板件贴皮记录 * @return 待保存的板件贴皮记录
*/ */
private OrderPlateVeneerDO buildPlateVeneer(PlateDO plate, VeneerDO sourceVeneer, private OrderPlateVeneerDO buildPlateVeneer(PlateDO plate, VeneerDO sourceVeneer,
String veneerSide, BigDecimal outerMargin) { VeneerSideEnum veneerSide, BigDecimal outerMargin) {
int plateSurface = resolvePlateSurface(plate.getHoleArrange(), veneerSide); int plateSurface = resolvePlateSurface(plate.getHoleArrange(), veneerSide);
return OrderPlateVeneerDO.builder() return OrderPlateVeneerDO.builder()
.organId(plate.getOrganId()) .organId(plate.getOrganId())
@@ -365,10 +408,11 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
.plateWidth(plate.getWidth()) .plateWidth(plate.getWidth())
.plateThickness(plate.getThickness()) .plateThickness(plate.getThickness())
.holeArrange(plate.getHoleArrange()) .holeArrange(plate.getHoleArrange())
.veneerSide(veneerSide) .veneerSide(veneerSide.getSide())
.plateSurface(plateSurface) .plateSurface(plateSurface)
.veneerNo(plate.getPlateNo() + veneerSide) .veneerNo(plate.getPlateNo() + veneerSide.getSide())
.outerMargin(outerMargin) .outerMargin(outerMargin)
.planId(0L)
.sourceVeneerId(sourceVeneer.getId()) .sourceVeneerId(sourceVeneer.getId())
.sourceVeneerNo(sourceVeneer.getVeneerNo()) .sourceVeneerNo(sourceVeneer.getVeneerNo())
.veneerName(sourceVeneer.getVeneerName()) .veneerName(sourceVeneer.getVeneerName())
@@ -383,14 +427,18 @@ public class VeneerDecomposeServiceImpl implements VeneerDecomposeService {
* 反面排版时 A 面对应反面、B 面对应正面;正面或随意面排版时关系相反。 * 反面排版时 A 面对应反面、B 面对应正面;正面或随意面排版时关系相反。
* *
* @param holeArrange WebCAD 排版面 * @param holeArrange WebCAD 排版面
* @param veneerSide 贴皮逻辑面 * @param veneerSide 贴皮逻辑面枚举
* @return 实际板件面 * @return 实际板件面
*/ */
private int resolvePlateSurface(Integer holeArrange, String veneerSide) { private int resolvePlateSurface(Integer holeArrange, VeneerSideEnum veneerSide) {
boolean reverseArrange = Integer.valueOf(HOLE_ARRANGE_REVERSE).equals(holeArrange); boolean reverseArrange = PlatePlaceHoleEnum.BACK.equals(holeArrange);
if (VENEER_SIDE_A.equals(veneerSide)) { if (VeneerSideEnum.A == veneerSide) {
return reverseArrange ? PLATE_SURFACE_BACK : PLATE_SURFACE_FRONT; return reverseArrange
? VeneerPlateSurfaceEnum.BACK.getSurface()
: VeneerPlateSurfaceEnum.FRONT.getSurface();
} }
return reverseArrange ? PLATE_SURFACE_FRONT : PLATE_SURFACE_BACK; return reverseArrange
? VeneerPlateSurfaceEnum.FRONT.getSurface()
: VeneerPlateSurfaceEnum.BACK.getSurface();
} }
} }
@@ -1,12 +1,15 @@
package com.cf.imes.module.executor.service.veneer; package com.cf.imes.module.executor.service.veneer;
import com.cf.imes.framework.common.pojo.PageResult; 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.VeneerManageGoodsPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsRespVO; 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.VeneerManageOrderPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO; 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.VeneerManagePlateListReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateRespVO; 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.controller.admin.veneer.vo.VeneerManageReselectReqVO;
import java.util.List; import java.util.List;
@@ -32,10 +35,26 @@ public interface VeneerManageService {
*/ */
PageResult<VeneerManageGoodsRespVO> getGoodsPage(VeneerManageGoodsPageReqVO req); PageResult<VeneerManageGoodsRespVO> getGoodsPage(VeneerManageGoodsPageReqVO req);
/**
* 分页查询存在待排单贴皮的订单。
*
* @param req 订单查询条件
* @return 未排贴皮订单分页结果
*/
PageResult<VeneerManageOrderRespVO> getUnplannedOrderPage(VeneerManageOrderPageReqVO req);
/**
* 分页查询存在待排单贴皮的板材。
*
* @param req 板材查询条件
* @return 未排贴皮板材分页结果
*/
PageResult<VeneerManageGoodsRespVO> getUnplannedGoodsPage(VeneerManageGoodsPageReqVO req);
/** /**
* 按订单查询已分解板件贴皮列表。 * 按订单查询已分解板件贴皮列表。
* *
* @param req 查询条件,必须包含订单ID列表 * @param req 查询条件,必须包含订单ID列表,可按房间和柜体筛选
* @return 已分解板件贴皮列表 * @return 已分解板件贴皮列表
*/ */
List<VeneerManagePlateRespVO> getOrderPlateList(VeneerManagePlateListReqVO req); List<VeneerManagePlateRespVO> getOrderPlateList(VeneerManagePlateListReqVO req);
@@ -48,10 +67,25 @@ public interface VeneerManageService {
*/ */
List<VeneerManagePlateRespVO> getGoodsPlateList(VeneerManagePlateListReqVO req); List<VeneerManagePlateRespVO> getGoodsPlateList(VeneerManagePlateListReqVO req);
/**
* 按订单或板材查询未排贴皮、原板件及板件造型数据。
*
* @param req 互斥查询范围及可选房间、柜体条件
* @return 未排贴皮排单基础数据
*/
UnplannedVeneerPlateDataRespVO getUnplannedPlateData(UnplannedVeneerPlateDataReqVO req);
/** /**
* 批量为已分解板件贴皮重新选择贴皮库数据。 * 批量为已分解板件贴皮重新选择贴皮库数据。
* *
* @param req 板件贴皮记录ID及新贴皮库ID * @param req 板件贴皮记录ID及新贴皮库ID
*/ */
void reselectVeneer(VeneerManageReselectReqVO req); void reselectVeneer(VeneerManageReselectReqVO req);
/**
* 批量移除待排单板件贴皮,并在原板件无剩余有效贴皮时恢复其分解状态。
*
* @param req 待移除的板件贴皮记录ID列表
*/
void removeVeneer(VeneerManageRemoveReqVO req);
} }
@@ -2,6 +2,7 @@ package com.cf.imes.module.executor.service.veneer;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; 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.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; 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.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.date.LocalDateTimeUtils; 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.VeneerManageGoodsPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsRespVO; 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.VeneerManageOrderPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO; 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.VeneerManagePlateListReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateRespVO; 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.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.OrderPlateVeneerDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO; 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.OrderPlateVeneerMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerManageMapper; import com.cf.imes.module.executor.dal.mysql.veneer.VeneerManageMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerMapper; 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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; 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.List;
import java.util.Map;
import java.util.Objects; 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.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_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_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_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_RESELECT_SOURCE_REQUIRED;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_UNPLANNED_SCOPE_INVALID;
/** /**
* 贴皮管理 Service 实现。 * 贴皮管理 Service 实现。
@@ -45,6 +64,16 @@ public class VeneerManageServiceImpl implements VeneerManageService {
*/ */
private static final int RESELECT_BATCH_SIZE = 1000; 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 @Resource
private VeneerManageMapper veneerManageMapper; private VeneerManageMapper veneerManageMapper;
@@ -54,6 +83,12 @@ public class VeneerManageServiceImpl implements VeneerManageService {
@Resource @Resource
private VeneerMapper veneerMapper; private VeneerMapper veneerMapper;
@Resource
private VeneerDecomposeService veneerDecomposeService;
@Resource
private OrderMapper orderMapper;
@Override @Override
public PageResult<VeneerManageOrderRespVO> getOrderPage(VeneerManageOrderPageReqVO req) { public PageResult<VeneerManageOrderRespVO> getOrderPage(VeneerManageOrderPageReqVO req) {
PageDTO<VeneerManageOrderRespVO> page = new PageDTO<>(req.getPageNo(), req.getPageSize()); PageDTO<VeneerManageOrderRespVO> page = new PageDTO<>(req.getPageNo(), req.getPageSize());
@@ -75,12 +110,33 @@ public class VeneerManageServiceImpl implements VeneerManageService {
return new PageResult<>(result.getRecords(), result.getTotal()); return new PageResult<>(result.getRecords(), result.getTotal());
} }
@Override
public PageResult<VeneerManageOrderRespVO> getUnplannedOrderPage(VeneerManageOrderPageReqVO req) {
PageDTO<VeneerManageOrderRespVO> page = new PageDTO<>(req.getPageNo(), req.getPageSize());
IPage<Long> rootPage = veneerManageMapper.selectUnplannedRootOrderIdList(
page, req, LocalDateTimeUtils.generateTimeSection(req.getOrderDate()));
List<Long> rootIds = rootPage.getRecords();
if (CollUtil.isEmpty(rootIds)) {
return new PageResult<>();
}
IPage<VeneerManageOrderRespVO> result = veneerManageMapper.selectOrderList(
new PageDTO<>(req.getPageNo(), PAGE_SIZE_NONE), rootIds);
return new PageResult<>(result.getRecords(), rootPage.getTotal());
}
@Override
public PageResult<VeneerManageGoodsRespVO> getUnplannedGoodsPage(VeneerManageGoodsPageReqVO req) {
IPage<VeneerManageGoodsRespVO> result = veneerManageMapper.selectUnplannedGoodsPage(
new PageDTO<>(req.getPageNo(), req.getPageSize()), req);
return new PageResult<>(result.getRecords(), result.getTotal());
}
@Override @Override
public List<VeneerManagePlateRespVO> getOrderPlateList(VeneerManagePlateListReqVO req) { public List<VeneerManagePlateRespVO> getOrderPlateList(VeneerManagePlateListReqVO req) {
if (CollUtil.isEmpty(req.getOrderIds())) { if (CollUtil.isEmpty(req.getOrderIds())) {
return List.of(); return List.of();
} }
return veneerManageMapper.selectOrderPlateList(req.getOrderIds()); return veneerManageMapper.selectOrderPlateList(req);
} }
@Override @Override
@@ -91,6 +147,128 @@ public class VeneerManageServiceImpl implements VeneerManageService {
return veneerManageMapper.selectGoodsPlateList(req.getGoodsIds()); 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<UnplannedVeneerPlateRespVO> plates = mergeUnplannedPlateDetails(
veneerManageMapper.selectUnplannedPlateList(req));
if (CollUtil.isEmpty(plates)) {
return buildEmptyUnplannedPlateData();
}
// A/B 面共享同一份原板造型,按原板件ID只解析并返回一次。
Map<Long, OrderModelDO> 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<OrderModelDO> plateModels = new ArrayList<>(modelMap.values());
// 订单数量远小于板件数量,按小批次加载,避免一次生成过长的 IN 条件。
List<Long> orderIds = plates.stream()
.map(UnplannedVeneerPlateRespVO::getOrderId)
.distinct()
.sorted()
.toList();
List<OrderDO> orders = new ArrayList<>();
for (List<Long> 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<UnplannedVeneerPlateRespVO> mergeUnplannedPlateDetails(
List<UnplannedVeneerPlateRespVO> rawPlates) {
if (CollUtil.isEmpty(rawPlates)) {
return rawPlates;
}
Map<Long, UnplannedVeneerPlateRespVO> plateMap = new LinkedHashMap<>();
Map<Long, Set<String>> 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<String> groupNames = groupNameMap.get(id);
plate.setProcessGroupName(CollUtil.isEmpty(groupNames) ? null : String.join(",", groupNames));
});
return new ArrayList<>(plateMap.values());
}
/**
* 将所选订单整理为父子结构;缺少父订单的子单作为顶层数据保留。
*
* @param orders 所选贴皮关联的订单
* @return 订单父子结构
*/
private List<OrderDO> buildOrderTree(List<OrderDO> orders) {
if (CollUtil.isEmpty(orders)) {
return List.of();
}
Map<Long, OrderDO> orderMap = new LinkedHashMap<>();
for (OrderDO order : orders) {
orderMap.put(order.getId(), order);
}
List<OrderDO> 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 面记录的贴皮库关联及贴皮属性快照。 * 批量重选贴皮,仅替换所选 A/B 面记录的贴皮库关联及贴皮属性快照。
* *
@@ -122,12 +300,11 @@ public class VeneerManageServiceImpl implements VeneerManageService {
} }
for (List<Long> idBatch : CollUtil.split(ids, RESELECT_BATCH_SIZE)) { for (List<Long> idBatch : CollUtil.split(ids, RESELECT_BATCH_SIZE)) {
// 锁定本批有效记录,避免重选过程中记录被移除 // 查询本批有效记录,提前校验所选数据是否已删除或发生变化
List<OrderPlateVeneerDO> records = orderPlateVeneerMapper.selectList( List<OrderPlateVeneerDO> records = orderPlateVeneerMapper.selectList(
new LambdaQueryWrapper<OrderPlateVeneerDO>() new LambdaQueryWrapper<OrderPlateVeneerDO>()
.select(OrderPlateVeneerDO::getId) .select(OrderPlateVeneerDO::getId)
.in(OrderPlateVeneerDO::getId, idBatch) .in(OrderPlateVeneerDO::getId, idBatch));
.last("FOR UPDATE"));
if (records.size() != idBatch.size()) { if (records.size() != idBatch.size()) {
throw new ServiceException(VENEER_RESELECT_RECORD_STATUS_CHANGED); 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<Long> ids = normalizeRemoveIds(req);
// 删除条件包含待排单及未删除状态;更新数量不一致表示数据状态已发生变化。
for (List<Long> 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<OrderPlateVeneerDO> selectedRecords = new ArrayList<>();
for (List<Long> idBatch : CollUtil.split(ids, REMOVE_BATCH_SIZE)) {
selectedRecords.addAll(veneerManageMapper.selectRemovedPlateVeneerList(idBatch));
}
List<Long> plateIds = selectedRecords.stream()
.map(OrderPlateVeneerDO::getOrderPlateId)
.distinct()
.sorted()
.toList();
// 批量查询删除后仍存在有效 A/B 面的原板件,不逐板执行数据库查询。
Set<Long> remainingPlateIds = new HashSet<>();
for (List<Long> plateIdBatch : CollUtil.split(plateIds, REMOVE_BATCH_SIZE)) {
remainingPlateIds.addAll(veneerManageMapper.selectRemainingPlateIdList(plateIdBatch));
}
List<Long> restoredPlateIds = plateIds.stream()
.filter(plateId -> !remainingPlateIds.contains(plateId))
.toList();
if (CollUtil.isEmpty(restoredPlateIds)) {
return;
}
// 仅当原板已无任何有效贴皮面时恢复为未分解,并校验每批更新数量。
for (List<Long> 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<Long> affectedOrderIds = selectedRecords.stream()
.map(OrderPlateVeneerDO::getOrderId)
.distinct()
.sorted()
.toList();
List<Long> affectedOrderGoodsIds = selectedRecords.stream()
.map(OrderPlateVeneerDO::getOrderGoodsId)
.distinct()
.sorted()
.toList();
// 以 order_plate 当前状态重新统计,不使用删除贴皮记录数进行增量扣减。
veneerDecomposeService.refreshDecomposedPlateCount(
affectedOrderIds, affectedOrderGoodsIds);
}
/**
* 校验并标准化待移除记录ID。
*
* @param req 批量移除参数
* @return 去重并升序排列的记录ID列表
*/
private List<Long> 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();
}
} }
@@ -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<Long> planIds);
/**
* 分页查询已创建的贴皮排单。
*
* @param req 分页及筛选条件
* @return 贴皮排单分页结果
*/
PageResult<VeneerPlanPageRespVO> getPlanPage(VeneerPlanPageReqVO req);
/**
* 查询指定贴皮排单下的全部贴皮明细。
*
* @param planId 贴皮排单ID
* @return 排单贴皮明细列表
*/
List<VeneerPlanPlateRespVO> getPlanPlateList(Long planId);
/**
* 查询未完成开料的贴皮排单优先级列表。
*
* @return 贴皮排单优先级列表
*/
List<VeneerPlanSortRespVO> getPlanSortPage();
/**
* 批量设置未完成开料贴皮排单的优先级。
*
* @param req 排单ID及排序值列表
*/
void setPlanSort(List<VeneerPlanSortReqVO> req);
}
@@ -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<Integer> 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<String, List<Long>> groupIdMap = normalizeGroups(req.getGroups());
Map<String, Long> sourceVeneerIdMap = loadSourceVeneerIdMap(groupIdMap.keySet());
List<Long> veneerIds = groupIdMap.values().stream().flatMap(List::stream).toList();
// 将所有分组ID统一分批查询,避免逐组查询产生N+1,同时控制IN条件长度。
List<OrderPlateVeneerDO> veneers = new ArrayList<>();
for (List<Long> 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<Long, OrderPlateVeneerDO> 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<OrderVeneerPlanDO> plans = new ArrayList<>(groupIdMap.size());
// 前端已按贴皮商品编号分组;后端逐组校验归属,并为每个分组创建一个排单。
for (Map.Entry<String, List<Long>> entry : groupIdMap.entrySet()) {
String sourceVeneerNo = entry.getKey();
Long sourceVeneerId = sourceVeneerIdMap.get(sourceVeneerNo);
List<Long> groupIds = entry.getValue();
List<OrderPlateVeneerDO> 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<Long> 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<Long> affectedOrderIds = veneers.stream()
.map(OrderPlateVeneerDO::getOrderId)
.distinct()
.sorted()
.toList();
List<Long> 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<OrderVeneerPlanDO>()
.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<OrderVeneerPlanDO>()
.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<OrderVeneerPlanDO>()
.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<Long> veneerIds = normalizePlanVeneerIds(
req == null ? null : req.getVeneerIds());
// 分批加载全部待排贴皮,避免大IN条件,并校验贴皮商品与目标排单一致。
List<OrderPlateVeneerDO> veneers = new ArrayList<>();
for (List<Long> 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<Long> 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<Long> veneerIds = normalizePlanVeneerIds(
req == null ? null : req.getVeneerIds());
// 仅允许移除当前排单中的有效贴皮,并保留归属范围用于后续重新统计。
List<OrderPlateVeneerDO> veneers = new ArrayList<>();
for (List<Long> 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<Long> 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<VeneerPlanPageRespVO> getPlanPage(VeneerPlanPageReqVO req) {
IPage<VeneerPlanPageRespVO> page = veneerPlanMapper.selectPlanPage(
new PageDTO<>(req.getPageNo(), req.getPageSize()),
req);
return new PageResult<>(page.getRecords(), page.getTotal());
}
@Override
public List<VeneerPlanPlateRespVO> getPlanPlateList(Long planId) {
if (planId == null) {
return List.of();
}
return veneerPlanMapper.selectPlanPlateList(planId);
}
@Override
public List<VeneerPlanSortRespVO> getPlanSortPage() {
return veneerPlanMapper.selectPlanSortList(
VeneerPlanStatusEnum.OPENED.getStatus());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void setPlanSort(List<VeneerPlanSortReqVO> req) {
List<VeneerPlanSortReqVO> normalizedSortList = normalizePlanSortList(req);
// 更新条件排除已开料状态并递增version;任意排单状态发生变化时整次排序回滚。
for (List<VeneerPlanSortReqVO> 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<Long> planIds) {
List<Long> normalizedPlanIds = normalizeDeletePlanIds(planIds);
List<OrderVeneerPlanDO> 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<OrderPlateVeneerDO> 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<Long> affectedOrderIds = affectedScopes.stream()
.map(OrderPlateVeneerDO::getOrderId)
.distinct()
.sorted()
.toList();
List<Long> affectedOrderGoodsIds = affectedScopes.stream()
.map(OrderPlateVeneerDO::getOrderGoodsId)
.distinct()
.sorted()
.toList();
refreshPlannedVeneerPlateCount(affectedOrderIds, affectedOrderGoodsIds);
}
/**
* 清洗并校验待删除贴皮排单ID列表。
*
* @param planIds 原始贴皮排单ID列表
* @return 去空、去重并排序后的贴皮排单ID列表
*/
private List<Long> normalizeDeletePlanIds(List<Long> planIds) {
if (CollUtil.isEmpty(planIds) || planIds.stream().anyMatch(Objects::isNull)) {
throw new ServiceException(VENEER_PLAN_DELETE_RECORD_REQUIRED);
}
List<Long> 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<VeneerPlanSortReqVO> normalizePlanSortList(List<VeneerPlanSortReqVO> req) {
if (CollUtil.isEmpty(req)) {
throw new ServiceException(VENEER_PLAN_SORT_RECORD_REQUIRED);
}
Set<Long> 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<Integer> 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<Long> normalizePlanVeneerIds(List<Long> veneerIds) {
if (CollUtil.isEmpty(veneerIds) || veneerIds.stream().anyMatch(Objects::isNull)) {
throw new ServiceException(VENEER_PLAN_PLATE_RECORD_REQUIRED);
}
List<Long> 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<OrderVeneerPlanDO>()
.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<OrderPlateVeneerDO> veneers) {
List<Long> orderIds = veneers.stream()
.map(OrderPlateVeneerDO::getOrderId)
.distinct()
.sorted()
.toList();
List<Long> orderGoodsIds = veneers.stream()
.map(OrderPlateVeneerDO::getOrderGoodsId)
.distinct()
.sorted()
.toList();
refreshPlannedVeneerPlateCount(orderIds, orderGoodsIds);
}
/**
* 按贴皮商品编号批量查询组织内贴皮库主键,并校验编号唯一性和完整性。
*
* @param sourceVeneerNos 本次排单涉及的贴皮商品编号集合
* @return 贴皮商品编号与贴皮库主键的映射
*/
private Map<String, Long> loadSourceVeneerIdMap(Set<String> sourceVeneerNos) {
List<VeneerDO> sourceVeneers = new ArrayList<>();
List<String> sourceVeneerNoList = new ArrayList<>(sourceVeneerNos);
for (List<String> sourceVeneerNoBatch
: CollUtil.split(sourceVeneerNoList, PLAN_BATCH_SIZE)) {
sourceVeneers.addAll(veneerMapper.selectList(
new LambdaQueryWrapper<VeneerDO>()
.select(VeneerDO::getId, VeneerDO::getVeneerNo)
.in(VeneerDO::getVeneerNo, sourceVeneerNoBatch)));
}
Map<String, Long> 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<Long> orderIds, List<Long> orderGoodsIds) {
refreshOrderPlannedVeneerPlateCount(orderIds);
refreshGoodsPlannedVeneerPlateCount(orderGoodsIds);
}
/**
* 分批重新统计并覆盖订单的已排贴皮板件数量,更新时使用 version 进行乐观锁校验。
*
* @param orderIds 订单ID列表
*/
private void refreshOrderPlannedVeneerPlateCount(List<Long> orderIds) {
for (List<Long> orderIdBatch : CollUtil.split(orderIds, PLAN_STAT_BATCH_SIZE)) {
Map<Long, OrderDO> orderMap = new HashMap<>();
for (OrderDO order : orderMapper.selectByIds(orderIdBatch)) {
orderMap.put(order.getId(), order);
}
Map<Long, Integer> 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<OrderDO>()
.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<Long> orderGoodsIds) {
for (List<Long> orderGoodsIdBatch : CollUtil.split(orderGoodsIds, PLAN_STAT_BATCH_SIZE)) {
Map<Long, GoodsDO> goodsMap = new HashMap<>();
for (GoodsDO goods : goodsMapper.selectByIds(orderGoodsIdBatch)) {
goodsMap.put(goods.getId(), goods);
}
Map<Long, Integer> 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<GoodsDO>()
.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<Long, Integer> convertPlannedVeneerPlateCount(
List<VeneerPlannedPlateCountDO> databaseCounts) {
Map<Long, Integer> countMap = new HashMap<>();
for (VeneerPlannedPlateCountDO databaseCount : databaseCounts) {
countMap.put(databaseCount.getTargetId(), databaseCount.getPlannedVeneerPlateNum());
}
return countMap;
}
/**
* 校验并整理前端提交的贴皮商品编号分组。
* 同一贴皮商品编号只能出现一个分组,同一板件贴皮记录也只能属于一个分组。
*
* @param groups 前端按贴皮商品编号整理的分组列表
* @return 以贴皮商品编号为键、板件贴皮记录ID列表为值的有序分组
*/
private Map<String, List<Long>> normalizeGroups(List<VeneerPlanGroupReqVO> groups) {
if (CollUtil.isEmpty(groups)) {
throw new ServiceException(VENEER_PLAN_GROUP_REQUIRED);
}
Map<String, List<Long>> groupIdMap = new LinkedHashMap<>(groups.size());
Set<Long> 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<Long> 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<Long> normalizeVeneerIds(List<Long> veneerIds) {
if (CollUtil.isEmpty(veneerIds)) {
throw new ServiceException(VENEER_PLAN_RECORD_REQUIRED);
}
List<Long> 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<OrderPlateVeneerDO> 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);
}
}
@@ -101,40 +101,88 @@
</select> </select>
<resultMap id="PlateMap" <resultMap id="PlateMap"
type="com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO"> type="com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDecomposePlateRespVO"
autoMapping="true">
<id property="plateId" column="plateId"/> <id property="plateId" column="plateId"/>
<result property="roomName" column="roomName"/> <result property="modelData" column="modelData"
<result property="bodyName" column="bodyName"/> typeHandler="com.cf.imes.framework.mybatis.core.type.CompressStringTypeHandler"/>
<result property="plateNo" column="plateNo"/>
<result property="material" column="material"/>
<result property="color" column="color"/>
<result property="plateName" column="plateName"/>
<result property="length" column="length"/>
<result property="width" column="width"/>
<result property="thickness" column="thickness"/>
<result property="area" column="area"/>
</resultMap> </resultMap>
<sql id="PlateColumns">
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 &gt; 0 AND op.back_model_count &gt; 0
THEN 1 ELSE 0 END AS doubleSideModel,
CASE WHEN op.front_hole_count &gt; 0 AND op.back_hole_count &gt; 0
THEN 1 ELSE 0 END AS doubleSideHole,
CASE WHEN op.front_model_count &gt; 0 THEN 1 ELSE 0 END AS frontModel,
CASE WHEN op.back_model_count &gt; 0 THEN 1 ELSE 0 END AS backModel,
CASE WHEN op.front_hole_count &gt; 0 THEN 1 ELSE 0 END AS frontHole,
CASE WHEN op.back_hole_count &gt; 0 THEN 1 ELSE 0 END AS backHole,
op.is_side_sculpt AS sideModel,
CASE WHEN op.side_hole_count &gt; 0 THEN 1 ELSE 0 END AS sideHole,
op.model_data AS modelData
</sql>
<select id="selectOrderPlateList" resultMap="PlateMap"> <select id="selectOrderPlateList" resultMap="PlateMap">
SELECT op.id AS plateId, SELECT
ob.room_name AS roomName, <include refid="PlateColumns"/>
ob.name AS bodyName,
op.plate_no AS plateNo,
og.material AS material,
og.color AS color,
op.name AS plateName,
op.height AS length,
op.width AS width,
op.thickness AS thickness,
op.area AS area
FROM order_plate op FROM order_plate op
JOIN order_goods og ON og.order_id = op.order_id JOIN order_goods og ON og.order_id = op.order_id
AND og.id = op.goods_id AND og.id = op.goods_id
JOIN order_item oi ON oi.order_id = op.order_id JOIN order_item oi ON oi.order_id = op.order_id
AND oi.plate_id = op.id AND oi.plate_id = op.id
AND oi.plate_id != 0 AND oi.plate_id != 0
JOIN order_body ob ON ob.order_id = op.order_id LEFT JOIN order_group opg ON opg.order_id = op.order_id
AND ob.id = oi.body_id AND opg.id = oi.group_id
WHERE op.deleted = false WHERE op.deleted = false
AND op.is_decomposed = false AND op.is_decomposed = false
<if test="req.orderIds != null and req.orderIds.size() > 0"> <if test="req.orderIds != null and req.orderIds.size() > 0">
@@ -155,29 +203,20 @@
#{bodyId} #{bodyId}
</foreach> </foreach>
</if> </if>
ORDER BY op.id DESC ORDER BY op.id DESC, oi.group_id
</select> </select>
<select id="selectGoodsPlateList" resultMap="PlateMap"> <select id="selectGoodsPlateList" resultMap="PlateMap">
SELECT op.id AS plateId, SELECT
ob.room_name AS roomName, <include refid="PlateColumns"/>
ob.name AS bodyName,
op.plate_no AS plateNo,
og.material AS material,
og.color AS color,
op.name AS plateName,
op.height AS length,
op.width AS width,
op.thickness AS thickness,
op.area AS area
FROM order_plate op FROM order_plate op
JOIN order_goods og ON og.order_id = op.order_id JOIN order_goods og ON og.order_id = op.order_id
AND og.id = op.goods_id AND og.id = op.goods_id
JOIN order_item oi ON oi.order_id = op.order_id JOIN order_item oi ON oi.order_id = op.order_id
AND oi.plate_id = op.id AND oi.plate_id = op.id
AND oi.plate_id != 0 AND oi.plate_id != 0
JOIN order_body ob ON ob.order_id = op.order_id LEFT JOIN order_group opg ON opg.order_id = op.order_id
AND ob.id = oi.body_id AND opg.id = oi.group_id
WHERE op.deleted = false WHERE op.deleted = false
AND op.is_decomposed = false AND op.is_decomposed = false
<if test="req.goodsIds != null and req.goodsIds.size() > 0"> <if test="req.goodsIds != null and req.goodsIds.size() > 0">
@@ -186,7 +225,7 @@
#{goodsId} #{goodsId}
</foreach> </foreach>
</if> </if>
ORDER BY op.id DESC ORDER BY op.id DESC, oi.group_id
</select> </select>
<select id="selectUndecomposedPlateList" <select id="selectUndecomposedPlateList"
@@ -224,18 +263,6 @@
</otherwise> </otherwise>
</choose> </choose>
ORDER BY op.id ORDER BY op.id
FOR UPDATE
</select>
<select id="selectOrderIdForUpdate" resultType="java.lang.Long">
SELECT id
FROM orders
WHERE id IN
<foreach collection="orderIds" item="orderId" open="(" close=")" separator=",">
#{orderId}
</foreach>
ORDER BY id
FOR UPDATE
</select> </select>
<update id="updatePlateDecomposed"> <update id="updatePlateDecomposed">
@@ -275,4 +302,5 @@
</foreach> </foreach>
GROUP BY op.goods_id GROUP BY op.goods_id
</select> </select>
</mapper> </mapper>
@@ -34,6 +34,7 @@
<result property="length" column="length"/> <result property="length" column="length"/>
<result property="width" column="width"/> <result property="width" column="width"/>
<result property="thickness" column="thickness"/> <result property="thickness" column="thickness"/>
<result property="planId" column="planId"/>
<result property="sourceVeneerNo" column="sourceVeneerNo"/> <result property="sourceVeneerNo" column="sourceVeneerNo"/>
<result property="veneerName" column="veneerName"/> <result property="veneerName" column="veneerName"/>
<result property="productLevel" column="productLevel"/> <result property="productLevel" column="productLevel"/>
@@ -41,6 +42,15 @@
<result property="material" column="material"/> <result property="material" column="material"/>
</resultMap> </resultMap>
<resultMap id="UnplannedPlateMap"
type="com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateRespVO"
autoMapping="true">
<id property="id" column="id"/>
<id property="plateId" column="plateId"/>
<result property="modelData" column="modelData"
typeHandler="com.cf.imes.framework.mybatis.core.type.CompressStringTypeHandler"/>
</resultMap>
<select id="selectRootOrderIdList" resultType="java.lang.Long"> <select id="selectRootOrderIdList" resultType="java.lang.Long">
SELECT o.root_id SELECT o.root_id
FROM orders o FROM orders o
@@ -116,6 +126,78 @@
ORDER BY og.thickness DESC ORDER BY og.thickness DESC
</select> </select>
<select id="selectUnplannedRootOrderIdList" resultType="java.lang.Long">
SELECT o.root_id
FROM orders o
WHERE o.deleted = false
AND IFNULL(o.plate_num, 0) &gt; IFNULL(o.planned_veneer_plate_num, 0)
AND EXISTS (
SELECT 1
FROM order_plate_veneer opv
WHERE opv.order_id = o.id
AND opv.deleted = false
AND opv.plan_id = 0
)
<if test="req.orderId != null and req.orderId != ''">
AND CAST(o.id AS CHAR) LIKE CONCAT('%', #{req.orderId}, '%')
</if>
<if test="req.customOrderNo != null and req.customOrderNo != ''">
AND o.custom_order_no LIKE CONCAT('%', #{req.customOrderNo}, '%')
</if>
<if test="req.customer != null and req.customer != ''">
AND o.customer LIKE CONCAT('%', #{req.customer}, '%')
</if>
<if test="req.address != null and req.address != ''">
AND o.address LIKE CONCAT('%', #{req.address}, '%')
</if>
<if test="orderDateTime != null and orderDateTime.length == 2">
AND o.order_date BETWEEN #{orderDateTime[0]} AND #{orderDateTime[1]}
</if>
GROUP BY o.root_id
ORDER BY o.root_id DESC
</select>
<select id="selectUnplannedGoodsPage" resultMap="GoodsMap">
SELECT og.goods_id AS goodsId,
og.goods_name AS goodsName,
og.material,
og.color,
og.thickness,
SUM(og.plate_num) AS num,
SUM(og.area) AS area
FROM orders o
JOIN order_goods og ON og.order_id = o.id
WHERE o.deleted = false
AND o.status IN (1, 2, 3)
AND og.deleted = false
AND IFNULL(og.plate_num, 0) &gt; IFNULL(og.planned_veneer_plate_num, 0)
AND EXISTS (
SELECT 1
FROM order_plate_veneer opv
WHERE opv.order_id = og.order_id
AND opv.order_goods_id = og.id
AND opv.deleted = false
AND opv.plan_id = 0
)
<if test="req.goodsName != null and req.goodsName != ''">
AND og.goods_name LIKE CONCAT('%', #{req.goodsName}, '%')
</if>
<if test="req.goodsId != null and req.goodsId != ''">
AND og.goods_id LIKE CONCAT('%', #{req.goodsId}, '%')
</if>
<if test="req.material != null and req.material != ''">
AND og.material LIKE CONCAT('%', #{req.material}, '%')
</if>
<if test="req.color != null and req.color != ''">
AND og.color LIKE CONCAT('%', #{req.color}, '%')
</if>
<if test="req.thickness != null">
AND og.thickness = #{req.thickness}
</if>
GROUP BY og.goods_id, og.goods_name, og.material, og.color, og.thickness
ORDER BY og.thickness DESC
</select>
<sql id="PlateVeneerColumns"> <sql id="PlateVeneerColumns">
opv.id, opv.id,
opv.veneer_no AS veneerNo, opv.veneer_no AS veneerNo,
@@ -124,6 +206,7 @@
opv.plate_length AS length, opv.plate_length AS length,
opv.plate_width AS width, opv.plate_width AS width,
opv.plate_thickness AS thickness, opv.plate_thickness AS thickness,
opv.plan_id AS planId,
opv.source_veneer_no AS sourceVeneerNo, opv.source_veneer_no AS sourceVeneerNo,
opv.veneer_name AS veneerName, opv.veneer_name AS veneerName,
opv.product_level AS productLevel, opv.product_level AS productLevel,
@@ -137,9 +220,31 @@
FROM order_plate_veneer opv FROM order_plate_veneer opv
WHERE opv.deleted = false WHERE opv.deleted = false
AND opv.order_id IN AND opv.order_id IN
<foreach collection="orderIds" item="orderId" open="(" close=")" separator=","> <foreach collection="req.orderIds" item="orderId" open="(" close=")" separator=",">
#{orderId} #{orderId}
</foreach> </foreach>
<if test="(req.roomIds != null and req.roomIds.size() > 0)
or (req.bodyIds != null and req.bodyIds.size() > 0)">
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
<if test="req.roomIds != null and req.roomIds.size() > 0">
AND oi.room_id IN
<foreach collection="req.roomIds" item="roomId" open="(" close=")" separator=",">
#{roomId}
</foreach>
</if>
<if test="req.bodyIds != null and req.bodyIds.size() > 0">
AND oi.body_id IN
<foreach collection="req.bodyIds" item="bodyId" open="(" close=")" separator=",">
#{bodyId}
</foreach>
</if>
)
</if>
ORDER BY opv.order_plate_id DESC, opv.veneer_side ASC ORDER BY opv.order_plate_id DESC, opv.veneer_side ASC
</select> </select>
@@ -157,4 +262,176 @@
</foreach> </foreach>
ORDER BY opv.order_plate_id DESC, opv.veneer_side ASC ORDER BY opv.order_plate_id DESC, opv.veneer_side ASC
</select> </select>
<sql id="UnplannedPlateColumns">
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 &gt; 0 AND op.back_model_count &gt; 0
THEN 1 ELSE 0 END AS doubleSideModel,
CASE WHEN op.front_hole_count &gt; 0 AND op.back_hole_count &gt; 0
THEN 1 ELSE 0 END AS doubleSideHole,
CASE WHEN op.front_model_count &gt; 0 THEN 1 ELSE 0 END AS frontModel,
CASE WHEN op.back_model_count &gt; 0 THEN 1 ELSE 0 END AS backModel,
CASE WHEN op.front_hole_count &gt; 0 THEN 1 ELSE 0 END AS frontHole,
CASE WHEN op.back_hole_count &gt; 0 THEN 1 ELSE 0 END AS backHole,
op.is_side_sculpt AS sideModel,
CASE WHEN op.side_hole_count &gt; 0 THEN 1 ELSE 0 END AS sideHole,
op.model_data AS modelData
</sql>
<select id="selectUnplannedPlateList" resultMap="UnplannedPlateMap">
SELECT
<include refid="UnplannedPlateColumns"/>
FROM order_plate_veneer opv
JOIN order_plate op ON op.order_id = opv.order_id
AND op.id = opv.order_plate_id
JOIN order_goods og ON og.order_id = opv.order_id
AND og.id = opv.order_goods_id
AND og.id = op.goods_id
JOIN order_item oi ON oi.order_id = op.order_id
AND oi.plate_id = op.id
AND oi.plate_id != 0
LEFT JOIN order_group opg ON opg.order_id = op.order_id
AND opg.id = oi.group_id
WHERE opv.deleted = false
AND opv.plan_id = 0
AND op.deleted = false
AND og.deleted = false
<choose>
<when test="req.orderIds != null and req.orderIds.size() > 0">
AND opv.order_id IN
<foreach collection="req.orderIds" item="orderId" open="(" close=")" separator=",">
#{orderId}
</foreach>
</when>
<otherwise>
AND og.goods_id IN
<foreach collection="req.goodsIds" item="goodsId" open="(" close=")" separator=",">
#{goodsId}
</foreach>
</otherwise>
</choose>
<if test="req.roomIds != null and req.roomIds.size() > 0">
AND oi.room_id IN
<foreach collection="req.roomIds" item="roomId" open="(" close=")" separator=",">
#{roomId}
</foreach>
</if>
<if test="req.bodyIds != null and req.bodyIds.size() > 0">
AND oi.body_id IN
<foreach collection="req.bodyIds" item="bodyId" open="(" close=")" separator=",">
#{bodyId}
</foreach>
</if>
ORDER BY op.id DESC, opv.veneer_side, oi.group_id
</select>
<select id="selectRemovedPlateVeneerList"
resultType="com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO">
SELECT opv.id,
opv.order_id AS orderId,
opv.order_goods_id AS orderGoodsId,
opv.order_plate_id AS orderPlateId
FROM order_plate_veneer opv
WHERE opv.deleted = true
AND opv.id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<update id="softDeletePendingPlateVeneer">
UPDATE order_plate_veneer
SET deleted = true,
update_time = CURRENT_TIMESTAMP
WHERE deleted = false
AND plan_id = 0
AND id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</update>
<select id="selectRemainingPlateIdList" resultType="java.lang.Long">
SELECT opv.order_plate_id
FROM order_plate_veneer opv
WHERE opv.deleted = false
AND opv.order_plate_id IN
<foreach collection="plateIds" item="plateId" open="(" close=")" separator=",">
#{plateId}
</foreach>
</select>
<update id="restorePlateUndecomposed">
UPDATE order_plate
SET is_decomposed = false,
update_time = CURRENT_TIMESTAMP
WHERE deleted = false
AND is_decomposed = true
AND id IN
<foreach collection="plateIds" item="plateId" open="(" close=")" separator=",">
#{plateId}
</foreach>
</update>
</mapper> </mapper>
@@ -0,0 +1,259 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cf.imes.module.executor.dal.mysql.veneer.VeneerPlanMapper">
<select id="selectPlanPage"
resultType="com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPageRespVO">
SELECT ovp.id,
ovp.sort,
ovp.process_id AS processId,
ovp.process_name AS processName,
CONCAT_WS('-', NULLIF(v.material, ''), NULLIF(v.color, ''),
CONCAT(CAST(v.thickness AS CHAR), 'mm')) AS veneerInfo,
ovp.status,
ovp.is_scheduled AS isScheduled,
ovp.veneer_num AS veneerNum,
ovp.veneer_area AS veneerArea,
ovp.create_time AS createTime,
ovp.produce_time AS produceTime
FROM order_veneer_plan ovp
JOIN veneers v ON v.id = ovp.source_veneer_id
AND v.organ_id = ovp.organ_id
AND v.deleted = false
WHERE ovp.deleted = false
<if test="req.id != null and req.id != ''">
AND CAST(ovp.id AS CHAR) LIKE CONCAT('%', #{req.id}, '%')
</if>
<if test="req.processName != null and req.processName != ''">
AND ovp.process_name LIKE CONCAT('%', #{req.processName}, '%')
</if>
<if test="req.status != null and req.status.size() > 0">
AND ovp.status IN
<foreach collection="req.status" item="status" open="(" close=")" separator=",">
#{status}
</foreach>
</if>
ORDER BY ovp.id DESC
</select>
<select id="selectPlanSortList"
resultType="com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanSortRespVO">
SELECT ovp.id,
ovp.sort,
ovp.process_id AS processId,
ovp.process_name AS processName,
CONCAT_WS('-', NULLIF(v.material, ''), NULLIF(v.color, ''),
CONCAT(CAST(v.thickness AS CHAR), 'mm')) AS veneerInfo,
ovp.status,
ovp.is_scheduled AS isScheduled,
ovp.veneer_num AS veneerNum,
ovp.create_time AS createTime,
ovp.produce_time AS produceTime
FROM order_veneer_plan ovp
JOIN veneers v ON v.id = ovp.source_veneer_id
AND v.organ_id = ovp.organ_id
AND v.deleted = false
WHERE ovp.deleted = false
AND ovp.status != #{openedStatus}
ORDER BY ovp.sort, ovp.id
</select>
<update id="batchUpdatePlanSort">
UPDATE order_veneer_plan
SET sort = CASE id
<foreach collection="sortList" item="item">
WHEN #{item.planId} THEN #{item.sort}
</foreach>
END,
version = version + 1,
update_time = CURRENT_TIMESTAMP
WHERE deleted = false
AND status != #{openedStatus}
AND id IN
<foreach collection="sortList" item="item" open="(" close=")" separator=",">
#{item.planId}
</foreach>
</update>
<select id="selectPlannedVeneerList"
resultType="com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO">
SELECT opv.id,
opv.order_id AS orderId,
opv.order_goods_id AS orderGoodsId,
opv.order_plate_id AS orderPlateId,
opv.source_veneer_id AS sourceVeneerId
FROM order_plate_veneer opv
WHERE opv.deleted = false
AND opv.plan_id = #{planId}
AND opv.id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
ORDER BY opv.id
</select>
<update id="resetPlanVeneer">
UPDATE order_plate_veneer
SET plan_id = 0,
update_time = CURRENT_TIMESTAMP
WHERE deleted = false
AND plan_id = #{planId}
AND id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</update>
<select id="selectPlanStatistics"
resultType="com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlanStatisticsDO">
SELECT COUNT(*) AS veneerNum,
COALESCE(ROUND(SUM(
(opv.plate_length + opv.outer_margin * 2)
* (opv.plate_width + opv.outer_margin * 2) / 1000000
), 4), 0) AS veneerArea
FROM order_plate_veneer opv
WHERE opv.deleted = false
AND opv.plan_id = #{planId}
</select>
<delete id="physicalDeleteEmptyPlan">
DELETE FROM order_veneer_plan
WHERE deleted = false
AND id = #{planId}
AND status NOT IN (#{openingStatus}, #{openedStatus})
AND version = #{version}
</delete>
<select id="selectPlanPlateList"
resultType="com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPlanPlateRespVO">
SELECT opv.veneer_no AS veneerNo,
opv.plate_no AS plateNo,
opv.plate_name AS plateName,
opv.plate_length AS length,
opv.plate_width AS width,
opv.plate_thickness AS thickness,
opv.source_veneer_no AS sourceVeneerNo,
opv.veneer_name AS veneerName,
opv.product_level AS productLevel,
opv.color,
opv.material
FROM order_plate_veneer opv
WHERE opv.deleted = false
AND opv.plan_id = #{planId}
ORDER BY opv.order_plate_id DESC, opv.veneer_side
</select>
<select id="selectPendingVeneerList"
resultType="com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO">
SELECT opv.id,
opv.organ_id AS organId,
opv.order_id AS orderId,
opv.order_goods_id AS orderGoodsId,
opv.order_plate_id AS orderPlateId,
opv.plate_no AS plateNo,
opv.plate_length AS plateLength,
opv.plate_width AS plateWidth,
opv.outer_margin AS outerMargin,
opv.source_veneer_id AS sourceVeneerId,
opv.source_veneer_no AS sourceVeneerNo
FROM order_plate_veneer opv
WHERE opv.deleted = false
AND opv.plan_id = 0
AND opv.id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
ORDER BY opv.id
</select>
<update id="updateVeneerPlanned">
UPDATE order_plate_veneer
SET plan_id = #{planId},
update_time = CURRENT_TIMESTAMP
WHERE deleted = false
AND plan_id = 0
AND id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</update>
<select id="selectOrderPlannedVeneerPlateCount"
resultType="com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlannedPlateCountDO">
SELECT opv.order_id AS targetId,
COUNT(DISTINCT opv.order_plate_id) AS plannedVeneerPlateNum
FROM order_plate_veneer opv
WHERE opv.deleted = false
AND opv.plan_id != 0
AND opv.order_id IN
<foreach collection="orderIds" item="orderId" open="(" close=")" separator=",">
#{orderId}
</foreach>
AND NOT EXISTS (
SELECT 1
FROM order_plate_veneer pending
WHERE pending.order_id = opv.order_id
AND pending.order_plate_id = opv.order_plate_id
AND pending.deleted = false
AND pending.plan_id = 0
)
GROUP BY opv.order_id
</select>
<select id="selectGoodsPlannedVeneerPlateCount"
resultType="com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlannedPlateCountDO">
SELECT opv.order_goods_id AS targetId,
COUNT(DISTINCT opv.order_plate_id) AS plannedVeneerPlateNum
FROM order_plate_veneer opv
WHERE opv.deleted = false
AND opv.plan_id != 0
AND opv.order_goods_id IN
<foreach collection="orderGoodsIds" item="orderGoodsId" open="(" close=")" separator=",">
#{orderGoodsId}
</foreach>
AND NOT EXISTS (
SELECT 1
FROM order_plate_veneer pending
WHERE pending.order_id = opv.order_id
AND pending.order_goods_id = opv.order_goods_id
AND pending.order_plate_id = opv.order_plate_id
AND pending.deleted = false
AND pending.plan_id = 0
)
GROUP BY opv.order_goods_id
</select>
<select id="selectPlanAffectedScope"
resultType="com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO">
SELECT DISTINCT opv.order_id AS orderId,
opv.order_goods_id AS orderGoodsId
FROM order_plate_veneer opv
WHERE opv.deleted = false
AND opv.plan_id IN
<foreach collection="planIds" item="planId" open="(" close=")" separator=",">
#{planId}
</foreach>
</select>
<update id="resetVeneerPlan">
UPDATE order_plate_veneer
SET plan_id = 0,
update_time = CURRENT_TIMESTAMP
WHERE deleted = false
AND plan_id IN
<foreach collection="planIds" item="planId" open="(" close=")" separator=",">
#{planId}
</foreach>
</update>
<delete id="physicalDeletePlan">
DELETE FROM order_veneer_plan
WHERE deleted = false
AND status NOT IN (#{openingStatus}, #{openedStatus})
AND id IN
<foreach collection="planIds" item="planId" open="(" close=")" separator=",">
#{planId}
</foreach>
</delete>
</mapper>
@@ -78,6 +78,35 @@ veneer.decompose.outer.margin.min=外擴餘量不能小於0
veneer.reselect.record.required=請選擇需要重選貼皮的資料 veneer.reselect.record.required=請選擇需要重選貼皮的資料
veneer.reselect.source.required=請選擇新的貼皮 veneer.reselect.source.required=請選擇新的貼皮
veneer.reselect.record.status.changed=板件貼皮資料狀態已變更,請重新整理後再試 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.no.exist=貼皮編號已存在
veneer.import.required.lack=第{0}行存在帶*必填項為空,請檢查檔案 veneer.import.required.lack=第{0}行存在帶*必填項為空,請檢查檔案
@@ -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.record.required=Please select records for veneer reselection
veneer.reselect.source.required=Please select a new veneer veneer.reselect.source.required=Please select a new veneer
veneer.reselect.record.status.changed=Plate veneer data has changed. Please refresh and retry 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.no.exist=veneer no already exists
veneer.import.required.lack=The required field is empty in line {0}, please check the file veneer.import.required.lack=The required field is empty in line {0}, please check the file
@@ -78,6 +78,35 @@ veneer.decompose.outer.margin.min=外扩余量不能小于0
veneer.reselect.record.required=请选择需要重选贴皮的数据 veneer.reselect.record.required=请选择需要重选贴皮的数据
veneer.reselect.source.required=请选择新的贴皮 veneer.reselect.source.required=请选择新的贴皮
veneer.reselect.record.status.changed=板件贴皮数据状态已变更,请刷新后重试 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.no.exist=贴皮编号已存在
veneer.import.required.lack=第{0}行存在带*必填项为空,请检查文件 veneer.import.required.lack=第{0}行存在带*必填项为空,请检查文件