package cn.iocoder.yudao.module.mes.service.md.autocode; import cn.hutool.core.util.ReflectUtil; import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodePartDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodeRecordDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodeRuleDO; import cn.iocoder.yudao.module.mes.dal.mysql.md.autocode.MesMdAutoCodeRecordMapper; import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodePartTypeEnum; import cn.iocoder.yudao.module.mes.service.md.autocode.strategy.MesMdAutoCodeContext; import cn.iocoder.yudao.module.mes.service.md.autocode.strategy.MesMdAutoCodePartStrategy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; /** * {@link MesMdAutoCodeRecordServiceImpl} 的单元测试 * * @author 芋道源码 */ @ExtendWith(MockitoExtension.class) public class MesMdAutoCodeRecordServiceImplTest { @InjectMocks private MesMdAutoCodeRecordServiceImpl recordService; @Mock private MesMdAutoCodeRecordMapper recordMapper; @Mock private MesMdAutoCodeRuleService ruleService; @Mock private MesMdAutoCodePartService partService; @Mock private MesMdAutoCodePartStrategy fixedCharStrategy; @Mock private MesMdAutoCodePartStrategy serialNumberStrategy; @Mock private MesMdAutoCodePartStrategy inputCharStrategy; @Mock private MesMdAutoCodePartStrategy dateStrategy; @BeforeEach public void setUp() { // 使用反射注入 strategyList List strategyList = Arrays.asList( fixedCharStrategy, serialNumberStrategy, inputCharStrategy, dateStrategy ); when(fixedCharStrategy.getType()).thenReturn(MesMdAutoCodePartTypeEnum.FIXED_CHAR.getType()); when(serialNumberStrategy.getType()).thenReturn(MesMdAutoCodePartTypeEnum.SERIAL_NUMBER.getType()); when(inputCharStrategy.getType()).thenReturn(MesMdAutoCodePartTypeEnum.INPUT_CHAR.getType()); when(dateStrategy.getType()).thenReturn(MesMdAutoCodePartTypeEnum.DATE.getType()); ReflectUtil.setFieldValue(recordService, "strategyList", strategyList); // 手动调用 init 方法初始化策略 map recordService.init(); } @Test public void testGenerateAutoCode_fixedCharAndSerialNumber() { // 准备参数 String ruleCode = "ITEM_CODE"; MesMdAutoCodeRuleDO rule = new MesMdAutoCodeRuleDO().setId(1L).setCode(ruleCode).setPadded(false); MesMdAutoCodePartDO part1 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.FIXED_CHAR.getType()) .setFixCharacter("ITEM_").setLength(5).setSort(1); MesMdAutoCodePartDO part2 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.SERIAL_NUMBER.getType()) .setSerialStartNo(1).setSerialStep(1).setLength(4).setSort(2).setCycleFlag(false); // mock when(ruleService.getAutoCodeRuleByCode(ruleCode)).thenReturn(rule); when(partService.getAutoCodePartListByRuleId(1L)).thenReturn(Arrays.asList(part1, part2)); when(fixedCharStrategy.generate(eq(part1), any(MesMdAutoCodeContext.class))).thenReturn("ITEM_"); when(serialNumberStrategy.generate(eq(part2), any(MesMdAutoCodeContext.class))).thenAnswer(invocation -> { MesMdAutoCodeContext context = invocation.getArgument(1); context.setSerialNo(1L); return "0001"; }); when(recordMapper.selectByResult("ITEM_0001")).thenReturn(null); // 调用 String result = recordService.generateAutoCode(ruleCode, null); // 断言 assertEquals("ITEM_0001", result); ArgumentCaptor captor = ArgumentCaptor.forClass(MesMdAutoCodeRecordDO.class); verify(recordMapper).insert(captor.capture()); MesMdAutoCodeRecordDO record = captor.getValue(); assertEquals(1L, record.getRuleId()); assertEquals("ITEM_0001", record.getResult()); assertEquals(1, record.getSerialNo()); } @Test public void testGenerateAutoCode_retryWhenResultExists() { // 准备参数 String ruleCode = "DV_MACHINERY_CODE"; MesMdAutoCodeRuleDO rule = new MesMdAutoCodeRuleDO().setId(5L).setCode(ruleCode).setPadded(false); MesMdAutoCodePartDO part1 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.FIXED_CHAR.getType()) .setFixCharacter("M").setLength(1).setSort(1); MesMdAutoCodePartDO part2 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.SERIAL_NUMBER.getType()) .setSerialStartNo(1).setSerialStep(1).setLength(5).setSort(2).setCycleFlag(false); // mock when(ruleService.getAutoCodeRuleByCode(ruleCode)).thenReturn(rule); when(partService.getAutoCodePartListByRuleId(5L)).thenReturn(Arrays.asList(part1, part2)); when(fixedCharStrategy.generate(eq(part1), any(MesMdAutoCodeContext.class))).thenReturn("M"); long[] serialNoRef = {0L}; when(serialNumberStrategy.generate(eq(part2), any(MesMdAutoCodeContext.class))).thenAnswer(invocation -> { MesMdAutoCodeContext context = invocation.getArgument(1); long serialNo = ++serialNoRef[0]; context.setSerialNo(serialNo); return String.format("%05d", serialNo); }); when(recordMapper.selectByResult("M00001")).thenReturn(new MesMdAutoCodeRecordDO()); when(recordMapper.selectByResult("M00002")).thenReturn(new MesMdAutoCodeRecordDO()); when(recordMapper.selectByResult("M00003")).thenReturn(null); // 调用 String result = recordService.generateAutoCode(ruleCode, null); // 断言 assertEquals("M00003", result); ArgumentCaptor captor = ArgumentCaptor.forClass(MesMdAutoCodeRecordDO.class); verify(recordMapper).insert(captor.capture()); MesMdAutoCodeRecordDO record = captor.getValue(); assertEquals(5L, record.getRuleId()); assertEquals("M00003", record.getResult()); assertEquals(3L, record.getSerialNo()); } @Test public void testGenerateAutoCode_withInputChar() { // 准备参数 String ruleCode = "CLIENT_CODE"; String inputChar = "A"; MesMdAutoCodeRuleDO rule = new MesMdAutoCodeRuleDO().setId(2L).setCode(ruleCode).setPadded(false); MesMdAutoCodePartDO part1 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.INPUT_CHAR.getType()) .setLength(1).setSort(1); MesMdAutoCodePartDO part2 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.SERIAL_NUMBER.getType()) .setSerialStartNo(1).setSerialStep(1).setLength(4).setSort(2).setCycleFlag(false); // mock when(ruleService.getAutoCodeRuleByCode(ruleCode)).thenReturn(rule); when(partService.getAutoCodePartListByRuleId(2L)).thenReturn(Arrays.asList(part1, part2)); when(inputCharStrategy.generate(eq(part1), any(MesMdAutoCodeContext.class))).thenReturn("A"); when(serialNumberStrategy.generate(eq(part2), any(MesMdAutoCodeContext.class))).thenReturn("0001"); when(recordMapper.selectByResult("A0001")).thenReturn(null); // 调用 String result = recordService.generateAutoCode(ruleCode, inputChar); // 断言 assertEquals("A0001", result); ArgumentCaptor captor = ArgumentCaptor.forClass(MesMdAutoCodeRecordDO.class); verify(recordMapper).insert(captor.capture()); MesMdAutoCodeRecordDO record = captor.getValue(); assertEquals(inputChar, record.getInputChar()); assertEquals("A0001", record.getResult()); } @Test public void testGenerateAutoCode_withPadding() { // 准备参数 String ruleCode = "PADDED_CODE"; MesMdAutoCodeRuleDO rule = new MesMdAutoCodeRuleDO().setId(3L).setCode(ruleCode) .setPadded(true).setMaxLength(10).setPaddedChar("0").setPaddedMethod(1); MesMdAutoCodePartDO part1 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.FIXED_CHAR.getType()) .setFixCharacter("IT").setLength(2).setSort(1); // mock when(ruleService.getAutoCodeRuleByCode(ruleCode)).thenReturn(rule); when(partService.getAutoCodePartListByRuleId(3L)).thenReturn(Arrays.asList(part1)); when(fixedCharStrategy.generate(eq(part1), any(MesMdAutoCodeContext.class))).thenReturn("IT"); when(recordMapper.selectByResult("00000000IT")).thenReturn(null); // 调用 String result = recordService.generateAutoCode(ruleCode, null); // 断言:IT 补齐到 10 位,左补 0 assertEquals("00000000IT", result); verify(recordMapper).insert(any(MesMdAutoCodeRecordDO.class)); } @Test public void testGenerateAutoCode_withDatePart() { // 准备参数 String ruleCode = "DATE_CODE"; MesMdAutoCodeRuleDO rule = new MesMdAutoCodeRuleDO().setId(4L).setCode(ruleCode).setPadded(false); MesMdAutoCodePartDO part1 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.DATE.getType()) .setDateFormat("yyyyMMdd").setLength(8).setSort(1); MesMdAutoCodePartDO part2 = new MesMdAutoCodePartDO().setType(MesMdAutoCodePartTypeEnum.SERIAL_NUMBER.getType()) .setSerialStartNo(1).setSerialStep(1).setLength(4).setSort(2).setCycleFlag(false); // mock when(ruleService.getAutoCodeRuleByCode(ruleCode)).thenReturn(rule); when(partService.getAutoCodePartListByRuleId(4L)).thenReturn(Arrays.asList(part1, part2)); when(dateStrategy.generate(eq(part1), any(MesMdAutoCodeContext.class))).thenReturn("20260304"); when(serialNumberStrategy.generate(eq(part2), any(MesMdAutoCodeContext.class))).thenReturn("0001"); when(recordMapper.selectByResult("202603040001")).thenReturn(null); // 调用 String result = recordService.generateAutoCode(ruleCode, null); // 断言:日期 8 位 + 流水号 4 位 = 12 位 assertEquals(12, result.length()); assertEquals("202603040001", result); verify(recordMapper).insert(any(MesMdAutoCodeRecordDO.class)); } }