from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from sqlalchemy import and_ from pydantic import BaseModel, Field from typing import Optional import uuid from datetime import datetime from ..database import get_db from ..models import ( User, LoginLog, RegisterSetting, gen_uuid ) from ..auth import get_current_user, hash_password, verify_password, create_access_token from ..middleware.log_service import LogService router = APIRouter() class LoginRequest(BaseModel): username: str password: str class RegisterRequest(BaseModel): username: str = Field(min_length=3, max_length=50) password: str = Field(min_length=6, max_length=100) email: Optional[str] = None phone: Optional[str] = None @router.post("/login") def login(req: LoginRequest, db: Session = Depends(get_db)): import logging logger = logging.getLogger(__name__) user = db.query(User).filter(User.username == req.username).first() if not user: logger.warning(f"登录失败: 用户 '{req.username}' 不存在") raise HTTPException(status_code=401, detail="用户名不存在") stored_hash = user.password_hash logger.info(f"登录调试: username={req.username} hash_prefix={stored_hash[:7] if stored_hash else 'None'} hash_len={len(stored_hash) if stored_hash else 0}") if not verify_password(req.password, user.password_hash): logger.warning(f"登录失败: 用户 '{req.username}' 密码验证失败 hash_prefix={stored_hash[:7] if stored_hash else 'None'} hash_len={len(stored_hash) if stored_hash else 0}") raise HTTPException(status_code=401, detail="密码错误") if user.status == "banned": raise HTTPException(status_code=403, detail="账号已被封禁") token_data = {"sub": user.id, "username": user.username, "role": user.role} access_token = create_access_token(token_data) # 记录登录日志 login_log = LoginLog( id=gen_uuid(), user_id=user.id, ip="", login_at=datetime.now(), ) db.add(login_log) # 记录操作日志 LogService.log_operation( user_id=user.id, username=user.username, action="login", target="user", detail=f"用户 {user.username} 登录成功", ) db.commit() return { "code": 0, "data": { "access_token": access_token, "token_type": "bearer", "user": { "id": user.id, "username": user.username, "email": user.email, "phone": user.phone, "avatar": user.avatar, "role": user.role, "status": user.status, "membership_level": user.membership_level, "membership_expire_at": str(user.membership_expire_at) if user.membership_expire_at else None, "created_at": str(user.created_at) if user.created_at else None, }, }, "message": "登录成功", } @router.post("/register") def register(req: RegisterRequest, db: Session = Depends(get_db)): # 检查注册设置 reg_setting = db.query(RegisterSetting).first() if reg_setting and reg_setting.phone_required and not req.phone: raise HTTPException(status_code=400, detail="手机号为必填项") if reg_setting and reg_setting.email_required and not req.email: raise HTTPException(status_code=400, detail="邮箱为必填项") # 检查用户名是否已存在 existing = db.query(User).filter(User.username == req.username).first() if existing: raise HTTPException(status_code=400, detail="用户名已存在") if req.email: existing_email = db.query(User).filter(User.email == req.email).first() if existing_email: raise HTTPException(status_code=400, detail="邮箱已被注册") if req.phone: existing_phone = db.query(User).filter(User.phone == req.phone).first() if existing_phone: raise HTTPException(status_code=400, detail="手机号已被注册") user = User( id=gen_uuid(), username=req.username, password_hash=hash_password(req.password), email=req.email, phone=req.phone, avatar=None, role="user", status="active", real_name_status="unverified", membership_level=0, created_at=datetime.now(), updated_at=datetime.now(), ) db.add(user) db.flush() # 创建Token余额记录 from ..models import TokenBalance balance = TokenBalance( id=gen_uuid(), user_id=user.id, total_consumed=0, balance=0, daily_free_used=0, ) db.add(balance) token_data = {"sub": user.id, "username": user.username, "role": user.role} access_token = create_access_token(token_data) LogService.log_operation( user_id=user.id, username=user.username, action="register", target="user", detail=f"新用户 {user.username} 注册成功", ) db.commit() return { "code": 0, "data": { "access_token": access_token, "token_type": "bearer", "user": { "id": user.id, "username": user.username, "email": user.email or "", "phone": user.phone or "", "avatar": user.avatar or "", "role": user.role, "status": user.status, "membership_level": user.membership_level, "created_at": str(user.created_at) if user.created_at else None, }, }, "message": "注册成功", } @router.get("/me") def get_me(current_user: User = Depends(get_current_user)): return { "code": 0, "data": { "id": current_user.id, "username": current_user.username, "email": current_user.email or "", "phone": current_user.phone or "", "avatar": current_user.avatar or "", "role": current_user.role, "status": current_user.status, "real_name_status": current_user.real_name_status, "membership_level": current_user.membership_level, "membership_expire_at": str(current_user.membership_expire_at) if current_user.membership_expire_at else None, "auto_renew": current_user.auto_renew, "created_at": str(current_user.created_at) if current_user.created_at else None, }, "message": "ok", } @router.post("/logout") def logout(current_user: User = Depends(get_current_user)): LogService.log_operation( user_id=current_user.id, username=current_user.username, action="logout", target="user", detail=f"用户 {current_user.username} 登出", ) return {"code": 0, "message": "登出成功"} @router.get("/settings") def get_settings(db: Session = Depends(get_db)): settings = db.query(RegisterSetting).first() if not settings: return {"code": 0, "data": {}, "message": "ok"} return { "code": 0, "data": { "phone_required": settings.phone_required, "phone_sms_required": settings.phone_sms_required, "email_required": settings.email_required, "email_verify_required": settings.email_verify_required, "real_name_required": settings.real_name_required, }, "message": "ok", }