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);
+ }
+}