This commit is contained in:
2026-09-15 14:18:18 +08:00
parent 6a2075fe4b
commit a474ca4d93
35 changed files with 751 additions and 228 deletions
@@ -39,7 +39,7 @@ async function command(context, target, action, path, body) {
`${BASE}/${target.containerBookingId}/${path}`, body, { 'Idempotency-Key': key })
context.assert(MODULE, `${action}重放状态和版本零漂移`, replay.response,
JSON.stringify(replay.data) === JSON.stringify(first.data), trace(replay.response), OPTIONS)
const changed = ['pickup', 'empty-return'].includes(path) ? { ...body, remark: `${body.remark}-DIFFERENT` }
const changed = path === 'empty-return' ? { ...body, remark: `${body.remark}-DIFFERENT` }
: { ...body, reason: `${body.reason}-DIFFERENT` }
const hashConflict = await context.request('POST', `${BASE}/${target.containerBookingId}/${path}`,
changed, context.token, { 'Idempotency-Key': key })
@@ -103,15 +103,7 @@ export async function prepareTerminalDrayage(context, producer) {
trace(scheduled.response), OPTIONS)
if (!success(scheduled.response)) return null
const pickupAt = localIso(new Date(Date.parse(`${appointmentAt}Z`) + 60 * 60_000))
const picked = await command(context, target, '确认实际提柜', 'pickup', {
expectedVersion: Number(scheduled.data.version), actualAt: pickupAt, remark: context.marker,
})
context.assert(MODULE, '实际提柜同步PICKED_UP并等待海外仓卸柜', picked.response,
picked.data?.status === 'PICKED_UP' && picked.data?.actualPickupAt === pickupAt,
trace(picked.response), OPTIONS)
if (!success(picked.response) || picked.data?.status !== 'PICKED_UP') return null
resource.prepared = { ...target, appointmentAt, pickupAt, pickedVersion: Number(picked.data.version) }
resource.prepared = { ...target, appointmentAt, scheduledVersion: Number(scheduled.data.version) }
resource.containerBookingId = target.containerBookingId
resource.containerNo = target.containerNo
return resource.prepared
@@ -149,15 +141,15 @@ export async function run(context) {
context.assert(MODULE, '卸柜聚合保留提柜事实并开放还柜', initial.response,
id(initial.data?.containerBookingId) === target.containerBookingId
&& id(initial.data?.containerNo) === id(target.completed.containerNo)
&& Number(initial.data?.version) === Number(target.pickedVersion)
&& Number(initial.data?.version) === Number(target.scheduledVersion)
&& initial.data?.status === 'UNLOADED'
&& initial.data?.actualPickupAt === target.pickupAt
&& initial.data?.actualPickupAt === initial.data?.unloadingCompletedAt
&& initial.data?.allowedActions?.includes('RECORD_EMPTY_RETURN'),
`pollRequests=${pollRequests}, status=${initial.data?.status || 'missing'}, actions=${initial.data?.allowedActions?.join(',') || 'missing'}; ${trace(initial.response)}`, OPTIONS)
if (!success(initial.response)) return
const unloadingAt = Date.parse(`${initial.data?.unloadingCompletedAt}Z`)
const returnBase = Math.max(Date.parse(`${target.pickupAt}Z`), Number.isNaN(unloadingAt) ? 0 : unloadingAt)
const returnBase = Number.isNaN(unloadingAt) ? Date.now() : unloadingAt
const returnedAt = localIso(new Date(returnBase + 60 * 60_000))
const returned = await command(context, target, '确认实际还柜', 'empty-return', {
expectedVersion: Number(initial.data.version), actualAt: returnedAt, remark: context.marker,
@@ -176,13 +168,12 @@ export async function run(context) {
&& list.data[0]?.status === 'EMPTY_RETURNED', trace(list.response), OPTIONS)
const history = await api(context, 'GET', '读取目的港码头拖车不可变历史', `${path}/history`)
const actions = new Set((Array.isArray(history.data) ? history.data : []).map((item) => item?.action))
context.assert(MODULE, '历史完整记录四个业务动作和连续版本', history.response,
['SELECT_FLEET', 'SCHEDULE_PICKUP', 'RECORD_PICKUP', 'RECORD_EMPTY_RETURN'].every((action) => actions.has(action))
context.assert(MODULE, '历史完整记录三个业务动作和连续版本', history.response,
['SELECT_FLEET', 'SCHEDULE_PICKUP', 'RECORD_EMPTY_RETURN'].every((action) => actions.has(action))
&& history.data.every((item) => Number(item?.toVersion) === Number(item?.fromVersion) + 1),
`actions=${[...actions].join(',')}; ${trace(history.response)}`, OPTIONS)
context.assert(MODULE, '提柜和还柜备注写入不可变历史', history.response,
['RECORD_PICKUP', 'RECORD_EMPTY_RETURN'].every((action) =>
history.data?.some((item) => item.action === action && item.reason === context.marker)),
context.assert(MODULE, '还柜备注写入不可变历史', history.response,
history.data?.some((item) => item.action === 'RECORD_EMPTY_RETURN' && item.reason === context.marker),
trace(history.response), OPTIONS)
context.createdResources.phase5TerminalDrayage = {
...context.createdResources.phase5TerminalDrayage,