Commit 80183a18a625eba8512430f6bfdfb41aaf17c479
1 parent
a1a35aa5
✨ feat(qc): 增加到货通知单状态校验及文件 URL 格式验证
新增 validateArrivalNoticeReadyForReceipt 方法,确保到货通知单状态为待入库,并校验所有需检行已完成 IQC。同时,增强了文件 URL 格式验证,确保文件值为有效的 http/https URL。
Showing
7 changed files
with
355 additions
and
7 deletions
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 "mes_wm_batch"; | @@ -20,3 +20,4 @@ DELETE FROM "mes_wm_batch"; | ||
| 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 "mes_wm_product_sales_detail" ( | @@ -810,3 +810,23 @@ CREATE TABLE IF NOT EXISTS "mes_wm_product_sales_detail" ( | ||
| 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 | +); |