diff --git a/backend/app/api/v1/user.py b/backend/app/api/v1/user.py index 2edfc8f8..d3b6c3a6 100644 --- a/backend/app/api/v1/user.py +++ b/backend/app/api/v1/user.py @@ -10,13 +10,14 @@ from backend.app.common.pagination import paging_data, PageDepends from backend.app.common.response.response_schema import response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.user import ( - CreateUser, + RegisterUser, GetAllUserInfo, ResetPassword, UpdateUser, Avatar, GetCurrentUserInfo, UpdateUserRole, + AddUser, ) from backend.app.services.user_service import UserService from backend.app.utils.serializers import select_to_json @@ -25,11 +26,17 @@ router = APIRouter() @router.post('/register', summary='用户注册') -async def user_register(obj: CreateUser): +async def user_register(obj: RegisterUser): await UserService.register(obj=obj) return await response_base.success() +@router.post('/add', summary='添加用户', dependencies=[DependsRBAC]) +async def add_user(obj: AddUser): + await UserService.add(obj=obj) + return await response_base.success() + + @router.post('/password/reset', summary='密码重置', dependencies=[DependsJwtAuth]) async def password_reset(request: Request, obj: ResetPassword): count = await UserService.pwd_reset(request=request, obj=obj) diff --git a/backend/app/crud/crud_user.py b/backend/app/crud/crud_user.py index ed5696b5..faa4e20b 100644 --- a/backend/app/crud/crud_user.py +++ b/backend/app/crud/crud_user.py @@ -12,10 +12,10 @@ from sqlalchemy.sql import Select from backend.app.common import jwt from backend.app.crud.base import CRUDBase from backend.app.models import User, Role -from backend.app.schemas.user import CreateUser, UpdateUser, Avatar, UpdateUserRole +from backend.app.schemas.user import RegisterUser, UpdateUser, Avatar, UpdateUserRole, AddUser -class CRUDUser(CRUDBase[User, CreateUser, UpdateUser]): +class CRUDUser(CRUDBase[User, RegisterUser, UpdateUser]): async def get(self, db: AsyncSession, user_id: int) -> User | None: return await self.get_(db, pk=user_id) @@ -23,6 +23,10 @@ class CRUDUser(CRUDBase[User, CreateUser, UpdateUser]): user = await db.execute(select(self.model).where(self.model.username == username)) return user.scalars().first() + async def get_by_nickname(self, db: AsyncSession, nickname: str) -> User | None: + user = await db.execute(select(self.model).where(self.model.nickname == nickname)) + return user.scalars().first() + async def update_login_time(self, db: AsyncSession, username: str, login_time: datetime) -> int: user = await db.execute( update(self.model).where(self.model.username == username).values(last_login_time=login_time) @@ -30,7 +34,15 @@ class CRUDUser(CRUDBase[User, CreateUser, UpdateUser]): await db.commit() return user.rowcount - async def create(self, db: AsyncSession, obj: CreateUser) -> NoReturn: + async def create(self, db: AsyncSession, obj: RegisterUser) -> NoReturn: + salt = text_captcha(5) + obj.password = await jwt.get_hash_password(obj.password + salt) + dict_obj = obj.dict() + dict_obj.update({'salt': salt}) + new_user = self.model(**dict_obj) + db.add(new_user) + + async def add(self, db: AsyncSession, obj: AddUser) -> NoReturn: salt = text_captcha(5) obj.password = await jwt.get_hash_password(obj.password + salt) dict_obj = obj.dict(exclude={'roles'}) diff --git a/backend/app/schemas/user.py b/backend/app/schemas/user.py index 45a22883..02ec4419 100644 --- a/backend/app/schemas/user.py +++ b/backend/app/schemas/user.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import random from datetime import datetime from email_validator import validate_email, EmailNotValidError @@ -20,10 +21,23 @@ class AuthLogin(Auth): captcha: str -class CreateUser(Auth): - dept_id: int | None = None +class RegisterUser(Auth): + nickname: str = Field(f'用户{random.randrange(10000, 99999)}') + email: str = Field(..., example='user@example.com') + + @validator('email') + def email_validate(cls, v): + try: + validate_email(v, check_deliverability=False).email + except EmailNotValidError: + raise ValueError('邮箱格式错误') + return v + + +class AddUser(Auth): + dept_id: int roles: list[int] - nickname: str + nickname: str = Field(f'用户{random.randrange(10000, 99999)}') email: str = Field(..., example='user@example.com') @validator('email') diff --git a/backend/app/services/menu_service.py b/backend/app/services/menu_service.py index 681aa727..c00ac93b 100644 --- a/backend/app/services/menu_service.py +++ b/backend/app/services/menu_service.py @@ -41,8 +41,9 @@ class MenuService: async with async_db_session() as db: roles = request.user.roles menu_ids = [] - for role in roles: - menu_ids.extend([menu.id for menu in role.menus]) + if roles: + for role in roles: + menu_ids.extend([menu.id for menu in role.menus]) menu_select = await MenuDao.get_role_menus(db, request.user.is_superuser, menu_ids) menu_tree = await get_tree_data(menu_select) return menu_tree diff --git a/backend/app/services/user_service.py b/backend/app/services/user_service.py index 65eb7d66..9b8cde7d 100644 --- a/backend/app/services/user_service.py +++ b/backend/app/services/user_service.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from typing import NoReturn - from fastapi import Request from sqlalchemy import Select @@ -15,19 +13,33 @@ from backend.app.crud.crud_role import RoleDao from backend.app.crud.crud_user import UserDao from backend.app.database.db_mysql import async_db_session from backend.app.models import User -from backend.app.schemas.user import CreateUser, ResetPassword, UpdateUser, Avatar, UpdateUserRole +from backend.app.schemas.user import RegisterUser, ResetPassword, UpdateUser, Avatar, UpdateUserRole, AddUser class UserService: @staticmethod - async def register(*, obj: CreateUser) -> NoReturn: + async def register(*, obj: RegisterUser) -> None: async with async_db_session.begin() as db: username = await UserDao.get_by_username(db, obj.username) if username: raise errors.ForbiddenError(msg='该用户名已注册') + nickname = await UserDao.get_by_nickname(db, obj.nickname) + if nickname: + raise errors.ForbiddenError(msg='该昵称已注册') email = await UserDao.check_email(db, obj.email) if email: raise errors.ForbiddenError(msg='该邮箱已注册') + await UserDao.create(db, obj) + + @staticmethod + async def add(*, obj: AddUser) -> None: + async with async_db_session.begin() as db: + username = await UserDao.get_by_username(db, obj.username) + if username: + raise errors.ForbiddenError(msg='该用户名已注册') + nickname = await UserDao.get_by_nickname(db, obj.nickname) + if nickname: + raise errors.ForbiddenError(msg='该昵称已注册') dept = await DeptDao.get(db, obj.dept_id) if not dept: raise errors.NotFoundError(msg='部门不存在') @@ -35,7 +47,10 @@ class UserService: role = await RoleDao.get(db, role_id) if not role: raise errors.NotFoundError(msg='角色不存在') - await UserDao.create(db, obj) + email = await UserDao.check_email(db, obj.email) + if email: + raise errors.ForbiddenError(msg='该邮箱已注册') + await UserDao.add(db, obj) @staticmethod async def pwd_reset(*, request: Request, obj: ResetPassword) -> int: @@ -72,16 +87,17 @@ class UserService: if not input_user: raise errors.NotFoundError(msg='用户不存在') if input_user.username != obj.username: - username = await UserDao.get_by_username(db, obj.username) - if username: + _username = await UserDao.get_by_username(db, obj.username) + if _username: raise errors.ForbiddenError(msg='该用户名已存在') + if input_user.nickname != obj.nickname: + nickname = await UserDao.get_by_nickname(db, obj.nickname) + if nickname: + raise errors.ForbiddenError(msg='改昵称已存在') if input_user.email != obj.email: email = await UserDao.check_email(db, obj.email) if email: raise errors.ForbiddenError(msg='该邮箱已注册') - dept = await DeptDao.get(db, obj.dept_id) - if not dept: - raise errors.NotFoundError(msg='部门不存在') count = await UserDao.update_userinfo(db, input_user, obj) return count