test(admin): align phase3 loading producer contract

This commit is contained in:
2026-08-11 17:47:19 +08:00
parent 4561245a71
commit a02dd3a422
3 changed files with 111 additions and 62 deletions
@@ -169,16 +169,17 @@ export async function run(context) {
{ expectedVersion: Number(link.loadingPlanVersion) },
{ 'Idempotency-Key': `${context.marker}-bridge-push`.slice(0, 128) })
const pushedLoadingPlan = context.apiSuccess(MODULE, '桥接后推送柜次计划并生成真实 PDA 任务', pushResponse, '', OPTIONS)
context.assert(MODULE, '推送后计划保持 EDITING 并生成真实 PDA 任务', pushResponse,
pushedLoadingPlan?.status === 'EDITING', `status=${pushedLoadingPlan?.status || 'missing'}`, OPTIONS)
if (pushedLoadingPlan?.status !== 'EDITING') return
context.assert(MODULE, '推送后柜次计划保持 EDITING 且版本递增', pushResponse,
pushedLoadingPlan?.status === 'EDITING'
&& Number(pushedLoadingPlan?.version) === Number(link.loadingPlanVersion) + 1,
`status=${pushedLoadingPlan?.status || 'missing'}, version=${pushedLoadingPlan?.version ?? 'missing'}`, OPTIONS)
const preload = await context.request('GET', `${base}/preload?tenantId=${encodeURIComponent(tenantId)}`)
const preloadData = context.apiSuccess(MODULE, '回查预装清单', preload, '', OPTIONS)
context.assert(MODULE, '预装清单归属、明细和 marker 持久化', preload,
id(preloadData?.id) === planId && id(preloadData?.inboundId) === inboundId && id(preloadData?.tenantId) === tenantId
&& preloadData?.items?.[0]?.containerCode === `P3-${suffix}` && preloadData?.remark === context.marker,
`planId=${id(preloadData?.id) || 'missing'}, items=${preloadData?.items?.length ?? 'n/a'}`, OPTIONS)
const blockedBeforePreloadConfirm = await request(context, 'POST', `${base}/loading/complete?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: initialVersion, remark: 'must confirm preload first' },
{ 'Idempotency-Key': `${context.marker}-p3-complete-before-preload-confirm`.slice(0, 128) })
context.addStep(MODULE, '预装未确认时拒绝完成装柜', blockedBeforePreloadConfirm,
failure(blockedBeforePreloadConfirm),
`HTTP=${blockedBeforePreloadConfirm.http}, code=${codeOf(blockedBeforePreloadConfirm)}`, OPTIONS)
const canonicalTaskResponse = await context.request('GET',
`${pdaBase}/tasks?containerNo=${encodeURIComponent(containerNo)}&warehouseId=${encodeURIComponent(warehouseId)}`)
@@ -192,49 +193,19 @@ export async function run(context) {
if (!canonicalTaskId || !canonicalItem?.id) return
const claim = await request(context, 'POST', `${pdaBase}/tasks/${canonicalTaskId}/claim`, {
expectedAssignmentVersion: 0, operationId: `${context.marker}-bridge-claim`.slice(0, 128),
expectedAssignmentVersion: Number(canonicalTask?.assignment?.version || 0),
operationId: `${context.marker}-bridge-claim`.slice(0, 128),
})
const claimed = context.apiSuccess(MODULE, '领取柜次 PDA 任务', claim, '', OPTIONS)
context.assert(MODULE, '柜次 PDA 任务绑定当前作业员', claim,
claimed?.workflowStatus === 'IN_PROGRESS' && Boolean(claimed?.assignment?.assigneeId),
`workflow=${claimed?.workflowStatus || 'missing'}`, OPTIONS)
const claimedPlanResponse = await request(context, 'GET', `${smartLoadingBase}/plans/${loadingPlanId}`)
const claimedPlan = context.apiSuccess(MODULE, '领取后回查柜次计划进入 LOADING', claimedPlanResponse, '', OPTIONS)
context.assert(MODULE, '领取后计划进入 LOADING', claimedPlanResponse,
claimedPlan?.status === 'LOADING', `status=${claimedPlan?.status || 'missing'}`, OPTIONS)
if (claimedPlan?.status !== 'LOADING') return
const confirming = await request(context, 'POST', `${base}/preload/confirming?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: Number(link.preloadVersion) }, { 'Idempotency-Key': `${context.marker}-p3-confirming`.slice(0, 128) })
const confirmingData = context.apiSuccess(MODULE, '提交预装清单确认', confirming, '', OPTIONS)
context.assert(MODULE, '预装清单进入 CONFIRMING 且版本递增', confirming,
confirmingData?.preloadPlan?.status === 'CONFIRMING' && Number(confirmingData?.preloadPlan?.version) === 2,
`status=${confirmingData?.preloadPlan?.status || 'missing'}, version=${confirmingData?.preloadPlan?.version ?? 'missing'}`, OPTIONS)
if (confirmingData?.preloadPlan?.status !== 'CONFIRMING') return
const confirm = await request(context, 'POST', `${base}/preload/confirm?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: Number(confirmingData.preloadPlan.version) }, { 'Idempotency-Key': `${context.marker}-p3-confirm`.slice(0, 128) })
const confirmed = context.apiSuccess(MODULE, '确认预装并进入装柜中', confirm, '', OPTIONS)
context.assert(MODULE, '预装确认推进主状态 LOADING 和计划 CONFIRMED', confirm,
confirmed?.mainStatus === 'LOADING' && confirmed?.preloadPlan?.status === 'CONFIRMED'
&& Number(confirmed?.preloadPlan?.version) === 3,
`mainStatus=${confirmed?.mainStatus || 'missing'}, planStatus=${confirmed?.preloadPlan?.status || 'missing'}, version=${confirmed?.preloadPlan?.version ?? 'missing'}`, OPTIONS)
if (confirmed?.mainStatus !== 'LOADING') return
const loadingResponse = await context.request('GET', detailPath)
const loading = context.apiSuccess(MODULE, '读取装柜中入库单版本', loadingResponse, '', OPTIONS)
const loadingVersion = Number(loading?.summary?.version)
context.assert(MODULE, '入库单已进入 LOADING 且版本有效', loadingResponse,
loading?.summary?.processStatus === 'LOADING' && Number.isInteger(loadingVersion),
`status=${loading?.summary?.processStatus || 'missing'}, version=${loading?.summary?.version ?? 'missing'}`, OPTIONS)
if (!Number.isInteger(loadingVersion)) return
const blockedBeforePicking = await request(context, 'POST', `${base}/loading/complete?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: loadingVersion, remark: 'must require picking' },
{ 'Idempotency-Key': `${context.marker}-p3-complete-before-picking`.slice(0, 128) })
context.addStep(MODULE, '未完成 PDA 拣货时拒绝完成装柜', blockedBeforePicking, failure(blockedBeforePicking),
`HTTP=${blockedBeforePicking.http}, code=${codeOf(blockedBeforePicking)}`, OPTIONS)
const claimedPlanResponse = await context.request('GET', `${smartLoadingBase}/plans/${loadingPlanId}`)
const claimedPlan = context.apiSuccess(MODULE, '领取后回查柜次计划', claimedPlanResponse, '', OPTIONS)
context.assert(MODULE, '领取 PDA 任务后计划进入 LOADING 且版本递增', claimedPlanResponse,
claimedPlan?.status === 'LOADING' && Number(claimedPlan?.version) === Number(pushedLoadingPlan?.version) + 1,
`status=${claimedPlan?.status || 'missing'}, version=${claimedPlan?.version ?? 'missing'}`, OPTIONS)
const batchPath = `${pdaBase}/tasks/${canonicalTaskId}/batch`
const batchKey = `${context.marker}-bridge-batch`.slice(0, 128)
@@ -333,6 +304,49 @@ export async function run(context) {
atomicBatchReplayVerified,
`HTTP=${canonicalBatchReplay.http}, code=${codeOf(canonicalBatchReplay)}, taskVersion=${canonicalBatchReplay.body?.data?.version ?? 'missing'}`, OPTIONS)
const pickedPlanResponse = await context.request('GET', `${smartLoadingBase}/plans/${loadingPlanId}`)
const pickedPlan = context.apiSuccess(MODULE, '首笔真实拣货后回查柜次计划', pickedPlanResponse, '', OPTIONS)
context.assert(MODULE, '首笔真实拣货触发装柜状态并由单明细批次直接完成', pickedPlanResponse,
directPdaCompletion && pickedPlan?.status === 'COMPLETED',
`status=${pickedPlan?.status || 'missing'}, version=${pickedPlan?.version ?? 'missing'}`, OPTIONS)
const confirming = await request(context, 'POST', `${base}/preload/confirming?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: Number(link.preloadVersion) }, { 'Idempotency-Key': `${context.marker}-p3-confirming`.slice(0, 128) })
const confirmingData = context.apiSuccess(MODULE, '提交预装清单确认', confirming, '', OPTIONS)
context.assert(MODULE, '预装清单进入 CONFIRMING 且版本递增', confirming,
confirmingData?.preloadPlan?.status === 'CONFIRMING' && Number(confirmingData?.preloadPlan?.version) === 2,
`status=${confirmingData?.preloadPlan?.status || 'missing'}, version=${confirmingData?.preloadPlan?.version ?? 'missing'}`, OPTIONS)
if (confirmingData?.preloadPlan?.status !== 'CONFIRMING') return
const confirm = await request(context, 'POST', `${base}/preload/confirm?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: Number(confirmingData.preloadPlan.version) }, { 'Idempotency-Key': `${context.marker}-p3-confirm`.slice(0, 128) })
const confirmed = context.apiSuccess(MODULE, '确认预装并进入装柜中', confirm, '', OPTIONS)
context.assert(MODULE, directPdaCompletion
? '装柜已开始时预装确认保持 LOADING 和 CONFIRMING'
: '预装确认推进主状态 LOADING 和计划 CONFIRMED', confirm,
directPdaCompletion
? confirmed?.mainStatus === 'LOADING' && confirmed?.preloadPlan?.status === 'CONFIRMING'
&& Number(confirmed?.preloadPlan?.version) === 2
: confirmed?.mainStatus === 'LOADING' && confirmed?.preloadPlan?.status === 'CONFIRMED'
&& Number(confirmed?.preloadPlan?.version) === 3,
`mainStatus=${confirmed?.mainStatus || 'missing'}, planStatus=${confirmed?.preloadPlan?.status || 'missing'}, version=${confirmed?.preloadPlan?.version ?? 'missing'}`, OPTIONS)
if (confirmed?.mainStatus !== 'LOADING') return
const preload = await context.request('GET', `${base}/preload?tenantId=${encodeURIComponent(tenantId)}`)
const preloadData = context.apiSuccess(MODULE, '回查已确认预装清单', preload, '', OPTIONS)
context.assert(MODULE, '预装清单归属、明细和 marker 持久化', preload,
id(preloadData?.id) === planId && id(preloadData?.inboundId) === inboundId && id(preloadData?.tenantId) === tenantId
&& preloadData?.items?.[0]?.containerCode === `P3-${suffix}` && preloadData?.remark === context.marker,
`planId=${id(preloadData?.id) || 'missing'}, items=${preloadData?.items?.length ?? 'n/a'}`, OPTIONS)
const loadingResponse = await context.request('GET', detailPath)
const loading = context.apiSuccess(MODULE, '读取装柜中入库单版本', loadingResponse, '', OPTIONS)
const loadingVersion = Number(loading?.summary?.version)
context.assert(MODULE, '入库单已进入 LOADING 且版本有效', loadingResponse,
loading?.summary?.processStatus === 'LOADING' && Number.isInteger(loadingVersion),
`status=${loading?.summary?.processStatus || 'missing'}, version=${loading?.summary?.version ?? 'missing'}`, OPTIONS)
if (!Number.isInteger(loadingVersion)) return
// The deployed settlement contract completes a one-item task during pick. A
// review call is therefore a negative compatibility assertion, never a second
// positive action that can poison the marker fixture with A0409.