web_auth_roles.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. """Web 管理端登录:基于 V0.3 sys_user / sys_user_role / sys_role 的角色与菜单权限解析"""
  2. from __future__ import annotations
  3. from typing import Set
  4. from sqlalchemy import and_
  5. from sqlalchemy.orm import Session
  6. from app.core.config import settings
  7. from app.models.models import SysMenu, SysRole, SysRoleMenu, SysUserRole
  8. # 无 sys_user_role 绑定时的 Web 端角色(不可为 super_admin,避免越权)
  9. NO_ROLE_WEB_ROLE = "guest"
  10. # 一级菜单权限码(用于顶部菜单权限控制)
  11. TOP_MENU_PERMS = frozenset({"menu:market", "menu:production", "menu:quality", "menu:operations", "menu:system-manage"})
  12. def _map_role_name_to_web(role_name: str) -> str:
  13. """将 sys_role.role_name 映射为 web-admin 使用的 role 字符串。"""
  14. n = (role_name or "").strip()
  15. if not n:
  16. return "admin"
  17. if "超级" in n or n in ("系统管理员", "超级管理员"):
  18. return "super_admin"
  19. if "管理" in n or "运营" in n:
  20. return "admin"
  21. return "admin"
  22. def _active_role_name_rows(db: Session, user_id: str):
  23. """查询用户当前有效的 sys_role.role_name 列表。"""
  24. if not user_id:
  25. return []
  26. tenant = settings.DEFAULT_TENANT_NO
  27. rows = (
  28. db.query(SysRole.role_name)
  29. .join(SysUserRole, SysUserRole.role_id == SysRole.id)
  30. .filter(
  31. SysUserRole.user_id == user_id,
  32. SysUserRole.del_flag == 0,
  33. SysUserRole.tenant_no == tenant,
  34. SysRole.del_flag == 0,
  35. SysRole.status == "1",
  36. SysRole.tenant_no == tenant,
  37. )
  38. .order_by(SysRole.create_time.desc())
  39. .all()
  40. )
  41. return [str(r[0]).strip() for r in rows if r and r[0]]
  42. def user_has_active_roles(db: Session, user_id: str) -> bool:
  43. """用户是否至少绑定一个有效角色(sys_user_role → sys_role)。"""
  44. return bool(_active_role_name_rows(db, user_id))
  45. def get_user_role_names(db: Session, user_id: str) -> list:
  46. """用户当前绑定的 sys_role.role_name 列表(去重、保序)。"""
  47. names = _active_role_name_rows(db, user_id)
  48. return list(dict.fromkeys(names))
  49. def resolve_web_role_for_user(db: Session, user_id: str) -> str:
  50. """根据用户关联角色解析 Web 端 role(admin / super_admin / guest)。"""
  51. names = _active_role_name_rows(db, user_id)
  52. if not names:
  53. return NO_ROLE_WEB_ROLE
  54. mapped = [_map_role_name_to_web(n) for n in names]
  55. if "super_admin" in mapped:
  56. return "super_admin"
  57. return "admin"
  58. def iter_user_menu_perm_codes(db: Session, user_id: str) -> Set[str]:
  59. """用户通过角色关联得到的菜单权限标识(sys_menu.perms,空则退回菜单 id)。
  60. 注:不过滤 visible——F 类型按钮等 visible=0 的菜单仍可通过角色授权生效;
  61. visible 仅控制侧栏/菜单树展示,不影响权限码解析。
  62. 一级菜单(menu:market 等)同样经本函数返回,用于前端顶部菜单权限控制。
  63. """
  64. if not user_id:
  65. return set()
  66. tenant = settings.DEFAULT_TENANT_NO
  67. try:
  68. q = (
  69. db.query(SysMenu.perms, SysMenu.id)
  70. .join(
  71. SysRoleMenu,
  72. and_(
  73. SysRoleMenu.menu_id == SysMenu.id,
  74. SysRoleMenu.del_flag == 0,
  75. SysRoleMenu.tenant_no == tenant,
  76. ),
  77. )
  78. .join(
  79. SysUserRole,
  80. and_(
  81. SysUserRole.role_id == SysRoleMenu.role_id,
  82. SysUserRole.user_id == user_id,
  83. SysUserRole.del_flag == 0,
  84. SysUserRole.tenant_no == tenant,
  85. ),
  86. )
  87. .filter(
  88. SysMenu.del_flag == 0,
  89. SysMenu.tenant_no == tenant,
  90. SysMenu.status == "1",
  91. )
  92. .distinct()
  93. )
  94. out: Set[str] = set()
  95. for perms, mid in q.all():
  96. p = (perms or "").strip()
  97. if p:
  98. out.add(p)
  99. elif mid:
  100. out.add(str(mid).strip())
  101. return out
  102. except Exception:
  103. return set()