perf(firstleg): scope appointment fact source aggregation by container

This commit is contained in:
2026-10-15 20:03:41 +08:00
parent ae33780dae
commit 4277601999
4 changed files with 37 additions and 12 deletions
@@ -18,15 +18,25 @@ class AppointmentWorkSourceMapperTest {
s.execute("CREATE TABLE fl_direct_delivery_unloading(container_booking_id BIGINT)");
s.execute("INSERT INTO fl_container_booking VALUES(1,'D1',NULL,'上海',NULL,1,'ACTIVE'),(2,'D2','C2','上海','私人仓',1,'ACTIVE'),(3,'D3','C3','上海',NULL,1,'ACTIVE'),(4,'D4','C4','上海',NULL,1,'MERGED')");
s.execute("INSERT INTO fl_carrier_schedule_node_fact VALUES(10,1,'DEPARTED_ORIGIN_PORT','2026-10-01'),(20,2,'EMPTY_RETURNED','2026-10-03'),(30,3,'LOADED_ON_VESSEL','2026-10-04'),(40,4,'DEPARTED_ORIGIN_PORT','2026-10-01'),(50,3,'DEPARTED_ORIGIN_PORT',NULL)");
s.execute("INSERT INTO fl_direct_delivery_unloading VALUES(2)");
s.execute("INSERT INTO fl_direct_delivery_unloading VALUES(2),(2)");
s.execute("INSERT INTO fl_carrier_schedule_node_fact VALUES(60,1,'IN_TRANSIT','2026-10-02'),(70,1,'ARRIVED_DESTINATION_PORT','2026-10-03'),(80,2,'EMPTY_RETURNED',NULL)");
}
var config=new MybatisConfiguration();config.setMapUnderscoreToCamelCase(true);
config.setEnvironment(new Environment("test",new JdbcTransactionFactory(),ds));config.addMapper(AppointmentWorkSourceMapper.class);
try(var session=new MybatisSqlSessionFactoryBuilder().build(config).openSession()) {
var rows=session.getMapper(AppointmentWorkSourceMapper.class).sources();
var mapper=session.getMapper(AppointmentWorkSourceMapper.class);
var rows=mapper.sources();
assertEquals(2,rows.size());assertEquals(1,rows.get(0).id());assertEquals(2,rows.get(1).id());
assertNull(rows.get(0).destinationWarehouseName());assertEquals(1,rows.get(1).deliveryVersion());
assertNull(rows.get(0).destinationWarehouseName());assertEquals(2,rows.get(1).deliveryVersion());
assertNull(rows.get(1).departedLocalAt());assertNotNull(rows.get(1).sailedEvidenceLocalAt());
assertEquals(70,rows.get(0).factVersion());assertEquals(20,rows.get(1).factVersion());
// The optimized path must equal the former full-manifest + Java filter, including
// dates, versions, null names, unloading counts, duplicate IDs and ineligible sources.
for(var ids:java.util.List.of(java.util.List.of(2L,1L),java.util.List.of(1L,1L),
java.util.List.of(2L,3L,4L,999L),java.util.List.of(3L,4L),java.util.List.<Long>of())) {
assertEquals(rows.stream().filter(row->ids.contains(row.id())).toList(),mapper.sourcesByIds(ids));
}
assertTrue(mapper.sourcesByIds(null).isEmpty());
}
}
}
@@ -52,8 +52,8 @@ class AppointmentWorkSourceServiceTest {
when(mapper.sources()).thenReturn(List.of(source(1,1)));
assertEquals(503,assertThrows(ResponseStatusException.class,()->service.container(1)).getStatusCode().value());
}
@Test void factBatchReadsManifestOnceAndPreservesEachContainerIdentity() {
when(mapper.sources()).thenReturn(IntStream.rangeClosed(1,100).mapToObj(id->source(id,1)).toList());
@Test void factBatchReadsOnlyRequestedSourcesAndPreservesEachContainerIdentity() {
when(mapper.sourcesByIds(anyList())).thenReturn(IntStream.rangeClosed(1,100).mapToObj(id->source(id,1)).toList());
when(mapper.cargo(anyList())).thenReturn(List.of());
stubFlows("UNKNOWN");
var fact=mock(Fact.class);
@@ -66,19 +66,21 @@ class AppointmentWorkSourceServiceTest {
var requests=IntStream.rangeClosed(1,100).boxed().flatMap(id->List.of(
request("PICKUP-"+id,"PICKUP",id),request("RETURN-"+id,"EMPTY_RETURN",id)).stream()).toList();
assertEquals(200,service.facts(requests).size());
verify(mapper,times(1)).sources();
verify(mapper,times(1)).sourcesByIds(IntStream.rangeClosed(1,100).mapToObj(Long::valueOf).toList());
verify(mapper,never()).sources();
verify(mapper,times(1)).cargo(IntStream.rangeClosed(1,100).mapToObj(Long::valueOf).toList());
verify(facts,times(1)).establishedFlows(anyList());
verify(facts,times(1)).read(any(),any());
}
@Test void nextFactBatchReadsFreshManifestAndRejectsRemovedContainer() {
when(mapper.sources()).thenReturn(List.of(source(1,1)),List.of());
@Test void nextFactBatchReadsFreshSourcesAndRejectsRemovedContainer() {
when(mapper.sourcesByIds(List.of(1L))).thenReturn(List.of(source(1,1)),List.of());
when(mapper.cargo(List.of(1L))).thenReturn(List.of());
stubFlows("UNKNOWN");
var request=request("PICKUP-1","PICKUP",1);
service.facts(List.of(request));
assertEquals(404,assertThrows(ResponseStatusException.class,()->service.facts(List.of(request))).getStatusCode().value());
verify(mapper,times(2)).sources();
verify(mapper,times(2)).sourcesByIds(List.of(1L));
verify(mapper,never()).sources();
verify(facts,times(1)).read(any(),any());
}
private void stubFlows(String flow) {