fix(appointment): retain unbookable cargo as blocked work

This commit is contained in:
2026-10-15 17:28:22 +08:00
parent 14b8cc625b
commit 42679d4d54
5 changed files with 62 additions and 4 deletions
@@ -63,11 +63,16 @@ public class AppointmentWorkDeliveryProvider implements AppointmentWorkFactGatew
if(request.sources().isEmpty()) { blocks.add("尾程货源尚未建立,请先核对整柜来源与入仓计划"); missing.add("LASTLEG_SOURCE"); }
else {
for(var source:request.sources()) {
var context=cargo.get(source.sourceKey(),source.customerId(),source.destinationKey());
fingerprints.add(context.toString()); blocks.addAll(context.blockReasons());
try {
var context=cargo.get(source.sourceKey(),source.customerId(),source.destinationKey());
fingerprints.add(context.toString()); blocks.addAll(context.blockReasons());
} catch(org.springframework.web.server.ResponseStatusException error) {
if(!AppointmentWorkSourceAvailability.unresolved(error)) throw error;
fingerprints.add(source+":"+error.getReason());blocks.add(error.getReason());missing.add("SOURCE_CONFIGURATION");
}
}
var first=request.sources().get(0);
action=new Action(ActionCode.VERIFY_CARGO,"核实货物并建立预约",request.workItemKey(),new CargoTarget(first));
if(missing.isEmpty()) action=new Action(ActionCode.VERIFY_CARGO,"核实货物并建立预约",request.workItemKey(),new CargoTarget(first));
}
return new Fact(request.workItemKey(),digest(request.requirementKey()+":"+fingerprints),"UNBATCHED",false,PlanStatus.UNPLANNED,ExecutionStatus.PENDING,
null,null,null,null,null,null,List.of(ORDER_READ),action==null?List.of():List.of(OverseasPermissions.INBOUND_ORDER_FBA_APPOINTMENT),action,
@@ -36,7 +36,17 @@ public class AppointmentWorkRequirementProvider implements AppointmentWorkFactGa
if("CUSTOMER".equals(requirement.destinationKind())) { customer(requirement,sourceOrders,privateDispatches,result);continue; }
if(!"FBA".equals(requirement.destinationKind())) { result.put(requirement.requirementKey(),requirement); continue; }
var refs=canonical(sourceOrders.stream().map(s->new SourceReference(s.customerId(),"ORDER:"+s.id(),null)).toList());
var resolved=sources.resolveBatch(refs);
List<AppointmentSourceService.Source> resolved;
try { resolved=sources.resolveBatch(refs); }
catch(org.springframework.web.server.ResponseStatusException error) {
if(!AppointmentWorkSourceAvailability.unresolved(error)) throw error;
// Keep the complete physical obligation and its canonical source identities. The fact
// provider reports the original prerequisite and exposes no booking action.
result.put(requirement.requirementKey(),new Requirement(requirement.requirementKey(),null,null,
requirement.destinationKind(),requirement.originName(),requirement.destinationName(),requirement.timezone(),
requirement.references(),List.of(),refs.stream().map(r->new SourceReference(r.customerId(),r.sourceKey(),r.destinationKey())).toList()));
continue;
}
var complete=resolved.stream().map(s->new AppointmentCoverageService.SourceRef(s.customerId(),s.sourceKey(),s.destinationKey())).toList();
var history=appointments.relatedBatchSnapshots(complete);
var active=history.stream().filter(AppointmentWorkRequirementProvider::active).toList();
@@ -0,0 +1,15 @@
package com.baiyue.lastleg.inboundplan.work;
import java.util.Set;
import org.springframework.web.server.ResponseStatusException;
/** An unbookable physical source remains visible work; transport and integrity errors still fail the scan. */
final class AppointmentWorkSourceAvailability {
private AppointmentWorkSourceAvailability() { }
private static final Set<String> PREREQUISITES=Set.of(
"当前货源类型不支持FBA预约", "货物尚无已开船或后续实际船节点", "当前货源不是FBA送仓货物",
"接仓点不在启用的美国仓目录中", "目的仓不在启用的美国FBA目录中", "目的地已变化,请返回原业务重新核对");
static boolean unresolved(ResponseStatusException error) {
return error.getStatusCode().value()==400 && error.getReason()!=null && PREREQUISITES.contains(error.getReason());
}
}