From 6449daf8231f4a11ddeac535ecd770b859e7e2a7 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 1 Sep 2026 20:36:24 +0800 Subject: [PATCH] fix: allow optional phase5 custom admin role --- deploy/sql/头程数据库统一增量-MySQL8.sql | 15 +- iam/pom.xml | 2 +- .../db/phase5-delivery-permissions.sql | 15 +- ...ase5DeliveryPermissionSqlContractTest.java | 6 +- ...se5DeliveryPermissionSqlExecutionTest.java | 220 ++++++++++++++++++ 5 files changed, 244 insertions(+), 14 deletions(-) create mode 100644 iam/src/test/java/com/baiyue/iam/repository/Phase5DeliveryPermissionSqlExecutionTest.java diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index f2b80e184..3a951cdd9 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -16711,11 +16711,14 @@ BEGIN 'platform:overseas-warehouse:customer-visit:edit')) <> 14 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='五期海外派送权限初始化失败:平台管理员授权不完整'; END IF; - IF (SELECT COUNT(*) FROM iam_role_permission grant_row - JOIN iam_role role ON role.id=grant_row.role_id AND role.tenant_id=grant_row.tenant_id - JOIN iam_permission permission ON permission.id=grant_row.permission_id - WHERE role.role_code='PLATFORM_CUSTOM_ADMIN' - AND permission.permission_code IN ( + IF EXISTS (SELECT 1 FROM iam_role role + WHERE role.tenant_id=0 AND role.app_code='PLATFORM' + AND UPPER(role.role_code)='PLATFORM_CUSTOM_ADMIN' + AND role.status='ACTIVE' AND role.deleted=0 + AND (SELECT COUNT(*) FROM iam_role_permission grant_row + JOIN iam_permission permission ON permission.id=grant_row.permission_id + WHERE grant_row.role_id=role.id AND grant_row.tenant_id=role.tenant_id + AND permission.permission_code IN ( 'platform:overseas-warehouse:dispatch:list', 'platform:overseas-warehouse:dispatch:edit', 'platform:overseas-warehouse:dispatch:cancel', @@ -16729,7 +16732,7 @@ BEGIN 'platform:overseas-warehouse:delivery:return', 'platform:overseas-warehouse:delivery:status', 'platform:overseas-warehouse:customer-visit:list', - 'platform:overseas-warehouse:customer-visit:edit')) <> 14 THEN + 'platform:overseas-warehouse:customer-visit:edit')) <> 14) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='五期海外派送权限初始化失败:平台自定义管理员授权不完整'; END IF; SELECT COALESCE(SUM(role_code='WAREHOUSE_OPERATOR'),0), diff --git a/iam/pom.xml b/iam/pom.xml index 7f583178c..fcfc28a60 100644 --- a/iam/pom.xml +++ b/iam/pom.xml @@ -18,8 +18,8 @@ org.springframework.cloudspring-cloud-starter-loadbalancer org.springframework.bootspring-boot-starter-actuator com.mysqlmysql-connector-jruntime + com.h2databaseh2test org.springframework.bootspring-boot-starter-testtest org.springframework.bootspring-boot-maven-plugin - diff --git a/iam/src/main/resources/db/phase5-delivery-permissions.sql b/iam/src/main/resources/db/phase5-delivery-permissions.sql index 0254b102b..10a22894b 100644 --- a/iam/src/main/resources/db/phase5-delivery-permissions.sql +++ b/iam/src/main/resources/db/phase5-delivery-permissions.sql @@ -223,11 +223,14 @@ BEGIN 'platform:overseas-warehouse:customer-visit:edit')) <> 14 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='五期海外派送权限初始化失败:平台管理员授权不完整'; END IF; - IF (SELECT COUNT(*) FROM iam_role_permission grant_row - JOIN iam_role role ON role.id=grant_row.role_id AND role.tenant_id=grant_row.tenant_id - JOIN iam_permission permission ON permission.id=grant_row.permission_id - WHERE role.role_code='PLATFORM_CUSTOM_ADMIN' - AND permission.permission_code IN ( + IF EXISTS (SELECT 1 FROM iam_role role + WHERE role.tenant_id=0 AND role.app_code='PLATFORM' + AND UPPER(role.role_code)='PLATFORM_CUSTOM_ADMIN' + AND role.status='ACTIVE' AND role.deleted=0 + AND (SELECT COUNT(*) FROM iam_role_permission grant_row + JOIN iam_permission permission ON permission.id=grant_row.permission_id + WHERE grant_row.role_id=role.id AND grant_row.tenant_id=role.tenant_id + AND permission.permission_code IN ( 'platform:overseas-warehouse:dispatch:list', 'platform:overseas-warehouse:dispatch:edit', 'platform:overseas-warehouse:dispatch:cancel', @@ -241,7 +244,7 @@ BEGIN 'platform:overseas-warehouse:delivery:return', 'platform:overseas-warehouse:delivery:status', 'platform:overseas-warehouse:customer-visit:list', - 'platform:overseas-warehouse:customer-visit:edit')) <> 14 THEN + 'platform:overseas-warehouse:customer-visit:edit')) <> 14) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='五期海外派送权限初始化失败:平台自定义管理员授权不完整'; END IF; SELECT COALESCE(SUM(role_code='WAREHOUSE_OPERATOR'),0), diff --git a/iam/src/test/java/com/baiyue/iam/repository/Phase5DeliveryPermissionSqlContractTest.java b/iam/src/test/java/com/baiyue/iam/repository/Phase5DeliveryPermissionSqlContractTest.java index 1ddfa354d..a88640226 100644 --- a/iam/src/test/java/com/baiyue/iam/repository/Phase5DeliveryPermissionSqlContractTest.java +++ b/iam/src/test/java/com/baiyue/iam/repository/Phase5DeliveryPermissionSqlContractTest.java @@ -108,7 +108,11 @@ class Phase5DeliveryPermissionSqlContractTest { "INTO v_warehouse_operator_permission_count, v_warehouse_tally_permission_count", "IF v_warehouse_operator_permission_count <> 14", "OR v_warehouse_tally_permission_count <> 5 THEN", + "IF EXISTS (SELECT 1 FROM iam_role role", + "AND UPPER(role.role_code)='PLATFORM_CUSTOM_ADMIN'", + "WHERE grant_row.role_id=role.id AND grant_row.tenant_id=role.tenant_id", "平台自定义管理员授权不完整"); + assertThat(sql).doesNotContain("WHERE role.role_code='PLATFORM_CUSTOM_ADMIN'"); assertThat(sql).doesNotContain( "IF (SELECT COUNT(*) FROM tmp_phase5_delivery_role_permission", "OR (SELECT COUNT(*) FROM tmp_phase5_delivery_role_permission"); @@ -137,7 +141,7 @@ class Phase5DeliveryPermissionSqlContractTest { int phase4 = unifiedSql.indexOf("-- 四期海外权限统一目录"); int phase5 = unifiedSql.indexOf("-- 五期海外排车、出库与派送权限(来源:iam/db/phase5-delivery-permissions.sql)"); int bodyStart = unifiedSql.indexOf("USE baiyue_iam;", phase5); - int nextSection = unifiedSql.indexOf("-- 海外智能排车草稿锁", bodyStart); + int nextSection = unifiedSql.indexOf("-- IAM 客户提交即成功", bodyStart); assertThat(phase4).isGreaterThanOrEqualTo(0); assertThat(phase5).isGreaterThan(phase4); diff --git a/iam/src/test/java/com/baiyue/iam/repository/Phase5DeliveryPermissionSqlExecutionTest.java b/iam/src/test/java/com/baiyue/iam/repository/Phase5DeliveryPermissionSqlExecutionTest.java new file mode 100644 index 000000000..0e7944937 --- /dev/null +++ b/iam/src/test/java/com/baiyue/iam/repository/Phase5DeliveryPermissionSqlExecutionTest.java @@ -0,0 +1,220 @@ +package com.baiyue.iam.repository; + +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; + +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.assertThat; + +class Phase5DeliveryPermissionSqlExecutionTest { + private static final Pattern PLATFORM_CODE = + Pattern.compile("platform:overseas-warehouse:[a-z0-9:-]+"); + + @Test + void noCustomAdministratorDoesNotCreateAnAssertionViolation() throws Exception { + try (Connection connection = database()) { + executeGrant(connection); + + assertThat(customAdministratorViolations(connection)).isZero(); + } + } + + @Test + void onePartiallyGrantedCustomAdministratorIsCompletedIdempotently() throws Exception { + try (Connection connection = database()) { + insertRole(connection, 11L, "platform_custom_admin"); + grantFirstPermissions(connection, 11L, 3); + assertThat(customAdministratorViolations(connection)).isEqualTo(1); + + executeGrant(connection); + assertThat(grantCount(connection, 11L)).isEqualTo(14); + assertThat(customAdministratorViolations(connection)).isZero(); + + executeGrant(connection); + assertThat(grantCount(connection, 11L)).isEqualTo(14); + assertThat(totalGrantCount(connection)).isEqualTo(14); + } + } + + @Test + void multipleCustomAdministratorsExposeTheSingleIncompleteRole() throws Exception { + try (Connection connection = database()) { + insertRole(connection, 21L, "PLATFORM_CUSTOM_ADMIN"); + insertRole(connection, 22L, "Platform_Custom_Admin"); + grantFirstPermissions(connection, 21L, 14); + grantFirstPermissions(connection, 22L, 13); + + assertThat(customAdministratorViolations(connection)).isEqualTo(1); + + executeGrant(connection); + assertThat(grantCount(connection, 21L)).isEqualTo(14); + assertThat(grantCount(connection, 22L)).isEqualTo(14); + assertThat(customAdministratorViolations(connection)).isZero(); + } + } + + @Test + void multipleCompleteCustomAdministratorsRemainCompleteAfterReplay() throws Exception { + try (Connection connection = database()) { + insertRole(connection, 31L, "PLATFORM_CUSTOM_ADMIN"); + insertRole(connection, 32L, "platform_custom_admin"); + grantFirstPermissions(connection, 31L, 14); + grantFirstPermissions(connection, 32L, 14); + + assertThat(customAdministratorViolations(connection)).isZero(); + executeGrant(connection); + executeGrant(connection); + + assertThat(grantCount(connection, 31L)).isEqualTo(14); + assertThat(grantCount(connection, 32L)).isEqualTo(14); + assertThat(totalGrantCount(connection)).isEqualTo(28); + } + } + + private static Connection database() throws Exception { + Connection connection = DriverManager.getConnection( + "jdbc:h2:mem:phase5_" + UUID.randomUUID() + ";MODE=MySQL;DATABASE_TO_LOWER=TRUE"); + try (Statement statement = connection.createStatement()) { + statement.execute(""" + CREATE TABLE iam_role ( + id BIGINT PRIMARY KEY, + tenant_id BIGINT NOT NULL, + app_code VARCHAR(32) NOT NULL, + role_code VARCHAR(64) NOT NULL, + status VARCHAR(16) NOT NULL, + deleted INT NOT NULL + ) + """); + statement.execute(""" + CREATE TABLE iam_permission ( + id BIGINT PRIMARY KEY, + tenant_id BIGINT NOT NULL, + app_code VARCHAR(32) NOT NULL, + permission_code VARCHAR(160) NOT NULL, + status VARCHAR(16) NOT NULL, + deleted INT NOT NULL + ) + """); + statement.execute(""" + CREATE TABLE iam_role_permission ( + tenant_id BIGINT NOT NULL, + role_id BIGINT NOT NULL, + permission_id BIGINT NOT NULL, + created_at TIMESTAMP NOT NULL, + PRIMARY KEY (tenant_id, role_id, permission_id) + ) + """); + } + long id = 1; + for (String code : platformPermissions()) { + try (PreparedStatement insert = connection.prepareStatement( + "INSERT INTO iam_permission VALUES (?,0,'PLATFORM',?,'ACTIVE',0)")) { + insert.setLong(1, id++); + insert.setString(2, code); + insert.executeUpdate(); + } + } + return connection; + } + + private static void insertRole(Connection connection, long id, String roleCode) throws Exception { + try (PreparedStatement insert = connection.prepareStatement( + "INSERT INTO iam_role VALUES (?,0,'PLATFORM',?,'ACTIVE',0)")) { + insert.setLong(1, id); + insert.setString(2, roleCode); + insert.executeUpdate(); + } + } + + private static void grantFirstPermissions(Connection connection, long roleId, int limit) throws Exception { + try (PreparedStatement insert = connection.prepareStatement(""" + INSERT INTO iam_role_permission (tenant_id, role_id, permission_id, created_at) + SELECT 0, ?, id, CURRENT_TIMESTAMP FROM iam_permission ORDER BY id LIMIT ? + """)) { + insert.setLong(1, roleId); + insert.setInt(2, limit); + insert.executeUpdate(); + } + } + + private static void executeGrant(Connection connection) throws Exception { + try (Statement statement = connection.createStatement()) { + statement.executeUpdate(administratorGrantSql()); + } + } + + private static int grantCount(Connection connection, long roleId) throws Exception { + try (PreparedStatement query = connection.prepareStatement( + "SELECT COUNT(*) FROM iam_role_permission WHERE tenant_id=0 AND role_id=?")) { + query.setLong(1, roleId); + try (ResultSet result = query.executeQuery()) { + result.next(); + return result.getInt(1); + } + } + } + + private static int totalGrantCount(Connection connection) throws Exception { + try (Statement query = connection.createStatement(); + ResultSet result = query.executeQuery("SELECT COUNT(*) FROM iam_role_permission")) { + result.next(); + return result.getInt(1); + } + } + + private static int customAdministratorViolations(Connection connection) throws Exception { + String placeholders = String.join(",", platformPermissions().stream().map(code -> "?").toList()); + String sql = """ + SELECT COUNT(*) FROM iam_role role + WHERE role.tenant_id=0 AND role.app_code='PLATFORM' + AND UPPER(role.role_code)='PLATFORM_CUSTOM_ADMIN' + AND role.status='ACTIVE' AND role.deleted=0 + AND (SELECT COUNT(*) FROM iam_role_permission grant_row + JOIN iam_permission permission ON permission.id=grant_row.permission_id + WHERE grant_row.role_id=role.id AND grant_row.tenant_id=role.tenant_id + AND permission.permission_code IN (%s)) <> 14 + """.formatted(placeholders); + try (PreparedStatement query = connection.prepareStatement(sql)) { + int index = 1; + for (String code : platformPermissions()) query.setString(index++, code); + try (ResultSet result = query.executeQuery()) { + result.next(); + return result.getInt(1); + } + } + } + + private static String administratorGrantSql() throws Exception { + String sql = sql(); + int comment = sql.indexOf("-- 平台管理员默认拥有五期全部后台动作"); + int start = sql.indexOf("INSERT IGNORE INTO iam_role_permission", comment); + int end = sql.indexOf(';', start); + assertThat(comment).isGreaterThanOrEqualTo(0); + assertThat(start).isGreaterThan(comment); + assertThat(end).isGreaterThan(start); + return sql.substring(start, end); + } + + private static Set platformPermissions() throws Exception { + LinkedHashSet codes = new LinkedHashSet<>(); + String grant = administratorGrantSql(); + PLATFORM_CODE.matcher(grant).results().map(result -> result.group()).forEach(codes::add); + assertThat(codes).hasSize(14); + return codes; + } + + private static String sql() throws Exception { + return new ClassPathResource("db/phase5-delivery-permissions.sql") + .getContentAsString(StandardCharsets.UTF_8); + } +}