mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-22 05:22:56 +00:00
Add role-related interfaces (#89)
This commit is contained in:
@@ -1,7 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from fastapi import APIRouter
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Query, Request
|
||||
|
||||
from backend.app.common.casbin_rbac import DependsRBAC
|
||||
from backend.app.common.jwt import DependsJwtAuth
|
||||
from backend.app.common.pagination import PageDepends, paging_data
|
||||
from backend.app.common.response.response_schema import response_base
|
||||
from backend.app.database.db_mysql import CurrentSession
|
||||
from backend.app.schemas.role import GetAllRole, CreateRole, UpdateRole
|
||||
from backend.app.services.role_service import RoleService
|
||||
from backend.app.utils.serializers import select_to_json
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# TODO: 添加 role 相关接口
|
||||
|
||||
@router.get('/{pk}', summary='获取角色详情', dependencies=[DependsJwtAuth])
|
||||
async def get_role(pk: int):
|
||||
role = await RoleService.get(pk=pk)
|
||||
data = GetAllRole(**select_to_json(role))
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@router.get('', summary='(模糊条件)分页获取所有角色', dependencies=[DependsJwtAuth, PageDepends])
|
||||
async def get_all_roles(
|
||||
db: CurrentSession,
|
||||
name: Annotated[str | None, Query()] = None,
|
||||
data_scope: Annotated[int | None, Query()] = None,
|
||||
):
|
||||
role_select = await RoleService.get_select(name=name, data_scope=data_scope)
|
||||
page_data = await paging_data(db, role_select, GetAllRole)
|
||||
return response_base.success(data=page_data)
|
||||
|
||||
|
||||
@router.post('', summary='创建角色', dependencies=[DependsRBAC])
|
||||
async def create_role(request: Request, obj: CreateRole):
|
||||
await RoleService.create(obj=obj, user_id=request.user.id)
|
||||
return response_base.success()
|
||||
|
||||
|
||||
@router.put('/{pk}', summary='更新角色', dependencies=[DependsRBAC])
|
||||
async def update_role(request: Request, pk: int, obj: UpdateRole):
|
||||
count = await RoleService.update(pk=pk, obj=obj, user_id=request.user.id)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
|
||||
@router.delete('', summary='(批量)删除角色', dependencies=[DependsRBAC])
|
||||
async def delete_role(pk: Annotated[list[int], Query(...)]):
|
||||
count = await RoleService.delete(pk=pk)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -6,8 +6,8 @@ from backend.app.schemas.menu import CreateMenu, UpdateMenu
|
||||
|
||||
|
||||
class CRUDMenu(CRUDBase[Menu, CreateMenu, UpdateMenu]):
|
||||
# TODO: 添加 menu 相关数据库操作
|
||||
pass
|
||||
async def get(self, db, menu_id: int) -> Menu | None:
|
||||
return await self.get_(db, menu_id)
|
||||
|
||||
|
||||
MenuDao: CRUDMenu = CRUDMenu(Menu)
|
||||
|
||||
@@ -1,13 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import NoReturn
|
||||
|
||||
from sqlalchemy import select, update, delete
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from backend.app.crud.base import CRUDBase
|
||||
from backend.app.models import Role
|
||||
from backend.app.models import Role, Menu
|
||||
from backend.app.schemas.role import CreateRole, UpdateRole
|
||||
|
||||
|
||||
class CRUDRole(CRUDBase[Role, CreateRole, UpdateRole]):
|
||||
async def get(self, db, role_id: int):
|
||||
async def get(self, db, role_id: int) -> Role | None:
|
||||
return await self.get_(db, role_id)
|
||||
|
||||
async def get_with_relation(self, db, role_id: int) -> Role | None:
|
||||
role = await db.execute(
|
||||
select(self.model)
|
||||
.options(selectinload(self.model.menus))
|
||||
.where(self.model.id == role_id)
|
||||
)
|
||||
return role.scalars().first()
|
||||
|
||||
async def get_all(self, name: str = None, data_scope: int = None):
|
||||
se = select(self.model).options(selectinload(self.model.menus)).order_by(self.model.created_time.desc())
|
||||
where_list = []
|
||||
if name:
|
||||
where_list.append(self.model.name.like(f'%{name}%'))
|
||||
if data_scope:
|
||||
where_list.append(self.model.data_scope == data_scope)
|
||||
if where_list:
|
||||
se = se.where(*where_list)
|
||||
return se
|
||||
|
||||
async def get_by_name(self, db, name: str) -> Role | None:
|
||||
role = await db.execute(select(self.model).where(self.model.name == name))
|
||||
return role.scalars().first()
|
||||
|
||||
async def create(self, db, obj_in: CreateRole, user_id: int) -> NoReturn:
|
||||
new_role = self.model(**obj_in.dict(exclude={'menus'}), create_user=user_id)
|
||||
menu_list = []
|
||||
for menu_id in obj_in.menus:
|
||||
menu_list.append(await db.get(Menu, menu_id))
|
||||
new_role.menus.append(*menu_list)
|
||||
db.add(new_role)
|
||||
|
||||
async def update(self, db, role_id: int, obj_in: UpdateRole, user_id: int) -> int:
|
||||
role = await db.execute(
|
||||
update(self.model)
|
||||
.where(self.model.id == role_id)
|
||||
.values(**obj_in.dict(exclude={'menus'}), update_user=user_id)
|
||||
)
|
||||
current_role = await self.get_with_relation(db, role_id)
|
||||
# 删除角色所有菜单
|
||||
for i in list(current_role.menus):
|
||||
current_role.menus.remove(i)
|
||||
# 添加角色菜单
|
||||
menu_list = []
|
||||
for menu_id in obj_in.menus:
|
||||
menu_list.append(await db.get(Menu, menu_id))
|
||||
current_role.menus.append(*menu_list)
|
||||
return role.rowcount
|
||||
|
||||
async def delete(self, db, role_id: list[int]) -> int:
|
||||
roles = await db.execute(delete(self.model).where(self.model.id.in_(role_id)))
|
||||
return roles.rowcount
|
||||
|
||||
|
||||
RoleDao: CRUDRole = CRUDRole(Role)
|
||||
|
||||
@@ -20,6 +20,6 @@ class Dept(Base):
|
||||
phone: Mapped[str | None] = mapped_column(String(11), default=None, comment='手机')
|
||||
email: Mapped[str | None] = mapped_column(String(50), default=None, comment='邮箱')
|
||||
status: Mapped[bool] = mapped_column(default=True, comment='部门状态(0停用 1正常)')
|
||||
del_flag: Mapped[bool] = mapped_column(default=True, comment='删除标志(0删除 1存在)')
|
||||
del_flag: Mapped[bool] = mapped_column(default=False, comment='删除标志(0删除 1存在)')
|
||||
# 用户部门一对多
|
||||
users: Mapped['User'] = relationship(init=False, back_populates='dept') # noqa: F821
|
||||
|
||||
@@ -22,7 +22,7 @@ class Menu(Base):
|
||||
menu_type: Mapped[int] = mapped_column(default=0, comment='菜单类型(0目录 1菜单 2按钮)')
|
||||
icon: Mapped[str | None] = mapped_column(String(100), default='#', comment='菜单图标')
|
||||
remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注')
|
||||
del_flag: Mapped[bool] = mapped_column(default=True, comment='删除标志(0删除 1存在)')
|
||||
del_flag: Mapped[bool] = mapped_column(default=False, comment='删除标志(0删除 1存在)')
|
||||
# 菜单角色多对多
|
||||
roles: Mapped[list['Role']] = relationship( # noqa: F821
|
||||
init=False, secondary=sys_role_menu, back_populates='menus'
|
||||
|
||||
@@ -15,9 +15,8 @@ class Role(Base):
|
||||
|
||||
id: Mapped[id_key] = mapped_column(init=False)
|
||||
name: Mapped[str] = mapped_column(String(20), unique=True, comment='角色名称')
|
||||
sort: Mapped[int] = mapped_column(default=0, comment='显示顺序')
|
||||
data_scope: Mapped[int | None] = mapped_column(default=2, comment='数据范围(1:全部数据权限 2:自定数据权限)')
|
||||
del_flag: Mapped[bool] = mapped_column(default=True, comment='删除标志(0删除 1存在)')
|
||||
del_flag: Mapped[bool] = mapped_column(default=False, comment='删除标志(0删除 1存在)')
|
||||
# 角色用户多对多
|
||||
users: Mapped[list['User']] = relationship( # noqa: F821
|
||||
init=False, secondary=sys_user_role, back_populates='roles'
|
||||
|
||||
@@ -10,7 +10,6 @@ from backend.app.schemas.menu import GetAllMenu
|
||||
|
||||
class RoleBase(BaseModel):
|
||||
name: str
|
||||
sort: int = Field(default=0, ge=0, description='排序')
|
||||
data_scope: int | None = Field(default=RoleDataScope.custom, description='数据范围(1:全部数据权限 2:自定数据权限)') # noqa: E501
|
||||
del_flag: bool
|
||||
|
||||
@@ -22,11 +21,11 @@ class RoleBase(BaseModel):
|
||||
|
||||
|
||||
class CreateRole(RoleBase):
|
||||
menu_ids: list[int]
|
||||
menus: list[int]
|
||||
|
||||
|
||||
class UpdateRole(RoleBase):
|
||||
menu_ids: list[int]
|
||||
menus: list[int]
|
||||
|
||||
|
||||
class GetAllRole(RoleBase):
|
||||
|
||||
@@ -12,7 +12,7 @@ from backend.app.schemas.api import CreateApi, UpdateApi
|
||||
class ApiService:
|
||||
@staticmethod
|
||||
async def get(*, pk: int) -> Api:
|
||||
async with async_db_session.begin() as db:
|
||||
async with async_db_session() as db:
|
||||
api = await ApiDao.get(db, pk)
|
||||
if not api:
|
||||
raise errors.NotFoundError(msg='接口不存在')
|
||||
|
||||
@@ -1,7 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from sqlalchemy import Select
|
||||
|
||||
from backend.app.common.exception import errors
|
||||
from backend.app.crud.crud_menu import MenuDao
|
||||
from backend.app.crud.crud_role import RoleDao
|
||||
from backend.app.database.db_mysql import async_db_session
|
||||
from backend.app.models import Role
|
||||
from backend.app.schemas.role import CreateRole, UpdateRole
|
||||
|
||||
|
||||
class RoleService:
|
||||
# TODO: 添加 role 相关服务
|
||||
pass
|
||||
@staticmethod
|
||||
async def get(pk: int) -> Role:
|
||||
async with async_db_session() as db:
|
||||
role = await RoleDao.get_with_relation(db, pk)
|
||||
if not role:
|
||||
raise errors.NotFoundError(msg='角色不存在')
|
||||
return role
|
||||
|
||||
@staticmethod
|
||||
async def get_select(*, name: str = None, data_scope: int = None) -> Select:
|
||||
return await RoleDao.get_all(name=name, data_scope=data_scope)
|
||||
|
||||
@staticmethod
|
||||
async def create(*, obj: CreateRole, user_id: int) -> None:
|
||||
async with async_db_session.begin() as db:
|
||||
role = await RoleDao.get_by_name(db, obj.name)
|
||||
if role:
|
||||
raise errors.ForbiddenError(msg='角色已存在')
|
||||
for menu_id in obj.menus:
|
||||
menu = await MenuDao.get(db, menu_id)
|
||||
if not menu:
|
||||
raise errors.ForbiddenError(msg='菜单不存在')
|
||||
await RoleDao.create(db, obj, user_id)
|
||||
|
||||
@staticmethod
|
||||
async def update(*, pk: int, obj: UpdateRole, user_id: int) -> int:
|
||||
async with async_db_session.begin() as db:
|
||||
role = await RoleDao.get(db, pk)
|
||||
if not role:
|
||||
raise errors.NotFoundError(msg='角色不存在')
|
||||
if role.name != obj.name:
|
||||
role = await RoleDao.get_by_name(db, obj.name)
|
||||
if role:
|
||||
raise errors.ForbiddenError(msg='角色已存在')
|
||||
for menu_id in obj.menus:
|
||||
menu = await MenuDao.get(db, menu_id)
|
||||
if not menu:
|
||||
raise errors.ForbiddenError(msg='菜单不存在')
|
||||
count = await RoleDao.update(db, pk, obj, user_id)
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async with async_db_session.begin() as db:
|
||||
count = await RoleDao.delete(db, pk)
|
||||
return count
|
||||
|
||||
Reference in New Issue
Block a user