fix(firstleg): map broker submission fields in customs container

This commit is contained in:
2026-09-26 13:57:53 +08:00
parent ff2386e5bf
commit e46d541780
3 changed files with 16 additions and 12 deletions
@@ -49,8 +49,8 @@ test('mixed fixture producer is registered and uses only public HTTP composition
test('coverage matrix freezes every requested product boundary', () => {
const text = CONTAINER_CUSTOMS_MATRIX.map((item) => `${item.scenario}:${item.expected}`).join('\n')
for (const token of ['买单XLSX', '多对多', '覆盖', '改关联', '删除', '冻结', '逐份完成文件',
'最后一份', '混合', '缺资料', '开始后锁定', '客户隔离', '买单隐藏', '50MiB', '任意格式',
for (const token of ['买单XLSX', '多对多', '覆盖', '改关联', '删除', '冻结', '回执确认', '报关行执行', '完税文件',
'最后上传', '混合', '缺资料', '开始后锁定', '客户隔离', '买单隐藏', '50MiB', '任意格式',
'幂等', '并发', 'A0409', '零漂移', '自动离仓', 'Outbox', '历史', 'Admin与Client权限']) {
assert.match(text, new RegExp(token))
}
@@ -69,7 +69,7 @@ test('HTTP scenario contains file, state, idempotency, concurrency and zero-drif
for (const token of ['EXACT_50_MB + 1', '.customblob', 'application/octet-stream',
'缺退税资料禁止开始且零漂移', '开始后资料范围锁定A0409零漂移',
"['上传'", "['覆盖'", "['改关联'", "['删除'", '开始后${label}锁定A0409零漂移',
'最后一份完成文件前禁止整柜完成', '客户确认整份回执后纯退税柜完成', '客户确认整份回执并同键重放',
'回执未确认且未发送执行时禁止上传完税文件', '客户确认整份回执后仍处于报关中', '真实报关完税文件齐全后纯退税柜完成', '客户确认回执后仍未完成整柜',
'混合柜平台详情精确包含BUY与TAX长ID票', 'Client只见本客户TAX票且完全隐藏BUY票',
'买单导出只处理实际装柜BUY行', '混合柜最后一份完成文件后整柜COMPLETED',
'自动离仓只写一次SYSTEM历史', '自动离仓只创建一个OUTBOUND_CONFIRM长ID任务',
@@ -87,7 +87,7 @@ test('HTTP scenario contains file, state, idempotency, concurrency and zero-drif
test('database evidence list covers atomicity, history, dispatch and outbox without embedding SQL writes', () => {
for (const table of ['fl_customs_container', 'fl_customs_customer_document_inbound',
'fl_customs_completion_document', 'fl_customs_command', 'fl_customs_container_history',
'fl_customs_completion_document', 'fl_customs_final_document', 'fl_customs_command', 'fl_customs_container_history',
'fl_outbound_dispatch', 'fl_event_outbox']) assert.ok(READ_ONLY_DB_EVIDENCE.includes(table), table)
assert.doesNotMatch(source, /\b(?:INSERT|UPDATE|DELETE)\s+(?:INTO|FROM|[a-z_])/i)
})
@@ -58,12 +58,12 @@ async function runAllBuy(context, routes, fixture) {
xlsx.http === 200 && xlsxMagic(xlsx.bytes),
`HTTP=${xlsx.http}, bytes=${xlsx.bytes?.length ?? 0}, contentType=${xlsx.contentType || 'missing'}`, OPTIONS)
const startBody = { expectedVersion: Number(detail.body?.data?.version), remark: context.marker }
const startBody = { expectedVersion: Number(detail.body?.data?.version), confirm: true, remark: context.marker }
const started = await routes.platformStart(context, fixture, startBody, key(context, 'all-buy-start'))
context.assert(MODULE, '全买单柜开始报关', started,
success(started) && started.body?.data?.status === 'DECLARING', evidence(started), OPTIONS)
const completeBody = { expectedVersion: Number(started.body?.data?.version), remark: context.marker }
const completeBody = { expectedVersion: Number(started.body?.data?.version), confirm: true, remark: context.marker }
const completed = await routes.platformComplete(context, fixture, completeBody, key(context, 'all-buy-complete'))
context.assert(MODULE, '全买单柜完成并触发自动离仓意图', completed,
success(completed) && completed.body?.data?.status === 'COMPLETED', evidence(completed), OPTIONS)
@@ -153,7 +153,7 @@ async function runRefund(context, routes, fixture) {
const ready = await platformDetail(context, routes, fixture)
const started = await routes.platformStart(context, fixture, {
expectedVersion: Number(ready.body?.data?.version), remark: context.marker,
expectedVersion: Number(ready.body?.data?.version), confirm: true, remark: context.marker,
}, key(context, 'refund-start'))
context.assert(MODULE, '纯退税柜资料齐全后开始并冻结', started,
success(started) && started.body?.data?.status === 'DECLARING'
@@ -184,7 +184,7 @@ async function runRefund(context, routes, fixture) {
const documents = frozenSnapshot?.documents || []
for (const [index, document] of documents.entries()) {
const receipt = await routes.platformUploadReceipt(context, fixture, {
documentId: id(document.documentId), expectedVersion: Number(document.source?.version),
documentId: id(document.documentId), expectedVersion: 0,
fileName: `${context.marker}-broker-receipt-${index + 1}.pdf`,
contentType: 'application/pdf', content: Uint8Array.from([0x52, index + 1]),
}, key(context, `receipt-${index + 1}`))
@@ -216,11 +216,11 @@ async function runRefund(context, routes, fixture) {
success(afterConfirm) && afterConfirm.body?.data?.status === 'DECLARING' && confirmed.every(success),
evidence(afterConfirm), OPTIONS)
const submitted = await routes.platformSubmitToBroker(context, fixture,
{ expectedVersion: Number(afterConfirm.body?.data?.version), remark: context.marker }, key(context, 'submit-to-broker'))
{ expectedVersion: Number(afterConfirm.body?.data?.version), confirm: true, remark: context.marker }, key(context, 'submit-to-broker'))
context.assert(MODULE, '平台发送客户确认回执给报关行执行', submitted,
success(submitted) && submitted.body?.data?.status === 'DECLARING', evidence(submitted), OPTIONS)
const submitReplay = await routes.platformSubmitToBroker(context, fixture,
{ expectedVersion: Number(submitted.body?.data?.version), remark: context.marker }, key(context, 'submit-to-broker'))
{ expectedVersion: Number(afterConfirm.body?.data?.version), confirm: true, remark: context.marker }, key(context, 'submit-to-broker'))
context.assert(MODULE, '发送报关行执行同键重放', submitReplay, success(submitReplay), evidence(submitReplay), OPTIONS)
for (const [index, document] of documents.entries()) {
const finalDocument = await routes.platformUploadFinal(context, fixture, {
@@ -365,7 +365,7 @@ async function runMixedConcurrency(context, routes, fixture) {
&& receiptConfirmReplay.body?.data?.status === 'DECLARING',
[receiptConfirm, receiptConfirmReplay].map((response) => evidence(response)).join('; '), OPTIONS)
const submitted = await routes.platformSubmitToBroker(context, fixture,
{ expectedVersion: Number(receiptConfirm.body?.data?.version), remark: context.marker }, key(context, 'mixed-submit-to-broker'))
{ expectedVersion: Number(receiptConfirm.body?.data?.version), confirm: true, remark: context.marker }, key(context, 'mixed-submit-to-broker'))
context.assert(MODULE, '混合柜发送确认回执给报关行执行', submitted,
success(submitted) && submitted.body?.data?.status === 'DECLARING', evidence(submitted), OPTIONS)
const finalRequest = {
@@ -381,6 +381,9 @@ async function runMixedConcurrency(context, routes, fixture) {
&& id(finalDocument.body?.data?.finalDocument?.fileId) === id(finalReplay.body?.data?.finalDocument?.fileId)
context.assert(MODULE, '真实报关完税文件同键重放收敛', finalReplay || finalDocument, finalConverged,
[finalDocument, finalReplay].filter(Boolean).map((response) => evidence(response)).join('; '), OPTIONS)
if (!finalConverged) {
blocker(context, '完税文件幂等收敛', '完成文件未成功或同键重放未收敛,已停止后续自动离仓写入')
}
const finalDifferent = await routes.platformUploadFinal(context, fixture,
{ ...finalRequest, content: Uint8Array.from([0x44, 0x49, 0x46]) }, finalKey)
context.assert(MODULE, '完税文件同键异载荷A0409', finalDifferent, a0409(finalDifferent), evidence(finalDifferent), OPTIONS)