diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/MesTmToolSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/MesTmToolSaveReqVO.java index 1ba1b3293c..9f91349729 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/MesTmToolSaveReqVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/MesTmToolSaveReqVO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Data; import java.time.LocalDateTime; @@ -16,16 +17,20 @@ public class MesTmToolSaveReqVO { @Schema(description = "工具编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "T-001") @NotEmpty(message = "工具编码不能为空") + @Size(max = 64, message = "工具编码长度不能超过 64 个字符") private String code; @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "5mm 铣刀") @NotEmpty(message = "工具名称不能为空") + @Size(max = 255, message = "工具名称长度不能超过 255 个字符") private String name; @Schema(description = "品牌", example = "三菱") + @Size(max = 255, message = "品牌长度不能超过 255 个字符") private String brand; @Schema(description = "型号规格", example = "M5-100") + @Size(max = 255, message = "型号规格长度不能超过 255 个字符") private String spec; @Schema(description = "工具类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") @@ -36,7 +41,8 @@ public class MesTmToolSaveReqVO { @NotNull(message = "数量不能为空") private Integer quantity; - @Schema(description = "可用数量", example = "1") + @Schema(description = "可用数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "可用数量不能为空") private Integer availableQuantity; @Schema(description = "保养维护类型", example = "1") @@ -53,6 +59,7 @@ public class MesTmToolSaveReqVO { private Integer status; @Schema(description = "备注", example = "备注") + @Size(max = 500, message = "备注长度不能超过 500 个字符") private String remark; - } +} \ No newline at end of file diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/type/MesTmToolTypeSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/type/MesTmToolTypeSaveReqVO.java index e331930f48..49305c8cd7 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/type/MesTmToolTypeSaveReqVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/type/MesTmToolTypeSaveReqVO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Data; @Schema(description = "管理后台 - MES 工具类型新增/修改 Request VO") @@ -14,10 +15,12 @@ public class MesTmToolTypeSaveReqVO { @Schema(description = "类型编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "TT-001") @NotEmpty(message = "类型编码不能为空") + @Size(max = 64, message = "类型编码长度不能超过 64 个字符") private String code; @Schema(description = "类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "铣刀") @NotEmpty(message = "类型名称不能为空") + @Size(max = 255, message = "类型名称长度不能超过 255 个字符") private String name; @Schema(description = "是否编码管理", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") @@ -31,6 +34,7 @@ public class MesTmToolTypeSaveReqVO { private Integer maintenPeriod; @Schema(description = "备注", example = "备注") + @Size(max = 500, message = "备注长度不能超过 500 个字符") private String remark; - } +} \ No newline at end of file diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemconsume/vo/MesWmItemConsumeLinePageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemconsume/vo/MesWmItemConsumeLinePageReqVO.java index 7cfdbdac8d..000554f968 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemconsume/vo/MesWmItemConsumeLinePageReqVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemconsume/vo/MesWmItemConsumeLinePageReqVO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.wm.itemconsume.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -13,6 +14,7 @@ import lombok.ToString; public class MesWmItemConsumeLinePageReqVO extends PageParam { @Schema(description = "报工记录编号", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "报工记录编号不能为空") private Long feedbackId; } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/workstation/MesMdWorkstationToolMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/workstation/MesMdWorkstationToolMapper.java index fcb8268f62..c1ac2f44ca 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/workstation/MesMdWorkstationToolMapper.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/workstation/MesMdWorkstationToolMapper.java @@ -29,4 +29,8 @@ public interface MesMdWorkstationToolMapper extends BaseMapperX details = BeanUtils.toBean(updateReqVO.getItems(), diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java index 3363c6696f..2136db95c1 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java @@ -283,7 +283,7 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService { * 推导工序 ID * *

通过产品 → 工艺路线产品 → 工艺路线 → 校验工位工序是否在该路线中 - *

如果工位的工序在路线中,则返回该 processId;否则返回 null + *

如果工位的工序在路线中,则返回该 processId;否则返回 null(与 KTG 保持一致) * * @param productId 产品 ID(工单产品) * @param workstationProcessId 工位关联的工序 ID diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java index 6e276aab4f..943a0f5e0b 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java @@ -221,8 +221,8 @@ public class MesQcOqcServiceImpl implements MesQcOqcService { return null; } if (Objects.equals(sourceDocType, MesBizTypeConstants.WM_PRODUCT_SALES)) { - MesWmProductSalesLineDO salesLine = productSalesLineService.getProductSalesLine(sourceLineId); - if (salesLine != null && salesLine.getSalesId() != null) { + MesWmProductSalesLineDO salesLine = productSalesLineService.validateProductSalesLineExists(sourceLineId); + if (salesLine.getSalesId() != null) { MesWmProductSalesDO sales = productSalesService.getProductSales(salesLine.getSalesId()); return sales != null ? sales.getCode() : null; } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/tm/tool/MesTmToolTypeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/tm/tool/MesTmToolTypeServiceImpl.java index 664dc4fc74..93c0d8889a 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/tm/tool/MesTmToolTypeServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/tm/tool/MesTmToolTypeServiceImpl.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type.MesTmToolTyp import cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type.MesTmToolTypeSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolTypeDO; import cn.iocoder.yudao.module.mes.dal.mysql.tm.tool.MesTmToolTypeMapper; +import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationToolService; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -37,6 +38,10 @@ public class MesTmToolTypeServiceImpl implements MesTmToolTypeService { @Lazy private MesTmToolService toolService; + @Resource + @Lazy + private MesMdWorkstationToolService workstationToolService; + @Override public Long createToolType(MesTmToolTypeSaveReqVO createReqVO) { // 校验数据 @@ -75,6 +80,10 @@ public class MesTmToolTypeServiceImpl implements MesTmToolTypeService { if (toolService.getToolCountByToolTypeId(id) > 0) { throw exception(TM_TOOL_TYPE_HAS_TOOL); } + // 校验是否被工作站工装资源引用 + if (workstationToolService.getWorkstationToolCountByToolTypeId(id) > 0) { + throw exception(TM_TOOL_TYPE_HAS_WORKSTATION_TOOL); + } // 删除 toolTypeMapper.deleteById(id); diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java index 483a2287e2..adf954f658 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java @@ -39,6 +39,11 @@ import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; @Slf4j public class MesWmBatchServiceImpl implements MesWmBatchService { + /** + * 批次追溯最大递归深度,防止极端场景下性能问题 + */ + private static final int MAX_TRACE_DEPTH = 20; + @Resource private MesWmBatchMapper batchMapper; @Resource @@ -196,11 +201,11 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { @Override public List getForwardBatchList(String code) { - return getForwardBatchList(code, new HashSet<>()); + return getForwardBatchList(code, new HashSet<>(), 0); } - private List getForwardBatchList(String code, Set visited) { - if (code == null || !visited.add(code)) { + private List getForwardBatchList(String code, Set visited, int depth) { + if (code == null || !visited.add(code) || depth >= MAX_TRACE_DEPTH) { return new ArrayList<>(); } List list = batchMapper.selectListByForward(code); @@ -210,18 +215,18 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { // 继续递归查询下游批次 List results = new ArrayList<>(list); for (MesWmBatchDO batch : list) { - results.addAll(getForwardBatchList(batch.getCode(), visited)); + results.addAll(getForwardBatchList(batch.getCode(), visited, depth + 1)); } return results; } @Override public List getBackwardBatchList(String code) { - return getBackwardBatchList(code, new HashSet<>()); + return getBackwardBatchList(code, new HashSet<>(), 0); } - private List getBackwardBatchList(String code, Set visited) { - if (code == null || !visited.add(code)) { + private List getBackwardBatchList(String code, Set visited, int depth) { + if (code == null || !visited.add(code) || depth >= MAX_TRACE_DEPTH) { return new ArrayList<>(); } List list = batchMapper.selectListByBackward(code); @@ -231,7 +236,7 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { // 继续递归查询上游批次 List results = new ArrayList<>(list); for (MesWmBatchDO batch : list) { - results.addAll(getBackwardBatchList(batch.getCode(), visited)); + results.addAll(getBackwardBatchList(batch.getCode(), visited, depth + 1)); } return results; } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineService.java index f1327bc7dc..54e2b19199 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineService.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineService.java @@ -68,6 +68,14 @@ public interface MesWmProductSalesLineService { */ PageResult getProductSalesLinePage(MesWmProductSalesLinePageReqVO pageReqVO); + /** + * 校验销售出库单行存在 + * + * @param id 编号 + * @return 销售出库单行 + */ + MesWmProductSalesLineDO validateProductSalesLineExists(Long id); + /** * OQC 检验完成后,更新销售出库单行的 OQC 质检状态 * diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java index 7ee9711770..5ee07c2beb 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java @@ -124,7 +124,8 @@ public class MesWmProductSalesLineServiceImpl implements MesWmProductSalesLineSe return productSalesLineMapper.selectPage(pageReqVO); } - private MesWmProductSalesLineDO validateProductSalesLineExists(Long id) { + @Override + public MesWmProductSalesLineDO validateProductSalesLineExists(Long id) { MesWmProductSalesLineDO line = productSalesLineMapper.selectById(id); if (line == null) { throw exception(WM_PRODUCT_SALES_LINE_NOT_EXISTS); diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImplTest.java index 98471d337a..d3c8046da6 100644 --- a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImplTest.java +++ b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImplTest.java @@ -8,6 +8,9 @@ import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService; +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; +import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService; @@ -54,6 +57,12 @@ public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { private AdminUserApi adminUserApi; @MockitoBean private MesProFeedbackService feedbackService; + @MockitoBean + private MesProRouteProductService routeProductService; + @MockitoBean + private MesProRouteProcessService routeProcessService; + @MockitoBean + private MesProTaskService taskService; @Test public void testFinishIpqc_writeBack_feedback() { @@ -211,4 +220,153 @@ public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { assertThrows(Exception.class, () -> ipqcService.finishIpqc(ipqc.getId())); } + // ==================== processId 推导行为测试 ==================== + + /** + * 工位有工序 + 该工序在产品工艺路线中 → processId 应该被设置为工位工序 + */ + @Test + public void testCreateIpqc_processId_workstationHasProcess() { + // 准备参数 + Long workstationId = randomLongId(); + Long workOrderId = randomLongId(); + Long productId = randomLongId(); + Long processId = randomLongId(); + Long routeId = randomLongId(); + Long templateId = randomLongId(); + + MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); + reqVO.setCode("IPQC-TEST-001"); + reqVO.setWorkstationId(workstationId); + reqVO.setWorkOrderId(workOrderId); + reqVO.setInspectorUserId(randomLongId()); + + // mock 工位返回有工序 + MesMdWorkstationDO workstation = new MesMdWorkstationDO(); + workstation.setId(workstationId); + workstation.setProcessId(processId); + when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); + // mock 工单 + MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); + workOrder.setId(workOrderId); + workOrder.setProductId(productId); + when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); + when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); + // mock 模板匹配 + MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); + templateItem.setTemplateId(templateId); + when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); + // mock 工艺路线 + MesProRouteProductDO routeProduct = new MesProRouteProductDO(); + routeProduct.setRouteId(routeId); + when(routeProductService.getRouteProductByItemId(productId)).thenReturn(routeProduct); + // mock 工序在路线中 + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) + .thenReturn(new MesProRouteProcessDO()); + + // 调用 + Long ipqcId = ipqcService.createIpqc(reqVO); + + // 断言:processId 应该是工位工序 + MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); + assertEquals(processId, ipqc.getProcessId()); + } + + /** + * 工位没有工序(processId=null)→ processId 应该留 null(与 KTG 对齐,不回退到任务工序) + */ + @Test + public void testCreateIpqc_processId_workstationNoProcess_shouldBeNull() { + // 准备参数 + Long workstationId = randomLongId(); + Long workOrderId = randomLongId(); + Long productId = randomLongId(); + Long taskId = randomLongId(); + Long templateId = randomLongId(); + + MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); + reqVO.setCode("IPQC-TEST-002"); + reqVO.setWorkstationId(workstationId); + reqVO.setWorkOrderId(workOrderId); + reqVO.setTaskId(taskId); // 故意传入 taskId,验证不会回退 + reqVO.setInspectorUserId(randomLongId()); + + // mock 工位没有工序 + MesMdWorkstationDO workstation = new MesMdWorkstationDO(); + workstation.setId(workstationId); + workstation.setProcessId(null); // 无工序 + when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); + // mock 工单 + MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); + workOrder.setId(workOrderId); + workOrder.setProductId(productId); + when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); + when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); + // mock 模板匹配 + MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); + templateItem.setTemplateId(templateId); + when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); + + // 调用 + Long ipqcId = ipqcService.createIpqc(reqVO); + + // 断言:processId 应该是 null,即使传入了 taskId 也不应该回退 + MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); + assertNull(ipqc.getProcessId(), + "工位无工序时 processId 应为 null(与 KTG 对齐),不应回退到任务工序"); + // 断言:不应该读取任务的 processId + verify(routeProductService, never()).getRouteProductByItemId(any()); + } + + /** + * 工位有工序但该工序不在产品工艺路线中 → processId 应该留 null + */ + @Test + public void testCreateIpqc_processId_processNotInRoute() { + // 准备参数 + Long workstationId = randomLongId(); + Long workOrderId = randomLongId(); + Long productId = randomLongId(); + Long processId = randomLongId(); + Long routeId = randomLongId(); + Long templateId = randomLongId(); + + MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); + reqVO.setCode("IPQC-TEST-003"); + reqVO.setWorkstationId(workstationId); + reqVO.setWorkOrderId(workOrderId); + reqVO.setInspectorUserId(randomLongId()); + + // mock 工位有工序 + MesMdWorkstationDO workstation = new MesMdWorkstationDO(); + workstation.setId(workstationId); + workstation.setProcessId(processId); + when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); + // mock 工单 + MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); + workOrder.setId(workOrderId); + workOrder.setProductId(productId); + when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); + when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); + // mock 模板匹配 + MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); + templateItem.setTemplateId(templateId); + when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); + // mock 工艺路线存在 + MesProRouteProductDO routeProduct = new MesProRouteProductDO(); + routeProduct.setRouteId(routeId); + when(routeProductService.getRouteProductByItemId(productId)).thenReturn(routeProduct); + // mock 工序不在路线中 + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) + .thenReturn(null); + + // 调用 + Long ipqcId = ipqcService.createIpqc(reqVO); + + // 断言:processId 应该是 null + MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); + assertNull(ipqc.getProcessId()); + } + } +