diff --git a/sql/diagnose_data_perm_bad_guids.sql b/sql/diagnose_data_perm_bad_guids.sql new file mode 100644 index 0000000..a97359a --- /dev/null +++ b/sql/diagnose_data_perm_bad_guids.sql @@ -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-%'; diff --git a/sql/fix_data_perm_audit_menu.sql b/sql/fix_data_perm_audit_menu.sql index 59d744c..27b6611 100644 --- a/sql/fix_data_perm_audit_menu.sql +++ b/sql/fix_data_perm_audit_menu.sql @@ -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'); diff --git a/sql/fix_data_perm_menu_invalid_guids.sql b/sql/fix_data_perm_menu_invalid_guids.sql new file mode 100644 index 0000000..eba625b --- /dev/null +++ b/sql/fix_data_perm_menu_invalid_guids.sql @@ -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}$'); diff --git a/sql/fix_data_perm_role_permission_bad_guids.sql b/sql/fix_data_perm_role_permission_bad_guids.sql new file mode 100644 index 0000000..b659964 --- /dev/null +++ b/sql/fix_data_perm_role_permission_bad_guids.sql @@ -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}$'); diff --git a/sql/fix_data_perm_template_bad_guids.sql b/sql/fix_data_perm_template_bad_guids.sql new file mode 100644 index 0000000..8a9b9d0 --- /dev/null +++ b/sql/fix_data_perm_template_bad_guids.sql @@ -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; diff --git a/sql/install_data_permission.sql b/sql/install_data_permission.sql index c9e5e53..67857bd 100644 --- a/sql/install_data_permission.sql +++ b/sql/install_data_permission.sql @@ -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`); diff --git a/sql/seed_data_perm_rbac_menu.sql b/sql/seed_data_perm_rbac_menu.sql index 4271daf..b33a4bb 100644 --- a/sql/seed_data_perm_rbac_menu.sql +++ b/sql/seed_data_perm_rbac_menu.sql @@ -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 diff --git a/src/Oms.Application/DataPerm/DataPermStore.cs b/src/Oms.Application/DataPerm/DataPermStore.cs index 0b6cc5e..737ac6e 100644 --- a/src/Oms.Application/DataPerm/DataPermStore.cs +++ b/src/Oms.Application/DataPerm/DataPermStore.cs @@ -499,8 +499,18 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac LIMIT {pageSize} OFFSET {offset} """; - var items = await dbContext.Database.SqlQueryRaw(listSql, args.ToArray()) + var rows = await dbContext.Database.SqlQueryRaw(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 { 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(listSql).ToListAsync(cancellationToken); - return new DataPermPagedResult { Total = total, Items = items }; + var items = await dbContext.Database.SqlQueryRaw(listSql) + .ToListAsync(cancellationToken); + return new DataPermPagedResult + { + 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> GetTemplateListAsync( @@ -594,12 +617,18 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac LIMIT {pageSize} OFFSET {offset} """; - var items = await dbContext.Database.SqlQueryRaw(sql, args.ToArray()) + var rows = await dbContext.Database.SqlQueryRaw(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 { Total = total, Items = items }; } @@ -614,7 +643,7 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac LIMIT 1 """; - var header = await dbContext.Database.SqlQueryRaw(headerSql, templateId.ToString()) + var headerRow = await dbContext.Database.SqlQueryRaw(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(itemSql, templateId.ToString()) .ToListAsync(cancellationToken); - header.Items = itemRows.Select(x => new DataPermTemplateItemDto + var header = new DataPermTemplateDetailDto { - Id = x.Id.ToString(), - DimensionType = x.DimensionType, - ItemContent = JsonSerializer.Deserialize>(x.ItemContentJson, JsonOptions) ?? [], - SortOrder = x.SortOrder - }).ToList(); + 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>(x.ItemContentJson, JsonOptions) ?? [], + SortOrder = x.SortOrder + }).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; }