const MODULE = 'merchant-qualification-fixture' function id(value) { return value == null ? '' : String(value) } async function waitForDimensionInitialization(context, tenantId) { let response = null let status = 'PENDING' let attempt = 0 for (; attempt < 30; attempt += 1) { response = await context.request('GET', `/api/platform/v1/iam/tenants/${encodeURIComponent(tenantId)}/dimension-initialization`) status = response.http === 200 && String(response.body?.code) === '200' ? response.body?.data?.status : 'REQUEST_FAILED' if (status === 'READY' || status === 'FAILED') break await new Promise((resolve) => setTimeout(resolve, 500)) } context.assert(MODULE, '客户默认尺寸后台初始化已就绪', response, status === 'READY', `tenantId=${tenantId}, status=${status}, attempts=${attempt + 1}`, { deploymentBlocker: true }) if (status !== 'READY') throw new Error(`客户默认尺寸初始化未就绪: ${status}`) } export function normalizeFixedCustomerId(value) { const text = String(value || '').trim().replace(/^TEN/, '') if (!/^[1-9]\d{0,19}$/.test(text)) throw new Error('固定客户编号必须为 TEN 编号或数字 ID') return text } export function requireFixedSecondaryCustomer(options) { const secondary = options.fixedSecondaryCustomer if (!secondary?.customerId || !secondary.username || !secondary.password) throw new Error('固定客户模式未配置第二个客户,跨客户场景阻断;禁止自动建客或用同一客户替代隔离验证') if (normalizeFixedCustomerId(secondary.customerId) === normalizeFixedCustomerId(options.fixedCustomerId)) throw new Error('第二个固定客户必须不同于主客户,禁止替代隔离验证') return secondary } async function reuseFixedCustomer(context, publishEnvironment, requireDistinctCustomer) { const secondary = requireDistinctCustomer ? requireFixedSecondaryCustomer(context.options) : null const tenantId = normalizeFixedCustomerId(secondary?.customerId || context.options.fixedCustomerId) const prefix = context.options.environment === 'gray' ? 'MERCHANT_GRAY' : 'MERCHANT_TEST' const username = secondary ? secondary.username : context.options.merchantUsername || process.env[`${prefix}_USERNAME`] const password = secondary ? secondary.password : context.options.merchantPassword || process.env[`${prefix}_PASSWORD`] if (!username || !password) throw new Error(`请配置固定客户的 ${prefix}_USERNAME / ${prefix}_PASSWORD;不会新建客户或重置密码`) const loginResponse = await context.request('POST', '/api/auth/login', {loginId:username,password}, '') const login = context.apiSuccess(MODULE, '登录已配置的固定客户账号', loginResponse) if (!login?.accessToken) throw new Error('固定客户账号登录失败') let profile try { const response = await context.request('GET', '/api/merchant/v1/iam/tenant/profile', undefined, login.accessToken) profile = context.apiSuccess(MODULE, '核对固定客户账号实际归属和状态', response) const valid = id(profile?.id) === tenantId && profile?.status === 'ACTIVE' context.assert(MODULE, '固定客户账号属于指定已审核客户', response, valid, `expected=${tenantId}, actual=${id(profile?.id)}, status=${profile?.status}`, {deploymentBlocker:true}) if (!valid) throw new Error('固定客户账号归属或状态不匹配,停止造数;不会新建客户') await waitForDimensionInitialization(context, tenantId) } finally { await context.request('POST', '/api/auth/logout', {refreshToken:login.refreshToken}, login.accessToken) } if (publishEnvironment) { process.env[`${prefix}_USERNAME`] = username process.env[`${prefix}_PASSWORD`] = password } const fixture = {tenantId,tenantName:profile.tenantName,username,marker:context.marker,reused:true} context.context.merchantQualificationFixture = {...fixture,password} context.context.customerId = tenantId context.createdResources.merchantQualificationFixture = fixture } export async function run(context, { publishEnvironment = true, requireDistinctCustomer = !publishEnvironment } = {}) { if (!context.options?.fixedCustomerId) throw new Error('必须配置固定客户,禁止自动建客、补交资质或自动审核') return reuseFixedCustomer(context, publishEnvironment, requireDistinctCustomer) }