| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- """为已通过校验的 SysUser 签发与 web 登录一致的 JWT 会话包。"""
- from __future__ import annotations
- from typing import List, Optional, Tuple
- from sqlalchemy.orm import Session
- from app.core.auth import (
- create_access_token,
- create_refresh_token,
- store_refresh_token,
- store_session,
- )
- from app.core.config import settings
- from app.core.utils import generate_uuid
- from app.crud.user import list_post_ids_for_user
- from app.models.models import SysDept, SysPost, SysUser
- from app.services.web_auth_roles import (
- get_user_role_names,
- resolve_web_role_for_user,
- user_has_active_roles,
- )
- def _avatar_url_field(user: SysUser) -> Optional[str]:
- raw = getattr(user, "avatar", None)
- if not raw:
- return None
- s = str(raw).strip()
- return s if s else None
- def resolve_user_org_labels(db: Session, user: SysUser) -> Tuple[Optional[str], List[str]]:
- """解析用户所属部门名称与职位名称列表(保序)。"""
- tenant_no = (getattr(user, "tenant_no", None) or "").strip()
- dept_name: Optional[str] = None
- dept_id = (getattr(user, "dept_id", None) or "").strip()
- if dept_id:
- dept = (
- db.query(SysDept)
- .filter(
- SysDept.id == dept_id,
- SysDept.tenant_no == tenant_no,
- SysDept.del_flag == 0,
- )
- .first()
- )
- if dept and (dept.dept_name or "").strip():
- dept_name = str(dept.dept_name).strip()
- post_names: List[str] = []
- post_ids = list_post_ids_for_user(
- db, user.id, tenant_no=tenant_no
- )
- if post_ids:
- posts = (
- db.query(SysPost)
- .filter(
- SysPost.id.in_(post_ids),
- SysPost.tenant_no == tenant_no,
- SysPost.status == "1",
- SysPost.del_flag == 0,
- )
- .all()
- )
- name_map = {
- p.id: str(p.post_name).strip()
- for p in posts
- if p.id and (p.post_name or "").strip()
- }
- post_names = [name_map[pid] for pid in post_ids if pid in name_map]
- return dept_name, post_names
- def build_user_info_payload(
- user: SysUser,
- web_role: str,
- role_names: list = None,
- *,
- db: Session = None,
- ) -> dict:
- phone = (getattr(user, "phone_no", None) or "").strip() or None
- dept_name: Optional[str] = None
- post_names: List[str] = []
- if db is not None:
- dept_name, post_names = resolve_user_org_labels(db, user)
- return {
- "id": user.id,
- "username": user.user_no,
- "realName": user.nick_name,
- "phone": phone,
- "email": (getattr(user, "email", None) or "").strip() or None,
- "hireDate": user.hire_date.strftime("%Y-%m-%d") if getattr(user, "hire_date", None) else None,
- "probationMonths": getattr(user, "probation_months", None),
- "lastWorkDay": user.last_work_day.strftime("%Y-%m-%d")
- if getattr(user, "last_work_day", None)
- else None,
- "sex": getattr(user, "sex", None) or "0",
- "remark": (getattr(user, "remark", None) or "").strip() or None,
- "role": web_role,
- "roleNames": role_names or [],
- "regionId": user.dept_id,
- "regionName": dept_name,
- "deptName": dept_name,
- "postNames": post_names,
- "avatarUrl": _avatar_url_field(user),
- }
- def _jwt_user_payload(user: SysUser, web_role: str) -> dict:
- return {
- "sub": user.id,
- "username": user.user_no,
- "role": web_role,
- "region_id": user.dept_id,
- "real_name": user.nick_name,
- }
- async def issue_tokens_for_user(db: Session, user: SysUser) -> dict:
- """校验停用/角色后签发 token。失败抛 ValueError(中文 message)。"""
- if user.status != "1":
- raise ValueError("账号已被停用,请联系管理员")
- tenant_no = (getattr(user, "tenant_no", None) or "").strip() or None
- if not user_has_active_roles(db, user.id, tenant_no=tenant_no):
- raise ValueError("账号未分配角色,请联系管理员")
- web_role = resolve_web_role_for_user(
- db, user.id, tenant_no=tenant_no
- )
- role_names = get_user_role_names(db, user.id, tenant_no=tenant_no)
- session_id = generate_uuid()
- access_jti = generate_uuid()
- refresh_jti = generate_uuid()
- access_token = create_access_token(
- data=_jwt_user_payload(user, web_role),
- session_id=session_id,
- token_jti=access_jti,
- )
- refresh_token = create_refresh_token(
- data={
- "sub": user.id,
- "username": user.user_no,
- "role": web_role,
- },
- session_id=session_id,
- refresh_jti=refresh_jti,
- )
- await store_session(session_id=session_id, user_id=user.id)
- await store_refresh_token(refresh_jti=refresh_jti, user_id=user.id)
- return {
- "token": access_token,
- "refreshToken": refresh_token,
- "expiresIn": settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
- "userInfo": build_user_info_payload(user, web_role, role_names, db=db),
- }
|