mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-21 21:15:13 +00:00
236 lines
11 KiB
Python
236 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
import random
|
|
|
|
from fastapi import Request
|
|
from sqlalchemy import Select
|
|
|
|
from backend.app.admin.crud.crud_dept import dept_dao
|
|
from backend.app.admin.crud.crud_role import role_dao
|
|
from backend.app.admin.crud.crud_user import user_dao
|
|
from backend.app.admin.model import User
|
|
from backend.app.admin.schema.user import (
|
|
AddUserParam,
|
|
AvatarParam,
|
|
RegisterUserParam,
|
|
ResetPasswordParam,
|
|
UpdateUserParam,
|
|
UpdateUserRoleParam,
|
|
)
|
|
from backend.common.exception import errors
|
|
from backend.common.security.jwt import get_hash_password, get_token, password_verify, superuser_verify
|
|
from backend.core.conf import settings
|
|
from backend.database.db_mysql import async_db_session
|
|
from backend.database.db_redis import redis_client
|
|
|
|
|
|
class UserService:
|
|
@staticmethod
|
|
async def register(*, obj: RegisterUserParam) -> None:
|
|
async with async_db_session.begin() as db:
|
|
if not obj.password:
|
|
raise errors.ForbiddenError(msg='密码为空')
|
|
username = await user_dao.get_by_username(db, obj.username)
|
|
if username:
|
|
raise errors.ForbiddenError(msg='该用户名已注册')
|
|
obj.nickname = obj.nickname if obj.nickname else f'用户{random.randrange(10000, 99999)}'
|
|
nickname = await user_dao.get_by_nickname(db, obj.nickname)
|
|
if nickname:
|
|
raise errors.ForbiddenError(msg='昵称已注册')
|
|
email = await user_dao.check_email(db, obj.email)
|
|
if email:
|
|
raise errors.ForbiddenError(msg='该邮箱已注册')
|
|
await user_dao.create(db, obj)
|
|
|
|
@staticmethod
|
|
async def add(*, request: Request, obj: AddUserParam) -> None:
|
|
async with async_db_session.begin() as db:
|
|
await superuser_verify(request)
|
|
username = await user_dao.get_by_username(db, obj.username)
|
|
if username:
|
|
raise errors.ForbiddenError(msg='此用户名已注册')
|
|
obj.nickname = obj.nickname if obj.nickname else f'用户{random.randrange(10000, 99999)}'
|
|
nickname = await user_dao.get_by_nickname(db, obj.nickname)
|
|
if nickname:
|
|
raise errors.ForbiddenError(msg='昵称已注册')
|
|
if not obj.password:
|
|
raise errors.ForbiddenError(msg='密码为空')
|
|
email = await user_dao.check_email(db, obj.email)
|
|
if email:
|
|
raise errors.ForbiddenError(msg='该邮箱已注册')
|
|
dept = await dept_dao.get(db, obj.dept_id)
|
|
if not dept:
|
|
raise errors.NotFoundError(msg='部门不存在')
|
|
for role_id in obj.roles:
|
|
role = await role_dao.get(db, role_id)
|
|
if not role:
|
|
raise errors.NotFoundError(msg='角色不存在')
|
|
await user_dao.add(db, obj)
|
|
|
|
@staticmethod
|
|
async def pwd_reset(*, request: Request, obj: ResetPasswordParam) -> int:
|
|
async with async_db_session.begin() as db:
|
|
if not await password_verify(f'{obj.old_password}{request.user.salt}', request.user.password):
|
|
raise errors.ForbiddenError(msg='旧密码错误')
|
|
np1 = obj.new_password
|
|
np2 = obj.confirm_password
|
|
if np1 != np2:
|
|
raise errors.ForbiddenError(msg='两次密码输入不一致')
|
|
new_pwd = await get_hash_password(f'{obj.new_password}{request.user.salt}')
|
|
count = await user_dao.reset_password(db, request.user.id, new_pwd)
|
|
prefix = [
|
|
f'{settings.TOKEN_REDIS_PREFIX}:{request.user.id}:',
|
|
f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{request.user.id}:',
|
|
]
|
|
for i in prefix:
|
|
await redis_client.delete_prefix(i)
|
|
return count
|
|
|
|
@staticmethod
|
|
async def get_userinfo(*, username: str) -> User:
|
|
async with async_db_session() as db:
|
|
user = await user_dao.get_with_relation(db, username=username)
|
|
if not user:
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
return user
|
|
|
|
@staticmethod
|
|
async def update(*, request: Request, username: str, obj: UpdateUserParam) -> int:
|
|
async with async_db_session.begin() as db:
|
|
if not request.user.is_superuser:
|
|
if request.user.username != username:
|
|
raise errors.ForbiddenError(msg='你只能修改自己的信息')
|
|
input_user = await user_dao.get_with_relation(db, username=username)
|
|
if not input_user:
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
if input_user.username != obj.username:
|
|
_username = await user_dao.get_by_username(db, obj.username)
|
|
if _username:
|
|
raise errors.ForbiddenError(msg='该用户名已存在')
|
|
if input_user.nickname != obj.nickname:
|
|
nickname = await user_dao.get_by_nickname(db, obj.nickname)
|
|
if nickname:
|
|
raise errors.ForbiddenError(msg='改昵称已存在')
|
|
if input_user.email != obj.email:
|
|
email = await user_dao.check_email(db, obj.email)
|
|
if email:
|
|
raise errors.ForbiddenError(msg='该邮箱已注册')
|
|
count = await user_dao.update_userinfo(db, input_user, obj)
|
|
return count
|
|
|
|
@staticmethod
|
|
async def update_roles(*, request: Request, username: str, obj: UpdateUserRoleParam) -> None:
|
|
async with async_db_session.begin() as db:
|
|
if not request.user.is_superuser:
|
|
if request.user.username != username:
|
|
raise errors.ForbiddenError(msg='你只能修改自己的角色')
|
|
input_user = await user_dao.get_with_relation(db, username=username)
|
|
if not input_user:
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
for role_id in obj.roles:
|
|
role = await role_dao.get(db, role_id)
|
|
if not role:
|
|
raise errors.NotFoundError(msg='角色不存在')
|
|
await user_dao.update_role(db, input_user, obj)
|
|
await redis_client.delete_prefix(f'{settings.PERMISSION_REDIS_PREFIX}:{request.user.uuid}')
|
|
|
|
@staticmethod
|
|
async def update_avatar(*, request: Request, username: str, avatar: AvatarParam) -> int:
|
|
async with async_db_session.begin() as db:
|
|
if not request.user.is_superuser:
|
|
if request.user.username != username:
|
|
raise errors.ForbiddenError(msg='你只能修改自己的头像')
|
|
input_user = await user_dao.get_by_username(db, username)
|
|
if not input_user:
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
count = await user_dao.update_avatar(db, input_user, avatar)
|
|
return count
|
|
|
|
@staticmethod
|
|
async def get_select(*, dept: int, username: str = None, phone: str = None, status: int = None) -> Select:
|
|
return await user_dao.get_list(dept=dept, username=username, phone=phone, status=status)
|
|
|
|
@staticmethod
|
|
async def update_permission(*, request: Request, pk: int) -> int:
|
|
async with async_db_session.begin() as db:
|
|
await superuser_verify(request)
|
|
if not await user_dao.get(db, pk):
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
else:
|
|
user_id = request.user.id
|
|
if pk == user_id:
|
|
raise errors.ForbiddenError(msg='禁止修改自身管理员权限')
|
|
super_status = await user_dao.get_super(db, user_id)
|
|
count = await user_dao.set_super(db, pk, False if super_status else True)
|
|
return count
|
|
|
|
@staticmethod
|
|
async def update_staff(*, request: Request, pk: int) -> int:
|
|
async with async_db_session.begin() as db:
|
|
await superuser_verify(request)
|
|
if not await user_dao.get(db, pk):
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
else:
|
|
user_id = request.user.id
|
|
if pk == user_id:
|
|
raise errors.ForbiddenError(msg='禁止修改自身后台管理登陆权限')
|
|
staff_status = await user_dao.get_staff(db, user_id)
|
|
count = await user_dao.set_staff(db, pk, False if staff_status else True)
|
|
return count
|
|
|
|
@staticmethod
|
|
async def update_status(*, request: Request, pk: int) -> int:
|
|
async with async_db_session.begin() as db:
|
|
await superuser_verify(request)
|
|
if not await user_dao.get(db, pk):
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
else:
|
|
user_id = request.user.id
|
|
if pk == user_id:
|
|
raise errors.ForbiddenError(msg='禁止修改自身状态')
|
|
status = await user_dao.get_status(db, user_id)
|
|
count = await user_dao.set_status(db, pk, False if status else True)
|
|
return count
|
|
|
|
@staticmethod
|
|
async def update_multi_login(*, request: Request, pk: int) -> int:
|
|
async with async_db_session.begin() as db:
|
|
await superuser_verify(request)
|
|
if not await user_dao.get(db, pk):
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
else:
|
|
count = await user_dao.set_multi_login(db, pk)
|
|
token = await get_token(request)
|
|
user_id = request.user.id
|
|
latest_multi_login = await user_dao.get_multi_login(db, pk)
|
|
# TODO: 删除用户 refresh token, 此操作需要传参,暂时不考虑实现
|
|
# 当前用户修改自身时(普通/超级),除当前token外,其他token失效
|
|
if pk == user_id:
|
|
if not latest_multi_login:
|
|
prefix = f'{settings.TOKEN_REDIS_PREFIX}:{pk}:'
|
|
await redis_client.delete_prefix(prefix, exclude=prefix + token)
|
|
# 超级用户修改他人时,他人token将全部失效
|
|
else:
|
|
if not latest_multi_login:
|
|
prefix = f'{settings.TOKEN_REDIS_PREFIX}:{pk}:'
|
|
await redis_client.delete_prefix(prefix)
|
|
return count
|
|
|
|
@staticmethod
|
|
async def delete(*, username: str) -> int:
|
|
async with async_db_session.begin() as db:
|
|
input_user = await user_dao.get_by_username(db, username)
|
|
if not input_user:
|
|
raise errors.NotFoundError(msg='用户不存在')
|
|
count = await user_dao.delete(db, input_user.id)
|
|
prefix = [
|
|
f'{settings.TOKEN_REDIS_PREFIX}:{input_user.id}:',
|
|
f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{input_user.id}:',
|
|
]
|
|
for i in prefix:
|
|
await redis_client.delete_prefix(i)
|
|
return count
|
|
|
|
|
|
user_service = UserService()
|