优化
This commit is contained in:
+159
-16
@@ -4001,22 +4001,123 @@ CREATE TABLE IF NOT EXISTS wh_overseas_storage_fee_tier (
|
||||
) ENGINE=InnoDB COMMENT='海外仓阶梯仓储费明细';
|
||||
|
||||
-- 兼容已存在的旧版海外仓费用表:补齐新字段及免费档/无上限档约束。
|
||||
ALTER TABLE wh_overseas_storage_fee_rule
|
||||
ADD COLUMN IF NOT EXISTS initialization_key VARCHAR(191) NULL COMMENT '初始化幂等键';
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
ADD COLUMN IF NOT EXISTS fee_type VARCHAR(32) NOT NULL DEFAULT 'STORAGE' COMMENT '费用类型',
|
||||
ADD COLUMN IF NOT EXISTS billing_unit VARCHAR(16) NOT NULL DEFAULT 'CBM' COMMENT '计费单位';
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
MODIFY COLUMN end_day SMALLINT UNSIGNED NULL COMMENT '结束天,含边界;NULL表示以上',
|
||||
MODIFY COLUMN unit_price DECIMAL(18,6) NOT NULL COMMENT '仓储费标准,可为0';
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
DROP CHECK chk_overseas_fee_tier_days,
|
||||
DROP CHECK chk_overseas_fee_tier_price,
|
||||
ADD CONSTRAINT chk_overseas_fee_tier_days CHECK (start_day >= 1 AND (end_day IS NULL OR end_day >= start_day)),
|
||||
ADD CONSTRAINT chk_overseas_fee_tier_price CHECK (unit_price >= 0);
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
DROP INDEX uk_overseas_fee_tier_start,
|
||||
ADD UNIQUE KEY uk_overseas_fee_tier_start (rule_id, fee_type, active_start_day);
|
||||
DROP PROCEDURE IF EXISTS migrate_overseas_storage_fee_schema;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE migrate_overseas_storage_fee_schema()
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'wh_overseas_storage_fee_rule'
|
||||
AND column_name = 'initialization_key'
|
||||
) THEN
|
||||
SET @overseas_fee_ddl =
|
||||
'ALTER TABLE wh_overseas_storage_fee_rule ADD COLUMN initialization_key VARCHAR(191) NULL COMMENT ''初始化幂等键''';
|
||||
PREPARE overseas_fee_statement FROM @overseas_fee_ddl;
|
||||
EXECUTE overseas_fee_statement;
|
||||
DEALLOCATE PREPARE overseas_fee_statement;
|
||||
END IF;
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND column_name = 'fee_type'
|
||||
) THEN
|
||||
SET @overseas_fee_ddl =
|
||||
'ALTER TABLE wh_overseas_storage_fee_tier ADD COLUMN fee_type VARCHAR(32) NOT NULL DEFAULT ''STORAGE'' COMMENT ''费用类型''';
|
||||
PREPARE overseas_fee_statement FROM @overseas_fee_ddl;
|
||||
EXECUTE overseas_fee_statement;
|
||||
DEALLOCATE PREPARE overseas_fee_statement;
|
||||
END IF;
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND column_name = 'billing_unit'
|
||||
) THEN
|
||||
SET @overseas_fee_ddl =
|
||||
'ALTER TABLE wh_overseas_storage_fee_tier ADD COLUMN billing_unit VARCHAR(16) NOT NULL DEFAULT ''CBM'' COMMENT ''计费单位''';
|
||||
PREPARE overseas_fee_statement FROM @overseas_fee_ddl;
|
||||
EXECUTE overseas_fee_statement;
|
||||
DEALLOCATE PREPARE overseas_fee_statement;
|
||||
END IF;
|
||||
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
MODIFY COLUMN end_day SMALLINT UNSIGNED NULL COMMENT '结束天,含边界;NULL表示以上',
|
||||
MODIFY COLUMN unit_price DECIMAL(18,6) NOT NULL COMMENT '仓储费标准,可为0';
|
||||
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM wh_overseas_storage_fee_tier
|
||||
WHERE start_day < 1
|
||||
OR (end_day IS NOT NULL AND end_day < start_day)
|
||||
OR unit_price < 0
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = 'Warehouse 迁移失败:海外仓费用阶梯存在非法天数或负价格';
|
||||
END IF;
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM wh_overseas_storage_fee_tier
|
||||
WHERE active_start_day IS NOT NULL
|
||||
GROUP BY rule_id, fee_type, active_start_day
|
||||
HAVING COUNT(*) > 1
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = 'Warehouse 迁移失败:海外仓费用阶梯目标唯一键存在重复数据';
|
||||
END IF;
|
||||
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_schema = DATABASE()
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND constraint_name = 'chk_overseas_fee_tier_days'
|
||||
AND constraint_type = 'CHECK'
|
||||
) THEN
|
||||
ALTER TABLE wh_overseas_storage_fee_tier DROP CHECK chk_overseas_fee_tier_days;
|
||||
END IF;
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_schema = DATABASE()
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND constraint_name = 'chk_overseas_fee_tier_price'
|
||||
AND constraint_type = 'CHECK'
|
||||
) THEN
|
||||
ALTER TABLE wh_overseas_storage_fee_tier DROP CHECK chk_overseas_fee_tier_price;
|
||||
END IF;
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
ADD CONSTRAINT chk_overseas_fee_tier_days
|
||||
CHECK (start_day >= 1 AND (end_day IS NULL OR end_day >= start_day)),
|
||||
ADD CONSTRAINT chk_overseas_fee_tier_price CHECK (unit_price >= 0);
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM (
|
||||
SELECT index_name, non_unique,
|
||||
GROUP_CONCAT(column_name ORDER BY seq_in_index SEPARATOR ',') AS index_columns
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND index_name = 'uk_overseas_fee_tier_start'
|
||||
GROUP BY index_name, non_unique
|
||||
) existing_index
|
||||
WHERE non_unique = 0
|
||||
AND index_columns = 'rule_id,fee_type,active_start_day'
|
||||
) THEN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND index_name = 'uk_overseas_fee_tier_start'
|
||||
) THEN
|
||||
ALTER TABLE wh_overseas_storage_fee_tier DROP INDEX uk_overseas_fee_tier_start;
|
||||
END IF;
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
ADD UNIQUE KEY uk_overseas_fee_tier_start (rule_id, fee_type, active_start_day);
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL migrate_overseas_storage_fee_schema();
|
||||
DROP PROCEDURE IF EXISTS migrate_overseas_storage_fee_schema;
|
||||
|
||||
USE baiyue_iam;
|
||||
INSERT INTO iam_permission (
|
||||
@@ -4066,12 +4167,54 @@ BEGIN
|
||||
END IF;
|
||||
IF (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||||
WHERE table_schema = 'baiyue_warehouse'
|
||||
AND table_name IN ('wh_overseas_storage_fee_rule', 'wh_overseas_storage_fee_tier')
|
||||
AND index_name IN ('uk_overseas_fee_active_customer', 'idx_overseas_fee_page',
|
||||
'idx_overseas_fee_customer_name', 'uk_overseas_fee_tier_start',
|
||||
'idx_overseas_fee_tier_rule')) <> 5 THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '统一迁移失败:海外仓阶梯费用索引未完整创建';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = 'baiyue_warehouse'
|
||||
AND table_name = 'wh_overseas_storage_fee_rule'
|
||||
AND column_name = 'initialization_key'
|
||||
AND LOWER(column_type) = 'varchar(191)'
|
||||
AND is_nullable = 'YES' AND column_default IS NULL) <> 1
|
||||
OR (SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = 'baiyue_warehouse'
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND ((column_name = 'fee_type' AND LOWER(column_type) = 'varchar(32)'
|
||||
AND is_nullable = 'NO' AND column_default = 'STORAGE')
|
||||
OR (column_name = 'billing_unit' AND LOWER(column_type) = 'varchar(16)'
|
||||
AND is_nullable = 'NO' AND column_default = 'CBM')
|
||||
OR (column_name = 'end_day' AND LOWER(column_type) = 'smallint unsigned'
|
||||
AND is_nullable = 'YES' AND column_default IS NULL)
|
||||
OR (column_name = 'unit_price' AND LOWER(column_type) = 'decimal(18,6)'
|
||||
AND is_nullable = 'NO'))) <> 4 THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '统一迁移失败:海外仓阶梯费用字段签名不正确';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*)
|
||||
FROM (
|
||||
SELECT non_unique,
|
||||
GROUP_CONCAT(column_name ORDER BY seq_in_index SEPARATOR ',') AS index_columns
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = 'baiyue_warehouse'
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND index_name = 'uk_overseas_fee_tier_start'
|
||||
GROUP BY index_name, non_unique
|
||||
) exact_index
|
||||
WHERE non_unique = 0
|
||||
AND index_columns = 'rule_id,fee_type,active_start_day') <> 1
|
||||
OR (SELECT COUNT(*) FROM information_schema.table_constraints
|
||||
WHERE constraint_schema = 'baiyue_warehouse'
|
||||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||||
AND constraint_type = 'CHECK'
|
||||
AND constraint_name IN ('chk_overseas_fee_tier_days',
|
||||
'chk_overseas_fee_tier_price')) <> 2 THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '统一迁移失败:海外仓阶梯费用唯一键或 CHECK 结构不正确';
|
||||
END IF;
|
||||
IF (SELECT COUNT(DISTINCT permission_code) FROM baiyue_iam.iam_permission
|
||||
WHERE tenant_id = 0 AND status = 'ACTIVE' AND deleted = 0
|
||||
AND permission_code IN (
|
||||
|
||||
@@ -0,0 +1,612 @@
|
||||
{
|
||||
"reportName": "后台管理测试环境 HTTP API 验收",
|
||||
"marker": "ADMIN-API-20260808054354368",
|
||||
"gateway": "http://8.163.30.128:9000",
|
||||
"generatedAt": "2026-08-08T05:43:55.898Z",
|
||||
"startedAt": "2026-08-08T05:43:54.368Z",
|
||||
"success": false,
|
||||
"fatal": null,
|
||||
"summary": {
|
||||
"total": 27,
|
||||
"passed": 15,
|
||||
"failed": 12
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"name": "orders",
|
||||
"startedAt": "2026-08-08T05:43:54.747Z",
|
||||
"finishedAt": "2026-08-08T05:43:55.309Z",
|
||||
"success": false,
|
||||
"passed": 7,
|
||||
"failed": 5,
|
||||
"fatal": "缺少创建测试订单所需的可用渠道"
|
||||
},
|
||||
{
|
||||
"name": "inbound-weight-ocr",
|
||||
"startedAt": "2026-08-08T05:43:55.310Z",
|
||||
"finishedAt": "2026-08-08T05:43:55.373Z",
|
||||
"success": false,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"fatal": null
|
||||
},
|
||||
{
|
||||
"name": "orders-inbound-measurement-fixture",
|
||||
"startedAt": "2026-08-08T05:43:55.373Z",
|
||||
"finishedAt": "2026-08-08T05:43:55.896Z",
|
||||
"success": false,
|
||||
"passed": 7,
|
||||
"failed": 5,
|
||||
"fatal": "缺少创建测试订单所需的可用渠道"
|
||||
},
|
||||
{
|
||||
"name": "inbound-measurement",
|
||||
"startedAt": "2026-08-08T05:43:55.897Z",
|
||||
"finishedAt": "2026-08-08T05:43:55.898Z",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"name": "mojia-carton-measurement",
|
||||
"module": "inbound-measurement",
|
||||
"success": false,
|
||||
"deploymentBlocked": true,
|
||||
"passed": 0,
|
||||
"failed": 1
|
||||
}
|
||||
],
|
||||
"deploymentBlockers": [
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": "isolated-order-chain",
|
||||
"name": "Admin 查询客户固定箱型",
|
||||
"evidence": "tenantId=10001",
|
||||
"code": null,
|
||||
"requestId": "369dc88c-363",
|
||||
"traceId": null
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": "isolated-order-chain",
|
||||
"name": "固定四箱型顺序、内径及每边加2cm正确",
|
||||
"evidence": "types=missing",
|
||||
"code": null,
|
||||
"requestId": "369dc88c-363",
|
||||
"traceId": null
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": "isolated-order-chain",
|
||||
"name": "Admin 查询客户固定箱型",
|
||||
"evidence": "tenantId=10001",
|
||||
"code": null,
|
||||
"requestId": "369dc88c-371",
|
||||
"traceId": null
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": "isolated-order-chain",
|
||||
"name": "固定四箱型顺序、内径及每边加2cm正确",
|
||||
"evidence": "types=missing",
|
||||
"code": null,
|
||||
"requestId": "369dc88c-371",
|
||||
"traceId": null
|
||||
},
|
||||
{
|
||||
"module": "inbound-measurement",
|
||||
"scenario": "mojia-carton-measurement",
|
||||
"name": "取得隔离的待测量入库单",
|
||||
"evidence": "orders 模块未提供 marker=ADMIN-API-20260808054354368-MOJIA 的 inboundId/tenantId;为避免修改随机历史订单,本模块停止写入",
|
||||
"code": null,
|
||||
"requestId": null,
|
||||
"traceId": null
|
||||
}
|
||||
],
|
||||
"createdResources": {
|
||||
"orchestrationFixtures": {
|
||||
"inboundMeasurement": {
|
||||
"marker": "ADMIN-API-20260808054354368-MOJIA",
|
||||
"failed": 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": "265addfa-988b-48a8-8c80-5e69c2024b17, 675070cb-a2d6-4eb1-898e-c1e4c0d4354b",
|
||||
"traceId": "4dd7905ad59341619a7094372897f3c5",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:54.746Z"
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": null,
|
||||
"name": "Client 测试账号登录",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/auth/login"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "d58ac1e6-0e1b-4f36-83a9-94b267ee2261, 4d853cc1-fb5a-472e-b969-80258d453ce0",
|
||||
"traceId": "efcdac52e87d4777a18077b7b70b2181",
|
||||
"evidence": "username=merchant_admin",
|
||||
"checkedAt": "2026-08-08T05:43:55.097Z"
|
||||
},
|
||||
{
|
||||
"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": "102db2f5-4d47-4eb3-bb37-0740b445bc4d, 86f76b5d-2537-4bf3-a521-5af39a70d9b3",
|
||||
"traceId": "1c6762dc4dbd4e33ba9078ab8854ac35",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:55.145Z"
|
||||
},
|
||||
{
|
||||
"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": "102db2f5-4d47-4eb3-bb37-0740b445bc4d, 86f76b5d-2537-4bf3-a521-5af39a70d9b3",
|
||||
"traceId": "1c6762dc4dbd4e33ba9078ab8854ac35",
|
||||
"evidence": "tenantId=10001, status=ACTIVE",
|
||||
"checkedAt": "2026-08-08T05:43:55.145Z"
|
||||
},
|
||||
{
|
||||
"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": "369dc88c-363",
|
||||
"traceId": null,
|
||||
"evidence": "tenantId=10001",
|
||||
"checkedAt": "2026-08-08T05:43:55.168Z"
|
||||
},
|
||||
{
|
||||
"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": "369dc88c-363",
|
||||
"traceId": null,
|
||||
"evidence": "types=missing",
|
||||
"checkedAt": "2026-08-08T05:43:55.168Z"
|
||||
},
|
||||
{
|
||||
"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": "5df3d448-b678-42e7-a872-8cad35fdb1bf, 9b4309fd-f21e-4390-b8d5-d3e3b1dbfdc3",
|
||||
"traceId": "14295af99925417c8cac9ba5017e5382",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:55.209Z"
|
||||
},
|
||||
{
|
||||
"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": "5df3d448-b678-42e7-a872-8cad35fdb1bf, 9b4309fd-f21e-4390-b8d5-d3e3b1dbfdc3",
|
||||
"traceId": "14295af99925417c8cac9ba5017e5382",
|
||||
"evidence": "count=2",
|
||||
"checkedAt": "2026-08-08T05:43:55.209Z"
|
||||
},
|
||||
{
|
||||
"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": "c1b8251c-5430-4664-8e72-b57854c6f398, bc5c512f-f283-4fc8-96b3-0e387533f0ee",
|
||||
"traceId": "946b84ea56024e6cab7954005c917330",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:55.274Z"
|
||||
},
|
||||
{
|
||||
"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": "c1b8251c-5430-4664-8e72-b57854c6f398, bc5c512f-f283-4fc8-96b3-0e387533f0ee",
|
||||
"traceId": "946b84ea56024e6cab7954005c917330",
|
||||
"evidence": "count=200",
|
||||
"checkedAt": "2026-08-08T05:43:55.274Z"
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": null,
|
||||
"name": "Client 查询可用计费渠道",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=193000000000200001"
|
||||
},
|
||||
"http": 503,
|
||||
"code": null,
|
||||
"message": null,
|
||||
"requestId": "b399112a-366",
|
||||
"traceId": null,
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:55.309Z"
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": null,
|
||||
"name": "存在带计价税制的可用渠道",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=193000000000200001"
|
||||
},
|
||||
"http": 503,
|
||||
"code": null,
|
||||
"message": null,
|
||||
"requestId": "b399112a-366",
|
||||
"traceId": null,
|
||||
"evidence": "count=n/a",
|
||||
"checkedAt": "2026-08-08T05:43:55.309Z"
|
||||
},
|
||||
{
|
||||
"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-08T05:43:55.309Z"
|
||||
},
|
||||
{
|
||||
"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-20260808054354368, 未找到本轮创建且状态为 WAIT_MEASURE/MEASURING/MEASURE_REJECTED 的入库单",
|
||||
"checkedAt": "2026-08-08T05:43:55.373Z"
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": null,
|
||||
"name": "Client 测试账号登录",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/auth/login"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "fa0f753e-136e-470f-8739-a373d982e200, b5f2f773-8ea7-43de-9670-dfc00fa0e5aa",
|
||||
"traceId": "49349e4462484491b675c501291f82aa",
|
||||
"evidence": "username=merchant_admin",
|
||||
"checkedAt": "2026-08-08T05:43:55.710Z"
|
||||
},
|
||||
{
|
||||
"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": "2362b5a0-5c32-4220-bb13-7d559054b9fa, 49cbadb9-dacb-498a-8586-566d492637aa",
|
||||
"traceId": "7be0232daa6243cbbf8be4cbe5278d29",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:55.748Z"
|
||||
},
|
||||
{
|
||||
"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": "2362b5a0-5c32-4220-bb13-7d559054b9fa, 49cbadb9-dacb-498a-8586-566d492637aa",
|
||||
"traceId": "7be0232daa6243cbbf8be4cbe5278d29",
|
||||
"evidence": "tenantId=10001, status=ACTIVE",
|
||||
"checkedAt": "2026-08-08T05:43:55.748Z"
|
||||
},
|
||||
{
|
||||
"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": "369dc88c-371",
|
||||
"traceId": null,
|
||||
"evidence": "tenantId=10001",
|
||||
"checkedAt": "2026-08-08T05:43:55.774Z"
|
||||
},
|
||||
{
|
||||
"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": "369dc88c-371",
|
||||
"traceId": null,
|
||||
"evidence": "types=missing",
|
||||
"checkedAt": "2026-08-08T05:43:55.774Z"
|
||||
},
|
||||
{
|
||||
"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": "04497201-663d-4373-b09a-a69da2390a3f, 04fc3e2e-081e-404a-a3e8-3f95e1d7fbb0",
|
||||
"traceId": "86e92a3f196b4671a9a6c583fdc7f2b8",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:55.812Z"
|
||||
},
|
||||
{
|
||||
"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": "04497201-663d-4373-b09a-a69da2390a3f, 04fc3e2e-081e-404a-a3e8-3f95e1d7fbb0",
|
||||
"traceId": "86e92a3f196b4671a9a6c583fdc7f2b8",
|
||||
"evidence": "count=2",
|
||||
"checkedAt": "2026-08-08T05:43:55.813Z"
|
||||
},
|
||||
{
|
||||
"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": "77abe5aa-19f3-41b2-8cff-b8ebc75344a1, 3df48e12-971d-4e45-9abb-8be49cb0c6e6",
|
||||
"traceId": "b79177fee34143a3b87da6ea489444a4",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:55.866Z"
|
||||
},
|
||||
{
|
||||
"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": "77abe5aa-19f3-41b2-8cff-b8ebc75344a1, 3df48e12-971d-4e45-9abb-8be49cb0c6e6",
|
||||
"traceId": "b79177fee34143a3b87da6ea489444a4",
|
||||
"evidence": "count=200",
|
||||
"checkedAt": "2026-08-08T05:43:55.866Z"
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": null,
|
||||
"name": "Client 查询可用计费渠道",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=193000000000200001"
|
||||
},
|
||||
"http": 503,
|
||||
"code": null,
|
||||
"message": null,
|
||||
"requestId": "b399112a-374",
|
||||
"traceId": null,
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:43:55.896Z"
|
||||
},
|
||||
{
|
||||
"module": "orders",
|
||||
"scenario": null,
|
||||
"name": "存在带计价税制的可用渠道",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=193000000000200001"
|
||||
},
|
||||
"http": 503,
|
||||
"code": null,
|
||||
"message": null,
|
||||
"requestId": "b399112a-374",
|
||||
"traceId": null,
|
||||
"evidence": "count=n/a",
|
||||
"checkedAt": "2026-08-08T05:43:55.896Z"
|
||||
},
|
||||
{
|
||||
"module": "orders-inbound-measurement-fixture",
|
||||
"scenario": null,
|
||||
"name": "模块执行",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": null,
|
||||
"http": null,
|
||||
"code": null,
|
||||
"message": null,
|
||||
"requestId": null,
|
||||
"traceId": null,
|
||||
"evidence": "缺少创建测试订单所需的可用渠道",
|
||||
"checkedAt": "2026-08-08T05:43:55.896Z"
|
||||
},
|
||||
{
|
||||
"module": "inbound-measurement",
|
||||
"scenario": "mojia-carton-measurement",
|
||||
"name": "取得隔离的待测量入库单",
|
||||
"passed": false,
|
||||
"deploymentBlocker": true,
|
||||
"request": null,
|
||||
"http": null,
|
||||
"code": null,
|
||||
"message": null,
|
||||
"requestId": null,
|
||||
"traceId": null,
|
||||
"evidence": "orders 模块未提供 marker=ADMIN-API-20260808054354368-MOJIA 的 inboundId/tenantId;为避免修改随机历史订单,本模块停止写入",
|
||||
"checkedAt": "2026-08-08T05:43:55.898Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
{
|
||||
"reportName": "后台管理测试环境 HTTP API 验收",
|
||||
"marker": "ADMIN-API-20260808053940400",
|
||||
"gateway": "http://8.163.30.128:9000",
|
||||
"generatedAt": "2026-08-08T05:39:42.568Z",
|
||||
"startedAt": "2026-08-08T05:39:40.400Z",
|
||||
"success": false,
|
||||
"fatal": null,
|
||||
"summary": {
|
||||
"total": 20,
|
||||
"passed": 14,
|
||||
"failed": 6
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"name": "supplemental-boundaries",
|
||||
"startedAt": "2026-08-08T05:39:40.888Z",
|
||||
"finishedAt": "2026-08-08T05:39:42.568Z",
|
||||
"success": false,
|
||||
"passed": 13,
|
||||
"failed": 6,
|
||||
"fatal": null
|
||||
}
|
||||
],
|
||||
"scenarios": [
|
||||
{
|
||||
"name": "admin-boundary-automation",
|
||||
"module": "supplemental-boundaries",
|
||||
"success": false,
|
||||
"deploymentBlocked": true,
|
||||
"passed": 13,
|
||||
"failed": 6
|
||||
}
|
||||
],
|
||||
"deploymentBlockers": [
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "托盘/库位真实占用删除保护自动化缺口",
|
||||
"evidence": "当前公共验收 API 未提供可安全构造并释放占用关联的独立 fixture;未操作历史资源",
|
||||
"code": null,
|
||||
"requestId": null,
|
||||
"traceId": null
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "船期价格非法状态边界响应正确",
|
||||
"evidence": "HTTP=503, code=missing",
|
||||
"code": null,
|
||||
"requestId": "8588809e-50",
|
||||
"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": "59e62f5a-a19a-481e-a9ef-4e7da203bad0, c17f4a61-3a7b-4c0d-9e81-3b4991ce3c40",
|
||||
"traceId": "190e5ac2b5be439b9d19e56d06eb0246",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:39:40.886Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "创建边界验收隔离仓库",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/platform/v1/warehouse-admin"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "9e6150b0-9cb4-4c92-b78b-9077c673108b, c864a23e-4c96-4e70-a8de-1aba95ee0b62",
|
||||
"traceId": "38f6146c977e4b62b3d8c1c1c63d4c9e",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:39:41.092Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "创建边界验收隔离库位",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/platform/v1/locations?tenantId=0"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "129bf3a7-b7fa-459e-86f6-def0a3c4b9ea, d547468c-6949-4fa9-b211-f2cfe54dc813",
|
||||
"traceId": "c2b5020e43884096840999908cc41d12",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:39:41.193Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "读取隔离库位版本",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/platform/v1/locations/2085964137512353794?tenantId=0&warehouseId=2085964137071951874"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "8f4bfb34-ba8c-446d-9306-9e46bf42a940, 8f51b111-9683-4ef6-9a67-b1cf1478105b",
|
||||
"traceId": "7174f84fdd8d4fbaa73728f35b2835a5",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:39:41.247Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "更新隔离库位以推进版本",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"path": "/api/platform/v1/locations/2085964137512353794?tenantId=0&warehouseId=2085964137071951874"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "1a2c4f72-7904-47b4-aedc-e4fad8fc439b, d4a63cbf-2fb1-4ce4-a68c-8fa22b8823da",
|
||||
"traceId": "f8d11c13de5c4dfc973c447a537a0af3",
|
||||
"evidence": "version=0",
|
||||
"checkedAt": "2026-08-08T05:39:41.313Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "库位旧 version PUT 返回冲突",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"path": "/api/platform/v1/locations/2085964137512353794?tenantId=0&warehouseId=2085964137071951874"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "A0409",
|
||||
"message": "库位已被其他操作修改,请刷新后重试",
|
||||
"requestId": "c431047d-9ae5-4884-b697-e0aa4e7efa81",
|
||||
"traceId": "c0bed96d902b4f85b921bd2a75f7502c",
|
||||
"evidence": "HTTP=200, code=A0409",
|
||||
"checkedAt": "2026-08-08T05:39:41.365Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "冲突后回查库位",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/platform/v1/locations/2085964137512353794?tenantId=0&warehouseId=2085964137071951874"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "9ed0db1a-7baf-40c7-9fa2-25171421f0cf, cba60cdf-3160-49a7-86e1-b7c9f1a12774",
|
||||
"traceId": "6223aae1c5e547b18e1f9851aaae822a",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:39:41.415Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "库位冲突未造成部分更新",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/platform/v1/locations/2085964137512353794?tenantId=0&warehouseId=2085964137071951874"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "9ed0db1a-7baf-40c7-9fa2-25171421f0cf, cba60cdf-3160-49a7-86e1-b7c9f1a12774",
|
||||
"traceId": "6223aae1c5e547b18e1f9851aaae822a",
|
||||
"evidence": "name=边界库位-编辑",
|
||||
"checkedAt": "2026-08-08T05:39:41.415Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "托盘/库位真实占用删除保护自动化缺口",
|
||||
"passed": false,
|
||||
"deploymentBlocker": true,
|
||||
"request": null,
|
||||
"http": null,
|
||||
"code": null,
|
||||
"message": null,
|
||||
"requestId": null,
|
||||
"traceId": null,
|
||||
"evidence": "当前公共验收 API 未提供可安全构造并释放占用关联的独立 fixture;未操作历史资源",
|
||||
"checkedAt": "2026-08-08T05:39:41.415Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "import-boundaries",
|
||||
"name": "仓库空文件安全失败且不模拟导入成功",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/platform/v1/warehouse-admin/import?tenantId=0&dryRun=true"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "43673cd0-7cfa-4df8-979d-69811460f20a, 4fe85980-bff6-4efd-abd6-12731c0dbc64",
|
||||
"traceId": "31abe2fc66c042149a2c27f9212b5d0d",
|
||||
"evidence": "HTTP=200, code=200, bytes=0",
|
||||
"checkedAt": "2026-08-08T05:39:41.486Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "import-boundaries",
|
||||
"name": "仓库缺列伪 XLSX安全失败且不模拟导入成功",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/platform/v1/warehouse-admin/import?tenantId=0&dryRun=true"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "29f0d6a5-c5a3-4be7-bc4b-2e2dc78e9e61, e2d41aa3-dfaa-4297-85fc-d7262cb28979",
|
||||
"traceId": "d0276db3a031414bb66659d5a35f75c5",
|
||||
"evidence": "HTTP=200, code=200, bytes=6",
|
||||
"checkedAt": "2026-08-08T05:39:41.612Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "import-boundaries",
|
||||
"name": "快递公司错误扩展名安全失败且不模拟导入成功",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/platform/v1/express-companies/import?tenantId=0"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "0df47055-d23d-4d2a-a4d0-d273c043beaa, 491dab6e-0fcf-41dd-9bbc-90726e9b8081",
|
||||
"traceId": "77cb6c065dbc4c808c481ddcc40bd4c1",
|
||||
"evidence": "HTTP=200, code=200, bytes=27",
|
||||
"checkedAt": "2026-08-08T05:39:41.651Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "import-boundaries",
|
||||
"name": "往来单位非 XLSX安全失败且不模拟导入成功",
|
||||
"passed": false,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/platform/v1/partners/import?tenantId=0"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "bd5fd0ad-a772-4141-8e62-7900e689a801, 6bf285d4-f212-4697-bb67-8be0b4f062a1",
|
||||
"traceId": "bb9116a22f144153a6ba6b2c4062465b",
|
||||
"evidence": "HTTP=200, code=200, bytes=27",
|
||||
"checkedAt": "2026-08-08T05:39:41.686Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "错误客户海外仓费用 ID 安全失败",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/platform/v1/iam/tenants/999999999999999999/overseas-storage-fees"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "S0500",
|
||||
"message": "Internal server error",
|
||||
"requestId": "27956363-1cd8-49c9-9c5c-f2e3a172bdab",
|
||||
"traceId": "5c1894c4d5af4633b201b5bdfc0d9cc8",
|
||||
"evidence": "HTTP=200, code=S0500",
|
||||
"checkedAt": "2026-08-08T05:39:41.729Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "库位列表未授权边界响应正确",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/platform/v1/locations?page=1&size=1&tenantId=0"
|
||||
},
|
||||
"http": 401,
|
||||
"code": "A0401",
|
||||
"message": "未登录或令牌已失效",
|
||||
"requestId": "8588809e-48",
|
||||
"traceId": null,
|
||||
"evidence": "HTTP=401, code=A0401",
|
||||
"checkedAt": "2026-08-08T05:39:41.763Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "下载中心非法页码边界响应正确",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/platform/v1/files/export-tasks?page=0&size=1"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "A0422",
|
||||
"message": "page.page: must be greater than or equal to 1",
|
||||
"requestId": "62163d22-4c08-4f42-8f41-b37a9e030479",
|
||||
"traceId": "1969a2349eda4dc487aae32454b81665",
|
||||
"evidence": "HTTP=200, code=A0422",
|
||||
"checkedAt": "2026-08-08T05:39:42.210Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "船期价格非法状态边界响应正确",
|
||||
"passed": false,
|
||||
"deploymentBlocker": true,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/platform/v1/fba-pricing/carrier-rates/price-books?status=INVALID"
|
||||
},
|
||||
"http": 503,
|
||||
"code": null,
|
||||
"message": null,
|
||||
"requestId": "8588809e-50",
|
||||
"traceId": null,
|
||||
"evidence": "HTTP=503, code=missing",
|
||||
"checkedAt": "2026-08-08T05:39:42.325Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "停用隔离库位以便清理",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "PATCH",
|
||||
"path": "/api/platform/v1/locations/2085964137512353794/status?tenantId=0&warehouseId=2085964137071951874"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "6866d82d-457a-4270-bc94-81a25f14caef, 33e987a8-7b79-4dcb-a3a9-8930683cc200",
|
||||
"traceId": "86ee5c486dd24e8392296db09e07d8f8",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:39:42.410Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "删除隔离库位",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "DELETE",
|
||||
"path": "/api/platform/v1/locations/2085964137512353794?tenantId=0&warehouseId=2085964137071951874&version=2"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "7f84fc98-7a21-4748-b605-b4ae4dd4b9f2, 4f3fdb6e-fda6-4781-9c09-19b32517061e",
|
||||
"traceId": "752b78a1938845eb99d4a9970b16b871",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:39:42.496Z"
|
||||
},
|
||||
{
|
||||
"module": "supplemental-boundaries",
|
||||
"scenario": "admin-boundary-automation",
|
||||
"name": "停用隔离仓库",
|
||||
"passed": true,
|
||||
"deploymentBlocker": false,
|
||||
"request": {
|
||||
"method": "PATCH",
|
||||
"path": "/api/platform/v1/warehouse-admin/2085964137071951874/status?tenantId=0&status=INACTIVE"
|
||||
},
|
||||
"http": 200,
|
||||
"code": "200",
|
||||
"message": "success",
|
||||
"requestId": "44111f4f-669b-4c90-84cb-1328d0431543, 486b37d3-b302-4bdf-9c1b-62edc6621831",
|
||||
"traceId": "150ae3f066df4da8b4c56fa5fa5c46f2",
|
||||
"evidence": "",
|
||||
"checkedAt": "2026-08-08T05:39:42.568Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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']
|
||||
const DEFAULT_MODULES = ['customers', 'overseas-storage-fees', 'system', 'master-data', 'orders', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'supplemental', 'supplemental-boundaries']
|
||||
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',
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
const MODULE = 'supplemental-boundaries'
|
||||
const OPTIONS = { scenario: 'admin-boundary-automation', deploymentBlocker: true }
|
||||
|
||||
function id(value) { return value == null ? '' : String(value) }
|
||||
function codeOf(response) { return response?.body?.code == null ? 'missing' : String(response.body.code) }
|
||||
function failure(response) {
|
||||
return [400, 401, 403, 404, 409].includes(response?.http)
|
||||
|| (response?.http === 200 && codeOf(response) !== '200')
|
||||
}
|
||||
function missingRoute(response) {
|
||||
return response?.http === 404 || response?.http === 405
|
||||
|| /Not Found|No handler|No static resource/i.test(String(response?.raw || response?.body?.message || ''))
|
||||
}
|
||||
|
||||
async function multipart(context, path, bytes, fileName, contentType = 'application/octet-stream') {
|
||||
const form = new FormData()
|
||||
form.append('file', new Blob([bytes], { type: contentType }), 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 content = await response.text()
|
||||
let body = null
|
||||
try { body = content ? JSON.parse(content) : null } catch { /* bounded raw evidence below */ }
|
||||
return {
|
||||
method: 'POST', path, http: response.status, body,
|
||||
raw: body === null ? content.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 }
|
||||
}
|
||||
}
|
||||
|
||||
function warehousePayload(context, suffix) {
|
||||
return {
|
||||
tenantId: 0, warehouseCode: `BW${suffix}`, warehouseName: `边界验收仓-${suffix}`,
|
||||
warehouseType: 'DOMESTIC', countryCode: 'CN', timezone: 'Asia/Shanghai',
|
||||
contactName: '边界验收员', contactPhone: '13800000003', contactEmail: `boundary-${suffix}@example.com`,
|
||||
status: 'ACTIVE', remark: context.marker,
|
||||
address: { addressType: 'WAREHOUSE', countryCode: 'CN', stateProvince: '广东省', city: '深圳市', district: '南山区', postalCode: '518000', addressLine1: '边界验收地址', addressLine2: null, latitude: null, longitude: null },
|
||||
capabilities: [{ capabilityCode: 'RECEIVING', capabilityName: '收货', enabled: true, cutoffTime: '16:00:00', description: context.marker }],
|
||||
}
|
||||
}
|
||||
|
||||
export async function run(context) {
|
||||
const start = context.steps.length
|
||||
const suffix = context.marker.replace(/\D/g, '').slice(-12)
|
||||
let warehouseId = ''
|
||||
let locationId = ''
|
||||
let warehousePayloadValue
|
||||
let locationPayload
|
||||
let locationVersion = 0
|
||||
|
||||
// Create only marker-owned warehouse/location resources for the one
|
||||
// executable optimistic-lock scenario. Pallets and master-data records have
|
||||
// no version field in the public DTO, so they are not falsely claimed here.
|
||||
warehousePayloadValue = warehousePayload(context, suffix)
|
||||
const createWarehouse = await context.request('POST', '/api/platform/v1/warehouse-admin', warehousePayloadValue)
|
||||
warehouseId = id(context.apiSuccess(MODULE, '创建边界验收隔离仓库', createWarehouse, '', OPTIONS))
|
||||
if (warehouseId) {
|
||||
locationPayload = {
|
||||
warehouseId, zoneCode: `BZ${suffix.slice(-5)}`, zoneName: '边界区',
|
||||
locationCode: `BL${suffix}`, locationName: '边界库位', locationType: 'SHELF',
|
||||
maxWeightKg: 1000, status: 'AVAILABLE', remark: context.marker,
|
||||
}
|
||||
const createLocation = await context.request('POST', '/api/platform/v1/locations?tenantId=0', locationPayload)
|
||||
locationId = id(context.apiSuccess(MODULE, '创建边界验收隔离库位', createLocation, '', OPTIONS))
|
||||
}
|
||||
if (locationId) {
|
||||
const locationPath = `/api/platform/v1/locations/${locationId}?tenantId=0&warehouseId=${warehouseId}`
|
||||
const detail = await context.request('GET', locationPath)
|
||||
const data = context.apiSuccess(MODULE, '读取隔离库位版本', detail, '', OPTIONS)
|
||||
locationVersion = Number(data?.location?.version)
|
||||
const firstUpdate = await context.request('PUT', locationPath, { ...locationPayload, locationName: `${locationPayload.locationName}-编辑`, version: locationVersion })
|
||||
context.apiSuccess(MODULE, '更新隔离库位以推进版本', firstUpdate, `version=${locationVersion}`, OPTIONS)
|
||||
const stale = await context.request('PUT', locationPath, { ...locationPayload, locationName: `${locationPayload.locationName}-STALE`, version: locationVersion })
|
||||
const conflict = stale.http === 200 && ['A0409', '409'].includes(codeOf(stale))
|
||||
context.addStep(MODULE, '库位旧 version PUT 返回冲突', stale, conflict, `HTTP=${stale.http}, code=${codeOf(stale)}`, OPTIONS)
|
||||
const after = await context.request('GET', locationPath)
|
||||
const afterData = context.apiSuccess(MODULE, '冲突后回查库位', after, '', OPTIONS)
|
||||
context.assert(MODULE, '库位冲突未造成部分更新', after,
|
||||
afterData?.location?.locationName === `${locationPayload.locationName}-编辑`, `name=${afterData?.location?.locationName || 'missing'}`, OPTIONS)
|
||||
locationVersion = Number(afterData?.location?.version)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// Import boundary probes use only in-memory bytes and dry-run where supported.
|
||||
const bogusXlsx = new Uint8Array([0x50, 0x4b, 0x03, 0x04, 0x00, 0x00])
|
||||
const empty = new Uint8Array()
|
||||
const probes = [
|
||||
['仓库空文件', '/api/platform/v1/warehouse-admin/import?tenantId=0&dryRun=true', empty, `${context.marker}.xlsx`],
|
||||
['仓库缺列伪 XLSX', '/api/platform/v1/warehouse-admin/import?tenantId=0&dryRun=true', bogusXlsx, `${context.marker}-missing.xlsx`],
|
||||
['快递公司错误扩展名', '/api/platform/v1/express-companies/import?tenantId=0', new TextEncoder().encode(context.marker), `${context.marker}.txt`],
|
||||
['往来单位非 XLSX', '/api/platform/v1/partners/import?tenantId=0', new TextEncoder().encode(context.marker), `${context.marker}.txt`],
|
||||
]
|
||||
for (const [name, path, bytes, fileName] of probes) {
|
||||
const response = await multipart(context, path, bytes, fileName)
|
||||
context.addStep(MODULE, `${name}安全失败且不模拟导入成功`, response, failure(response),
|
||||
`HTTP=${response.http}, code=${codeOf(response)}, bytes=${bytes.length}`, { scenario: 'import-boundaries', deploymentBlocker: missingRoute(response) })
|
||||
}
|
||||
|
||||
// Invalid customer/price routes and authentication boundaries are read-only.
|
||||
const invalidFee = await context.request('GET', '/api/platform/v1/iam/tenants/999999999999999999/overseas-storage-fees')
|
||||
context.addStep(MODULE, '错误客户海外仓费用 ID 安全失败', invalidFee, failure(invalidFee),
|
||||
`HTTP=${invalidFee.http}, code=${codeOf(invalidFee)}`, OPTIONS)
|
||||
for (const [name, path] of [
|
||||
['库位列表未授权', '/api/platform/v1/locations?page=1&size=1&tenantId=0'],
|
||||
['下载中心非法页码', '/api/platform/v1/files/export-tasks?page=0&size=1'],
|
||||
['船期价格非法状态', '/api/platform/v1/fba-pricing/carrier-rates/price-books?status=INVALID'],
|
||||
]) {
|
||||
const response = await context.request('GET', path, undefined, name.includes('未授权') ? '' : context.token)
|
||||
const passed = name.includes('未授权') ? response.http === 401 : failure(response)
|
||||
context.addStep(MODULE, `${name}边界响应正确`, response, passed, `HTTP=${response.http}, code=${codeOf(response)}`, OPTIONS)
|
||||
}
|
||||
|
||||
if (locationId) {
|
||||
const disable = await context.request('PATCH', `/api/platform/v1/locations/${locationId}/status?tenantId=0&warehouseId=${warehouseId}`, { status: 'DISABLED', version: locationVersion })
|
||||
context.apiSuccess(MODULE, '停用隔离库位以便清理', disable, '', OPTIONS)
|
||||
const remove = await context.request('DELETE', `/api/platform/v1/locations/${locationId}?tenantId=0&warehouseId=${warehouseId}&version=${locationVersion + 1}`)
|
||||
context.apiSuccess(MODULE, '删除隔离库位', remove, '', OPTIONS)
|
||||
}
|
||||
if (warehouseId) {
|
||||
const disableWarehouse = await context.request('PATCH', `/api/platform/v1/warehouse-admin/${warehouseId}/status?tenantId=0&status=INACTIVE`)
|
||||
context.apiSuccess(MODULE, '停用隔离仓库', disableWarehouse, '', OPTIONS)
|
||||
}
|
||||
|
||||
const steps = context.steps.slice(start)
|
||||
context.scenarios.push({ name: 'admin-boundary-automation', module: MODULE, success: steps.every((step) => step.passed), deploymentBlocked: steps.some((step) => step.deploymentBlocker), passed: steps.filter((step) => step.passed).length, failed: steps.filter((step) => !step.passed).length })
|
||||
}
|
||||
+31
@@ -16,6 +16,30 @@ class OverseasStorageFeeSqlContractTest {
|
||||
assertThat(sql)
|
||||
.contains("CREATE TABLE IF NOT EXISTS wh_overseas_storage_fee_rule")
|
||||
.contains("CREATE TABLE IF NOT EXISTS wh_overseas_storage_fee_tier")
|
||||
.containsOnlyOnce("CREATE PROCEDURE migrate_overseas_storage_fee_schema()")
|
||||
.doesNotContain("ADD COLUMN IF NOT EXISTS initialization_key")
|
||||
.doesNotContain("ADD COLUMN IF NOT EXISTS fee_type")
|
||||
.doesNotContain("ADD COLUMN IF NOT EXISTS billing_unit")
|
||||
.contains("table_name = 'wh_overseas_storage_fee_rule'")
|
||||
.contains("column_name = 'initialization_key'")
|
||||
.contains("column_name = 'fee_type'")
|
||||
.contains("column_name = 'billing_unit'")
|
||||
.contains("PREPARE overseas_fee_statement FROM @overseas_fee_ddl")
|
||||
.contains("EXECUTE overseas_fee_statement")
|
||||
.contains("DEALLOCATE PREPARE overseas_fee_statement")
|
||||
.contains("constraint_name = 'chk_overseas_fee_tier_days'")
|
||||
.contains("constraint_name = 'chk_overseas_fee_tier_price'")
|
||||
.contains("index_columns = 'rule_id,fee_type,active_start_day'")
|
||||
.contains("Warehouse 迁移失败:海外仓费用阶梯存在非法天数或负价格")
|
||||
.contains("Warehouse 迁移失败:海外仓费用阶梯目标唯一键存在重复数据")
|
||||
.contains("CALL migrate_overseas_storage_fee_schema()")
|
||||
.contains("LOWER(column_type) = 'varchar(191)'")
|
||||
.contains("LOWER(column_type) = 'varchar(32)'")
|
||||
.contains("LOWER(column_type) = 'varchar(16)'")
|
||||
.contains("LOWER(column_type) = 'smallint unsigned'")
|
||||
.contains("LOWER(column_type) = 'decimal(18,6)'")
|
||||
.contains("统一迁移失败:海外仓阶梯费用字段签名不正确")
|
||||
.contains("统一迁移失败:海外仓阶梯费用唯一键或 CHECK 结构不正确")
|
||||
.contains("uk_overseas_fee_active_customer")
|
||||
.contains("idx_overseas_fee_page")
|
||||
.contains("idx_overseas_fee_customer_name")
|
||||
@@ -27,5 +51,12 @@ class OverseasStorageFeeSqlContractTest {
|
||||
.contains("platform:overseas-storage-fee:delete")
|
||||
.contains("CALL unified_assert_overseas_storage_fee()")
|
||||
.contains("统一增量全部执行完成");
|
||||
|
||||
int addColumns = sql.indexOf("column_name = 'fee_type'");
|
||||
int migrate = sql.indexOf("CALL migrate_overseas_storage_fee_schema()");
|
||||
int assertSchema = sql.indexOf("CALL unified_assert_overseas_storage_fee()");
|
||||
assertThat(addColumns).isGreaterThan(0);
|
||||
assertThat(migrate).isGreaterThan(addColumns);
|
||||
assertThat(assertSchema).isGreaterThan(migrate);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user