优化
This commit is contained in:
@@ -4,6 +4,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import process from 'node:process'
|
||||
import { normalizeFixedCustomerId, requireFixedSecondaryCustomer } from './admin-test-env-modules/merchant-qualification-fixture.mjs'
|
||||
|
||||
const CONFIGURED_MODULES = ['merchant-qualification-fixture', 'customers', 'overseas-storage-fees', 'system', 'notifications', 'pda-releases', 'audit', 'dashboard', 'self-operated-analytics', 'master-data', 'suppliers', 'domestic-container', 'orders', 'inbound-hold', 'fee-items', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'inbound-fees', 'phase3-container-loading', 'phase3-container-overview', 'phase3-loading', 'phase3-loading-model-bridge', 'phase3-pda-convergence', 'phase3-evidence-gate', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments', 'service-center-flow', 'phase3-loading-finalize', 'phase3-container-documents', 'phase3-customs', 'phase3-container-customs', 'phase3-port-schedules', 'sailing-calendar', 'phase3-carrier-schedule-queries', 'phase3-outbound-dispatch', 'phase3-outbound-aggregate', 'phase3-dispatch-gate', 'phase3-waiting-for-loading', 'phase3-warehouse-rbac', 'phase3-order-splitting', 'manual-state-matrix', 'phase4-overseas-warehouse', 'phase4-carrier-pickup-ready', 'appointment-work', 'phase4-tail-order-controls', 'phase4-overseas-pda', 'phase4-overseas-pda-operations', 'phase4-overseas-return-receipt', 'phase4-overseas-return-followups', 'phase4-overseas-inbound-operations', 'phase4-overseas-client-services', 'phase4-overseas-dispatch', 'phase5-overseas-fulfillment', 'phase5-terminal-drayage', 'phase4-cross-region-events', 'phase4-overseas-business', 'phase4-overseas-master-data', 'fleet-management', 'phase4-carrier-nodes', 'finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full', 'finance-direct-paid-isolation', 'finance-downloads', 'firstleg-container-fee-allocation', 'phase4-overseas-finance-cross-region', 'phase4-overseas-wallet-and-pallet-cost', 'customer-operating-statistics']
|
||||
const OPTIONAL_MODULES = ['phase5-root-fulfillment', 'manual-order-splitting-fixture', 'manual-order-splitting-verify',
|
||||
@@ -324,6 +325,7 @@ function usage() {
|
||||
--pda-username <name> 国内装柜链必填,实际领取任务的 PDA 作业员账号
|
||||
--pda-password <value> 国内装柜链必填,PDA 作业员密码
|
||||
--environment <name> 默认 test;gray 仅允许显式 FBA 定向闭环
|
||||
--fixed-customer-id <id> 复用既有客户编号,灰度必填或配置 ADMIN_GRAY_FIXED_CUSTOMER_ID
|
||||
--modules <names> 逗号分隔,默认 ${DEFAULT_MODULES.join(',')}
|
||||
--f40-tax-mode <mode> F40 税制:BUY_DECLARATION 或 TAX_REFUND
|
||||
--customer-name <name> 指定本轮隔离客户名称,默认自动生成
|
||||
@@ -354,20 +356,39 @@ function usage() {
|
||||
ADMIN_TEST_WEIGHT_IMAGE_PATH, ADMIN_TEST_CUSTOMER_NAME, MERCHANT_TEST_USERNAME,
|
||||
MERCHANT_TEST_PASSWORD, ADMIN_PDA_USERNAME, ADMIN_PDA_PASSWORD, ADMIN_WAIT_SPLIT_COUNT。
|
||||
gray 使用独立 ADMIN_GRAY_GATEWAY/USERNAME/PASSWORD、ADMIN_GRAY_PDA_USERNAME/PASSWORD、
|
||||
ADMIN_GRAY_CUSTOMER_NAME、MERCHANT_GRAY_USERNAME/PASSWORD;不回落 TEST 凭据。`)
|
||||
ADMIN_GRAY_FIXED_CUSTOMER_ID、MERCHANT_GRAY_USERNAME/PASSWORD;必须复用既有客户,不回落 TEST 凭据。`)
|
||||
}
|
||||
|
||||
const fleetPoolGrayOptions = new WeakSet()
|
||||
|
||||
/** 灰度拼车只从本次显式配置读取两个既有客户,不借用TEST凭据或自动建客。 */
|
||||
export function grayFixedCustomerOptions(env) {
|
||||
const options = { fixedCustomerId: env.CHONGCHONG_ACCEPTANCE_CUSTOMER_ID,
|
||||
merchantUsername: env.CHONGCHONG_MERCHANT_USERNAME, merchantPassword: env.CHONGCHONG_MERCHANT_PASSWORD,
|
||||
fixedSecondaryCustomer: { customerId: env.CHONGCHONG_ACCEPTANCE_SECONDARY_CUSTOMER_ID,
|
||||
username: env.CHONGCHONG_SECONDARY_MERCHANT_USERNAME, password: env.CHONGCHONG_SECONDARY_MERCHANT_PASSWORD } }
|
||||
if (['fixedCustomerId', 'merchantUsername', 'merchantPassword'].some(key => typeof options[key] !== 'string' || !options[key].trim()))
|
||||
throw Error('GRAY 必须配置 CHONGCHONG_ACCEPTANCE_CUSTOMER_ID 和 CHONGCHONG_MERCHANT_USERNAME/PASSWORD;禁止自动建客')
|
||||
options.fixedCustomerId = normalizeFixedCustomerId(options.fixedCustomerId)
|
||||
const secondary = requireFixedSecondaryCustomer(options)
|
||||
options.fixedSecondaryCustomer = { ...secondary, customerId: normalizeFixedCustomerId(secondary.customerId) }
|
||||
if (env.CHONGCHONG_ACCEPTANCE_FLEET_RESOURCES) options.fixedFleetResources = JSON.parse(env.CHONGCHONG_ACCEPTANCE_FLEET_RESOURCES)
|
||||
if (env.CHONGCHONG_ACCEPTANCE_FIXED_ACTORS) options.fixedActors = JSON.parse(env.CHONGCHONG_ACCEPTANCE_FIXED_ACTORS)
|
||||
if (env.CHONGCHONG_ACCEPTANCE_PDA_ACTORS) options.fixedPdaActors = JSON.parse(env.CHONGCHONG_ACCEPTANCE_PDA_ACTORS)
|
||||
return options
|
||||
}
|
||||
|
||||
/** Dedicated pool orchestrator transport only: no generic module execution or CLI scope switch. */
|
||||
export function createFleetPoolGrayOptions(input) {
|
||||
const keys = ['gateway', 'username', 'password', 'output', 'markerNamespace']
|
||||
if (!input || Object.keys(input).some(key => !keys.includes(key))
|
||||
const keys = ['gateway', 'username', 'password', 'output', 'markerNamespace', 'fixedCustomerId', 'merchantUsername', 'merchantPassword']
|
||||
if (!input || Object.keys(input).some(key => ![...keys, 'fixedSecondaryCustomer', 'fixedFleetResources', 'fixedActors', 'fixedPdaActors'].includes(key))
|
||||
|| keys.some(key => typeof input[key] !== 'string' || !input[key].trim())) throw Error('Explicit gray pool transport configuration required')
|
||||
const customerId = normalizeFixedCustomerId(input.fixedCustomerId), secondary = requireFixedSecondaryCustomer(input)
|
||||
const target = new URL(input.gateway)
|
||||
if (target.protocol !== 'https:' || target.username || target.password || target.search || target.hash || target.pathname !== '/')
|
||||
throw Error('Gray pool requires the unified HTTPS origin')
|
||||
const options = Object.freeze({ ...input, environment: 'gray', executionScope: 'FLEET_CAPACITY_POOL_GRAY',
|
||||
const options = Object.freeze({ ...input, fixedCustomerId: customerId,
|
||||
fixedSecondaryCustomer: Object.freeze({ ...secondary, customerId: normalizeFixedCustomerId(secondary.customerId) }), environment: 'gray', executionScope: 'FLEET_CAPACITY_POOL_GRAY',
|
||||
modules: Object.freeze([]), requestedModules: Object.freeze([]), quiet: true, waitSplitCount: 1, verifyUnloadEventRecovery: false })
|
||||
fleetPoolGrayOptions.add(options)
|
||||
return options
|
||||
@@ -443,7 +464,7 @@ export function parseArguments(argv) {
|
||||
modules,
|
||||
requestedModules,
|
||||
stopFile: values.get('stop-file') || '',
|
||||
fixedCustomerId: environment === 'test' ? process.env.ADMIN_TEST_FIXED_CUSTOMER_ID || '' : '',
|
||||
fixedCustomerId: values.get('fixed-customer-id') || envValue('ADMIN_TEST_FIXED_CUSTOMER_ID', 'ADMIN_GRAY_FIXED_CUSTOMER_ID'),
|
||||
customerName: values.get('customer-name') || envValue('ADMIN_TEST_CUSTOMER_NAME', 'ADMIN_GRAY_CUSTOMER_NAME'),
|
||||
merchantUsername,
|
||||
merchantPassword,
|
||||
@@ -484,6 +505,9 @@ export function validateExecutionOptions(options) {
|
||||
if (target.protocol !== 'https:' || target.username || target.password || target.search || target.hash
|
||||
|| target.pathname !== '/') throw new Error('gray gateway 必须为统一 HTTPS 源站,不带 /api、凭据或查询参数')
|
||||
if (!options.output?.trim()) throw new Error('gray 必须指定独立 --output 报告路径')
|
||||
if (!options.fixedCustomerId || !options.merchantUsername || !options.merchantPassword)
|
||||
throw new Error('gray 必须配置 ADMIN_GRAY_FIXED_CUSTOMER_ID、MERCHANT_GRAY_USERNAME/PASSWORD;禁止自动创建客户')
|
||||
normalizeFixedCustomerId(options.fixedCustomerId)
|
||||
}
|
||||
if (options.verifyUnloadEventRecovery && !options.modules.includes('phase4-overseas-pda')) {
|
||||
throw new Error('卸柜事件恢复验收必须包含正式 phase4-overseas-pda 收货链')
|
||||
@@ -655,6 +679,15 @@ export class AcceptanceRun {
|
||||
constructor(options) {
|
||||
assertGrayExecutionScope(options)
|
||||
this.options = { ...options }
|
||||
this.fixedPdaLeases = new Set()
|
||||
if (options.environment === 'gray') {
|
||||
this.options.fixedCustomerId ||= process.env.ADMIN_GRAY_FIXED_CUSTOMER_ID || ''
|
||||
this.options.merchantUsername ||= process.env.MERCHANT_GRAY_USERNAME || ''
|
||||
this.options.merchantPassword ||= process.env.MERCHANT_GRAY_PASSWORD || ''
|
||||
this.options.fixedFleetResources ??= JSON.parse(process.env.ADMIN_GRAY_FLEET_RESOURCES || '[]')
|
||||
this.options.fixedActors ??= JSON.parse(process.env.ADMIN_GRAY_FIXED_ACTORS || '{}')
|
||||
this.options.fixedPdaActors ??= JSON.parse(process.env.ADMIN_GRAY_PDA_ACTORS || '[]')
|
||||
}
|
||||
if (options.environment === 'test') {
|
||||
this.options.fixedCustomerId ||= process.env.ADMIN_TEST_FIXED_CUSTOMER_ID || ''
|
||||
this.options.merchantUsername ||= process.env.MERCHANT_TEST_USERNAME || ''
|
||||
@@ -662,6 +695,7 @@ export class AcceptanceRun {
|
||||
this.options.fixedPdaActors ??= JSON.parse(process.env.ADMIN_TEST_OVERSEAS_PDA_ACTORS || '[]')
|
||||
this.options.fixedDomesticDenyActor ??= JSON.parse(process.env.ADMIN_TEST_DOMESTIC_DENY_ACTOR || 'null')
|
||||
this.options.fixedFleetResources ??= JSON.parse(process.env.ADMIN_TEST_FLEET_RESOURCES || '[]')
|
||||
this.options.fixedActors ??= JSON.parse(process.env.ADMIN_TEST_FIXED_ACTORS || '{}')
|
||||
if (!this.options.fixedSecondaryCustomer && process.env.ADMIN_TEST_SECONDARY_CUSTOMER_ID) {
|
||||
this.options.fixedSecondaryCustomer = { customerId: process.env.ADMIN_TEST_SECONDARY_CUSTOMER_ID,
|
||||
username: process.env.ADMIN_TEST_SECONDARY_MERCHANT_USERNAME || '', password: process.env.ADMIN_TEST_SECONDARY_MERCHANT_PASSWORD || '' }
|
||||
@@ -686,12 +720,14 @@ export class AcceptanceRun {
|
||||
}
|
||||
|
||||
assertCustomerWriteAllowed(method, path) {
|
||||
if (this.options.environment !== 'test') return
|
||||
const pathname = new URL(path, 'https://acceptance.invalid').pathname.replace(/\/$/, '')
|
||||
if (String(method).toUpperCase() === 'POST'
|
||||
&& /^\/api\/platform\/v1\/(iam\/(roles|users|permissions)|suppliers)$/.test(pathname))
|
||||
throw new Error('固定资源验收禁止新增角色、账号、权限或供应商;请配置并复用既有资源')
|
||||
if (String(method).toUpperCase() === 'POST' && (pathname === '/api/platform/v1/iam/tenants'
|
||||
|| pathname === '/api/auth/register'
|
||||
|| /^\/api\/platform\/v1\/iam\/tenants\/[^/]+\/(archive|submit-review)$/.test(pathname))) {
|
||||
throw new Error('TEST 固定客户模式禁止创建、归档客户或重提资质;请复用已配置客户')
|
||||
throw new Error(`${String(this.options.environment || 'acceptance').toUpperCase()} 固定客户模式禁止创建、归档客户或重提资质;请复用已配置客户`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1888,12 +1924,13 @@ export class AcceptanceRun {
|
||||
}
|
||||
|
||||
async run() {
|
||||
if (this.options.environment === 'test' && !this.options.fixedCustomerId) {
|
||||
throw new Error('TEST 验收必须配置 ADMIN_TEST_FIXED_CUSTOMER_ID;禁止自动创建测试客户')
|
||||
if (['test', 'gray'].includes(this.options.environment) && !this.options.fixedCustomerId) {
|
||||
throw new Error(`${this.options.environment.toUpperCase()} 验收必须配置固定客户;禁止自动创建客户`)
|
||||
}
|
||||
if (this.options.merchantUsername && this.options.merchantPassword) {
|
||||
process.env.MERCHANT_TEST_USERNAME = this.options.merchantUsername
|
||||
process.env.MERCHANT_TEST_PASSWORD = this.options.merchantPassword
|
||||
const prefix = this.options.environment === 'gray' ? 'MERCHANT_GRAY' : 'MERCHANT_TEST'
|
||||
process.env[`${prefix}_USERNAME`] = this.options.merchantUsername
|
||||
process.env[`${prefix}_PASSWORD`] = this.options.merchantPassword
|
||||
}
|
||||
if (this.options.waitSplitCount > 1 || this.options.waitSplitPackageCount > 0
|
||||
|| this.options.orderProfileMode === 'randomized' || this.options.routeMode === 'random'
|
||||
@@ -2053,6 +2090,7 @@ export class AcceptanceRun {
|
||||
createdResources: this.createdResources,
|
||||
})
|
||||
const forbiddenValues = [this.options.password, this.options.merchantPassword, this.options.fixedSecondaryCustomer?.password,
|
||||
...Object.values(this.options.fixedActors || {}).map(actor => actor?.password),
|
||||
this.token, this.refreshToken]
|
||||
.filter((value) => typeof value === 'string' && value.length >= 8)
|
||||
const sensitivePatterns = [
|
||||
|
||||
Reference in New Issue
Block a user