This commit is contained in:
2026-08-13 14:55:50 +08:00
parent 939e90912a
commit ef9852e757
14 changed files with 72 additions and 62 deletions
@@ -1494,7 +1494,7 @@ VALUES
'/downloads', 40, 'ACTIVE', 1, 0)
ON DUPLICATE KEY UPDATE
permission_name = VALUES(permission_name), route_path = VALUES(route_path), sort_no = VALUES(sort_no),
status = 'ACTIVE', built_in = 1, deleted = 0;
status = VALUES(status), built_in = 1, deleted = VALUES(deleted);
INSERT INTO iam_permission
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name,
@@ -8857,10 +8857,10 @@ BEGIN
END IF;
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
WHERE tenant_id=0 AND app_code='PLATFORM' AND id BETWEEN 5102140 AND 5102149
AND permission_code LIKE 'platform:firstleg:ocean-booking:%') <> 10
AND permission_code LIKE 'platform:firstleg:ocean-booking:%') <> 9
OR (SELECT COUNT(*) FROM baiyue_iam.iam_permission
WHERE tenant_id=0 AND app_code='PLATFORM' AND id BETWEEN 5102150 AND 5102154
AND permission_code LIKE 'platform:firstleg:truck-appointment:%') <> 5 THEN
AND permission_code LIKE 'platform:firstleg:truck-appointment:%') <> 4 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:订舱或拖车预约权限未完整写入';
END IF;
IF (SELECT COUNT(DISTINCT CONCAT(r.role_code, '|', p.permission_code))
@@ -8868,13 +8868,13 @@ BEGIN
JOIN baiyue_iam.iam_role r ON r.id=rp.role_id AND r.tenant_id=rp.tenant_id
JOIN baiyue_iam.iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id
WHERE r.tenant_id=0 AND r.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
AND p.permission_code LIKE 'platform:firstleg:ocean-booking:%') <> 20
AND p.permission_code LIKE 'platform:firstleg:ocean-booking:%') <> 18
OR (SELECT COUNT(DISTINCT CONCAT(r.role_code, '|', p.permission_code))
FROM baiyue_iam.iam_role_permission rp
JOIN baiyue_iam.iam_role r ON r.id=rp.role_id AND r.tenant_id=rp.tenant_id
JOIN baiyue_iam.iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id
WHERE r.tenant_id=0 AND r.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
AND p.permission_code LIKE 'platform:firstleg:truck-appointment:%') <> 10 THEN
AND p.permission_code LIKE 'platform:firstleg:truck-appointment:%') <> 8 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:订舱或拖车预约角色授权未完整写入';
END IF;
END$$
@@ -13913,7 +13913,7 @@ BEGIN
IF EXISTS (
SELECT 1
FROM baiyue_iam.iam_permission
OR (id = 5105024 AND permission_code <> 'platform:finance:fee-source:review')
WHERE (id = 5105024 AND permission_code <> 'platform:finance:fee-source:review')
OR (id = 5105025 AND permission_code <> 'platform:finance:fee-source:settlement')
OR (id = 5105026 AND permission_code <> 'platform:finance:reconciliation:create')
OR (id = 5105027 AND permission_code <> 'platform:finance:reconciliation:review')
@@ -13967,7 +13967,7 @@ BEGIN
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
http_method = VALUES(http_method), sort_no = VALUES(sort_no),
status = 'ACTIVE', built_in = 1, deleted = 0;
status = VALUES(status), built_in = 1, deleted = VALUES(deleted);
IF (SELECT COUNT(*)
FROM baiyue_iam.iam_permission
@@ -13989,7 +13989,7 @@ BEGIN
'platform:finance:settlement-ar:create',
'platform:finance:settlement-ap:create',
'platform:finance:payment:create',
'platform:finance:settlement:void')) <> 13 THEN
'platform:finance:settlement:void')) <> 11 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'finance G1-G4 permission catalog is incomplete';
END IF;
@@ -13998,6 +13998,14 @@ DELIMITER ;
CALL migrate_finance_g1_g4_permissions();
DROP PROCEDURE IF EXISTS migrate_finance_g1_g4_permissions;
DELETE rp FROM baiyue_iam.iam_role_permission rp
JOIN baiyue_iam.iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id
WHERE p.tenant_id=0 AND p.app_code='PLATFORM'
AND p.permission_code IN ('platform:finance:bill:view','platform:finance:account:view','platform:finance:fee-source:view','platform:finance:settlement-ar:view','platform:finance:settlement-ap:view');
UPDATE baiyue_iam.iam_permission SET status='INACTIVE',deleted=1
WHERE tenant_id=0 AND app_code='PLATFORM'
AND permission_code IN ('platform:finance:bill:view','platform:finance:account:view','platform:finance:fee-source:view','platform:finance:settlement-ar:view','platform:finance:settlement-ap:view');
INSERT INTO baiyue_iam.iam_role_permission (role_id, permission_id, tenant_id)
SELECT 4000001, id, 0 FROM baiyue_iam.iam_permission
WHERE tenant_id=0 AND app_code='PLATFORM' AND permission_code IN (
@@ -14012,7 +14020,6 @@ ON DUPLICATE KEY UPDATE tenant_id=VALUES(tenant_id);
INSERT INTO baiyue_iam.iam_role_permission (role_id, permission_id, tenant_id)
SELECT 4000001, id, 0 FROM baiyue_iam.iam_permission
WHERE tenant_id=0 AND app_code='PLATFORM' AND permission_code IN (
'platform:finance:account:view', 'platform:finance:fee-source:view',
'platform:finance:fee-source:review', 'platform:finance:fee-source:settlement',
'platform:finance:reconciliation:create', 'platform:finance:reconciliation:review',
'platform:finance:reconciliation:issue', 'platform:finance:reconciliation:adjust',
@@ -14025,7 +14032,21 @@ SELECT 4000101, id, 10001 FROM baiyue_iam.iam_permission
WHERE tenant_id=10001 AND app_code='MERCHANT' AND (permission_code='merchant:finance:statement' OR permission_code LIKE 'merchant:finance:%')
ON DUPLICATE KEY UPDATE tenant_id=VALUES(tenant_id);
-- 开发期 list-only 收口:先移除角色关联并停用旧只读资源,再执行最终 Finance 断言。
DELETE rp FROM baiyue_iam.iam_role_permission rp
JOIN baiyue_iam.iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id
WHERE p.tenant_id=0 AND p.app_code='PLATFORM'
AND p.permission_code IN ('platform:finance:bill:view','platform:finance:account:view',
'platform:finance:fee-source:view','platform:finance:settlement-ar:view',
'platform:finance:settlement-ap:view');
UPDATE baiyue_iam.iam_permission SET status='INACTIVE', deleted=1
WHERE tenant_id=0 AND app_code='PLATFORM'
AND permission_code IN ('platform:finance:bill:view','platform:finance:account:view',
'platform:finance:fee-source:view','platform:finance:settlement-ar:view',
'platform:finance:settlement-ap:view');
-- Finance 权限和角色授权断言。
DROP PROCEDURE IF EXISTS assert_finance_permissions;
DELIMITER $$
CREATE PROCEDURE assert_finance_permissions()
BEGIN
@@ -14472,17 +14493,12 @@ VALUES
(5105044,0,'PLATFORM','MENU',5105000,'platform:finance:settlement-ap:list','结算单应付','/finance/settlement-payables','GET',50,'ACTIVE',1,0)
ON DUPLICATE KEY UPDATE permission_type=VALUES(permission_type), parent_id=VALUES(parent_id), permission_name=VALUES(permission_name), route_path=VALUES(route_path), http_method=VALUES(http_method), sort_no=VALUES(sort_no), status='ACTIVE', built_in=1, deleted=0;
UPDATE iam_permission SET status='INACTIVE', deleted=1 WHERE tenant_id=0 AND app_code='PLATFORM' AND permission_code IN ('platform:overseas-storage-fee:create','platform:overseas-storage-fee:delete');
DELETE rp FROM iam_role_permission rp JOIN iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id WHERE p.tenant_id=0 AND p.app_code='PLATFORM' AND p.permission_code IN ('platform:finance:bill:view','platform:finance:account:view','platform:finance:fee-source:view','platform:finance:settlement-ar:view','platform:finance:settlement-ap:view');
UPDATE iam_permission SET status='INACTIVE', deleted=1 WHERE tenant_id=0 AND app_code='PLATFORM' AND permission_code IN ('platform:finance:bill:view','platform:finance:account:view','platform:finance:fee-source:view','platform:finance:settlement-ar:view','platform:finance:settlement-ap:view');
INSERT IGNORE INTO iam_role_permission (tenant_id, role_id, permission_id, created_at)
SELECT 0, r.id, p.id, CURRENT_TIMESTAMP(3) FROM iam_role r JOIN iam_permission p ON p.tenant_id=0 AND p.app_code='PLATFORM'
WHERE r.tenant_id=0 AND r.app_code='PLATFORM' AND r.role_code='PLATFORM_ADMIN' AND p.permission_code IN ('platform:finance:customer-account:list','platform:finance:customer-arap:list','platform:finance:customer-statement:list','platform:finance:settlement-ar:list','platform:finance:settlement-ap:list');
UPDATE iam_permission p JOIN (SELECT 'platform:finance:account:view' code,5105040 parent_id UNION ALL SELECT 'platform:finance:fee-source:view',5105041 UNION ALL SELECT 'platform:finance:fee-source:review',5105041 UNION ALL SELECT 'platform:finance:fee-source:settlement',5105041 UNION ALL SELECT 'platform:finance:reconciliation:create',5105042 UNION ALL SELECT 'platform:finance:reconciliation:review',5105042 UNION ALL SELECT 'platform:finance:reconciliation:issue',5105042 UNION ALL SELECT 'platform:finance:reconciliation:adjust',5105042 UNION ALL SELECT 'platform:finance:reconciliation:void',5105042 UNION ALL SELECT 'platform:finance:settlement-ar:create',5105043 UNION ALL SELECT 'platform:finance:settlement-ap:create',5105044 UNION ALL SELECT 'platform:finance:payment:create',5105044) mapping ON mapping.code=p.permission_code SET p.parent_id=mapping.parent_id WHERE p.tenant_id=0 AND p.app_code='PLATFORM' AND p.permission_type='BUTTON';
DELETE rp FROM iam_role_permission rp JOIN iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id
WHERE p.tenant_id=0 AND p.app_code='PLATFORM'
AND p.permission_code IN('platform:finance:bill:view','platform:finance:settlement-ar:view','platform:finance:settlement-ap:view');
UPDATE iam_permission SET status='INACTIVE',deleted=1
WHERE tenant_id=0 AND app_code='PLATFORM'
AND permission_code IN('platform:finance:bill:view','platform:finance:settlement-ar:view','platform:finance:settlement-ap:view');
UPDATE iam_permission p JOIN (SELECT 'platform:finance:fee-source:review' code,5105041 parent_id UNION ALL SELECT 'platform:finance:fee-source:settlement',5105041 UNION ALL SELECT 'platform:finance:reconciliation:create',5105042 UNION ALL SELECT 'platform:finance:reconciliation:review',5105042 UNION ALL SELECT 'platform:finance:reconciliation:issue',5105042 UNION ALL SELECT 'platform:finance:reconciliation:adjust',5105042 UNION ALL SELECT 'platform:finance:reconciliation:void',5105042 UNION ALL SELECT 'platform:finance:settlement-ar:create',5105043 UNION ALL SELECT 'platform:finance:settlement-ap:create',5105044 UNION ALL SELECT 'platform:finance:payment:create',5105044) mapping ON mapping.code=p.permission_code SET p.parent_id=mapping.parent_id WHERE p.tenant_id=0 AND p.app_code='PLATFORM' AND p.permission_type='BUTTON';
INSERT INTO baiyue_iam.iam_permission
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, deleted)
@@ -25,13 +25,13 @@ class AdminPrototypeMenuPermissionSqlContractTest {
assertThat(sql).contains("(" + (5105040 + index) + ",0,'PLATFORM','MENU',5105000,'" + FINANCE_MENUS[index] + "'");
}
assertThat(sql)
.contains("'platform:finance:account:view' code, 5105040 parent_id")
.contains("'platform:finance:fee-source:view', 5105041")
.contains("'platform:finance:fee-source:review' code, 5105041 parent_id")
.contains("'platform:finance:reconciliation:create', 5105042")
.contains("'platform:finance:settlement-ar:create', 5105043")
.contains("'platform:finance:settlement-ap:create', 5105044")
.contains("'platform:finance:payment:create', 5105044)")
.contains("'platform:finance:bill:view','platform:finance:settlement-ar:view'")
.contains("'platform:finance:bill:view','platform:finance:account:view'")
.contains("'platform:finance:fee-source:view','platform:finance:settlement-ar:view'")
.contains("'platform:finance:settlement-ap:view'")
.doesNotContain("SELECT 'platform:finance:settlement-ar:view', 5105043")
.doesNotContain("SELECT 'platform:finance:settlement-ap:view', 5105044")
@@ -11,22 +11,21 @@ import static org.assertj.core.api.Assertions.assertThat;
class FinanceG1G2PermissionSqlContractTest {
@Test
void declaresTheFourControllerPermissionsUnderFinance() throws Exception {
void declaresActionPermissionsAndRetiresSupersededReadPermissions() throws Exception {
String sql = new ClassPathResource("db/finance-g1-g2-permissions.sql")
.getContentAsString(StandardCharsets.UTF_8);
assertThat(sql)
.contains("(5105022, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:account:view'")
.contains("(5105023, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:view'")
.contains("(5105024, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:review'")
.contains("(5105025, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:settlement'")
.contains("NULL, 'GET', 130, 'ACTIVE', 1, 0")
.contains("NULL, 'GET', 140, 'ACTIVE', 1, 0")
.contains("NULL, 'POST', 150, 'ACTIVE', 1, 0")
.contains("NULL, 'PUT', 160, 'ACTIVE', 1, 0")
.contains("ON DUPLICATE KEY UPDATE")
.contains("CALL migrate_finance_g1_g2_permissions()")
.contains("DROP PROCEDURE IF EXISTS migrate_finance_g1_g2_permissions");
.contains("DROP PROCEDURE IF EXISTS migrate_finance_g1_g2_permissions")
.contains("status='INACTIVE', deleted=1")
.doesNotContain("'platform:finance:account:view',\n '查看客户财务账户及流水'")
.doesNotContain("'platform:finance:fee-source:view',\n '查看财务费用来源'");
}
@Test
@@ -36,7 +35,6 @@ class FinanceG1G2PermissionSqlContractTest {
assertThat(sql)
.contains("SIGNAL SQLSTATE '45000'")
.contains("id = 5105022 AND permission_code <> 'platform:finance:account:view'")
.contains("permission_code = 'platform:finance:fee-source:settlement' AND id <> 5105025")
.contains("tenant_id <> 0 OR app_code <> 'PLATFORM'")
.contains("parent_id = 5105000")
@@ -53,21 +51,17 @@ class FinanceG1G2PermissionSqlContractTest {
sql.indexOf("CALL assert_unified_final_schema()"));
assertThat(financeSection)
.contains("(5105022, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:account:view'")
.contains("(5105023, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:view'")
.contains("(5105024, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:review'")
.contains("(5105025, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:settlement'")
.contains("NULL, 'GET', 130, 'ACTIVE', 1, 0")
.contains("NULL, 'GET', 140, 'ACTIVE', 1, 0")
.contains("NULL, 'POST', 150, 'ACTIVE', 1, 0")
.contains("NULL, 'PUT', 160, 'ACTIVE', 1, 0")
.contains("permission_code IN (\n 'platform:finance',")
.contains("permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 24")
.contains("permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 22")
.contains("平台管理员 Finance 授权不完整")
.contains("Finance G1-G4");
assertThat(financeSection)
.contains("SELECT 4000001, id, 0 FROM baiyue_iam.iam_permission")
.contains("platform:finance:account:view");
.contains("status='INACTIVE',deleted=1");
assertThat(sql)
.contains("platform:finance:settlement-ar:list")
.contains("platform:finance:settlement-ap:list");
@@ -45,7 +45,7 @@ class FinanceG3G4PermissionSqlContractTest {
assertThat(financeSection)
.contains("migrate_finance_g1_g4_permissions")
.contains("finance G1-G4 permission id/code conflict")
.contains("permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 24");
.contains("permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 22");
assertThat(financeSection)
.contains("SELECT 4000001, id, 0 FROM baiyue_iam.iam_permission")
.contains("platform:finance:reconciliation:create");
+1 -1
View File
@@ -87,7 +87,7 @@ Dry-run 只检查根 `pom.xml` 和关键回归测试文件并打印 Maven 命令
### 本地 MySQL 全量验收
正式入口是 `local-db-full-acceptance.ps1`,只允许连接本机 `127.0.0.1` 或 `localhost`,默认端口 `3306`;测试库 `8.163.30.128` 会立即以退出码 2 拒绝。脚本固定校验统一 SQL SHA-256 `BE7C5C98712D5202F5D187EA9A37C64EAB9F6AA9AE772C89A51F186507B1AE53` 和 14706 行,并静态确认中文注释升级采用校验入队、每表一次 ALTER、结构签名复核、九库覆盖断言及唯一最终 `PASS`。
正式入口是 `local-db-full-acceptance.ps1`,只允许连接本机 `127.0.0.1` 或 `localhost`,默认端口 `3306`;测试库 `8.163.30.128` 会立即以退出码 2 拒绝。脚本固定校验统一 SQL SHA-256 `3E0A02C2E3B971AB7D8AB9C9D8AEE1D933F950DBCE82BD9B6AD431A4DD179F8A` 和 14713 行,并静态确认中文注释升级采用校验入队、每表一次 ALTER、结构签名复核、九库覆盖断言及唯一最终 `PASS`。
```powershell
.\scripts\local-db-full-acceptance.ps1 -DryRun
@@ -17,7 +17,7 @@ test('retains all clickable container, loading-list and PDA workflow states thro
assert.match(source, /\.\/orders\.mjs/)
assert.match(source, /\.\/inbound-measurement\.mjs/)
assert.doesNotMatch(source, /context\.request\('POST', CONTAINERS/)
assert.match(source, /\['WAIT_SPLIT', 'INBOUNDED'\]\.includes/)
assert.match(source, /summary\?\.processStatus === 'WAIT_SPLIT'/)
const batch = source.slice(source.indexOf('async function pickAll'), source.indexOf('async function complete'))
assert.doesNotMatch(batch, /containerNo:/)
})
@@ -88,7 +88,7 @@ async function readInbound(context, fixture) {
const value = context.apiSuccess(MODULE, '读取人工装柜独立入库单', response, '', OPTIONS)
const warehouseId = id(value?.summary?.warehouseId)
context.assert(MODULE, '人工装柜来源入库单可独立排柜', response,
['WAIT_SPLIT', 'INBOUNDED'].includes(value?.summary?.processStatus)
value?.summary?.processStatus === 'WAIT_SPLIT'
&& value?.summary?.measureReviewStatus === 'APPROVED'
&& Boolean(warehouseId),
`inboundId=${fixture.inboundId}, status=${value?.summary?.processStatus || 'missing'}`, OPTIONS)
@@ -39,8 +39,8 @@ test('入库不可独立保留状态显式阻断', () => {
assert.match(source, new RegExp(`${status}:`))
}
assert.match(source, /AutomaticInboundCompletionService 在同一调用内立即从 INBOUNDING 推进 WAIT_SPLIT/)
assert.match(source, /processStatus=INBOUNDED/)
assert.match(source, /查询存量 INBOUNDED 兼容样本/)
assert.match(source, /processStatus=WAIT_SPLIT/)
assert.match(source, /查询存量 WAIT_SPLIT 样本/)
assert.match(source, /没有作废入库单 API/)
assert.match(source, /context\.assert\(MODULE, `入库列表保留 \$\{status\}`/)
})
@@ -48,7 +48,7 @@ test('入库不可独立保留状态显式阻断', () => {
test('统一订单声明合法可达状态并显式阻断废弃或无 API 状态', () => {
assert.match(source, /result\.unifiedOrders\.PENDING_REVIEW/)
assert.match(source, /result\.unifiedOrders\.WAIT_MEASURE/)
for (const status of ['INBOUNDED', 'WAIT_SPLIT', 'LOADING', 'LOADED', 'DELIVERED']) {
for (const status of ['WAIT_SPLIT', 'LOADING', 'LOADED', 'DELIVERED']) {
assert.match(source, new RegExp(`'${status}'`))
}
assert.doesNotMatch(source, /WAIT_SPLIT 标记为历史废弃状态/)
@@ -15,10 +15,10 @@ const ORDER_TARGETS = [
]
const INBOUND_FILTER_STATUSES = [
'WAIT_MEASURE', 'MEASURING', 'MEASURE_PENDING', 'MEASURE_REJECTED', 'WAIT_PRICING',
'FEE_CONFIRMING', 'INBOUNDING', 'INBOUNDED', 'VOIDED',
'FEE_CONFIRMING', 'INBOUNDING', 'WAIT_SPLIT', 'VOIDED',
]
const UNIFIED_FILTER_STATUSES = [
'PENDING_REVIEW', 'WAIT_MEASURE', 'INBOUNDED', 'WAIT_SPLIT', 'LOADING', 'LOADED', 'DELIVERED',
'PENDING_REVIEW', 'WAIT_MEASURE', 'WAIT_SPLIT', 'LOADING', 'LOADED', 'DELIVERED',
]
function id(value) { return value == null ? '' : String(value) }
@@ -277,19 +277,19 @@ export async function run(context, matrix = context.createdResources.manualState
}
const legacyResponse = await context.request('GET',
`/api/platform/v1/inbounds?tenantId=${query(fixture.tenantId)}&processStatus=INBOUNDED&page=1&size=20`)
const legacyPage = context.apiSuccess(MODULE, '查询存量 INBOUNDED 兼容样本', legacyResponse, '', OPTIONS)
`/api/platform/v1/inbounds?tenantId=${query(fixture.tenantId)}&processStatus=WAIT_SPLIT&page=1&size=20`)
const legacyPage = context.apiSuccess(MODULE, '查询存量 WAIT_SPLIT 样本', legacyResponse, '', OPTIONS)
const legacyInbound = rows(legacyPage)[0]
if (legacyInbound?.id && legacyInbound?.orderId) {
const sample = { inboundId: id(legacyInbound.id), orderId: id(legacyInbound.orderId), tenantId: fixture.tenantId,
status: 'INBOUNDED', inboundCode: legacyInbound.inboundCode || null }
result.inbounds.INBOUNDED = sample
result.unifiedOrders.INBOUNDED = sample
matrix.pages.inbounds.fixtures.INBOUNDED = {
id: sample.inboundId, status: 'INBOUNDED', detailPath: `/orders/${sample.orderId}`,
status: 'WAIT_SPLIT', inboundCode: legacyInbound.inboundCode || null }
result.inbounds.WAIT_SPLIT = sample
result.unifiedOrders.WAIT_SPLIT = sample
matrix.pages.inbounds.fixtures.WAIT_SPLIT = {
id: sample.inboundId, status: 'WAIT_SPLIT', detailPath: `/orders/${sample.orderId}`,
}
matrix.pages.unifiedOrders.fixtures.INBOUNDED = {
id: sample.orderId, status: 'INBOUNDED', detailPath: `/orders/${sample.orderId}`,
matrix.pages.unifiedOrders.fixtures.WAIT_SPLIT = {
id: sample.orderId, status: 'WAIT_SPLIT', detailPath: `/orders/${sample.orderId}`,
}
}
@@ -62,7 +62,7 @@ export async function run(context) {
`tenantId=${tenantId}, inboundId=${inboundId}`, OPTIONS)
const warehouseId = id(inbound?.summary?.warehouseId)
context.assert(MODULE, '柜次候选入库单状态、测量和仓库门禁有效', inboundResponse,
['WAIT_SPLIT', 'INBOUNDED'].includes(inbound?.summary?.processStatus)
inbound?.summary?.processStatus === 'WAIT_SPLIT'
&& inbound?.summary?.measureReviewStatus === 'APPROVED'
&& Boolean(warehouseId),
`status=${inbound?.summary?.processStatus || 'missing'}, review=${inbound?.summary?.measureReviewStatus || 'missing'}, warehouseId=${warehouseId || 'missing'}`, OPTIONS)
@@ -108,9 +108,9 @@ export async function run(context) {
const initialStatus = initial?.summary?.processStatus
const initialVersion = Number(initial?.summary?.version)
context.assert(MODULE, '隔离入库单处于智能分柜门禁状态', initialResponse,
['INBOUNDED', 'WAIT_SPLIT'].includes(initialStatus) && Number.isInteger(initialVersion),
initialStatus === 'WAIT_SPLIT' && Number.isInteger(initialVersion),
`status=${initialStatus || 'missing'}, version=${initial?.summary?.version ?? 'missing'}`, OPTIONS)
if (!['INBOUNDED', 'WAIT_SPLIT'].includes(initialStatus) || !Number.isInteger(initialVersion)) return
if (initialStatus !== 'WAIT_SPLIT' || !Number.isInteger(initialVersion)) return
const base = `/api/platform/v1/inbounds/${inboundId}`
const suffix = context.marker.replace(/\D/g, '').slice(-10)
@@ -168,7 +168,7 @@ export async function run(context) {
const smartData = context.apiSuccess(MODULE, '智能分柜并生成预装清单', smart, `inboundId=${inboundId}`, OPTIONS)
const planId = id(smartData?.preloadPlan?.id)
context.assert(MODULE, '智能分柜预装计划 PENDING、版本和明细正确', smart,
smartData?.mainStatus === 'INBOUNDED' && Boolean(planId) && smartData?.preloadPlan?.status === 'PENDING'
smartData?.mainStatus === 'WAIT_SPLIT' && Boolean(planId) && smartData?.preloadPlan?.status === 'PENDING'
&& Number(smartData?.preloadPlan?.version) === 0 && smartData?.preloadPlan?.items?.length === 1,
`mainStatus=${smartData?.mainStatus || 'missing'}, planId=${planId || 'missing'}, status=${smartData?.preloadPlan?.status || 'missing'}`, OPTIONS)
if (!planId) return
@@ -628,7 +628,7 @@ async function unifiedOrderModule() {
const row = pageRecords(json.data)
.find((item) => String(item.orderId) === report.resources.terminalOrderId)
assertPresent(row, 'unified terminal order row')
assertEqual(row.mainStatus, 'INBOUNDED', 'unified main status')
assertEqual(row.mainStatus, 'WAIT_SPLIT', 'unified main status')
assertEqual(String(row.inboundId), report.resources.terminalInboundId, 'unified inbound id')
assertEqual(row.measurement?.reviewStatus, 'APPROVED', 'unified measurement review status')
assertTrue(Number(row.actualGrossWeightKg) > 0, 'unified actual gross weight must be positive')
@@ -646,7 +646,7 @@ async function unifiedOrderModule() {
}, ({ json }) => {
requireSuccess(json)
assertEqual(String(json.data?.summary?.orderId), report.resources.terminalOrderId, 'unified order id')
assertEqual(json.data?.summary?.mainStatus, 'INBOUNDED', 'unified main status')
assertEqual(json.data?.summary?.mainStatus, 'WAIT_SPLIT', 'unified main status')
assertEqual(String(json.data?.summary?.inboundId), report.resources.terminalInboundId,
'unified inbound id')
assertEqual(json.data?.summary?.measurement?.reviewStatus, 'APPROVED',
+2 -2
View File
@@ -11,8 +11,8 @@ $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 = 'BE7C5C98712D5202F5D187EA9A37C64EAB9F6AA9AE772C89A51F186507B1AE53'
$ExpectedLines = 14706
$ExpectedSha256 = '3E0A02C2E3B971AB7D8AB9C9D8AEE1D933F950DBCE82BD9B6AD431A4DD179F8A'
$ExpectedLines = 14713
if ($TargetHost -notin @('127.0.0.1', 'localhost')) {
[Console]::Error.WriteLine('Refusing non-local database; only 127.0.0.1 or localhost is allowed.')
+3 -3
View File
@@ -405,16 +405,16 @@ try {
$approvedInboundResponse = Invoke-Api GET "/api/platform/inbounds/$($ids.inboundId)?tenantId=$TenantId" $platformToken
$approvedInbound = Require-Success '审核后回查入库单' $approvedInboundResponse
$versions.inbound = [int]$approvedInbound.summary.version
Require-Condition '二期边界停在 INBOUNDED' $approvedInboundResponse ($approvedInbound.summary.processStatus -eq 'INBOUNDED' -and $approvedInbound.summary.measureReviewStatus -eq 'APPROVED') "processStatus=$($approvedInbound.summary.processStatus), reviewStatus=$($approvedInbound.summary.measureReviewStatus)"
Require-Condition '二期边界停在 WAIT_SPLIT' $approvedInboundResponse ($approvedInbound.summary.processStatus -eq 'WAIT_SPLIT' -and $approvedInbound.summary.measureReviewStatus -eq 'APPROVED') "processStatus=$($approvedInbound.summary.processStatus), reviewStatus=$($approvedInbound.summary.measureReviewStatus)"
$feeResponse = Invoke-Api POST "/api/platform/v1/inbounds/$($ids.inboundId)/fees/calculate?tenantId=$TenantId" $platformToken @{} @{ 'Idempotency-Key' = (New-Key 'fee-calculate') }
$calculatedFee = Require-Success '二期计费接口试算' $feeResponse
$ids.feeId = [string]$calculatedFee.feeId
Require-Condition '计费试算保持 INBOUNDED 主流程边界' $feeResponse ($calculatedFee.nextProcessStatus -eq 'INBOUNDED') "nextProcessStatus=$($calculatedFee.nextProcessStatus)"
Require-Condition '计费试算保持 WAIT_SPLIT 主流程边界' $feeResponse ($calculatedFee.nextProcessStatus -eq 'WAIT_SPLIT') "nextProcessStatus=$($calculatedFee.nextProcessStatus)"
$feeConfirmResponse = Invoke-Api POST "/api/platform/v1/inbounds/$($ids.inboundId)/fees/confirm?tenantId=$TenantId" $platformToken $null @{ 'Idempotency-Key' = (New-Key 'fee-confirm') }
$confirmedFee = Require-Success '确认二期费用快照' $feeConfirmResponse
Require-Condition '费用确认后仍保持 INBOUNDED 主流程边界' $feeConfirmResponse ($confirmedFee.feeStatus -eq 'CONFIRMED' -and $confirmedFee.nextProcessStatus -eq 'INBOUNDED') "feeStatus=$($confirmedFee.feeStatus), nextProcessStatus=$($confirmedFee.nextProcessStatus)"
Require-Condition '费用确认后仍保持 WAIT_SPLIT 主流程边界' $feeConfirmResponse ($confirmedFee.feeStatus -eq 'CONFIRMED' -and $confirmedFee.nextProcessStatus -eq 'WAIT_SPLIT') "feeStatus=$($confirmedFee.feeStatus), nextProcessStatus=$($confirmedFee.nextProcessStatus)"
}
catch {
$fatal = $_.Exception.Message