灰度晋级:Firstleg 与 Lastleg 排车发布规则及累计修复 #31

Merged
admin merged 5 commits from test into main 2026-10-23 13:40:44 +08:00
4 changed files with 24 additions and 2 deletions
Showing only changes of commit ecc76d4f91 - Show all commits
+6
View File
@@ -459,6 +459,7 @@ export function parseArguments(argv) {
waitSplitRandomValue: Number(values.get('wait-split-random-value') || values.get('wait-split-sequence') || 1),
prepareDropshipManualCount: values.get('prepare-dropship-manual-count') === 'true',
prepareMeasurementReview: values.get('prepare-measurement-review') === 'true',
verifyNonIsoContainer: values.get('verify-non-iso-container') === 'true',
verifyPalletLocationGuide: values.has('verify-pallet-location-guide'),
routeMode: values.get('route-mode') || 'fixed',
quiet: values.get('quiet') === 'true',
@@ -495,6 +496,11 @@ export function validateExecutionOptions(options) {
if (options.environment && !['test', 'gray'].includes(options.environment)) throw new Error('Only test or explicit gray acceptance is supported')
if (options.f40TaxMode && !['BUY_DECLARATION', 'TAX_REFUND'].includes(options.f40TaxMode)) throw new Error('f40-tax-mode 必须为 BUY_DECLARATION 或 TAX_REFUND')
if (options.prepareDomesticPdaH5 && (options.requestedModules?.length !== 1 || options.requestedModules[0] !== 'phase5-root-fulfillment')) throw new Error('prepare-domestic-pda-h5 仅允许单独指定 phase5-root-fulfillment')
if (options.verifyNonIsoContainer && (options.environment !== 'test'
|| options.requestedModules?.length !== 1 || options.requestedModules[0] !== 'phase3-container-loading'
|| options.verifyPalletLocationGuide)) {
throw new Error('verify-non-iso-container 仅允许 TEST 单独柜号链,不能混用库位块删除探针')
}
if (options.prepareMeasurementReview && (options.environment !== 'test'
|| options.requestedModules?.length !== 1 || options.requestedModules[0] !== 'inbound-measurement'
|| options.waitSplitCount !== 1 || options.prepareDomesticPdaH5 || options.prepareDropshipManualCount)) {
+1 -1
View File
@@ -451,7 +451,7 @@ plan/item 版本、分配箱数及总数投影全部零漂移。脚本不从当
`phase3-pda-convergence` 同时回查 `/tasks/pending` 与稳定 task detail:两者的 `workflowStatus`、`itemCount`、`pendingItemCount` 必须来自同一任务明细事实。全拣任务必须在两处均为 `AWAITING_REVIEW`,可执行证据 GET,但派生 `actions=[]`,不得重复发送 batch;操作历史必须恰有一次有效 `BATCH`。若 pending 仍按 assignment 返回 `IN_PROGRESS`,该轮以真实 HTTP 失败阻断,不由脚本或前端改写状态。
`phase3-container-loading` 使用非 ISO 柜号 `JZXH10231043` 验证关联与预装确认,PDA 领取前再纠正为本轮 `TEST-…-NOISO` 柜号,继续完成拣货、五类合成装柜证据及装柜完成,保留空号拒绝和作业后禁改校验;不修改既有实际作业单。跨服务接收须按本轮来源柜及最终柜号另行回查,不能仅凭 PDA 完成视为海外消费通过。
`phase3-container-loading` 使用非 ISO 柜号 `JZXH10231043` 验证关联与预装确认,PDA 领取前再纠正为本轮 `TEST-…-NOISO` 柜号,继续完成拣货、五类合成装柜证据及装柜完成,保留空号拒绝和作业后禁改校验;不修改既有实际作业单。仅验证柜号链时,从总入口使用 `--modules phase3-container-loading --verify-non-iso-container true`,跳过跨渠道旁路分配和移除并明确记录该覆盖缺口;不添加整票库位块删除探针。跨服务接收须按本轮来源柜及最终柜号另行回查,不能仅凭 PDA 完成视为海外消费通过。
部署后需要定向验证该完整链时,模块顺序固定为:
@@ -238,7 +238,10 @@ export async function run(context) {
const channelIsolation = selectPlatformChannelIsolationPair(
context.createdResources?.platformLogisticsChannels, context.marker, warehouseId, channelCode)
if (!channelIsolation.pair) {
if (context.options?.verifyNonIsoContainer) {
context.addCoverageGap(MODULE, OPTIONS.scenario, '跨渠道分配及移除',
'本轮仅验证非 ISO 柜号履约链,不创建或删除旁路分配', 'out-of-scope', { blocking: false })
} else if (!channelIsolation.pair) {
context.addStep(MODULE, '两个真实有效渠道的柜次隔离 fixture', null, false,
`${channelIsolation.reason}; originWarehouseId=${warehouseId}, primaryChannelCode=${channelCode}`, OPTIONS)
} else {
@@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs'
import test from 'node:test'
import { controlledPhotoDownload } from './phase3-container-overview.mjs'
import { parseArguments, validateExecutionOptions } from '../admin-test-env-acceptance.mjs'
const read = (path) => readFileSync(new URL(path, import.meta.url), 'utf8')
const overviewOpenApi = read('../../firstleg/src/main/resources/openapi/phase3-container-overview-v1.yaml')
@@ -24,6 +25,18 @@ const phase3LoadingAcceptance = read('phase3-loading.mjs')
const truckAcceptance = read('truck-appointments.mjs')
const matrix = read('../../../docs/国内货柜履约/归档/货柜管理全链验收矩阵-2026-08-13.md')
test('non-ISO HTTP scope excludes unrelated deletion probes and keeps non-ISO completion facts', () => {
const options = parseArguments(['--modules', 'phase3-container-loading', '--verify-non-iso-container', 'true',
'--gateway', 'https://acceptance.invalid', '--username', 'test', '--password', 'test',
'--pda-username', 'pda', '--pda-password', 'test'])
assert.equal(validateExecutionOptions(options).verifyNonIsoContainer, true)
assert.throws(() => validateExecutionOptions({ ...options, verifyPalletLocationGuide: true }), /删除探针/)
assert.throws(() => validateExecutionOptions({ ...options, requestedModules: ['phase3-loading'] }), /单独柜号链/)
assert.match(containerLoadingAcceptance, /const realContainerNo = 'JZXH10231043'/)
assert.match(containerLoadingAcceptance, /const replacementContainerNo = `TEST-\$\{context.marker\}-NOISO`/)
assert.match(containerLoadingAcceptance, /if \(context.options\?\.verifyNonIsoContainer\)[\s\S]*?addCoverageGap[\s\S]*?else if \(!channelIsolation.pair\)/)
})
test('container acceptance reads the canonical domestic workbench route', () => {
assert.match(containerLoadingAcceptance, /const DOMESTIC_CONTAINERS = `\$\{CONTAINERS\}\/domestic`/)
assert.match(containerLoadingAcceptance, /function containerRecord\(value\) \{ return value\?\.container \}/)