package cn.iocoder.yudao.module.mes.service.pro.bagcode; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.*; import cn.iocoder.yudao.module.mes.controller.admin.pro.batch.vo.MesProBatchPreGenerateReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper; import cn.iocoder.yudao.module.mes.dal.redis.MesCodeRedisDAO; import cn.iocoder.yudao.module.mes.service.pro.pallet.MesProPalletServiceImpl; import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchServiceImpl; import cn.iocoder.yudao.module.mes.framework.qrcode.MesProQrLabelBuilder; 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.List; import java.util.Collection; import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class PalletBatchBusinessCodeTest { private final MesProPalletServiceImpl palletService = new MesProPalletServiceImpl(); private final MesProBatchServiceImpl batchService = new MesProBatchServiceImpl(); private final MesProPalletMapper pallets = mock(MesProPalletMapper.class); private final MesProBatchMapper batches = mock(MesProBatchMapper.class); private final MesProBagCodeMapper bags = mock(MesProBagCodeMapper.class); private final MesCodeRedisDAO codes = mock(MesCodeRedisDAO.class); @BeforeEach void setup() { ReflectionTestUtils.setField(palletService, "palletMapper", pallets); ReflectionTestUtils.setField(palletService, "batchMapper", batches); ReflectionTestUtils.setField(palletService, "bagCodeMapper", bags); ReflectionTestUtils.setField(palletService, "codeRedisDao", codes); ReflectionTestUtils.setField(palletService,"integrityService",mock(cn.iocoder.yudao.module.mes.service.trace.integrity.MesTraceIntegrityService.class)); ReflectionTestUtils.setField(batchService, "batchMapper", batches); ReflectionTestUtils.setField(batchService, "codeRedisDao", codes); AtomicLong sequence = new AtomicLong(1000000000000000L); when(codes.generate()).thenAnswer(invocation -> String.valueOf(sequence.incrementAndGet())); } @Test void preGeneratedPalletsAndBatchesAlreadyHaveIndependentBusinessCodes() { var palletReq = new MesProPalletPreGenerateReqVO(); palletReq.setCount(2); var batchReq = new MesProBatchPreGenerateReqVO(); batchReq.setCount(2); var values = new java.util.HashSet(); palletService.preGeneratePallet(palletReq).forEach(record -> { assertTrue(values.add(record.getTempCode())); assertTrue(values.add(record.getBusinessCode())); }); batchService.preGenerateBatch(batchReq).forEach(record -> { assertTrue(values.add(record.getTempCode())); assertTrue(values.add(record.getBusinessCode())); }); assertEquals(8, values.size()); assertTrue(values.stream().allMatch(code -> code.matches("[0-9]{16}"))); verify(pallets).insertBatch(anyCollection()); verify(batches, times(2)).insert(any(MesProBatchDO.class)); // 预生成只能写入新记录,不应读取或截取数据库中的旧临时码。 verifyNoMoreInteractions(pallets, batches); } @Test @SuppressWarnings({"unchecked", "rawtypes"}) void directPalletCreationCreatesBothCodes() { when(batches.selectById(2L)).thenReturn(new MesProBatchDO().setId(2L).setCode("batch").setStatus(20)); var req = new MesProPalletBatchCreateReqVO(); req.setBatchId(2L); req.setCount(1); palletService.batchCreatePallet(req); ArgumentCaptor> saved = ArgumentCaptor.forClass((Class) Collection.class); verify(pallets).insertBatch(saved.capture()); var pallet = saved.getValue().iterator().next(); assertNotNull(pallet.getBusinessCode()); assertNotEquals(pallet.getCode(), pallet.getBusinessCode()); } @Test void batchBindingKeepsBusinessCodeAndCopiesActualFormalCodeToBags() { var pallet = new MesProPalletDO().setId(1L).setItemId(3L).setTempCode("1111111111111111").setBusinessCode("2222222222222222").setStatus(10); when(pallets.selectByIdForUpdate(1L)).thenReturn(pallet); when(batches.selectByIdForUpdate(2L)).thenReturn(new MesProBatchDO().setId(2L).setCode("BATCH").setStatus(20)); var req = new MesProPalletBindBatchReqVO(); req.setBatchId(2L); req.setIds(List.of(1L)); palletService.bindBatch(req); var update = ArgumentCaptor.forClass(MesProPalletDO.class); verify(pallets).updateById(update.capture()); assertNull(update.getValue().getBusinessCode()); // MyBatis ignores null fields, so stable business code is retained. assertEquals("2222222222222222", pallet.getBusinessCode()); verify(bags).updatePalletCodeByPalletId(1L, update.getValue().getCode()); } @Test void supplementationNeverChangesPreviouslyIssuedPalletOrBatchCode() { when(pallets.selectByIdForUpdate(1L)).thenReturn(new MesProPalletDO().setId(1L).setStatus(10).setBusinessCode("1111111111111111")); when(batches.selectByIdForUpdate(2L)).thenReturn(new MesProBatchDO().setId(2L).setStatus(20).setBusinessCode("2222222222222222")); assertEquals(0, palletService.generateBusinessCodes(List.of(1L, 1L))); assertEquals(0, batchService.generateBusinessCodes(List.of(2L))); verifyNoInteractions(codes); verify(pallets, never()).updateById(any(MesProPalletDO.class)); verify(batches, never()).updateById(any(MesProBatchDO.class)); } @Test void supplementationRejectsCanceledAndMissingBeforeWritingAnyCode() { when(pallets.selectByIdForUpdate(1L)).thenReturn(new MesProPalletDO().setId(1L).setStatus(10)); when(pallets.selectByIdForUpdate(2L)).thenReturn(new MesProPalletDO().setId(2L).setStatus(20)); when(batches.selectByIdForUpdate(1L)).thenReturn(new MesProBatchDO().setId(1L).setStatus(40)); assertThrows(ServiceException.class, () -> palletService.generateBusinessCodes(List.of(1L, 2L))); assertThrows(ServiceException.class, () -> batchService.generateBusinessCodes(List.of(1L))); assertThrows(ServiceException.class, () -> batchService.generateBusinessCodes(List.of(3L))); verifyNoInteractions(codes); verify(pallets, never()).updateById(any(MesProPalletDO.class)); } @Test void publicQueryRemainsSeparateFromInternalBusinessQuery() { palletService.getPalletByCode("old"); batchService.getBatchByCode("old"); verify(pallets).selectByCode("old"); verify(pallets).selectByTempCode("old"); verify(batches).selectByCode("old"); verify(batches).selectByTempCode("old"); palletService.getPalletByScanCode("business"); batchService.getBatchByScanCode("business"); verify(pallets).selectByScanCode("business"); verify(batches).selectByScanCode("business"); assertEquals("1111111111111111", MesProQrLabelBuilder.buildPalletBusinessCodeLabel( new MesProPalletDO().setBusinessCode("1111111111111111")).getCode()); assertEquals("2222222222222222", MesProQrLabelBuilder.buildBatchBusinessCodeLabel( new MesProBatchDO().setBusinessCode("2222222222222222")).getCode()); } }