feat(fleet): match cargo stops when binding dispatches

This commit is contained in:
2026-10-09 15:04:51 +08:00
parent 2ec5414b39
commit c4a7c2a22d
3 changed files with 51 additions and 1 deletions
@@ -206,11 +206,36 @@ public class FleetTripService {
try { if (bindings.insert(binding) != 1) throw conflict("排车单关联失败"); }
catch (DuplicateKeyException duplicate) { throw conflict("排车单已关联其他车次,请刷新后重试"); }
bookingReservations.bindIncoming(trip, dispatch, dispatches.sourceCargo(dispatch.dispatchNo()), operator);
var route = matchedStops(response(trip).stops(), dispatches.manifest(dispatch.dispatchNo(), false).cargo());
validatePlan(instant(trip.getPlannedDepartureAt()), instant(trip.getExpectedDriverEndAt()), route);
bookingReservations.requireRoute(trip, route);
trip.setStopsJson(write(route));
dispatches.assign(dispatch, assignment(trip));
persist(trip, request.recordVersion(), "BIND_DISPATCH", request.reason(), operator);
return detail(trip);
}
static List<FleetTripModels.Stop> matchedStops(List<FleetTripModels.Stop> existing, List<FleetExecutionModels.Cargo> cargo) {
// Keep established stop indexes/order: confirmed bookings may refer to them.
var result = new ArrayList<>(existing);
boolean onlyPlaceholder = existing.size() == 2 && existing.get(1).destinationKey() == null
&& existing.get(1).name().startsWith("待确定卸货点");
if (onlyPlaceholder) result.remove(1);
var keys = new HashSet<String>();
for (var stop : result) if (stop.destinationKey() != null) keys.add(stop.destinationKey());
for (var item : cargo) {
if (item.destinationKey() == null || item.destinationKey().isBlank())
throw conflict("排车货物目的地未确认,请先完善排车单目的地");
if (keys.add(item.destinationKey())) {
if (item.destinationLabel() == null || item.destinationLabel().isBlank())
throw conflict("排车货物目的地名称缺失,请先完善排车单");
result.add(new FleetTripModels.Stop(item.destinationLabel(), null, item.destinationKey()));
}
}
if (result.size() < 2) throw conflict("排车单没有可用卸货目的地");
return List.copyOf(result);
}
@Transactional
public FleetTripModels.Detail unbindDispatch(String tripNo, String operator, FleetTripModels.BindingCommand request) {
var trip = planned(tripNo, request.recordVersion());
@@ -220,7 +220,7 @@ paths:
parameters: [{$ref: '#/components/parameters/TripNo'}]
post:
summary: 将客户排车单关联到车次
description: '权限:platform:fleet:trip:edit;按业务单号解析客户归属,同仓且未开工;关联和车/司机快照同步原子提交。'
description: '权限:platform:fleet:trip:edit;按业务单号解析客户归属,同仓且未开工;关联、车/司机快照同步及按真实货物目的地标识补齐卸货站原子提交;目的地缺失拒绝关联,已有有效站点顺序与索引保留,初始默认占位站替换为真实目的地。'
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/BindingCommand'}}}}
responses: {'200': {$ref: '#/components/responses/Detail'}}
/api/platform/v1/last-leg/fleet/trips/{tripNo}/dispatches/unbind: