2026-10-21 13:22:35 +08:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
import { readFile } from 'node:fs/promises'
|
|
|
|
|
import { resolve } from 'node:path'
|
|
|
|
|
import { pathToFileURL } from 'node:url'
|
|
|
|
|
import { describeAcceptanceScope } from './admin-test-env-acceptance.mjs'
|
|
|
|
|
|
2026-10-22 14:37:59 +08:00
|
|
|
/** 首页八个业务入口的脚本审计;登记范围不是运行证据。 */
|
|
|
|
|
export function describeHomepageBusinessCoverage() {
|
|
|
|
|
const scope = describeAcceptanceScope()
|
|
|
|
|
return [
|
|
|
|
|
{ title: '头程下单', route: '/orders/first-leg/create', modules: ['orders', 'phase5-overseas-fulfillment'],
|
|
|
|
|
additionalModules: ['phase5-root-fulfillment', 'dropship-pallet-matrix'],
|
|
|
|
|
covered: '默认头程至海外履约主链;拆分根单、一件代发矩阵为可选专项',
|
|
|
|
|
remaining: '各订单类型、税制及根/余量分支未由默认链全部执行' },
|
2026-10-22 15:41:50 +08:00
|
|
|
{ title: '退货换标', route: '/overseas-warehouse/return-relabel/create', modules: ['phase4-overseas-return-receipt', 'phase4-overseas-return-followups'],
|
|
|
|
|
covered: '退货预报、审核、签收、合箱、换标、客户确认及换标后出库',
|
2026-10-22 14:37:59 +08:00
|
|
|
remaining: '换标、客户确认及换标后出库已接入默认后续模块;固定客户跨客户边界仍缺资源,真实HTTP待验' },
|
|
|
|
|
{ title: '美国本土卡', route: '/cross-border/domestic-trucking/create', modules: ['phase4-overseas-business'],
|
2026-10-22 15:41:50 +08:00
|
|
|
covered: '创建、编辑、审核及原单实收、两托部分至全部分配、派车、运输完成,逐步幂等重放', remaining: '独立本土卡车正常及部分分配链已接入;超托审核与真实HTTP待验,不能替代车队签收证据' },
|
2026-10-22 14:37:59 +08:00
|
|
|
{ title: '美本土散板', route: '/cross-border/pallet-shipping/create', modules: ['phase4-overseas-business', 'phase5-overseas-fulfillment'],
|
|
|
|
|
covered: '原单创建、需求池接车、PDA实收、原车加货及共享装车签收,核对原单终态与数量', remaining: '正常链已接入默认编排;异常履约、完整费用边界及真实HTTP待验' },
|
2026-10-22 15:41:50 +08:00
|
|
|
{ title: '海外采购', route: '/cross-border/procurement/create', modules: ['phase4-overseas-business', 'phase4-overseas-wallet-and-pallet-cost'],
|
|
|
|
|
covered: '创建、备注编辑、审核驳回;固定客户正常样品采购至寄客户及资金核对已接入默认链',
|
2026-10-22 14:37:59 +08:00
|
|
|
remaining: '固定客户已接入正常样品采购、收货、核费及寄客户;异常、退款及其他采购类型未全覆盖,真实HTTP待验' },
|
2026-10-22 15:41:50 +08:00
|
|
|
{ title: '贴标申请', route: '/overseas-warehouse/labeling/create', modules: ['phase4-overseas-client-services', 'phase4-overseas-dispatch', 'phase5-overseas-fulfillment'],
|
|
|
|
|
covered: '客户原申请审核、PDA换标、客户确认、同单新箱唛出库签收、车次释放及原费用结算回查', remaining: '异常边界与真实HTTP仍待验' },
|
2026-10-22 14:37:59 +08:00
|
|
|
{ title: '仓储转运', route: '/overseas-warehouse/storage-transfer/create', modules: ['phase4-overseas-client-services'],
|
2026-10-22 15:41:50 +08:00
|
|
|
covered: '客户申请、提交、审核与拒绝、取消边界;已有库存原单分配、关联换标、出库、异仓收货及唯一库存流水与结算终态核验', remaining: '在途转运入库计量、多来源及部分收货异常矩阵仍待补;真实HTTP待验' },
|
2026-10-22 14:37:59 +08:00
|
|
|
{ title: '货物废弃', route: '/overseas-warehouse/cargo-disposal/create', modules: ['phase4-overseas-client-services'],
|
|
|
|
|
covered: '申请、拒绝释放、批准锁箱、PDA照片与实际扣库、客户确认及终态重放', remaining: '已接入同一申请公共库存总账核对;独立箱/SKU流水证据、多来源边界及真实HTTP仍待验' },
|
|
|
|
|
].map(business => ({ ...business,
|
|
|
|
|
defaultModules: business.modules.filter(name => scope.defaultModules.includes(name)),
|
|
|
|
|
additionalModules: (business.additionalModules || []).map(name => ({ name,
|
|
|
|
|
defaultSelected: scope.defaultModules.includes(name),
|
|
|
|
|
skippedWithFixedResources: scope.fixedResourceSkippedModules.includes(name) })),
|
|
|
|
|
endToEndCoverage: 'INCOMPLETE', currentVersionVerified: false,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-21 13:22:35 +08:00
|
|
|
/** 只输出白名单统计和模块名,不转抄报告中的账号、地址、凭据或业务载荷。 */
|
|
|
|
|
export function summarizeReport(report) {
|
|
|
|
|
if (!report || !Array.isArray(report.steps) || report.steps.length === 0
|
|
|
|
|
|| report.steps.some((step) => typeof step.passed !== 'boolean')) {
|
|
|
|
|
throw new Error('报告缺少有效 steps,不能认定验收结果')
|
|
|
|
|
}
|
|
|
|
|
const steps = report.steps
|
|
|
|
|
const failed = steps.filter((step) => !step.passed && !step.skipped)
|
|
|
|
|
const skipped = steps.filter((step) => step.skipped)
|
|
|
|
|
const skippedModules = Array.isArray(report.modules) ? report.modules.filter((module) => module.skipped).length : 0
|
|
|
|
|
const gaps = Array.isArray(report.coverageGaps) ? report.coverageGaps : []
|
|
|
|
|
const blockers = gaps.filter((gap) => gap.blocking || gap.deploymentBlocker)
|
|
|
|
|
const scope = report.executionScope
|
|
|
|
|
// 历史报告不包含计划信息,不能从断言数量或模块名字推定完整执行。
|
|
|
|
|
const scopeKnown = Array.isArray(scope?.plannedModules) && scope.plannedModules.length > 0
|
|
|
|
|
&& Array.isArray(scope.defaultModules) && scope.defaultModules.length > 0
|
|
|
|
|
const observedModules = new Set(steps.map((step) => step.module))
|
|
|
|
|
const missingModules = scopeKnown ? scope.plannedModules.filter((name) => !observedModules.has(name)) : []
|
|
|
|
|
const limited = Boolean(scope?.preparationOnly || scope?.returnMainChainOnly || report.fullAcceptance === false)
|
|
|
|
|
const failedResult = Boolean(report.fatal) || report.success === false || failed.length > 0 || blockers.length > 0
|
|
|
|
|
return {
|
|
|
|
|
result: failedResult ? 'FAILED' : skipped.length || skippedModules || gaps.length || missingModules.length || limited ? 'INCOMPLETE' : 'RECORDED_CHECKS_PASSED',
|
|
|
|
|
passed: steps.filter((step) => step.passed && !step.skipped).length,
|
|
|
|
|
failed: failed.length,
|
|
|
|
|
skipped: skipped.length,
|
|
|
|
|
skippedModules,
|
|
|
|
|
blockingGaps: blockers.length,
|
|
|
|
|
nonBlockingGaps: gaps.length - blockers.length,
|
|
|
|
|
failedModules: [...new Set(failed.map((step) => step.module).filter(Boolean))],
|
|
|
|
|
// 无请求的失败包含前置不足和模块退出,不能计作独立接口缺陷。
|
|
|
|
|
failuresWithRequest: failed.filter((step) => step.request).length,
|
|
|
|
|
failuresWithoutRequest: failed.filter((step) => !step.request).length,
|
|
|
|
|
scope: !scopeKnown ? 'UNKNOWN_LEGACY_REPORT' : limited ? 'LIMITED_SCOPE'
|
|
|
|
|
: scope.defaultModules.every((name) => scope.plannedModules.includes(name)) ? 'DEFAULT_ADMIN_PLAN' : 'SELECTED_MODULES',
|
|
|
|
|
missingPlannedModules: missingModules,
|
|
|
|
|
currentVersionVerified: false,
|
|
|
|
|
pageAcceptance: 'NOT_VERIFIED_BY_HTTP',
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 默认仅展示范围;显式报告只读分析,不发现历史目录、不联网、不执行验收。 */
|
|
|
|
|
async function main(argv) {
|
|
|
|
|
if (argv.length && !(argv.length === 2 && argv[0] === '--report')) {
|
|
|
|
|
throw new Error('用法:node scripts/acceptance-overview.mjs [--report <明确JSON路径>]')
|
|
|
|
|
}
|
|
|
|
|
const scope = describeAcceptanceScope()
|
|
|
|
|
const result = {
|
|
|
|
|
purpose: '离线验收范围总览,不执行HTTP,不证明当前部署通过',
|
|
|
|
|
admin: { entry: 'scripts/admin-test-env-acceptance.mjs', ...scope },
|
2026-10-22 14:37:59 +08:00
|
|
|
homepageBusinesses: describeHomepageBusinessCoverage(),
|
2026-10-21 13:22:35 +08:00
|
|
|
otherEntries: [
|
|
|
|
|
{ entry: 'scripts/client-test-env-acceptance.mjs', scope: 'Client全量HTTP,独立执行' },
|
|
|
|
|
{ entry: 'scripts/client-phase2-test-env-acceptance.mjs', scope: '测量计费专项,不替代Client全量' },
|
|
|
|
|
{ entry: 'scripts/fleet-test-env-acceptance.mjs', scope: '车辆专项,不计入Admin默认全量' },
|
|
|
|
|
],
|
|
|
|
|
boundaries: ['可选模块不等于默认执行;其他专项入口详见scripts/README.md',
|
|
|
|
|
'本地合同、HTTP、页面与真机验收分别报告',
|
|
|
|
|
'报告通过只代表该次记录;需另核对源码、配置与实际部署版本',
|
|
|
|
|
'环境与凭据前置仍由实际入口校验;本工具不读取环境文件'],
|
|
|
|
|
}
|
|
|
|
|
if (argv.length) result.report = summarizeReport(JSON.parse(await readFile(resolve(argv[1]), 'utf8')))
|
|
|
|
|
console.log(JSON.stringify(result, null, 2))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
|
|
|
main(process.argv.slice(2)).catch((error) => { console.error(error.message); process.exitCode = 1 })
|
|
|
|
|
}
|