161 lines
5.5 KiB
Python
161 lines
5.5 KiB
Python
"""Web 管理端登录:基于 V0.3 sys_user / sys_user_role / sys_role 的角色与菜单权限解析"""
|
||
from __future__ import annotations
|
||
|
||
from typing import Optional, Set
|
||
|
||
from sqlalchemy import and_
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.core.config import settings
|
||
from app.models.models import SysMenu, SysRole, SysRoleMenu, SysUserRole
|
||
|
||
|
||
def _map_role_name_to_web(role_name: str) -> str:
|
||
"""将 sys_role.role_name 映射为 web-admin 使用的 role 字符串。"""
|
||
n = (role_name or "").strip()
|
||
if not n:
|
||
return "admin"
|
||
if "超级" in n or n in ("系统管理员", "超级管理员"):
|
||
return "super_admin"
|
||
if "管理" in n or "运营" in n:
|
||
return "admin"
|
||
return "admin"
|
||
|
||
|
||
def _role_rows_for_user(db: Session, user_id: str, tenant_no: str):
|
||
return (
|
||
db.query(SysRole.role_name)
|
||
.join(SysUserRole, SysUserRole.role_id == SysRole.id)
|
||
.filter(
|
||
SysUserRole.user_id == user_id,
|
||
SysUserRole.del_flag == 0,
|
||
SysUserRole.tenant_no == tenant_no,
|
||
SysRole.del_flag == 0,
|
||
SysRole.status == "1",
|
||
SysRole.tenant_no == tenant_no,
|
||
)
|
||
.order_by(SysRole.create_time.desc())
|
||
.all()
|
||
)
|
||
|
||
|
||
def user_has_bound_roles(db: Session, user_id: str, tenant_no: Optional[str] = None) -> bool:
|
||
"""用户是否在 sys_user_role 中绑定了至少一个有效角色。"""
|
||
if not user_id:
|
||
return False
|
||
tenant = (tenant_no or "").strip() or settings.DEFAULT_TENANT_NO
|
||
rows = _role_rows_for_user(db, user_id, tenant)
|
||
if not rows and tenant != settings.DEFAULT_TENANT_NO:
|
||
rows = _role_rows_for_user(db, user_id, settings.DEFAULT_TENANT_NO)
|
||
return bool(rows)
|
||
|
||
|
||
def resolve_web_role_for_user(db: Session, user_id: str, tenant_no: Optional[str] = None) -> str:
|
||
"""根据用户关联角色解析 Web 端 role(admin / super_admin)。"""
|
||
if not user_id:
|
||
return settings.WEB_AUTH_DEFAULT_ROLE
|
||
tenant = (tenant_no or "").strip() or settings.DEFAULT_TENANT_NO
|
||
rows = _role_rows_for_user(db, user_id, tenant)
|
||
if not rows and tenant != settings.DEFAULT_TENANT_NO:
|
||
rows = _role_rows_for_user(db, user_id, settings.DEFAULT_TENANT_NO)
|
||
names = [str(r[0]).strip() for r in rows if r and r[0]]
|
||
if not names:
|
||
# 未绑定角色:视为普通 admin,无菜单权限(不可默认 super_admin 全菜单)
|
||
return "admin"
|
||
mapped = [_map_role_name_to_web(n) for n in names]
|
||
if "super_admin" in mapped:
|
||
return "super_admin"
|
||
return "admin"
|
||
|
||
|
||
def _granted_menu_ids_for_user(db: Session, user_id: str, tenant: str) -> Set[str]:
|
||
rows = (
|
||
db.query(SysMenu.id)
|
||
.join(
|
||
SysRoleMenu,
|
||
and_(
|
||
SysRoleMenu.menu_id == SysMenu.id,
|
||
SysRoleMenu.del_flag == 0,
|
||
SysRoleMenu.tenant_no == tenant,
|
||
),
|
||
)
|
||
.join(
|
||
SysUserRole,
|
||
and_(
|
||
SysUserRole.role_id == SysRoleMenu.role_id,
|
||
SysUserRole.user_id == user_id,
|
||
SysUserRole.del_flag == 0,
|
||
SysUserRole.tenant_no == tenant,
|
||
),
|
||
)
|
||
.filter(
|
||
SysMenu.del_flag == 0,
|
||
SysMenu.tenant_no == tenant,
|
||
SysMenu.status == "1",
|
||
SysMenu.visible == "1",
|
||
)
|
||
.distinct()
|
||
.all()
|
||
)
|
||
return {str(r[0]).strip() for r in rows if r and r[0]}
|
||
|
||
|
||
def _expand_with_ancestor_menu_ids(db: Session, menu_ids: Set[str], tenant: str) -> Set[str]:
|
||
"""角色仅勾选按钮(F)时,补齐祖先 C/M 菜单 id,便于前端匹配 :list 路由。"""
|
||
expanded = set(menu_ids)
|
||
frontier = list(menu_ids)
|
||
while frontier:
|
||
rows = (
|
||
db.query(SysMenu.parent_id)
|
||
.filter(
|
||
SysMenu.id.in_(frontier),
|
||
SysMenu.del_flag == 0,
|
||
SysMenu.tenant_no == tenant,
|
||
)
|
||
.all()
|
||
)
|
||
next_frontier: list[str] = []
|
||
for (parent_id,) in rows:
|
||
pid = str(parent_id or "").strip()
|
||
if pid and pid not in expanded:
|
||
expanded.add(pid)
|
||
next_frontier.append(pid)
|
||
frontier = next_frontier
|
||
return expanded
|
||
|
||
|
||
def iter_user_menu_perm_codes(
|
||
db: Session, user_id: str, tenant_no: Optional[str] = None
|
||
) -> Set[str]:
|
||
"""用户通过角色关联得到的菜单权限标识(sys_menu.perms,与前端 meta.menuCode 对齐)。"""
|
||
if not user_id:
|
||
return set()
|
||
if not user_has_bound_roles(db, user_id, tenant_no):
|
||
return set()
|
||
tenant = (tenant_no or "").strip() or settings.DEFAULT_TENANT_NO
|
||
try:
|
||
menu_ids = _granted_menu_ids_for_user(db, user_id, tenant)
|
||
if not menu_ids and tenant != settings.DEFAULT_TENANT_NO:
|
||
menu_ids = _granted_menu_ids_for_user(db, user_id, settings.DEFAULT_TENANT_NO)
|
||
if not menu_ids:
|
||
return set()
|
||
all_menu_ids = _expand_with_ancestor_menu_ids(db, menu_ids, tenant)
|
||
rows = (
|
||
db.query(SysMenu.perms)
|
||
.filter(
|
||
SysMenu.id.in_(all_menu_ids),
|
||
SysMenu.del_flag == 0,
|
||
SysMenu.tenant_no == tenant,
|
||
SysMenu.status == "1",
|
||
)
|
||
.all()
|
||
)
|
||
out: Set[str] = set()
|
||
for (perms,) in rows:
|
||
p = (perms or "").strip()
|
||
if p:
|
||
out.add(p)
|
||
return out
|
||
except Exception:
|
||
return set()
|