日期: 2026-08-02
范围: sys_post / sys_post_role / 用户有效角色解析 / 职位管理「角色配置」/ 用户管理职位多选
方案: 运行时并集(方案 1),不物化复制到 sys_user_role
| 项 | 结论 |
|---|---|
| 权限来源 | 并集:用户直接角色 ∪ 职位继承角色 |
| 继承内容 | 菜单权限 + 数据权限(与现有多角色并集语义一致) |
| 配置入口 | 职位管理增加「角色配置」;用户管理补上职位多选 |
| 非目标 | 角色页反向关联职位;权限来源可视化;OA OaRole / 审批人解析;职位自带菜单树 |
sys_role)是权限载体:sys_role_menu(菜单)、data_scope + sys_role_data_scope_detail(数据范围)。sys_user_role 绑定角色;登录后 iter_user_menu_perm_codes、apply_data_scope(_resolve_user_role_scopes)仅查直接角色。sys_post)仅有 sys_user_post 关联;不参与 RBAC。OA 表单「职位」控件另用,本设计不改动。postIds,用户管理前端未暴露职位多选。用户 ──sys_user_role──────────────▶ 角色 ──▶ 菜单 / 数据权限
│ ▲
└──sys_user_post──▶ 职位 ──sys_post_role──┘
有效角色 = 启用直接角色 ∪(启用职位所关联的启用角色)
有效菜单码 / 数据范围 = 对有效角色取并集(与现规范一致)
sys_post_role对齐 sys_user_role / sys_user_post 结构:
| 字段 | 说明 |
|---|---|
id |
VARCHAR(64) UUID PK |
post_id |
职位 ID |
role_id |
角色 ID |
| 公共字段 | tenant_no, order_num, remark, create_by, create_dept_id, create_time, update_by, update_time, del_flag |
del_flag=1,查询默认排除。(post_id, role_id) 仅一条有效关联(写入时软删旧行再插,或应用层去重;与现有 sys_user_role 替换策略一致)。docs/sql/sys_post_role.sql;模型:SysPostRole(backend/app/models/models.py)。既有表不变:sys_post、sys_user_post、sys_user_role、sys_role*。
在 backend/app/services/web_auth_roles.py(或抽出共享 helper,供 data_scope 复用)提供:
get_effective_role_ids(db, user_id) -> List[str]
sys_user_role → sys_role(del_flag=0, status='1', 租户一致)sys_user_post → sys_post(del_flag=0, status='1')→ sys_post_role → sys_role(同上)基于有效角色 ID 改造:
user_has_active_roles:有效角色非空即为真(仅职位带角色、无直接角色也通过登录门禁)。get_user_role_names / _active_role_name_rows / resolve_web_role_for_user:按有效角色名称映射 admin / super_admin。iter_user_menu_perm_codes:SysRoleMenu 的 role_id 属于有效角色集合(可用 role_id IN (...) 或等价 join,避免只 join SysUserRole)。data_scope._resolve_user_role_scopes:角色查询改为基于有效角色 ID,不再仅 join SysUserRole。
| 状态 | 是否参与并集 |
|---|---|
职位 status != '1' 或已删 |
否(其下角色关联全部不计入) |
角色 status != '1' 或已删 |
否 |
sys_post_role / sys_user_post / sys_user_role del_flag=1 |
否 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /web/admin/posts/{postId}/roles |
返回该职位已关联 roleIds(及可选角色摘要) |
| PUT | /web/admin/posts/{postId}/roles |
body:{ roleIds: string[] },全量替换关联(对齐 PUT .../users) |
postIds;确保 _replace_user_posts 与角色替换互不影响(已有行为保留)。del_flag/status 自动失效;可选同步软删 sys_post_role(与删职位时处理 sys_user_post 的方式对齐即可)。路由注册:沿用 admin 路由模块,无需新 router 文件。
web-admin/src/views/system/post/index.vueel-select multiple 或勾选表格),加载全部启用角色 + 当前 roleIds,确定调用 PUT .../roles。web-admin/src/views/system/user/index.vuepostIds),数据源 fetchPostList(或现有 posts API)。postIds;详情回填;查看弹窗展示职位名称。web-admin/src/api/ 增加 fetchPostRoleIds / fetchUpdatePostRoles(命名与 posts users API 对称)。实现时同步更新(最小必要):
docs/sql/sys_post_role.sql(及视情况合并说明到基线 SQL 的维护方式与仓库惯例一致)。docs/中科兴皖需求V0.3.md §2.1.8:补充职位可关联角色;用户权限 = 直接角色 ∪ 职位角色。permission-conventions:多角色并集扩展为「直接角色 ∪ 经职位继承的角色」;菜单与数据权限均适用。postIds 不影响 roleIds,反之亦然。super_admin 映射:若职位关联的角色名命中超级管理员规则,JWT/web role 应为 super_admin。SysUserRole 的权限路径都要改为有效角色;以 web_auth_roles + data_scope._resolve_user_role_scopes 为必改清单,实现前再 grep SysUserRole 防漏。apply_data_scope 保持与现网同级查询次数(一次角色查询 + 明细)。sys_role;不触及 oa_role / oa_role_user。SysPostRoleGET/PUT posts/{id}/rolesget_effective_role_ids + 改造 web_auth_roles / data_scope./scripts/update-graph.sh backend(及改前端时 frontend)