perf(lastleg): reuse warehouse directory within sync generation

This commit is contained in:
2026-10-17 09:57:54 +08:00
parent 81e4123b10
commit 2971d6f7f0
11 changed files with 212 additions and 42 deletions
@@ -58,18 +58,36 @@ public class AppointmentSourceService {
return response.data().warehouse().timezone();
} catch(RuntimeException error) { throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE,"目的仓时区读取失败,请稍后核对",error); }
}
/** One catalog request and batched source reads, including mixed first-leg and external bookings. */
/** 普通批量读取创建独立目录作用域,同时读取头程和外接登记来源。 */
public List<Source> resolveBatch(List<AppointmentCoverageService.SourceRef> requested) {
return resolveBatch(requested,true).stream().map(Resolution::requireSource).toList();
return openReadScope().resolveBatch(requested);
}
/** 创建显式单次读取作用域;仅成功的目录结果可在本次生成中复用。 */
public ReadScope openReadScope() { return new ReadScope(); }
public final class ReadScope {
private List<AppointmentWarehouseClient.Warehouse> directory;
/** 只能由所属来源服务创建,不允许传入未经校验的目录。 */
private ReadScope() { }
/** 每次仍读取原订单和权限事实,仅目录共享;不保存来源解析结果。 */
public List<Source> resolveBatch(List<AppointmentCoverageService.SourceRef> requested) {
return AppointmentSourceService.this.resolveBatch(requested,true,this).stream().map(Resolution::requireSource).toList();
}
/** 读取失败不赋值,下次显式调用仍按原目录接口重读。 */
private List<AppointmentWarehouseClient.Warehouse> catalog() {
if(directory==null) directory=AppointmentSourceService.this.catalog();
return directory;
}
}
/** Read-only inspection keeps each source's prerequisite failure separate; infrastructure errors still escape. */
public record Resolution(AppointmentCoverageService.SourceRef reference,Source source,ResponseStatusException error) {
public Source requireSource() { if(error!=null) throw error; return source; }
}
/** 每次检查独立读取目录,保留逐来源前置条件失败与基础设施异常边界。 */
public List<Resolution> inspectBatch(List<AppointmentCoverageService.SourceRef> requested) {
return resolveBatch(requested,false);
return resolveBatch(requested,false,openReadScope());
}
private List<Resolution> resolveBatch(List<AppointmentCoverageService.SourceRef> requested,boolean strict) {
/** 保留原批量校验与错误边界,目录只从调用方的局部作用域读取。 */
private List<Resolution> resolveBatch(List<AppointmentCoverageService.SourceRef> requested,boolean strict,ReadScope scope) {
if(requested==null || requested.isEmpty() || requested.size()>100) throw bad("每批请选择1至100票真实货源");
var keys=new LinkedHashSet<String>(); var orderIds=new LinkedHashSet<Long>();
var bookingRefs=new java.util.ArrayList<AppointmentExternalSourceGateway.SourceRef>();
@@ -96,7 +114,7 @@ public class AppointmentSourceService {
var destinations=transferRefs.isEmpty()?Map.<Long,AppointmentCoverageMapper.Source>of():destinationFacts.sources(transferRefs).stream().collect(Collectors.toMap(AppointmentCoverageMapper.Source::orderId,Function.identity()));
var containerIds=orderRows.stream().filter(o->"FIRST_LEG".equals(o.getOrderType())).map(com.baiyue.lastleg.entity.LastlegOrder::getSourceContainerBookingId).filter(Objects::nonNull).distinct().toList();
var gates=containerIds.isEmpty()?Map.<String,Boolean>of():shipping.selectBatchIds(containerIds).stream().collect(Collectors.toMap(g->g.getContainerBookingId(),g->g.getNodeSequence()!=null && g.getNodeSequence()>=40));
var catalog=catalog();
var catalog=scope.catalog();
var result=requested.stream().map(ref->{
try {
if(ref.sourceKey().startsWith("ORDER:") && externalOrders.contains(new AppointmentExternalSourceGateway.OrderRef(ref.customerId(),orderId(ref.sourceKey()))))