Files
fastapi-best-architecture/backend/app/admin/crud/crud_user.py
T

266 lines
7.9 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import bcrypt
from sqlalchemy import 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 (
AddOAuth2UserParam,
AddUserParam,
UpdateUserParam,
)
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 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)
stmt = select(Role).where(Role.id.in_(obj.roles))
roles = await db.execute(stmt)
new_user.roles = roles.scalars().all()
db.add(new_user)
async def add_by_oauth2(self, db: AsyncSession, obj: AddOAuth2UserParam) -> None:
"""
通过 OAuth2 添加用户
:param db: 数据库会话
:param obj: 注册用户参数
:return:
"""
salt = bcrypt.gensalt()
obj.password = get_hash_password(obj.password, salt)
dict_obj = obj.model_dump()
dict_obj.update({'is_staff': True, 'salt': salt})
new_user = self.model(**dict_obj)
stmt = select(Role)
role = await db.execute(stmt)
new_user.roles = [role.scalars().first()] # 默认绑定第一个角色
db.add(new_user)
async def update(self, db: AsyncSession, input_user: User, obj: UpdateUserParam) -> int:
"""
更新用户信息
:param db: 数据库会话
:param input_user: 用户 ID
:param obj: 更新用户参数
:return:
"""
role_ids = obj.roles
del obj.roles
count = await self.update_model(db, input_user.id, obj)
stmt = select(Role).where(Role.id.in_(role_ids))
roles = await db.execute(stmt)
input_user.roles = roles.scalars().all()
return count
async def update_avatar(self, db: AsyncSession, user_id: int, avatar: str) -> int:
"""
更新用户头像
:param db: 数据库会话
:param user_id: 用户 ID
:param avatar: 头像地址
:return:
"""
return await self.update_model(db, user_id, {'avatar': avatar})
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, username: str | None, phone: str | None, status: int | None) -> Select:
"""
获取用户列表
:param dept: 部门 ID
:param username: 用户名
:param phone: 电话号码
:param status: 用户状态
:return:
"""
filters = {}
if dept:
filters['dept_id'] = dept
if username:
filters['username__like'] = f'%{username}%'
if phone:
filters['phone_like'] = f'%{phone}%'
if status is not None:
filters['status'] = status
return await self.select_order(
'id',
'desc',
load_options=[
selectinload(self.model.dept).options(noload(Dept.parent), noload(Dept.children), noload(Dept.users)),
selectinload(self.model.roles).options(noload(Role.users), noload(Role.menus), noload(Role.scopes)),
],
**filters,
)
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:
"""
filters = {}
if user_id:
filters['id'] = user_id
if username:
filters['username'] = username
return await self.select_model_by_column(
db,
load_options=[selectinload(self.model.roles).options(selectinload(Role.menus), selectinload(Role.scopes))],
load_strategies=['dept'],
**filters,
)
user_dao: CRUDUser = CRUDUser(User)