package cn.iocoder.yudao.module.mes.service.pro.bagcode; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi; import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO; import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.*; import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeReplaceMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper; import cn.iocoder.yudao.module.mes.dal.redis.MesCodeRedisDAO; import cn.iocoder.yudao.module.mes.framework.qrcode.MesProQrLabelBuilder; import cn.iocoder.yudao.module.mes.framework.qrcode.QrCodeImageUtils; import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; import cn.hutool.extra.qrcode.QrCodeUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.springframework.test.util.ReflectionTestUtils; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class BagBusinessCodeTest { private final MesProBagCodeServiceImpl service = new MesProBagCodeServiceImpl(); private final MesProBagCodeMapper bags = mock(MesProBagCodeMapper.class); private final MesCodeRedisDAO codes = mock(MesCodeRedisDAO.class); private final MesMdmItemService items = mock(MesMdmItemService.class); private final MesProBatchMapper batches = mock(MesProBatchMapper.class); private final MesProBagCodeReplaceMapper replacements = mock(MesProBagCodeReplaceMapper.class); private final MdmUnitMeasureApi units = mock(MdmUnitMeasureApi.class); @BeforeEach void setup() { ReflectionTestUtils.setField(service, "bagCodeMapper", bags); ReflectionTestUtils.setField(service, "codeRedisDao", codes); ReflectionTestUtils.setField(service, "mdmItemService", items); ReflectionTestUtils.setField(service, "batchMapper", batches); ReflectionTestUtils.setField(service, "replaceMapper", replacements); ReflectionTestUtils.setField(service, "unitMeasureApi", units); when(items.validateItemExists(1L)).thenReturn(new MesMdmItemDO().setId(1L).setUnitMeasureId(2L)); AtomicLong sequence = new AtomicLong(1000000000000000L); when(codes.generate()).thenAnswer(invocation -> String.valueOf(sequence.incrementAndGet())); when(codes.allocateBagNoStart(anyInt(), any())).thenReturn(1); } @Test void rawGenerationCreatesDistinctPairsInSameRecords() { MesProBagCodeRawGenerateReqVO req = new MesProBagCodeRawGenerateReqVO(); req.setItemId(1L); req.setCount(2); service.generateRawBagCodes(req); assertDistinctPairs(2); } @Test void dataPackageGenerationCreatesBusinessCodes() { when(units.getUnitMeasure(2L)).thenReturn(CommonResult.success(new MdmUnitMeasureRespDTO().setType("吨"))); MesProBagCodeGenerateReqVO req = new MesProBagCodeGenerateReqVO(); req.setItemId(1L); req.setBagCount(2); service.generateBagCodes(req); assertDistinctPairs(2); } @Test @SuppressWarnings({"unchecked", "rawtypes"}) void automaticPalletCarriesVariety() { var pallets=mock(cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper.class); ReflectionTestUtils.setField(service,"palletMapper",pallets); when(units.getUnitMeasure(2L)).thenReturn(CommonResult.success(new MdmUnitMeasureRespDTO().setType("2千克"))); var req=new MesProBagCodeGenerateReqVO();req.setItemId(1L);req.setPalletCount(1); service.generateBagCodes(req); ArgumentCaptor> saved=ArgumentCaptor.forClass((Class)Collection.class); verify(pallets).insertBatch(saved.capture());assertEquals(1L,saved.getValue().iterator().next().getItemId()); } @Test void replacementIssuesNewPairInsteadOfReusingOldBusinessCode() { when(batches.selectByIdForUpdate(2L)).thenReturn(new MesProBatchDO().setId(2L).setStatus(20)); when(bags.selectListByIdsForUpdate(List.of(1L))).thenReturn(List.of(new MesProBagCodeDO() .setId(1L).setBatchId(2L).setStatus(10).setCode("9999999999999999").setBusinessCode("8888888888888888"))); when(bags.cancelById(eq(1L), eq(10), eq(30), any(), any())).thenReturn(1); MesProBagCodeReplaceReqVO req = new MesProBagCodeReplaceReqVO(); req.setBatchId(2L); req.setOldIds(List.of(1L)); req.setReason("破损"); service.replaceBagCodes(req); assertDistinctPairs(1); } @Test void supplementationLocksAndNeverChangesExistingCode() { when(bags.selectListByIdsForUpdate(List.of(1L, 2L))).thenReturn(List.of( new MesProBagCodeDO().setId(1L).setStatus(10), new MesProBagCodeDO().setId(2L).setStatus(20).setBusinessCode("8888888888888888"))); assertEquals(1, service.generateBusinessCodes(List.of(2L, 1L, 1L))); ArgumentCaptor update = ArgumentCaptor.forClass(MesProBagCodeDO.class); verify(bags).updateById(update.capture()); assertEquals(1L, update.getValue().getId()); assertNull(update.getValue().getCode()); assertNotNull(update.getValue().getBusinessCode()); when(bags.selectListByIdsForUpdate(List.of(1L))).thenReturn(List.of(update.getValue().setStatus(10))); assertEquals(0, service.generateBusinessCodes(List.of(1L))); verify(codes, times(1)).generate(); } @Test void canceledOrMissingBagCannotBeSupplemented() { when(bags.selectListByIdsForUpdate(List.of(1L))).thenReturn(List.of(new MesProBagCodeDO().setId(1L).setStatus(30))); assertThrows(ServiceException.class, () -> service.generateBusinessCodes(List.of(1L))); assertThrows(ServiceException.class, () -> service.generateBusinessCodes(List.of(2L))); verifyNoInteractions(codes); verify(bags, never()).updateById(any(MesProBagCodeDO.class)); } @Test void publicLookupUsesTraceColumnAndInternalLookupUsesBoth() { service.getBagCodeByCode("1111111111111111"); verify(bags).selectByCode("1111111111111111"); service.getBagCodeByScanCode("2222222222222222"); verify(bags).selectByScanCode("2222222222222222"); } @Test void businessQrEncodesStoredValueAndRejectsMissingCode() { var label = MesProQrLabelBuilder.buildBagBusinessCodeLabel(new MesProBagCodeDO() .setCode("9999999999999999").setBusinessCode("8888888888888888")); assertEquals("8888888888888888", QrCodeUtil.decode(QrCodeImageUtils.createQrLabel(label.getCode(), label.getFileName()))); assertThrows(ServiceException.class, () -> MesProQrLabelBuilder.buildBagBusinessCodeLabel(new MesProBagCodeDO())); } @SuppressWarnings({"unchecked", "rawtypes"}) private void assertDistinctPairs(int count) { ArgumentCaptor> saved = ArgumentCaptor.forClass((Class) Collection.class); verify(bags).insertBatch(saved.capture()); assertEquals(count, saved.getValue().size()); var values = new HashSet(); for (MesProBagCodeDO bag : saved.getValue()) { assertTrue(bag.getBusinessCode().matches("[0-9]{16}")); assertTrue(values.add(bag.getCode())); assertTrue(values.add(bag.getBusinessCode())); } } }