package cn.iocoder.yudao.module.mes.service.qc.ipqc; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; import cn.iocoder.yudao.module.mes.dal.mysql.qc.ipqc.MesQcIpqcMapper; import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum; import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; 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.indicatorresult.MesQcIndicatorResultService; import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService; import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Import; import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.math.BigDecimal; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; /** * {@link MesQcIpqcServiceImpl} 的单元测试 * * @author 芋道源码 */ @Import(MesQcIpqcServiceImpl.class) public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { @Resource private MesQcIpqcServiceImpl ipqcService; @Resource private MesQcIpqcMapper ipqcMapper; @MockitoBean private MesQcTemplateItemService templateItemService; @MockitoBean private MesQcIpqcLineService ipqcLineService; @MockitoBean private MesProWorkOrderService workOrderService; @MockitoBean private MesMdWorkstationService workstationService; @MockitoBean private MesMdItemService itemService; @MockitoBean private MesQcDefectRecordService defectRecordService; @MockitoBean private AdminUserApi adminUserApi; @MockitoBean private MesProFeedbackService feedbackService; @MockitoBean private MesProRouteProductService routeProductService; @MockitoBean private MesProRouteProcessService routeProcessService; @MockitoBean private MesProTaskService taskService; @MockitoBean private MesQcIndicatorResultService indicatorResultService; @MockitoBean private MesWmProductProduceLineService productProduceLineService; @Test public void testFinishIpqc_writeBack_feedback() { // 准备数据:插入一条草稿状态的 IPQC 单,来源为报工单(PRO_FEEDBACK=304) Long sourceDocId = randomLongId(); Long sourceLineId = randomLongId(); BigDecimal qualifiedQty = BigDecimal.valueOf(80); BigDecimal unqualifiedQty = BigDecimal.valueOf(20); BigDecimal laborScrapQty = BigDecimal.valueOf(5); BigDecimal materialScrapQty = BigDecimal.valueOf(10); BigDecimal otherScrapQty = BigDecimal.valueOf(5); MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setCheckResult(1); // 非空,满足 finishIpqc 的校验 o.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); o.setSourceDocId(sourceDocId); o.setSourceLineId(sourceLineId); o.setQualifiedQuantity(qualifiedQty); o.setUnqualifiedQuantity(unqualifiedQty); o.setLaborScrapQuantity(laborScrapQty); o.setMaterialScrapQuantity(materialScrapQty); o.setOtherScrapQuantity(otherScrapQty); }); ipqcMapper.insert(ipqc); // 调用 ipqcService.finishIpqc(ipqc.getId()); // 断言 1:状态更新为已完成 MesQcIpqcDO updatedIpqc = ipqcMapper.selectById(ipqc.getId()); assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedIpqc.getStatus()); // 断言 2:调用了 feedbackService,传递 sourceLineId // 注意:数量经过 DB 存取后 scale 可能变化(例如 80 → 80.00),所以用 any() 匹配 verify(feedbackService).updateProFeedbackWhenIpqcFinish( eq(sourceDocId), eq(sourceLineId), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class)); } @Test public void testFinishIpqc_writeBack_feedback_withNullQuantities() { // 准备数据:合格品/不合格品/废品数量为 null 的场景,应该 defaultIfNull 为 0 Long sourceDocId = randomLongId(); Long sourceLineId = randomLongId(); MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setCheckResult(1); o.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); o.setSourceDocId(sourceDocId); o.setSourceLineId(sourceLineId); o.setQualifiedQuantity(null); o.setUnqualifiedQuantity(null); o.setLaborScrapQuantity(null); o.setMaterialScrapQuantity(null); o.setOtherScrapQuantity(null); }); ipqcMapper.insert(ipqc); // 调用 ipqcService.finishIpqc(ipqc.getId()); // 断言:所有数量参数都是 BigDecimal.ZERO(defaultIfNull 处理) verify(feedbackService).updateProFeedbackWhenIpqcFinish( eq(sourceDocId), eq(sourceLineId), eq(BigDecimal.ZERO), eq(BigDecimal.ZERO), eq(BigDecimal.ZERO), eq(BigDecimal.ZERO), eq(BigDecimal.ZERO)); } @Test public void testFinishIpqc_writeBack_noSourceDoc() { // 准备数据:sourceDocType 为空,不需要回写 MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setCheckResult(1); o.setSourceDocType(null); o.setSourceDocId(null); o.setSourceLineId(null); }); ipqcMapper.insert(ipqc); // 调用 ipqcService.finishIpqc(ipqc.getId()); // 断言 1:状态更新成功 MesQcIpqcDO updatedIpqc = ipqcMapper.selectById(ipqc.getId()); assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedIpqc.getStatus()); // 断言 2:不应该调用 feedbackService verify(feedbackService, never()).updateProFeedbackWhenIpqcFinish( anyLong(), anyLong(), any(), any(), any(), any(), any()); } @Test public void testFinishIpqc_writeBack_sourceDocTypeDirtyData() { // 准备数据:sourceDocType 非空但 sourceDocId 为空,应该抛异常 MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setCheckResult(1); o.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); o.setSourceDocId(null); // 脏数据 o.setSourceLineId(null); }); ipqcMapper.insert(ipqc); // 调用,并断言异常 assertThrows(IllegalArgumentException.class, () -> ipqcService.finishIpqc(ipqc.getId())); } @Test public void testFinishIpqc_writeBack_unknownSourceDocType() { // 准备数据:sourceDocType 无法识别 MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setCheckResult(1); o.setSourceDocType(99999); // 无法识别的类型 o.setSourceDocId(randomLongId()); o.setSourceLineId(null); }); ipqcMapper.insert(ipqc); // 调用,并断言异常 assertThrows(IllegalArgumentException.class, () -> ipqcService.finishIpqc(ipqc.getId())); } @Test public void testFinishIpqc_checkResultEmpty() { // 准备数据:checkResult 为空,应该报错 MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setCheckResult(null); // 检测结论为空 o.setSourceDocType(null); }); ipqcMapper.insert(ipqc); // 调用,应该抛异常(检测结论必填) assertThrows(Exception.class, () -> ipqcService.finishIpqc(ipqc.getId())); } @Test public void testFinishIpqc_notDraftStatus() { // 准备数据:已完成状态,不能再次完成 MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { o.setStatus(MesQcStatusEnum.FINISHED.getStatus()); o.setCheckResult(1); }); ipqcMapper.insert(ipqc); // 调用,应该抛异常(不是草稿状态) 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(不回退到任务工序) */ @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 任务 MesProTaskDO task = new MesProTaskDO(); task.setId(taskId); task.setWorkOrderId(workOrderId); task.setWorkstationId(workstationId); task.setItemId(productId); when(taskService.validateTaskNotFinished(taskId)).thenReturn(task); // 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,不应回退到任务工序"); // 断言:不应该读取任务的 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()); } // ==================== sourceLineId 校验测试 ==================== /** * sourceDocType=PRO_FEEDBACK 时 sourceLineId 为空 → 创建失败 */ @Test public void testCreateIpqc_sourceLineId_required() { // 准备参数 Long workstationId = randomLongId(); Long workOrderId = randomLongId(); Long productId = randomLongId(); Long feedbackId = randomLongId(); Long templateId = randomLongId(); MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); reqVO.setCode("IPQC-SL-001"); reqVO.setWorkstationId(workstationId); reqVO.setWorkOrderId(workOrderId); reqVO.setInspectorUserId(randomLongId()); reqVO.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); reqVO.setSourceDocId(feedbackId); reqVO.setSourceLineId(null); // 不传 sourceLineId // mock MesMdWorkstationDO workstation = new MesMdWorkstationDO(); workstation.setId(workstationId); workstation.setProcessId(null); when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); workOrder.setId(workOrderId); workOrder.setProductId(productId); when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); templateItem.setTemplateId(templateId); when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); // 调用,断言异常 ServiceException ex = assertThrows(ServiceException.class, () -> ipqcService.createIpqc(reqVO)); assertEquals(QC_IPQC_SOURCE_LINE_REQUIRED.getCode(), ex.getCode()); } /** * sourceLineId 不属于该报工的产出行 → 创建失败 */ @Test public void testCreateIpqc_sourceLineId_notBelong() { // 准备参数 Long workstationId = randomLongId(); Long workOrderId = randomLongId(); Long productId = randomLongId(); Long feedbackId = randomLongId(); Long sourceLineId = randomLongId(); Long templateId = randomLongId(); MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); reqVO.setCode("IPQC-SL-002"); reqVO.setWorkstationId(workstationId); reqVO.setWorkOrderId(workOrderId); reqVO.setInspectorUserId(randomLongId()); reqVO.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); reqVO.setSourceDocId(feedbackId); reqVO.setSourceLineId(sourceLineId); // mock MesMdWorkstationDO workstation = new MesMdWorkstationDO(); workstation.setId(workstationId); workstation.setProcessId(null); when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); workOrder.setId(workOrderId); workOrder.setProductId(productId); when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); templateItem.setTemplateId(templateId); when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); // mock:产出行存在但 feedbackId 不匹配 MesWmProductProduceLineDO line = new MesWmProductProduceLineDO(); line.setId(sourceLineId); line.setFeedbackId(randomLongId()); // 不同的 feedbackId line.setQualityStatus(MesWmQualityStatusEnum.PENDING.getStatus()); when(productProduceLineService.validateProductProduceLineExists(sourceLineId)).thenReturn(line); // 调用,断言异常 ServiceException ex = assertThrows(ServiceException.class, () -> ipqcService.createIpqc(reqVO)); assertEquals(QC_IPQC_SOURCE_LINE_NOT_BELONG.getCode(), ex.getCode()); } /** * sourceLineId 对应的产出行不是待检验状态 → 创建失败 */ @Test public void testCreateIpqc_sourceLineId_notPending() { // 准备参数 Long workstationId = randomLongId(); Long workOrderId = randomLongId(); Long productId = randomLongId(); Long feedbackId = randomLongId(); Long sourceLineId = randomLongId(); Long templateId = randomLongId(); MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); reqVO.setCode("IPQC-SL-003"); reqVO.setWorkstationId(workstationId); reqVO.setWorkOrderId(workOrderId); reqVO.setInspectorUserId(randomLongId()); reqVO.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); reqVO.setSourceDocId(feedbackId); reqVO.setSourceLineId(sourceLineId); // mock MesMdWorkstationDO workstation = new MesMdWorkstationDO(); workstation.setId(workstationId); workstation.setProcessId(null); when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); workOrder.setId(workOrderId); workOrder.setProductId(productId); when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); templateItem.setTemplateId(templateId); when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); // mock:产出行存在且归属正确,但不是 PENDING 状态 MesWmProductProduceLineDO line = new MesWmProductProduceLineDO(); line.setId(sourceLineId); line.setFeedbackId(feedbackId); line.setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus()); // 已合格,非待检 when(productProduceLineService.validateProductProduceLineExists(sourceLineId)).thenReturn(line); // 调用,断言异常 ServiceException ex = assertThrows(ServiceException.class, () -> ipqcService.createIpqc(reqVO)); assertEquals(QC_IPQC_SOURCE_LINE_NOT_PENDING.getCode(), ex.getCode()); } /** * sourceLineId 校验全部通过 → 创建成功 */ @Test public void testCreateIpqc_sourceLineId_success() { // 准备参数 Long workstationId = randomLongId(); Long workOrderId = randomLongId(); Long productId = randomLongId(); Long feedbackId = randomLongId(); Long sourceLineId = randomLongId(); Long templateId = randomLongId(); MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); reqVO.setCode("IPQC-SL-004"); reqVO.setWorkstationId(workstationId); reqVO.setWorkOrderId(workOrderId); reqVO.setInspectorUserId(randomLongId()); reqVO.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); reqVO.setSourceDocId(feedbackId); reqVO.setSourceLineId(sourceLineId); // mock MesMdWorkstationDO workstation = new MesMdWorkstationDO(); workstation.setId(workstationId); workstation.setProcessId(null); when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); workOrder.setId(workOrderId); workOrder.setProductId(productId); when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); templateItem.setTemplateId(templateId); when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); MesProFeedbackDO feedback = new MesProFeedbackDO(); feedback.setId(feedbackId); feedback.setCode("FB-001"); when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(feedback); // mock:产出行存在、归属正确、PENDING 状态 MesWmProductProduceLineDO line = new MesWmProductProduceLineDO(); line.setId(sourceLineId); line.setFeedbackId(feedbackId); line.setQualityStatus(MesWmQualityStatusEnum.PENDING.getStatus()); when(productProduceLineService.validateProductProduceLineExists(sourceLineId)).thenReturn(line); // 调用 Long ipqcId = ipqcService.createIpqc(reqVO); // 断言:创建成功,sourceDocCode 回写 MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); assertNotNull(ipqc); assertEquals(feedbackId, ipqc.getSourceDocId()); assertEquals(sourceLineId, ipqc.getSourceLineId()); assertEquals("FB-001", ipqc.getSourceDocCode()); } }