Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add department data operation permissions #443

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion backend/app/admin/api/v1/sys/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@

from fastapi import APIRouter, Depends, Path, Query, Request

from backend.app.admin.schema.role import CreateRoleParam, GetRoleListDetails, UpdateRoleMenuParam, UpdateRoleParam
from backend.app.admin.schema.role import (
CreateRoleParam,
GetRoleListDetails,
UpdateRoleDeptParam,
UpdateRoleMenuParam,
UpdateRoleParam,
)
from backend.app.admin.service.menu_service import menu_service
from backend.app.admin.service.role_service import role_service
from backend.common.pagination import DependsPagination, paging_data
Expand Down Expand Up @@ -109,6 +115,23 @@ async def update_role_menus(
return response_base.fail()


@router.put(
'/{pk}/dept',
summary='更新角色部门',
dependencies=[
Depends(RequestPermission('sys:role:dept:edit')),
DependsRBAC,
],
)
async def update_role_depts(
request: Request, pk: Annotated[int, Path(...)], dept_ids: UpdateRoleDeptParam
) -> ResponseModel:
count = await role_service.update_role_dept(request=request, pk=pk, menu_ids=dept_ids)
if count > 0:
return response_base.success()
return response_base.fail()


@router.delete(
'',
summary='(批量)删除角色',
Expand Down
19 changes: 17 additions & 2 deletions backend/app/admin/crud/crud_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from sqlalchemy.orm import selectinload
from sqlalchemy_crud_plus import CRUDPlus

from backend.app.admin.model import Menu, Role, User
from backend.app.admin.schema.role import CreateRoleParam, UpdateRoleMenuParam, UpdateRoleParam
from backend.app.admin.model import Dept, Menu, Role, User
from backend.app.admin.schema.role import CreateRoleParam, UpdateRoleDeptParam, UpdateRoleMenuParam, UpdateRoleParam


class CRUDRole(CRUDPlus[Role]):
Expand Down Expand Up @@ -122,6 +122,21 @@ async def update_menus(self, db, role_id: int, menu_ids: UpdateRoleMenuParam) ->
current_role.menus = menus.scalars().all()
return len(current_role.menus)

async def update_depts(self, db, role_id: int, dept_ids: UpdateRoleDeptParam) -> int:
"""
更新角色部门

:param db:
:param role_id:
:param dept_ids:
:return:
"""
stmt = select(Dept).where(Dept.id.in_(dept_ids.depts))
depts = await db.execute(stmt)
current_role = await self.get_with_relation(db, role_id)
current_role.depts = depts.scalars().all()
return len(current_role.depts)

async def delete(self, db, role_id: list[int]) -> int:
"""
删除角色
Expand Down
14 changes: 8 additions & 6 deletions backend/app/admin/crud/crud_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,10 @@ async def get_list(self, dept: int = None, username: str = None, phone: str = No
"""
stmt = (
select(self.model)
.options(selectinload(self.model.dept))
.options(selectinload(self.model.roles).selectinload(Role.menus))
.options(
selectinload(self.model.dept),
selectinload(self.model.roles).selectinload(Role.menus),
)
.order_by(desc(self.model.join_time))
)
where_list = []
Expand Down Expand Up @@ -297,10 +299,10 @@ async def get_with_relation(self, db: AsyncSession, *, user_id: int = None, user
:param username:
:return:
"""
stmt = (
select(self.model)
.options(selectinload(self.model.dept))
.options(selectinload(self.model.roles).joinedload(Role.menus))
stmt = select(self.model).options(
selectinload(self.model.dept),
selectinload(self.model.roles).joinedload(Role.menus),
selectinload(self.model.roles).joinedload(Role.depts),
)
filters = []
if user_id:
Expand Down
29 changes: 29 additions & 0 deletions backend/app/admin/model/m2m.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from sqlalchemy import INT, Column, ForeignKey, Integer, Table

from backend.common.model import MappedBase

sys_user_role = Table(
'sys_user_role',
MappedBase.metadata,
Column('id', INT, primary_key=True, unique=True, index=True, autoincrement=True, comment='主键ID'),
Column('user_id', Integer, ForeignKey('sys_user.id', ondelete='CASCADE'), primary_key=True, comment='用户ID'),
Column('role_id', Integer, ForeignKey('sys_role.id', ondelete='CASCADE'), primary_key=True, comment='角色ID'),
)

sys_role_menu = Table(
'sys_role_menu',
MappedBase.metadata,
Column('id', INT, primary_key=True, unique=True, index=True, autoincrement=True, comment='主键ID'),
Column('role_id', Integer, ForeignKey('sys_role.id', ondelete='CASCADE'), primary_key=True, comment='角色ID'),
Column('menu_id', Integer, ForeignKey('sys_menu.id', ondelete='CASCADE'), primary_key=True, comment='菜单ID'),
)

sys_role_dept = Table(
'sys_role_dept',
MappedBase.metadata,
Column('id', INT, primary_key=True, unique=True, index=True, autoincrement=True, comment='主键ID'),
Column('role_id', Integer, ForeignKey('sys_role.id', ondelete='CASCADE'), primary_key=True, comment='角色ID'),
Column('dept_id', Integer, ForeignKey('sys_dept.id', ondelete='CASCADE'), primary_key=True, comment='部门ID'),
)
5 changes: 5 additions & 0 deletions backend/app/admin/model/sys_dept.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@ class Dept(Base):
email: Mapped[str | None] = mapped_column(String(50), default=None, comment='邮箱')
status: Mapped[int] = mapped_column(default=1, comment='部门状态(0停用 1正常)')
del_flag: Mapped[bool] = mapped_column(default=False, comment='删除标志(0删除 1存在)')

# 父级部门一对多
parent_id: Mapped[int | None] = mapped_column(
ForeignKey('sys_dept.id', ondelete='SET NULL'), default=None, index=True, comment='父部门ID'
)
parent: Mapped[Union['Dept', None]] = relationship(init=False, back_populates='children', remote_side=[id])
children: Mapped[list['Dept'] | None] = relationship(init=False, back_populates='parent')

# 部门用户一对多
users: Mapped[list['User']] = relationship(init=False, back_populates='dept') # noqa: F821

# 部门角色多对多
roles: Mapped[list['Role']] = relationship(init=False, back_populates='dept') # noqa: F821
5 changes: 4 additions & 1 deletion backend/app/admin/model/sys_dict_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class DictData(Base):
sort: Mapped[int] = mapped_column(default=0, comment='排序')
status: Mapped[int] = mapped_column(default=1, comment='状态(0停用 1正常)')
remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注')

# 字典类型一对多
type_id: Mapped[int] = mapped_column(ForeignKey('sys_dict_type.id'), default=0, comment='字典类型关联ID')
type_id: Mapped[int] = mapped_column(
ForeignKey('sys_dict_type.id', ondelete='CASCADE'), default=0, comment='字典类型关联ID'
)
type: Mapped['DictType'] = relationship(init=False, back_populates='datas') # noqa: F821
1 change: 1 addition & 0 deletions backend/app/admin/model/sys_dict_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ class DictType(Base):
code: Mapped[str] = mapped_column(String(32), unique=True, comment='字典类型编码')
status: Mapped[int] = mapped_column(default=1, comment='状态(0停用 1正常)')
remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注')

# 字典类型一对多
datas: Mapped[list['DictData']] = relationship(init=False, back_populates='type') # noqa: F821
8 changes: 4 additions & 4 deletions backend/app/admin/model/sys_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sqlalchemy.dialects.mysql import LONGTEXT
from sqlalchemy.orm import Mapped, mapped_column, relationship

from backend.app.admin.model.sys_role_menu import sys_role_menu
from backend.app.admin.model.m2m import sys_role_menu
from backend.common.model import Base, id_key


Expand All @@ -29,13 +29,13 @@ class Menu(Base):
show: Mapped[int] = mapped_column(default=1, comment='是否显示(0否 1是)')
cache: Mapped[int] = mapped_column(default=1, comment='是否缓存(0否 1是)')
remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注')

# 父级菜单一对多
parent_id: Mapped[int | None] = mapped_column(
ForeignKey('sys_menu.id', ondelete='SET NULL'), default=None, index=True, comment='父菜单ID'
)
parent: Mapped[Union['Menu', None]] = relationship(init=False, back_populates='children', remote_side=[id])
children: Mapped[list['Menu'] | None] = relationship(init=False, back_populates='parent')

# 菜单角色多对多
roles: Mapped[list['Role']] = relationship( # noqa: F821
init=False, secondary=sys_role_menu, back_populates='menus'
)
roles: Mapped[list['Role']] = relationship(init=False, secondary=sys_role_menu, back_populates='menus') # noqa: F821
21 changes: 12 additions & 9 deletions backend/app/admin/model/sys_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from sqlalchemy.dialects.mysql import LONGTEXT
from sqlalchemy.orm import Mapped, mapped_column, relationship

from backend.app.admin.model.sys_role_menu import sys_role_menu
from backend.app.admin.model.sys_user_role import sys_user_role
from backend.app.admin.model.m2m import sys_role_dept, sys_role_menu, sys_user_role
from backend.common.model import Base, id_key


Expand All @@ -16,14 +15,18 @@ class Role(Base):

id: Mapped[id_key] = mapped_column(init=False)
name: Mapped[str] = mapped_column(String(20), unique=True, comment='角色名称')
data_scope: Mapped[int | None] = mapped_column(default=2, comment='权限范围(1:全部数据权限 2:自定义数据权限)')
data_scope: Mapped[int | None] = mapped_column(
default=0,
comment='权限范围(0: 全部数据,1: 自定义数据,2: 所在部门及以下数据,3: 所在部门数据,4: 仅本人数据)',
)
status: Mapped[int] = mapped_column(default=1, comment='角色状态(0停用 1正常)')
remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注')

# 角色用户多对多
users: Mapped[list['User']] = relationship( # noqa: F821
init=False, secondary=sys_user_role, back_populates='roles'
)
users: Mapped[list['User']] = relationship(init=False, secondary=sys_user_role, back_populates='roles') # noqa: F821

# 角色菜单多对多
menus: Mapped[list['Menu']] = relationship( # noqa: F821
init=False, secondary=sys_role_menu, back_populates='roles'
)
menus: Mapped[list['Menu']] = relationship(init=False, secondary=sys_role_menu, back_populates='roles') # noqa: F821

# 角色部门多对多
depts: Mapped[list['Dept']] = relationship(init=False, secondary=sys_role_dept, back_populates='roles') # noqa: F821
13 changes: 0 additions & 13 deletions backend/app/admin/model/sys_role_menu.py

This file was deleted.

13 changes: 7 additions & 6 deletions backend/app/admin/model/sys_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sqlalchemy import ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column, relationship

from backend.app.admin.model.sys_user_role import sys_user_role
from backend.app.admin.model.m2m import sys_user_role
from backend.common.model import Base, id_key
from backend.database.db_mysql import uuid4_str
from backend.utils.timezone import timezone
Expand All @@ -32,14 +32,15 @@ class User(Base):
phone: Mapped[str | None] = mapped_column(String(11), default=None, comment='手机号')
join_time: Mapped[datetime] = mapped_column(init=False, default_factory=timezone.now, comment='注册时间')
last_login_time: Mapped[datetime | None] = mapped_column(init=False, onupdate=timezone.now, comment='上次登录')

# 部门用户一对多
dept_id: Mapped[int | None] = mapped_column(
ForeignKey('sys_dept.id', ondelete='SET NULL'), default=None, comment='部门关联ID'
)
dept: Mapped[Union['Dept', None]] = relationship(init=False, back_populates='users') # noqa: F821
# 用户角色多对多
roles: Mapped[list['Role']] = relationship( # noqa: F821
init=False, secondary=sys_user_role, back_populates='users'
)
# 用户 OAuth2 一对多

# 用户社交信息一对多
socials: Mapped[list['UserSocial']] = relationship(init=False, back_populates='user') # noqa: F821

# 用户角色多对多
roles: Mapped[list['Role']] = relationship(init=False, secondary=sys_user_role, back_populates='users') # noqa: F821
13 changes: 0 additions & 13 deletions backend/app/admin/model/sys_user_role.py

This file was deleted.

3 changes: 2 additions & 1 deletion backend/app/admin/model/sys_user_social.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class UserSocial(Base):
union_id: Mapped[str | None] = mapped_column(String(20), default=None, comment='第三方用户的 union id')
scope: Mapped[str | None] = mapped_column(String(120), default=None, comment='第三方用户授予的权限')
code: Mapped[str | None] = mapped_column(String(50), default=None, comment='用户的授权 code')
# 用户 OAuth2 一对多

# 用户社交信息一对多
user_id: Mapped[int | None] = mapped_column(
ForeignKey('sys_user.id', ondelete='SET NULL'), default=None, comment='用户关联ID'
)
Expand Down
4 changes: 4 additions & 0 deletions backend/app/admin/schema/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class UpdateRoleMenuParam(SchemaBase):
menus: list[int]


class UpdateRoleDeptParam(SchemaBase):
depts: list[int]


class GetRoleListDetails(RoleSchemaBase):
model_config = ConfigDict(from_attributes=True)

Expand Down
18 changes: 17 additions & 1 deletion backend/app/admin/service/role_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from fastapi import Request
from sqlalchemy import Select

from backend.app.admin.crud.crud_dept import dept_dao
from backend.app.admin.crud.crud_menu import menu_dao
from backend.app.admin.crud.crud_role import role_dao
from backend.app.admin.model import Role
from backend.app.admin.schema.role import CreateRoleParam, UpdateRoleMenuParam, UpdateRoleParam
from backend.app.admin.schema.role import CreateRoleParam, UpdateRoleDeptParam, UpdateRoleMenuParam, UpdateRoleParam
from backend.common.exception import errors
from backend.core.conf import settings
from backend.database.db_mysql import async_db_session
Expand Down Expand Up @@ -77,6 +78,21 @@ async def update_role_menu(*, request: Request, pk: int, menu_ids: UpdateRoleMen
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{request.user.id}')
return count

@staticmethod
async def update_role_dept(*, request: Request, pk: int, dept_ids: UpdateRoleDeptParam) -> int:
async with async_db_session.begin() as db:
role = await role_dao.get(db, pk)
if not role:
raise errors.NotFoundError(msg='角色不存在')
for dept_id in dept_ids.depts:
dept = await dept_dao.get(db, dept_id)
if not dept:
raise errors.NotFoundError(msg='部门不存在')
count = await role_dao.update_depts(db, pk, dept_ids)
if pk in [role.id for role in request.user.roles]:
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{request.user.id}')
return count

@staticmethod
async def delete(*, pk: list[int]) -> int:
async with async_db_session.begin() as db:
Expand Down
2 changes: 1 addition & 1 deletion backend/app/generator/api/v1/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ async def delete_model(pk: Annotated[int, Path(...)]) -> ResponseModel:
return response_base.fail()


@router.get('/tables', summary='获取数据库表', dependencies=[DependsRBAC])
@router.get('/tables', summary='获取数据库表')
async def get_all_tables(table_schema: Annotated[str, Query(..., description='数据库名')] = 'fba') -> ResponseModel:
data = await gen_service.get_tables(table_schema=table_schema)
return response_base.success(data=data)
Expand Down
6 changes: 3 additions & 3 deletions backend/common/security/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ async def get_current_user(db: AsyncSession, pk: int) -> User:
raise AuthorizationError(msg='用户已被锁定,请联系系统管理员')
if user.dept_id:
if not user.dept.status:
raise AuthorizationError(msg='用户所属部门已锁定')
raise AuthorizationError(msg='用户所属部门已被锁定,请联系系统管理员')
if user.dept.del_flag:
raise AuthorizationError(msg='用户所属部门已删除')
raise AuthorizationError(msg='用户所属部门已被删除,请联系系统管理员')
if user.roles:
role_status = [role.status for role in user.roles]
if all(status == 0 for status in role_status):
raise AuthorizationError(msg='用户所属角色已锁定')
raise AuthorizationError(msg='用户所属角色已被锁定,请联系系统管理员')
return user


Expand Down
Loading