Files
Foxel/domain/user/service.py
2026-02-09 13:19:28 +08:00

191 lines
6.6 KiB
Python

from typing import List
from fastapi import HTTPException
from domain.auth.service import AuthService
from domain.permission.service import PermissionService
from models.database import Role, UserAccount, UserRole
from .types import UserCreate, UserDetail, UserInfo, UserUpdate
class UserService:
"""用户管理服务"""
@classmethod
async def get_all_users(cls) -> List[UserInfo]:
users = await UserAccount.all().order_by("id")
return [
UserInfo(
id=u.id,
username=u.username,
email=u.email,
full_name=u.full_name,
disabled=u.disabled,
is_admin=u.is_admin,
created_at=u.created_at,
last_login=u.last_login,
)
for u in users
]
@classmethod
async def get_user(cls, user_id: int) -> UserDetail:
user = await UserAccount.get_or_none(id=user_id).prefetch_related("created_by")
if not user:
raise HTTPException(404, detail="用户不存在")
user_roles = await UserRole.filter(user_id=user_id).prefetch_related("role")
roles = [ur.role.name for ur in user_roles]
created_by_username = None
if user.created_by_id:
creator = await UserAccount.get_or_none(id=user.created_by_id)
if creator:
created_by_username = creator.username
return UserDetail(
id=user.id,
username=user.username,
email=user.email,
full_name=user.full_name,
disabled=user.disabled,
is_admin=user.is_admin,
created_at=user.created_at,
last_login=user.last_login,
roles=roles,
created_by_username=created_by_username,
)
@classmethod
async def get_users_by_role(cls, role_id: int) -> List[UserInfo]:
role = await Role.get_or_none(id=role_id)
if not role:
raise HTTPException(404, detail="角色不存在")
user_roles = await UserRole.filter(role_id=role_id).prefetch_related("user")
users = [ur.user for ur in user_roles if ur.user]
users.sort(key=lambda u: u.id)
return [
UserInfo(
id=u.id,
username=u.username,
email=u.email,
full_name=u.full_name,
disabled=u.disabled,
is_admin=u.is_admin,
created_at=u.created_at,
last_login=u.last_login,
)
for u in users
]
@classmethod
async def create_user(cls, data: UserCreate, creator_id: int) -> UserDetail:
existing = await UserAccount.get_or_none(username=data.username)
if existing:
raise HTTPException(400, detail="用户名已存在")
if data.email:
existing_email = await UserAccount.get_or_none(email=data.email)
if existing_email:
raise HTTPException(400, detail="邮箱已被使用")
hashed_password = AuthService.get_password_hash(data.password)
user = await UserAccount.create(
username=data.username,
email=data.email,
full_name=data.full_name,
hashed_password=hashed_password,
disabled=data.disabled,
is_admin=data.is_admin,
created_by_id=creator_id,
)
if data.role_ids:
for role_id in data.role_ids:
role = await Role.get_or_none(id=role_id)
if role:
await UserRole.create(user_id=user.id, role_id=role_id)
return await cls.get_user(user.id)
@classmethod
async def update_user(cls, user_id: int, data: UserUpdate, operator_id: int) -> UserDetail:
user = await UserAccount.get_or_none(id=user_id)
if not user:
raise HTTPException(404, detail="用户不存在")
if data.is_admin is not None and user_id == operator_id:
raise HTTPException(400, detail="不能修改自己的管理员状态")
if data.email is not None:
existing = await UserAccount.filter(email=data.email).exclude(id=user_id).first()
if existing:
raise HTTPException(400, detail="邮箱已被使用")
user.email = data.email
if data.full_name is not None:
user.full_name = data.full_name
if data.password is not None:
user.hashed_password = AuthService.get_password_hash(data.password)
if data.is_admin is not None:
user.is_admin = data.is_admin
if data.disabled is not None:
if user_id == operator_id and data.disabled:
raise HTTPException(400, detail="不能禁用自己")
user.disabled = data.disabled
await user.save()
PermissionService.clear_cache(user_id)
return await cls.get_user(user_id)
@classmethod
async def delete_user(cls, user_id: int, operator_id: int) -> None:
if user_id == operator_id:
raise HTTPException(400, detail="不能删除自己")
user = await UserAccount.get_or_none(id=user_id)
if not user:
raise HTTPException(404, detail="用户不存在")
await UserRole.filter(user_id=user_id).delete()
await user.delete()
PermissionService.clear_cache(user_id)
@classmethod
async def set_user_roles(cls, user_id: int, role_ids: List[int]) -> List[str]:
user = await UserAccount.get_or_none(id=user_id)
if not user:
raise HTTPException(404, detail="用户不存在")
roles = await Role.filter(id__in=role_ids)
valid_role_ids = {r.id for r in roles}
invalid_ids = set(role_ids) - valid_role_ids
if invalid_ids:
raise HTTPException(400, detail=f"无效的角色ID: {invalid_ids}")
await UserRole.filter(user_id=user_id).delete()
for role_id in role_ids:
await UserRole.create(user_id=user_id, role_id=role_id)
PermissionService.clear_cache(user_id)
return [r.name for r in roles if r.id in role_ids]
@classmethod
async def remove_user_role(cls, user_id: int, role_id: int) -> List[str]:
user = await UserAccount.get_or_none(id=user_id)
if not user:
raise HTTPException(404, detail="用户不存在")
await UserRole.filter(user_id=user_id, role_id=role_id).delete()
PermissionService.clear_cache(user_id)
user_roles = await UserRole.filter(user_id=user_id).prefetch_related("role")
return [ur.role.name for ur in user_roles]