2026-08-12 20:36:11 +08:00
|
|
|
const MODULE = 'manual-state-matrix'
|
|
|
|
|
const OPTIONS = { scenario: 'manual-clickable-list-state-matrix', deploymentBlocker: true }
|
|
|
|
|
|
|
|
|
|
const GROUPS = [
|
|
|
|
|
'order-inbound-status-fixtures',
|
|
|
|
|
'manual-state-loading-pda',
|
|
|
|
|
'manual-state-ocean-trucking',
|
|
|
|
|
]
|
|
|
|
|
|
2026-08-14 12:50:37 +08:00
|
|
|
const FIXTURE_UNAVAILABLE = {
|
|
|
|
|
inbounds: {
|
2026-08-24 14:08:17 +08:00
|
|
|
MEASURE_REJECTED: '当前稳定 Admin 契约未开放测量审核驳回接口,无法稳定构造该状态',
|
2026-10-08 17:07:57 +08:00
|
|
|
INBOUNDING: '当前公开接口没有可独立保留的 INBOUNDING 生成或暂停链路;不通过写业务SQL伪造中间状态',
|
2026-08-14 12:50:37 +08:00
|
|
|
VOIDED: '当前 Controller/Service 没有作废入库单的公开 API',
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-12 20:36:11 +08:00
|
|
|
function rows(value) { return Array.isArray(value) ? value : [] }
|
2026-08-14 12:50:37 +08:00
|
|
|
function id(value) { return value == null ? '' : String(value) }
|
|
|
|
|
|
|
|
|
|
export function normalizeStateRequirements(matrix) {
|
|
|
|
|
for (const [page, value] of Object.entries(matrix?.pages || {})) {
|
|
|
|
|
const supportedStatuses = [...new Set([
|
|
|
|
|
...rows(value?.supportedStatuses), ...rows(value?.requiredStatuses),
|
|
|
|
|
])]
|
|
|
|
|
const unavailable = FIXTURE_UNAVAILABLE[page] || {}
|
|
|
|
|
value.supportedStatuses = supportedStatuses
|
|
|
|
|
value.requiredStatuses = supportedStatuses.filter((status) => !unavailable[status])
|
|
|
|
|
if (Object.keys(unavailable).length > 0) {
|
|
|
|
|
value.coverageGaps = Object.fromEntries(Object.entries(unavailable).map(([status, reason]) => [status, {
|
|
|
|
|
category: 'fixture-unavailable', reason,
|
|
|
|
|
}]))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matrix
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function missingRequiredStateFixtures(matrix) {
|
|
|
|
|
return Object.entries(matrix?.pages || {}).flatMap(([page, value]) => rows(value?.requiredStatuses)
|
|
|
|
|
.filter((status) => !value?.fixtures?.[status]?.id)
|
|
|
|
|
.map((status) => `${page}:${status}`))
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-03 06:26:33 +08:00
|
|
|
export async function attachDeliveredOrderFixture(context, matrix) {
|
|
|
|
|
const fulfilled = context.createdResources?.phase5OverseasFulfillment
|
|
|
|
|
const proof = fulfilled?.orderCompletionProof
|
|
|
|
|
const customerId = id(proof?.customerId)
|
|
|
|
|
const orderId = id(proof?.rootOrderId)
|
|
|
|
|
if (fulfilled?.marker !== context.marker || !customerId || !orderId || !proof?.rootDelivered || proof.expectedCompleted !== false) {
|
|
|
|
|
throw new Error('DELIVERED 样本须复用本轮已签收且仍有有效余量的履约根')
|
|
|
|
|
}
|
|
|
|
|
const response = await context.request('GET',
|
|
|
|
|
`/api/platform/v1/orders/${encodeURIComponent(orderId)}?tenantId=${encodeURIComponent(customerId)}`)
|
|
|
|
|
const detail = context.apiSuccess(MODULE, '回读真实签收产生的 DELIVERED 统一订单', response, '', OPTIONS)
|
|
|
|
|
const valid = id(detail?.summary?.orderId) === orderId && detail?.summary?.mainStatus === 'DELIVERED'
|
|
|
|
|
context.assert(MODULE, 'DELIVERED 样本客户与真实根签收状态正确', response, valid,
|
|
|
|
|
`orderId=${orderId}, status=${detail?.summary?.mainStatus || 'missing'}`, OPTIONS)
|
|
|
|
|
if (!valid) throw new Error('DELIVERED 履约根详情未收敛到真实签收状态')
|
|
|
|
|
matrix.pages.unifiedOrders.fixtures.DELIVERED = { id: orderId, status: 'DELIVERED',
|
|
|
|
|
customerId, detailPath: `/orders/${orderId}` }
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-14 12:50:37 +08:00
|
|
|
function registerFixtureUnavailableGaps(context, matrix) {
|
|
|
|
|
for (const [page, value] of Object.entries(matrix?.pages || {})) {
|
|
|
|
|
for (const [status, gap] of Object.entries(value?.coverageGaps || {})) {
|
|
|
|
|
if (value?.fixtures?.[status]?.id) continue
|
|
|
|
|
if (rows(context.coverageGaps).some((item) => item?.category === gap.category
|
|
|
|
|
&& String(item?.name || '').includes(status))) continue
|
|
|
|
|
context.addCoverageGap?.(MODULE, OPTIONS.scenario, `${page}:${status}`, gap.reason,
|
2026-08-25 00:24:43 +08:00
|
|
|
gap.category, { marker: context.marker, page, status, blocking: false })
|
2026-08-14 12:50:37 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function attachUnifiedOrderFixture(context, matrix, resourceName, expectedStatus) {
|
|
|
|
|
const resource = context.createdResources?.[resourceName]
|
|
|
|
|
const inboundId = id(resource?.inboundId)
|
|
|
|
|
const tenantId = id(resource?.tenantId)
|
|
|
|
|
context.assert(MODULE, `${expectedStatus} 专项资源已生成`, null,
|
|
|
|
|
Boolean(inboundId && tenantId),
|
|
|
|
|
`resource=${resourceName}, inboundId=${inboundId || 'missing'}, tenantId=${tenantId || 'missing'}`, OPTIONS)
|
|
|
|
|
if (!inboundId || !tenantId) return
|
|
|
|
|
|
|
|
|
|
const inboundResponse = await context.request('GET',
|
|
|
|
|
`/api/platform/inbounds/${encodeURIComponent(inboundId)}?tenantId=${encodeURIComponent(tenantId)}`)
|
|
|
|
|
const inbound = context.apiSuccess(MODULE, `读取 ${expectedStatus} 专项入库详情`, inboundResponse, '', OPTIONS)
|
|
|
|
|
const orderId = id(inbound?.summary?.orderId)
|
|
|
|
|
context.assert(MODULE, `${expectedStatus} 入库详情身份与状态正确`, inboundResponse,
|
|
|
|
|
id(inbound?.summary?.id) === inboundId && id(inbound?.summary?.tenantId) === tenantId
|
|
|
|
|
&& Boolean(orderId) && inbound?.summary?.processStatus === expectedStatus,
|
|
|
|
|
`inboundId=${id(inbound?.summary?.id) || 'missing'}, orderId=${orderId || 'missing'}, status=${inbound?.summary?.processStatus || 'missing'}`, OPTIONS)
|
|
|
|
|
if (!orderId || inbound?.summary?.processStatus !== expectedStatus) return
|
|
|
|
|
|
|
|
|
|
const unifiedResponse = await context.request('GET',
|
|
|
|
|
`/api/platform/v1/orders/${encodeURIComponent(orderId)}?tenantId=${encodeURIComponent(tenantId)}`)
|
|
|
|
|
const unified = context.apiSuccess(MODULE, `读取 ${expectedStatus} 统一订单详情`, unifiedResponse, '', OPTIONS)
|
|
|
|
|
const valid = id(unified?.summary?.orderId) === orderId && unified?.summary?.mainStatus === expectedStatus
|
|
|
|
|
context.assert(MODULE, `${expectedStatus} 统一订单详情可打开`, unifiedResponse, valid,
|
|
|
|
|
`orderId=${id(unified?.summary?.orderId) || 'missing'}, mainStatus=${unified?.summary?.mainStatus || 'missing'}`, OPTIONS)
|
|
|
|
|
if (!valid) return
|
|
|
|
|
|
|
|
|
|
matrix.pages.unifiedOrders.fixtures[expectedStatus] = {
|
|
|
|
|
id: orderId, status: expectedStatus, inboundId,
|
|
|
|
|
detailPath: `/orders/${orderId}`,
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-12 20:36:11 +08:00
|
|
|
|
|
|
|
|
export async function run(context) {
|
|
|
|
|
const matrix = { marker: context.marker, generatedAt: new Date().toISOString(), pages: {} }
|
|
|
|
|
context.createdResources.manualStateMatrix = matrix
|
|
|
|
|
|
|
|
|
|
for (const name of GROUPS) {
|
|
|
|
|
const extension = await import(new URL(`./${name}.mjs`, import.meta.url))
|
|
|
|
|
if (typeof extension.run !== 'function') throw new Error(`${name} 必须导出 async function run(context)`)
|
|
|
|
|
await extension.run(context, matrix)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-14 12:50:37 +08:00
|
|
|
await attachUnifiedOrderFixture(context, matrix, 'phase3Loading', 'LOADED')
|
2026-10-03 06:26:33 +08:00
|
|
|
await attachDeliveredOrderFixture(context, matrix)
|
2026-08-14 12:50:37 +08:00
|
|
|
normalizeStateRequirements(matrix)
|
|
|
|
|
registerFixtureUnavailableGaps(context, matrix)
|
|
|
|
|
|
2026-08-12 20:36:11 +08:00
|
|
|
const pages = Object.entries(matrix.pages)
|
2026-08-14 12:50:37 +08:00
|
|
|
const missing = missingRequiredStateFixtures(matrix)
|
2026-08-13 09:49:55 +08:00
|
|
|
const missingActions = pages.flatMap(([page, value]) => rows(value?.requiredActions)
|
|
|
|
|
.filter((action) => !value?.actions?.[action]?.id
|
|
|
|
|
|| !value?.actions?.[action]?.documentId || !value?.actions?.[action]?.completedAt)
|
|
|
|
|
.map((action) => `${page}:action:${action}`))
|
2026-08-12 20:36:11 +08:00
|
|
|
context.assert(MODULE, '人工验收列表状态数据全部生成并保留', null,
|
2026-08-13 09:49:55 +08:00
|
|
|
pages.length > 0 && missing.length === 0 && missingActions.length === 0,
|
|
|
|
|
`pages=${pages.length}, missing=${[...missing, ...missingActions].join(',') || 'none'}`, OPTIONS)
|
2026-08-12 20:36:11 +08:00
|
|
|
}
|