| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- """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()
|