diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index 5d67ffcb1..5d86ee7b7 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -14045,28 +14045,6 @@ WHERE tenant_id=0 AND app_code='PLATFORM' '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 - IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=0 AND app_code='PLATFORM' AND permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 22 THEN - SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:平台 Finance 权限不完整'; - END IF; - IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=10001 AND app_code='MERCHANT' AND permission_code IN ('merchant:finance:statement:list','merchant:finance:statement:view') AND status='ACTIVE' AND deleted=0) <> 2 THEN - SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:客户 Finance 只读权限不完整'; - END IF; - IF (SELECT COUNT(*) 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 rp.role_id=4000001 AND rp.tenant_id=0 AND (p.permission_code='platform:finance' OR p.permission_code LIKE 'platform:finance:%')) <> 23 THEN - SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:平台管理员 Finance 授权不完整'; - END IF; - IF (SELECT COUNT(*) 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 rp.role_id=4000101 AND rp.tenant_id=10001 AND (p.permission_code='merchant:finance:statement' OR p.permission_code LIKE 'merchant:finance:%')) <> 3 THEN - SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:客户管理员 Finance 只读授权不完整'; - END IF; -END$$ -DELIMITER ; -CALL assert_finance_permissions(); -DROP PROCEDURE IF EXISTS assert_finance_permissions; - CALL assert_loading_pick_task_shapes(1); DROP PROCEDURE IF EXISTS assert_loading_pick_task_shapes; @@ -14374,26 +14352,23 @@ BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:IAM 消息中心唯一索引不完整'; END IF; IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=0 AND status='ACTIVE' AND deleted=0 - AND (permission_code LIKE 'platform:notification%' OR permission_code='merchant:notification:view')) <> 11 THEN + AND (permission_code LIKE 'platform:notification%' OR permission_code='merchant:notification:view')) <> 13 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:IAM 消息中心权限不完整'; END IF; IF (SELECT COUNT(*) 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.role_code='PLATFORM_ADMIN' AND p.permission_code LIKE 'platform:notification%') <> 10 THEN + WHERE r.role_code='PLATFORM_ADMIN' AND p.permission_code LIKE 'platform:notification%') <> 12 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:平台管理员消息中心授权不完整'; END IF; IF (SELECT COUNT(*) 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.role_code='MERCHANT_OWNER' AND p.permission_code='merchant:notification:view') <> 1 THEN + WHERE r.role_code='MERCHANT_OWNER' AND p.permission_code IN ('merchant:notification:view','merchant:notification:read','merchant:notification:read-all')) <> 3 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:企业管理员消息查看授权不完整'; END IF; END$$ DELIMITER ; -CALL baiyue_iam.assert_notification_center_schema(); -DROP PROCEDURE IF EXISTS baiyue_iam.assert_notification_center_schema; - -- 货柜清关与装柜清单报关采用同一份轻量文件事实,不再要求独立报关 case 状态机。 USE baiyue_firstleg; DROP PROCEDURE IF EXISTS phase3_simple_customs_add_column; @@ -14478,7 +14453,9 @@ CALL phase3_container_exception_add_column('request_hash',"CHAR(64) NULL COMMENT DROP PROCEDURE phase3_container_exception_add_column; UPDATE fl_container_exception_history SET operation_id=CONCAT('LEGACY-',id) WHERE operation_id IS NULL OR operation_id=''; UPDATE fl_container_exception_history SET request_hash=SHA2(CONCAT(action,'|',container_booking_id,'|',id,'|',reason),256) WHERE request_hash IS NULL OR request_hash=''; -ALTER TABLE fl_container_exception_history MODIFY operation_id VARCHAR(128) NOT NULL, MODIFY request_hash CHAR(64) NOT NULL; +ALTER TABLE fl_container_exception_history + MODIFY operation_id VARCHAR(128) NOT NULL COMMENT '同一操作人幂等键', + MODIFY request_hash CHAR(64) NOT NULL COMMENT '规范化请求摘要'; SET @has_exception_operation_index=(SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema='baiyue_firstleg' AND table_name='fl_container_exception_history' AND index_name='uk_container_exception_operation'); SET @ddl=IF(@has_exception_operation_index=0,'ALTER TABLE fl_container_exception_history ADD UNIQUE KEY uk_container_exception_operation(container_booking_id,operator_id,operation_id)','SELECT 1'); PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement; @@ -14714,6 +14691,9 @@ WHERE r.tenant_id=0 AND r.status='ACTIVE' AND r.deleted=0 AND ((r.app_code='PLATFORM' AND r.role_code='PLATFORM_ADMIN' AND p.permission_code IN('platform:notification:read','platform:notification:read-all')) OR (r.app_code='MERCHANT' AND r.role_code='MERCHANT_OWNER' AND p.permission_code IN('merchant:notification:read','merchant:notification:read-all'))); +CALL baiyue_iam.assert_notification_center_schema(); +DROP PROCEDURE IF EXISTS baiyue_iam.assert_notification_center_schema; + -- 开发期不保留独立详情权限:列表权限是唯一只读权限。 DELETE rp FROM iam_role_permission rp JOIN iam_permission p ON p.tenant_id=rp.tenant_id AND p.id=rp.permission_id WHERE p.permission_code IN('platform:firstleg:container:view','platform:firstleg:preload:view','platform:firstleg:loading-list:view','platform:supplier:view'); @@ -14721,7 +14701,30 @@ UPDATE iam_permission SET status='INACTIVE',deleted=1 WHERE permission_code IN('platform:firstleg:container:view','platform:firstleg:preload:view','platform:firstleg:loading-list:view','platform:supplier:view'); -- 入库完成后的唯一状态为 WAIT_SPLIT。 -CALL assert_unified_final_schema(); -DROP PROCEDURE IF EXISTS assert_unified_final_schema; +-- Finance 权限和角色授权断言必须位于五个财务列表菜单及其授权写入之后,保证整份脚本可重复执行。 +DROP PROCEDURE IF EXISTS assert_finance_permissions; +DELIMITER $$ +CREATE PROCEDURE assert_finance_permissions() +BEGIN + IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=0 AND app_code='PLATFORM' AND permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 27 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:平台 Finance 权限不完整'; + END IF; + IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=10001 AND app_code='MERCHANT' AND permission_code IN ('merchant:finance:statement:list','merchant:finance:statement:view') AND status='ACTIVE' AND deleted=0) <> 2 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:客户 Finance 只读权限不完整'; + END IF; + IF (SELECT COUNT(*) 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 rp.role_id=4000001 AND rp.tenant_id=0 AND (p.permission_code='platform:finance' OR p.permission_code LIKE 'platform:finance:%')) <> 28 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:平台管理员 Finance 授权不完整'; + END IF; + IF (SELECT COUNT(*) 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 rp.role_id=4000101 AND rp.tenant_id=10001 AND (p.permission_code='merchant:finance:statement' OR p.permission_code LIKE 'merchant:finance:%')) <> 3 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:客户管理员 Finance 只读授权不完整'; + END IF; +END$$ +DELIMITER ; +CALL assert_finance_permissions(); +DROP PROCEDURE IF EXISTS assert_finance_permissions; + +USE baiyue_firstleg; +CALL baiyue_firstleg.assert_unified_final_schema(); +DROP PROCEDURE IF EXISTS baiyue_firstleg.assert_unified_final_schema; SELECT 'PASS' AS migration_status, '统一增量全部执行完成;父子订单、履约分片、装柜门禁、订舱拖车、报关船期、船司查询、仓库角色、费用、Finance人工财务、公共资源及可靠审计结构校验通过' AS message; diff --git a/iam/src/test/java/com/baiyue/iam/notification/NotificationSqlContractTest.java b/iam/src/test/java/com/baiyue/iam/notification/NotificationSqlContractTest.java index 1823db26a..5020e27cf 100644 --- a/iam/src/test/java/com/baiyue/iam/notification/NotificationSqlContractTest.java +++ b/iam/src/test/java/com/baiyue/iam/notification/NotificationSqlContractTest.java @@ -18,6 +18,12 @@ class NotificationSqlContractTest { "assert_notification_center_schema","permission_code='merchant:notification:view'", "permission_code LIKE 'platform:notification%'"); assertThat(sql).contains("COUNT(DISTINCT index_name) FROM information_schema.statistics WHERE table_schema='baiyue_iam'"); - assertThat(sql).contains(")) <> 7 THEN"); + assertThat(sql) + .contains(")) <> 7 THEN") + .contains("permission_code='merchant:notification:view')) <> 13 THEN") + .contains("permission_code LIKE 'platform:notification%') <> 12 THEN") + .contains("'merchant:notification:view','merchant:notification:read','merchant:notification:read-all')) <> 3 THEN"); + assertThat(sql.indexOf("(5106014,0,'MERCHANT','BUTTON'")) + .isLessThan(sql.indexOf("CALL baiyue_iam.assert_notification_center_schema();")); } } diff --git a/iam/src/test/java/com/baiyue/iam/repository/FinanceG1G2PermissionSqlContractTest.java b/iam/src/test/java/com/baiyue/iam/repository/FinanceG1G2PermissionSqlContractTest.java index d8a050ce3..243e20aff 100644 --- a/iam/src/test/java/com/baiyue/iam/repository/FinanceG1G2PermissionSqlContractTest.java +++ b/iam/src/test/java/com/baiyue/iam/repository/FinanceG1G2PermissionSqlContractTest.java @@ -48,7 +48,7 @@ class FinanceG1G2PermissionSqlContractTest { void unifiedSqlMirrorsCatalogAndGrantsTheFinanceCatalogToPlatformAdmin() throws Exception { String sql = Files.readString(resolveUnifiedSql(), StandardCharsets.UTF_8); String financeSection = sql.substring(sql.indexOf("-- Finance F1 人工财务权限"), - sql.indexOf("CALL assert_unified_final_schema()")); + sql.indexOf("-- Admin 原型菜单与财务五页权限补充")); assertThat(financeSection) .contains("(5105024, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:review'") @@ -56,15 +56,17 @@ class FinanceG1G2PermissionSqlContractTest { .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) <> 22") - .contains("平台管理员 Finance 授权不完整") .contains("Finance G1-G4"); assertThat(financeSection) .contains("SELECT 4000001, id, 0 FROM baiyue_iam.iam_permission") .contains("status='INACTIVE',deleted=1"); assertThat(sql) .contains("platform:finance:settlement-ar:list") - .contains("platform:finance:settlement-ap:list"); + .contains("platform:finance:settlement-ap:list") + .contains("permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 27") + .contains("rp.role_id=4000001") + .contains("LIKE 'platform:finance:%')) <> 28") + .contains("平台管理员 Finance 授权不完整"); } private static Path resolveUnifiedSql() { diff --git a/iam/src/test/java/com/baiyue/iam/repository/FinanceG3G4PermissionSqlContractTest.java b/iam/src/test/java/com/baiyue/iam/repository/FinanceG3G4PermissionSqlContractTest.java index 3b1375b7a..8463aa869 100644 --- a/iam/src/test/java/com/baiyue/iam/repository/FinanceG3G4PermissionSqlContractTest.java +++ b/iam/src/test/java/com/baiyue/iam/repository/FinanceG3G4PermissionSqlContractTest.java @@ -40,18 +40,20 @@ class FinanceG3G4PermissionSqlContractTest { void unifiedSqlContainsCatalogAndGrantsItToPlatformAdmin() throws Exception { String sql = Files.readString(resolveUnifiedSql(), StandardCharsets.UTF_8); String financeSection = sql.substring(sql.indexOf("-- Finance F1 人工财务权限"), - sql.indexOf("CALL assert_unified_final_schema()")); + sql.indexOf("-- Admin 原型菜单与财务五页权限补充")); for (String code : CODES) assertThat(financeSection).contains(code); 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) <> 22"); + .contains("finance G1-G4 permission id/code conflict"); assertThat(financeSection) .contains("SELECT 4000001, id, 0 FROM baiyue_iam.iam_permission") .contains("platform:finance:reconciliation:create"); assertThat(sql) .contains("platform:finance:settlement-ar:list") - .contains("platform:finance:settlement-ap:list"); + .contains("platform:finance:settlement-ap:list") + .contains("permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 27") + .contains("rp.role_id=4000001") + .contains("LIKE 'platform:finance:%')) <> 28"); } private static Path resolveUnifiedSql() { diff --git a/scripts/README.md b/scripts/README.md index 057252ab1..f1d035b10 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -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 `3E0A02C2E3B971AB7D8AB9C9D8AEE1D933F950DBCE82BD9B6AD431A4DD179F8A` 和 14713 行,并静态确认中文注释升级采用校验入队、每表一次 ALTER、结构签名复核、九库覆盖断言及唯一最终 `PASS`。 +正式入口是 `local-db-full-acceptance.ps1`,只允许连接本机 `127.0.0.1` 或 `localhost`,默认端口 `3306`;测试库 `8.163.30.128` 会立即以退出码 2 拒绝。脚本固定校验统一 SQL SHA-256 `10FDF9175B579D564AB5B6FACB8DBE0B8DB52032C9DC11928147E179D21D2041` 和 14730 行,并静态确认中文注释升级采用校验入队、每表一次 ALTER、结构签名复核、九库覆盖断言及唯一最终 `PASS`。 ```powershell .\scripts\local-db-full-acceptance.ps1 -DryRun diff --git a/scripts/local-db-full-acceptance.ps1 b/scripts/local-db-full-acceptance.ps1 index 479bea2eb..ff30d315b 100644 --- a/scripts/local-db-full-acceptance.ps1 +++ b/scripts/local-db-full-acceptance.ps1 @@ -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 = '3E0A02C2E3B971AB7D8AB9C9D8AEE1D933F950DBCE82BD9B6AD431A4DD179F8A' -$ExpectedLines = 14713 +$ExpectedSha256 = '10FDF9175B579D564AB5B6FACB8DBE0B8DB52032C9DC11928147E179D21D2041' +$ExpectedLines = 14730 if ($TargetHost -notin @('127.0.0.1', 'localhost')) { [Console]::Error.WriteLine('Refusing non-local database; only 127.0.0.1 or localhost is allowed.')