diff --git a/scripts/admin-test-env-modules/phase4-carrier-nodes-contract.test.mjs b/scripts/admin-test-env-modules/phase4-carrier-nodes-contract.test.mjs index 1fd31f443..edce73035 100644 --- a/scripts/admin-test-env-modules/phase4-carrier-nodes-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase4-carrier-nodes-contract.test.mjs @@ -128,7 +128,7 @@ function markerOwnedContext() { }] const histories = [{ id: String(nextId++), action: 'AUTO_CREATE', fromVersion: null, toVersion: 0, - reason: '国内确认出仓自动创建', operatorId: CUSTOMER_ID, operatorName: 'fixture', + reason: '国内确认出仓自动创建', operatorId: '1000001', operatorName: 'fixture', createdAt: '2026-08-24T08:00:01', }] const detail = () => ({ query: { query: clone(task), expired: true }, @@ -178,7 +178,7 @@ function markerOwnedContext() { } else task.status = 'READY' } histories.push({ id: String(nextId++), action: 'MANUAL_NODE', fromVersion: task.version - 1, - toVersion: task.version, reason: body.reason, operatorId: CUSTOMER_ID, operatorName: 'fixture', + toVersion: task.version, reason: body.reason, operatorId: '1000001', operatorName: 'fixture', createdAt: body.observedAt }) const saved = detail(); idempotency.set(key, { signature, detail: clone(saved) }) return ok(saved, `req-${key}`) diff --git a/scripts/admin-test-env-modules/phase4-carrier-nodes.mjs b/scripts/admin-test-env-modules/phase4-carrier-nodes.mjs index a2a5ba478..c5883a15b 100644 --- a/scripts/admin-test-env-modules/phase4-carrier-nodes.mjs +++ b/scripts/admin-test-env-modules/phase4-carrier-nodes.mjs @@ -71,7 +71,7 @@ function validHistory(item) { return validLongId(item?.id) && Boolean(item?.action) && (item?.fromVersion == null || number(item.fromVersion)) && (item?.toVersion == null || number(item.toVersion)) - && nullableText(item?.reason) && nullableId(item?.operatorId) + && nullableText(item?.reason) && (item?.operatorId == null || validNumericId(item.operatorId)) && nullableText(item?.operatorName) && nullableIso(item?.createdAt) } diff --git a/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs b/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs index 07be663c7..45e5a374e 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs @@ -45,6 +45,9 @@ test('PDA module covers the container-number receiving contract and excludes out assert.match(source, /retained as acceptance evidence/) assert.match(source, /preferredProducer\?\.cartonMarks\?\.length/) assert.match(source, /for \(const \[index, cartonMark\] of cartonMarks\.entries\(\)\)/) + assert.match(source, /last-leg\/orders\?page=1&size=200&containerNo=/) + assert.match(source, /phase4OverseasPda\.orderId = id\(order\.id\)/) + assert.match(source, /phase4OverseasPda\.customerId = id\(order\.customerId\)/) }) test('missing WAREHOUSE_PICKER overseas authority is a deployment blocker', async () => { diff --git a/scripts/admin-test-env-modules/phase4-overseas-pda.mjs b/scripts/admin-test-env-modules/phase4-overseas-pda.mjs index 1720315b5..d24debae7 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-pda.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-pda.mjs @@ -17,6 +17,7 @@ function forbidden(response) { return [401, 403].includes(response?.http) || (re function trace(response) { return `HTTP=${response?.http ?? 'missing'}, code=${response?.body?.code ?? 'missing'}, requestId=${response?.requestId || 'missing'}, traceId=${response?.traceId || 'missing'}` } function key(context, action, suffix = '') { return `${context.marker}-${context.pdaContainerSuffix || ''}-${action}-${suffix}`.replace(/[^A-Za-z0-9:_-]/g, '').slice(0, 120) } function markerSuffix(context) { return `${context.marker}${context.pdaContainerSuffix || ''}`.replace(/[^A-Za-z0-9]/g, '').slice(-16).toUpperCase() } +function records(data) { return Array.isArray(data?.records) ? data.records : Array.isArray(data) ? data : [] } function blockerGap(context, name, reason, category = 'fixture-unavailable', response = null) { context.addCoverageGap(MODULE, SCENARIO, name, reason, category, { deploymentBlocker: true }) @@ -35,6 +36,23 @@ async function expected(context, token, method, name, path, body, predicate) { return response } +async function captureCompletedOrder(context, containerNo, expectedCustomerId) { + const response = await context.requestOverseas('GET', + `/api/platform/v1/last-leg/orders?page=1&size=200&containerNo=${encodeURIComponent(containerNo)}`) + const candidates = records(response?.body?.data) + .filter((item) => id(item?.containerNo) === id(containerNo)) + const exactCustomer = expectedCustomerId == null ? candidates + : candidates.filter((item) => id(item?.customerId) === id(expectedCustomerId)) + const order = exactCustomer[0] || candidates[0] + const mapped = success(response) && longId(order?.id) && nonBlank(id(order?.customerId)) + context.assert(MODULE, '完成柜回查平台尾程订单并冻结跨区身份', response, mapped, + `containerNo=${containerNo}, orderId=${id(order?.id) || 'missing'}, customerId=${id(order?.customerId) || 'missing'}; ${trace(response)}`, OPTIONS) + if (mapped) { + context.createdResources.phase4OverseasPda.orderId = id(order.id) + context.createdResources.phase4OverseasPda.customerId = id(order.customerId) + } +} + async function loginMerchantForNegativeProbe(context) { const fixture = context.context?.merchantQualificationFixture if (!fixture?.username || !fixture?.password) return null @@ -362,11 +380,14 @@ async function runReceiving(context, token, secondaryToken, merchantToken, prefe const after = await context.requestOverseas('GET', '/api/pda/v1/last-leg/receiving-containers/current', undefined, {}, token) context.addStep(MODULE, '完成后current返回404正常空态', after, notFound(after), trace(after), OPTIONS) context.createdResources.phase4OverseasPda.finalStatus = completed.data?.status || null + if (context.createdResources.phase4OverseasPda.finalStatus === 'COMPLETED') { + await captureCompletedOrder(context, containerNo, preferredProducer?.tenantId) + } } export async function run(context) { context.createdResources.phase4OverseasPda = { - marker: context.marker, containerNo: null, customerId: null, + marker: context.marker, containerNo: null, customerId: null, orderId: null, palletIds: [], locationIds: [], finalStatus: null, pickerMembershipIds: [], completedContainers: [], overseasWarehouseId: null, overseasWarehouseName: null, cleanup: 'Shared warehouse, pallets and locations used by completed inventory are retained as acceptance evidence',