- 重构用户和角色模型,优化字段定义和关系 - 增强初始化数据脚本,支持数据更新检查 - 改进用户和角色API端点,增加验证逻辑 - 扩展Pydantic模型,分离请求和响应模式 - 自定义Swagger UI界面并优化API文档 - 移除测试文件并更新依赖项配置
190 lines
6.1 KiB
Python
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
|