fix: improve shadow diagnostic matching

This commit is contained in:
zgw
2026-08-28 11:55:43 +08:00
parent da5a0a1561
commit 3f7ed3718d
9 changed files with 218 additions and 20 deletions

View File

@@ -5,6 +5,9 @@ import com.ruoyi.algorithm.inbound.InboundBorrowType;
import com.ruoyi.algorithm.inbound.InboundLadderRung;
import com.ruoyi.algorithm.inbound.InboundPlan;
import com.ruoyi.algorithm.integration.SchedulingFacade;
import com.ruoyi.algorithm.outbound.OutboundAssignment;
import com.ruoyi.algorithm.outbound.OutboundPlan;
import com.ruoyi.algorithm.outbound.FifoDecision;
import com.ruoyi.algorithm.repository.VersionToken;
import com.ruoyi.algorithm.service.InboundPlanningResult;
import com.ruoyi.algorithm.shadow.mapper.ShadowInputMapper;
@@ -16,8 +19,10 @@ import org.springframework.transaction.support.SimpleTransactionStatus;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class ShadowPlanningEngineTest {
@@ -25,13 +30,14 @@ class ShadowPlanningEngineTest {
@Test
void inboundPlanningUsesRequiresNewAndConfiguredTimeout() {
ShadowObservationProperties properties = new ShadowObservationProperties();
properties.setWarehouseId("W01");
properties.setWarehouseId("FALLBACK");
properties.setTimeoutSeconds(7);
CapturingTransactionManager transactions = new CapturingTransactionManager();
ShadowInputMapper mapper = mock(ShadowInputMapper.class);
SchedulingFacade facade = mock(SchedulingFacade.class);
ShadowInboundReference input = new ShadowInboundReference();
input.setRequestId("JOB-1");
input.setWarehouseId("W01");
input.setTrayNo("TRAY-1");
input.setProjectNo("PROJECT-1");
input.setProductId("PRODUCT-1");
@@ -56,6 +62,69 @@ class ShadowPlanningEngineTest {
assertThat(observation.proposedSpaceId()).isEqualTo("SPACE-9");
}
@Test
void outboundPlanningUsesReferenceWarehouseAndKeepsMatchDiagnostics() {
ShadowObservationProperties properties = new ShadowObservationProperties();
properties.setWarehouseId("FALLBACK");
CapturingTransactionManager transactions = new CapturingTransactionManager();
ShadowInputMapper mapper = mock(ShadowInputMapper.class);
SchedulingFacade facade = mock(SchedulingFacade.class);
ShadowStockReference reference = new ShadowStockReference();
reference.setRequestId("DETAIL-1");
reference.setWarehouseId("W02");
reference.setProjectNo("PROJECT-1");
reference.setProductId("PRODUCT-1");
reference.setSnapshotSynchronizedAt("2026-08-28T02:53:45.133Z");
reference.setLiveMatchCount(2);
reference.setShadowMatchCount(1);
when(mapper.selectProjectOutboundReference("DETAIL-1")).thenReturn(reference);
OutboundAssignment assignment = new OutboundAssignment(
"DETAIL-1", "TRAY-1", "PROJECT-1", "PRODUCT-1", "SPACE-9", 1,
true, java.util.List.of(), java.util.List.of(), false,
1.0, 2.0, 3.0, FifoDecision.off(), java.util.Optional.empty(),
"shadow proposal");
when(facade.planOutbound(eq("W02"), any()))
.thenReturn(OutboundPlan.ready(assignment, com.ruoyi.algorithm.outbound.TaskCountEstimate.zero()));
ShadowPlanningEngine engine = new ShadowPlanningEngine(facade, mapper, properties, transactions);
OutboundShadowObservation observation = engine.planProjectOutbound("DETAIL-1");
engine.compareOutbound(observation, "10");
assertThat(observation.warehouseId()).isEqualTo("W02");
assertThat(observation.requestId()).isEqualTo("DETAIL-1");
assertThat(observation.proposedSpaceId()).isEqualTo("SPACE-9");
verify(mapper).insertComparison(any(), eq("W02"), eq("OUTBOUND_COMPARE"), eq("LEGACY_10"),
argThat(reason -> reason.contains("detail=DETAIL-1")
&& reason.contains("project=PROJECT-1")
&& reason.contains("product=PRODUCT-1")
&& reason.contains("snapshotSync=2026-08-28T02:53:45.133Z")
&& reason.contains("legacyMatches=2")
&& reason.contains("shadowMatches=1")));
}
@Test
void outboundPlanningTreatsPaddedAndUnpaddedWarehouseIdsAsEquivalent() {
ShadowObservationProperties properties = new ShadowObservationProperties();
properties.setWarehouseId("01");
CapturingTransactionManager transactions = new CapturingTransactionManager();
ShadowInputMapper mapper = mock(ShadowInputMapper.class);
SchedulingFacade facade = mock(SchedulingFacade.class);
ShadowStockReference reference = new ShadowStockReference();
reference.setRequestId("DETAIL-2");
reference.setWarehouseId("1");
reference.setProjectNo("PROJECT-2");
when(mapper.selectProjectOutboundReference("DETAIL-2")).thenReturn(reference);
when(facade.planOutbound(eq("01"), any())).thenReturn(
OutboundPlan.error(new com.ruoyi.algorithm.outbound.OutboundError(
com.ruoyi.algorithm.outbound.OutboundErrorCode.STOCK_NOT_FOUND,
"no stock", java.util.Map.of())));
ShadowPlanningEngine engine = new ShadowPlanningEngine(facade, mapper, properties, transactions);
OutboundShadowObservation observation = engine.planProjectOutbound("DETAIL-2");
assertThat(observation.warehouseId()).isEqualTo("01");
}
private static final class CapturingTransactionManager implements PlatformTransactionManager {
private TransactionDefinition definition;

View File

@@ -123,6 +123,7 @@ class OutStockoutServiceImplShadowTest {
private void prepareAutoAllot() {
OutStockout bill = new OutStockout();
bill.setStatus("2");
bill.setWarehouseId("1");
when(outStockoutMapper.selectOutStockoutByStockoutId("OUT-1")).thenReturn(bill);
when(outStockoutDetailService.selectOutStockoutDetailList(any()))
.thenReturn(List.of(pendingDetail));