feat(dispatch): unify whole vehicle scheduling, source ownership and staged execution

This commit is contained in:
2026-10-19 23:40:42 +08:00
parent e0677e7ae7
commit 9dd34791f7
346 changed files with 29245 additions and 12602 deletions
@@ -10,8 +10,23 @@ const version = value => /^(0|[1-9]\d*)$/.test(text(value))
const trace = response => ({ http: response?.http, code: response?.body?.code,
requestId: response?.requestId, traceId: response?.traceId })
/** 仅生成明确TEST计划占用;这些预计值不提供真实装载测量证据。 */
export function testPlannedCapacity(cargo, stops) {
const pallets = new Map()
if (!Array.isArray(cargo) || !cargo.length || !Array.isArray(stops)) throw new Error('计划容量缺少本轮真实货物或路线')
for (const row of cargo) {
if (!row.warehousePalletId || !row.palletNo || !row.destinationKey) throw new Error('计划货物缺少真实实托和目的地')
const matches = stops.map((stop, index) => stop.destinationKey === row.destinationKey ? index : -1).filter(index => index > 0)
if (matches.length !== 1) throw new Error('计划货物目的地不在唯一路线卸货站')
const palletId = text(row.warehousePalletId), deliveryIndex = matches[0]
if (pallets.has(palletId) && pallets.get(palletId).deliveryIndex !== deliveryIndex) throw new Error('同一实托存在不同计划卸货站')
pallets.set(palletId, { warehousePalletId: palletId, palletSpaces: 1, weightKg: 100, volumeCbm: 1, deliveryIndex })
}
return [...pallets.values()]
}
/** Internal HTTP helper. Operates only on the current run's trip before picking; no SQL or cleanup on failure. */
export async function verifyFleetBookingReservation(context, module, tripProof, measurements) {
export async function verifyFleetBookingReservation(context, module, tripProof) {
if (!context.createdResources?.fleetTrips?.includes(tripProof) || !tripProof.tripNo || tripProof.reservation)
throw new Error('外接占位验收仅接受本轮新建且尚未开始验收的车次;失败后保留原证据')
const proof = tripProof.reservation = { status: 'CHECKING', marker: context.marker, requests: [], bookings: [] }
@@ -37,7 +52,9 @@ export async function verifyFleetBookingReservation(context, module, tripProof,
await check('自有货容量来源完整', initial?.cargo?.length > 0 && !initial.blockReason
&& initial.cargo.every(row => row.customerId && row.warehousePalletId && row.destinationKey)
&& version(initial.recordVersion) && /^[0-9a-f]{64}$/.test(initial.manifestHash || ''))
const pallets = measurements(initial.cargo, tripProof.stops)
const pallets = testPlannedCapacity(initial.cargo, tripProof.stops)
proof.planningBasis = { kind: 'CONTROLLED_TEST_PLAN', actual: false, pallets,
remark: '仅预计每实托1托位/100kg/1m³,用于计划余位测试;不得作为装车完成实测回执' }
const plan = await request('POST', '/capacity-plan', {
recordVersion: initial.recordVersion, manifestHash: initial.manifestHash, pallets,
reason: '受控测试计划:按本轮真实实托确认预计占用,非生产测量',