Commit 80183a18a625eba8512430f6bfdfb41aaf17c479

Authored by YunaiV
1 parent a1a35aa5

✨ feat(qc): 增加到货通知单状态校验及文件 URL 格式验证

新增 validateArrivalNoticeReadyForReceipt 方法,确保到货通知单状态为待入库,并校验所有需检行已完成 IQC。同时,增强了文件 URL 格式验证,确保文件值为有效的 http/https URL。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImpl.java
@@ -29,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional; @@ -29,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional;
29 import org.springframework.validation.annotation.Validated; 29 import org.springframework.validation.annotation.Validated;
30 30
31 import java.math.BigDecimal; 31 import java.math.BigDecimal;
  32 +import java.net.URI;
32 import java.util.*; 33 import java.util.*;
33 34
34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -181,7 +182,7 @@ public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultServ @@ -181,7 +182,7 @@ public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultServ
181 * 按检测项的 resultType 校验明细值格式 182 * 按检测项的 resultType 校验明细值格式
182 * 183 *
183 * <p>FLOAT → 必须可解析为 BigDecimal;INTEGER → 必须可解析为整数; 184 * <p>FLOAT → 必须可解析为 BigDecimal;INTEGER → 必须可解析为整数;
184 - * TEXT / DICT / FILE → 放行 185 + * DICT → 必须属于字典值域;FILE → 必须为 http/https URL;TEXT → 放行
185 */ 186 */
186 private void validateDetailValues(List<MesQcIndicatorResultSaveReqVO.Item> items, 187 private void validateDetailValues(List<MesQcIndicatorResultSaveReqVO.Item> items,
187 Map<Long, MesQcIndicatorDO> indicatorMap) { 188 Map<Long, MesQcIndicatorDO> indicatorMap) {
@@ -219,7 +220,23 @@ public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultServ @@ -219,7 +220,23 @@ public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultServ
219 dictDataApi.validateDictDataList(dictType, Collections.singleton(item.getValue())); 220 dictDataApi.validateDictDataList(dictType, Collections.singleton(item.getValue()));
220 } 221 }
221 } 222 }
222 - // FILE / TEXT 不做格式校验 223 + if (Objects.equals(resultType, MesQcResultValueTypeEnum.FILE.getType())
  224 + && !isHttpUrl(item.getValue())) {
  225 + throw exception(QC_RESULT_VALUE_FORMAT_INVALID,
  226 + "检测项[" + indicator.getName() + "]要求文件 URL,实际值=" + item.getValue());
  227 + }
  228 + // TEXT 不做格式校验
  229 + }
  230 + }
  231 +
  232 + private boolean isHttpUrl(String value) {
  233 + try {
  234 + URI uri = URI.create(value);
  235 + String scheme = uri.getScheme();
  236 + return StrUtil.isNotBlank(uri.getHost())
  237 + && ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme));
  238 + } catch (IllegalArgumentException e) {
  239 + return false;
223 } 240 }
224 } 241 }
225 242
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeService.java
@@ -117,6 +117,15 @@ public interface MesWmArrivalNoticeService { @@ -117,6 +117,15 @@ public interface MesWmArrivalNoticeService {
117 void validateArrivalNoticeAndLineExists(Long noticeId, Long lineId); 117 void validateArrivalNoticeAndLineExists(Long noticeId, Long lineId);
118 118
119 /** 119 /**
  120 + * 校验到货通知单已就绪可被采购入库引用
  121 + * <p>校验状态为待入库 + 所有需检行都已完成 IQC</p>
  122 + *
  123 + * @param id 到货通知单编号
  124 + * @return 到货通知单
  125 + */
  126 + MesWmArrivalNoticeDO validateArrivalNoticeReadyForReceipt(Long id);
  127 +
  128 + /**
120 * 查询指定供应商的到货通知单数量 129 * 查询指定供应商的到货通知单数量
121 * 130 *
122 * @param vendorId 供应商编号 131 * @param vendorId 供应商编号
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
@@ -210,6 +210,23 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService @@ -210,6 +210,23 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService
210 return notice; 210 return notice;
211 } 211 }
212 212
  213 + @Override
  214 + public MesWmArrivalNoticeDO validateArrivalNoticeReadyForReceipt(Long id) {
  215 + // 1. 校验到货通知单存在且状态为待入库
  216 + MesWmArrivalNoticeDO notice = validateArrivalNoticeExists(id);
  217 + if (ObjUtil.notEqual(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus(), notice.getStatus())) {
  218 + throw exception(WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT);
  219 + }
  220 + // 2. 行级防御校验:确保所有需检行都已完成 IQC
  221 + List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(id);
  222 + boolean hasUnchecked = CollectionUtils.anyMatch(lines,
  223 + line -> Boolean.TRUE.equals(line.getIqcCheckFlag()) && line.getIqcId() == null);
  224 + if (hasUnchecked) {
  225 + throw exception(WM_ARRIVAL_NOTICE_IQC_PENDING);
  226 + }
  227 + return notice;
  228 + }
  229 +
213 private void validateCodeUnique(Long id, String code) { 230 private void validateCodeUnique(Long id, String code) {
214 MesWmArrivalNoticeDO notice = arrivalNoticeMapper.selectByCode(code); 231 MesWmArrivalNoticeDO notice = arrivalNoticeMapper.selectByCode(code);
215 if (notice == null) { 232 if (notice == null) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
@@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceip @@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceip
14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO; 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO;
15 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptMapper; 15 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptMapper;
16 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; 16 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
17 -import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum;  
18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemReceiptStatusEnum; 17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemReceiptStatusEnum;
19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; 18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
20 import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService; 19 import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
@@ -92,10 +91,7 @@ public class MesWmItemReceiptServiceImpl implements MesWmItemReceiptService { @@ -92,10 +91,7 @@ public class MesWmItemReceiptServiceImpl implements MesWmItemReceiptService {
92 vendorService.validateVendorExistsAndEnable(reqVO.getVendorId()); 91 vendorService.validateVendorExistsAndEnable(reqVO.getVendorId());
93 // 校验到货通知单存在 92 // 校验到货通知单存在
94 if (reqVO.getNoticeId() != null) { 93 if (reqVO.getNoticeId() != null) {
95 - MesWmArrivalNoticeDO notice = arrivalNoticeService.validateArrivalNoticeExists(reqVO.getNoticeId());  
96 - if (ObjUtil.notEqual(notice.getStatus(), MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus())) {  
97 - throw exception(WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT);  
98 - } 94 + MesWmArrivalNoticeDO notice = arrivalNoticeService.validateArrivalNoticeReadyForReceipt(reqVO.getNoticeId());
99 if (ObjUtil.notEqual(notice.getVendorId(), reqVO.getVendorId())) { 95 if (ObjUtil.notEqual(notice.getVendorId(), reqVO.getVendorId())) {
100 throw exception(WM_ARRIVAL_NOTICE_VENDOR_MISMATCH); 96 throw exception(WM_ARRIVAL_NOTICE_VENDOR_MISMATCH);
101 } 97 }
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.qc.indicatorresult;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.MesQcIndicatorResultPageReqVO;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.MesQcIndicatorResultSaveReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
  10 +import cn.iocoder.yudao.module.mes.dal.mysql.qc.indicatorresult.MesQcIndicatorResultMapper;
  11 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcResultValueTypeEnum;
  12 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
  13 +import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
  14 +import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService;
  15 +import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
  16 +import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
  17 +import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService;
  18 +import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
  19 +import org.junit.jupiter.api.Test;
  20 +import org.springframework.context.annotation.Import;
  21 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  22 +
  23 +import jakarta.annotation.Resource;
  24 +import java.util.List;
  25 +import java.util.Map;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  28 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  29 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  30 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  31 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  32 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  33 +import static org.junit.jupiter.api.Assertions.*;
  34 +import static org.mockito.ArgumentMatchers.*;
  35 +import static org.mockito.Mockito.*;
  36 +
  37 +/**
  38 + * {@link MesQcIndicatorResultServiceImpl} 的单元测试
  39 + *
  40 + * @author 芋道源码
  41 + */
  42 +@Import(MesQcIndicatorResultServiceImpl.class)
  43 +public class MesQcIndicatorResultServiceImplTest extends BaseDbUnitTest {
  44 +
  45 + @Resource
  46 + private MesQcIndicatorResultServiceImpl indicatorResultService;
  47 +
  48 + @Resource
  49 + private MesQcIndicatorResultMapper resultMapper;
  50 +
  51 + @MockitoBean
  52 + private MesQcIndicatorResultDetailService resultDetailService;
  53 + @MockitoBean
  54 + private MesQcIndicatorService indicatorService;
  55 + @MockitoBean
  56 + private MesQcIqcService iqcService;
  57 + @MockitoBean
  58 + private MesQcIpqcService ipqcService;
  59 + @MockitoBean
  60 + private MesQcOqcService oqcService;
  61 + @MockitoBean
  62 + private MesQcRqcService rqcService;
  63 + @MockitoBean
  64 + private DictDataApi dictDataApi;
  65 +
  66 + @Test
  67 + public void testCreateIndicatorResult_success() {
  68 + // 准备参数
  69 + MesQcIndicatorResultSaveReqVO reqVO = buildReqVO("plain text value", MesQcResultValueTypeEnum.TEXT.getType());
  70 + // mock 方法
  71 + mockIqcAndIndicator(reqVO, MesQcResultValueTypeEnum.TEXT.getType());
  72 +
  73 + // 调用
  74 + Long resultId = indicatorResultService.createIndicatorResult(reqVO);
  75 +
  76 + // 断言
  77 + assertNotNull(resultId);
  78 + MesQcIndicatorResultDO dbResult = resultMapper.selectById(resultId);
  79 + assertNotNull(dbResult);
  80 + assertEquals(reqVO.getCode(), dbResult.getCode());
  81 + assertEquals(reqVO.getQcId(), dbResult.getQcId());
  82 + assertEquals(reqVO.getQcType(), dbResult.getQcType());
  83 + assertEquals(100L, dbResult.getItemId()); // 从 mock IQC 获取
  84 + verify(resultDetailService).createDetailList(anyList());
  85 + }
  86 +
  87 + @Test
  88 + public void testCreateIndicatorResult_fileValueInvalid() {
  89 + // 准备参数
  90 + MesQcIndicatorResultSaveReqVO reqVO = buildReqVO("not-a-url", MesQcResultValueTypeEnum.FILE.getType());
  91 + // mock 方法
  92 + mockIqcAndIndicator(reqVO, MesQcResultValueTypeEnum.FILE.getType());
  93 +
  94 + // 调用,并断言异常
  95 + assertServiceException(() -> indicatorResultService.createIndicatorResult(reqVO),
  96 + QC_RESULT_VALUE_FORMAT_INVALID, "检测项[检测项A]要求文件 URL,实际值=not-a-url");
  97 +
  98 + // 断言:未入库
  99 + assertEquals(0, resultMapper.selectCount());
  100 + verify(resultDetailService, never()).createDetailList(anyList());
  101 + }
  102 +
  103 + @Test
  104 + public void testUpdateIndicatorResult_success() {
  105 + // mock 数据
  106 + MesQcIndicatorResultDO dbResult = randomPojo(MesQcIndicatorResultDO.class, o -> {
  107 + o.setQcType(MesQcTypeEnum.IQC.getType());
  108 + });
  109 + resultMapper.insert(dbResult);
  110 + // 准备参数
  111 + MesQcIndicatorResultSaveReqVO reqVO = buildReqVO("updated text", MesQcResultValueTypeEnum.TEXT.getType());
  112 + reqVO.setId(dbResult.getId());
  113 + // mock 方法
  114 + MesQcIndicatorDO indicator = new MesQcIndicatorDO();
  115 + indicator.setId(reqVO.getItems().get(0).getIndicatorId());
  116 + indicator.setName("检测项A");
  117 + indicator.setResultType(MesQcResultValueTypeEnum.TEXT.getType());
  118 + when(indicatorService.validateIndicatorListExists(anySet()))
  119 + .thenReturn(Map.of(indicator.getId(), indicator));
  120 +
  121 + // 调用
  122 + indicatorResultService.updateIndicatorResult(reqVO);
  123 +
  124 + // 断言
  125 + MesQcIndicatorResultDO updatedResult = resultMapper.selectById(dbResult.getId());
  126 + assertEquals(reqVO.getCode(), updatedResult.getCode());
  127 + // qcId/qcType/itemId 不允许改挂,应保持原值
  128 + assertEquals(dbResult.getQcId(), updatedResult.getQcId());
  129 + assertEquals(dbResult.getQcType(), updatedResult.getQcType());
  130 + assertEquals(dbResult.getItemId(), updatedResult.getItemId());
  131 + verify(resultDetailService).createOrUpdateDetailList(anyList());
  132 + }
  133 +
  134 + @Test
  135 + public void testUpdateIndicatorResult_notExists() {
  136 + // 准备参数
  137 + MesQcIndicatorResultSaveReqVO reqVO = buildReqVO("text", MesQcResultValueTypeEnum.TEXT.getType());
  138 + reqVO.setId(randomLongId());
  139 +
  140 + // 调用,并断言异常
  141 + assertServiceException(() -> indicatorResultService.updateIndicatorResult(reqVO),
  142 + QC_RESULT_NOT_EXISTS);
  143 + }
  144 +
  145 + @Test
  146 + public void testDeleteIndicatorResult_success() {
  147 + // mock 数据
  148 + MesQcIndicatorResultDO dbResult = randomPojo(MesQcIndicatorResultDO.class);
  149 + resultMapper.insert(dbResult);
  150 +
  151 + // 调用
  152 + indicatorResultService.deleteIndicatorResult(dbResult.getId());
  153 +
  154 + // 断言
  155 + assertNull(resultMapper.selectById(dbResult.getId()));
  156 + verify(resultDetailService).deleteDetailByResultId(dbResult.getId());
  157 + }
  158 +
  159 + @Test
  160 + public void testDeleteIndicatorResult_notExists() {
  161 + // 调用,并断言异常
  162 + assertServiceException(() -> indicatorResultService.deleteIndicatorResult(randomLongId()),
  163 + QC_RESULT_NOT_EXISTS);
  164 + }
  165 +
  166 + @Test
  167 + public void testGetIndicatorResult() {
  168 + // mock 数据
  169 + MesQcIndicatorResultDO dbResult = randomPojo(MesQcIndicatorResultDO.class);
  170 + resultMapper.insert(dbResult);
  171 +
  172 + // 调用
  173 + MesQcIndicatorResultDO result = indicatorResultService.getIndicatorResult(dbResult.getId());
  174 +
  175 + // 断言
  176 + assertPojoEquals(dbResult, result);
  177 + }
  178 +
  179 + @Test
  180 + public void testGetIndicatorResultPage() {
  181 + // mock 数据
  182 + MesQcIndicatorResultDO dbResult = randomPojo(MesQcIndicatorResultDO.class, o -> {
  183 + o.setQcId(1L);
  184 + o.setQcType(MesQcTypeEnum.IQC.getType());
  185 + o.setCode("SPL-001");
  186 + o.setItemId(100L);
  187 + });
  188 + resultMapper.insert(dbResult);
  189 + // 测试 qcId 不匹配
  190 + resultMapper.insert(cloneIgnoreId(dbResult, o -> o.setQcId(2L)));
  191 + // 测试 qcType 不匹配
  192 + resultMapper.insert(cloneIgnoreId(dbResult, o -> o.setQcType(MesQcTypeEnum.IPQC.getType())));
  193 + // 测试 code 不匹配
  194 + resultMapper.insert(cloneIgnoreId(dbResult, o -> o.setCode("SPL-999")));
  195 + // 测试 itemId 不匹配
  196 + resultMapper.insert(cloneIgnoreId(dbResult, o -> o.setItemId(999L)));
  197 + // 准备参数
  198 + MesQcIndicatorResultPageReqVO pageReqVO = new MesQcIndicatorResultPageReqVO();
  199 + pageReqVO.setQcId(1L);
  200 + pageReqVO.setQcType(MesQcTypeEnum.IQC.getType());
  201 + pageReqVO.setCode("SPL-001");
  202 + pageReqVO.setItemId(100L);
  203 +
  204 + // 调用
  205 + PageResult<MesQcIndicatorResultDO> pageResult = indicatorResultService.getIndicatorResultPage(pageReqVO);
  206 +
  207 + // 断言
  208 + assertEquals(1, pageResult.getTotal());
  209 + assertEquals(1, pageResult.getList().size());
  210 + assertPojoEquals(dbResult, pageResult.getList().get(0));
  211 + }
  212 +
  213 + @Test
  214 + public void testGetIndicatorResultCountByQcIdAndType() {
  215 + // mock 数据
  216 + MesQcIndicatorResultDO result1 = randomPojo(MesQcIndicatorResultDO.class, o -> {
  217 + o.setQcId(1L);
  218 + o.setQcType(MesQcTypeEnum.IQC.getType());
  219 + });
  220 + resultMapper.insert(result1);
  221 + MesQcIndicatorResultDO result2 = randomPojo(MesQcIndicatorResultDO.class, o -> {
  222 + o.setQcId(1L);
  223 + o.setQcType(MesQcTypeEnum.IQC.getType());
  224 + });
  225 + resultMapper.insert(result2);
  226 + // 不匹配
  227 + resultMapper.insert(randomPojo(MesQcIndicatorResultDO.class, o -> {
  228 + o.setQcId(2L);
  229 + o.setQcType(MesQcTypeEnum.IPQC.getType());
  230 + }));
  231 +
  232 + // 调用
  233 + Long count = indicatorResultService.getIndicatorResultCountByQcIdAndType(1L, MesQcTypeEnum.IQC.getType());
  234 +
  235 + // 断言
  236 + assertEquals(2L, count);
  237 + }
  238 +
  239 + @Test
  240 + public void testValidateIndicatorResultExistsByQcIdAndType_success() {
  241 + // mock 数据
  242 + resultMapper.insert(randomPojo(MesQcIndicatorResultDO.class, o -> {
  243 + o.setQcId(1L);
  244 + o.setQcType(MesQcTypeEnum.IQC.getType());
  245 + }));
  246 +
  247 + // 调用(不抛异常即通过)
  248 + indicatorResultService.validateIndicatorResultExistsByQcIdAndType(1L, MesQcTypeEnum.IQC.getType());
  249 + }
  250 +
  251 + @Test
  252 + public void testValidateIndicatorResultExistsByQcIdAndType_notExists() {
  253 + // 调用,并断言异常
  254 + assertServiceException(
  255 + () -> indicatorResultService.validateIndicatorResultExistsByQcIdAndType(randomLongId(), MesQcTypeEnum.IQC.getType()),
  256 + QC_FINISH_INDICATOR_RESULT_REQUIRED);
  257 + }
  258 +
  259 + // ==================== 辅助方法 ====================
  260 +
  261 + private void mockIqcAndIndicator(MesQcIndicatorResultSaveReqVO reqVO, Integer resultType) {
  262 + MesQcIqcDO iqc = new MesQcIqcDO();
  263 + iqc.setId(reqVO.getQcId());
  264 + iqc.setItemId(100L);
  265 + when(iqcService.validateIqcExists(reqVO.getQcId())).thenReturn(iqc);
  266 +
  267 + MesQcIndicatorDO indicator = new MesQcIndicatorDO();
  268 + indicator.setId(reqVO.getItems().get(0).getIndicatorId());
  269 + indicator.setName("检测项A");
  270 + indicator.setResultType(resultType);
  271 + when(indicatorService.validateIndicatorListExists(anySet()))
  272 + .thenReturn(Map.of(indicator.getId(), indicator));
  273 + }
  274 +
  275 + private MesQcIndicatorResultSaveReqVO buildReqVO(String value, Integer resultType) {
  276 + MesQcIndicatorResultSaveReqVO.Item item = new MesQcIndicatorResultSaveReqVO.Item();
  277 + item.setIndicatorId(10L);
  278 + item.setValue(value);
  279 +
  280 + MesQcIndicatorResultSaveReqVO reqVO = new MesQcIndicatorResultSaveReqVO();
  281 + reqVO.setCode("SPL-001");
  282 + reqVO.setQcId(1L);
  283 + reqVO.setQcType(MesQcTypeEnum.IQC.getType());
  284 + reqVO.setItems(List.of(item));
  285 + return reqVO;
  286 + }
  287 +
  288 +}
yudao-module-mes/src/test/resources/sql/clean.sql
@@ -20,3 +20,4 @@ DELETE FROM &quot;mes_wm_batch&quot;; @@ -20,3 +20,4 @@ DELETE FROM &quot;mes_wm_batch&quot;;
20 DELETE FROM "mes_wm_material_stock"; 20 DELETE FROM "mes_wm_material_stock";
21 DELETE FROM "mes_pro_task"; 21 DELETE FROM "mes_pro_task";
22 DELETE FROM "mes_pro_route_process"; 22 DELETE FROM "mes_pro_route_process";
  23 +DELETE FROM "mes_qc_indicator_result";
yudao-module-mes/src/test/resources/sql/create_tables.sql
@@ -810,3 +810,23 @@ CREATE TABLE IF NOT EXISTS &quot;mes_wm_product_sales_detail&quot; ( @@ -810,3 +810,23 @@ CREATE TABLE IF NOT EXISTS &quot;mes_wm_product_sales_detail&quot; (
810 "tenant_id" bigint NOT NULL DEFAULT 0, 810 "tenant_id" bigint NOT NULL DEFAULT 0,
811 PRIMARY KEY ("id") 811 PRIMARY KEY ("id")
812 ); 812 );
  813 +
  814 +-- ----------------------------
  815 +-- MES 检验结果记录
  816 +-- ----------------------------
  817 +CREATE TABLE IF NOT EXISTS "mes_qc_indicator_result" (
  818 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  819 + "code" varchar(64) DEFAULT NULL,
  820 + "qc_id" bigint DEFAULT NULL,
  821 + "qc_type" int DEFAULT NULL,
  822 + "item_id" bigint DEFAULT NULL,
  823 + "sn" varchar(128) DEFAULT NULL,
  824 + "remark" varchar(500) DEFAULT NULL,
  825 + "creator" varchar(64) DEFAULT '',
  826 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  827 + "updater" varchar(64) DEFAULT '',
  828 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  829 + "deleted" bit NOT NULL DEFAULT FALSE,
  830 + "tenant_id" bigint NOT NULL DEFAULT 0,
  831 + PRIMARY KEY ("id")
  832 +);