dingdanquanliucheng/backend/app/api/auth.py

148 lines
5.6 KiB
Python
Raw Normal View History

"""
认证路由模块
职责
处理用户认证相关接口URL 前缀为 /api/auth
包括登录获取当前用户信息登出绑定微信 OpenID
"""
import json
from fastapi import APIRouter, Depends, Header
2026-05-26 11:36:57 +08:00
from pydantic import BaseModel
from sqlalchemy.orm import Session
2026-05-14 13:51:06 +08:00
from backend.app.api.deps import get_auth_service, get_current_user
from backend.app.db import get_db_session
from backend.app.schemas.auth import LoginRequest, WechatLoginRequest
2026-05-14 13:51:06 +08:00
from backend.app.schemas.common import success_payload
from backend.app.services.auth_service import AuthService
2026-05-26 11:36:57 +08:00
from backend.app.services.wechat_notification_service import wechat_notification_service
2026-05-14 13:51:06 +08:00
router = APIRouter(prefix="/api/auth", tags=["auth"])
2026-05-26 11:36:57 +08:00
class BindOpenIdRequest(BaseModel):
"""绑定微信 OpenID 请求体"""
2026-05-26 11:36:57 +08:00
open_id: str
class BindOpenIdByCodeRequest(BaseModel):
"""通过 wx.login code 绑定微信 OpenID"""
code: str
2026-05-14 13:51:06 +08:00
@router.post("/login")
def login(
payload: LoginRequest, # 登录请求体,包含用户名、密码、角色类型
auth_service: AuthService = Depends(get_auth_service), # 注入认证服务
session: Session = Depends(get_db_session), # 注入数据库会话
) -> dict:
"""用户登录接口
用途验证用户名和密码返回 JWT Token
请求参数LoginRequestusernamepasswordrole_type
返回值登录结果包含 token 等认证信息
权限要求无需认证公开接口
"""
return success_payload(auth_service.login(payload.username, payload.password, payload.role_type, session))
2026-05-14 13:51:06 +08:00
@router.post("/wechat-login")
def wechat_login(
payload: WechatLoginRequest, # 微信手机号登录请求体
auth_service: AuthService = Depends(get_auth_service), # 注入认证服务
session: Session = Depends(get_db_session), # 注入数据库会话
) -> dict:
"""微信手机号一键登录接口
用途通过微信手机号授权登录自动匹配系统用户并绑定 open_id
请求参数WechatLoginRequestcodephone_code
返回值登录结果包含 token 等认证信息
权限要求无需认证公开接口
"""
return success_payload(auth_service.wechat_login(payload.code, payload.phone_code, session))
2026-05-14 13:51:06 +08:00
@router.get("/me")
def me(
current_user: dict = Depends(get_current_user), # 从 JWT Token 解析当前用户
2026-05-14 13:51:06 +08:00
) -> dict:
"""获取当前登录用户信息
用途返回当前已登录用户的详细信息角色权限等
请求参数通过请求头 Authorization 传递 Token
返回值当前用户信息字典
权限要求必须已登录携带有效 Token
"""
return success_payload(current_user)
2026-05-14 13:51:06 +08:00
@router.post("/logout")
def logout(
authorization: str | None = Header(default=None), # 请求头中的 Authorization 字段
auth_service: AuthService = Depends(get_auth_service), # 注入认证服务
) -> dict:
"""用户登出接口
用途使当前 Token 失效完成登出
请求参数通过请求头 Authorization 传递 Bearer Token
返回值登出结果
权限要求无需严格校验但需传递 Token
"""
token = (authorization or "").removeprefix("Bearer").strip()
return success_payload(auth_service.logout(token))
2026-05-26 11:36:57 +08:00
@router.post("/bind-openid")
def bind_openid(
payload: BindOpenIdRequest, # 绑定请求体,包含微信 OpenID
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(get_current_user), # 当前登录用户
2026-05-26 11:36:57 +08:00
) -> dict:
"""绑定微信 OpenID 接口
用途将当前用户的账号与微信 OpenID 绑定用于微信消息通知
请求参数BindOpenIdRequestopen_id - 微信用户的 OpenID
返回值绑定结果 {"success": true}
权限要求必须已登录
"""
2026-05-26 11:36:57 +08:00
wechat_notification_service.bind_open_id(session, current_user["user_id"], payload.open_id)
return success_payload({"success": True})
@router.post("/bind-openid-by-code")
def bind_openid_by_code(
payload: BindOpenIdByCodeRequest, # 通过 code 绑定
auth_service: AuthService = Depends(get_auth_service), # 注入认证服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(get_current_user), # 当前登录用户
) -> dict:
"""通过 wx.login code 绑定微信 OpenID
用途密码登录后调用 code 换取 open_id 并绑定到当前用户
请求参数BindOpenIdByCodeRequestcode - wx.login 获取的临时凭证
返回值绑定结果 {"success": true}
权限要求必须已登录
"""
return success_payload(auth_service.bind_openid_by_code(current_user["user_id"], payload.code, session))
@router.get("/service-bind-status")
def service_bind_status(
session: Session = Depends(get_db_session),
current_user: dict = Depends(get_current_user),
) -> dict:
"""查询当前用户是否已绑定服务号通知
用途小程序页面据此决定是否显示 <official-account> 组件
返回值{"bound": true/false}
权限要求必须已登录
"""
from backend.app.models.system import User
from sqlalchemy import select
user = session.execute(
select(User.service_open_id).where(User.id == current_user["user_id"])
).scalar_one_or_none()
bound = bool(user)
return success_payload({"bound": bound})