From 73bfc5a65c2c3ab9f045c5442843e2b6b19fbcfe Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sat, 8 Aug 2026 10:17:43 +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 --- config/nacos/cn/firstleg-service.yml | 4 +- config/nacos/local/firstleg-service.yml | 4 +- .../config/MeasurementOpenApiProperties.java | 2 + .../OpenApiInboundMeasurementController.java | 12 +- .../MeasurementRequestAuthenticator.java | 44 +- ...in-test-env-acceptance-20260808021317.json | 288 ++++ ...in-test-env-acceptance-20260808021429.json | 49 + ...in-test-env-acceptance-20260808021540.json | 378 +++++ ...in-test-env-acceptance-20260808021651.json | 780 ++++++++++ ...ient-test-env-20260808021636-b5090589.json | 1378 +++++++++++++++++ scripts/client-test-env-acceptance.mjs | 197 +++ 11 files changed, 3109 insertions(+), 27 deletions(-) create mode 100644 reports/admin-test-env-acceptance-20260808021317.json create mode 100644 reports/admin-test-env-acceptance-20260808021429.json create mode 100644 reports/admin-test-env-acceptance-20260808021540.json create mode 100644 reports/admin-test-env-acceptance-20260808021651.json create mode 100644 reports/client-test-env-20260808021636-b5090589.json diff --git a/config/nacos/cn/firstleg-service.yml b/config/nacos/cn/firstleg-service.yml index 0771aee5c..dd326ed80 100644 --- a/config/nacos/cn/firstleg-service.yml +++ b/config/nacos/cn/firstleg-service.yml @@ -15,10 +15,12 @@ firstleg: warehouse-resource-tenant-id: 0 measurement-openapi: enabled: ${MEASUREMENT_OPENAPI_ENABLED:false} + # 墨家首轮联调暂不验 HMAC;通讯确认后将环境变量改为 true 即可恢复。 + signature-required: ${MEASUREMENT_OPENAPI_SIGNATURE_REQUIRED:false} max-clock-skew: ${MEASUREMENT_OPENAPI_MAX_CLOCK_SKEW:5m} nonce-ttl: ${MEASUREMENT_OPENAPI_NONCE_TTL:10m} apps: - # 墨家设备群共用一组固定 AppKey/Secret;每台机器用 data.deviceCode 区分。 + # 墨家设备群共用固定 AppKey;签名恢复后再启用 Secret。每台机器用 data.deviceCode 区分。 mojia: enabled: ${MOJIA_MEASUREMENT_ENABLED:false} secret: ${MOJIA_MEASUREMENT_SECRET:} diff --git a/config/nacos/local/firstleg-service.yml b/config/nacos/local/firstleg-service.yml index eb233e21c..1676cb3a7 100644 --- a/config/nacos/local/firstleg-service.yml +++ b/config/nacos/local/firstleg-service.yml @@ -14,10 +14,12 @@ firstleg: warehouse-resource-tenant-id: 0 measurement-openapi: enabled: ${MEASUREMENT_OPENAPI_ENABLED:false} + # 墨家首轮联调暂不验 HMAC;通讯确认后将环境变量改为 true 即可恢复。 + signature-required: ${MEASUREMENT_OPENAPI_SIGNATURE_REQUIRED:false} max-clock-skew: ${MEASUREMENT_OPENAPI_MAX_CLOCK_SKEW:5m} nonce-ttl: ${MEASUREMENT_OPENAPI_NONCE_TTL:10m} apps: - # 墨家设备群共用一组固定 AppKey/Secret;每台机器用 data.deviceCode 区分。 + # 墨家设备群共用固定 AppKey;签名恢复后再启用 Secret。每台机器用 data.deviceCode 区分。 mojia: enabled: ${MOJIA_MEASUREMENT_ENABLED:false} secret: ${MOJIA_MEASUREMENT_SECRET:} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/config/MeasurementOpenApiProperties.java b/firstleg/src/main/java/com/baiyue/firstleg/config/MeasurementOpenApiProperties.java index bb0776a82..c5ecb8d05 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/config/MeasurementOpenApiProperties.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/config/MeasurementOpenApiProperties.java @@ -18,6 +18,8 @@ import java.util.Map; @ConfigurationProperties(prefix = "firstleg.measurement-openapi") public class MeasurementOpenApiProperties { private boolean enabled = false; + /** 联调期间可关闭 HMAC、时间戳和 Nonce;固定 AppKey 与业务幂等仍强制校验。 */ + private boolean signatureRequired = true; private Duration maxClockSkew = Duration.ofMinutes(5); private Duration nonceTtl = Duration.ofMinutes(10); private Map apps = new LinkedHashMap<>(); diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/OpenApiInboundMeasurementController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/OpenApiInboundMeasurementController.java index b8a9d687c..a89af6f6c 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/controller/OpenApiInboundMeasurementController.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/OpenApiInboundMeasurementController.java @@ -47,9 +47,9 @@ public class OpenApiInboundMeasurementController { @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ApiResponse receive( @RequestHeader("X-App-Key") String appKey, - @RequestHeader("X-Timestamp") String timestamp, - @RequestHeader("X-Nonce") String nonce, - @RequestHeader("X-Signature") String signature, + @RequestHeader(value = "X-Timestamp", required = false) String timestamp, + @RequestHeader(value = "X-Nonce", required = false) String nonce, + @RequestHeader(value = "X-Signature", required = false) String signature, @RequestHeader("Idempotency-Key") String idempotencyKey, @RequestPart("data") String rawData, @RequestPart("image") MultipartFile image) { @@ -67,9 +67,9 @@ public class OpenApiInboundMeasurementController { @Parameter(description = "第三方业务事件号", required = true) @PathVariable @Pattern(regexp = "[A-Za-z0-9._:-]{1,128}") String eventNo, @RequestHeader("X-App-Key") String appKey, - @RequestHeader("X-Timestamp") String timestamp, - @RequestHeader("X-Nonce") String nonce, - @RequestHeader("X-Signature") String signature) { + @RequestHeader(value = "X-Timestamp", required = false) String timestamp, + @RequestHeader(value = "X-Nonce", required = false) String nonce, + @RequestHeader(value = "X-Signature", required = false) String signature) { return ApiResponse.success(service.query(eventNo, new MeasurementRequestHeaders(appKey, timestamp, nonce, signature, null))); } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/security/MeasurementRequestAuthenticator.java b/firstleg/src/main/java/com/baiyue/firstleg/security/MeasurementRequestAuthenticator.java index 088810acd..f7f9fabf3 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/security/MeasurementRequestAuthenticator.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/security/MeasurementRequestAuthenticator.java @@ -36,35 +36,43 @@ public class MeasurementRequestAuthenticator { throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "第三方测量开放接口未启用"); } requireHeader(headers.appKey(), "X-App-Key"); - requireHeader(headers.timestamp(), "X-Timestamp"); - requireHeader(headers.nonce(), "X-Nonce"); - requireHeader(headers.signature(), "X-Signature"); + validateTokenLength(headers.appKey(), 128, "X-App-Key"); if (requireIdempotencyKey) { requireHeader(headers.idempotencyKey(), "Idempotency-Key"); - } - validateTokenLength(headers.nonce(), 128, "X-Nonce"); - if (requireIdempotencyKey) { validateTokenLength(headers.idempotencyKey(), 128, "Idempotency-Key"); } MeasurementOpenApiProperties.AppCredential credential = properties.getApps().get(headers.appKey()); - if (credential == null || !credential.isEnabled() || !StringUtils.hasText(credential.getSecret()) + if (credential == null || !credential.isEnabled() + || properties.isSignatureRequired() && !StringUtils.hasText(credential.getSecret()) || credential.getTenantId() == null || credential.getTenantId() <= 0 || credential.getSubjectId() == null || credential.getSubjectId() <= 0) { throw unauthorized("应用凭据无效"); } - long timestamp = parseTimestamp(headers.timestamp()); - Duration drift = Duration.between(Instant.ofEpochMilli(timestamp), clock.instant()).abs(); - if (drift.compareTo(properties.getMaxClockSkew()) > 0) { - throw unauthorized("请求时间戳已过期"); - } - String canonical = canonicalString(method, path, headers.timestamp(), headers.nonce(), - requireIdempotencyKey ? headers.idempotencyKey() : "", sha256Hex(rawBody)); - String expected = hmacHex(credential.getSecret(), canonical); - if (!constantTimeEquals(expected, headers.signature())) { - throw unauthorized("请求签名无效"); + if (properties.isSignatureRequired()) { + requireHeader(headers.timestamp(), "X-Timestamp"); + requireHeader(headers.nonce(), "X-Nonce"); + requireHeader(headers.signature(), "X-Signature"); + validateTokenLength(headers.nonce(), 128, "X-Nonce"); + long timestamp = parseTimestamp(headers.timestamp()); + Duration drift = Duration.between(Instant.ofEpochMilli(timestamp), clock.instant()).abs(); + if (drift.compareTo(properties.getMaxClockSkew()) > 0) { + throw unauthorized("请求时间戳已过期"); + } + String canonical = canonicalString(method, path, headers.timestamp(), headers.nonce(), + requireIdempotencyKey ? headers.idempotencyKey() : "", sha256Hex(rawBody)); + String expected = hmacHex(credential.getSecret(), canonical); + if (!constantTimeEquals(expected, headers.signature())) { + throw unauthorized("请求签名无效"); + } + claimNonce(headers); } + return new AuthenticatedMeasurementApp(headers.appKey(), credential.getTenantId(), + credential.getSubjectId(), Map.copyOf(credential.getWarehouseBindings())); + } + + private void claimNonce(MeasurementRequestHeaders headers) { try { if (!nonceReplayGuard.claim(headers.appKey(), headers.nonce(), properties.getNonceTtl())) { throw new BizException(ResultCode.CONFLICT, "Nonce 已使用,拒绝重放请求"); @@ -74,8 +82,6 @@ public class MeasurementRequestAuthenticator { } catch (RuntimeException exception) { throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "Nonce 防重放服务暂不可用"); } - return new AuthenticatedMeasurementApp(headers.appKey(), credential.getTenantId(), - credential.getSubjectId(), Map.copyOf(credential.getWarehouseBindings())); } public static String canonicalString(String method, String path, String timestamp, String nonce, diff --git a/reports/admin-test-env-acceptance-20260808021317.json b/reports/admin-test-env-acceptance-20260808021317.json new file mode 100644 index 000000000..35d7b60c0 --- /dev/null +++ b/reports/admin-test-env-acceptance-20260808021317.json @@ -0,0 +1,288 @@ +{ + "reportName": "后台管理测试环境 HTTP API 验收", + "marker": "ADMIN-API-20260808021317129", + "gateway": "http://8.163.30.128:9000", + "generatedAt": "2026-08-08T02:13:18.249Z", + "startedAt": "2026-08-08T02:13:17.129Z", + "success": false, + "fatal": null, + "summary": { + "total": 12, + "passed": 4, + "failed": 8 + }, + "modules": [ + { + "name": "orders", + "startedAt": "2026-08-08T02:13:17.613Z", + "finishedAt": "2026-08-08T02:13:18.186Z", + "success": false, + "passed": 3, + "failed": 7, + "fatal": "缺少创建测试订单所需的收货仓或目的仓" + }, + { + "name": "inbound-weight-ocr", + "startedAt": "2026-08-08T02:13:18.188Z", + "finishedAt": "2026-08-08T02:13:18.249Z", + "success": false, + "passed": 0, + "failed": 1, + "fatal": null + } + ], + "scenarios": [ + { + "name": "weight-image-recognition-and-confirmation", + "module": "inbound-weight-ocr", + "success": false, + "deploymentBlocked": false, + "passed": 0, + "failed": 1 + } + ], + "deploymentBlockers": [ + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Admin 查询客户固定箱型", + "evidence": "tenantId=10001", + "code": null, + "requestId": "000491d0-2057", + "traceId": null + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "固定四箱型顺序、内径及每边加2cm正确", + "evidence": "types=missing", + "code": null, + "requestId": "000491d0-2057", + "traceId": null + } + ], + "createdResources": {}, + "excluded": [ + { + "scope": "dashboard", + "reason": "首页为静态演示数据,不调用后端 API" + }, + { + "scope": "hidden-prototypes", + "reason": "隐藏原型页面尚无已确认后端契约" + } + ], + "securityNote": "报告不保存登录密码、Access Token、Refresh Token、图片二进制或 OCR 原文全文。", + "steps": [ + { + "module": "authentication", + "scenario": null, + "name": "平台管理员登录", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/auth/login" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "0ab24bf5-f65c-4c9e-98ac-686032ee17c4, 5de04702-c17c-4a47-b824-ac05b6fa8801", + "traceId": "142f895f3b8546a6966df4ae56fcb6da", + "evidence": "", + "checkedAt": "2026-08-08T02:13:17.612Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 测试账号登录", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/auth/login" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "0c5a6873-bb38-4be1-96f4-97ee438adbc5, 77a28b17-ead2-4562-9620-9c5ab9abc82b", + "traceId": "7fa548e89954462eb6c67db4bed51844", + "evidence": "username=merchant_admin", + "checkedAt": "2026-08-08T02:13:18.048Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询当前企业资料", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/iam/tenant/profile" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "c578974b-d386-4c2d-88cd-0de93fcb1d7e, 5669ad2d-1772-4162-ae07-989f738a9b3d", + "traceId": "40123a07c80d4bdfb70a4b460d9d92a9", + "evidence": "", + "checkedAt": "2026-08-08T02:13:18.091Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 测试企业已审核且客户 ID 有效", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/iam/tenant/profile" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "c578974b-d386-4c2d-88cd-0de93fcb1d7e, 5669ad2d-1772-4162-ae07-989f738a9b3d", + "traceId": "40123a07c80d4bdfb70a4b460d9d92a9", + "evidence": "tenantId=10001, status=ACTIVE", + "checkedAt": "2026-08-08T02:13:18.091Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Admin 查询客户固定箱型", + "passed": false, + "deploymentBlocker": true, + "request": { + "method": "GET", + "path": "/api/platform/v1/first-leg/customer-dimension-config?tenantId=10001" + }, + "http": 503, + "code": null, + "message": null, + "requestId": "000491d0-2057", + "traceId": null, + "evidence": "tenantId=10001", + "checkedAt": "2026-08-08T02:13:18.124Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "固定四箱型顺序、内径及每边加2cm正确", + "passed": false, + "deploymentBlocker": true, + "request": { + "method": "GET", + "path": "/api/platform/v1/first-leg/customer-dimension-config?tenantId=10001" + }, + "http": 503, + "code": null, + "message": null, + "requestId": "000491d0-2057", + "traceId": null, + "evidence": "types=missing", + "checkedAt": "2026-08-08T02:13:18.124Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询国内收货仓", + "passed": false, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=DOMESTIC" + }, + "http": 503, + "code": null, + "message": null, + "requestId": "37626d2f-2058", + "traceId": null, + "evidence": "", + "checkedAt": "2026-08-08T02:13:18.159Z" + }, + { + "module": "orders", + "scenario": null, + "name": "存在可用国内收货仓", + "passed": false, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=DOMESTIC" + }, + "http": 503, + "code": null, + "message": null, + "requestId": "37626d2f-2058", + "traceId": null, + "evidence": "count=0", + "checkedAt": "2026-08-08T02:13:18.159Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询 FBA 目的仓", + "passed": false, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=FBA" + }, + "http": 503, + "code": null, + "message": null, + "requestId": "000491d0-2059", + "traceId": null, + "evidence": "", + "checkedAt": "2026-08-08T02:13:18.186Z" + }, + { + "module": "orders", + "scenario": null, + "name": "存在可用 FBA 目的仓", + "passed": false, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=FBA" + }, + "http": 503, + "code": null, + "message": null, + "requestId": "000491d0-2059", + "traceId": null, + "evidence": "count=0", + "checkedAt": "2026-08-08T02:13:18.186Z" + }, + { + "module": "orders", + "scenario": null, + "name": "模块执行", + "passed": false, + "deploymentBlocker": false, + "request": null, + "http": null, + "code": null, + "message": null, + "requestId": null, + "traceId": null, + "evidence": "缺少创建测试订单所需的收货仓或目的仓", + "checkedAt": "2026-08-08T02:13:18.186Z" + }, + { + "module": "inbound-weight-ocr", + "scenario": "weight-image-recognition-and-confirmation", + "name": "仅选择带本轮测试标记的可编辑入库单", + "passed": false, + "deploymentBlocker": false, + "request": null, + "http": null, + "code": null, + "message": null, + "requestId": null, + "traceId": null, + "evidence": "marker=ADMIN-API-20260808021317129, 未找到本轮创建且状态为 WAIT_MEASURE/MEASURING/MEASURE_REJECTED 的入库单", + "checkedAt": "2026-08-08T02:13:18.249Z" + } + ] +} diff --git a/reports/admin-test-env-acceptance-20260808021429.json b/reports/admin-test-env-acceptance-20260808021429.json new file mode 100644 index 000000000..5a82b67de --- /dev/null +++ b/reports/admin-test-env-acceptance-20260808021429.json @@ -0,0 +1,49 @@ +{ + "reportName": "后台管理测试环境 HTTP API 验收", + "marker": "ADMIN-API-20260808021429703", + "gateway": "http://8.163.30.128:9000", + "generatedAt": "2026-08-08T02:14:30.190Z", + "startedAt": "2026-08-08T02:14:29.703Z", + "success": false, + "fatal": "平台管理员登录失败,未执行业务模块", + "summary": { + "total": 1, + "passed": 0, + "failed": 1 + }, + "modules": [], + "scenarios": [], + "deploymentBlockers": [], + "createdResources": {}, + "excluded": [ + { + "scope": "dashboard", + "reason": "首页为静态演示数据,不调用后端 API" + }, + { + "scope": "hidden-prototypes", + "reason": "隐藏原型页面尚无已确认后端契约" + } + ], + "securityNote": "报告不保存登录密码、Access Token、Refresh Token、图片二进制或 OCR 原文全文。", + "steps": [ + { + "module": "authentication", + "scenario": null, + "name": "平台管理员登录", + "passed": false, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/auth/login" + }, + "http": 200, + "code": "S0503", + "message": "IAM 权限服务暂时不可用", + "requestId": "ff84ec01-d420-4118-9590-2c358d8654af", + "traceId": "f1f5b5009a1740db96bbf1efff8d0b09", + "evidence": "", + "checkedAt": "2026-08-08T02:14:30.190Z" + } + ] +} diff --git a/reports/admin-test-env-acceptance-20260808021540.json b/reports/admin-test-env-acceptance-20260808021540.json new file mode 100644 index 000000000..3cc23e33f --- /dev/null +++ b/reports/admin-test-env-acceptance-20260808021540.json @@ -0,0 +1,378 @@ +{ + "reportName": "后台管理测试环境 HTTP API 验收", + "marker": "ADMIN-API-20260808021540611", + "gateway": "http://8.163.30.128:9000", + "generatedAt": "2026-08-08T02:15:45.804Z", + "startedAt": "2026-08-08T02:15:40.612Z", + "success": false, + "fatal": null, + "summary": { + "total": 17, + "passed": 13, + "failed": 4 + }, + "modules": [ + { + "name": "orders", + "startedAt": "2026-08-08T02:15:41.594Z", + "finishedAt": "2026-08-08T02:15:45.414Z", + "success": false, + "passed": 12, + "failed": 3, + "fatal": "产品图片新契约未部署,无法创建带图片的隔离订单" + }, + { + "name": "inbound-weight-ocr", + "startedAt": "2026-08-08T02:15:45.416Z", + "finishedAt": "2026-08-08T02:15:45.804Z", + "success": false, + "passed": 0, + "failed": 1, + "fatal": null + } + ], + "scenarios": [ + { + "name": "weight-image-recognition-and-confirmation", + "module": "inbound-weight-ocr", + "success": false, + "deploymentBlocked": false, + "passed": 0, + "failed": 1 + } + ], + "deploymentBlockers": [ + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Client 上传产品图片", + "evidence": "contentType=image/png", + "code": null, + "requestId": "700de02f-10", + "traceId": null + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "产品图片上传返回字符串文件 ID", + "evidence": "fileIdType=undefined, contentType=missing", + "code": null, + "requestId": "700de02f-10", + "traceId": null + } + ], + "createdResources": {}, + "excluded": [ + { + "scope": "dashboard", + "reason": "首页为静态演示数据,不调用后端 API" + }, + { + "scope": "hidden-prototypes", + "reason": "隐藏原型页面尚无已确认后端契约" + } + ], + "securityNote": "报告不保存登录密码、Access Token、Refresh Token、图片二进制或 OCR 原文全文。", + "steps": [ + { + "module": "authentication", + "scenario": null, + "name": "平台管理员登录", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/auth/login" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "6d4b2e6d-8811-4f56-a889-e595ff7cb503, 7f19250e-90f1-49df-a269-0f758c4be79e", + "traceId": "d89b6dad97df4c2c842ba58703984168", + "evidence": "", + "checkedAt": "2026-08-08T02:15:41.592Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 测试账号登录", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/auth/login" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "8032a1de-240f-431c-8e79-de1b31484353, 272c9865-6588-4524-929d-d3d049d6d95a", + "traceId": "adc915919bc3446fa4f9472da2d43352", + "evidence": "username=merchant_admin", + "checkedAt": "2026-08-08T02:15:42.008Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询当前企业资料", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/iam/tenant/profile" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "eafa4f3a-9cc3-4ee6-a91c-2e5fdc0e3ebe, ee54cfa1-250a-4a96-b540-e6a02962480f", + "traceId": "36a69786b59548b985c67f49d970a51c", + "evidence": "", + "checkedAt": "2026-08-08T02:15:42.655Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 测试企业已审核且客户 ID 有效", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/iam/tenant/profile" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "eafa4f3a-9cc3-4ee6-a91c-2e5fdc0e3ebe, ee54cfa1-250a-4a96-b540-e6a02962480f", + "traceId": "36a69786b59548b985c67f49d970a51c", + "evidence": "tenantId=10001, status=ACTIVE", + "checkedAt": "2026-08-08T02:15:42.655Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Admin 查询客户固定箱型", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/first-leg/customer-dimension-config?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "b701963f-025e-4612-af31-f7997c04094a, dc095ff3-98d0-4c26-b6b3-b51b5fcac89f", + "traceId": "48bb3f6f9ae941f09122277efdcb92ce", + "evidence": "tenantId=10001", + "checkedAt": "2026-08-08T02:15:43.480Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "固定四箱型顺序、内径及每边加2cm正确", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/first-leg/customer-dimension-config?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "b701963f-025e-4612-af31-f7997c04094a, dc095ff3-98d0-4c26-b6b3-b51b5fcac89f", + "traceId": "48bb3f6f9ae941f09122277efdcb92ce", + "evidence": "types=SMALL,MEDIUM,LARGE,EXTRA_LARGE", + "checkedAt": "2026-08-08T02:15:43.480Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询国内收货仓", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=DOMESTIC" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "43c7b3a0-810e-4566-972b-c0ab53203622, 6c4c9fb8-8370-4744-bdf0-1a2ea0dc2fc9", + "traceId": "8797b3f5ce7c4a18928ba0353691eb41", + "evidence": "", + "checkedAt": "2026-08-08T02:15:44.944Z" + }, + { + "module": "orders", + "scenario": null, + "name": "存在可用国内收货仓", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=DOMESTIC" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "43c7b3a0-810e-4566-972b-c0ab53203622, 6c4c9fb8-8370-4744-bdf0-1a2ea0dc2fc9", + "traceId": "8797b3f5ce7c4a18928ba0353691eb41", + "evidence": "count=2", + "checkedAt": "2026-08-08T02:15:44.944Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询 FBA 目的仓", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=FBA" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "26ba298e-5f94-4132-bf84-578083f21fad, bd16e2d8-0559-4b4b-9793-0daec1be7a90", + "traceId": "f0fa3d314cba4f96aaf8ebf4900ef648", + "evidence": "", + "checkedAt": "2026-08-08T02:15:45.113Z" + }, + { + "module": "orders", + "scenario": null, + "name": "存在可用 FBA 目的仓", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=FBA" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "26ba298e-5f94-4132-bf84-578083f21fad, bd16e2d8-0559-4b4b-9793-0daec1be7a90", + "traceId": "f0fa3d314cba4f96aaf8ebf4900ef648", + "evidence": "count=200", + "checkedAt": "2026-08-08T02:15:45.113Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询可用计费渠道", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=193000000000200001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "b8484f51-9267-4002-9a7b-40d2d4466d38, b3845f30-7193-4c73-b90a-8cb198149137", + "traceId": "7a6a7d17aae1429e8f48162205b7f2fa", + "evidence": "", + "checkedAt": "2026-08-08T02:15:45.192Z" + }, + { + "module": "orders", + "scenario": null, + "name": "存在带计价税制的可用渠道", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=193000000000200001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "b8484f51-9267-4002-9a7b-40d2d4466d38, b3845f30-7193-4c73-b90a-8cb198149137", + "traceId": "7a6a7d17aae1429e8f48162205b7f2fa", + "evidence": "count=15", + "checkedAt": "2026-08-08T02:15:45.193Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询发货客户候选", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/partners/candidates?type=SHIPPER" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "c0310692-e6a1-4dde-9828-c7f084c75cbe, 513ea693-c31e-47e7-a746-b8a9499ff48a", + "traceId": "5955689a145d42a8ad2b293b67b8b3fb", + "evidence": "", + "checkedAt": "2026-08-08T02:15:45.289Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Client 上传产品图片", + "passed": false, + "deploymentBlocker": true, + "request": { + "method": "POST", + "path": "/api/merchant/v1/files/product-images" + }, + "http": 503, + "code": null, + "message": null, + "requestId": "700de02f-10", + "traceId": null, + "evidence": "contentType=image/png", + "checkedAt": "2026-08-08T02:15:45.413Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "产品图片上传返回字符串文件 ID", + "passed": false, + "deploymentBlocker": true, + "request": { + "method": "POST", + "path": "/api/merchant/v1/files/product-images" + }, + "http": 503, + "code": null, + "message": null, + "requestId": "700de02f-10", + "traceId": null, + "evidence": "fileIdType=undefined, contentType=missing", + "checkedAt": "2026-08-08T02:15:45.414Z" + }, + { + "module": "orders", + "scenario": null, + "name": "模块执行", + "passed": false, + "deploymentBlocker": false, + "request": null, + "http": null, + "code": null, + "message": null, + "requestId": null, + "traceId": null, + "evidence": "产品图片新契约未部署,无法创建带图片的隔离订单", + "checkedAt": "2026-08-08T02:15:45.414Z" + }, + { + "module": "inbound-weight-ocr", + "scenario": "weight-image-recognition-and-confirmation", + "name": "仅选择带本轮测试标记的可编辑入库单", + "passed": false, + "deploymentBlocker": false, + "request": null, + "http": null, + "code": null, + "message": null, + "requestId": null, + "traceId": null, + "evidence": "marker=ADMIN-API-20260808021540611, 未找到本轮创建且状态为 WAIT_MEASURE/MEASURING/MEASURE_REJECTED 的入库单", + "checkedAt": "2026-08-08T02:15:45.804Z" + } + ] +} diff --git a/reports/admin-test-env-acceptance-20260808021651.json b/reports/admin-test-env-acceptance-20260808021651.json new file mode 100644 index 000000000..8de516e6b --- /dev/null +++ b/reports/admin-test-env-acceptance-20260808021651.json @@ -0,0 +1,780 @@ +{ + "reportName": "后台管理测试环境 HTTP API 验收", + "marker": "ADMIN-API-20260808021651990", + "gateway": "http://8.163.30.128:9000", + "generatedAt": "2026-08-08T02:17:14.712Z", + "startedAt": "2026-08-08T02:16:51.990Z", + "success": false, + "fatal": null, + "summary": { + "total": 39, + "passed": 36, + "failed": 3 + }, + "modules": [ + { + "name": "orders", + "startedAt": "2026-08-08T02:16:52.371Z", + "finishedAt": "2026-08-08T02:17:14.620Z", + "success": false, + "passed": 35, + "failed": 2, + "fatal": "订单审核后的入库单契约未部署,无法向 OCR/墨家模块交接" + }, + { + "name": "inbound-weight-ocr", + "startedAt": "2026-08-08T02:17:14.621Z", + "finishedAt": "2026-08-08T02:17:14.712Z", + "success": false, + "passed": 0, + "failed": 1, + "fatal": null + } + ], + "scenarios": [ + { + "name": "weight-image-recognition-and-confirmation", + "module": "inbound-weight-ocr", + "success": false, + "deploymentBlocked": false, + "passed": 0, + "failed": 1 + } + ], + "deploymentBlockers": [ + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "审核后生成隔离入库单", + "evidence": "20秒内未返回 inboundId", + "code": "S0503", + "requestId": "40ba574d-240f-47d9-a6cb-2aecc922b0ff", + "traceId": "75a3df822dce4d60a7b02e8906c487e2" + } + ], + "createdResources": { + "orders": { + "marker": "ADMIN-API-20260808021651990", + "tenantId": "10001", + "orderId": "2085913101392740353", + "productImageFileId": "2085913100780371970", + "boxTypes": [ + "SMALL", + "MEDIUM", + "LARGE", + "EXTRA_LARGE" + ], + "inboundId": null, + "orderNo": "FL2085913101392740353" + } + }, + "excluded": [ + { + "scope": "dashboard", + "reason": "首页为静态演示数据,不调用后端 API" + }, + { + "scope": "hidden-prototypes", + "reason": "隐藏原型页面尚无已确认后端契约" + } + ], + "securityNote": "报告不保存登录密码、Access Token、Refresh Token、图片二进制或 OCR 原文全文。", + "steps": [ + { + "module": "authentication", + "scenario": null, + "name": "平台管理员登录", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/auth/login" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "e272618f-4e86-4b6a-ba0c-487af0c7ac6d, 49ea627a-9612-431a-8884-72fc25ffe15c", + "traceId": "36c286458df84c698ec00318c68f23dd", + "evidence": "", + "checkedAt": "2026-08-08T02:16:52.369Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 测试账号登录", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/auth/login" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "12dbaf22-d81e-4a3a-9fc3-49958a9bd3ae, f224dadb-eefc-4d98-8b8e-79ef0c0414d0", + "traceId": "0f9813a6b2f24803a71c74a665acd36e", + "evidence": "username=merchant_admin", + "checkedAt": "2026-08-08T02:16:52.719Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询当前企业资料", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/iam/tenant/profile" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "3368029c-78c4-4a7e-bf64-dfa159720729, b3bf6a65-de45-41f1-b211-d02f46ade1be", + "traceId": "c45e982e250c467a9483081e349c9487", + "evidence": "", + "checkedAt": "2026-08-08T02:16:52.745Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 测试企业已审核且客户 ID 有效", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/iam/tenant/profile" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "3368029c-78c4-4a7e-bf64-dfa159720729, b3bf6a65-de45-41f1-b211-d02f46ade1be", + "traceId": "c45e982e250c467a9483081e349c9487", + "evidence": "tenantId=10001, status=ACTIVE", + "checkedAt": "2026-08-08T02:16:52.745Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Admin 查询客户固定箱型", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/first-leg/customer-dimension-config?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "a54b86f9-cd41-493c-b02e-d8ca707b8142, 633dc5d0-7a56-49a2-8028-40be150ca3b4", + "traceId": "2b2232099c934fbf876d0b384a54dd2e", + "evidence": "tenantId=10001", + "checkedAt": "2026-08-08T02:16:52.779Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "固定四箱型顺序、内径及每边加2cm正确", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/first-leg/customer-dimension-config?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "a54b86f9-cd41-493c-b02e-d8ca707b8142, 633dc5d0-7a56-49a2-8028-40be150ca3b4", + "traceId": "2b2232099c934fbf876d0b384a54dd2e", + "evidence": "types=SMALL,MEDIUM,LARGE,EXTRA_LARGE", + "checkedAt": "2026-08-08T02:16:52.779Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询国内收货仓", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=DOMESTIC" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "7db19d61-25d3-4105-90d2-12118b7e3ccb, db7843ae-a339-4e87-877c-7be642074ade", + "traceId": "e6f82e098e9641ffb688549437438af6", + "evidence": "", + "checkedAt": "2026-08-08T02:16:52.824Z" + }, + { + "module": "orders", + "scenario": null, + "name": "存在可用国内收货仓", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=DOMESTIC" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "7db19d61-25d3-4105-90d2-12118b7e3ccb, db7843ae-a339-4e87-877c-7be642074ade", + "traceId": "e6f82e098e9641ffb688549437438af6", + "evidence": "count=2", + "checkedAt": "2026-08-08T02:16:52.824Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询 FBA 目的仓", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=FBA" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "4e7d7a42-f778-47fa-a45d-128d003fb634, 62bee44e-e55a-4b72-8a6f-f2cc3b8c052c", + "traceId": "18698b25d0004d87bc0e00ea935b1526", + "evidence": "", + "checkedAt": "2026-08-08T02:16:52.897Z" + }, + { + "module": "orders", + "scenario": null, + "name": "存在可用 FBA 目的仓", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=FBA" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "4e7d7a42-f778-47fa-a45d-128d003fb634, 62bee44e-e55a-4b72-8a6f-f2cc3b8c052c", + "traceId": "18698b25d0004d87bc0e00ea935b1526", + "evidence": "count=200", + "checkedAt": "2026-08-08T02:16:52.898Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询可用计费渠道", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=193000000000200001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "562d3951-bb94-4b66-810a-26f2026e5330, 2ae0789c-1f0d-4507-90b9-22583a0db68e", + "traceId": "400f283c7e5042caacbe50b60e97bf34", + "evidence": "", + "checkedAt": "2026-08-08T02:16:52.942Z" + }, + { + "module": "orders", + "scenario": null, + "name": "存在带计价税制的可用渠道", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=193000000000200001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "562d3951-bb94-4b66-810a-26f2026e5330, 2ae0789c-1f0d-4507-90b9-22583a0db68e", + "traceId": "400f283c7e5042caacbe50b60e97bf34", + "evidence": "count=15", + "checkedAt": "2026-08-08T02:16:52.942Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询发货客户候选", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/partners/candidates?type=SHIPPER" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "f048d5dd-2640-435a-b765-58198f727648, cd8380eb-c1bd-4089-ac1a-12845ff1a0b7", + "traceId": "e86dfd6fb2254adc9b7065fea9ebd0bc", + "evidence": "", + "checkedAt": "2026-08-08T02:16:52.978Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Client 上传产品图片", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/merchant/v1/files/product-images" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "801af666-8a7d-43c2-8a35-b0c4caafc377, 6e6b965b-1f72-43ef-b010-b06a657e2a84", + "traceId": "3ed0546f615a455a911a0c6c4f135c71", + "evidence": "contentType=image/png", + "checkedAt": "2026-08-08T02:16:53.015Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "产品图片上传返回字符串文件 ID", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/merchant/v1/files/product-images" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "801af666-8a7d-43c2-8a35-b0c4caafc377, 6e6b965b-1f72-43ef-b010-b06a657e2a84", + "traceId": "3ed0546f615a455a911a0c6c4f135c71", + "evidence": "fileIdType=string, contentType=image/png", + "checkedAt": "2026-08-08T02:16:53.015Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Client 可下载本轮产品图片", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/files/product-images/2085913100780371970" + }, + "http": 200, + "code": null, + "message": null, + "requestId": "2286600c-e017-47a6-97ac-918a72a1aaac, 863ceda5-6595-405e-b840-b3c6cf58921a", + "traceId": "a4ba09bf35fa401bb7791b86c4fa22f5", + "evidence": "bytes=68, contentType=image/png", + "checkedAt": "2026-08-08T02:16:53.055Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "Client 创建四箱型隔离订单", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/merchant/v1/orders/first-leg" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "1528b351-0fcc-46f6-8cf7-79f377b4b805, 97f0d8fe-6c04-4f21-a097-d65851064fd1", + "traceId": "5f3bfaff8b994082ba048cdd8fa5a86d", + "evidence": "marker=ADMIN-API-20260808021651990", + "checkedAt": "2026-08-08T02:16:53.182Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 创建订单幂等重放", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/merchant/v1/orders/first-leg" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "9761aea8-33b4-4bbf-aa91-842e62a7008e, 8b75e2d0-baa4-4cad-a0e0-f04b0e8ee6d1", + "traceId": "09006fcaf41d4c63a7ebc491977b53ae", + "evidence": "", + "checkedAt": "2026-08-08T02:16:53.233Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 创建幂等结果一致", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/merchant/v1/orders/first-leg" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "9761aea8-33b4-4bbf-aa91-842e62a7008e, 8b75e2d0-baa4-4cad-a0e0-f04b0e8ee6d1", + "traceId": "09006fcaf41d4c63a7ebc491977b53ae", + "evidence": "orderId=2085913101392740353, replayedId=2085913101392740353", + "checkedAt": "2026-08-08T02:16:53.233Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 查询隔离订单草稿", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/orders/first-leg/2085913101392740353" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "ca544cb8-1e75-421f-aa95-9faed072cb47, 87bcdea7-b151-416d-9d1b-105870a89e5d", + "traceId": "7a4e0dc0d8454770b2cfe718886b367e", + "evidence": "", + "checkedAt": "2026-08-08T02:16:53.273Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 草稿身份、marker 和状态正确", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/orders/first-leg/2085913101392740353" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "ca544cb8-1e75-421f-aa95-9faed072cb47, 87bcdea7-b151-416d-9d1b-105870a89e5d", + "traceId": "7a4e0dc0d8454770b2cfe718886b367e", + "evidence": "orderNo=FL2085913101392740353, status=DRAFT", + "checkedAt": "2026-08-08T02:16:53.273Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "四箱型产品字段及图片契约完整回显", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/orders/first-leg/2085913101392740353" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "ca544cb8-1e75-421f-aa95-9faed072cb47, 87bcdea7-b151-416d-9d1b-105870a89e5d", + "traceId": "7a4e0dc0d8454770b2cfe718886b367e", + "evidence": "cargoes=4, imageFileId=2085913100780371970", + "checkedAt": "2026-08-08T02:16:53.273Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 提交隔离订单", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/merchant/v1/orders/first-leg/2085913101392740353/submit" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "8ee44952-0e26-4b01-8959-b5bbbe6cace3, 8f685c45-7a23-4bfa-9cf8-c57a18f17d23", + "traceId": "eee77623ea4448b092825f0f1d0eb999", + "evidence": "orderId=2085913101392740353", + "checkedAt": "2026-08-08T02:16:53.386Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 回查已提交订单", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/orders/first-leg/2085913101392740353" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "2da02ea1-cfcf-47d2-a10f-c39764c6b657, ff179a27-7e26-48c7-a409-202265885d86", + "traceId": "9399c417117d43ce9a6bcd02c517c0f2", + "evidence": "", + "checkedAt": "2026-08-08T02:16:53.431Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Client 订单已进入待审核状态", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/merchant/v1/orders/first-leg/2085913101392740353" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "2da02ea1-cfcf-47d2-a10f-c39764c6b657, ff179a27-7e26-48c7-a409-202265885d86", + "traceId": "9399c417117d43ce9a6bcd02c517c0f2", + "evidence": "status=SUBMITTED", + "checkedAt": "2026-08-08T02:16:53.431Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 查询统一订单列表", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders?page=1&size=100&tenantId=10001&orderNo=FL2085913101392740353" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "454e5e07-4344-4de8-afdb-99c366e263cf, abade6f4-90e8-4bee-bc96-45ec2a5897b9", + "traceId": "09b015cf86aa4e548e80ee93b9582ff3", + "evidence": "", + "checkedAt": "2026-08-08T02:16:53.527Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 统一订单列表只核对本轮订单", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders?page=1&size=100&tenantId=10001&orderNo=FL2085913101392740353" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "454e5e07-4344-4de8-afdb-99c366e263cf, abade6f4-90e8-4bee-bc96-45ec2a5897b9", + "traceId": "09b015cf86aa4e548e80ee93b9582ff3", + "evidence": "orderId=2085913101392740353, found=true", + "checkedAt": "2026-08-08T02:16:53.527Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 查询统一订单详情", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders/2085913101392740353?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "c91190bc-2731-4c60-b569-f2058a410841, c07b4a89-d538-4f02-b3e0-9f60843c27ff", + "traceId": "a64f30af38884040973011a84d7f0954", + "evidence": "", + "checkedAt": "2026-08-08T02:16:53.587Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 统一订单详情身份与待审核状态正确", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders/2085913101392740353?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "c91190bc-2731-4c60-b569-f2058a410841, c07b4a89-d538-4f02-b3e0-9f60843c27ff", + "traceId": "a64f30af38884040973011a84d7f0954", + "evidence": "mainStatus=PENDING_REVIEW, timeline=2", + "checkedAt": "2026-08-08T02:16:53.587Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 查询头程订单列表", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders/first-leg?tenantId=10001&keyword=FL2085913101392740353&page=1&size=100" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "9d86bda2-951e-4c39-806d-9ea740d9d46b, 33437fe3-3f4b-4ab7-9248-eb222f1714a8", + "traceId": "e830d79cacc64cfcbccbeeeef37202b5", + "evidence": "", + "checkedAt": "2026-08-08T02:16:53.681Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 头程订单列表只核对本轮订单", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders/first-leg?tenantId=10001&keyword=FL2085913101392740353&page=1&size=100" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "9d86bda2-951e-4c39-806d-9ea740d9d46b, 33437fe3-3f4b-4ab7-9248-eb222f1714a8", + "traceId": "e830d79cacc64cfcbccbeeeef37202b5", + "evidence": "orderId=2085913101392740353, found=true", + "checkedAt": "2026-08-08T02:16:53.682Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 查询头程订单详情", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders/first-leg/2085913101392740353?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "3c6e7418-6ade-4094-ab9b-4a655fcb69c0, 106d28b4-800b-4cab-95d8-c0295d55fbd8", + "traceId": "4eb1b902666b4e52a42f851694ffdb56", + "evidence": "", + "checkedAt": "2026-08-08T02:16:53.732Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 头程订单详情聚合结构完整", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders/first-leg/2085913101392740353?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "3c6e7418-6ade-4094-ab9b-4a655fcb69c0, 106d28b4-800b-4cab-95d8-c0295d55fbd8", + "traceId": "4eb1b902666b4e52a42f851694ffdb56", + "evidence": "cargoes=4, history=2", + "checkedAt": "2026-08-08T02:16:53.732Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "四箱型产品字段及图片契约完整回显", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders/first-leg/2085913101392740353?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "3c6e7418-6ade-4094-ab9b-4a655fcb69c0, 106d28b4-800b-4cab-95d8-c0295d55fbd8", + "traceId": "4eb1b902666b4e52a42f851694ffdb56", + "evidence": "cargoes=4, imageFileId=2085913100780371970", + "checkedAt": "2026-08-08T02:16:53.732Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 为本轮订单添加内部备注", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/platform/v1/orders/first-leg/2085913101392740353/internal-notes?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "6d9e2123-2645-405f-a151-50be16b7a9b9, 8a8ccea0-019d-47e7-8d02-3a1157cc56cc", + "traceId": "f9c9ab1efed24902a8961d211de3a53b", + "evidence": "", + "checkedAt": "2026-08-08T02:16:53.802Z" + }, + { + "module": "orders", + "scenario": null, + "name": "Admin 审核通过本轮订单", + "passed": true, + "deploymentBlocker": false, + "request": { + "method": "POST", + "path": "/api/platform/v1/orders/first-leg/2085913101392740353/approve?tenantId=10001" + }, + "http": 200, + "code": "200", + "message": "success", + "requestId": "ded05598-6114-4a8c-81db-a6ca56aa15cc, 0f3e0813-94c5-47a5-b5bd-f7a6761191e3", + "traceId": "05d5aab5d5ba45be8087bc4571b11385", + "evidence": "orderId=2085913101392740353", + "checkedAt": "2026-08-08T02:16:53.891Z" + }, + { + "module": "orders", + "scenario": "isolated-order-chain", + "name": "审核后生成隔离入库单", + "passed": false, + "deploymentBlocker": true, + "request": { + "method": "GET", + "path": "/api/platform/v1/orders/2085913101392740353?tenantId=10001" + }, + "http": 200, + "code": "S0503", + "message": "统一订单履约详情响应无效", + "requestId": "40ba574d-240f-47d9-a6cb-2aecc922b0ff", + "traceId": "75a3df822dce4d60a7b02e8906c487e2", + "evidence": "20秒内未返回 inboundId", + "checkedAt": "2026-08-08T02:17:14.620Z" + }, + { + "module": "orders", + "scenario": null, + "name": "模块执行", + "passed": false, + "deploymentBlocker": false, + "request": null, + "http": null, + "code": null, + "message": null, + "requestId": null, + "traceId": null, + "evidence": "订单审核后的入库单契约未部署,无法向 OCR/墨家模块交接", + "checkedAt": "2026-08-08T02:17:14.620Z" + }, + { + "module": "inbound-weight-ocr", + "scenario": "weight-image-recognition-and-confirmation", + "name": "仅选择带本轮测试标记的可编辑入库单", + "passed": false, + "deploymentBlocker": false, + "request": null, + "http": null, + "code": null, + "message": null, + "requestId": null, + "traceId": null, + "evidence": "marker=ADMIN-API-20260808021651990, 未找到本轮创建且状态为 WAIT_MEASURE/MEASURING/MEASURE_REJECTED 的入库单", + "checkedAt": "2026-08-08T02:17:14.712Z" + } + ] +} diff --git a/reports/client-test-env-20260808021636-b5090589.json b/reports/client-test-env-20260808021636-b5090589.json new file mode 100644 index 000000000..1c8c4952d --- /dev/null +++ b/reports/client-test-env-20260808021636-b5090589.json @@ -0,0 +1,1378 @@ +{ + "marker": "20260808021636-b5090589", + "gateway": "http://8.163.30.128:9000", + "generatedAt": "2026-08-08T02:16:36.893Z", + "completedAt": "2026-08-08T02:16:44.430Z", + "success": false, + "fatal": { + "name": "Error", + "message": "business code: expected=\"200\" actual=\"S0503\"" + }, + "testAccount": { + "username": "client_20260808021636_e73573", + "companyName": "客户端全链路测试企业-20260808021636-b5090589", + "legalPhone": "13955396895" + }, + "resources": { + "tenantId": "2085913035121741825", + "membershipId": "2085913035121741827", + "reviewId": "2085913049034248194", + "businessLicenseQualificationId": "2085913047201337345", + "businessLicenseDocumentUrl": "/api/merchant/v1/files/qualifications/26d99313-ceb2-4a94-8059-3660d667093a.png", + "productImageFileId": "2085913040382394369", + "enterpriseAddressId": "2085913054931439617", + "receivingWarehouseId": "193000000000200001", + "destinationWarehouseId": "193000000000000001", + "channelCode": "EXX_RAIL_NY", + "roleId": "2085913058190413826", + "testMembershipId": "2085913059490648068", + "orderIds": [ + "2085913044845133826" + ] + }, + "steps": [ + { + "module": "registration", + "name": "enterprise self-registration", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:36.895Z", + "durationMs": 671, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-a248266d-1a9b-44a1-866a-68829b418724, client-e2e-a248266d-1a9b-44a1-866a-68829b418724", + "traceId": "1f3a84e7d8864a38888620b19183b594", + "evidence": { + "tenantId": "2085913035121741825", + "membershipId": "2085913035121741827", + "tenantStatus": "PROFILE_REQUIRED", + "nextStep": "LOGIN_AND_COMPLETE_QUALIFICATIONS" + } + }, + { + "module": "registration", + "name": "duplicate username is rejected", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:37.568Z", + "durationMs": 37, + "http": 200, + "code": "A0409", + "message": "Username already exists", + "requestId": "client-e2e-e08c93c0-c1ba-462f-9f09-941fcba702d0, client-e2e-e08c93c0-c1ba-462f-9f09-941fcba702d0", + "traceId": "0ea82ae9c5cd4642a6165b68537354b3", + "evidence": null + }, + { + "module": "registration", + "name": "login before approval", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:37.607Z", + "durationMs": 357, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-3132dc82-c65e-499b-8987-f2a394553114, client-e2e-3132dc82-c65e-499b-8987-f2a394553114", + "traceId": "19d3dd8b9b864a8f8175adf77fdd60f7", + "evidence": { + "username": "client_20260808021636_e73573" + } + }, + { + "module": "registration", + "name": "profile is incomplete before qualifications", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:37.966Z", + "durationMs": 54, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-2b84f248-88ea-40f0-935e-90d96ada7518, client-e2e-2b84f248-88ea-40f0-935e-90d96ada7518", + "traceId": "4ed9d0c7f48247a8a50ba933ad00d87b", + "evidence": { + "id": "2085913035121741825", + "status": "PROFILE_REQUIRED", + "tenantName": "客户端全链路测试企业-20260808021636-b5090589" + } + }, + { + "module": "pre-approval-master-data", + "name": "load active domestic receiving warehouses", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:38.021Z", + "durationMs": 47, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-a1daacd3-3235-4df0-bdc2-c4dcdf67acae, client-e2e-a1daacd3-3235-4df0-bdc2-c4dcdf67acae", + "traceId": "ed88c4579e3a42d293435cd132c9c10f", + "evidence": { + "total": "2", + "warehouseCount": 2 + } + }, + { + "module": "pre-approval-master-data", + "name": "load active FBA destination warehouses", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:38.069Z", + "durationMs": 70, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-cfbac58b-6e3d-4d1c-95d9-77fc6cc4a8e3, client-e2e-cfbac58b-6e3d-4d1c-95d9-77fc6cc4a8e3", + "traceId": "207e9cfcf9ef4a72ba2da706f9e55c25", + "evidence": { + "total": "253", + "warehouseCount": 200 + } + }, + { + "module": "pre-approval-master-data", + "name": "load receiving warehouse detail", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:38.140Z", + "durationMs": 60, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-11918fa2-b6a2-4132-9ab8-ff0e6acea509, client-e2e-11918fa2-b6a2-4132-9ab8-ff0e6acea509", + "traceId": "9b90c8fefb2941e9aa3b586e73fc7fcb", + "evidence": { + "id": "193000000000200001", + "name": "义乌仓" + } + }, + { + "module": "pre-approval-master-data", + "name": "load active pricing channels", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:38.201Z", + "durationMs": 55, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-c1bef786-969d-45a6-b01f-5d693794cb8f, client-e2e-c1bef786-969d-45a6-b01f-5d693794cb8f", + "traceId": "b828391a54df4a968c918749289a6b92", + "evidence": { + "channels": [ + { + "channelCode": "EXX_RAIL_NY", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "EXX_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "HEDE_PARCEL", + "priceModel": "REGION_WEIGHT_TIER", + "pricingTaxModes": [ + "TAX_INCLUDED" + ] + }, + { + "channelCode": "HEDE_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "MATSON_CLX_PARCEL", + "priceModel": "REGION_WEIGHT_TIER", + "pricingTaxModes": [ + "TAX_INCLUDED" + ] + }, + { + "channelCode": "MATSON_CLX_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "MATSON_MAX_PARCEL", + "priceModel": "REGION_WEIGHT_TIER", + "pricingTaxModes": [ + "TAX_INCLUDED" + ] + }, + { + "channelCode": "MATSON_MAX_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "OA_CHICAGO_RAIL", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "OA_HOUSTON_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "OA_LA_FULL_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "OA_LA_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "OA_NEW_YORK_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + }, + { + "channelCode": "OA_PARCEL", + "priceModel": "REGION_WEIGHT_TIER", + "pricingTaxModes": [ + "TAX_INCLUDED" + ] + }, + { + "channelCode": "OA_SAVANNAH_TRUCK", + "priceModel": "FBA_WAREHOUSE_RATE", + "pricingTaxModes": [ + "TAX_INCLUDED", + "TAX_EXCLUDED" + ] + } + ] + } + }, + { + "module": "pre-approval-master-data", + "name": "load public shipper candidates", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:38.257Z", + "durationMs": 36, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-bf2d4fab-c383-4dcf-8145-d9105924ad3c, client-e2e-bf2d4fab-c383-4dcf-8145-d9105924ad3c", + "traceId": "da268f44908b4748a8180eb03429693b", + "evidence": { + "candidateCount": 3 + } + }, + { + "module": "pre-approval-product-image", + "name": "upload product image", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:38.294Z", + "durationMs": 332, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-c6a2a9be-87e2-4bd4-8b5e-3ba9da7f579b, client-e2e-c6a2a9be-87e2-4bd4-8b5e-3ba9da7f579b", + "traceId": "7aa8654d00634075bd5514a180cfd76f", + "evidence": { + "fileId": "2085913040382394369", + "fileName": "product-20260808021636-b5090589.png", + "contentType": "image/png", + "sizeBytes": "68" + } + }, + { + "module": "order-validation", + "name": "missing pricingTaxMode is rejected", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:38.628Z", + "durationMs": 642, + "http": 200, + "code": "A0422", + "message": "pricingTaxMode: must not be blank", + "requestId": "client-e2e-7862c030-22c2-4be7-8fad-fd637cbc6df7, client-e2e-7862c030-22c2-4be7-8fad-fd637cbc6df7", + "traceId": "03e4cda3554d48b296611a2615087d58", + "evidence": null + }, + { + "module": "order-before-approval", + "name": "create draft before approval", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:39.271Z", + "durationMs": 461, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-b010b108-4461-4462-82e9-8802f651738d, client-e2e-b010b108-4461-4462-82e9-8802f651738d", + "traceId": "a3135274d8be409f9de5fd29b040b482", + "evidence": { + "orderId": "2085913044845133826", + "status": "DRAFT" + } + }, + { + "module": "order-before-approval", + "name": "create draft idempotent replay", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:39.733Z", + "durationMs": 192, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-1354248a-4da5-4816-aea4-1332480d14b9, client-e2e-1354248a-4da5-4816-aea4-1332480d14b9", + "traceId": "81498744d4aa4d9ba591d8cee0b756a6", + "evidence": { + "orderId": "2085913044845133826", + "replayed": true + } + }, + { + "module": "order-before-approval", + "name": "submit is blocked before approval", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:39.925Z", + "durationMs": 94, + "http": 200, + "code": "A0403", + "message": "Enterprise information must be approved before ordering", + "requestId": "client-e2e-d0b1f6f5-6da9-437a-bad6-7bd50d9874e1, client-e2e-d0b1f6f5-6da9-437a-bad6-7bd50d9874e1", + "traceId": "35e9fbf315fa42c4819b8cb86aa50f35", + "evidence": { + "orderId": "2085913044845133826", + "rejectionCode": "A0403" + } + }, + { + "module": "qualification-review", + "name": "upload required qualification package", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:40.020Z", + "durationMs": 302, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-684837de-aaa1-4c77-904a-c5fcd49c4762, client-e2e-684837de-aaa1-4c77-904a-c5fcd49c4762", + "traceId": "e7be70e65e5e470d88de1e6bcec4d3a2", + "evidence": { + "businessLicenseQualificationId": "2085913047201337345", + "legalPersonIdQualificationId": "2085913047234891778", + "tenantStatus": "PROFILE_REQUIRED" + } + }, + { + "module": "qualification-review", + "name": "upload optional qualification FREIGHT_FORWARDER_CERTIFICATE", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:40.325Z", + "durationMs": 87, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-1514e038-ec50-43f1-a0e0-d917fd52d436, client-e2e-1514e038-ec50-43f1-a0e0-d917fd52d436", + "traceId": "e4d4f40014074c77acc2a1a1cd7c74be", + "evidence": { + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "qualificationId": "2085913047876620289", + "tenantStatus": "PROFILE_REQUIRED" + } + }, + { + "module": "qualification-review", + "name": "upload optional qualification BANK_ACCOUNT_LICENSE", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:40.412Z", + "durationMs": 61, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-a80dcf9b-46f4-41d7-be3c-cbd288773499, client-e2e-a80dcf9b-46f4-41d7-be3c-cbd288773499", + "traceId": "c701ecf01bb34d1c9d56ba4966bd24d5", + "evidence": { + "qualificationType": "BANK_ACCOUNT_LICENSE", + "qualificationId": "2085913048157638657", + "tenantStatus": "PROFILE_REQUIRED" + } + }, + { + "module": "qualification-review", + "name": "duplicate optional qualification requires explicit replacement", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:40.473Z", + "durationMs": 61, + "http": 200, + "code": "A0409", + "message": "Qualification type already uploaded / 该类资质已上传", + "requestId": "client-e2e-ec1847b6-ef9f-4178-bb1e-6928a8603ae5, client-e2e-ec1847b6-ef9f-4178-bb1e-6928a8603ae5", + "traceId": "1f3fca28ad96400f956c3a5aac175b2f", + "evidence": { + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "rejectionCode": "A0409" + } + }, + { + "module": "qualification-review", + "name": "replace optional qualification explicitly", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:40.535Z", + "durationMs": 50, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-062697ae-1e2f-4959-855c-3aa1005c6069, client-e2e-062697ae-1e2f-4959-855c-3aa1005c6069", + "traceId": "f513c77575b54a069e040bd4bbdc0c3b", + "evidence": { + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "qualificationId": "2085913048631595010", + "replaced": true + } + }, + { + "module": "qualification-review", + "name": "required qualifications are pending", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:40.586Z", + "durationMs": 34, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-26d83a84-1c80-4ac5-a354-a2e74797aa65, client-e2e-26d83a84-1c80-4ac5-a354-a2e74797aa65", + "traceId": "ec9de9d7e56f48e1b99a3e0da21899c8", + "evidence": { + "qualifications": [ + { + "qualificationId": "2085913047201337345", + "qualificationType": "BUSINESS_LICENSE", + "status": "PENDING" + }, + { + "qualificationId": "2085913047234891778", + "qualificationType": "LEGAL_PERSON_ID", + "status": "PENDING" + }, + { + "qualificationId": "2085913048157638657", + "qualificationType": "BANK_ACCOUNT_LICENSE", + "status": "PENDING" + }, + { + "qualificationId": "2085913048631595010", + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "status": "PENDING" + }, + { + "qualificationId": "2085913047876620289", + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "status": "SUPERSEDED" + } + ] + } + }, + { + "module": "qualification-review", + "name": "submit enterprise review", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:40.621Z", + "durationMs": 60, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-b7734868-849d-45ec-b336-8d246e0e5dd8, client-e2e-b7734868-849d-45ec-b336-8d246e0e5dd8", + "traceId": "51adc05f20b342039a42b5eb4d107101", + "evidence": { + "tenantId": "2085913035121741825", + "status": "PENDING" + } + }, + { + "module": "qualification-review", + "name": "platform reviewer login", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:40.681Z", + "durationMs": 348, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-f4a47459-9be8-4bae-a1ce-08785db3f386, client-e2e-f4a47459-9be8-4bae-a1ce-08785db3f386", + "traceId": "2c0aa7008b2245d19ee713950d8c10ec", + "evidence": { + "username": "platform_admin" + } + }, + { + "module": "qualification-review", + "name": "platform finds pending review", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.030Z", + "durationMs": 58, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-e5dc95ad-3f11-4aab-9140-81325495e36b, client-e2e-e5dc95ad-3f11-4aab-9140-81325495e36b", + "traceId": "ccb2796a2c47499fbd6903aabbe5b729", + "evidence": { + "reviewId": "2085913049034248194", + "tenantId": "2085913035121741825", + "status": "PENDING" + } + }, + { + "module": "qualification-review", + "name": "platform sees required qualifications", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.088Z", + "durationMs": 58, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-ff937a0f-179b-4ca3-a779-c666fb591e9a, client-e2e-ff937a0f-179b-4ca3-a779-c666fb591e9a", + "traceId": "61b0c387b53949c080f8682fd7f89923", + "evidence": { + "qualifications": [ + { + "qualificationId": "2085913047201337345", + "qualificationType": "BUSINESS_LICENSE", + "status": "PENDING" + }, + { + "qualificationId": "2085913047234891778", + "qualificationType": "LEGAL_PERSON_ID", + "status": "PENDING" + }, + { + "qualificationId": "2085913048157638657", + "qualificationType": "BANK_ACCOUNT_LICENSE", + "status": "PENDING" + }, + { + "qualificationId": "2085913048631595010", + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "status": "PENDING" + }, + { + "qualificationId": "2085913047876620289", + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "status": "SUPERSEDED" + } + ] + } + }, + { + "module": "qualification-review", + "name": "platform approves enterprise", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.146Z", + "durationMs": 106, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-4aac6812-5a68-4fcf-9a87-8b7a0995f4c0, client-e2e-4aac6812-5a68-4fcf-9a87-8b7a0995f4c0", + "traceId": "cd4b2aa3c8ae4e7cbbad3475a53a9d99", + "evidence": { + "tenantId": "2085913035121741825", + "administratorCreated": false, + "generatedCredentialsReturned": false + } + }, + { + "module": "qualification-review", + "name": "registered account remains enterprise owner", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.252Z", + "durationMs": 57, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-913c9048-1a82-4958-b405-1d5013824010, client-e2e-913c9048-1a82-4958-b405-1d5013824010", + "traceId": "12e123b953d1422191adc9009e39a6ab", + "evidence": { + "membershipId": "2085913035121741827", + "username": "client_20260808021636_e73573", + "memberType": "OWNER", + "status": "ACTIVE" + } + }, + { + "module": "merchant-session", + "name": "client re-login after approval", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.310Z", + "durationMs": 357, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-1c736667-37e8-4e5c-af3e-d6aef31cfb1c, client-e2e-1c736667-37e8-4e5c-af3e-d6aef31cfb1c", + "traceId": "7e5abfe580904d949235cae9685060c3", + "evidence": { + "username": "client_20260808021636_e73573" + } + }, + { + "module": "merchant-session", + "name": "profile is active after approval", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.667Z", + "durationMs": 39, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-ec4c6ef8-d443-4b8e-8eb2-d0697c5c5a87, client-e2e-ec4c6ef8-d443-4b8e-8eb2-d0697c5c5a87", + "traceId": "32b880d8c4f04ccc813fbba52156fefb", + "evidence": { + "id": "2085913035121741825", + "status": "ACTIVE", + "tenantName": "客户端全链路测试企业-20260808021636-b5090589" + } + }, + { + "module": "merchant-session", + "name": "required qualifications are approved", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.706Z", + "durationMs": 29, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-f9ec5626-4a04-4109-bc94-a0396e8b456e, client-e2e-f9ec5626-4a04-4109-bc94-a0396e8b456e", + "traceId": "ce348875c29544f393d71c77eceb527e", + "evidence": { + "qualifications": [ + { + "qualificationId": "2085913047201337345", + "qualificationType": "BUSINESS_LICENSE", + "status": "APPROVED" + }, + { + "qualificationId": "2085913047234891778", + "qualificationType": "LEGAL_PERSON_ID", + "status": "APPROVED" + }, + { + "qualificationId": "2085913048157638657", + "qualificationType": "BANK_ACCOUNT_LICENSE", + "status": "APPROVED" + }, + { + "qualificationId": "2085913048631595010", + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "status": "APPROVED" + }, + { + "qualificationId": "2085913047876620289", + "qualificationType": "FREIGHT_FORWARDER_CERTIFICATE", + "status": "SUPERSEDED" + } + ] + } + }, + { + "module": "merchant-session", + "name": "preview approved qualification file", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.735Z", + "durationMs": 31, + "http": 200, + "code": null, + "message": null, + "requestId": "client-e2e-468f7cce-e4e4-4126-b0b3-b0a1010ec4e4, client-e2e-468f7cce-e4e4-4126-b0b3-b0a1010ec4e4", + "traceId": "6b97e73f4a42487e94657dd9dc1c8f63", + "evidence": { + "sizeBytes": 68, + "contentType": "image/png" + } + }, + { + "module": "profile-management", + "name": "load enterprise profile for editing", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.767Z", + "durationMs": 32, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-a52082f4-ca75-4f3a-8da1-fb51c1949d21, client-e2e-a52082f4-ca75-4f3a-8da1-fb51c1949d21", + "traceId": "af02fc3de58244328489a33713d57260", + "evidence": { + "id": "2085913035121741825", + "tenantName": "客户端全链路测试企业-20260808021636-b5090589", + "status": "ACTIVE" + } + }, + { + "module": "profile-management", + "name": "edit enterprise profile", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.799Z", + "durationMs": 71, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-a205f6fc-6389-4dca-8f93-41023f80e794, client-e2e-a205f6fc-6389-4dca-8f93-41023f80e794", + "traceId": "b23a1aa57d7a4f19a73e4a68e12d626c", + "evidence": { + "id": "2085913035121741825", + "tenantName": "客户端全链路测试企业-20260808021636-b5090589-已编辑", + "contactName": "企业联系人-20260808021636-b5090589", + "status": "ACTIVE" + } + }, + { + "module": "profile-management", + "name": "enterprise profile edit is persisted", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.870Z", + "durationMs": 41, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-74583f76-17fa-4c71-9634-61c66e164173, client-e2e-74583f76-17fa-4c71-9634-61c66e164173", + "traceId": "51b7fa0c93164dc39a2ed0e770b25eb5", + "evidence": { + "id": "2085913035121741825", + "tenantName": "客户端全链路测试企业-20260808021636-b5090589-已编辑", + "contactEmail": "company_20260808021636b5090589@example.com" + } + }, + { + "module": "profile-validation", + "name": "invalid enterprise contact email is rejected", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.912Z", + "durationMs": 73, + "http": 200, + "code": "A0422", + "message": "contactEmail: must be a well-formed email address", + "requestId": "client-e2e-32c5d0bb-1cdf-4aa3-987c-7fb34464ba97, client-e2e-32c5d0bb-1cdf-4aa3-987c-7fb34464ba97", + "traceId": "d0b056ea898a425596ba25ef6b322322", + "evidence": { + "rejectionCode": "A0422" + } + }, + { + "module": "profile-validation", + "name": "blank enterprise address detail is rejected", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:41.985Z", + "durationMs": 48, + "http": 200, + "code": "A0422", + "message": "detail: must not be blank", + "requestId": "client-e2e-8ffb0912-de9a-4177-bbf2-e3a4415a4718, client-e2e-8ffb0912-de9a-4177-bbf2-e3a4415a4718", + "traceId": "4877f8b1c6c64d06a9f69a488a2ef170", + "evidence": { + "rejectionCode": "A0422" + } + }, + { + "module": "profile-management", + "name": "add enterprise operating address", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.033Z", + "durationMs": 50, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-083cdd3b-3ab2-4aaa-be09-7c0398c96586, client-e2e-083cdd3b-3ab2-4aaa-be09-7c0398c96586", + "traceId": "26fa9c8ae558454ea25ef14ea4b5dcec", + "evidence": { + "id": "2085913054931439617", + "addressType": "OPERATING", + "city": "深圳市", + "isDefault": true + } + }, + { + "module": "profile-management", + "name": "enterprise address list contains created address", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.084Z", + "durationMs": 48, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-2910f958-ebe4-4c5e-98b2-2e2373a40a86, client-e2e-2910f958-ebe4-4c5e-98b2-2e2373a40a86", + "traceId": "db4a824cfd924b6ab8971fb64b1c9ffc", + "evidence": { + "addressCount": 2, + "addressId": "2085913054931439617" + } + }, + { + "module": "profile-management", + "name": "platform can read created enterprise address", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.133Z", + "durationMs": 41, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-59d3dea4-a130-4333-9967-3bfdfd6c2805, client-e2e-59d3dea4-a130-4333-9967-3bfdfd6c2805", + "traceId": "5084dee661fe4ab8987352ef9bcdfe35", + "evidence": { + "addressCount": 2, + "addressId": "2085913054931439617" + } + }, + { + "module": "profile-management", + "name": "load personal profile", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.175Z", + "durationMs": 45, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-915cc1fb-88ad-49fb-9966-b143c992be14, client-e2e-915cc1fb-88ad-49fb-9966-b143c992be14", + "traceId": "6c0460846589432e8f995cee14fdeeab", + "evidence": { + "userId": "2085913035121741826", + "username": "client_20260808021636_e73573", + "displayName": "测试管理员-20260808021636-b5090589" + } + }, + { + "module": "profile-validation", + "name": "invalid personal email is rejected", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.221Z", + "durationMs": 42, + "http": 200, + "code": "A0422", + "message": "email: must be a well-formed email address", + "requestId": "client-e2e-12ab4a5e-e1aa-422f-aa03-af9f3ece3cfb, client-e2e-12ab4a5e-e1aa-422f-aa03-af9f3ece3cfb", + "traceId": "8be51cd3b235428185dd1aa9740f6c02", + "evidence": { + "rejectionCode": "A0422" + } + }, + { + "module": "profile-management", + "name": "edit personal profile", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.264Z", + "durationMs": 42, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-a4bac633-9307-4685-8e8d-1844fb3937b3, client-e2e-a4bac633-9307-4685-8e8d-1844fb3937b3", + "traceId": "5ef053a505664d5b8607f068df322916", + "evidence": { + "userId": "2085913035121741826", + "username": "client_20260808021636_e73573", + "displayName": "测试企业管理员-20260808021636-b5090589" + } + }, + { + "module": "profile-management", + "name": "re-login after personal profile token revocation", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.307Z", + "durationMs": 338, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-5b57ee9f-1457-4985-aa74-cbe75be6209a, client-e2e-5b57ee9f-1457-4985-aa74-cbe75be6209a", + "traceId": "c295cb6afc3143a0a3bdf95453aacbeb", + "evidence": { + "username": "client_20260808021636_e73573" + } + }, + { + "module": "profile-management", + "name": "personal profile edit is persisted", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.646Z", + "durationMs": 34, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-f381e1f5-e9ad-4e77-a04a-54fdb3181bb9, client-e2e-f381e1f5-e9ad-4e77-a04a-54fdb3181bb9", + "traceId": "fb1f419be5634701a689cafecae74345", + "evidence": { + "userId": "2085913035121741826", + "username": "client_20260808021636_e73573", + "displayName": "测试企业管理员-20260808021636-b5090589" + } + }, + { + "module": "rbac-member", + "name": "load merchant permission resources", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.681Z", + "durationMs": 35, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-f68d9fd2-424d-4fb2-86d6-f5227845e8f5, client-e2e-f68d9fd2-424d-4fb2-86d6-f5227845e8f5", + "traceId": "8e6624d1a8a34110b588082303041af8", + "evidence": { + "permissionCount": 27 + } + }, + { + "module": "menu-permissions", + "name": "load owner effective permissions", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.717Z", + "durationMs": 44, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-96bb40ad-282b-4262-b12b-0b0bc6afe5f1, client-e2e-96bb40ad-282b-4262-b12b-0b0bc6afe5f1", + "traceId": "38639d1c459147a487b43b27628a218b", + "evidence": { + "backendPermissionCount": 27, + "visibleClientMenus": [ + "account:profile:view", + "company:member:list", + "company:permission:list", + "company:profile:view", + "company:role:list", + "dashboard:view", + "order:first-leg:list" + ] + } + }, + { + "module": "rbac-member", + "name": "load initial merchant roles", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.761Z", + "durationMs": 44, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-ad5d31a6-b871-44d2-a3f7-e9d4a0136aa9, client-e2e-ad5d31a6-b871-44d2-a3f7-e9d4a0136aa9", + "traceId": "b3dc4e6e488a49628087802e3eddd995", + "evidence": { + "roleCount": 0 + } + }, + { + "module": "rbac-member", + "name": "create custom role", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.806Z", + "durationMs": 55, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-b4b00a81-d78d-42a0-b0da-538e328dc157, client-e2e-b4b00a81-d78d-42a0-b0da-538e328dc157", + "traceId": "dd74d53236534e7a9643bf1cf487e93d", + "evidence": { + "roleId": "2085913058190413826", + "roleCode": "E2E_20260808021636_b5090589", + "roleType": "CUSTOM", + "status": "ACTIVE" + } + }, + { + "module": "rbac-member", + "name": "grant permissions to custom role", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.862Z", + "durationMs": 49, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-85c639a1-faf3-4a98-8555-ecb27b1becb0, client-e2e-85c639a1-faf3-4a98-8555-ecb27b1becb0", + "traceId": "81f718a9da7f400ba45eccd241708e9d", + "evidence": { + "roleId": "2085913058190413826", + "permissionIds": [ + "5301010", + "5301011" + ], + "permissionCodes": [ + "merchant:order:list", + "merchant:order:view" + ] + } + }, + { + "module": "rbac-member", + "name": "custom role permission detail is persisted", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.912Z", + "durationMs": 28, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-cf4ba777-d91d-4472-a3fc-46f7739cd8f0, client-e2e-cf4ba777-d91d-4472-a3fc-46f7739cd8f0", + "traceId": "48bba4f6f12f4251abc46a346c05e48b", + "evidence": { + "roleId": "2085913058190413826", + "roleName": "订单只读-20260808021636-b5090589", + "permissionIds": [ + "5301010", + "5301011" + ] + } + }, + { + "module": "rbac-member", + "name": "edit custom role", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.940Z", + "durationMs": 47, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-3496ac3b-953e-4b62-b11c-4bb8a9ead422, client-e2e-3496ac3b-953e-4b62-b11c-4bb8a9ead422", + "traceId": "fa6d02f69c16476ba0fb4a59d583cbfd", + "evidence": { + "roleId": "2085913058190413826", + "roleName": "订单查询员-20260808021636-b5090589", + "description": "edited 20260808021636-b5090589" + } + }, + { + "module": "rbac-member", + "name": "disable custom role", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:42.988Z", + "durationMs": 43, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-c8381cc2-dd26-4921-81cc-840054012e47, client-e2e-c8381cc2-dd26-4921-81cc-840054012e47", + "traceId": "bf9594d4dc714369b9490df93aa13531", + "evidence": { + "roleId": "2085913058190413826", + "status": "DISABLED" + } + }, + { + "module": "rbac-member", + "name": "enable custom role", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.031Z", + "durationMs": 40, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-be2f89ac-e745-4318-a272-95099ed62cb0, client-e2e-be2f89ac-e745-4318-a272-95099ed62cb0", + "traceId": "83ff69653d094b09b5eb7686a3b3e936", + "evidence": { + "roleId": "2085913058190413826", + "status": "ACTIVE" + } + }, + { + "module": "member-validation", + "name": "member with weak password and invalid email is rejected", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.072Z", + "durationMs": 45, + "http": 200, + "code": "A0422", + "message": "email: must be a well-formed email address;password: 密码必须同时包含字母和数字", + "requestId": "client-e2e-18a589fd-036c-4d19-8d84-5878d5e43db6, client-e2e-18a589fd-036c-4d19-8d84-5878d5e43db6", + "traceId": "d601cd5a1fb04f6d92dc28677cfd575a", + "evidence": { + "rejectionCode": "A0422" + } + }, + { + "module": "rbac-member", + "name": "create enterprise member with role", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.118Z", + "durationMs": 373, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-8a82cd4d-2086-4a82-8fd2-f9c2cfa4cb55, client-e2e-8a82cd4d-2086-4a82-8fd2-f9c2cfa4cb55", + "traceId": "4c5eeaf8db84431cb43cb39cdb548ec3", + "evidence": { + "membershipId": "2085913059490648068", + "memberType": "EMPLOYEE", + "status": "ACTIVE", + "roleIds": [ + "2085913058190413826" + ] + } + }, + { + "module": "rbac-member", + "name": "enterprise member list contains created member and roles", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.492Z", + "durationMs": 51, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-107b0833-c800-4afa-bcc1-34c249110f78, client-e2e-107b0833-c800-4afa-bcc1-34c249110f78", + "traceId": "a21e41834f1540ff87ad059c80802f74", + "evidence": { + "memberCount": 2, + "membershipId": "2085913059490648068" + } + }, + { + "module": "rbac-member", + "name": "enterprise member detail matches created fields and roles", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.543Z", + "durationMs": 40, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-3b17da79-6a02-4474-95d2-16c09235d807, client-e2e-3b17da79-6a02-4474-95d2-16c09235d807", + "traceId": "3de0a54489164c1ea1484092cb07335a", + "evidence": { + "membershipId": "2085913059490648068", + "status": "ACTIVE", + "roleIds": [ + "2085913058190413826" + ] + } + }, + { + "module": "rbac-member", + "name": "duplicate member username is rejected", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.583Z", + "durationMs": 48, + "http": 200, + "code": "A0409", + "message": "企业成员编号已存在", + "requestId": "client-e2e-bed5ffb8-30ac-431e-b8e3-f4a41235a9c9, client-e2e-bed5ffb8-30ac-431e-b8e3-f4a41235a9c9", + "traceId": "6e90a3bcb4e04d25bc848bdbb0646e27", + "evidence": { + "username": "member_20260808021643_1f28b", + "rejectionCode": "A0409" + } + }, + { + "module": "rbac-member", + "name": "assigned custom role cannot be deleted", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.632Z", + "durationMs": 42, + "http": 200, + "code": "A0409", + "message": "角色已分配给成员,不能删除", + "requestId": "client-e2e-207c0a56-ca4d-4ff1-87b3-28d8ceae94ce, client-e2e-207c0a56-ca4d-4ff1-87b3-28d8ceae94ce", + "traceId": "c3cbe89305a742fdbfd30e10bf7e727c", + "evidence": { + "roleId": "2085913058190413826", + "rejectionCode": "A0409" + } + }, + { + "module": "rbac-member", + "name": "enterprise owner cannot be disabled", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.674Z", + "durationMs": 35, + "http": 200, + "code": "A0403", + "message": "企业所有者不能被停用", + "requestId": "client-e2e-3ce951f0-3dff-43cc-8037-69648273131b, client-e2e-3ce951f0-3dff-43cc-8037-69648273131b", + "traceId": "2417135e6cf347458877cd64464d66bd", + "evidence": { + "membershipId": "2085913035121741827", + "rejectionCode": "A0403" + } + }, + { + "module": "rbac-member", + "name": "enterprise owner cannot be edited through member API", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.709Z", + "durationMs": 48, + "http": 200, + "code": "A0403", + "message": "不能通过成员管理接口创建或转让企业所有者", + "requestId": "client-e2e-21316927-28bb-4ec6-887d-018ca1c7e99d, client-e2e-21316927-28bb-4ec6-887d-018ca1c7e99d", + "traceId": "4e8c9b60769040e99762ab6c62aa3067", + "evidence": { + "membershipId": "2085913035121741827", + "rejectionCode": "A0403" + } + }, + { + "module": "rbac-member", + "name": "new enterprise member can login", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:43.758Z", + "durationMs": 346, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-0f73dfe9-76b9-40bd-9fb8-d0a135c64827, client-e2e-0f73dfe9-76b9-40bd-9fb8-d0a135c64827", + "traceId": "ad4c18cd394b44ed81dac1e90cc6c086", + "evidence": { + "username": "member_20260808021643_1f28b" + } + }, + { + "module": "rbac-member", + "name": "member can use granted order list permission", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:44.105Z", + "durationMs": 182, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-ec14739d-948f-421a-8f48-67c819d889b1, client-e2e-ec14739d-948f-421a-8f48-67c819d889b1", + "traceId": "70b19237070f402aaa8d18adb5458095", + "evidence": { + "total": "1" + } + }, + { + "module": "rbac-member", + "name": "member can view order with granted permission", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:44.288Z", + "durationMs": 52, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-53528352-6b7c-43ca-ac9d-481f5f1a030f, client-e2e-53528352-6b7c-43ca-ac9d-481f5f1a030f", + "traceId": "62eb97b421b541e187e1ca2aa4766b9e", + "evidence": { + "orderId": "2085913044845133826", + "status": "DRAFT" + } + }, + { + "module": "menu-permissions", + "name": "read-only member effective permissions match role", + "passed": true, + "skipped": false, + "startedAt": "2026-08-08T02:16:44.341Z", + "durationMs": 31, + "http": 200, + "code": "200", + "message": "success", + "requestId": "client-e2e-6a713dfc-c724-4a2e-a8e8-420c3da59b17, client-e2e-6a713dfc-c724-4a2e-a8e8-420c3da59b17", + "traceId": "92fea9bedcee464986ac3eafff3ecdf8", + "evidence": { + "backendPermissions": [ + "merchant:order:list", + "merchant:order:view" + ], + "visibleClientMenus": [ + "account:profile:view", + "dashboard:view", + "order:first-leg:list" + ] + } + }, + { + "module": "menu-permissions", + "name": "disable assigned role", + "passed": false, + "skipped": false, + "startedAt": "2026-08-08T02:16:44.372Z", + "durationMs": 57, + "http": 200, + "code": "S0503", + "message": "认证会话撤销失败,角色变更未生效", + "requestId": "client-e2e-d9e860c8-b061-43fd-b67b-196f76d95b3d, client-e2e-d9e860c8-b061-43fd-b67b-196f76d95b3d", + "traceId": "f2812578869349b1b4393421498d05df", + "evidence": null, + "error": { + "name": "Error", + "message": "business code: expected=\"200\" actual=\"S0503\"" + } + } + ], + "futureModules": [ + "measurement", + "ocr", + "mojia", + "billing" + ] +} diff --git a/scripts/client-test-env-acceptance.mjs b/scripts/client-test-env-acceptance.mjs index fd272ee34..42a0535cb 100644 --- a/scripts/client-test-env-acceptance.mjs +++ b/scripts/client-test-env-acceptance.mjs @@ -61,6 +61,8 @@ const report = { tenantId: null, membershipId: null, reviewId: null, + businessLicenseQualificationId: null, + businessLicenseDocumentUrl: null, productImageFileId: null, enterpriseAddressId: null, receivingWarehouseId: null, @@ -367,6 +369,8 @@ async function merchantAfterApprovalModule() { const businessLicense = arrayData(approvedQualifications.json.data) .find((item) => item.qualificationType === 'BUSINESS_LICENSE') assertPresent(businessLicense?.documentUrl, 'business license documentUrl') + report.resources.businessLicenseQualificationId = businessLicense.id ?? businessLicense.qualificationId + report.resources.businessLicenseDocumentUrl = businessLicense.documentUrl await apiStep('merchant-session', 'preview approved qualification file', { method: 'GET', path: apiPath(businessLicense.documentUrl), token: merchantToken, responseType: 'bytes', }, ({ status, bytes, headers }) => { @@ -524,6 +528,9 @@ async function rbacAndMemberModule() { .filter((item) => item.status === 'ACTIVE' && desiredCodes.includes(item.permissionCode)) assertEqual(grantedPermissions.length, desiredCodes.length, 'order permission count') const permissionIds = grantedPermissions.map((item) => item.id) + const orderListPermissionId = grantedPermissions + .find((item) => item.permissionCode === 'merchant:order:list')?.id + assertPresent(orderListPermissionId, 'merchant:order:list permission id') const ownerPermissions = await apiStep('menu-permissions', 'load owner effective permissions', { method: 'GET', path: '/api/merchant/v1/iam/authorization/permissions', token: merchantToken, @@ -773,6 +780,55 @@ async function rbacAndMemberModule() { assertSetEqual(visibleClientMenus(arrayData(json.data)), ['account:profile:view', 'dashboard:view', 'order:first-leg:list'], 'visible Client menus') }, ({ json }) => ({ backendPermissions: json.data, visibleClientMenus: visibleClientMenus(arrayData(json.data)) })) + await apiStep('menu-permissions', 'remove order view permission from assigned role', { + method: 'PUT', path: '/api/merchant/v1/iam/roles/permissions', token: merchantToken, + body: { roleId, permissionIds: [orderListPermissionId] }, + }, ({ json }) => requireSuccess(json), () => ({ roleId, permissionCodes: [desiredCodes[0]] })) + await softApiStep('menu-permissions', 'permission removal revokes stale member token', { + method: 'GET', path: `/api/merchant/v1/orders/first-leg/${report.resources.orderIds[0]}`, token: memberToken, + }, ({ json, status }) => { + assertTrue([200, 401, 403].includes(status), `unexpected HTTP status ${status}`) + assertTrue(['A0401', 'A0403'].includes(String(json?.code)), `expected A0401/A0403, actual=${json?.code}`) + }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) + + const reducedPermissionLogin = await apiStep('menu-permissions', 'member re-login after permission removal', { + method: 'POST', path: '/api/auth/login', body: { username: memberUsername, password: memberPassword }, + }, ({ json }) => { + requireSuccess(json) + assertPresent(json.data?.accessToken, 'member accessToken') + }, () => ({ username: memberUsername })) + memberToken = reducedPermissionLogin.json.data.accessToken + await apiStep('menu-permissions', 'permission removal is reflected after re-login', { + method: 'GET', path: '/api/merchant/v1/iam/authorization/permissions', token: memberToken, + }, ({ json }) => { + requireSuccess(json) + assertSetEqual(arrayData(json.data), [desiredCodes[0]], 'reduced member permissions') + }, ({ json }) => ({ backendPermissions: json.data })) + await apiStep('menu-permissions', 'member without view permission cannot read order detail', { + method: 'GET', path: `/api/merchant/v1/orders/first-leg/${report.resources.orderIds[0]}`, token: memberToken, + }, ({ json, status }) => { + assertTrue([200, 403].includes(status), `unexpected HTTP status ${status}`) + assertEqual(String(json?.code), 'A0403', 'business code') + }, ({ json }) => ({ rejectionCode: json?.code })) + + await apiStep('menu-permissions', 'restore assigned role permissions', { + method: 'PUT', path: '/api/merchant/v1/iam/roles/permissions', token: merchantToken, + body: { roleId, permissionIds }, + }, ({ json }) => requireSuccess(json), () => ({ roleId, permissionCodes: desiredCodes })) + const restoredPermissionLogin = await apiStep('menu-permissions', 'member re-login after permission restoration', { + method: 'POST', path: '/api/auth/login', body: { username: memberUsername, password: memberPassword }, + }, ({ json }) => { + requireSuccess(json) + assertPresent(json.data?.accessToken, 'member accessToken') + }, () => ({ username: memberUsername })) + memberToken = restoredPermissionLogin.json.data.accessToken + await apiStep('menu-permissions', 'restored permission allows order detail again', { + method: 'GET', path: `/api/merchant/v1/orders/first-leg/${report.resources.orderIds[0]}`, token: memberToken, + }, ({ json }) => { + requireSuccess(json) + assertEqual(String(json.data?.order?.id), String(report.resources.orderIds[0]), 'order id') + }, ({ json }) => ({ orderId: json.data.order.id })) + const editedMemberBody = { displayName: `已编辑订单成员-${marker}`.slice(0, 64), phone: memberBody.phone, @@ -811,6 +867,12 @@ async function rbacAndMemberModule() { requireSuccess(json) assertEqual(json.data?.status, 'DISABLED', 'member status') }, ({ json }) => ({ membershipId: json.data.membershipId, status: json.data.status })) + await softApiStep('rbac-member', 'disabled member stale token is rejected', { + method: 'GET', path: '/api/merchant/v1/orders/first-leg?page=1&size=20', token: memberToken, + }, ({ json, status }) => { + assertTrue([200, 401, 403].includes(status), `unexpected HTTP status ${status}`) + assertTrue(['A0401', 'A0403'].includes(String(json?.code)), `expected A0401/A0403, actual=${json?.code}`) + }, ({ json }) => ({ membershipId, rejectionCode: json?.code })) await apiStep('rbac-member', 'disabled member login is rejected', { method: 'POST', path: '/api/auth/login', body: { username: memberUsername, password: resetMemberPassword }, }, ({ json, status }) => { @@ -824,6 +886,17 @@ async function rbacAndMemberModule() { assertEqual(json.data?.status, 'ACTIVE', 'member status') }, ({ json }) => ({ membershipId: json.data.membershipId, status: json.data.status })) + const enabledMemberLogin = await apiStep('rbac-member', 'member re-login after account is enabled', { + method: 'POST', path: '/api/auth/login', body: { username: memberUsername, password: resetMemberPassword }, + }, ({ json }) => { + requireSuccess(json) + assertPresent(json.data?.accessToken, 'member accessToken') + }, () => ({ username: memberUsername })) + memberToken = enabledMemberLogin.json.data.accessToken + await apiStep('rbac-member', 're-enabled member can list orders before roles are cleared', { + method: 'GET', path: '/api/merchant/v1/orders/first-leg?page=1&size=20', token: memberToken, + }, ({ json }) => requireSuccess(json), ({ json }) => ({ total: json.data?.total })) + await apiStep('rbac-member', 'clear enterprise member roles', { method: 'PUT', path: '/api/merchant/v1/iam/members/roles', token: merchantToken, body: { membershipId, roleIds: [] }, @@ -882,6 +955,33 @@ async function orderLifecycleModule() { assertEqual(String(json?.code), 'A0400', 'business code') }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) + const emptyImageForm = new FormData() + emptyImageForm.append('file', new Blob([], { type: 'image/png' }), `empty-product-${marker}.png`) + await softApiStep('order-product-image', 'empty product image is rejected', { + method: 'POST', path: '/api/merchant/v1/files/product-images', token: merchantToken, body: emptyImageForm, + }, ({ json, status }) => { + assertEqual(status, 200, 'HTTP status') + assertEqual(String(json?.code), 'A0400', 'business code') + }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) + + const forgedImageForm = new FormData() + forgedImageForm.append('file', new Blob(['not-a-real-png'], { type: 'image/png' }), `forged-product-${marker}.png`) + await softApiStep('order-product-image', 'forged product image signature is rejected', { + method: 'POST', path: '/api/merchant/v1/files/product-images', token: merchantToken, body: forgedImageForm, + }, ({ json, status }) => { + assertEqual(status, 200, 'HTTP status') + assertEqual(String(json?.code), 'A0400', 'business code') + }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) + + const oversizedImageForm = new FormData() + oversizedImageForm.append('file', new Blob([Buffer.alloc(5 * 1024 * 1024 + 1)], { type: 'image/png' }), `oversized-product-${marker}.png`) + await softApiStep('order-product-image', 'product image over 5MB is rejected', { + method: 'POST', path: '/api/merchant/v1/files/product-images', token: merchantToken, body: oversizedImageForm, + }, ({ json, status }) => { + assertEqual(status, 200, 'HTTP status') + assertEqual(String(json?.code), 'A0400', 'business code') + }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) + await apiStep('order-lifecycle', 'download own product image', { method: 'GET', path: `/api/merchant/v1/files/product-images/${productImageFileId}`, token: merchantToken, responseType: 'bytes', @@ -925,6 +1025,18 @@ async function orderLifecycleModule() { name: 'invalid product image file ID is rejected', mutate(value) { value.cargoes[0].productImageFileId = -1 }, }, + { + name: 'zero package count weight and volume are rejected', + mutate(value) { + value.cargoes[0].packageCount = 0 + value.cargoes[0].grossWeightKg = 0 + value.cargoes[0].volumeCbm = 0 + }, + }, + { + name: 'customer remark over 1000 characters is rejected', + mutate(value) { value.customerRemark = 'x'.repeat(1001) }, + }, ] for (const testCase of invalidOrderCases) { const invalidPayload = structuredClone(payload) @@ -937,6 +1049,19 @@ async function orderLifecycleModule() { assertEqual(String(json?.code), 'A0422', 'business code') }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) } + await softApiStep('order-validation', 'missing Idempotency-Key is rejected', { + method: 'POST', path: '/api/merchant/v1/orders/first-leg', token: merchantToken, body: payload, + }, ({ json, status }) => { + assertTrue([200, 400].includes(status), `unexpected HTTP status ${status}`) + assertEqual(String(json?.code), 'A0400', 'business code') + }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) + await softApiStep('order-validation', 'Idempotency-Key over 128 characters is rejected', { + method: 'POST', path: '/api/merchant/v1/orders/first-leg', token: merchantToken, + headers: { 'Idempotency-Key': 'x'.repeat(129) }, body: payload, + }, ({ json, status }) => { + assertEqual(status, 200, 'HTTP status') + assertEqual(String(json?.code), 'A0422', 'business code') + }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) const createKey = newIdempotencyKey('approved-create') const created = await apiStep('order-lifecycle', 'create valid draft after approval', { method: 'POST', path: '/api/merchant/v1/orders/first-leg', token: merchantToken, @@ -1044,6 +1169,41 @@ async function orderLifecycleModule() { assertPresent(pageRecords(json.data).find((item) => String(item.orderId) === String(orderId)), 'submitted status-filtered order') }, ({ json }) => ({ total: json.data?.total, orderId, status: 'SUBMITTED' })) + await apiStep('order-filters', 'warehouse channel and destination filters contain submitted order', { + method: 'GET', + path: `/api/merchant/v1/orders/first-leg?page=1&size=50&receivingWarehouseId=${payload.receivingWarehouseId}` + + `&channelName=${encodeURIComponent(payload.channelName)}&destinationType=${payload.destinationType}` + + `&destinationWarehouseId=${payload.destinationWarehouseId}`, + token: merchantToken, + }, ({ json }) => { + requireSuccess(json) + assertPresent(pageRecords(json.data).find((item) => String(item.orderId) === String(orderId)), 'combined-filtered order') + }, ({ json }) => ({ total: json.data?.total, orderId })) + await apiStep('order-filters', 'sender phone filter contains submitted order', { + method: 'GET', path: `/api/merchant/v1/orders/first-leg?page=1&size=50&customerContact=${encodeURIComponent(legalPhone)}`, token: merchantToken, + }, ({ json }) => { + requireSuccess(json) + assertPresent(pageRecords(json.data).find((item) => String(item.orderId) === String(orderId)), 'sender-phone-filtered order') + }, ({ json }) => ({ total: json.data?.total, orderId })) + await apiStep('order-filters', 'unmatched keyword returns an empty order page', { + method: 'GET', path: `/api/merchant/v1/orders/first-leg?page=1&size=50&keyword=${randomUUID()}`, token: merchantToken, + }, ({ json }) => { + requireSuccess(json) + assertEqual(pageRecords(json.data).length, 0, 'unmatched order count') + }, ({ json }) => ({ total: json.data?.total })) + await softApiStep('order-validation', 'invalid order status filter is rejected', { + method: 'GET', path: '/api/merchant/v1/orders/first-leg?page=1&size=20&status=UNKNOWN', token: merchantToken, + }, ({ json, status }) => { + assertTrue([200, 400].includes(status), `unexpected HTTP status ${status}`) + assertEqual(String(json?.code), 'A0400', 'business code') + }, ({ json }) => ({ rejectionCode: json?.code, message: json?.message })) + await apiStep('order-boundary', 'unknown order detail is rejected', { + method: 'GET', path: '/api/merchant/v1/orders/first-leg/1', token: merchantToken, + }, ({ json, status }) => { + assertEqual(status, 200, 'HTTP status') + assertEqual(String(json?.code), 'A0404', 'business code') + }, ({ json }) => ({ orderId: 1, rejectionCode: json?.code })) + await softApiStep('order-boundary', 'submitted order cannot be edited by client', { method: 'PUT', path: `/api/merchant/v1/orders/first-leg/${orderId}`, token: merchantToken, body: editedPayload, }, ({ json, status }) => { @@ -1142,9 +1302,37 @@ async function isolationModule() { assertTrue([200, 403, 404].includes(status), `unexpected HTTP status ${status}`) assertTrue(['A0403', 'A0404'].includes(String(json?.code)), `expected A0403/A0404, actual=${json?.code}`) }, ({ json }) => ({ fileId: report.resources.productImageFileId, rejectionCode: json?.code })) + await softApiStep('isolation', 'cross-client member detail access is rejected', { + method: 'GET', path: `/api/merchant/v1/iam/members/${report.resources.testMembershipId}`, token: existingClientToken, + }, ({ json, status }) => { + assertEqual(status, 200, 'HTTP status') + assertTrue(['A0403', 'A0404'].includes(String(json?.code)), `expected A0403/A0404, actual=${json?.code}`) + }, ({ json }) => ({ membershipId: report.resources.testMembershipId, rejectionCode: json?.code })) + await apiStep('isolation', 'existing client address list excludes new enterprise address', { + method: 'GET', path: '/api/merchant/v1/iam/tenant/addresses', token: existingClientToken, + }, ({ json }) => { + requireSuccess(json) + assertTrue(!arrayData(json.data).some((item) => String(item.id) === String(report.resources.enterpriseAddressId)), + 'cross-client address must not be visible') + }, ({ json }) => ({ addressCount: arrayData(json.data).length, excludedAddressId: report.resources.enterpriseAddressId })) + await apiStep('isolation', 'existing client qualification list excludes new enterprise qualification', { + method: 'GET', path: '/api/merchant/v1/iam/tenant/qualifications', token: existingClientToken, + }, ({ json }) => { + requireSuccess(json) + assertTrue(!arrayData(json.data).some((item) => String(item.id ?? item.qualificationId) + === String(report.resources.businessLicenseQualificationId)), 'cross-client qualification must not be visible') + }, ({ json }) => ({ qualificationCount: arrayData(json.data).length, + excludedQualificationId: report.resources.businessLicenseQualificationId })) + await softApiStep('isolation', 'cross-client qualification file access is rejected', { + method: 'GET', path: apiPath(report.resources.businessLicenseDocumentUrl), token: existingClientToken, + }, ({ json, status }) => { + assertTrue([200, 403, 404].includes(status), `unexpected HTTP status ${status}`) + assertTrue(['A0403', 'A0404'].includes(String(json?.code)), `expected A0403/A0404, actual=${json?.code}`) + }, ({ json }) => ({ qualificationId: report.resources.businessLicenseQualificationId, rejectionCode: json?.code })) } async function accountSecurityModule() { + const consumedRefreshToken = merchantRefreshToken const rotated = await apiStep('account-security', 'rotate refresh token', { method: 'POST', path: '/api/auth/refresh', body: { refreshToken: merchantRefreshToken }, }, ({ json }) => { @@ -1154,6 +1342,12 @@ async function accountSecurityModule() { }, () => ({ rotated: true })) merchantToken = rotated.json.data.accessToken merchantRefreshToken = rotated.json.data.refreshToken + await apiStep('account-security', 'consumed refresh token cannot be replayed', { + method: 'POST', path: '/api/auth/refresh', body: { refreshToken: consumedRefreshToken }, + }, ({ json, status }) => { + assertEqual(status, 200, 'HTTP status') + assertEqual(String(json?.code), 'A0401', 'business code') + }, ({ json }) => ({ rejectionCode: json?.code })) temporaryOwnerPassword = `Temporary${randomUUID().replaceAll('-', '').slice(0, 14)}6!` await apiStep('account-security', 'change owner password to temporary value', { @@ -1204,6 +1398,9 @@ async function accountSecurityModule() { assertEqual(status, 200, 'HTTP status') assertEqual(String(json?.code), 'A0401', 'business code') }, ({ json }) => ({ rejectionCode: json.code })) + await apiStep('account-security', 'repeated logout remains idempotent', { + method: 'POST', path: '/api/auth/logout', body: { refreshToken: loggedOutRefreshToken }, + }, ({ json }) => requireSuccess(json), () => ({ repeated: true })) } async function recoverOwnerPassword() {