mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-20 20:49:53 +00:00
333 lines
9.8 KiB
Python
333 lines
9.8 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
import bcrypt
|
|
|
|
from sqlalchemy import and_, desc, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.orm import noload, selectinload
|
|
from sqlalchemy.sql import Select
|
|
from sqlalchemy_crud_plus import CRUDPlus
|
|
|
|
from backend.app.admin.model import Dept, Role, User
|
|
from backend.app.admin.schema.user import (
|
|
AddUserParam,
|
|
AvatarParam,
|
|
RegisterUserParam,
|
|
UpdateUserParam,
|
|
UpdateUserRoleParam,
|
|
)
|
|
from backend.common.security.jwt import get_hash_password
|
|
from backend.utils.timezone import timezone
|
|
|
|
|
|
class CRUDUser(CRUDPlus[User]):
|
|
"""用户数据库操作类"""
|
|
|
|
async def get(self, db: AsyncSession, user_id: int) -> User | None:
|
|
"""
|
|
获取用户详情
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:return:
|
|
"""
|
|
return await self.select_model(db, user_id)
|
|
|
|
async def get_by_username(self, db: AsyncSession, username: str) -> User | None:
|
|
"""
|
|
通过用户名获取用户
|
|
|
|
:param db: 数据库会话
|
|
:param username: 用户名
|
|
:return:
|
|
"""
|
|
return await self.select_model_by_column(db, username=username)
|
|
|
|
async def get_by_nickname(self, db: AsyncSession, nickname: str) -> User | None:
|
|
"""
|
|
通过昵称获取用户
|
|
|
|
:param db: 数据库会话
|
|
:param nickname: 用户昵称
|
|
:return:
|
|
"""
|
|
return await self.select_model_by_column(db, nickname=nickname)
|
|
|
|
async def update_login_time(self, db: AsyncSession, username: str) -> int:
|
|
"""
|
|
更新用户最后登录时间
|
|
|
|
:param db: 数据库会话
|
|
:param username: 用户名
|
|
:return:
|
|
"""
|
|
return await self.update_model_by_column(db, {'last_login_time': timezone.now()}, username=username)
|
|
|
|
async def create(self, db: AsyncSession, obj: RegisterUserParam, *, social: bool = False) -> None:
|
|
"""
|
|
创建用户
|
|
|
|
:param db: 数据库会话
|
|
:param obj: 注册用户参数
|
|
:param social: 是否社交用户
|
|
:return:
|
|
"""
|
|
if not social:
|
|
salt = bcrypt.gensalt()
|
|
obj.password = get_hash_password(obj.password, salt)
|
|
dict_obj = obj.model_dump()
|
|
dict_obj.update({'is_staff': True, 'salt': salt})
|
|
else:
|
|
dict_obj = obj.model_dump()
|
|
dict_obj.update({'is_staff': True, 'salt': None})
|
|
new_user = self.model(**dict_obj)
|
|
db.add(new_user)
|
|
|
|
async def add(self, db: AsyncSession, obj: AddUserParam) -> None:
|
|
"""
|
|
添加用户
|
|
|
|
:param db: 数据库会话
|
|
:param obj: 添加用户参数
|
|
:return:
|
|
"""
|
|
salt = bcrypt.gensalt()
|
|
obj.password = get_hash_password(obj.password, salt)
|
|
dict_obj = obj.model_dump(exclude={'roles'})
|
|
dict_obj.update({'salt': salt})
|
|
new_user = self.model(**dict_obj)
|
|
|
|
role_list = []
|
|
for role_id in obj.roles:
|
|
role_list.append(await db.get(Role, role_id))
|
|
new_user.roles.extend(role_list)
|
|
|
|
db.add(new_user)
|
|
|
|
async def update_userinfo(self, db: AsyncSession, input_user: int, obj: UpdateUserParam) -> int:
|
|
"""
|
|
更新用户信息
|
|
|
|
:param db: 数据库会话
|
|
:param input_user: 用户 ID
|
|
:param obj: 更新用户参数
|
|
:return:
|
|
"""
|
|
return await self.update_model(db, input_user, obj)
|
|
|
|
@staticmethod
|
|
async def update_role(db: AsyncSession, input_user: User, obj: UpdateUserRoleParam) -> None:
|
|
"""
|
|
更新用户角色
|
|
|
|
:param db: 数据库会话
|
|
:param input_user: 用户对象
|
|
:param obj: 更新角色参数
|
|
:return:
|
|
"""
|
|
for i in list(input_user.roles):
|
|
input_user.roles.remove(i)
|
|
|
|
role_list = []
|
|
for role_id in obj.roles:
|
|
role_list.append(await db.get(Role, role_id))
|
|
input_user.roles.extend(role_list)
|
|
|
|
async def update_avatar(self, db: AsyncSession, input_user: int, avatar: AvatarParam) -> int:
|
|
"""
|
|
更新用户头像
|
|
|
|
:param db: 数据库会话
|
|
:param input_user: 用户 ID
|
|
:param avatar: 头像地址
|
|
:return:
|
|
"""
|
|
return await self.update_model(db, input_user, {'avatar': avatar.url})
|
|
|
|
async def delete(self, db: AsyncSession, user_id: int) -> int:
|
|
"""
|
|
删除用户
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:return:
|
|
"""
|
|
return await self.delete_model(db, user_id)
|
|
|
|
async def check_email(self, db: AsyncSession, email: str) -> User | None:
|
|
"""
|
|
检查邮箱是否已被注册
|
|
|
|
:param db: 数据库会话
|
|
:param email: 电子邮箱
|
|
:return:
|
|
"""
|
|
return await self.select_model_by_column(db, email=email)
|
|
|
|
async def reset_password(self, db: AsyncSession, pk: int, new_pwd: str) -> int:
|
|
"""
|
|
重置用户密码
|
|
|
|
:param db: 数据库会话
|
|
:param pk: 用户 ID
|
|
:param new_pwd: 新密码(已加密)
|
|
:return:
|
|
"""
|
|
return await self.update_model(db, pk, {'password': new_pwd})
|
|
|
|
async def get_list(
|
|
self, dept: int | None = None, username: str | None = None, phone: str | None = None, status: int | None = None
|
|
) -> Select:
|
|
"""
|
|
获取用户列表
|
|
|
|
:param dept: 部门 ID
|
|
:param username: 用户名
|
|
:param phone: 电话号码
|
|
:param status: 用户状态
|
|
:return:
|
|
"""
|
|
stmt = (
|
|
select(self.model)
|
|
.options(
|
|
selectinload(self.model.dept).options(noload(Dept.parent), noload(Dept.children), noload(Dept.users)),
|
|
noload(self.model.socials),
|
|
selectinload(self.model.roles).options(noload(Role.users), noload(Role.menus), noload(Role.rules)),
|
|
)
|
|
.order_by(desc(self.model.join_time))
|
|
)
|
|
|
|
filters = []
|
|
if dept:
|
|
filters.append(self.model.dept_id == dept)
|
|
if username:
|
|
filters.append(self.model.username.like(f'%{username}%'))
|
|
if phone:
|
|
filters.append(self.model.phone.like(f'%{phone}%'))
|
|
if status is not None:
|
|
filters.append(self.model.status == status)
|
|
|
|
if filters:
|
|
stmt = stmt.where(and_(*filters))
|
|
|
|
return stmt
|
|
|
|
async def get_super(self, db: AsyncSession, user_id: int) -> bool:
|
|
"""
|
|
获取用户是否为超级管理员
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:return:
|
|
"""
|
|
user = await self.get(db, user_id)
|
|
return user.is_superuser
|
|
|
|
async def get_staff(self, db: AsyncSession, user_id: int) -> bool:
|
|
"""
|
|
获取用户是否可以登录后台
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:return:
|
|
"""
|
|
user = await self.get(db, user_id)
|
|
return user.is_staff
|
|
|
|
async def get_status(self, db: AsyncSession, user_id: int) -> int:
|
|
"""
|
|
获取用户状态
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:return:
|
|
"""
|
|
user = await self.get(db, user_id)
|
|
return user.status
|
|
|
|
async def get_multi_login(self, db: AsyncSession, user_id: int) -> bool:
|
|
"""
|
|
获取用户是否允许多端登录
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:return:
|
|
"""
|
|
user = await self.get(db, user_id)
|
|
return user.is_multi_login
|
|
|
|
async def set_super(self, db: AsyncSession, user_id: int, is_super: bool) -> int:
|
|
"""
|
|
设置用户超级管理员状态
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:param is_super: 是否超级管理员
|
|
:return:
|
|
"""
|
|
return await self.update_model(db, user_id, {'is_superuser': is_super})
|
|
|
|
async def set_staff(self, db: AsyncSession, user_id: int, is_staff: bool) -> int:
|
|
"""
|
|
设置用户后台登录状态
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:param is_staff: 是否可登录后台
|
|
:return:
|
|
"""
|
|
return await self.update_model(db, user_id, {'is_staff': is_staff})
|
|
|
|
async def set_status(self, db: AsyncSession, user_id: int, status: int) -> int:
|
|
"""
|
|
设置用户状态
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:param status: 状态
|
|
:return:
|
|
"""
|
|
return await self.update_model(db, user_id, {'status': status})
|
|
|
|
async def set_multi_login(self, db: AsyncSession, user_id: int, multi_login: bool) -> int:
|
|
"""
|
|
设置用户多端登录状态
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:param multi_login: 是否允许多端登录
|
|
:return:
|
|
"""
|
|
return await self.update_model(db, user_id, {'is_multi_login': multi_login})
|
|
|
|
async def get_with_relation(
|
|
self, db: AsyncSession, *, user_id: int | None = None, username: str | None = None
|
|
) -> User | None:
|
|
"""
|
|
获取用户关联信息
|
|
|
|
:param db: 数据库会话
|
|
:param user_id: 用户 ID
|
|
:param username: 用户名
|
|
:return:
|
|
"""
|
|
stmt = select(self.model).options(
|
|
selectinload(self.model.dept),
|
|
selectinload(self.model.roles).options(selectinload(Role.menus), selectinload(Role.rules)),
|
|
)
|
|
|
|
filters = []
|
|
if user_id:
|
|
filters.append(self.model.id == user_id)
|
|
if username:
|
|
filters.append(self.model.username == username)
|
|
|
|
if filters:
|
|
stmt = stmt.where(and_(*filters))
|
|
|
|
user = await db.execute(stmt)
|
|
return user.scalars().first()
|
|
|
|
|
|
user_dao: CRUDUser = CRUDUser(User)
|