""" 认证API """ from fastapi import APIRouter, HTTPException, Depends, Request from app.models.auth import ( SendCodeRequest, SendCodeResponse, LoginRequest, LoginResponse, RefreshTokenResponse, UserInfo ) from app.models.database import User from app.services.auth_service import auth_service from app.services.jwt_service import jwt_service from app.services.db_service import db_service from app.middleware.auth_middleware import get_current_user, get_client_ip from app.utils.logger import logger router = APIRouter(prefix="/api/auth", tags=["认证"]) @router.post("/send-code", response_model=SendCodeResponse) async def send_verification_code( request_data: SendCodeRequest, request: Request ): """ 发送验证码 - 同一手机号60秒内只能发送一次 - 同一IP每小时最多发送10次 - 验证码5分钟有效期 """ try: client_ip = get_client_ip(request) db = db_service.get_session() try: result = await auth_service.send_verification_code( db=db, phone=request_data.phone, ip_address=client_ip ) return SendCodeResponse(**result) finally: db.close() except Exception as e: logger.error(f"发送验证码失败: {e}") raise HTTPException(status_code=500, detail="发送验证码失败") @router.post("/login", response_model=LoginResponse) async def login_with_code( request_data: LoginRequest, request: Request ): """ 验证码登录 - 验证验证码是否正确且未过期 - 用户不存在则自动注册 - 生成JWT token(7天有效期) - 更新最后登录时间 """ try: client_ip = get_client_ip(request) db = db_service.get_session() try: result = await auth_service.login_with_code( db=db, phone=request_data.phone, code=request_data.code, ip_address=client_ip ) if not result["success"]: return LoginResponse( success=False, message=result["message"] ) return LoginResponse( success=True, token=result["token"], user=UserInfo(**result["user"]) ) finally: db.close() except Exception as e: logger.error(f"登录失败: {e}") raise HTTPException(status_code=500, detail="登录失败") @router.post("/refresh", response_model=RefreshTokenResponse) async def refresh_token( current_user: User = Depends(get_current_user) ): """ 刷新Token 需要提供有效的JWT token """ try: # 生成新的token new_token = jwt_service.create_access_token( current_user.id, current_user.phone ) return RefreshTokenResponse( success=True, token=new_token ) except Exception as e: logger.error(f"刷新token失败: {e}") raise HTTPException(status_code=500, detail="刷新token失败") @router.get("/me", response_model=UserInfo) async def get_current_user_info( current_user: User = Depends(get_current_user) ): """ 获取当前用户信息 需要提供有效的JWT token """ # 手机号脱敏 masked_phone = f"{current_user.phone[:3]}****{current_user.phone[-4:]}" return UserInfo( id=current_user.id, phone=masked_phone, created_at=current_user.created_at, last_login_at=current_user.last_login_at ) @router.post("/logout") async def logout( current_user: User = Depends(get_current_user) ): """ 登出 主要在前端清除token,后端记录日志 """ logger.info(f"用户登出: {current_user.phone}") return { "success": True, "message": "已登出" }