Files
baiyue-firstleg/scripts/admin-test-env-modules/phase3-evidence-gate.mjs
T
2026-08-24 19:58:35 +08:00

85 lines
6.4 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-evidence-gate'
const OPTIONS = { scenario: 'loading-evidence-gate', deploymentBlocker: true }
function id(value) { return value == null ? '' : String(value) }
function successful(response) { return response?.http === 200 && String(response?.body?.code ?? '') === '200' }
export async function run(context) {
const fixture = context.createdResources?.phase3Loading
const tenantId = id(fixture?.tenantId)
const inboundId = id(fixture?.inboundId)
const taskId = id(fixture?.canonicalPdaTaskId || fixture?.pickingTaskId)
const loadingPlanId = id(fixture?.loadingPlanId)
const containerBookingId = id(fixture?.containerBookingId)
const warehouseId = id(fixture?.warehouseId)
if (!tenantId || !inboundId || !taskId || !loadingPlanId || !containerBookingId || !warehouseId
|| fixture.marker !== context.marker) {
context.addStep(MODULE, '取得同 marker 的五证据门禁前置', null, false,
`tenantId=${tenantId || 'missing'}, inboundId=${inboundId || 'missing'}, taskId=${taskId || 'missing'}, loadingPlanId=${loadingPlanId || 'missing'}, containerBookingId=${containerBookingId || 'missing'}, fixtureMarker=${fixture?.marker || 'missing'}, runMarker=${context.marker}`,
OPTIONS)
context.addCoverageGap(MODULE, OPTIONS.scenario, '五类证据多图追加、作废审计和有效证据门禁',
'本轮未取得带 taskId/inboundId/loadingPlanId/containerBookingId 的 phase3-loading marker 隔离证据 fixture,未使用历史资源', 'fixture')
return
}
// AWAITING_REVIEW is a PDA workflow state, not evidence permission. Evidence is
// opened only after the server-side inbound aggregate has entered LOADING.
// The list endpoint only exposes tasks that are currently in the loading queue.
// Once PDA completion advances the task, use its stable task identity instead.
const taskResponse = await context.request('GET',
`/api/platform/v1/first-leg/pda-loading/tasks/${encodeURIComponent(taskId)}?warehouseId=${encodeURIComponent(warehouseId)}`)
const task = successful(taskResponse) ? taskResponse.body?.data : null
const taskItem = task?.items?.find?.((item) => id(item?.inboundId) === inboundId)
const taskIdentityValid = id(task?.id) === taskId
&& id(task?.loadingPlanId) === loadingPlanId
&& id(task?.containerBookingId) === containerBookingId
&& Boolean(taskItem) && id(taskItem?.inboundId) === inboundId
context.assert(MODULE, '证据前置任务身份使用本轮四元稳定关联', taskResponse,
taskIdentityValid,
`taskId=${id(task?.id) || 'missing'}, inboundId=${id(taskItem?.inboundId) || 'missing'}, loadingPlanId=${id(task?.loadingPlanId) || 'missing'}, containerBookingId=${id(task?.containerBookingId) || 'missing'}`, OPTIONS)
if (!taskIdentityValid) return
const inboundResponse = await context.request('GET',
`/api/platform/inbounds/${encodeURIComponent(inboundId)}?tenantId=${encodeURIComponent(tenantId)}`)
const inbound = successful(inboundResponse) ? inboundResponse.body?.data : null
const evidenceReadable = ['LOADING', 'LOADED'].includes(inbound?.summary?.processStatus)
context.assert(MODULE, '服务端入库进入 LOADING 或 LOADED 后开放装柜证据只读回查', inboundResponse,
evidenceReadable,
`processStatus=${inbound?.summary?.processStatus || 'missing'}, taskWorkflow=${task?.workflowStatus || 'missing'}`, OPTIONS)
if (!evidenceReadable) {
context.addStep(MODULE, '装柜证据读取前置必须由服务端进入 LOADING', inboundResponse, false,
`processStatus=${inbound?.summary?.processStatus || 'missing'};不能以 AWAITING_REVIEW 或 coverageGap 代替装柜阶段事实`, OPTIONS)
context.addCoverageGap(MODULE, OPTIONS.scenario, '服务端装柜阶段前不请求证据',
`taskId=${taskId}, inboundId=${inboundId}, loadingPlanId=${loadingPlanId}, containerBookingId=${containerBookingId}, processStatus=${inbound?.summary?.processStatus || 'missing'};AWAITING_REVIEW 不作为证据开放条件`, 'deployment-blocker')
return
}
const path = `/api/platform/v1/inbounds/${inboundId}/loading/evidence?tenantId=${encodeURIComponent(tenantId)}`
const response = await context.request('GET', path)
const data = context.apiSuccess(MODULE, '复核本轮装柜证据聚合', response, `inboundId=${fixture.inboundId}`, OPTIONS)
const evidences = Array.isArray(data?.evidences) ? data.evidences : []
const required = ['PHOTO_STEP_1', 'PHOTO_STEP_2', 'PHOTO_STEP_3', 'PHOTO_STEP_4', 'SEAL']
const active = evidences.filter((item) => item.status !== 'VOID')
const voided = evidences.find((item) => id(item.id) === id(fixture.voidedEvidenceId))
const missingTypeVoided = evidences.find((item) => id(item.id) === id(fixture.missingTypeVoidedEvidenceId))
context.assert(MODULE, '五类有效证据齐全且同类型多图追加不覆盖历史', response,
required.every((type) => active.some((item) => item.evidenceType === type))
&& evidences.filter((item) => item.evidenceType === 'PHOTO_STEP_1').length >= 2
&& active.some((item) => item.evidenceType === 'SEAL' && item.sealNo === fixture.sealNo)
&& active.every((item) => String(item.fileName || '').includes(context.marker)
&& String(item.remark || '').includes(context.marker)),
`active=${active.length}, types=${[...new Set(active.map((item) => item.evidenceType))].join(',')}`, OPTIONS)
context.assert(MODULE, '作废证据保留原因、操作者和时间且不计入有效门禁', response,
Boolean(voided) && voided.status === 'VOID' && voided.voidReason === `${context.marker}-wrong-photo`
&& Boolean(voided.voidedBy) && Boolean(voided.voidedAt)
&& !active.some((item) => id(item.id) === id(fixture.voidedEvidenceId)),
`voidedEvidenceId=${id(fixture.voidedEvidenceId) || 'missing'}, status=${voided?.status || 'missing'}`, OPTIONS)
context.assert(MODULE, '缺类作废审计保留且补传证据恢复有效门禁', response,
Boolean(missingTypeVoided) && missingTypeVoided.status === 'VOID'
&& missingTypeVoided.voidReason === `${context.marker}-temporary-missing-type`
&& Boolean(missingTypeVoided.voidedBy) && Boolean(missingTypeVoided.voidedAt)
&& active.some((item) => item.evidenceType === 'PHOTO_STEP_4'
&& id(item.fileId) === id(fixture.replacementEvidenceFileId)),
`voidedEvidenceId=${id(fixture.missingTypeVoidedEvidenceId) || 'missing'}, replacementFileId=${id(fixture.replacementEvidenceFileId) || 'missing'}`, OPTIONS)
}