feat(phase3): 收口父子单与全链路验收

This commit is contained in:
2026-08-10 12:23:43 +08:00
parent b294695169
commit 75d5fd4e26
55 changed files with 1110 additions and 115 deletions
@@ -227,6 +227,32 @@ export async function run(context) {
claimed?.workflowStatus === 'IN_PROGRESS' && Boolean(claimed?.assignment?.assigneeId),
`workflow=${claimed?.workflowStatus || 'missing'}`, OPTIONS)
const itemClaimBody = {
expectedAssignmentVersion: 0,
operationId: `${context.marker}-bridge-item-claim`.slice(0, 128),
}
const itemClaim = await request(context, 'POST',
`${pdaBase}/tasks/${canonicalTaskId}/items/${id(canonicalItem.id)}/claim`, itemClaimBody)
const itemClaimed = context.apiSuccess(MODULE, '领取柜次 PDA 明细单人作业权', itemClaim, '', OPTIONS)
const claimedItem = itemClaimed?.items?.find?.((item) => id(item.id) === id(canonicalItem.id))
context.assert(MODULE, 'PDA 明细作业员与任务领取人一致', itemClaim,
id(claimedItem?.assignment?.assigneeId) === id(itemClaimed?.assignment?.assigneeId)
&& Number(claimedItem?.assignment?.version) === 0,
`taskAssignee=${id(itemClaimed?.assignment?.assigneeId) || 'missing'}, itemAssignee=${id(claimedItem?.assignment?.assigneeId) || 'missing'}`, OPTIONS)
const itemClaimReplay = await request(context, 'POST',
`${pdaBase}/tasks/${canonicalTaskId}/items/${id(canonicalItem.id)}/claim`, itemClaimBody)
context.assert(MODULE, 'PDA 明细领取 operationId 幂等重放', itemClaimReplay,
codeOf(itemClaimReplay) === '200'
&& id(itemClaimReplay.body?.data?.items?.find?.((item) => id(item.id) === id(canonicalItem.id))?.assignment?.assigneeId)
=== id(claimedItem?.assignment?.assigneeId),
`HTTP=${itemClaimReplay.http}, code=${codeOf(itemClaimReplay)}`, OPTIONS)
const claimHashConflict = await request(context, 'POST',
`${pdaBase}/tasks/${canonicalTaskId}/items/${id(canonicalItem.id)}/claim`, {
...itemClaimBody, expectedAssignmentVersion: 1,
})
context.addStep(MODULE, 'PDA 明细同 operationId 不同版本请求被拒绝', claimHashConflict,
failure(claimHashConflict), `HTTP=${claimHashConflict.http}, code=${codeOf(claimHashConflict)}`, OPTIONS)
const canonicalPick = await request(context, 'POST',
`${pdaBase}/tasks/${canonicalTaskId}/items/${id(canonicalItem.id)}/pick`, {
warehouseId, pickedPackageCount: 1, locationIds: [], version: Number(canonicalItem.version),
@@ -370,26 +396,18 @@ export async function run(context) {
&& evidenceListData.evidences.some((item) => item.evidenceType === 'SEAL' && item.sealNo === `SEAL-${suffix}`),
`evidences=${evidenceListData?.evidences?.length ?? 'n/a'}`, OPTIONS)
const complete = await request(context, 'POST', `${base}/loading/complete?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: loadingVersion, remark: context.marker }, { 'Idempotency-Key': `${context.marker}-p3-complete`.slice(0, 128) })
const completed = context.apiSuccess(MODULE, '确认本轮入库单装柜完成', complete, '', OPTIONS)
context.assert(MODULE, '装柜完成推进主状态 LOADED 且保留五类证据', complete,
completed?.mainStatus === 'LOADED' && completed?.evidences?.evidences?.length >= evidenceTypes.length,
`mainStatus=${completed?.mainStatus || 'missing'}, evidences=${completed?.evidences?.evidences?.length ?? 'n/a'}`, OPTIONS)
const blockedBeforeDownstream = await request(context, 'POST', `${base}/loading/complete?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: loadingVersion, remark: `${context.marker}-before-downstream` },
{ 'Idempotency-Key': `${context.marker}-p3-complete-before-downstream`.slice(0, 128) })
context.addStep(MODULE, '订舱拖车报关未齐备时 LOADED 硬门禁拒绝', blockedBeforeDownstream,
failure(blockedBeforeDownstream),
`HTTP=${blockedBeforeDownstream.http}, code=${codeOf(blockedBeforeDownstream)}`, OPTIONS)
const finalResponse = await context.request('GET', detailPath)
const finalData = context.apiSuccess(MODULE, '回查三期装柜完成终态', finalResponse, '', OPTIONS)
context.assert(MODULE, '入库单终态 LOADED、版本递增且审计历史存在', finalResponse,
finalData?.summary?.processStatus === 'LOADED' && Number(finalData?.summary?.version) === loadingVersion + 1
&& finalData?.statusHistory?.some?.((item) => item.action === 'COMPLETE_LOADING' && item.operatorName),
`status=${finalData?.summary?.processStatus || 'missing'}, version=${finalData?.summary?.version ?? 'missing'}, histories=${finalData?.statusHistory?.length ?? 'n/a'}`, OPTIONS)
context.createdResources.phase3Loading = {
tenantId, inboundId, preloadPlanId: planId, evidenceIds: evidenceItems.map((item) => id(item.id)),
voidedEvidenceId: id(voidTarget?.id), evidenceTypes,
loadingPlanId, containerBookingId, containerNo, warehouseId, canonicalPdaTaskId: canonicalTaskId,
pickingTaskId: id(pickingCompleted?.id), marker: context.marker, finalStatus: 'LOADED',
pickingTaskId: id(pickingCompleted?.id), inboundVersion: loadingVersion,
marker: context.marker, inboundStatus: 'LOADING', loadingPlanStatus: 'COMPLETED', finalStatus: 'COMPLETED',
}
context.addCoverageGap(MODULE, OPTIONS.scenario, 'PDA 真机在线限制与设备持久化草稿批量提交',
'当前验收运行器只能发起平台 HTTP 请求,未具备 PDA 真机断网、设备草稿隔离及批量提交端点的可执行环境',
'device-and-batch-fixture')
}