package com.zy.ai.service.impl; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.zy.ai.domain.autotune.AutoTuneRoutePressureSnapshot; import com.zy.ai.domain.autotune.AutoTuneRuleSnapshotItem; import com.zy.ai.domain.autotune.AutoTuneSnapshot; import com.zy.ai.domain.autotune.AutoTuneStationRuntimeItem; import com.zy.ai.domain.autotune.AutoTuneTaskDetailItem; import com.zy.ai.domain.autotune.AutoTuneTaskSnapshot; import com.zy.ai.service.FlowTopologySnapshotService; import com.zy.ai.service.RoutePressureSnapshotService; import com.zy.asrs.entity.BasDevp; import com.zy.asrs.entity.BasStation; import com.zy.asrs.entity.WrkMast; import com.zy.asrs.service.BasDevpService; import com.zy.asrs.service.BasStationService; import com.zy.asrs.service.WrkMastService; import com.zy.core.enums.WrkIoType; import com.zy.core.enums.WrkStsType; import com.zy.core.model.StationObjModel; import com.zy.core.model.protocol.StationProtocol; import com.zy.system.service.ConfigService; 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.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class AutoTuneSnapshotServiceImplTest { private AutoTuneSnapshotServiceImpl service; @BeforeEach void setUp() { ConfigService configService = mock(ConfigService.class); service = new AutoTuneSnapshotServiceImpl(); ReflectionTestUtils.setField(service, "configService", configService); ReflectionTestUtils.setField(service, "autoTuneControlModeService", new AutoTuneControlModeServiceImpl(configService)); } @Test void buildStationOutTaskLimitMapPreservesNullZeroAndNegativeLimits() { Map result = service.buildStationOutTaskLimitMap(Arrays.asList( station(101, null), station(102, 0), station(103, -1), station(104, 5) )); assertNull(result.get("101")); assertEquals(0, result.get("102")); assertEquals(-1, result.get("103")); assertEquals(5, result.get("104")); } @Test void buildStationOutBufferCapacityMapPreservesConfiguredCapacity() { Map result = service.buildStationOutBufferCapacityMap(Arrays.asList( station(101, 1, null), station(102, 1, 0), station(103, 1, 3) )); assertNull(result.get("101")); assertEquals(0, result.get("102")); assertEquals(3, result.get("103")); } @Test void buildRuleSnapshotExposesStepRangeCooldownAndRiskNote() { List result = service.buildRuleSnapshot(); AutoTuneRuleSnapshotItem stationOutTaskRule = findRule(result, "station", "outTaskLimit"); assertEquals(0, stationOutTaskRule.getMinValue()); assertNull(stationOutTaskRule.getMaxValue()); assertEquals(3, stationOutTaskRule.getMaxStep()); assertEquals(10, stationOutTaskRule.getCooldownMinutes()); assertEquals(Boolean.FALSE, stationOutTaskRule.getDynamicMaxValue()); assertNull(stationOutTaskRule.getDynamicMaxSource()); assertEquals("单次调整幅度不能超过 maxStep;outBufferCapacity 代表出库站缓存位置数量,超过该容量可能使任务出现在主干道,仅作为风险参考,不是合法性上限。", stationOutTaskRule.getNote()); AutoTuneRuleSnapshotItem crnMaxOutRule = findRule(result, "crn", "maxOutTask"); assertEquals(3, crnMaxOutRule.getMaxStep()); assertNull(crnMaxOutRule.getDynamicMaxSource()); assertEquals("单次调整幅度不能超过 maxStep。", crnMaxOutRule.getNote()); AutoTuneRuleSnapshotItem crnMaxInRule = findRule(result, "crn", "maxInTask"); assertEquals(3, crnMaxInRule.getMaxStep()); assertRuleMaxStep(result, "sys_config", "crnOutBatchRunningLimit", 3); assertRuleMaxStep(result, "dual_crn", "maxOutTask", 3); assertRuleMaxStep(result, "dual_crn", "maxInTask", 3); } @Test void loadActiveTasksIncludesNullWrkStatusInGroupedCondition() { WrkMastService wrkMastService = mock(WrkMastService.class); when(wrkMastService.list(any(Wrapper.class))).thenReturn(Collections.emptyList()); ReflectionTestUtils.setField(service, "wrkMastService", wrkMastService); ReflectionTestUtils.invokeMethod(service, "loadActiveTasks"); ArgumentCaptor> wrapperCaptor = ArgumentCaptor.forClass(Wrapper.class); verify(wrkMastService).list(wrapperCaptor.capture()); String sqlSegment = wrapperCaptor.getValue().getSqlSegment(); String normalizedSqlSegment = sqlSegment.replaceAll("\\s+", " "); assertTrue(normalizedSqlSegment.contains("(wrk_sts NOT IN")); assertTrue(normalizedSqlSegment.contains("OR wrk_sts IS NULL")); } @Test void buildTaskSnapshotExposesStationLimitBlockedTasksAndOutboundOrder() { WrkMastService wrkMastService = mock(WrkMastService.class); when(wrkMastService.list(any(Wrapper.class))).thenReturn(Arrays.asList( outboundTask(190264, WrkStsType.STATION_RUN.sts, 101, 1, "MANUAL_OUT_20260427181201", 9, ""), outboundTask(190263, WrkStsType.NEW_OUTBOUND.sts, 101, 1, "MANUAL_OUT_20260427181201", 8, "目标出库站:101 已达出库任务上限,当前=1,上限=1") )); ReflectionTestUtils.setField(service, "wrkMastService", wrkMastService); AutoTuneTaskSnapshot snapshot = ReflectionTestUtils.invokeMethod(service, "buildTaskSnapshot"); assertEquals(2, snapshot.getActiveTaskCount()); assertEquals(1, snapshot.getByStatus().get("101")); assertEquals(1, snapshot.getByStatus().get("104")); List blockedTasks = snapshot.getStationLimitBlockedTasks(); assertEquals(1, blockedTasks.size()); assertEquals(190263, blockedTasks.get(0).getWrkNo()); assertEquals("生成出库任务", blockedTasks.get(0).getWrkStsDesc()); assertTrue(blockedTasks.get(0).getSystemMsg().contains("出库任务上限")); List outboundSamples = snapshot.getOutboundTaskSamples(); assertEquals(2, outboundSamples.size()); assertEquals(190263, outboundSamples.get(0).getWrkNo()); assertEquals(8, outboundSamples.get(0).getBatchSeq()); assertEquals(190264, outboundSamples.get(1).getWrkNo()); assertEquals(9, outboundSamples.get(1).getBatchSeq()); } @Test void buildSnapshotIncludesRoutePressureSnapshot() { WrkMastService wrkMastService = mock(WrkMastService.class); FlowTopologySnapshotService flowTopologySnapshotService = mock(FlowTopologySnapshotService.class); RoutePressureSnapshotService routePressureSnapshotService = mock(RoutePressureSnapshotService.class); AutoTuneRoutePressureSnapshot routePressureSnapshot = new AutoTuneRoutePressureSnapshot(); List activeTasks = Collections.singletonList( outboundTask(190263, WrkStsType.NEW_OUTBOUND.sts, 101, 1, "BATCH", 8, "目标出库站:101 已达出库任务上限,当前=1,上限=1") ); when(wrkMastService.list(any(Wrapper.class))).thenReturn(activeTasks); when(flowTopologySnapshotService.buildSnapshot(any())).thenReturn(Collections.emptyList()); when(routePressureSnapshotService.buildSnapshot(any(), any(), any())).thenReturn(routePressureSnapshot); ReflectionTestUtils.setField(service, "wrkMastService", wrkMastService); ReflectionTestUtils.setField(service, "flowTopologySnapshotService", flowTopologySnapshotService); ReflectionTestUtils.setField(service, "routePressureSnapshotService", routePressureSnapshotService); AutoTuneSnapshot snapshot = service.buildSnapshot(); assertSame(routePressureSnapshot, snapshot.getRoutePressureSnapshot()); assertEquals(1, snapshot.getTaskSnapshot().getActiveTaskCount()); ArgumentCaptor> activeTasksCaptor = ArgumentCaptor.forClass(List.class); ArgumentCaptor taskSnapshotCaptor = ArgumentCaptor.forClass(AutoTuneTaskSnapshot.class); ArgumentCaptor> stationRuntimeCaptor = ArgumentCaptor.forClass(List.class); verify(routePressureSnapshotService).buildSnapshot( activeTasksCaptor.capture(), taskSnapshotCaptor.capture(), stationRuntimeCaptor.capture() ); assertSame(activeTasks, activeTasksCaptor.getValue()); assertSame(snapshot.getTaskSnapshot(), taskSnapshotCaptor.getValue()); assertSame(snapshot.getStationRuntimeSnapshot(), stationRuntimeCaptor.getValue()); verify(wrkMastService).list(any(Wrapper.class)); } @Test void loadOutStationListOnlyQueriesBasDevpOutStations() { BasStationService basStationService = mock(BasStationService.class); BasDevpService basDevpService = mock(BasDevpService.class); ReflectionTestUtils.setField(service, "basStationService", basStationService); ReflectionTestUtils.setField(service, "basDevpService", basDevpService); when(basDevpService.list(any(Wrapper.class))).thenReturn(Arrays.asList( basDevp(1, 101, 102), basDevp(2, 102) )); when(basStationService.list(any(Wrapper.class))).thenReturn(Arrays.asList( station(101, 2), station(102, 3) )); java.util.List result = ReflectionTestUtils.invokeMethod(service, "loadOutStationList"); assertEquals(2, result.size()); assertEquals(101, result.get(0).getStationId()); assertEquals(102, result.get(1).getStationId()); ArgumentCaptor> basDevpWrapperCaptor = ArgumentCaptor.forClass(Wrapper.class); verify(basDevpService).list(basDevpWrapperCaptor.capture()); assertTrue(basDevpWrapperCaptor.getValue().getSqlSegment().contains("status =")); ArgumentCaptor> stationWrapperCaptor = ArgumentCaptor.forClass(Wrapper.class); verify(basStationService).list(stationWrapperCaptor.capture()); assertTrue(stationWrapperCaptor.getValue().getSqlSegment().contains("station_id IN")); } @Test void loadOutStationListDoesNotQueryStationsWhenBasDevpOutStationsAreEmpty() { BasStationService basStationService = mock(BasStationService.class); BasDevpService basDevpService = mock(BasDevpService.class); ReflectionTestUtils.setField(service, "basStationService", basStationService); ReflectionTestUtils.setField(service, "basDevpService", basDevpService); when(basDevpService.list(any(Wrapper.class))).thenReturn(Collections.singletonList(basDevp(1))); java.util.List result = ReflectionTestUtils.invokeMethod(service, "loadOutStationList"); assertTrue(result.isEmpty()); verify(basStationService, never()).list(any(Wrapper.class)); } @Test void toRuntimeItemExposesRunBlockForRoutePressure() { StationProtocol protocol = new StationProtocol(); protocol.setStationId(101); protocol.setAutoing(true); protocol.setLoading(false); protocol.setTaskNo(0); protocol.setRunBlock(true); AutoTuneStationRuntimeItem item = ReflectionTestUtils.invokeMethod(service, "toRuntimeItem", protocol); assertEquals(1, item.getRunBlock()); } private BasStation station(Integer stationId, Integer outTaskLimit) { return station(stationId, outTaskLimit, null); } private BasStation station(Integer stationId, Integer outTaskLimit, Integer outBufferCapacity) { BasStation station = new BasStation(); station.setStationId(stationId); station.setOutTaskLimit(outTaskLimit); station.setOutBufferCapacity(outBufferCapacity); return station; } private WrkMast outboundTask(Integer wrkNo, long wrkSts, Integer stationNo, Integer crnNo, String batch, Integer batchSeq, String systemMsg) { WrkMast wrkMast = new WrkMast(); wrkMast.setWrkNo(wrkNo); wrkMast.setWrkSts(wrkSts); wrkMast.setIoType(WrkIoType.OUT.id); wrkMast.setStaNo(stationNo); wrkMast.setCrnNo(crnNo); wrkMast.setBatch(batch); wrkMast.setBatchSeq(batchSeq); wrkMast.setSystemMsg(systemMsg); return wrkMast; } private BasDevp basDevp(Integer devpNo, Integer... stationIds) { BasDevp basDevp = new BasDevp(); basDevp.setDevpNo(devpNo); basDevp.setStatus(1); if (stationIds == null || stationIds.length == 0) { return basDevp; } basDevp.setOutStationList(JSON.toJSONString(Arrays.stream(stationIds) .map(this::stationObjModel) .toList())); return basDevp; } private StationObjModel stationObjModel(Integer stationId) { StationObjModel stationObjModel = new StationObjModel(); stationObjModel.setStationId(stationId); return stationObjModel; } private AutoTuneRuleSnapshotItem findRule(List rules, String targetType, String targetKey) { for (AutoTuneRuleSnapshotItem rule : rules) { if (targetType.equals(rule.getTargetType()) && targetKey.equals(rule.getTargetKey())) { return rule; } } throw new AssertionError("rule not found: " + targetType + "/" + targetKey); } private void assertRuleMaxStep(List rules, String targetType, String targetKey, int expectedMaxStep) { AutoTuneRuleSnapshotItem rule = findRule(rules, targetType, targetKey); assertEquals(expectedMaxStep, rule.getMaxStep()); } }