This commit is contained in:
2026-08-08 15:25:46 +08:00
parent 7b2b208467
commit 931009a518
11 changed files with 10920 additions and 3 deletions
@@ -0,0 +1,398 @@
{
"reportName": "后台管理测试环境 HTTP API 验收",
"marker": "ADMIN-API-20260808070759638",
"gateway": "http://8.163.30.128:9000",
"generatedAt": "2026-08-08T07:08:02.212Z",
"startedAt": "2026-08-08T07:07:59.638Z",
"success": true,
"fatal": null,
"summary": {
"total": 19,
"passed": 19,
"failed": 0
},
"modules": [
{
"name": "overseas-storage-fees",
"startedAt": "2026-08-08T07:08:00.028Z",
"finishedAt": "2026-08-08T07:08:02.212Z",
"success": true,
"passed": 18,
"failed": 0,
"fatal": null
}
],
"scenarios": [
{
"name": "customer-overseas-storage-fee-lifecycle",
"module": "overseas-storage-fees",
"success": true,
"deploymentBlocked": false,
"passed": 18,
"failed": 0
}
],
"deploymentBlockers": [],
"createdResources": {
"overseasStorageFeeCustomer": {
"tenantId": "2085986363912081410",
"marker": "ADMIN-API-20260808070759638",
"ownedByModule": true
}
},
"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": "08022e4a-3608-4a0a-a044-d9cf6e0be9ff, 3f9630d3-7d03-4065-91f3-98b51ec9b3a3",
"traceId": "799245d405814326b4fb662096e0ead8",
"evidence": "",
"checkedAt": "2026-08-08T07:08:00.027Z"
},
{
"module": "overseas-storage-fees",
"scenario": null,
"name": "检查测试联系电话可用性",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/contact-phone-availability?contactPhone=16172880029"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "03ecb397-ed46-4843-a596-17e190e0aa1a, 1c33fc17-a09f-4a6d-9d37-79fc2a19e912",
"traceId": "3073bdaea83a4dd79345134343fadcbd",
"evidence": "attempt=1",
"checkedAt": "2026-08-08T07:08:00.277Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "创建海外仓费用隔离客户",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "POST",
"path": "/api/platform/v1/iam/tenants"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "2c62bbe2-5bc2-4db7-8445-53d602398e10, 7aa8dec0-1d18-41d0-9190-c92d72b35fcf",
"traceId": "3bb8c275a791436894fbb691385909cf",
"evidence": "",
"checkedAt": "2026-08-08T07:08:01.251Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "海外仓费用隔离客户创建并带 marker",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "POST",
"path": "/api/platform/v1/iam/tenants"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "2c62bbe2-5bc2-4db7-8445-53d602398e10, 7aa8dec0-1d18-41d0-9190-c92d72b35fcf",
"traceId": "3bb8c275a791436894fbb691385909cf",
"evidence": "tenantId=2085986363912081410",
"checkedAt": "2026-08-08T07:08:01.251Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "查询客户海外仓费用配置",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "0a80a0c0-b310-4617-b1fb-6378183c34d1, d79c7b46-9048-40e8-af13-88edc9b179c4",
"traceId": "a0c4715331204699b317ee5c28dbc902",
"evidence": "tenantId=2085986363912081410",
"checkedAt": "2026-08-08T07:08:01.505Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "客户费用配置身份、默认 USD 与9档结构正确",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "0a80a0c0-b310-4617-b1fb-6378183c34d1, d79c7b46-9048-40e8-af13-88edc9b179c4",
"traceId": "a0c4715331204699b317ee5c28dbc902",
"evidence": "ruleId=2085986365855547394, customerId=2085986363912081410, currency=USD, tiers=9",
"checkedAt": "2026-08-08T07:08:01.506Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "请求体客户 ID 与路径不一致时安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "A0422",
"message": "客户ID与路径不一致",
"requestId": "9af89e20-5c1b-4a6e-a861-478915770c86",
"traceId": "4bdb61b246624177a3d5106ab5a30c05",
"evidence": "HTTP=200, code=A0422",
"checkedAt": "2026-08-08T07:08:01.667Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "整组修改客户海外仓费用",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "8e4776f4-6652-444c-8c0a-e852e144f447, 78724810-dc98-4d8a-9f98-ceac1bc2c090",
"traceId": "16bc0d76c86c4b6e83d81abfaa60da3d",
"evidence": "tenantId=2085986363912081410, tiers=9",
"checkedAt": "2026-08-08T07:08:01.776Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "回查整组修改后的客户费用",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "3099f742-1a4d-4938-8445-da774666b6ac, 63466c28-e78f-4ccb-8c24-7ea58f284be8",
"traceId": "019f7fbcc1554a9eaa37aa26b78bbf15",
"evidence": "",
"checkedAt": "2026-08-08T07:08:01.829Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "修改后币种、价格、9档及版本整体回显",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "3099f742-1a4d-4938-8445-da774666b6ac, 63466c28-e78f-4ccb-8c24-7ea58f284be8",
"traceId": "019f7fbcc1554a9eaa37aa26b78bbf15",
"evidence": "currency=EUR, version=1, tiers=9",
"checkedAt": "2026-08-08T07:08:01.830Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "旧 version 整组保存返回并发冲突",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "A0409",
"message": "费用配置已被修改,请刷新后重试",
"requestId": "529b1872-a54d-47fa-93e1-a83f8b958e6b",
"traceId": "cfe282a3f62d4951b27ae93cbcf831ab",
"evidence": "HTTP=200, code=A0409",
"checkedAt": "2026-08-08T07:08:01.882Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "冲突后回查费用配置",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "e03cb252-7e06-4cb4-8d4f-2275e78f7cd8, 82f92f72-f74d-46de-871d-e981ebfdc555",
"traceId": "5c605f53426144518bfea2eb8cfee8cf",
"evidence": "",
"checkedAt": "2026-08-08T07:08:01.927Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "冲突未造成部分更新",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "e03cb252-7e06-4cb4-8d4f-2275e78f7cd8, 82f92f72-f74d-46de-871d-e981ebfdc555",
"traceId": "5c605f53426144518bfea2eb8cfee8cf",
"evidence": "version=1, unchanged=true",
"checkedAt": "2026-08-08T07:08:01.927Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "恢复本轮客户费用原始配置",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "5c9ae1b7-7110-4f8d-ae67-6b6d5e540fea, 2ddda50c-4242-4bbc-b939-4838b3079c39",
"traceId": "1e99e9bb1b0146b3a33309e01d0304e8",
"evidence": "restore=initial",
"checkedAt": "2026-08-08T07:08:02.009Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "回查恢复后的客户费用",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "97755a72-6e69-404b-b2b3-ecbc8f07ce79, 0499aa74-77c0-4549-8cc9-e4979e9fe477",
"traceId": "1debe0048ffc460a8051330d840d38dd",
"evidence": "",
"checkedAt": "2026-08-08T07:08:02.050Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "恢复后9档、USD、免费档和无上限档一致",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "97755a72-6e69-404b-b2b3-ecbc8f07ce79, 0499aa74-77c0-4549-8cc9-e4979e9fe477",
"traceId": "1debe0048ffc460a8051330d840d38dd",
"evidence": "currency=USD, version=2, tiers=9",
"checkedAt": "2026-08-08T07:08:02.050Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "错误客户 ID 查询安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/999999999999999999/overseas-storage-fees"
},
"http": 200,
"code": "A0404",
"message": "海外仓费用不存在",
"requestId": "6d8fb159-9eb5-4441-b8b9-f3b56559c2b1",
"traceId": "5223ea09b0d74ac78294315a2c6f7b7f",
"evidence": "HTTP=200, code=A0404",
"checkedAt": "2026-08-08T07:08:02.092Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "清理费用隔离客户",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "POST",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/archive"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "24fc7ae4-5eec-446d-af30-36faeceef1aa, a5882354-26c6-40fb-bdf3-b4c84d8daa16",
"traceId": "f857e4e0ef8647909d4825baf9d4ab3c",
"evidence": "tenantId=2085986363912081410",
"checkedAt": "2026-08-08T07:08:02.212Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "费用隔离客户清理后停用",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "POST",
"path": "/api/platform/v1/iam/tenants/2085986363912081410/archive"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "24fc7ae4-5eec-446d-af30-36faeceef1aa, a5882354-26c6-40fb-bdf3-b4c84d8daa16",
"traceId": "f857e4e0ef8647909d4825baf9d4ab3c",
"evidence": "status=DISABLED",
"checkedAt": "2026-08-08T07:08:02.212Z"
}
]
}
@@ -0,0 +1,596 @@
{
"reportName": "后台管理测试环境 HTTP API 验收",
"marker": "ADMIN-API-20260808071157232",
"gateway": "http://8.163.30.128:9000",
"generatedAt": "2026-08-08T07:11:58.858Z",
"startedAt": "2026-08-08T07:11:57.232Z",
"success": true,
"fatal": null,
"summary": {
"total": 30,
"passed": 30,
"failed": 0
},
"modules": [
{
"name": "overseas-storage-fees",
"startedAt": "2026-08-08T07:11:57.611Z",
"finishedAt": "2026-08-08T07:11:58.858Z",
"success": true,
"passed": 29,
"failed": 0,
"fatal": null
}
],
"scenarios": [
{
"name": "customer-overseas-storage-fee-lifecycle",
"module": "overseas-storage-fees",
"success": true,
"deploymentBlocked": false,
"passed": 29,
"failed": 0
}
],
"deploymentBlockers": [],
"createdResources": {
"overseasStorageFeeCustomer": {
"tenantId": "2085987359342055425",
"marker": "ADMIN-API-20260808071157232",
"ownedByModule": true
}
},
"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": "74f372db-9641-4b79-b630-f3031e3c1b12, 26784c66-115f-4a5c-952c-7deae51e6b9b",
"traceId": "438754ec03024b9eb43a2e425875d6db",
"evidence": "",
"checkedAt": "2026-08-08T07:11:57.610Z"
},
{
"module": "overseas-storage-fees",
"scenario": null,
"name": "检查测试联系电话可用性",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/contact-phone-availability?contactPhone=16173117612"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "0e26abf8-7786-4f6e-ad14-442dd4248dfa, 49c9752d-86ac-419d-b584-93403189613c",
"traceId": "56c96ba33e1b49e892c2d89043f0f907",
"evidence": "attempt=1",
"checkedAt": "2026-08-08T07:11:57.659Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "创建海外仓费用隔离客户",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "POST",
"path": "/api/platform/v1/iam/tenants"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "5457bda0-53c8-48c6-912b-585584cbea06, ebd253d8-ca39-4a8a-aa8c-7067f132fe50",
"traceId": "e1f4f63a431941ae96137434ba824ea0",
"evidence": "",
"checkedAt": "2026-08-08T07:11:58.076Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "海外仓费用隔离客户创建并带 marker",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "POST",
"path": "/api/platform/v1/iam/tenants"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "5457bda0-53c8-48c6-912b-585584cbea06, ebd253d8-ca39-4a8a-aa8c-7067f132fe50",
"traceId": "e1f4f63a431941ae96137434ba824ea0",
"evidence": "tenantId=2085987359342055425",
"checkedAt": "2026-08-08T07:11:58.076Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "查询客户海外仓费用配置",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "d0a421cf-6bad-44a7-90ca-73708bdd7511, 987f6acf-0d54-4243-9c20-8cd3b78286f5",
"traceId": "a76b181237ac4279991f76e68decc188",
"evidence": "tenantId=2085987359342055425",
"checkedAt": "2026-08-08T07:11:58.125Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "客户费用配置身份、默认 USD 与9档结构正确",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "d0a421cf-6bad-44a7-90ca-73708bdd7511, 987f6acf-0d54-4243-9c20-8cd3b78286f5",
"traceId": "a76b181237ac4279991f76e68decc188",
"evidence": "ruleId=2085987359469387778, customerId=2085987359342055425, currency=USD, tiers=9",
"checkedAt": "2026-08-08T07:11:58.126Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "按客户查询海外仓费用规则",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/overseas-storage-fees?page=1&size=10&customerId=2085987359342055425"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "0f83d5cb-81d8-4552-9baa-482b21f16d09, e6f08a39-39ac-48af-b26b-940e0b872e33",
"traceId": "16905a27ddff44a394ae5f42c8a1e856",
"evidence": "tenantId=2085987359342055425",
"checkedAt": "2026-08-08T07:11:58.177Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "每个客户恰好一条规则且规则内恰好9档",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/overseas-storage-fees?page=1&size=10&customerId=2085987359342055425"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "0f83d5cb-81d8-4552-9baa-482b21f16d09, e6f08a39-39ac-48af-b26b-940e0b872e33",
"traceId": "16905a27ddff44a394ae5f42c8a1e856",
"evidence": "total=1, records=1, tiers=9",
"checkedAt": "2026-08-08T07:11:58.177Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "请求体客户 ID 与路径不一致时安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "A0422",
"message": "客户ID与路径不一致",
"requestId": "91de1e2f-bee8-4b8c-8323-9d54926839a0",
"traceId": "d386b412d111461eb2937c65604b9773",
"evidence": "HTTP=200, code=A0422",
"checkedAt": "2026-08-08T07:11:58.206Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "非法费用类型安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "A0422",
"message": "客户费用必须包含仓租5档和退货仓储费4档",
"requestId": "b7e124b3-d8d7-4402-ad4c-ab7c8d7f167b",
"traceId": "6936ef7b06344f87829ac9cffb722976",
"evidence": "HTTP=200, code=A0422",
"checkedAt": "2026-08-08T07:11:58.239Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "费用类型与计费单位不匹配安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "A0422",
"message": "费用类型和计费单位不匹配",
"requestId": "50911ba2-fdd2-423d-87c0-11c3fb0e49a0",
"traceId": "d4bd4bcee678411fbe2582aa4168099f",
"evidence": "HTTP=200, code=A0422",
"checkedAt": "2026-08-08T07:11:58.279Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "阶梯区间重叠安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "A0422",
"message": "阶梯必须从第1天开始且连续",
"requestId": "f31729e2-1fae-4f71-b1dc-8330639593ce",
"traceId": "6acb98e826734303b973c58755f55de4",
"evidence": "HTTP=200, code=A0422",
"checkedAt": "2026-08-08T07:11:58.312Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "阶梯区间断档安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "A0422",
"message": "阶梯必须从第1天开始且连续",
"requestId": "78582d72-1bf8-444a-af47-427308db0397",
"traceId": "20e3f53de2fe428ab563423940689bb0",
"evidence": "HTTP=200, code=A0422",
"checkedAt": "2026-08-08T07:11:58.347Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "非9档整组提交安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "A0422",
"message": "客户费用必须包含仓租5档和退货仓储费4档",
"requestId": "6169b4b7-79a0-49f2-ab51-1fe81d379336",
"traceId": "2bf1aa5b899740fb89970d50a576aeb2",
"evidence": "HTTP=200, code=A0422",
"checkedAt": "2026-08-08T07:11:58.370Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "负向请求后回查客户费用配置",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "f04344b4-2333-4afd-a4dd-e2a1f66a3362, f512a82d-5265-4969-88b8-3a01faf9dfa3",
"traceId": "1b3ff4dbce264da0b7800459ba044d1f",
"evidence": "",
"checkedAt": "2026-08-08T07:11:58.412Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "所有非法整组请求均未造成部分更新",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "f04344b4-2333-4afd-a4dd-e2a1f66a3362, f512a82d-5265-4969-88b8-3a01faf9dfa3",
"traceId": "1b3ff4dbce264da0b7800459ba044d1f",
"evidence": "version=0, tiers=9",
"checkedAt": "2026-08-08T07:11:58.412Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "整组修改客户海外仓费用",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "0521be7d-96e2-469a-bdbf-a9f8629089c4, 44ce2d0c-dc1b-4c61-a232-00276dc949c7",
"traceId": "e22d6bf1d9624ca7b7b1fe7d8385231b",
"evidence": "tenantId=2085987359342055425, tiers=9",
"checkedAt": "2026-08-08T07:11:58.471Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "回查整组修改后的客户费用",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "ff34e234-c735-4a27-8e38-25e5f11cb7cf, db0d76cd-8238-4029-8468-a7c4e1ff286b",
"traceId": "a9ac9c3a79f24a09884e1c4d9749ad44",
"evidence": "",
"checkedAt": "2026-08-08T07:11:58.505Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "修改后币种、价格、9档及版本整体回显",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "ff34e234-c735-4a27-8e38-25e5f11cb7cf, db0d76cd-8238-4029-8468-a7c4e1ff286b",
"traceId": "a9ac9c3a79f24a09884e1c4d9749ad44",
"evidence": "currency=EUR, version=1, tiers=9",
"checkedAt": "2026-08-08T07:11:58.506Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "编辑后再次按客户查询唯一规则",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/overseas-storage-fees?page=1&size=10&customerId=2085987359342055425"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "d0067f5e-31c0-4d36-924f-b41f0b08187f, ab4823c9-7b19-41f8-a134-fe7fa0d49b57",
"traceId": "0bafdbdba66544cbad51a08b74780369",
"evidence": "",
"checkedAt": "2026-08-08T07:11:58.535Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "重复初始化结果保持幂等且编辑值未被覆盖",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/overseas-storage-fees?page=1&size=10&customerId=2085987359342055425"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "d0067f5e-31c0-4d36-924f-b41f0b08187f, ab4823c9-7b19-41f8-a134-fe7fa0d49b57",
"traceId": "0bafdbdba66544cbad51a08b74780369",
"evidence": "total=1, ruleId=2085987359469387778, version=1",
"checkedAt": "2026-08-08T07:11:58.536Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "旧 version 整组保存返回并发冲突",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "A0409",
"message": "费用配置已被修改,请刷新后重试",
"requestId": "ae90d696-2b18-498f-9a0c-9abfab81174f",
"traceId": "280444f99fd84e0c8bf310cf2ea2bae5",
"evidence": "HTTP=200, code=A0409",
"checkedAt": "2026-08-08T07:11:58.573Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "冲突后回查费用配置",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "b56643b5-7c36-4898-a8dc-0d273d9d8c79, 8c9d56cb-b3dc-407c-90e3-867ef63e0dfd",
"traceId": "20ffdfb711d34f019bd8e55b780828af",
"evidence": "",
"checkedAt": "2026-08-08T07:11:58.616Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "冲突未造成部分更新",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "b56643b5-7c36-4898-a8dc-0d273d9d8c79, 8c9d56cb-b3dc-407c-90e3-867ef63e0dfd",
"traceId": "20ffdfb711d34f019bd8e55b780828af",
"evidence": "version=1, unchanged=true",
"checkedAt": "2026-08-08T07:11:58.616Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "恢复本轮客户费用原始配置",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "PUT",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "58df45db-66c2-43a9-bd33-24f70cf562eb, e13e8dca-762f-4293-8d0f-7de84a5da3c8",
"traceId": "2c0bc4985a164b3e8e5f7fbe9d35a761",
"evidence": "restore=initial",
"checkedAt": "2026-08-08T07:11:58.680Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "回查恢复后的客户费用",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "689eed47-a980-4dce-88a9-1b029dbf2049, d49bc4e7-6cd0-47ae-8a49-f994ede8aab7",
"traceId": "208d5b4525bb431f88bb06a86f93bb7d",
"evidence": "",
"checkedAt": "2026-08-08T07:11:58.720Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "恢复后9档、USD、免费档和无上限档一致",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/overseas-storage-fees"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "689eed47-a980-4dce-88a9-1b029dbf2049, d49bc4e7-6cd0-47ae-8a49-f994ede8aab7",
"traceId": "208d5b4525bb431f88bb06a86f93bb7d",
"evidence": "currency=USD, version=2, tiers=9",
"checkedAt": "2026-08-08T07:11:58.720Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "错误客户 ID 查询安全失败",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "GET",
"path": "/api/platform/v1/iam/tenants/999999999999999999/overseas-storage-fees"
},
"http": 200,
"code": "A0404",
"message": "海外仓费用不存在",
"requestId": "b094c65b-8a43-44d5-86a1-42df180d9af5",
"traceId": "2f9d44c21c0a4c549eadca74afdbdf34",
"evidence": "HTTP=200, code=A0404",
"checkedAt": "2026-08-08T07:11:58.748Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "清理费用隔离客户",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "POST",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/archive"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "7e1c4ef6-f609-4b64-bf91-357768e04b1f, 1a19c372-1148-4af1-a50f-f76869b56ef5",
"traceId": "06ffb8a032a54764b5e034e47ad3cba5",
"evidence": "tenantId=2085987359342055425",
"checkedAt": "2026-08-08T07:11:58.858Z"
},
{
"module": "overseas-storage-fees",
"scenario": "customer-overseas-storage-fee-lifecycle",
"name": "费用隔离客户清理后停用",
"passed": true,
"deploymentBlocker": false,
"request": {
"method": "POST",
"path": "/api/platform/v1/iam/tenants/2085987359342055425/archive"
},
"http": 200,
"code": "200",
"message": "success",
"requestId": "7e1c4ef6-f609-4b64-bf91-357768e04b1f, 1a19c372-1148-4af1-a50f-f76869b56ef5",
"traceId": "06ffb8a032a54764b5e034e47ad3cba5",
"evidence": "status=DISABLED",
"checkedAt": "2026-08-08T07:11:58.858Z"
}
]
}
File diff suppressed because it is too large Load Diff
+68
View File
@@ -0,0 +1,68 @@
# 后端全量回归
## 入口
在 `baiyue-firstleg` 根目录执行:
```powershell
.\scripts\backend-full-regression.ps1
```
## 前端全量回归
`frontend-full-regression.ps1` 只编排本地 Admin/Client 的 `pnpm test` 与 `pnpm typecheck`,不调用 Gateway、不部署、不连接数据库。
```powershell
.\scripts\frontend-full-regression.ps1 -DryRun
.\scripts\frontend-full-regression.ps1
.\scripts\frontend-full-regression.ps1 -SkipAdmin
.\scripts\frontend-full-regression.ps1 -SkipClient
```
脚本会检查 `pnpm` 和目标应用的 `package.json`;任一命令失败时立即透传退出码,不能同时跳过 Admin 和 Client。
脚本固定运行:
```text
mvn -f <仓库根>/pom.xml -pl order,firstleg -am test
```
这会先构建公共依赖,再运行 `order` 与 `firstleg` 的全部 Maven 测试。覆盖包括:
- 三期 Ocean Booking Controller/Service/Mapper/SQL 契约;
- 三期 Truck Appointment Controller/Service/Mapper 契约、状态机、乐观锁和幂等操作;
- 二期 `WAIT_SPLIT` 的 firstleg 查询、order 统一查询及筛选语义;
- 拣货/预装/装柜模型映射与既有 Smart Loading/Split 契约。
## Dry-run
```powershell
.\scripts\backend-full-regression.ps1 -DryRun
```
Dry-run 只检查根 `pom.xml` 和关键回归测试文件并打印 Maven 命令,不启动 Java 服务,不请求 Gateway,不连接或写入任何数据库。
## 现有相关脚本与边界
### 本地 MySQL 全量验收
正式入口是 `local-db-full-acceptance.ps1`,只允许连接本机 `127.0.0.1` 或 `localhost`,默认端口 `3306`;测试库 `8.163.30.128` 会立即以退出码 2 拒绝。脚本固定校验统一 SQL SHA-256 `E86BB8DE105E9B59736FFCD180CBA902F2E3F5032C7EFF2E3DC3B24B289360A0` 和 7803 行。
```powershell
.\local-db-full-acceptance.ps1 -DryRun
.\local-db-full-acceptance.ps1
```
Dry-run 只校验 SQL 输入并打印空库、复跑、两类 picking 旧结构、WAIT_SPLIT/IAM、海外仓旧结构、中断恢复和最终结构的执行顺序,不启动 MySQL、不写库。真实运行每阶段写入 `.codex-tmp/p23-04-acceptance/acceptance-run-*.log`,不使用 `--force`,任一 SQL 错误立即停止并透传退出码,最后执行 `verify-final.sql`。脚本不保存或传入数据库密码,使用本机默认认证环境。
仓库中 `phase2-test-env-acceptance.ps1`、`admin-test-env-acceptance.mjs`、`client-test-env-acceptance.mjs`、`export-center-test-env-acceptance.ps1` 等属于测试环境/API 验收脚本;它们可能读取或写入测试环境数据,不由本入口调用。`migrate-local-qualification-file.ps1`、`local-env.ps1` 等是环境/迁移辅助脚本,也不由本入口调用。
本入口不读取数据库凭据、不执行 MySQL、HTTP、迁移、DDL、INSERT/UPDATE/DELETE,也不启动 Nacos、Gateway 或业务服务。需要数据库矩阵时必须单独运行受控验收流程。
## 退出码
- `0`:dry-run 检查通过或 Maven 全量测试通过;
- `2`:仓库根 pom 或关键测试文件缺失,脚本未启动 Maven;
- 其他非零值:原样透传 Maven 失败退出码。
脚本不设置 `MAVEN_OPTS`、数据库 URL 或测试账号;环境参数仅为可选的 `$MavenCommand`(默认 `mvn`),用于指定本机 Maven 可执行文件。
+7 -1
View File
@@ -11,7 +11,7 @@ import process from 'node:process'
const DEFAULT_GATEWAY = 'http://8.163.30.128:9000'
const DEFAULT_USERNAME = 'platform_admin'
const DEFAULT_PASSWORD = 'ChangeMe123!'
const DEFAULT_MODULES = ['customers', 'overseas-storage-fees', 'system', 'master-data', 'orders', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments']
const DEFAULT_MODULES = ['customers', 'overseas-storage-fees', 'system', 'master-data', 'orders', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments', 'service-center-flow']
const WEIGHT_IMAGE_PATH = 'C:/Users/BY-OY/Desktop/上传图片包/测量图片/测量图片/orig_1cf27695-b2c3-44b3-98e3-c0be8b80f79a.JPG'
const CUSTOMER_FIELDS = [
'id', 'tenantCode', 'tenantName', 'status', 'legalName', 'legalRepresentative',
@@ -92,6 +92,7 @@ class AcceptanceRun {
this.scenarios = []
this.createdResources = {}
this.deploymentBlockers = []
this.coverageGaps = []
this.context = {}
this.token = ''
this.fatal = null
@@ -158,6 +159,10 @@ class AcceptanceRun {
return step.passed
}
addCoverageGap(module, scenario, name, reason, category = 'not-implemented') {
this.coverageGaps.push({ module, scenario, name, reason, category })
}
apiSuccess(module, name, response, evidence = '', options = {}) {
const passed = response.http === 200 && String(response.body?.code) === '200'
this.addStep(module, name, response, passed, evidence, options)
@@ -478,6 +483,7 @@ class AcceptanceRun {
success: this.fatal === null && failed === 0, fatal: this.fatal,
summary: { total: this.steps.length, passed: this.steps.length - failed, failed },
modules: this.modules, scenarios: this.scenarios, deploymentBlockers: this.deploymentBlockers,
coverageGaps: this.coverageGaps,
createdResources: this.createdResources,
excluded: [
{ scope: 'dashboard', reason: '首页为静态演示数据,不调用后端 API' },
@@ -5,6 +5,21 @@ function xlsxOk(response) {
&& response.bytes[0] === 0x50 && response.bytes[1] === 0x4b
}
async function uploadTemplate(context, path, bytes, fileName) {
const form = new FormData()
form.append('file', new Blob([bytes], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }), fileName)
try {
const response = await fetch(`${context.options.gateway}${path}`, {
method: 'POST', headers: { Accept: 'application/json', Authorization: `Bearer ${context.token}` },
body: form, signal: AbortSignal.timeout(60_000),
})
const raw = await response.text(); let body = null
try { body = raw ? JSON.parse(raw) : null } catch { /* bounded evidence only */ }
return { method: 'POST', path, http: response.status, body, raw: body === null ? raw.slice(0, 500) : null,
requestId: body?.requestId || response.headers.get('x-request-id'), traceId: body?.traceId || response.headers.get('x-trace-id') }
} catch (error) { return { method: 'POST', path, http: 0, body: null, raw: String(error), requestId: null, traceId: null } }
}
export async function run(context) {
const module = 'master-data'
const suffix = context.marker.replace(/\D/g, '').slice(-12)
@@ -150,8 +165,30 @@ export async function run(context) {
const response = await context.requestBinary('GET', path)
context.addStep(module, `下载${name}导入模板`, response, xlsxOk(response),
`bytes=${response.bytes?.length ?? 0}, contentType=${response.contentType || 'missing'}`)
if (xlsxOk(response)) {
const importPath = `${path.replace('/import-template', '/import')}?tenantId=0&dryRun=true`
const first = await uploadTemplate(context, importPath, response.bytes, `${context.marker}-${name}.xlsx`)
const firstData = first.body?.data
context.addStep(module, `${name}合法模板 dryRun 导入契约`, first,
first.http === 200 && String(first.body?.code) === '200',
`HTTP=${first.http}, code=${first.body?.code ?? 'missing'}, rows=${firstData?.totalRows ?? 'n/a'}, parserSuccess=${firstData?.success ?? 'n/a'}`,
{ scenario: 'valid-import', deploymentBlocker: first.http === 404 || first.http === 405 || first.http === 503 })
const second = await uploadTemplate(context, importPath, response.bytes, `${context.marker}-${name}-replay.xlsx`)
context.addStep(module, `${name}合法模板重复 dryRun 幂等结果稳定`, second,
second.http === 200 && String(second.body?.code) === '200'
&& second.body?.data?.totalRows === firstData?.totalRows && second.body?.data?.importedRows === firstData?.importedRows,
`firstRows=${firstData?.totalRows ?? 'n/a'}, replayRows=${second.body?.data?.totalRows ?? 'n/a'}`,
{ scenario: 'valid-import', deploymentBlocker: second.http === 404 || second.http === 405 || second.http === 503 })
context.addCoverageGap(module, 'valid-import', `${name}带数据行的合法 XLSX 写入`,
'当前验收运行时无 XLSX 写入依赖;仅上传真实服务模板做解析/重复结果稳定性验证,未伪造数据行或写入主数据', 'runtime-dependency')
} else {
context.addCoverageGap(module, 'valid-import', `${name}合法 XLSX 导入`, '模板下载契约不可用,无法安全取得真实字段模板;未构造伪 XLSX', 'deployment')
}
}
context.addCoverageGap(module, 'optimistic-lock', '仓库/托盘/快递/往来单位旧 version 冲突',
'真实 DTO 未声明 version/expectedVersion 字段,不能伪造并发冲突断言;库位版本已由 supplemental-boundaries 覆盖', 'contract-gap')
const disableWarehouseResponse = await context.request('PATCH', `/api/platform/v1/warehouse-admin/${warehouseId}/status?tenantId=0&status=INACTIVE`)
context.apiSuccess(module, '停用测试仓库', disableWarehouseResponse)
}
@@ -0,0 +1,45 @@
const MODULE = 'service-center-flow'
const OPTIONS = { scenario: 'service-center-cross-flow', deploymentBlocker: true }
const OCEAN = '/api/platform/v1/ocean-bookings'
const TRUCK = '/api/platform/v1/truck-appointments'
function id(value) { return value == null ? '' : String(value) }
function codeOf(response) { return response?.body?.code == null ? 'missing' : String(response.body.code) }
export async function run(context) {
const ocean = context.createdResources?.oceanBooking
if (!ocean?.id || ocean.finalStatus !== 'RELEASED') {
context.addStep(MODULE, '跨服务链路前置订舱终态可用', null, false,
'本轮海运订舱模块未返回 RELEASED 隔离记录,未伪造关联拖车预约', OPTIONS)
return
}
const suffix = context.marker.replace(/\D/g, '').slice(-10)
const customerId = Number(context.context?.customerId || 10001)
const payload = {
outboundOrderId: null, oceanBookingId: Number(ocean.id), containerBookingId: null, loadingPlanId: null,
warehouseId: Number(context.createdResources?.warehouse?.id || context.context?.warehouseId || 193000000000000001),
customerId, customerName: `服务中心链路客户-${suffix}`, carrierId: null, carrierName: `链路承运商-${suffix}`,
fleetContact: '链路车队联系人', fleetPhone: '13800000007', driverName: '链路司机', driverPhone: '13800000008',
driverIdCard: null, vehicleNo: `粤D-${suffix.slice(-6)}`, storeName: `链路门店-${suffix}`, storeContact: '链路联系人',
storePhone: '13800000009', storeAddress: '深圳市南山区链路地址', storeCity: '深圳市', storeZipcode: '518000',
packageCount: 1, grossWeightKg: 1, volumeCbm: 0.1, palletCount: 0, poNo: `FLOW-PO-${suffix}`,
plannedLoadingAt: '2026-09-03T08:00:00', transitMinutes: 60, payableAmount: 1, receivableAmount: 2,
currency: 'RMB', remark: context.marker, expectedVersion: 0,
}
const response = await context.request('POST', TRUCK, payload)
const appointment = context.apiSuccess(MODULE, '创建关联海运订舱的拖车预约', response, `oceanBookingId=${ocean.id}`, OPTIONS)
const appointmentId = id(appointment?.id)
context.assert(MODULE, '拖车预约关联订舱 ID、版本和审计字段完整', response,
Boolean(appointmentId) && id(appointment?.oceanBookingId) === id(ocean.id) && Number.isFinite(Number(appointment?.version))
&& Boolean(appointment?.createdAt || appointment?.createdBy || appointment?.operatorName),
`appointmentId=${appointmentId || 'missing'}, oceanBookingId=${appointment?.oceanBookingId || 'missing'}, version=${appointment?.version ?? 'missing'}`, OPTIONS)
if (!appointmentId) return
context.createdResources.serviceCenterFlow = { appointmentId, oceanBookingId: ocean.id, marker: context.marker, finalStatus: 'CANCELLED' }
const action = await context.request('POST', `${TRUCK}/${appointmentId}/cancel`, { expectedVersion: Number(appointment.version), reason: context.marker })
const cancelled = context.apiSuccess(MODULE, '取消跨服务链路拖车预约', action, '', OPTIONS)
context.assert(MODULE, '跨服务链路安全终态为 CANCELLED', action, cancelled?.status === 'CANCELLED', `status=${cancelled?.status || 'missing'}`, OPTIONS)
const detail = await context.request('GET', `${TRUCK}/${appointmentId}`)
const final = context.apiSuccess(MODULE, '回查跨服务链路审计历史', detail, '', OPTIONS)
context.assert(MODULE, '跨服务链路状态历史存在', detail, Array.isArray(final?.histories) && final.histories.length >= 1, `histories=${final?.histories?.length ?? 'n/a'}`, OPTIONS)
}
@@ -89,8 +89,8 @@ export async function run(context) {
// Public APIs do not expose a safe way to create a real occupied pallet/
// location association without an inbound allocation fixture. Keep this as
// an explicit automation gap instead of claiming a deletion-protection pass.
context.addStep(MODULE, '托盘/库位真实占用删除保护自动化缺口', null, false,
'当前公共验收 API 未提供可安全构造并释放占用关联的独立 fixture;未操作历史资源', OPTIONS)
context.addCoverageGap(MODULE, 'resource-occupancy', '托盘/库位真实占用删除保护',
'当前公共验收 API 未提供可安全构造并释放占用关联的独立 fixture;未操作历史资源', 'no-public-fixture-api')
// Import boundary probes use only in-memory bytes and dry-run where supported.
const bogusXlsx = new Uint8Array([0x50, 0x4b, 0x03, 0x04, 0x00, 0x00])
+54
View File
@@ -0,0 +1,54 @@
[CmdletBinding()]
param(
[switch]$DryRun,
[string]$MavenCommand = "mvn"
)
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$pom = Join-Path $repoRoot "pom.xml"
$requiredTests = @(
"firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/controller/PlatformOceanBookingControllerContractTest.java",
"firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/service/OceanBookingServiceTest.java",
"firstleg/src/test/java/com/baiyue/firstleg/repository/TruckAppointmentMapperContractTest.java",
"firstleg/src/test/java/com/baiyue/firstleg/service/TruckAppointmentServiceTest.java",
"firstleg/src/test/java/com/baiyue/firstleg/controller/TruckAppointmentControllerContractTest.java",
"firstleg/src/test/java/com/baiyue/firstleg/service/UnifiedInboundQueryServiceTest.java",
"order/src/test/java/com/baiyue/order/service/UnifiedOrderQueryServiceTest.java"
)
$mavenArgs = @("-f", $pom, "-pl", "order,firstleg", "-am", "test")
Write-Host "Backend regression root: $repoRoot"
Write-Host "Scope: order + firstleg Maven reactor (includes all module tests)"
Write-Host "Database/API access: none"
if (-not (Test-Path -LiteralPath $pom)) {
Write-Error "Missing Maven pom: $pom"
exit 2
}
$missing = @($requiredTests | Where-Object { -not (Test-Path -LiteralPath (Join-Path $repoRoot $_)) })
if ($missing.Count -gt 0) {
Write-Error ("Missing required regression coverage:`n" + ($missing -join "`n"))
exit 2
}
Write-Host ("Command: {0} {1}" -f $MavenCommand, ($mavenArgs -join " "))
if ($DryRun) {
Write-Host "DRY-RUN PASS: prerequisites and coverage files are present; Maven was not started."
exit 0
}
Push-Location $repoRoot
try {
& $MavenCommand @mavenArgs
$code = $LASTEXITCODE
} finally {
Pop-Location
}
if ($code -ne 0) {
Write-Error "Maven regression failed with exit code $code"
exit $code
}
Write-Host "Backend regression PASS"
exit 0
+40
View File
@@ -0,0 +1,40 @@
[CmdletBinding()]
param(
[switch]$DryRun,
[switch]$SkipAdmin,
[switch]$SkipClient
)
$ErrorActionPreference = 'Stop'
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '../..')).Path
function Invoke-FrontendStep {
param([string]$Name, [string]$Directory, [string]$Task)
$display = "pnpm --dir `"$Directory`" $Task"
Write-Host " [$Name] $display"
if ($DryRun) { return }
& pnpm --dir $Directory $Task
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
if ($SkipAdmin -and $SkipClient) { Write-Error 'At least one of Admin or Client must be selected.'; exit 2 }
if (-not (Get-Command pnpm -ErrorAction SilentlyContinue)) { Write-Error 'pnpm was not found on PATH.'; exit 2 }
$targets = @()
if (-not $SkipAdmin) { $targets += @{ Name = 'Admin'; Directory = Join-Path $repoRoot 'apps/admin-web' } }
if (-not $SkipClient) { $targets += @{ Name = 'Client'; Directory = Join-Path $repoRoot 'apps/client-web' } }
Write-Host 'Frontend full regression (local only)'
Write-Host "Repository: $repoRoot"
Write-Host "Mode: $(if ($DryRun) { 'DryRun' } else { 'Execute' })"
Write-Host 'Gateway/database/deployment: not used'
foreach ($target in $targets) {
$packageJson = Join-Path $target.Directory 'package.json'
if (-not (Test-Path -LiteralPath $packageJson)) { Write-Error "$($target.Name) package.json not found: $packageJson"; exit 2 }
Write-Host "[$($target.Name)]"
Invoke-FrontendStep 'test' $target.Directory 'test'
Invoke-FrontendStep 'typecheck' $target.Directory 'typecheck'
}
Write-Host 'Frontend regression completed.'
+65
View File
@@ -0,0 +1,65 @@
param(
[switch]$DryRun,
[Alias('Host')][string]$TargetHost = '127.0.0.1',
[int]$Port = 3306,
[string]$InputPath,
[string]$MysqlExe = 'C:\Program Files\MySQL\MySQL Server 8.4\bin\mysql.exe',
[string]$User = 'root'
)
$ErrorActionPreference = 'Stop'
$Repo = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$Workspace = (Resolve-Path (Join-Path $Repo '..')).Path
$Acceptance = Join-Path $Workspace '.codex-tmp\p23-04-acceptance'
$ExpectedSha256 = 'E86BB8DE105E9B59736FFCD180CBA902F2E3F5032C7EFF2E3DC3B24B289360A0'
$ExpectedLines = 7803
if ($TargetHost -notin @('127.0.0.1', 'localhost')) {
[Console]::Error.WriteLine('Refusing non-local database; only 127.0.0.1 or localhost is allowed.')
exit 2
}
if ([string]::IsNullOrWhiteSpace($InputPath)) {
$InputPath = (Get-ChildItem (Join-Path $Repo 'deploy\sql') -Filter '*MySQL8.sql' -File |
Where-Object { $_.Length -gt 100000 } | Select-Object -First 1).FullName
}
if (-not (Test-Path -LiteralPath $InputPath)) {
[Console]::Error.WriteLine("InputPath does not exist: $InputPath")
exit 2
}
$actual = ((Get-FileHash -Algorithm SHA256 -LiteralPath $InputPath).Hash).Trim().ToUpperInvariant()
$lines = [System.IO.File]::ReadAllLines($InputPath).Length
if ($actual -ne $ExpectedSha256 -or $lines -ne $ExpectedLines) {
[Console]::Error.WriteLine("Unified SQL snapshot mismatch: $actual/$lines")
exit 2
}
$sequence = @(
'empty: base.sql', 'replay: base.sql + input.sql + input.sql',
'smart-old: fixture-smart-old.sql', 'inbound-old: fixture-inbound-old.sql',
'waitsplit-iam: fixture-test-shape.sql', 'overseas-old: fixture-overseas-old.sql',
'overseas-missing-objects: fixture-overseas-missing-objects.sql',
'interrupted-recovery: fixture-interrupted.sql', 'final-structure: verify-final.sql')
Write-Output "SQL SHA-256: $actual / lines: $lines"
Write-Output "Target: $TargetHost`:$Port"
Write-Output 'Fixture order:'
$sequence | ForEach-Object { Write-Output " $_" }
if ($DryRun) { Write-Output 'DRY-RUN PASS (mysql not started; no database writes)'; exit 0 }
$run = 0
function Invoke-Stage([string]$Name, [string]$File) {
$script = Join-Path $Acceptance $File
$log = Join-Path $Acceptance ("acceptance-run-{0:D2}-{1}.log" -f $script:run, $Name)
$script:run++
if (-not (Test-Path -LiteralPath $script)) { [Console]::Error.WriteLine("Script does not exist: $script"); return 2 }
& $MysqlExe '--protocol=TCP' "--host=$TargetHost" "--port=$Port" "--user=$User" '--database=mysql' '--batch' ("--execute=source " + $script.Replace('\','/')) *> $log
return $LASTEXITCODE
}
$calls = @(
@('empty','base.sql'), @('replay-reset','base.sql'), @('replay-1','input.sql'), @('replay-2','input.sql'),
@('smart-old','fixture-smart-old.sql'), @('inbound-old','fixture-inbound-old.sql'), @('waitsplit-iam','fixture-test-shape.sql'),
@('overseas-old','fixture-overseas-old.sql'), @('overseas-missing','fixture-overseas-missing-objects.sql'),
@('interrupted','fixture-interrupted.sql'), @('final-verify','verify-final.sql'))
foreach ($call in $calls) { $code = Invoke-Stage $call[0] $call[1]; if ($code -ne 0) { exit $code } }
Write-Output 'FULL LOCAL ACCEPTANCE PASS'
exit 0