perf(lastleg): reuse work items within synchronization batch
This commit is contained in:
+49
@@ -12,6 +12,39 @@ import static com.baiyue.lastleg.appointmentwork.AppointmentWorkModels.*;
|
||||
import static com.baiyue.lastleg.appointmentwork.AppointmentWorkFactGateway.*;
|
||||
|
||||
class AppointmentWorkSyncServiceTest {
|
||||
/** 百柜同页只读三次完整工作集;新路线、既有历史及重复同步都不丢失。 */
|
||||
@Test void hundredContainersReuseOneGenerationWorkingSetAndKeepUnknownRoutesAndHistory() {
|
||||
var f=new WorkTestFixture();
|
||||
var historical=f.state("old",Type.DELIVERY,Flow.DIRECT,"old-demand");
|
||||
historical.setRetired(true);historical.setRetirementReason("BATCH_REPLACED");
|
||||
historical.setReplacementKeys(List.of("later-work"));f.add(historical);
|
||||
var sourceRows=java.util.stream.IntStream.rangeClosed(1,100).mapToObj(index->{
|
||||
String id=Integer.toString(index);
|
||||
var flow=index<=50?Flow.UNKNOWN:Flow.TRANSFER;
|
||||
var requirements=flow==Flow.UNKNOWN?List.<Requirement>of():List.of(new Requirement("INBOUND:"+id,null,null,
|
||||
"FBA","海外仓","ONT8",null,List.of(),List.of(),List.of()));
|
||||
return new ContainerSource(id,"CNT"+id,null,LocalDateTime.parse("2026-10-01T10:00:00"),null,"v1",flow,null,requirements);
|
||||
}).toList();
|
||||
when(f.gateway.discover(null,100)).thenReturn(new SourcePage(sourceRows,null,true,"scope"));
|
||||
clearInvocations(f.mapper);
|
||||
assertEquals(300,f.sync.synchronize(new SyncRequest(null,100)).created());
|
||||
verify(f.mapper,times(3)).all(AppointmentWorkStore.MAX_ITEMS+1);
|
||||
var states=f.store.all();assertEquals(301,states.size());
|
||||
assertEquals(50,states.stream().filter(s->s.getType()==Type.ROUTE && s.getFlow()==Flow.UNKNOWN).count());
|
||||
assertTrue(f.store.get(historical.getWorkItemKey()).isRetired());
|
||||
assertEquals(List.of("later-work"),f.store.get(historical.getWorkItemKey()).getReplacementKeys());
|
||||
var before=new TreeMap<>(f.rows);int historyCount=f.history.size();
|
||||
clearInvocations(f.mapper);
|
||||
assertEquals(0,f.sync.synchronize(new SyncRequest(null,100)).created());
|
||||
verify(f.mapper,times(3)).all(AppointmentWorkStore.MAX_ITEMS+1);
|
||||
assertEquals(before.keySet(),f.rows.keySet());
|
||||
before.forEach((key,row)->{
|
||||
assertEquals(row.getVersion(),f.rows.get(key).getVersion());
|
||||
assertEquals(row.getStateJson(),f.rows.get(key).getStateJson());
|
||||
});
|
||||
assertEquals(historyCount,f.history.size());
|
||||
assertDoesNotThrow(f.store::requireInitialized);
|
||||
}
|
||||
/** 同一实物批次拆成客户事项时,整体事项须有完整去向,运输仍保持唯一。 */
|
||||
@Test void batchWideDeliveryIsReplacedByItsExplicitCustomerRequirements() {
|
||||
var f=new WorkTestFixture();String batch="FBA_APPOINTMENT:1";
|
||||
@@ -66,6 +99,7 @@ class AppointmentWorkSyncServiceTest {
|
||||
f.sync.synchronize(new SyncRequest(null,10));
|
||||
assertEquals(keys,f.store.all().stream().filter(s->s.getType()==Type.DELIVERY && !s.isRetired()).map(AppointmentWorkState::getWorkItemKey).sorted().toList());
|
||||
}
|
||||
/** 同页移除主柜后,后柜沿最新对象更新;旧批次恢复也不得覆盖版本或丢客户关联。 */
|
||||
@Test void removingPrivateCargoDetachesOldContainerAndRestoresDemandWithoutRetiringOtherCargo() {
|
||||
var f=new WorkTestFixture();
|
||||
var source1=new SourceReference("C1","ORDER:1","CUSTOMER:1");var source2=new SourceReference("C2","ORDER:2","CUSTOMER:2");
|
||||
@@ -88,6 +122,21 @@ class AppointmentWorkSyncServiceTest {
|
||||
second=new ContainerSource("2","CNT2",null,LocalDateTime.parse("2026-10-01T10:00:00"),null,"v1",Flow.TRANSFER,null,List.of(root2));
|
||||
when(f.gateway.discover(null,10)).thenReturn(new SourcePage(List.of(first,second),null,true,"scope"));f.sync.synchronize(new SyncRequest(null,10));
|
||||
assertEquals(2,f.store.all().stream().filter(s->s.getType()==Type.DELIVERY && !s.isRetired() && s.getBatchKey()==null).count());
|
||||
var retired=f.store.get(kept.getWorkItemKey());assertTrue(retired.isRetired());
|
||||
int retiredVersion=retired.getVersion();
|
||||
first=new ContainerSource("1","CNT1",null,LocalDateTime.parse("2026-10-01T10:00:00"),null,"v1",Flow.TRANSFER,null,List.of(batch));
|
||||
second=new ContainerSource("2","CNT2",null,LocalDateTime.parse("2026-10-01T10:00:00"),null,"v1",Flow.TRANSFER,null,List.of(batch));
|
||||
when(f.gateway.discover(null,10)).thenReturn(new SourcePage(List.of(first,second),null,true,"scope"));
|
||||
clearInvocations(f.mapper);
|
||||
f.sync.synchronize(new SyncRequest(null,10));
|
||||
verify(f.mapper,times(3)).all(AppointmentWorkStore.MAX_ITEMS+1);
|
||||
var restored=f.store.get(kept.getWorkItemKey());assertFalse(restored.isRetired());
|
||||
assertTrue(restored.getVersion()>retiredVersion);
|
||||
assertEquals(Set.of("1","2"),new HashSet<>(restored.getContainers().stream().map(ContainerReference::containerBookingId).toList()));
|
||||
assertEquals(List.of(source1,source2),restored.getSources());
|
||||
assertEquals(1,f.store.all().stream().filter(s->s.getType()==Type.DELIVERY && !s.isRetired()).count());
|
||||
assertTrue(f.history.stream().anyMatch(h->"SOURCE_REMOVED".equals(h.getAction())));
|
||||
assertTrue(f.history.stream().anyMatch(h->"REPLACED".equals(h.getAction())));
|
||||
}
|
||||
@Test void removedPrivateBatchCannotBeReplacedByAnotherCustomersUnrelatedDemand() {
|
||||
var f=new WorkTestFixture();var oldSource=new SourceReference("C1","ORDER:1","CUSTOMER:1");
|
||||
|
||||
Reference in New Issue
Block a user