This commit is contained in:
2026-08-13 11:57:33 +08:00
parent 5a48992d78
commit 16dc03a2e2
102 changed files with 3656 additions and 120 deletions
+44 -2
View File
@@ -12,7 +12,7 @@ import process from 'node:process'
const DEFAULT_GATEWAY = 'http://8.163.30.128:9000'
const DEFAULT_USERNAME = 'platform_admin'
const DEFAULT_PASSWORD = 'ChangeMe123!'
const DEFAULT_MODULES = ['customers', 'overseas-storage-fees', 'system', 'master-data', 'orders', 'fee-items', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'phase3-container-loading', 'phase3-loading', 'phase3-loading-model-bridge', 'phase3-pda-convergence', 'phase3-evidence-gate', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments', 'service-center-flow', 'phase3-customs', 'phase3-port-schedules', 'phase3-carrier-schedule-queries', 'phase3-loading-finalize', 'phase3-outbound-dispatch', 'phase3-outbound-aggregate', 'phase3-dispatch-gate', 'phase3-waiting-for-loading', 'phase3-warehouse-rbac', 'phase3-order-splitting', 'manual-state-matrix', 'finance', 'finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full']
const DEFAULT_MODULES = ['customers', 'overseas-storage-fees', 'system', 'master-data', 'suppliers', 'domestic-container', 'orders', 'fee-items', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'phase3-container-loading', 'phase3-loading', 'phase3-loading-model-bridge', 'phase3-pda-convergence', 'phase3-evidence-gate', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments', 'service-center-flow', 'phase3-customs', 'phase3-port-schedules', 'phase3-carrier-schedule-queries', 'phase3-loading-finalize', 'phase3-outbound-dispatch', 'phase3-outbound-aggregate', 'phase3-dispatch-gate', 'phase3-waiting-for-loading', 'phase3-warehouse-rbac', 'phase3-order-splitting', 'manual-state-matrix', 'finance', 'finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full']
const WEIGHT_IMAGE_PATH = 'C:/Users/BY-OY/Desktop/上传图片包/测量图片/测量图片/orig_1cf27695-b2c3-44b3-98e3-c0be8b80f79a.JPG'
const CUSTOMER_FIELDS = [
'id', 'tenantCode', 'tenantName', 'status', 'legalName', 'legalRepresentative',
@@ -406,8 +406,50 @@ export class AcceptanceRun {
this.assert(module, '客户编辑正确回显', editResponse, edited?.tenantName === editedPayload.tenantName && edited?.contactEmail === editedPayload.contactEmail && Number(edited?.paymentTermDays) === 45, `tenantId=${tenantId}`)
const persistedResponse = await this.request('GET', detailPath); const persisted = this.apiSuccess(module, '回查客户编辑结果', persistedResponse)
this.assert(module, '客户编辑已持久化', persistedResponse, persisted?.tenantName === editedPayload.tenantName && persisted?.internalRemark === editedPayload.internalRemark, `tenantId=${tenantId}`)
let customerAuditPage = null
let customerAuditResponse = null
for (let attempt = 0; attempt < 8; attempt += 1) {
customerAuditResponse = await this.request('GET', `/api/platform/v1/audit/logs?accountId=${tenantId}&eventType=PLATFORM_TENANT_UPDATE&resourceType=TENANT&resourceId=${tenantId}&page=1&size=20`)
customerAuditPage = customerAuditResponse.http === 200 && String(customerAuditResponse.body?.code) === '200' ? customerAuditResponse.body?.data : null
if (customerAuditPage?.records?.some((item) => item.eventType === 'PLATFORM_TENANT_UPDATE' && String(item.accountId) === tenantId)) break
await new Promise((resolve) => setTimeout(resolve, 500))
}
const customerAudit = this.apiSuccess(module, '按客户查询操作日志', customerAuditResponse)
const customerAuditRow = customerAudit?.records?.find((item) => item.eventType === 'PLATFORM_TENANT_UPDATE' && String(item.accountId) === tenantId)
this.assert(module, '客户操作日志包含本轮编辑事件', customerAuditResponse, Boolean(customerAuditRow), `tenantId=${tenantId}, event=${customerAuditRow?.eventType || 'missing'}`)
if (customerAuditRow?.id) {
const auditDetailResponse = await this.request('GET', `/api/platform/v1/audit/logs/${customerAuditRow.id}`)
const auditDetail = this.apiSuccess(module, '查询客户操作日志详情', auditDetailResponse)
const snapshotsAreJson = ['beforeData', 'afterData'].every((field) => {
if (typeof auditDetail?.[field] !== 'string') return false
try { JSON.parse(auditDetail[field]); return true } catch { return false }
})
const snapshotText = `${auditDetail?.beforeData || ''}${auditDetail?.afterData || ''}`
const snapshotsAreRedacted = !/(password|authorization|accessToken|refreshToken)\s*[\"']?\s*:/i.test(snapshotText)
this.assert(module, '操作日志详情返回脱敏 JSON 快照', auditDetailResponse,
snapshotsAreJson && snapshotsAreRedacted && String(auditDetail.accountId) === tenantId && String(auditDetail.resourceId) === tenantId,
`accountId=${auditDetail?.accountId ?? 'missing'}, resourceId=${auditDetail?.resourceId ?? 'missing'}, snapshotsAreJson=${snapshotsAreJson}, snapshotsAreRedacted=${snapshotsAreRedacted}`)
}
const qualificationPackage = new FormData()
const qualificationImage = Uint8Array.from(Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=', 'base64'))
qualificationPackage.append('businessLicense', new Blob([qualificationImage], { type: 'image/png' }), `business-license-${suffix}.png`)
qualificationPackage.append('legalPersonId', new Blob([qualificationImage], { type: 'image/png' }), `legal-person-id-${suffix}.png`)
qualificationPackage.append('unifiedSocialCreditCode', `91440300${suffix.replace(/[^0-9A-Z]/gi, '').toUpperCase().padStart(10, '0').slice(-10)}`)
const packageResponse = await this.requestWithBody('POST', `/api/platform/v1/files/qualifications/${tenantId}/required-package`, qualificationPackage)
this.apiSuccess(module, '上传审核所需客户资质包', packageResponse, `tenantId=${tenantId}`)
const qualificationResponse = await this.request('GET', `${detailPath}/qualifications`); const qualifications = this.apiSuccess(module, '业务概况查询资质', qualificationResponse)
this.assert(module, '资质概况结构正确', qualificationResponse, Array.isArray(qualifications), `count=${Array.isArray(qualifications) ? qualifications.length : 'n/a'}`)
const requiredQualifications = Array.isArray(qualifications)
? qualifications.filter((item) => ['BUSINESS_LICENSE', 'LEGAL_PERSON_ID'].includes(item.qualificationType)) : []
this.assert(module, '审核资质列表包含营业执照和法人身份证', qualificationResponse,
requiredQualifications.length === 2 && requiredQualifications.every((item) => typeof item.documentUrl === 'string' && item.documentUrl.length > 0),
`types=${requiredQualifications.map((item) => item.qualificationType).join(',') || 'missing'}`)
for (const qualification of requiredQualifications) {
const fileName = decodeURIComponent(new URL(qualification.documentUrl, 'http://acceptance.local').pathname.split('/').pop() || '')
const downloadResponse = await this.requestBinary('GET', `/api/platform/v1/files/qualifications/${tenantId}/${encodeURIComponent(fileName)}`)
this.assert(module, `审核人员可下载${qualification.qualificationType}`, downloadResponse,
downloadResponse.http === 200 && downloadResponse.bytes.length > 0 && /^image\/|application\/pdf/.test(downloadResponse.contentType),
`file=${fileName}, bytes=${downloadResponse.bytes.length}, contentType=${downloadResponse.contentType || 'missing'}`)
}
const memberResponse = await this.request('GET', `${detailPath}/members`); const members = this.apiSuccess(module, '业务概况查询成员', memberResponse)
const owner = members?.find?.((item) => item.memberType === 'OWNER')
this.assert(module, '联系电话账号是企业管理员', memberResponse,