From ca288dec729efebdb15cb15cbf235805e699e43c Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Thu, 3 Sep 2026 18:13:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/admin-test-env-acceptance.mjs | 9 ++--- .../finance-direct-paid-isolation.mjs | 2 +- .../manual-state-loading-pda.mjs | 9 +++-- .../ocean-booking-fixture-contract.test.mjs | 6 ++++ .../admin-test-env-modules/phase3-customs.mjs | 2 +- .../admin-test-env-modules/phase3-loading.mjs | 2 +- ...admin-test-phone-fixture-contract.test.mjs | 11 ++++++ scripts/client-test-env-acceptance.mjs | 34 +++++++++++++------ 8 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 scripts/admin-test-phone-fixture-contract.test.mjs diff --git a/scripts/admin-test-env-acceptance.mjs b/scripts/admin-test-env-acceptance.mjs index 293371ddf..314510b89 100644 --- a/scripts/admin-test-env-acceptance.mjs +++ b/scripts/admin-test-env-acceptance.mjs @@ -530,14 +530,11 @@ export class AcceptanceRun { const headers = { Accept: 'application/json', ...extraHeaders } if (token) headers.Authorization = `Bearer ${token}` if (body !== undefined) headers['Content-Type'] = 'application/json; charset=utf-8' - const requestBody = path === '/api/auth/login' && body?.username && !body?.loginId - ? { ...body, loginId: body.username, username: undefined } - : body const maxAttempts = ['GET', 'HEAD'].includes(String(method).toUpperCase()) ? 3 : 1 for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { try { const response = await fetch(`${this.options.gateway}${path}`, { - method, headers, body: requestBody === undefined ? undefined : JSON.stringify(requestBody), + method, headers, body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(30_000), }) const content = await response.text() @@ -913,8 +910,8 @@ export class AcceptanceRun { async findAvailablePhone(module = 'customers') { this.phoneSequence = Number(this.phoneSequence || 0) + 1 - const markerDigits = this.marker.replace(/\D/g, '').slice(-8).padStart(8, '0') - const phone = `18${markerDigits}${String(this.phoneSequence % 10).padStart(1, '0')}` + const markerDigits = this.marker.replace(/\D/g, '').slice(-7).padStart(7, '0') + const phone = `18${markerDigits}${String(this.phoneSequence % 100).padStart(2, '0')}` this.addStep(module, '生成本轮隔离测试联系电话', null, /^1\d{10}$/.test(phone), `sequence=${this.phoneSequence}`) return phone diff --git a/scripts/admin-test-env-modules/finance-direct-paid-isolation.mjs b/scripts/admin-test-env-modules/finance-direct-paid-isolation.mjs index 20b0a4b67..3545aba6e 100644 --- a/scripts/admin-test-env-modules/finance-direct-paid-isolation.mjs +++ b/scripts/admin-test-env-modules/finance-direct-paid-isolation.mjs @@ -244,7 +244,7 @@ async function createSecondCustomer(context) { const fixture = context.context?.merchantQualificationFixture if (!fixture?.tenantId || !fixture?.username || !fixture?.password || fixture.marker !== context.marker) return null const loginResponse = await context.request('POST', '/api/auth/login', - { username: fixture.username, password: fixture.password }, '') + { loginId: fixture.username, password: fixture.password }, '') const login = context.apiSuccess(MODULE, '第二隔离客户登录', loginResponse, diagnostic(loginResponse), OPTIONS) return { customerId: id(fixture.tenantId), token: login?.accessToken } } finally { diff --git a/scripts/admin-test-env-modules/manual-state-loading-pda.mjs b/scripts/admin-test-env-modules/manual-state-loading-pda.mjs index 6390897a6..8364025f9 100644 --- a/scripts/admin-test-env-modules/manual-state-loading-pda.mjs +++ b/scripts/admin-test-env-modules/manual-state-loading-pda.mjs @@ -8,6 +8,8 @@ const CONTAINER_STATUSES = ['WAIT_LOADING', 'EDITING', 'LOADING', 'COMPLETED'] const PRELOAD_STATUSES = ['EDITING', 'LOADING'] const PDA_STATUSES = ['UNASSIGNED', 'IN_PROGRESS', 'AWAITING_REVIEW', 'COMPLETED'] +import { resolveOceanBookingAgent } from './ocean-booking-fixture.mjs' + function id(value) { return value == null ? '' : String(value) } function records(value) { return Array.isArray(value?.records) ? value.records : [] } function suffix(context, label) { @@ -128,14 +130,11 @@ async function createConfirmedContainer(context, source, label) { const value = suffix(context, label) const containerNo = iso6346ContainerNumber(`${context.marker}-manual-${label}`) const summary = source.detail.summary - const agentResponse = await context.request('GET', - '/api/platform/v1/suppliers?tenantId=0&category=BOOKING&status=ACTIVE&page=1&size=20') - const agentPage = context.apiSuccess(MODULE, `${label} 读取 ACTIVE 订舱代理`, agentResponse, '', OPTIONS) - const agent = agentPage?.records?.find?.((item) => item?.id && item?.name) - if (!agent) throw new Error(`${label} 未取得 ACTIVE 订舱代理`) const { ensureMatsonCarrierPartner } = await import('./carrier-partner-fixture.mjs') const carrier = await ensureMatsonCarrierPartner(context, MODULE, OPTIONS) if (!carrier?.id) throw new Error(`${label} 未取得 ACTIVE 船公司`) + const agent = await resolveOceanBookingAgent(context, MODULE, carrier.carrierCode, OPTIONS) + if (!agent) throw new Error(`${label} 未取得当前船公司关联的 ACTIVE 订舱代理`) const draft = { customerId: source.tenantId, customerName: summary.customerName || 'IT 测试客户', channelCode: summary.channelCode, warehouseId: source.warehouseId, diff --git a/scripts/admin-test-env-modules/ocean-booking-fixture-contract.test.mjs b/scripts/admin-test-env-modules/ocean-booking-fixture-contract.test.mjs index 709073d92..f3f4830b4 100644 --- a/scripts/admin-test-env-modules/ocean-booking-fixture-contract.test.mjs +++ b/scripts/admin-test-env-modules/ocean-booking-fixture-contract.test.mjs @@ -85,3 +85,9 @@ test('all production booking fixtures use DRAFT release and the shared requireme assert.match(source, /containerRequirements/, name) } }) + +test('manual PDA state fixture resolves an agent bound to the selected carrier', () => { + const source = read('manual-state-loading-pda.mjs') + assert.match(source, /resolveOceanBookingAgent\(context, MODULE, carrier\.carrierCode, OPTIONS\)/) + assert.doesNotMatch(source, /suppliers\?tenantId=0&category=BOOKING/) +}) diff --git a/scripts/admin-test-env-modules/phase3-customs.mjs b/scripts/admin-test-env-modules/phase3-customs.mjs index 28e5ffa0f..3b2b443e1 100644 --- a/scripts/admin-test-env-modules/phase3-customs.mjs +++ b/scripts/admin-test-env-modules/phase3-customs.mjs @@ -284,7 +284,7 @@ export async function run(context) { || !Number.isInteger(Number(carryingSlice?.packageCount)) || Number(carryingSlice.packageCount) <= 0) return const loginResponse = await request(context, 'POST', '/api/auth/login', { - username: MERCHANT_USERNAME, password: MERCHANT_PASSWORD, + loginId: MERCHANT_USERNAME, password: MERCHANT_PASSWORD, }, '') const merchantLogin = context.apiSuccess(MODULE, '客户账号登录用于真实报关文件协同', loginResponse, `username=${MERCHANT_USERNAME}`, OPTIONS) diff --git a/scripts/admin-test-env-modules/phase3-loading.mjs b/scripts/admin-test-env-modules/phase3-loading.mjs index dfb7b02c6..20cc67431 100644 --- a/scripts/admin-test-env-modules/phase3-loading.mjs +++ b/scripts/admin-test-env-modules/phase3-loading.mjs @@ -694,7 +694,7 @@ export async function run(context) { `taskVersion=${unchangedAfterInvalid?.version ?? 'missing'}, items=${taskItemsSignature(unchangedAfterInvalid)}, operations=${afterInvalidOperations?.length ?? 'missing'}`, OPTIONS) const merchantLogin = await context.request('POST', '/api/auth/login', { - username: MERCHANT_USERNAME, password: MERCHANT_PASSWORD, + loginId: MERCHANT_USERNAME, password: MERCHANT_PASSWORD, }, '') const merchantToken = merchantLogin.http === 200 && codeOf(merchantLogin) === '200' ? merchantLogin.body?.data?.accessToken : null diff --git a/scripts/admin-test-phone-fixture-contract.test.mjs b/scripts/admin-test-phone-fixture-contract.test.mjs new file mode 100644 index 000000000..2f97af0a2 --- /dev/null +++ b/scripts/admin-test-phone-fixture-contract.test.mjs @@ -0,0 +1,11 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const source = readFileSync(new URL('./admin-test-env-acceptance.mjs', import.meta.url), 'utf8') + +test('admin acceptance phone fixture keeps more than ten generated customers distinct', () => { + assert.match(source, /slice\(-7\)\.padStart\(7, '0'\)/) + assert.match(source, /phoneSequence % 100/) + assert.doesNotMatch(source, /phoneSequence % 10\)/) +}) diff --git a/scripts/client-test-env-acceptance.mjs b/scripts/client-test-env-acceptance.mjs index 0cebbba12..380fe7493 100644 --- a/scripts/client-test-env-acceptance.mjs +++ b/scripts/client-test-env-acceptance.mjs @@ -380,7 +380,7 @@ async function loginIdentifierModule() { }, ({ status, json }) => ({ status, rejectionCode: json?.code })) const renamedLogin = await apiStep('login-identifiers', 'login with the changed username', { - method: 'POST', path: '/api/auth/login', body: { username: temporaryUsername, password: clientPassword }, + method: 'POST', path: '/api/auth/login', body: { loginId: temporaryUsername, password: clientPassword }, }, ({ json }) => { requireSuccess(json) assertPresent(json.data?.accessToken, 'accessToken') @@ -397,7 +397,7 @@ async function loginIdentifierModule() { }, ({ json }) => ({ username: json.data.username })) const restoredLogin = await apiStep('login-identifiers', 'login after restoring the original username', { - method: 'POST', path: '/api/auth/login', body: { username, password: clientPassword }, + method: 'POST', path: '/api/auth/login', body: { loginId: username, password: clientPassword }, }, ({ json }) => { requireSuccess(json) assertPresent(json.data?.accessToken, 'accessToken') @@ -1770,7 +1770,7 @@ async function loadOrderMasterData(token, module) { requireSuccess(json) assertTrue(pageRecords(json.data).length > 0, 'at least one active DOMESTIC warehouse is required') }, ({ json }) => ({ total: json.data?.total, warehouseCount: pageRecords(json.data).length })) - const receiving = pageRecords(receivingWarehouses.json.data)[0] + const receivingCandidates = pageRecords(receivingWarehouses.json.data) const destinationWarehouses = await apiStep(module, 'load active FBA destination warehouses', { method: 'GET', path: '/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=FBA', token, @@ -1779,6 +1779,21 @@ async function loadOrderMasterData(token, module) { assertTrue(pageRecords(json.data).length > 0, 'at least one active FBA warehouse is required') }, ({ json }) => ({ total: json.data?.total, warehouseCount: pageRecords(json.data).length })) const destination = pageRecords(destinationWarehouses.json.data)[0] + let receiving = null + let pricedChannels = [] + for (const candidate of receivingCandidates) { + const response = await httpRequest({ + method: 'GET', path: `/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=${candidate.id}`, token, + }) + const candidates = String(response?.json?.code) === '200' ? arrayData(response.json.data) : [] + const available = candidates.filter((item) => arrayData(item.pricingTaxModes).length > 0) + if (available.length > 0) { + receiving = candidate + pricedChannels = available + break + } + } + assertPresent(receiving, 'active DOMESTIC warehouse with pricing channels') report.resources.receivingWarehouseId = receiving.id report.resources.destinationWarehouseId = destination.id @@ -1796,7 +1811,7 @@ async function loadOrderMasterData(token, module) { requireSuccess(json) assertTrue(arrayData(json.data).length > 0, 'at least one active pricing channel is required') }, ({ json }) => ({ channels: arrayData(json.data).map((item) => ({ channelCode: item.channelCode, priceModel: item.priceModel, pricingTaxModes: item.pricingTaxModes })) })) - const channel = arrayData(channels.json.data).find((item) => arrayData(item.pricingTaxModes).length > 0) + const channel = pricedChannels.find((item) => arrayData(item.pricingTaxModes).length > 0) assertPresent(channel, 'pricing channel with pricingTaxModes') report.resources.channelCode = channel.channelCode @@ -1812,7 +1827,7 @@ async function loadOrderMasterData(token, module) { destination, destinationCandidates: pageRecords(destinationWarehouses.json.data), channel, - channelCandidates: arrayData(channels.json.data).filter((item) => arrayData(item.pricingTaxModes).length > 0), + channelCandidates: pricedChannels, partner, } } @@ -2003,13 +2018,10 @@ async function softApiStep(module, name, request, validate, evidenceBuilder = de async function httpRequest({ method = 'GET', path, token, headers = {}, body, responseType = 'json' }) { const requestHeaders = { Accept: 'application/json', 'X-Request-Id': `client-e2e-${randomUUID()}`, ...headers } if (token) requestHeaders.Authorization = `Bearer ${token}` - const normalizedBody = path === '/api/auth/login' && body?.username && !body?.loginId - ? { ...body, loginId: body.username, username: undefined } - : body - let requestBody = normalizedBody - if (normalizedBody != null && !(normalizedBody instanceof FormData)) { + let requestBody = body + if (body != null && !(body instanceof FormData)) { requestHeaders['Content-Type'] = 'application/json' - requestBody = JSON.stringify(normalizedBody) + requestBody = JSON.stringify(body) } const response = await fetch(`${gateway}${path}`, { method,