perf(lastleg): reuse work items within synchronization batch

This commit is contained in:
2026-10-17 08:30:11 +08:00
parent 5fd74e9c8c
commit 732cf99007
3 changed files with 84 additions and 9 deletions
@@ -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");