fix(iam): 收敛仓库角色权限并纳入统一迁移
This commit is contained in:
@@ -43,7 +43,7 @@ INSERT INTO iam_role (
|
||||
description, deleted, version
|
||||
) VALUES
|
||||
(4000004, 0, 'PLATFORM', 'WAREHOUSE_OPERATOR', '仓库操作员', 'SYSTEM', 'ACTIVE',
|
||||
'负责排柜、预装、任务分派、装柜完成和国内出仓确认', 0, 0),
|
||||
'负责查看入仓业务并在后台维护装柜证据', 0, 0),
|
||||
(4000005, 0, 'PLATFORM', 'WAREHOUSE_PICKER', '仓库拣货员', 'SYSTEM', 'ACTIVE',
|
||||
'负责使用PDA执行柜次拣货和现场拍照', 0, 0),
|
||||
(4000006, 0, 'PLATFORM', 'WAREHOUSE_TALLY', '仓库理货员', 'SYSTEM', 'ACTIVE',
|
||||
@@ -59,71 +59,22 @@ CREATE TEMPORARY TABLE tmp_phase3_warehouse_role_permission (
|
||||
PRIMARY KEY (role_code, permission_code)
|
||||
) ENGINE=MEMORY;
|
||||
|
||||
-- 操作员:编排装柜并确认国内出仓,不授予财务、系统管理或出仓撤销权限。
|
||||
-- 操作员:查看入仓业务并维护后台装柜证据,不授予完成装柜、任务分派或出仓动作。
|
||||
INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VALUES
|
||||
('WAREHOUSE_OPERATOR', 'platform:order'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:master-data'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:warehouse:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:pallet:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:location:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:view'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:container:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:container:view'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:container:create'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:container:edit'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:smart-loading:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:smart-loading:create'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:smart-loading:assign'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:smart-loading:delete'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:smart-loading:push'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:preload:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:preload:view'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:preload:edit'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:preload:complete'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:preload:confirm'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:loading-list:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:loading-list:view'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:pda:view'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:pda:assign'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:pda:review'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:view'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:edit'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:complete'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:picking:view'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:outbound-dispatch:list'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:outbound-dispatch:view'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:outbound-dispatch:check'),
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:outbound-dispatch:confirm');
|
||||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:edit');
|
||||
|
||||
-- 拣货员:只允许柜次查询、PDA作业、拣货记录及现场照片,不授予任务分派或复核权限。
|
||||
-- 拣货员:只允许PDA任务查询、作业和现场照片,不授予后台页面、任务分派或复核权限。
|
||||
INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VALUES
|
||||
('WAREHOUSE_PICKER', 'platform:order'),
|
||||
('WAREHOUSE_PICKER', 'platform:master-data'),
|
||||
('WAREHOUSE_PICKER', 'platform:warehouse:list'),
|
||||
('WAREHOUSE_PICKER', 'platform:pallet:list'),
|
||||
('WAREHOUSE_PICKER', 'platform:location:list'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:inbound:list'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:inbound:view'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:container:list'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:container:view'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:preload:list'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:preload:view'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:loading-list:list'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:loading-list:view'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:pda:view'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:pda:work'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:inbound:loading:view'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:inbound:picking:edit'),
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:inbound:picking:view');
|
||||
('WAREHOUSE_PICKER', 'platform:firstleg:pda:work');
|
||||
|
||||
-- 理货员:负责到仓测量、装柜证据维护和PDA复核,不允许完成装柜或确认出仓。
|
||||
INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VALUES
|
||||
('WAREHOUSE_TALLY', 'platform:order'),
|
||||
('WAREHOUSE_TALLY', 'platform:master-data'),
|
||||
('WAREHOUSE_TALLY', 'platform:warehouse:list'),
|
||||
('WAREHOUSE_TALLY', 'platform:pallet:list'),
|
||||
('WAREHOUSE_TALLY', 'platform:location:list'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:list'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:view'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:measure:view'),
|
||||
@@ -131,12 +82,6 @@ INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VA
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:measure:submit'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:weight:edit'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:weight:confirm'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:container:list'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:container:view'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:preload:list'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:preload:view'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:loading-list:list'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:loading-list:view'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:pda:view'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:pda:review'),
|
||||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:view'),
|
||||
@@ -148,11 +93,11 @@ DELIMITER $$
|
||||
CREATE PROCEDURE assert_phase3_warehouse_role_permissions_available()
|
||||
BEGIN
|
||||
IF (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission
|
||||
WHERE role_code = 'WAREHOUSE_OPERATOR') <> 34
|
||||
WHERE role_code = 'WAREHOUSE_OPERATOR') <> 5
|
||||
OR (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission
|
||||
WHERE role_code = 'WAREHOUSE_PICKER') <> 18
|
||||
WHERE role_code = 'WAREHOUSE_PICKER') <> 2
|
||||
OR (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission
|
||||
WHERE role_code = 'WAREHOUSE_TALLY') <> 23 THEN
|
||||
WHERE role_code = 'WAREHOUSE_TALLY') <> 12 THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '仓库角色初始化失败:角色权限组合数量不符合预期';
|
||||
END IF;
|
||||
@@ -175,6 +120,24 @@ DELIMITER ;
|
||||
CALL assert_phase3_warehouse_role_permissions_available();
|
||||
DROP PROCEDURE assert_phase3_warehouse_role_permissions_available;
|
||||
|
||||
-- 三个系统角色的权限集合由迁移维护。重复执行时移除历史版本或人工误授的越权项,
|
||||
-- 避免最小权限角色因脚本升级只增不减。
|
||||
DELETE role_permission
|
||||
FROM iam_role_permission role_permission
|
||||
JOIN iam_role role
|
||||
ON role.id = role_permission.role_id
|
||||
AND role.tenant_id = role_permission.tenant_id
|
||||
JOIN iam_permission permission
|
||||
ON permission.id = role_permission.permission_id
|
||||
AND permission.tenant_id = role_permission.tenant_id
|
||||
LEFT JOIN tmp_phase3_warehouse_role_permission expected
|
||||
ON expected.role_code = role.role_code
|
||||
AND expected.permission_code = permission.permission_code
|
||||
WHERE role.tenant_id = 0
|
||||
AND role.app_code = 'PLATFORM'
|
||||
AND role.role_code IN ('WAREHOUSE_OPERATOR', 'WAREHOUSE_PICKER', 'WAREHOUSE_TALLY')
|
||||
AND expected.permission_code IS NULL;
|
||||
|
||||
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||||
SELECT role.id, permission.id, 0
|
||||
FROM tmp_phase3_warehouse_role_permission expected
|
||||
@@ -226,7 +189,7 @@ BEGIN
|
||||
JOIN iam_role_permission grant_row
|
||||
ON grant_row.role_id = role.id
|
||||
AND grant_row.permission_id = permission.id
|
||||
AND grant_row.tenant_id = 0) <> 75 THEN
|
||||
AND grant_row.tenant_id = 0) <> 19 THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '仓库角色初始化失败:最小权限授权未完整写入';
|
||||
END IF;
|
||||
|
||||
@@ -4,14 +4,23 @@ 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 WarehouseRoleSqlContractTest {
|
||||
@Test
|
||||
void initializesWarehouseRolesWithLeastPrivilegeAndStableIds() throws Exception {
|
||||
String sql = new ClassPathResource("db/phase3-warehouse-roles.sql")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
assertWarehouseRoleContract(moduleSql());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unifiedDeploymentContainsTheSameWarehouseRoleContract() throws Exception {
|
||||
assertWarehouseRoleContract(unifiedSql());
|
||||
}
|
||||
|
||||
private static void assertWarehouseRoleContract(String sql) {
|
||||
|
||||
assertThat(sql)
|
||||
.contains("(4000004, 0, 'PLATFORM', 'WAREHOUSE_OPERATOR', '仓库操作员', 'SYSTEM'")
|
||||
@@ -22,21 +31,31 @@ class WarehouseRoleSqlContractTest {
|
||||
.contains("'WAREHOUSE_PICKER', 'platform:firstleg:pda:work'")
|
||||
.contains("'WAREHOUSE_TALLY', 'platform:firstleg:pda:review'")
|
||||
.contains("'WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:edit'")
|
||||
.contains("'WAREHOUSE_OPERATOR', 'platform:firstleg:outbound-dispatch:confirm'")
|
||||
.contains("WHERE role_code = 'WAREHOUSE_OPERATOR') <> 34")
|
||||
.contains("WHERE role_code = 'WAREHOUSE_PICKER') <> 18")
|
||||
.contains("WHERE role_code = 'WAREHOUSE_TALLY') <> 23")
|
||||
.contains("'WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:edit'")
|
||||
.contains("WHERE role_code = 'WAREHOUSE_OPERATOR') <> 5")
|
||||
.contains("WHERE role_code = 'WAREHOUSE_PICKER') <> 2")
|
||||
.contains("WHERE role_code = 'WAREHOUSE_TALLY') <> 12")
|
||||
.contains("依赖的三期平台权限尚未完整部署")
|
||||
.contains("DELETE role_permission")
|
||||
.contains("expected.permission_code IS NULL")
|
||||
.contains("最小权限授权未完整写入")
|
||||
.contains("grant_row.tenant_id = 0) <> 19")
|
||||
.contains("(6000004, 4000004, 0, 'TENANT', NULL)")
|
||||
.contains("(6000005, 4000005, 0, 'TENANT', NULL)")
|
||||
.contains("(6000006, 4000006, 0, 'TENANT', NULL)");
|
||||
|
||||
assertRoleGrant(sql, "WAREHOUSE_PICKER", "platform:firstleg:pda:work");
|
||||
assertRoleGrant(sql, "WAREHOUSE_TALLY", "platform:firstleg:pda:review");
|
||||
assertRoleGrant(sql, "WAREHOUSE_OPERATOR", "platform:firstleg:pda:assign");
|
||||
assertRoleGrant(sql, "WAREHOUSE_OPERATOR", "platform:firstleg:inbound:loading:edit");
|
||||
|
||||
assertThat(roleGrantBlock(sql, "WAREHOUSE_PICKER"))
|
||||
.doesNotContain("platform:order")
|
||||
.doesNotContain("platform:master-data")
|
||||
.doesNotContain("platform:warehouse:list")
|
||||
.doesNotContain("platform:firstleg:inbound:")
|
||||
.doesNotContain("platform:firstleg:container:")
|
||||
.doesNotContain("platform:firstleg:preload:")
|
||||
.doesNotContain("platform:firstleg:loading-list:")
|
||||
.doesNotContain("platform:firstleg:pda:assign")
|
||||
.doesNotContain("platform:firstleg:pda:review")
|
||||
.doesNotContain("platform:firstleg:outbound-dispatch:confirm");
|
||||
@@ -44,9 +63,15 @@ class WarehouseRoleSqlContractTest {
|
||||
.doesNotContain("platform:firstleg:inbound:loading:complete")
|
||||
.doesNotContain("platform:firstleg:outbound-dispatch:confirm");
|
||||
assertThat(sql)
|
||||
.doesNotContain("'WAREHOUSE_OPERATOR', 'platform:firstleg:pda:assign'")
|
||||
.doesNotContain("'WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:complete'")
|
||||
.doesNotContain("'WAREHOUSE_OPERATOR', 'platform:firstleg:outbound-dispatch:confirm'")
|
||||
.doesNotContain("'WAREHOUSE_OPERATOR', 'platform:firstleg:outbound-dispatch:reverse'")
|
||||
.doesNotContain("'WAREHOUSE_PICKER', 'platform:firstleg:smart-loading:delete'")
|
||||
.doesNotContain("'WAREHOUSE_TALLY', 'firstleg:inbound:fee-detail:edit'");
|
||||
.doesNotContain("'WAREHOUSE_TALLY', 'firstleg:inbound:fee-detail:edit'")
|
||||
.doesNotContain("'WAREHOUSE_PICKER', 'merchant:")
|
||||
.doesNotContain("'WAREHOUSE_OPERATOR', 'merchant:")
|
||||
.doesNotContain("'WAREHOUSE_TALLY', 'merchant:");
|
||||
}
|
||||
|
||||
private static void assertRoleGrant(String sql, String roleCode, String permissionCode) {
|
||||
@@ -63,4 +88,17 @@ class WarehouseRoleSqlContractTest {
|
||||
int end = sql.indexOf("\n-- ", start + marker.length());
|
||||
return sql.substring(start, end > start ? end : sql.length());
|
||||
}
|
||||
|
||||
private static String moduleSql() throws Exception {
|
||||
return new ClassPathResource("db/phase3-warehouse-roles.sql")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static String unifiedSql() throws Exception {
|
||||
Path path = Path.of("deploy/sql/头程数据库统一增量-MySQL8.sql");
|
||||
if (!Files.exists(path)) {
|
||||
path = Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql").normalize();
|
||||
}
|
||||
return Files.readString(path, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user