-- ========================================================= -- RBAC/菜单/权限 数据审计(只读) -- 目标:快速定位“已授权但菜单为空 / 权限缺失 / 绑定缺失”等问题 -- 说明:不做任何写操作,可在生产环境安全执行 -- ========================================================= -- 0) 基础:租户列表 SELECT id, code, name, tenant_type, is_super, valid_state, deleted FROM t_sys_tenant WHERE deleted = 0 ORDER BY id; -- 1) 角色存在但未做菜单授权(role_menu 为空) -- 重点:tenant_admin / school_admin / teacher / student / judge 等关键角色 SELECT r.tenant_id, t.code AS tenant_code, r.id AS role_id, r.code AS role_code, r.name AS role_name FROM t_auth_role r JOIN t_sys_tenant t ON t.id = r.tenant_id AND t.deleted = 0 LEFT JOIN t_auth_role_menu rm ON rm.role_id = r.id WHERE r.deleted = 0 AND r.valid_state = 1 GROUP BY r.tenant_id, t.code, r.id, r.code, r.name HAVING COUNT(rm.id) = 0 ORDER BY r.tenant_id, r.code; -- 2) 菜单表中声明的 permission 在某些租户的 permission 表缺失 -- 由于 t_auth_menu 无 tenant_id,permission code 应在每个租户的 t_auth_permission 中都存在(除 super_admin 这种全局码也同理) SELECT t.id AS tenant_id, t.code AS tenant_code, m.permission AS menu_permission_code FROM t_sys_tenant t JOIN ( SELECT DISTINCT permission FROM t_auth_menu WHERE deleted = 0 AND valid_state = 1 AND permission IS NOT NULL AND permission <> '' ) m ON 1 = 1 LEFT JOIN t_auth_permission p ON p.tenant_id = t.id AND p.code = m.permission AND p.deleted = 0 AND p.valid_state = 1 WHERE t.deleted = 0 AND t.valid_state = 1 AND p.id IS NULL ORDER BY t.id, m.permission; -- 3) 关键前端权限码在各租户 permission 表缺失 -- 来自 docs/migration/permission-inventory.md(前端提取) SELECT t.id AS tenant_id, t.code AS tenant_code, req.code AS required_code FROM t_sys_tenant t JOIN ( SELECT 'activity:read' AS code UNION ALL SELECT 'ai-3d:create' UNION ALL SELECT 'class:create' UNION ALL SELECT 'class:delete' UNION ALL SELECT 'class:update' UNION ALL SELECT 'config:create' UNION ALL SELECT 'config:delete' UNION ALL SELECT 'config:update' UNION ALL SELECT 'contest:create' UNION ALL SELECT 'contest:delete' UNION ALL SELECT 'contest:publish' UNION ALL SELECT 'contest:read' UNION ALL SELECT 'contest:update' UNION ALL SELECT 'department:create' UNION ALL SELECT 'department:delete' UNION ALL SELECT 'department:update' UNION ALL SELECT 'dict:create' UNION ALL SELECT 'dict:delete' UNION ALL SELECT 'dict:update' UNION ALL SELECT 'grade:create' UNION ALL SELECT 'grade:delete' UNION ALL SELECT 'grade:update' UNION ALL SELECT 'homework:create' UNION ALL SELECT 'homework:delete' UNION ALL SELECT 'homework:read' UNION ALL SELECT 'homework:update' UNION ALL SELECT 'judge:create' UNION ALL SELECT 'judge:delete' UNION ALL SELECT 'judge:read' UNION ALL SELECT 'judge:update' UNION ALL SELECT 'log:delete' UNION ALL SELECT 'menu:create' UNION ALL SELECT 'menu:delete' UNION ALL SELECT 'menu:read' UNION ALL SELECT 'menu:update' UNION ALL SELECT 'notice:create' UNION ALL SELECT 'notice:delete' UNION ALL SELECT 'notice:update' UNION ALL SELECT 'permission:read' UNION ALL SELECT 'registration:read' UNION ALL SELECT 'review:score' UNION ALL SELECT 'role:create' UNION ALL SELECT 'role:delete' UNION ALL SELECT 'role:read' UNION ALL SELECT 'role:update' UNION ALL SELECT 'school:create' UNION ALL SELECT 'school:update' UNION ALL SELECT 'student:create' UNION ALL SELECT 'student:delete' UNION ALL SELECT 'student:update' UNION ALL SELECT 'teacher:create' UNION ALL SELECT 'teacher:delete' UNION ALL SELECT 'teacher:update' UNION ALL SELECT 'tenant:create' UNION ALL SELECT 'tenant:delete' UNION ALL SELECT 'tenant:update' UNION ALL SELECT 'work:read' ) req ON 1 = 1 LEFT JOIN t_auth_permission p ON p.tenant_id = t.id AND p.code = req.code AND p.deleted = 0 AND p.valid_state = 1 WHERE t.deleted = 0 AND t.valid_state = 1 AND p.id IS NULL ORDER BY t.id, req.code; -- 4) 角色拥有菜单,但缺少对应 permission(可能导致“菜单看得到但点进去 403 / 按钮缺失”) -- 规则:若菜单 permission 非空,则角色至少要拥有该 permission(role_permission) SELECT r.tenant_id, t.code AS tenant_code, r.id AS role_id, r.code AS role_code, m.id AS menu_id, m.name AS menu_name, m.permission AS menu_permission_code FROM t_auth_role r JOIN t_sys_tenant t ON t.id = r.tenant_id AND t.deleted = 0 JOIN t_auth_role_menu rm ON rm.role_id = r.id JOIN t_auth_menu m ON m.id = rm.menu_id AND m.deleted = 0 AND m.valid_state = 1 LEFT JOIN t_auth_permission p ON p.tenant_id = r.tenant_id AND p.code = m.permission AND p.deleted = 0 AND p.valid_state = 1 LEFT JOIN t_auth_role_permission rp ON rp.role_id = r.id AND rp.permission_id = p.id WHERE r.deleted = 0 AND r.valid_state = 1 AND m.permission IS NOT NULL AND m.permission <> '' AND (p.id IS NULL OR rp.id IS NULL) ORDER BY r.tenant_id, r.code, m.id;