外观
RBAC 数据模型
表结构说明。日常入口见 RBAC 文档,同步见同步规范,鉴权算法见算法说明。
老系统继续写 users / menu.qx;beta 建新表,lims:rbac-sync 只读镜像。第一版不要求 legacy 双写,也不停 init_qx_list() 动态加列。
表名
统一 rbac_ 前缀,与 legacy 表区分。Model 放 App\Models\Rbac\*。
| 新表 | 来源 |
|---|---|
rbac_permissions | qx_list 树 / qx.php / users 列 |
rbac_roles | users WHERE group='0' |
rbac_role_permissions | 组行 qx=1 的列 |
rbac_user_roles | 用户 group 里的 |组名| |
rbac_user_permission_snapshots | 快照 users.{qx} |
rbac_menu_permissions | 解析 menu.qx |
rbac_sync_runs | 同步审计 |
rbac_audit_logs | 权限操作记录;V1 sync 不写,beta 改权限起写入 |
permissions.code = qx_list.qx = users 列名,不做二次映射。std_code 为预留的标准编码,与 code 指向同一权限点。
rbac_permissions
权限目录与权限点共用一张父子表,对应 legacy qx_list 树形结构。
| 列 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| parent_id | bigint nullable FK | → permissions.id;顶级目录为 NULL |
| node_type | varchar(16) | category / permission |
| code | varchar(64) nullable | 权限点 = users 列名;目录节点为 NULL |
| std_code | varchar(128) nullable | 标准编码(如 site:station.manage);第一版可空,见下文 |
| name | varchar(128) | 目录名或权限显示名 |
| legacy_qx_list_id | int nullable | 对应 qx_list.id |
| sort | int default 0 | |
| is_active | tinyint(1) | 业务是否启用该权限;0=废弃功能,但快照仍同步以保证完整镜像 |
| is_system | tinyint(1) default 0 | admin/audit_test/user_manage 等 |
| source | varchar(32) default qx_list | qx_list / php_catalog / users_column / manual |
| source_file | varchar(255) nullable | 例如 user_manage/qx.php、gx_file/{dw}_qx.php |
| is_catalog_missing | tinyint(1) default 0 | 1=users 有列但目录无定义 |
| note | varchar(255) nullable | |
| created_at / updated_at | timestamp |
索引:(code) unique;(std_code) unique;(parent_id, sort);(node_type, is_active);(legacy_qx_list_id)
约束:
node_type='category'时code为 NULL,不参与鉴权。node_type='permission'时code必填且唯一。node_type='permission'时std_code可空;非空则全局唯一(MySQL 允许多行 NULL)。role_permissions、user_permission_snapshots、menu_permissions只关联node_type='permission'的行。
is_active 字段说明
- 含义:标识该权限是否在业务中启用(1=启用,0=废弃)
- 同步规则:快照同步(P5)不过滤
is_active,所有权限(包括 inactive)都写入user_permission_snapshots - 设计原则:快照应该是 Legacy
users.{qx}列的完整镜像,与业务是否使用该权限无关 - 使用场景:
- ✅ 同步层:同步所有权限,保证数据完整性
- ✅ 业务层:可根据
is_active决定是否在 UI 中显示该权限选项 - ✅ 鉴权层:可选择只检查
is_active=1的权限(第一版不实现)
示例:
- Legacy 有 175 个权限列,其中 73 个已废弃(对应旧功能模块)
- P1 同步:175 个都写入
rbac_permissions,废弃的标记is_active=0 - P5 同步:175 个权限全部写入快照(含 inactive)
- VR3 验证:验证 175 个权限的快照与 Legacy 一致性 ✅
std_code(预留)
| 项 | 说明 |
|---|---|
| 含义 | 同一权限点的「标准名」,建议形态 模块:功能:操作(如 huayan:report:export) |
与 code | code 仍是 legacy 主键(users 列、menu.qx);std_code 不替代 code |
| 第一版 sync | 不填,保持 NULL |
| 第一版鉴权 | has() 仅按 code 解析;不读 std_code |
| 后期启用 | has('site:station.manage') 可先查 std_code 再落到同一 permission_id;关联表仍只绑 permission_id |
| 多别名 | 若需多个别名,再增 permission_aliases 表;单列 std_code 只保留一个标准名 |
rbac_roles
对应 users.group='0' 的组行。
| 列 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| legacy_user_id | int unique | 组行 users.id |
| fzx_id | int | 分中心 |
| code | varchar(64) | = 组 userid(可能是中文,如"主任") |
| std_code | varchar(64) nullable | 标准角色代码(英文,API/URL 使用,如"director");第一版为 NULL |
| name | varchar(128) | |
| sort | int default 0 | 对应 px |
| is_active | tinyint(1) | 对应 deled 等 |
| created_at / updated_at | timestamp |
索引:(fzx_id, code) unique;(std_code) unique
说明:
code:保持 Legacy 原始标识(可能是中文),用于与users.group字段匹配std_code:预留的英文标识,用于 API/URL(第一版不填充,后期手动配置)- 示例:
code='主任',std_code='director',name='主任'
rbac_role_permissions
| 列 | 类型 | 说明 |
|---|---|---|
| role_id | bigint FK | |
| permission_id | bigint FK |
PK:(role_id, permission_id)。sync 时组行 qx=1 的列写入本表。
rbac_user_roles
| 列 | 类型 | 说明 |
|---|---|---|
| user_id | int | → users.id |
| role_id | bigint FK | |
| assigned_at | timestamp |
PK:(user_id, role_id);索引 (role_id)
rbac_user_permission_snapshots
Legacy 最终权限在 users.{qx} 宽表列。sync 以宽表快照为准。
| 列 | 类型 | 说明 |
|---|---|---|
| user_id | int | → users.id |
| permission_id | bigint FK | |
| has_permission | tinyint(1) | = users.{code} == '1' |
| same_as_role_permissions | tinyint(1) nullable | 与组 union 是否一致,仅 verify 审计 |
| synced_at | timestamp |
PK:(user_id, permission_id)
索引:(user_id, has_permission);(permission_id)
P5 规则(非 admin):
role_union(code) = EXISTS(role_permissions[role, code] for role in user_roles)
snapshot(code) = users.{code} == '1'
→ upsert user_permission_snapshots(..., has_permission=snapshot, same_as_role_permissions=(snapshot==role_union))admin 用户不写 user_permission_snapshots,运行时当超管。
rbac_menu_permissions
从 menu.qx 解析,不复制 menu 树。
| 列 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| legacy_menu_id | int | → menu.id |
| permission_id | bigint nullable FK | |
| token | varchar(32) nullable | admin/display/hide/empty |
| modifier | varchar(32) default require_any | require_any / deny_if_user_has / allow_all 等 |
| sort | int default 0 | CSV 顺序 |
索引:(legacy_menu_id);(permission_id)
rbac_sync_runs
| 列 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| started_at / finished_at | timestamp | |
| fzx_id | int nullable | |
| status | varchar(16) | running/success/failed |
| report_path | varchar(255) nullable | 报告文件路径 |
| error_message | text nullable |
rbac_audit_logs
权限 grant/revoke、角色分配、菜单规则变更等操作记录。与 rbac_sync_runs(只记录 sync 任务)分开。
| 列 | 类型 | 说明 |
|---|---|---|
| id | bigint PK | |
| actor_user_id | int | 操作人 |
| actor_userid | varchar(64) nullable | 操作人登录名 |
| fzx_id | int nullable | 分中心 |
| target_type | varchar(32) | user / role / permission / menu_permission / user_role / role_permission |
| target_id | bigint nullable | |
| subject_user_id | int nullable | 被改权限的用户 |
| role_id | bigint nullable FK | |
| permission_id | bigint nullable FK | |
| action | varchar(32) | grant / revoke / assign_role / remove_role / update_menu_rule 等 |
| before_snapshot | text nullable | 变更前 |
| after_snapshot | text nullable | 变更后 |
| ip | varchar(45) nullable | |
| created_at | timestamp |
索引:(actor_user_id);(subject_user_id);(target_type, target_id);(created_at)
写入:V1 sync 不写;legacy 保存仍用 other_log;beta 权限管理或切主后改 RBAC 必写。实现见 RbacAuditLogger(规划在 Foundation/Rbac 或 Services/Rbac)。
有效权限查询
sql
SELECT
ups.user_id,
p.code AS permission_code,
ups.has_permission
FROM rbac_user_permission_snapshots ups
INNER JOIN permissions p ON p.id = ups.permission_id
WHERE p.is_active = 1;读路径:超管短路 → 查 user_permission_snapshots → 无行 deny 并打日志。不 silent fallback Session 宽表。
menu.qx token 映射
| menu.qx | menu_permissions |
|---|---|
| 空 | token=empty, modifier=allow_all |
| admin | token=admin, modifier=require_super_admin |
| !audit_test | permission=audit_test, modifier=deny_if_user_has |
| display / 隐藏 | token=display_hidden, modifier=legacy_hide(beta 默认不出) |
| 普通 code | permission_id FK, modifier=require_any |
菜单数据仍读 legacy menu;可见性读 menu_permissions + canViewMenu()。
配置
| 变量 | 默认 | 说明 |
|---|---|---|
RBAC_ENFORCE | false | 路由鉴权 |
RBAC_FALLBACK_LEGACY_SESSION | false | 仅本地对比,生产勿开 |
RBAC_CACHE_TTL_MINUTES | 10 | user_permission_snapshots 缓存 |
RBAC_SYNC_ON_DEPLOY | false | 部署时可选自动 sync |