fix(数据权限): 修复 GUID 种子/SqlQuery 映射并补充诊断修复 SQL

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-07-03 11:58:24 +08:00
parent c442833d9c
commit ad367998d2
8 changed files with 376 additions and 54 deletions

View File

@ -0,0 +1,62 @@
-- 数据权限表:诊断 CHAR(36) 非法 GUID
-- 症状:GET /rbac/data-perm/template/list → Could not parse CHAR(36) value as Guid: 1
-- 或其它 data-perm 接口同类报错
--
-- ⚠️ 在 API 实际连接的 RBAC 库执行(与 user/role/menu 同库)
SET @GUID_RE = '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$';
SELECT 'data_perm_template bad Id' AS section, t.`Id`, t.`Name`, t.`Code`, t.`CreateUserId`
FROM `data_perm_template` t
WHERE t.`Id` NOT REGEXP @GUID_RE;
SELECT 'data_perm_template_item bad Id/TemplateId' AS section, i.`Id`, i.`TemplateId`, i.`DimensionType`
FROM `data_perm_template_item` i
WHERE i.`Id` NOT REGEXP @GUID_RE
OR i.`TemplateId` NOT REGEXP @GUID_RE;
SELECT 'data_resource bad Id' AS section, r.`Id`, r.`Code`, r.`Name`
FROM `data_resource` r
WHERE r.`Id` NOT REGEXP @GUID_RE;
SELECT 'data_perm_field_mask bad Id' AS section, f.`Id`, f.`DataResourceCode`, f.`FieldName`
FROM `data_perm_field_mask` f
WHERE f.`Id` NOT REGEXP @GUID_RE;
SELECT 'user_org_perm bad Id/UserId/OrganizationId' AS section, p.`Id`, p.`UserId`, p.`OrganizationId`
FROM `user_org_perm` p
WHERE p.`Id` NOT REGEXP @GUID_RE
OR p.`UserId` NOT REGEXP @GUID_RE
OR p.`OrganizationId` NOT REGEXP @GUID_RE;
SELECT 'role_org_perm bad Id/RoleId/OrganizationId' AS section, p.`Id`, p.`RoleId`, p.`OrganizationId`
FROM `role_org_perm` p
WHERE p.`Id` NOT REGEXP @GUID_RE
OR p.`RoleId` NOT REGEXP @GUID_RE
OR p.`OrganizationId` NOT REGEXP @GUID_RE;
SELECT 'user_data_scope bad Id/UserId' AS section, p.`Id`, p.`UserId`, p.`DataResourceCode`
FROM `user_data_scope` p
WHERE p.`Id` NOT REGEXP @GUID_RE
OR p.`UserId` NOT REGEXP @GUID_RE;
SELECT 'role_data_scope bad Id/RoleId' AS section, p.`Id`, p.`RoleId`, p.`DataResourceCode`
FROM `role_data_scope` p
WHERE p.`Id` NOT REGEXP @GUID_RE
OR p.`RoleId` NOT REGEXP @GUID_RE;
SELECT 'data_perm_change_log bad TargetId' AS section, l.`Id`, l.`TargetType`, l.`TargetId`, l.`Action`
FROM `data_perm_change_log` l
WHERE l.`TargetId` NOT REGEXP @GUID_RE;
SELECT 'data_perm_access_log bad UserId' AS section, l.`Id`, l.`UserId`, l.`DataResourceCode`, l.`ApiPath`
FROM `data_perm_access_log` l
WHERE l.`UserId` NOT REGEXP @GUID_RE;
-- 常见脏值快捷统计
SELECT 'template Id=1' AS hint, COUNT(*) AS cnt FROM `data_perm_template` WHERE TRIM(`Id`) IN ('0', '1', '2');
SELECT 'template_item Id=1' AS hint, COUNT(*) AS cnt FROM `data_perm_template_item` WHERE TRIM(`Id`) IN ('0', '1', '2');
SELECT 'template dp prefix' AS hint, COUNT(*) AS cnt FROM `data_perm_template` WHERE `Id` LIKE 'dp%';
SELECT 'template dr prefix' AS hint, COUNT(*) AS cnt FROM `data_perm_template` WHERE `Id` LIKE 'dr%';
SELECT 'data_resource dr000001 prefix' AS hint, COUNT(*) AS cnt FROM `data_resource` WHERE `Id` LIKE 'dr000001-%';
SELECT 'field_mask fm000001 prefix' AS hint, COUNT(*) AS cnt FROM `data_perm_field_mask` WHERE `Id` LIKE 'fm000001-%';

View File

@ -6,5 +6,5 @@ SET
`Route` = '/system/dataPerm/audit',
`Component` = 'views/system/dataPerm/audit/index',
`UpdateTime` = NOW()
WHERE `Id` = 'dp100001-0000-4000-8000-000000000004'
WHERE `Id` = 'da100001-0000-4000-8000-000000000004'
OR (`Route` = '/system/dataPerm/accessLog' AND `PermissionCode` = 'Rbac.DataPerm.Audit');

View File

@ -0,0 +1,25 @@
-- 修复 menu 表非法 GUID(Id/PId 含非十六进制字符,如 dp100001 中的 p)
-- 症状:登录 / get-menu-tree → Could not parse CHAR(36) value as Guid: dp100001-...
-- GUID 仅允许 0-9、a-f
--
-- ⚠️ 在 RBAC 库执行
SELECT 'before fix' AS stage, `Id`, `PId`, `Name`, `Route`, `PermissionCode`
FROM `menu`
WHERE `Id` LIKE 'dp100001-%'
OR `PId` LIKE 'dp100001-%';
-- 先改子节点 PId,再改 Id
UPDATE `menu`
SET `PId` = REPLACE(`PId`, 'dp100001-', 'da100001-')
WHERE `PId` LIKE 'dp100001-%';
UPDATE `menu`
SET `Id` = REPLACE(`Id`, 'dp100001-', 'da100001-')
WHERE `Id` LIKE 'dp100001-%';
SELECT 'after fix (should be empty)' AS stage, `Id`, `PId`, `Name`, `Route`
FROM `menu`
WHERE `Id` LIKE 'dp100001-%'
OR `PId` LIKE 'dp100001-%'
OR (`Id` NOT REGEXP '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$');

View File

@ -0,0 +1,32 @@
-- 修复 role_permission.CreateUserId = '0' 导致登录失败
-- 症状:POST /rbac/auth/login → Could not parse CHAR(36) value as Guid: 0
-- 原因:seed_data_perm_rbac_menu.sql 曾写入 CreateUserId = 0
--
-- ⚠️ 在 RBAC 库(fake_simple_admin 或 API 连接的 RBAC 库)执行
SET @ADMIN_USER_ID = (
SELECT `Id` FROM `user`
WHERE `Account` = 'admin' AND (`IsDeleted` = 0 OR `IsDeleted` IS NULL)
LIMIT 1
);
SET @ADMIN_USER_ID = IFNULL(
@ADMIN_USER_ID,
(SELECT `Id` FROM `user` WHERE `IsDeleted` = 0 OR `IsDeleted` IS NULL ORDER BY `CreateTime` LIMIT 1)
);
SELECT @ADMIN_USER_ID AS admin_user_id_used;
SELECT 'before fix' AS stage, rp.`Id`, rp.`RoleId`, rp.`PermissionCode`, rp.`CreateUserId`
FROM `role_permission` rp
WHERE CAST(rp.`CreateUserId` AS CHAR) = '0'
OR (rp.`CreateUserId` IS NOT NULL AND rp.`CreateUserId` NOT REGEXP '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$');
UPDATE `role_permission`
SET `CreateUserId` = @ADMIN_USER_ID
WHERE CAST(`CreateUserId` AS CHAR) = '0'
OR (`CreateUserId` IS NOT NULL AND `CreateUserId` NOT REGEXP '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$');
SELECT 'after fix (should be empty)' AS stage, rp.`Id`, rp.`RoleId`, rp.`PermissionCode`, rp.`CreateUserId`
FROM `role_permission` rp
WHERE CAST(rp.`CreateUserId` AS CHAR) = '0'
OR (rp.`CreateUserId` IS NOT NULL AND rp.`CreateUserId` NOT REGEXP '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$');

View File

@ -0,0 +1,101 @@
-- 修复 data_perm_* 表非法 GUID(如 Id='1'、dp/dr 前缀含非 hex 字符)
-- 症状:GET /rbac/data-perm/template/list → Could not parse CHAR(36) value as Guid: 1
--
-- ⚠️ 在 RBAC 库执行;执行前先跑 diagnose_data_perm_bad_guids.sql 确认脏数据
-- 若库中从未跑过 install_data_permission.sql,请先执行该脚本再跑本修复
SET @GUID_RE = '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$';
SELECT 'before fix — bad template rows' AS stage, t.`Id`, t.`Name`, t.`Code`
FROM `data_perm_template` t
WHERE t.`Id` NOT REGEXP @GUID_RE;
-- 1) 旧种子前缀 dp/dr → da(与 install_data_permission.sql 一致)
UPDATE `data_perm_template_item`
SET `TemplateId` = REPLACE(`TemplateId`, 'dp000001-', 'da000001-')
WHERE `TemplateId` LIKE 'dp000001-%';
UPDATE `data_perm_template_item`
SET `Id` = REPLACE(`Id`, 'dp200001-', 'da200001-')
WHERE `Id` LIKE 'dp200001-%';
UPDATE `data_perm_template_item`
SET `Id` = REPLACE(`Id`, 'dr200001-', 'da200001-')
WHERE `Id` LIKE 'dr200001-%';
UPDATE `data_perm_template`
SET `Id` = REPLACE(`Id`, 'dp000001-', 'da000001-')
WHERE `Id` LIKE 'dp000001-%';
UPDATE `data_perm_template`
SET `Id` = REPLACE(`Id`, 'dr000001-', 'da000001-')
WHERE `Id` LIKE 'dr000001-%';
UPDATE `data_resource`
SET `Id` = REPLACE(`Id`, 'dr000001-', 'da300001-')
WHERE `Id` LIKE 'dr000001-%';
UPDATE `data_resource`
SET `Id` = REPLACE(`Id`, 'dr300001-', 'da300001-')
WHERE `Id` LIKE 'dr300001-%';
UPDATE `data_resource`
SET `Id` = REPLACE(`Id`, 'dp300001-', 'da300001-')
WHERE `Id` LIKE 'dp300001-%';
UPDATE `data_perm_field_mask`
SET `Id` = REPLACE(`Id`, 'fm000001-', 'fa000001-')
WHERE `Id` LIKE 'fm000001-%';
-- 2) 删除无法自动映射的脏模板
DELETE FROM `data_perm_template_item`
WHERE `TemplateId` IN (
SELECT `Id` FROM (
SELECT `Id` FROM `data_perm_template` WHERE TRIM(`Id`) NOT REGEXP @GUID_RE
) AS bad_tpl
);
DELETE FROM `data_perm_template`
WHERE TRIM(`Id`) NOT REGEXP @GUID_RE;
-- 3) 重新插入内置模板(与 install_data_permission.sql §12 一致,可重复执行)
INSERT INTO `data_perm_template` (`Id`, `Name`, `Code`, `Description`, `RoleCode`, `IsBuiltIn`, `CreateUserId`, `CreateTime`) VALUES
('da000001-0000-4000-8000-000000000001', '普通销售', 'SalesStaff', '个人级+指定产品线+密级1-2', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000002', '销售主管', 'SalesSupervisor', '部门级+全部产品线+密级1-3', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000003', '分公司经理', 'BranchManager', '组织树级+密级1-3', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000004', '总部产品经理', 'ProductManager', '全局级+单产品线+密级1-2', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000005', '财务专员', 'FinanceStaff', '组织树级+密级含3-4', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000006', '客服专员', 'CsStaff', '个人级+密级1-2', NULL, 1, 0, NOW())
ON DUPLICATE KEY UPDATE
`Name` = VALUES(`Name`),
`Description` = VALUES(`Description`),
`IsBuiltIn` = VALUES(`IsBuiltIn`);
INSERT INTO `data_perm_template_item` (`Id`, `TemplateId`, `DimensionType`, `ItemContent`, `SortOrder`) VALUES
('da200001-0000-4000-8000-000000000011', 'da000001-0000-4000-8000-000000000001', 'data_scope', '{"dataScope":1,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000012', 'da000001-0000-4000-8000-000000000001', 'secret', '{"secretLevels":[1,2]}', 2),
('da200001-0000-4000-8000-000000000021', 'da000001-0000-4000-8000-000000000002', 'data_scope', '{"dataScope":3,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000022', 'da000001-0000-4000-8000-000000000002', 'secret', '{"secretLevels":[1,2,3]}', 2),
('da200001-0000-4000-8000-000000000031', 'da000001-0000-4000-8000-000000000003', 'data_scope', '{"dataScope":4,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000032', 'da000001-0000-4000-8000-000000000003', 'secret', '{"secretLevels":[1,2,3]}', 2),
('da200001-0000-4000-8000-000000000041', 'da000001-0000-4000-8000-000000000004', 'data_scope', '{"dataScope":5,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000042', 'da000001-0000-4000-8000-000000000004', 'secret', '{"secretLevels":[1,2]}', 2),
('da200001-0000-4000-8000-000000000051', 'da000001-0000-4000-8000-000000000005', 'data_scope', '{"dataScope":4,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000052', 'da000001-0000-4000-8000-000000000005', 'secret', '{"secretLevels":[1,2,3,4]}', 2),
('da200001-0000-4000-8000-000000000061', 'da000001-0000-4000-8000-000000000006', 'data_scope', '{"dataScope":1,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000062', 'da000001-0000-4000-8000-000000000006', 'secret', '{"secretLevels":[1,2]}', 2)
ON DUPLICATE KEY UPDATE
`ItemContent` = VALUES(`ItemContent`),
`SortOrder` = VALUES(`SortOrder`);
SELECT 'after fix — bad template' AS stage, t.`Id`, t.`Name`, t.`Code`
FROM `data_perm_template` t
WHERE t.`Id` NOT REGEXP @GUID_RE;
SELECT 'after fix — bad data_resource' AS stage, r.`Id`, r.`Code`, r.`Name`
FROM `data_resource` r
WHERE r.`Id` NOT REGEXP @GUID_RE;
SELECT 'after fix — bad field_mask' AS stage, f.`Id`, f.`DataResourceCode`, f.`FieldName`
FROM `data_perm_field_mask` f
WHERE f.`Id` NOT REGEXP @GUID_RE;

View File

@ -285,11 +285,11 @@ CREATE TABLE IF NOT EXISTS `data_perm_access_log` (
-- 11. 种子:数据资源注册
-- ---------------------------------------------------------------------------
INSERT INTO `data_resource` (`Id`, `Code`, `Name`, `Description`, `MainTable`, `OrgField`, `OwnerField`, `SortOrder`) VALUES
('dr000001-0000-4000-8000-000000000001', 'Order', '订单', '运营后台订单查询', 't_order', 'OrganizationId', 'CreateUserId', 10),
('dr000001-0000-4000-8000-000000000002', 'Customer', '客户', '客户资料列表', 't_customer', 'OrganizationId', 'Salesman', 20),
('dr000001-0000-4000-8000-000000000003', 'Recharge', '充值', '充值审核列表', 't_recharge_record', NULL, 'CreateUserId', 30),
('dr000001-0000-4000-8000-000000000004', 'ChannelProduct', '销售产品', '渠道产品配置', 't_sales_product', NULL, NULL, 40),
('dr000001-0000-4000-8000-000000000005', 'Xhs', '小红书', '小红书模块', NULL, NULL, NULL, 50)
('da300001-0000-4000-8000-000000000001', 'Order', '订单', '运营后台订单查询', 't_order', 'OrganizationId', 'CreateUserId', 10),
('da300001-0000-4000-8000-000000000002', 'Customer', '客户', '客户资料列表', 't_customer', 'OrganizationId', 'Salesman', 20),
('da300001-0000-4000-8000-000000000003', 'Recharge', '充值', '充值审核列表', 't_recharge_record', NULL, 'CreateUserId', 30),
('da300001-0000-4000-8000-000000000004', 'ChannelProduct', '销售产品', '渠道产品配置', 't_sales_product', NULL, NULL, 40),
('da300001-0000-4000-8000-000000000005', 'Xhs', '小红书', '小红书模块', NULL, NULL, NULL, 50)
ON DUPLICATE KEY UPDATE
`Name` = VALUES(`Name`),
`Description` = VALUES(`Description`),
@ -301,35 +301,35 @@ ON DUPLICATE KEY UPDATE
-- 12. 种子:内置权限模板(明细需按环境组织 Id 调整后再 apply)
-- ---------------------------------------------------------------------------
INSERT INTO `data_perm_template` (`Id`, `Name`, `Code`, `Description`, `RoleCode`, `IsBuiltIn`, `CreateUserId`, `CreateTime`) VALUES
('dp000001-0000-4000-8000-000000000001', '普通销售', 'SalesStaff', '个人级+指定产品线+密级1-2', NULL, 1, 0, NOW()),
('dp000001-0000-4000-8000-000000000002', '销售主管', 'SalesSupervisor', '部门级+全部产品线+密级1-3', NULL, 1, 0, NOW()),
('dp000001-0000-4000-8000-000000000003', '分公司经理', 'BranchManager', '组织树级+密级1-3', NULL, 1, 0, NOW()),
('dp000001-0000-4000-8000-000000000004', '总部产品经理', 'ProductManager', '全局级+单产品线+密级1-2', NULL, 1, 0, NOW()),
('dp000001-0000-4000-8000-000000000005', '财务专员', 'FinanceStaff', '组织树级+密级含3-4', NULL, 1, 0, NOW()),
('dp000001-0000-4000-8000-000000000006', '客服专员', 'CsStaff', '个人级+密级1-2', NULL, 1, 0, NOW())
('da000001-0000-4000-8000-000000000001', '普通销售', 'SalesStaff', '个人级+指定产品线+密级1-2', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000002', '销售主管', 'SalesSupervisor', '部门级+全部产品线+密级1-3', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000003', '分公司经理', 'BranchManager', '组织树级+密级1-3', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000004', '总部产品经理', 'ProductManager', '全局级+单产品线+密级1-2', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000005', '财务专员', 'FinanceStaff', '组织树级+密级含3-4', NULL, 1, 0, NOW()),
('da000001-0000-4000-8000-000000000006', '客服专员', 'CsStaff', '个人级+密级1-2', NULL, 1, 0, NOW())
ON DUPLICATE KEY UPDATE
`Name` = VALUES(`Name`),
`Description` = VALUES(`Description`);
INSERT INTO `data_perm_template_item` (`Id`, `TemplateId`, `DimensionType`, `ItemContent`, `SortOrder`) VALUES
-- 普通销售
('dp200001-0000-4000-8000-000000000011', 'dp000001-0000-4000-8000-000000000001', 'data_scope', '{"dataScope":1,"dataResourceCode":null}', 1),
('dp200001-0000-4000-8000-000000000012', 'dp000001-0000-4000-8000-000000000001', 'secret', '{"secretLevels":[1,2]}', 2),
('da200001-0000-4000-8000-000000000011', 'da000001-0000-4000-8000-000000000001', 'data_scope', '{"dataScope":1,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000012', 'da000001-0000-4000-8000-000000000001', 'secret', '{"secretLevels":[1,2]}', 2),
-- 销售主管
('dp200001-0000-4000-8000-000000000021', 'dp000001-0000-4000-8000-000000000002', 'data_scope', '{"dataScope":3,"dataResourceCode":null}', 1),
('dp200001-0000-4000-8000-000000000022', 'dp000001-0000-4000-8000-000000000002', 'secret', '{"secretLevels":[1,2,3]}', 2),
('da200001-0000-4000-8000-000000000021', 'da000001-0000-4000-8000-000000000002', 'data_scope', '{"dataScope":3,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000022', 'da000001-0000-4000-8000-000000000002', 'secret', '{"secretLevels":[1,2,3]}', 2),
-- 分公司经理
('dp200001-0000-4000-8000-000000000031', 'dp000001-0000-4000-8000-000000000003', 'data_scope', '{"dataScope":4,"dataResourceCode":null}', 1),
('dp200001-0000-4000-8000-000000000032', 'dp000001-0000-4000-8000-000000000003', 'secret', '{"secretLevels":[1,2,3]}', 2),
('da200001-0000-4000-8000-000000000031', 'da000001-0000-4000-8000-000000000003', 'data_scope', '{"dataScope":4,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000032', 'da000001-0000-4000-8000-000000000003', 'secret', '{"secretLevels":[1,2,3]}', 2),
-- 总部产品经理
('dp200001-0000-4000-8000-000000000041', 'dp000001-0000-4000-8000-000000000004', 'data_scope', '{"dataScope":5,"dataResourceCode":null}', 1),
('dp200001-0000-4000-8000-000000000042', 'dp000001-0000-4000-8000-000000000004', 'secret', '{"secretLevels":[1,2]}', 2),
('da200001-0000-4000-8000-000000000041', 'da000001-0000-4000-8000-000000000004', 'data_scope', '{"dataScope":5,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000042', 'da000001-0000-4000-8000-000000000004', 'secret', '{"secretLevels":[1,2]}', 2),
-- 财务专员
('dp200001-0000-4000-8000-000000000051', 'dp000001-0000-4000-8000-000000000005', 'data_scope', '{"dataScope":4,"dataResourceCode":null}', 1),
('dp200001-0000-4000-8000-000000000052', 'dp000001-0000-4000-8000-000000000005', 'secret', '{"secretLevels":[1,2,3,4]}', 2),
('da200001-0000-4000-8000-000000000051', 'da000001-0000-4000-8000-000000000005', 'data_scope', '{"dataScope":4,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000052', 'da000001-0000-4000-8000-000000000005', 'secret', '{"secretLevels":[1,2,3,4]}', 2),
-- 客服专员
('dp200001-0000-4000-8000-000000000061', 'dp000001-0000-4000-8000-000000000006', 'data_scope', '{"dataScope":1,"dataResourceCode":null}', 1),
('dp200001-0000-4000-8000-000000000062', 'dp000001-0000-4000-8000-000000000006', 'secret', '{"secretLevels":[1,2]}', 2)
('da200001-0000-4000-8000-000000000061', 'da000001-0000-4000-8000-000000000006', 'data_scope', '{"dataScope":1,"dataResourceCode":null}', 1),
('da200001-0000-4000-8000-000000000062', 'da000001-0000-4000-8000-000000000006', 'secret', '{"secretLevels":[1,2]}', 2)
ON DUPLICATE KEY UPDATE
`ItemContent` = VALUES(`ItemContent`),
`SortOrder` = VALUES(`SortOrder`);
@ -338,12 +338,12 @@ ON DUPLICATE KEY UPDATE
-- 13. 种子:字段脱敏(Customer / Order)
-- ---------------------------------------------------------------------------
INSERT INTO `data_perm_field_mask` (`Id`, `DataResourceCode`, `FieldName`, `SecretLevel`, `MaskType`, `MaskPattern`) VALUES
('fm000001-0000-4000-8000-000000000001', 'Customer', 'Phone', 4, 'partial', 'phone'),
('fm000001-0000-4000-8000-000000000002', 'Customer', 'Email', 4, 'partial', 'email'),
('fm000001-0000-4000-8000-000000000003', 'Customer', 'Password', 1, 'hide', NULL),
('fm000001-0000-4000-8000-000000000004', 'Order', 'PayableAmount', 3, 'hide', NULL),
('fm000001-0000-4000-8000-000000000005', 'Order', 'CostAmount', 3, 'hide', NULL),
('fm000001-0000-4000-8000-000000000006', 'Order', 'ProfitAmount', 4, 'hide', NULL)
('fa000001-0000-4000-8000-000000000001', 'Customer', 'Phone', 4, 'partial', 'phone'),
('fa000001-0000-4000-8000-000000000002', 'Customer', 'Email', 4, 'partial', 'email'),
('fa000001-0000-4000-8000-000000000003', 'Customer', 'Password', 1, 'hide', NULL),
('fa000001-0000-4000-8000-000000000004', 'Order', 'PayableAmount', 3, 'hide', NULL),
('fa000001-0000-4000-8000-000000000005', 'Order', 'CostAmount', 3, 'hide', NULL),
('fa000001-0000-4000-8000-000000000006', 'Order', 'ProfitAmount', 4, 'hide', NULL)
ON DUPLICATE KEY UPDATE
`SecretLevel` = VALUES(`SecretLevel`),
`MaskType` = VALUES(`MaskType`);

View File

@ -26,7 +26,7 @@ INSERT INTO `menu` (
`ConcurrencyStamp`, `CreateUserId`, `CreateTime`, `UpdateUserId`, `UpdateTime`
) VALUES
(
'dp100001-0000-4000-8000-000000000001',
'da100001-0000-4000-8000-000000000001',
@SYSTEM_PID,
'数据权限',
'Rbac.DataPerm',
@ -39,8 +39,8 @@ INSERT INTO `menu` (
UUID(), @ADMIN_USER_ID, NOW(), @ADMIN_USER_ID, NOW()
),
(
'dp100001-0000-4000-8000-000000000002',
'dp100001-0000-4000-8000-000000000001',
'da100001-0000-4000-8000-000000000002',
'da100001-0000-4000-8000-000000000001',
'用户数据权限',
'Rbac.DataPerm.Query',
1,
@ -52,8 +52,8 @@ INSERT INTO `menu` (
UUID(), @ADMIN_USER_ID, NOW(), @ADMIN_USER_ID, NOW()
),
(
'dp100001-0000-4000-8000-000000000003',
'dp100001-0000-4000-8000-000000000001',
'da100001-0000-4000-8000-000000000003',
'da100001-0000-4000-8000-000000000001',
'权限模板',
'Rbac.DataPerm.Template',
1,
@ -65,8 +65,8 @@ INSERT INTO `menu` (
UUID(), @ADMIN_USER_ID, NOW(), @ADMIN_USER_ID, NOW()
),
(
'dp100001-0000-4000-8000-000000000004',
'dp100001-0000-4000-8000-000000000001',
'da100001-0000-4000-8000-000000000004',
'da100001-0000-4000-8000-000000000001',
'访问审计',
'Rbac.DataPerm.Audit',
1,
@ -78,8 +78,8 @@ INSERT INTO `menu` (
UUID(), @ADMIN_USER_ID, NOW(), @ADMIN_USER_ID, NOW()
),
(
'dp100001-0000-4000-8000-000000000005',
'dp100001-0000-4000-8000-000000000002',
'da100001-0000-4000-8000-000000000005',
'da100001-0000-4000-8000-000000000002',
'保存用户权限',
'Rbac.DataPerm.Update',
2,
@ -100,7 +100,7 @@ ON DUPLICATE KEY UPDATE
-- 超管角色分配数据权限(若存在 Admin 角色)
INSERT INTO `role_permission` (`Id`, `RoleId`, `PermissionCode`, `CreateUserId`, `CreateTime`)
SELECT UUID(), r.`Id`, p.`Code`, 0, NOW()
SELECT UUID(), r.`Id`, p.`Code`, @ADMIN_USER_ID, NOW()
FROM `role` r
CROSS JOIN (
SELECT 'Rbac.DataPerm' AS Code UNION ALL

View File

@ -499,8 +499,18 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
LIMIT {pageSize} OFFSET {offset}
""";
var items = await dbContext.Database.SqlQueryRaw<ChangeLogItemDto>(listSql, args.ToArray())
var rows = await dbContext.Database.SqlQueryRaw<ChangeLogRow>(listSql, args.ToArray())
.ToListAsync(cancellationToken);
var items = rows.Select(x => new ChangeLogItemDto
{
Id = x.Id,
TargetType = x.TargetType,
TargetId = x.TargetId.ToString(),
Action = x.Action,
OperatorUserId = x.OperatorUserId?.ToString(),
OperatorMapId = x.OperatorMapId,
CreateTime = x.CreateTime
}).ToList();
return new DataPermPagedResult<ChangeLogItemDto> { Total = total, Items = items };
}
@ -560,8 +570,21 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
LIMIT {pageSize} OFFSET {offset}
""";
var items = await dbContext.Database.SqlQueryRaw<AccessLogItemDto>(listSql).ToListAsync(cancellationToken);
return new DataPermPagedResult<AccessLogItemDto> { Total = total, Items = items };
var items = await dbContext.Database.SqlQueryRaw<AccessLogRow>(listSql)
.ToListAsync(cancellationToken);
return new DataPermPagedResult<AccessLogItemDto>
{
Total = total,
Items = items.Select(x => new AccessLogItemDto
{
Id = x.Id,
UserId = x.UserId.ToString(),
DataResourceCode = x.DataResourceCode,
ApiPath = x.ApiPath,
RowCount = x.RowCount,
CreateTime = x.CreateTime
}).ToList()
};
}
public async Task<DataPermPagedResult<DataPermTemplateListItemDto>> GetTemplateListAsync(
@ -594,12 +617,18 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
LIMIT {pageSize} OFFSET {offset}
""";
var items = await dbContext.Database.SqlQueryRaw<DataPermTemplateListItemDto>(sql, args.ToArray())
var rows = await dbContext.Database.SqlQueryRaw<TemplateListRow>(sql, args.ToArray())
.ToListAsync(cancellationToken);
foreach (var item in items)
var items = rows.Select(x => new DataPermTemplateListItemDto
{
item.IsBuiltIn = item.IsBuiltIn;
}
Id = x.Id.ToString(),
Name = x.Name,
Code = x.Code,
Description = x.Description,
RoleCode = x.RoleCode,
IsBuiltIn = x.IsBuiltIn != 0,
ItemCount = x.ItemCount
}).ToList();
return new DataPermPagedResult<DataPermTemplateListItemDto> { Total = total, Items = items };
}
@ -614,7 +643,7 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
LIMIT 1
""";
var header = await dbContext.Database.SqlQueryRaw<DataPermTemplateDetailDto>(headerSql, templateId.ToString())
var headerRow = await dbContext.Database.SqlQueryRaw<TemplateHeaderRow>(headerSql, templateId.ToString())
.FirstOrDefaultAsync(cancellationToken)
?? throw new BusinessException("模板不存在");
@ -628,13 +657,22 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
var itemRows = await dbContext.Database.SqlQueryRaw<TemplateItemRow>(itemSql, templateId.ToString())
.ToListAsync(cancellationToken);
header.Items = itemRows.Select(x => new DataPermTemplateItemDto
var header = new DataPermTemplateDetailDto
{
Id = headerRow.Id.ToString(),
Name = headerRow.Name,
Code = headerRow.Code,
Description = headerRow.Description,
RoleCode = headerRow.RoleCode,
IsBuiltIn = headerRow.IsBuiltIn != 0,
Items = itemRows.Select(x => new DataPermTemplateItemDto
{
Id = x.Id.ToString(),
DimensionType = x.DimensionType,
ItemContent = JsonSerializer.Deserialize<Dictionary<string, object?>>(x.ItemContentJson, JsonOptions) ?? [],
SortOrder = x.SortOrder
}).ToList();
}).ToList()
};
return header;
}
@ -856,6 +894,70 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
public string FilterContentJson { get; set; } = string.Empty;
}
private sealed class ChangeLogRow
{
public long Id { get; set; }
public string TargetType { get; set; } = string.Empty;
public Guid TargetId { get; set; }
public string Action { get; set; } = string.Empty;
public Guid? OperatorUserId { get; set; }
public int? OperatorMapId { get; set; }
public DateTime CreateTime { get; set; }
}
private sealed class AccessLogRow
{
public long Id { get; set; }
public Guid UserId { get; set; }
public string DataResourceCode { get; set; } = string.Empty;
public string ApiPath { get; set; } = string.Empty;
public int? RowCount { get; set; }
public DateTime CreateTime { get; set; }
}
private sealed class TemplateListRow
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Code { get; set; } = string.Empty;
public string? Description { get; set; }
public string? RoleCode { get; set; }
public int IsBuiltIn { get; set; }
public int ItemCount { get; set; }
}
private sealed class TemplateHeaderRow
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Code { get; set; } = string.Empty;
public string? Description { get; set; }
public string? RoleCode { get; set; }
public int IsBuiltIn { get; set; }
}
private sealed class TemplateItemRow
{
public Guid Id { get; set; }