BookSystem/backend/auth/apps/users/urls.py
jayhgq 001d0abca2 feat(auth): 重构用户认证模块并优化初始化数据逻辑
- 重构用户和角色模型,优化字段定义和关系
- 增强初始化数据脚本,支持数据更新检查
- 改进用户和角色API端点,增加验证逻辑
- 扩展Pydantic模型,分离请求和响应模式
- 自定义Swagger UI界面并优化API文档
- 移除测试文件并更新依赖项配置
2026-03-01 23:54:41 +08:00

190 lines
6.1 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from fastcrud import FastCRUD
from datetime import timedelta, datetime
import redis.asyncio as redis
from database import get_db
from models import User
from schemas import (
UserCreate,
UserResponse,
UserLogin,
Token,
UserCreateRequest,
UserUpdate,
)
from utils.password import verify_password, get_password_hash
from utils.jwt import create_access_token
from config import app_settings
from utils.redis_client import get_redis, set_token_in_redis
from middleware import get_current_user
router = APIRouter(prefix="/users", tags=["users"])
# 创建FastCRUD实例
user_crud = FastCRUD(User, UserResponse)
@router.post("/login", response_model=Token)
async def login(
user_data: UserLogin,
db: AsyncSession = Depends(get_db),
redis_conn: redis.Redis = Depends(get_redis),
):
"""用户登录"""
# 查找用户
result = await db.execute(select(User).filter(User.username == user_data.username))
user = result.scalar_one_or_none()
# 验证用户是否存在且密码正确
if not user or not verify_password(user_data.password, user.password_hash):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
)
# 创建访问令牌
access_token_expires = timedelta(minutes=app_settings.access_token_expire_minutes)
access_token = create_access_token(
data={"sub": str(user.id), "username": user.username},
expires_delta=access_token_expires,
)
# 将令牌存储到Redis
expire_seconds = int(access_token_expires.total_seconds())
await set_token_in_redis(redis_conn, user.id, access_token, expire_seconds)
# 更新用户最后登录时间
user.lastlogintime = datetime.utcnow()
await db.commit()
return {"access_token": access_token, "token_type": "bearer"}
@router.post("/", response_model=UserResponse)
async def create_user(user_data: UserCreateRequest, db: AsyncSession = Depends(get_db)):
"""创建用户"""
# 检查用户名是否已存在
result = await db.execute(select(User).filter(User.username == user_data.username))
existing_user = result.scalar_one_or_none()
if existing_user:
raise HTTPException(status_code=400, detail="用户名已存在")
# 生成密码哈希值
hashed_password = get_password_hash(user_data.password)
# 准备创建数据
create_data = user_data.model_dump()
create_data["password_hash"] = hashed_password
del create_data["password"] # 删除明文密码
# 转换为UserCreate模型
user_data_create = UserCreate(**create_data)
# 使用FastCRUD创建用户
created_user = await user_crud.create(
db, user_data_create, schema_to_select=UserResponse, return_as_model=True
)
return created_user
@router.get("/", response_model=list[UserResponse])
async def get_users(
db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)
):
"""获取所有用户"""
result = await user_crud.get_multi(db)
return result["data"]
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(
user_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取单个用户"""
user = await user_crud.get(db, id=user_id)
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return user
@router.put("/{user_id}", response_model=UserResponse)
async def update_user(
user_id: int,
user_data: UserUpdate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""更新用户"""
# 检查用户名是否被其他用户使用
result = await db.execute(
select(User).filter(User.username == user_data.username, User.id != user_id)
)
existing_user = result.scalar_one_or_none()
if existing_user:
raise HTTPException(status_code=400, detail="用户名已存在")
# 生成密码哈希值
hashed_password = (
get_password_hash(user_data.password) if user_data.password else None
)
# 准备更新数据
update_data = user_data.model_dump()
if hashed_password:
update_data["password_hash"] = hashed_password
del update_data["password"] # 删除明文密码
# 使用FastCRUD更新用户
updated_user = await user_crud.update(
db, update_data, id=user_id, schema_to_select=UserResponse, return_as_model=True
)
if not updated_user:
raise HTTPException(status_code=404, detail="用户不存在")
return updated_user
@router.delete("/{user_id}")
async def delete_user(
user_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""删除用户"""
# 检查用户是否存在
result = await db.execute(select(User).filter(User.id == user_id))
existing_user = result.scalar_one_or_none()
if not existing_user:
raise HTTPException(status_code=404, detail="用户不存在")
elif existing_user.id == current_user.id:
raise HTTPException(status_code=400, detail="不能删除当前登录用户")
elif existing_user.role_id == 1:
raise HTTPException(status_code=400, detail="不能删除管理员用户")
else:
# 使用FastCRUD删除用户
deleted = await user_crud.delete(db, id=user_id)
return {"message": "用户删除成功"}
@router.post("/logout")
async def logout(
current_user: User = Depends(get_current_user),
redis_conn: redis.Redis = Depends(get_redis),
):
"""用户登出"""
from utils.redis_client import delete_token_from_redis
await delete_token_from_redis(redis_conn, current_user.id)
return {"message": "登出成功"}
@router.get("/me", response_model=UserResponse)
async def get_current_user_info(current_user: User = Depends(get_current_user)):
"""获取当前用户信息"""
print(current_user.id)
return user