This commit is contained in:
2026-09-16 15:21:08 +08:00
parent 912feca56f
commit 8f0622058d
20 changed files with 447 additions and 41 deletions
@@ -13,6 +13,7 @@ import { runUntilPickupReady } from './phase4-carrier-nodes.mjs'
import { resolvePickerAccess, createPicker, disablePicker, uploadPhotos } from './phase4-overseas-pda.mjs'
import { receivingProgressMatches, outboundCandidateGuards, outboundCreationGuards, outboundTaskGuards, probePartialPick } from './dropship-outbound-edge-cases.mjs'
import { receiveMixedContainer } from './mixed-container-receiving.mjs'
import { exerciseOldCabinets } from './pda-loading-link-regression.mjs'
const MODULE = 'dropship-pallet-matrix'
const BASE = '/api/platform/v1/first-leg/pda-dropship'
@@ -42,7 +43,11 @@ export async function runMixedContainer(context) {
return runWorkflow(context, false, true)
}
async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = false) {
export async function runDomesticLinkRegression(context) {
return runWorkflow(context, false, false, true)
}
async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = false, linkRegression = false) {
const marker = context.marker
const savedOptions = context.options.prepareDropshipManualCount
const savedContext = { ...context.context }
@@ -122,7 +127,7 @@ async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = fals
record.summary = inbound.summary
record.domesticVerified = true
}
async function allocate(group) {
async function allocate(group, regression = {}) {
const label = group.map((item) => item.label).join('+')
context.marker = `${marker}-${group[0].label}-LOAD`
const smart = '/api/platform/v1/first-leg/smart-loading'
@@ -141,6 +146,7 @@ async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = fals
check(`${record.label}初始费用已计算`, null, Boolean(fee))
}
const summary = group[0].summary
if (regression.rounds) await exerciseOldCabinets({ context, group, pallets, command, check, rounds: regression.rounds })
let cabinet = await command('创建本场景模拟柜', 'POST', '/api/platform/v1/containers', {
channelGroupCode: channelGroup(summary.channelCode), warehouseId: String(summary.warehouseId),
containerType: '40HQ', capacityCbm: 68, maxWeightKg: 19500,
@@ -205,6 +211,13 @@ async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = fals
await conserve('智能推荐后')
}
for (const record of group) record.loading = { cabinet, plan, allocationVerified: true }
if (regression.activeUnstarted) {
const pallet = pallets.at(-1)
plan = await command('分配另一实托到未开始柜前移除', 'DELETE', `${path}/pallet-assignments/${pallet.palletId}`, {
expectedPlanVersion: Number(plan.version), operationId: `${context.marker}-split-remove`,
})
await exerciseOldCabinets({ context, group, pallets: [pallet], command, check, rounds: 1, active: true })
}
const carrier = await ensureMatsonCarrierPartner(context, MODULE, OPTIONS)
const agent = await resolveOceanBookingAgent(context, MODULE, 'MATSON', OPTIONS)
check(`${label}真实船司和订舱代理齐全`, null, Boolean(carrier?.id && agent?.id))
@@ -259,8 +272,22 @@ async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = fals
claimed.items.length > 0 && (mixedContainer
? claimed.items.some(item => item.unitType === 'SEALED_PALLET') && claimed.items.some(item => item.unitType !== 'SEALED_PALLET')
: claimed.items.every((item) => item.unitType === 'SEALED_PALLET')))
const picked = await command('整托拣货', 'POST', `${pda}/batch`, batch,
{ 'Idempotency-Key': `${context.marker}-pick` })
let picked = claimed
if (linkRegression) {
for (const palletNo of [...new Set(claimed.items.map(item => item.palletNo))]) {
const priorVersion = Number(picked.version)
const payload = { warehouseId: String(summary.warehouseId), palletNo,
expectedTaskVersion: priorVersion, operationId: `${context.marker}-pick-${palletNo}` }
picked = await command('逐托即时提交', 'POST', `${pda}/pallets/pick`, payload)
check('即时提交返回最新任务版本', null, Number(picked.version) > priorVersion)
const replay = await command('逐托提交同键重放', 'POST', `${pda}/pallets/pick`, payload)
check('重放不重复累加数量或版本', null,
replay.version === picked.version && JSON.stringify(replay.items) === JSON.stringify(picked.items))
}
} else {
picked = await command('整托拣货', 'POST', `${pda}/batch`, batch,
{ 'Idempotency-Key': `${context.marker}-pick` })
}
check(`${label}整托拣货等待最终确认`, null, picked.workflowStatus === 'AWAITING_REVIEW')
for (const evidenceType of ['PHOTO_STEP_1', 'PHOTO_STEP_2', 'PHOTO_STEP_3', 'PHOTO_STEP_4', 'SEAL']) {
const response = await uploadEvidence(context, taskId, String(summary.warehouseId), evidenceType, 0,
@@ -276,6 +303,18 @@ async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = fals
}, { 'Idempotency-Key': `${context.marker}-complete` })
const terminal = await waitForCompletion(context, `${pda}?warehouseId=${summary.warehouseId}`, `${pda}/operations`)
const finalTask = terminal.task
if (regression.activeUnstarted) {
check('有实际货物的未开始柜次阻止PDA完成', terminal.taskResponse,
finalTask?.taskStatus === 'COMPLETION_FAILED'
&& finalTask.completionCommand?.failureStage === 'OWNER_READY'
&& finalTask.completionCommand?.failureCode === 'A0409')
const inbound = await command('失败后回查入仓状态未完成', 'GET',
`/api/platform/inbounds/${group[0].source.inboundId}?tenantId=${group[0].order.tenantId}`)
check('负向完成未推进LOADED', null, inbound.summary?.processStatus === 'LOADING')
context.createdResources.pdaLoadingLinkRegression.scenarios.push({ name: label, taskId,
outcome: 'ACTIVE_UNSTARTED_BLOCKED', task: finalTask })
return
}
check(`${label}任务真实完成`, terminal.taskResponse, finalTask?.taskStatus === 'COMPLETED',
`status=${finalTask?.taskStatus}, attempts=${terminal.attempts}`)
for (const record of group) {
@@ -285,6 +324,8 @@ async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = fals
record.loading = { cabinet: linked, plan, task: finalTask, completed, booking: released,
allocationVerified: true, loadingVerified: true, containerNo }
}
if (linkRegression) context.createdResources.pdaLoadingLinkRegression.scenarios.push({
name: label, taskId, outcome: 'COMPLETED', task: finalTask })
}
async function progressShipping(group) {
const record = group[0]
@@ -580,6 +621,21 @@ async function runWorkflow(context, stopBeforeOverseasPda, mixedContainer = fals
context.createdResources.dropshipPalletMatrix.stage = 'MIXED_CONTAINER_INBOUND_COMPLETED'
return
}
if (linkRegression) {
const single = await prepare('LINK-SINGLE', 2)
await verify(single, 2)
await allocate([single], { rounds: 1 })
const first = await prepare('MULTI-MERGE-LINK-A', 1, { deferSeal: true })
const second = await prepare('MULTI-MERGE-LINK-B', 1, { targetPalletNo: first.task.pallets[0].palletNo })
await verify(first, 1)
await verify(second, 1)
await allocate([first, second], { rounds: 3 })
const negative = await prepare('LINK-NEGATIVE', 2)
await verify(negative, 2)
await allocate([negative], { activeUnstarted: true })
context.createdResources.dropshipPalletMatrix.stage = 'DOMESTIC_LINK_REGRESSION_VERIFIED'
return
}
if (stopBeforeOverseasPda) {
const record = await prepare('BROWSER-MULTI', 2)
await verify(record, 2)