Files
baiyue-firstleg/scripts/admin-test-env-modules/phase3-loading-finalize.mjs
T
2026-08-12 10:59:39 +08:00

91 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const MODULE = 'phase3-loading-finalize'
const OPTIONS = { scenario: 'phase3-loading-hard-gate-finalize', deploymentBlocker: true }
function id(value) { return value == null ? '' : String(value) }
function codeOf(response) { return response?.body?.code == null ? 'missing' : String(response.body.code) }
export function buildFinalizeResource(fixture, finalData, completeResponse) {
const observedStatus = finalData?.summary?.processStatus || fixture?.inboundStatus || 'UNKNOWN'
const observedVersion = Number(finalData?.summary?.version)
const loaded = observedStatus === 'LOADED'
const version = Number.isInteger(observedVersion) ? observedVersion : fixture?.inboundVersion
return {
...fixture,
...(Number.isInteger(version) ? { inboundVersion: version } : {}),
inboundStatus: observedStatus,
finalStatus: observedStatus,
finalizeSucceeded: loaded,
notCleanedReason: loaded ? null
: `装柜完成未成功,保留真实状态 ${observedStatus};completeCode=${codeOf(completeResponse)}`,
}
}
async function request(context, method, path, body, headers = {}) {
try {
const response = await fetch(`${context.options.gateway}${path}`, {
method,
headers: { Accept: 'application/json', Authorization: `Bearer ${context.token}`,
...(body === undefined ? {} : { 'Content-Type': 'application/json; charset=utf-8' }), ...headers },
body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(60_000),
})
const raw = await response.text(); let payload = null
try { payload = raw ? JSON.parse(raw) : null } catch { /* bounded raw evidence */ }
return { method, path, http: response.status, body: payload,
raw: payload === null ? raw.slice(0, 500) : null,
requestId: payload?.requestId || response.headers.get('x-request-id'),
traceId: payload?.traceId || response.headers.get('x-trace-id') }
} catch (error) {
return { method, path, http: 0, body: null, raw: String(error), requestId: null, traceId: null }
}
}
export async function run(context) {
const fixture = context.createdResources?.phase3Loading
const tenantId = id(fixture?.tenantId); const inboundId = id(fixture?.inboundId)
if (!tenantId || !inboundId || fixture?.marker !== context.marker
|| fixture?.loadingPlanStatus !== 'COMPLETED') {
context.addStep(MODULE, '取得下游门禁待完成装柜 fixture', null, false,
`tenantId=${tenantId || 'missing'}, inboundId=${inboundId || 'missing'}, marker=${fixture?.marker || 'missing'}, plan=${fixture?.loadingPlanStatus || 'missing'}`, OPTIONS)
return
}
const detailPath = `/api/platform/inbounds/${inboundId}?tenantId=${encodeURIComponent(tenantId)}`
const beforeResponse = await context.request('GET', detailPath)
const before = context.apiSuccess(MODULE, '读取下游门禁齐备后的装柜状态', beforeResponse, '', OPTIONS)
const version = Number(before?.summary?.version)
context.assert(MODULE, '最终装柜前保持 LOADING 且版本有效', beforeResponse,
before?.summary?.processStatus === 'LOADING' && Number.isInteger(version),
`status=${before?.summary?.processStatus || 'missing'}, version=${before?.summary?.version ?? 'missing'}`, OPTIONS)
if (before?.summary?.processStatus !== 'LOADING' || !Number.isInteger(version)) return
const base = `/api/platform/v1/inbounds/${inboundId}`
const completeResponse = await request(context, 'POST',
`${base}/loading/complete?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: version, remark: context.marker },
{ 'Idempotency-Key': `${context.marker}-p3-complete-after-downstream`.slice(0, 128) })
const completed = context.apiSuccess(MODULE, '订舱拖车报关齐备后完成装柜', completeResponse, '', OPTIONS)
const evidences = completed?.evidences?.evidences || []
context.assert(MODULE, '统一硬门禁通过后推进 LOADED 且五类有效证据仍完整', completeResponse,
completed?.mainStatus === 'LOADED' && fixture.evidenceTypes?.every?.((type) =>
evidences.some((item) => item.evidenceType === type && item.status !== 'VOID')),
`mainStatus=${completed?.mainStatus || 'missing'}, evidences=${evidences.length}`, OPTIONS)
const replayResponse = await request(context, 'POST',
`${base}/loading/complete?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: version, remark: context.marker },
{ 'Idempotency-Key': `${context.marker}-p3-complete-after-downstream`.slice(0, 128) })
context.assert(MODULE, 'LOADED 完成请求 Idempotency-Key 重放保持终态', replayResponse,
replayResponse.http === 200 && String(replayResponse.body?.code) === '200'
&& replayResponse.body?.data?.mainStatus === 'LOADED',
`HTTP=${replayResponse.http}, code=${replayResponse.body?.code ?? 'missing'}, status=${replayResponse.body?.data?.mainStatus || 'missing'}`, 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) === version + 1
&& finalData?.statusHistory?.some?.((item) => item.action === 'COMPLETE_LOADING'
&& item.operatorName && item.fromStatus === 'LOADING' && item.toStatus === 'LOADED'),
`status=${finalData?.summary?.processStatus || 'missing'}, version=${finalData?.summary?.version ?? 'missing'}, histories=${finalData?.statusHistory?.length ?? 'n/a'}`, OPTIONS)
context.createdResources.phase3Loading = buildFinalizeResource(fixture, finalData, completeResponse)
}