auth_service.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """认证业务:登录、当前用户、菜单树、操作日志"""
  2. import uuid
  3. from typing import Any, Dict, List, Optional
  4. from sqlalchemy.orm import Session
  5. from app.core.security import create_access_token, verify_password
  6. from app.crud import menu as menu_crud
  7. from app.crud import user as user_crud
  8. from app.models.system import OperLog, SysMenu, SysUser
  9. class AuthFailure(Exception):
  10. """登录失败等业务错误。"""
  11. def __init__(self, message: str = "用户名或密码错误"):
  12. self.message = message
  13. super().__init__(message)
  14. def _uuid() -> str:
  15. return str(uuid.uuid4())
  16. def write_oper_log(
  17. db: Session,
  18. *,
  19. module: str,
  20. action: str,
  21. user_id: Optional[str] = None,
  22. username: Optional[str] = None,
  23. detail: Optional[str] = None,
  24. ip: Optional[str] = None,
  25. ) -> None:
  26. db.add(
  27. OperLog(
  28. id=_uuid(),
  29. user_id=user_id,
  30. username=username,
  31. module=module,
  32. action=action,
  33. detail=detail,
  34. ip=ip,
  35. )
  36. )
  37. db.commit()
  38. def authenticate_user(db: Session, username: str, password: str) -> SysUser:
  39. user = user_crud.get_user_by_username(db, username)
  40. if not user or user.status != "0":
  41. raise AuthFailure()
  42. if not verify_password(password, user.password_hash):
  43. raise AuthFailure()
  44. return user
  45. def login(
  46. db: Session,
  47. username: str,
  48. password: str,
  49. ip: Optional[str] = None,
  50. ) -> Dict[str, str]:
  51. try:
  52. user = authenticate_user(db, username, password)
  53. except AuthFailure:
  54. write_oper_log(
  55. db,
  56. module="auth",
  57. action="login_failed",
  58. username=username,
  59. detail="invalid credentials",
  60. ip=ip,
  61. )
  62. raise
  63. token = create_access_token(sub=user.id, extra={"username": user.username})
  64. write_oper_log(
  65. db,
  66. module="auth",
  67. action="login",
  68. user_id=user.id,
  69. username=user.username,
  70. detail="login success",
  71. ip=ip,
  72. )
  73. return {"token": token, "tokenType": "Bearer"}
  74. def current_user_payload(db: Session, user_id: str) -> Dict[str, Any]:
  75. user, role = user_crud.get_user_with_role(db, user_id)
  76. if not user or not role:
  77. raise AuthFailure("用户不存在或已禁用")
  78. return {
  79. "id": user.id,
  80. "username": user.username,
  81. "displayName": user.display_name,
  82. "roleCode": role.role_code,
  83. "roleName": role.role_name,
  84. }
  85. def _menu_to_node(menu: SysMenu) -> Dict[str, Any]:
  86. return {
  87. "id": menu.id,
  88. "parentId": menu.parent_id,
  89. "menuCode": menu.menu_code,
  90. "menuName": menu.menu_name,
  91. "path": menu.path or "",
  92. "menuType": menu.menu_type,
  93. "sort": menu.sort,
  94. "children": [],
  95. }
  96. def build_menu_tree(menus: List[SysMenu]) -> List[Dict[str, Any]]:
  97. node_list = [_menu_to_node(m) for m in menus]
  98. by_id = {n["id"]: n for n in node_list}
  99. roots: List[Dict[str, Any]] = []
  100. for n in node_list:
  101. parent_id = n["parentId"]
  102. if parent_id and parent_id != "0" and parent_id in by_id:
  103. by_id[parent_id]["children"].append(n)
  104. else:
  105. roots.append(n)
  106. return roots
  107. def menus_payload(db: Session, role_id: str) -> Dict[str, Any]:
  108. page_menus = menu_crud.list_page_menus_for_role(db, role_id)
  109. perm_codes = menu_crud.list_menu_codes_for_role(db, role_id)
  110. return {
  111. "tree": build_menu_tree(page_menus),
  112. "permCodes": perm_codes,
  113. }