This commit is contained in:
2026-08-11 19:35:18 +08:00
parent 4d49d7a2e4
commit 3809d2a028
4 changed files with 301 additions and 2 deletions
@@ -0,0 +1,86 @@
-- Finance G1/G2 permissions exposed by FinancePhase1OperationController.
-- These resources are assignable to custom platform roles; this migration does not grant them by default.
USE baiyue_iam;
SET NAMES utf8mb4;
DROP PROCEDURE IF EXISTS migrate_finance_g1_g2_permissions;
DELIMITER $$
CREATE PROCEDURE migrate_finance_g1_g2_permissions()
BEGIN
IF EXISTS (
SELECT 1
FROM iam_permission
WHERE (id = 5105000 AND permission_code <> 'platform:finance')
OR (id = 5105022 AND permission_code <> 'platform:finance:account:view')
OR (id = 5105023 AND permission_code <> 'platform:finance:fee-source:view')
OR (id = 5105024 AND permission_code <> 'platform:finance:fee-source:review')
OR (id = 5105025 AND permission_code <> 'platform:finance:fee-source:settlement')
OR (permission_code = 'platform:finance' AND id <> 5105000)
OR (permission_code = 'platform:finance:account:view' AND id <> 5105022)
OR (permission_code = 'platform:finance:fee-source:view' AND id <> 5105023)
OR (permission_code = 'platform:finance:fee-source:review' AND id <> 5105024)
OR (permission_code = 'platform:finance:fee-source:settlement' AND id <> 5105025)
OR (id IN (5105000, 5105022, 5105023, 5105024, 5105025)
AND (tenant_id <> 0 OR app_code <> 'PLATFORM'))
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'finance G1/G2 permission id/code conflict';
END IF;
INSERT INTO iam_permission
(id, tenant_id, app_code, permission_type, parent_id, permission_code,
permission_name, route_path, http_method, sort_no, status, built_in, deleted)
VALUES
(5105000, 0, 'PLATFORM', 'MENU', 0, 'platform:finance',
'财务管理', '/finance', NULL, 60, 'ACTIVE', 1, 0),
(5105022, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:account:view',
'查看客户财务账户及流水', NULL, 'GET', 130, 'ACTIVE', 1, 0),
(5105023, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:view',
'查看财务费用来源', NULL, 'GET', 140, 'ACTIVE', 1, 0),
(5105024, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:review',
'审核财务费用来源', NULL, 'POST', 150, 'ACTIVE', 1, 0),
(5105025, 0, 'PLATFORM', 'BUTTON', 5105000, 'platform:finance:fee-source:settlement',
'设置费用来源参与结算', NULL, 'PUT', 160, '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;
IF (SELECT COUNT(*)
FROM iam_permission
WHERE tenant_id = 0
AND app_code = 'PLATFORM'
AND permission_type = 'BUTTON'
AND parent_id = 5105000
AND status = 'ACTIVE'
AND built_in = 1
AND deleted = 0
AND permission_code IN (
'platform:finance:account:view',
'platform:finance:fee-source:view',
'platform:finance:fee-source:review',
'platform:finance:fee-source:settlement')) <> 4 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'finance G1/G2 permission catalog is incomplete';
END IF;
IF (SELECT COUNT(*)
FROM iam_permission
WHERE id = 5105000
AND tenant_id = 0
AND app_code = 'PLATFORM'
AND permission_type = 'MENU'
AND parent_id = 0
AND permission_code = 'platform:finance'
AND status = 'ACTIVE'
AND built_in = 1
AND deleted = 0) <> 1 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'finance permission parent is invalid';
END IF;
END$$
DELIMITER ;
CALL migrate_finance_g1_g2_permissions();
DROP PROCEDURE IF EXISTS migrate_finance_g1_g2_permissions;
@@ -0,0 +1,87 @@
package com.baiyue.iam.repository;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
class FinanceG1G2PermissionSqlContractTest {
@Test
void declaresTheFourControllerPermissionsUnderFinanceWithoutDefaultRoleGrants() 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")
.doesNotContain("INSERT INTO iam_role_permission");
}
@Test
void failsClosedOnPermissionIdentityConflictsAndAssertsCatalogCompleteness() throws Exception {
String sql = new ClassPathResource("db/finance-g1-g2-permissions.sql")
.getContentAsString(StandardCharsets.UTF_8);
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")
.contains("permission_type = 'MENU'")
.contains("built_in = 1")
.contains("finance permission parent is invalid")
.contains("permission catalog is incomplete");
}
@Test
void unifiedSqlMirrorsCatalogButDoesNotAutoGrantTheNewPermissions() 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()"));
String platformAdminGrant = financeSection.substring(
financeSection.indexOf("SELECT 4000001, id, 0 FROM baiyue_iam.iam_permission"),
financeSection.indexOf("ON DUPLICATE KEY UPDATE tenant_id=VALUES(tenant_id);"));
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) <> 16")
.contains("p.permission_code IN ('platform:finance:account:view','platform:finance:fee-source:view','platform:finance:fee-source:review','platform:finance:fee-source:settlement')) <> 0")
.contains("平台管理员 Finance 授权不完整")
.contains("Finance G1/G2 新权限不得默认授予平台管理员");
assertThat(platformAdminGrant)
.contains("'platform:finance'")
.contains("'platform:finance:customer-terms:update'")
.doesNotContain("LIKE 'platform:finance:%'")
.doesNotContain("platform:finance:account:view")
.doesNotContain("platform:finance:fee-source:view")
.doesNotContain("platform:finance:fee-source:review")
.doesNotContain("platform:finance:fee-source:settlement");
}
private static Path resolveUnifiedSql() {
Path direct = Path.of("deploy", "sql", "头程数据库统一增量-MySQL8.sql");
return Files.exists(direct) ? direct : Path.of("..", "deploy", "sql", "头程数据库统一增量-MySQL8.sql");
}
}