初次提交
This commit is contained in:
252
backend/app/services/user_service.py
Normal file
252
backend/app/services/user_service.py
Normal file
@@ -0,0 +1,252 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from fastapi import HTTPException, status
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from app.models.user import User
|
||||
from app.core.security import get_password_hash, verify_password, create_access_token, create_refresh_token
|
||||
from app.schemas.auth import UserRegister, UserLogin, UserResponse
|
||||
from app.core.config import settings
|
||||
from app.exceptions.auth import UsernameAlreadyExistsException
|
||||
|
||||
class UserService:
|
||||
"""用户服务类"""
|
||||
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
|
||||
def create_user(self, user_data: UserRegister) -> User:
|
||||
"""创建新用户"""
|
||||
try:
|
||||
# 检查用户名是否已存在
|
||||
print(f"[DEBUG] Checking if username exists: {user_data.username}")
|
||||
existing_user_by_username = self.get_user_by_username(user_data.username)
|
||||
if existing_user_by_username:
|
||||
print(f"[DEBUG] Username already exists: {existing_user_by_username}")
|
||||
raise UsernameAlreadyExistsException()
|
||||
print(f"[DEBUG] Username check passed")
|
||||
|
||||
# 邮箱允许重复,不再检查邮箱是否已存在
|
||||
print(f"[DEBUG] Email uniqueness check skipped (emails can be duplicated)")
|
||||
|
||||
# 创建新用户
|
||||
print(f"[DEBUG] About to hash password...")
|
||||
try:
|
||||
hashed_password = get_password_hash(user_data.password)
|
||||
print(f"[DEBUG] Password hashed successfully")
|
||||
except Exception as hash_error:
|
||||
print(f"[ERROR] Password hashing failed: {hash_error}")
|
||||
print(f"[ERROR] Error type: {type(hash_error)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
print(f"[DEBUG] Creating User model...")
|
||||
db_user = User(
|
||||
username=user_data.username,
|
||||
email=user_data.email,
|
||||
password_hash=hashed_password,
|
||||
is_active=True,
|
||||
is_verified=False
|
||||
)
|
||||
print(f"[DEBUG] User model created")
|
||||
|
||||
self.db.add(db_user)
|
||||
self.db.commit()
|
||||
self.db.refresh(db_user)
|
||||
|
||||
return db_user
|
||||
|
||||
except IntegrityError as e:
|
||||
self.db.rollback()
|
||||
if "username" in str(e.orig):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={"code": "USERNAME_EXISTS", "message": "用户名已存在"}
|
||||
)
|
||||
elif "email" in str(e.orig):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={"code": "EMAIL_EXISTS", "message": "邮箱已被注册"}
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={"code": "INTEGRITY_ERROR", "message": "数据完整性错误"}
|
||||
)
|
||||
except (HTTPException, UsernameAlreadyExistsException):
|
||||
# 重新抛出HTTPException(不要覆盖自定义错误信息)
|
||||
self.db.rollback()
|
||||
raise
|
||||
except Exception as e:
|
||||
self.db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail={"code": "CREATION_FAILED", "message": "用户创建失败"}
|
||||
)
|
||||
|
||||
def authenticate_user(self, login_data: UserLogin) -> Optional[User]:
|
||||
"""验证用户登录"""
|
||||
# 尝试通过用户名查找用户
|
||||
user = self.get_user_by_username(login_data.username)
|
||||
|
||||
# 如果用户名找不到,尝试通过邮箱查找
|
||||
if not user:
|
||||
user = self.get_user_by_email(login_data.username)
|
||||
|
||||
# 如果找到用户且密码正确
|
||||
if user and verify_password(login_data.password, user.password_hash):
|
||||
# 更新最后登录时间
|
||||
user.last_login_at = datetime.utcnow()
|
||||
self.db.commit()
|
||||
return user
|
||||
|
||||
return None
|
||||
|
||||
def get_user_by_id(self, user_id: int) -> Optional[User]:
|
||||
"""根据ID获取用户"""
|
||||
return self.db.query(User).filter(User.id == user_id, User.is_active == True).first()
|
||||
|
||||
def get_user_by_username(self, username: str) -> Optional[User]:
|
||||
"""根据用户名获取用户"""
|
||||
return self.db.query(User).filter(User.username == username, User.is_active == True).first()
|
||||
|
||||
def get_user_by_email(self, email: str) -> Optional[User]:
|
||||
"""根据邮箱获取用户"""
|
||||
return self.db.query(User).filter(User.email == email, User.is_active == True).first()
|
||||
|
||||
def update_user(self, user_id: int, **kwargs) -> Optional[User]:
|
||||
"""更新用户信息"""
|
||||
user = self.get_user_by_id(user_id)
|
||||
if not user:
|
||||
return None
|
||||
|
||||
try:
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(user, key) and value is not None:
|
||||
setattr(user, key, value)
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(user)
|
||||
return user
|
||||
|
||||
except Exception as e:
|
||||
self.db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail={"code": "UPDATE_FAILED", "message": "用户信息更新失败"}
|
||||
)
|
||||
|
||||
def change_password(self, user_id: int, current_password: str, new_password: str) -> bool:
|
||||
"""修改用户密码"""
|
||||
user = self.get_user_by_id(user_id)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail={"code": "USER_NOT_FOUND", "message": "用户不存在"}
|
||||
)
|
||||
|
||||
# 验证当前密码
|
||||
if not verify_password(current_password, user.password_hash):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={"code": "INVALID_PASSWORD", "message": "当前密码不正确"}
|
||||
)
|
||||
|
||||
try:
|
||||
# 更新密码
|
||||
user.password_hash = get_password_hash(new_password)
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail={"code": "PASSWORD_CHANGE_FAILED", "message": "密码修改失败"}
|
||||
)
|
||||
|
||||
def deactivate_user(self, user_id: int) -> bool:
|
||||
"""停用用户"""
|
||||
user = self.get_user_by_id(user_id)
|
||||
if not user:
|
||||
return False
|
||||
|
||||
try:
|
||||
user.is_active = False
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail={"code": "DEACTIVATION_FAILED", "message": "用户停用失败"}
|
||||
)
|
||||
|
||||
def update_storage_usage(self, user_id: int, size_change: int) -> bool:
|
||||
"""更新用户存储使用量"""
|
||||
user = self.get_user_by_id(user_id)
|
||||
if not user:
|
||||
return False
|
||||
|
||||
try:
|
||||
new_usage = user.storage_used + size_change
|
||||
|
||||
# 检查存储配额
|
||||
if new_usage > user.storage_quota:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={"code": "STORAGE_EXCEEDED", "message": "存储空间不足"}
|
||||
)
|
||||
|
||||
user.storage_used = max(0, new_usage) # 确保不小于0
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
self.db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail={"code": "STORAGE_UPDATE_FAILED", "message": "存储空间更新失败"}
|
||||
)
|
||||
|
||||
def create_user_tokens(self, user: User) -> dict:
|
||||
"""为用户创建访问令牌和刷新令牌"""
|
||||
access_token_expires = timedelta(minutes=settings.JWT_EXPIRE_MINUTES)
|
||||
refresh_token_expires = timedelta(days=settings.JWT_REFRESH_EXPIRE_DAYS)
|
||||
|
||||
access_token = create_access_token(
|
||||
data={"sub": str(user.id), "username": user.username, "email": user.email},
|
||||
expires_delta=access_token_expires
|
||||
)
|
||||
|
||||
refresh_token = create_refresh_token(
|
||||
data={"sub": str(user.id)},
|
||||
expires_delta=refresh_token_expires
|
||||
)
|
||||
|
||||
return {
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
"token_type": "bearer",
|
||||
"expires_in": settings.JWT_EXPIRE_MINUTES * 60
|
||||
}
|
||||
|
||||
def to_user_response(self, user: User) -> UserResponse:
|
||||
"""将用户模型转换为响应模型"""
|
||||
return UserResponse(
|
||||
id=user.id,
|
||||
username=user.username,
|
||||
email=user.email,
|
||||
avatar_url=user.avatar_url,
|
||||
storage_quota=user.storage_quota,
|
||||
storage_used=user.storage_used,
|
||||
is_active=user.is_active,
|
||||
is_verified=user.is_verified,
|
||||
last_login_at=user.last_login_at,
|
||||
created_at=user.created_at
|
||||
)
|
||||
Reference in New Issue
Block a user