764 lines
26 KiB
Python
764 lines
26 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Role Service - 角色服务层
|
|
"""
|
|
from typing import Tuple, Dict, Any, Optional, List
|
|
|
|
from sqlalchemy import select, func, or_, and_
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
from app.base_service import BaseService
|
|
from core.role.model import Role
|
|
from core.role.schema import RoleCreate, RoleUpdate
|
|
|
|
|
|
def _filter_ai_agent_admin_role_menus(menus: List[Any]) -> List[Any]:
|
|
from core.menu.service import (
|
|
AI_AGENT_ADMIN_MENU_NAMES,
|
|
_normalize_ai_agent_admin_menu,
|
|
)
|
|
|
|
filtered = [menu for menu in menus if menu.name in AI_AGENT_ADMIN_MENU_NAMES]
|
|
for menu in filtered:
|
|
_normalize_ai_agent_admin_menu(menu)
|
|
return filtered
|
|
|
|
|
|
class RoleService(BaseService[Role, RoleCreate, RoleUpdate]):
|
|
"""
|
|
角色服务层
|
|
继承BaseService,自动获得增删改查功能
|
|
"""
|
|
|
|
model = Role
|
|
|
|
@classmethod
|
|
async def create(cls, db: AsyncSession, data: RoleCreate) -> Role:
|
|
"""创建角色,处理多对多关系"""
|
|
role_data = data.model_dump(exclude={'menu_ids', 'permission_ids', 'dept_ids'})
|
|
|
|
role = Role(**role_data)
|
|
db.add(role)
|
|
await db.flush()
|
|
|
|
# 设置多对多关系
|
|
if data.menu_ids:
|
|
from core.menu.model import Menu
|
|
result = await db.execute(select(Menu).where(Menu.id.in_(data.menu_ids)))
|
|
menus = list(result.scalars().all())
|
|
role.menus = menus
|
|
|
|
if data.permission_ids:
|
|
from core.permission.model import Permission
|
|
result = await db.execute(select(Permission).where(Permission.id.in_(data.permission_ids)))
|
|
permissions = list(result.scalars().all())
|
|
role.permissions = permissions
|
|
|
|
if data.dept_ids:
|
|
from core.dept.model import Dept
|
|
result = await db.execute(select(Dept).where(Dept.id.in_(data.dept_ids)))
|
|
depts = list(result.scalars().all())
|
|
role.depts = depts
|
|
|
|
await db.commit()
|
|
await db.refresh(role)
|
|
return role
|
|
|
|
@classmethod
|
|
async def update(
|
|
cls,
|
|
db: AsyncSession,
|
|
record_id: str,
|
|
data: RoleUpdate
|
|
) -> Optional[Role]:
|
|
"""更新角色,处理多对多关系"""
|
|
role = await cls.get_by_id(db, record_id)
|
|
if not role:
|
|
return None
|
|
|
|
update_data = data.model_dump(exclude_unset=True, exclude={'menu_ids', 'permission_ids', 'dept_ids'})
|
|
|
|
for field, value in update_data.items():
|
|
setattr(role, field, value)
|
|
|
|
# 更新多对多关系
|
|
menu_or_permission_updated = False
|
|
|
|
if data.menu_ids is not None:
|
|
from core.menu.model import Menu
|
|
result = await db.execute(select(Menu).where(Menu.id.in_(data.menu_ids)))
|
|
menus = list(result.scalars().all())
|
|
role.menus = menus
|
|
menu_or_permission_updated = True
|
|
|
|
if data.permission_ids is not None:
|
|
from core.permission.model import Permission
|
|
result = await db.execute(select(Permission).where(Permission.id.in_(data.permission_ids)))
|
|
permissions = list(result.scalars().all())
|
|
role.permissions = permissions
|
|
menu_or_permission_updated = True
|
|
|
|
if data.dept_ids is not None:
|
|
from core.dept.model import Dept
|
|
result = await db.execute(select(Dept).where(Dept.id.in_(data.dept_ids)))
|
|
depts = list(result.scalars().all())
|
|
role.depts = depts
|
|
|
|
await db.commit()
|
|
await db.refresh(role)
|
|
|
|
# 如果更新了菜单或权限,清除缓存
|
|
if menu_or_permission_updated:
|
|
await cls._invalidate_role_related_cache(record_id)
|
|
|
|
return role
|
|
|
|
@classmethod
|
|
async def _invalidate_role_related_cache(cls, role_id: str):
|
|
"""
|
|
清除角色相关的所有缓存,使权限变更立即生效
|
|
|
|
1. 清除菜单路由缓存
|
|
2. 清除 API 权限内存缓存(该角色)
|
|
3. 清除所有用户的 Redis 用户信息缓存(因为不知道哪些用户拥有该角色)
|
|
"""
|
|
# 1. 清除菜单路由缓存
|
|
from core.menu.service import MenuService
|
|
await MenuService.invalidate_cache()
|
|
|
|
# 2. 清除该角色的 API 权限 Redis 缓存
|
|
from utils.permission import clear_role_permission_cache
|
|
await clear_role_permission_cache(role_id)
|
|
|
|
# 3. 清除所有用户的 Redis 用户信息缓存
|
|
from utils.user_info_cache import delete_all_cached_user_info
|
|
await delete_all_cached_user_info()
|
|
|
|
@classmethod
|
|
async def get_by_id_with_relations(cls, db: AsyncSession, record_id: str) -> Optional[Role]:
|
|
"""获取角色详情(包含关联数据)"""
|
|
result = await db.execute(
|
|
select(Role)
|
|
.options(
|
|
selectinload(Role.menus),
|
|
selectinload(Role.permissions),
|
|
selectinload(Role.depts)
|
|
)
|
|
.where(Role.id == record_id, Role.is_deleted == False) # noqa: E712
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
@classmethod
|
|
async def get_user_count(cls, db: AsyncSession, role_id: str) -> int:
|
|
"""获取角色下的用户数量"""
|
|
from core.user.model import User
|
|
result = await db.execute(
|
|
select(func.count(User.id)).where(
|
|
User.role_id == role_id,
|
|
User.is_deleted == False # noqa: E712
|
|
)
|
|
)
|
|
return result.scalar() or 0
|
|
|
|
@classmethod
|
|
async def can_delete(cls, db: AsyncSession, role_id: str) -> Tuple[bool, str]:
|
|
"""检查角色是否可以删除"""
|
|
role = await cls.get_by_id(db, role_id)
|
|
if not role:
|
|
return False, "角色不存在"
|
|
|
|
if role.is_system_role():
|
|
return False, "系统角色不能删除"
|
|
|
|
user_count = await cls.get_user_count(db, role_id)
|
|
if user_count > 0:
|
|
return False, f"该角色下还有 {user_count} 个用户,无法删除"
|
|
|
|
return True, ""
|
|
|
|
@classmethod
|
|
async def batch_update_status(
|
|
cls,
|
|
db: AsyncSession,
|
|
ids: List[str],
|
|
status: bool
|
|
) -> int:
|
|
"""批量更新角色状态(系统角色不能禁用)"""
|
|
count = 0
|
|
for role_id in ids:
|
|
role = await cls.get_by_id(db, role_id)
|
|
if role and role.role_type == 1: # 只更新自定义角色
|
|
role.status = status
|
|
count += 1
|
|
|
|
if count > 0:
|
|
await db.commit()
|
|
|
|
return count
|
|
|
|
@classmethod
|
|
async def batch_delete(
|
|
cls,
|
|
db: AsyncSession,
|
|
ids: List[str],
|
|
hard: bool = False
|
|
) -> Tuple[int, List[str]]:
|
|
"""批量删除角色"""
|
|
success_count = 0
|
|
failed_ids = []
|
|
|
|
for role_id in ids:
|
|
can_del, reason = await cls.can_delete(db, role_id)
|
|
if can_del:
|
|
if await cls.delete(db, role_id, hard=hard):
|
|
success_count += 1
|
|
else:
|
|
failed_ids.append(role_id)
|
|
else:
|
|
failed_ids.append(role_id)
|
|
|
|
return success_count, failed_ids
|
|
|
|
@classmethod
|
|
async def search(
|
|
cls,
|
|
db: AsyncSession,
|
|
keyword: str,
|
|
page: int = 1,
|
|
page_size: int = 20
|
|
) -> Tuple[List[Role], int]:
|
|
"""搜索角色"""
|
|
if not keyword:
|
|
return [], 0
|
|
|
|
search_filter = or_(
|
|
Role.name.ilike(f"%{keyword}%"),
|
|
Role.code.ilike(f"%{keyword}%"),
|
|
Role.description.ilike(f"%{keyword}%")
|
|
)
|
|
|
|
count_result = await db.execute(
|
|
select(func.count(Role.id)).where(
|
|
search_filter,
|
|
Role.is_deleted == False # noqa: E712
|
|
)
|
|
)
|
|
total = count_result.scalar() or 0
|
|
|
|
result = await db.execute(
|
|
select(Role).where(
|
|
search_filter,
|
|
Role.is_deleted == False # noqa: E712
|
|
)
|
|
.order_by(Role.priority.desc(), Role.sys_update_datetime.desc())
|
|
.offset((page - 1) * page_size)
|
|
.limit(page_size)
|
|
)
|
|
items = list(result.scalars().all())
|
|
|
|
return items, total
|
|
|
|
@classmethod
|
|
async def get_all_active(cls, db: AsyncSession) -> List[Role]:
|
|
"""获取所有启用的角色"""
|
|
result = await db.execute(
|
|
select(Role).where(
|
|
Role.status == True, # noqa: E712
|
|
Role.is_deleted == False # noqa: E712
|
|
).order_by(Role.priority.desc(), Role.name)
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
@classmethod
|
|
async def get_by_ids(cls, db: AsyncSession, ids: List[str]) -> List[Role]:
|
|
"""根据ID列表批量获取角色"""
|
|
if not ids:
|
|
return []
|
|
|
|
result = await db.execute(
|
|
select(Role).where(
|
|
Role.id.in_(ids),
|
|
Role.is_deleted == False # noqa: E712
|
|
)
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
@classmethod
|
|
async def get_role_users(
|
|
cls,
|
|
db: AsyncSession,
|
|
role_id: str,
|
|
page: int = 1,
|
|
page_size: int = 20,
|
|
username: Optional[str] = None,
|
|
) -> Tuple[List[Dict[str, Any]], int]:
|
|
"""获取角色下的用户列表(SQL 分页 + JOIN 部门)"""
|
|
from core.dept.model import Dept
|
|
from core.user.model import User
|
|
from core.user.user_role_model import UserRole
|
|
|
|
# 基础条件
|
|
base_conditions = [
|
|
UserRole.role_id == role_id,
|
|
UserRole.is_deleted == False, # noqa: E712
|
|
User.user_status == 1,
|
|
User.is_deleted == False, # noqa: E712
|
|
]
|
|
if username:
|
|
base_conditions.append(
|
|
or_(
|
|
User.username.ilike(f"%{username}%"),
|
|
User.name.ilike(f"%{username}%"),
|
|
)
|
|
)
|
|
|
|
# count
|
|
count_stmt = (
|
|
select(func.count(User.id))
|
|
.join(UserRole, UserRole.user_id == User.id)
|
|
.where(*base_conditions)
|
|
)
|
|
total = (await db.execute(count_stmt)).scalar() or 0
|
|
|
|
# 分页查询,LEFT JOIN Dept 一次拿到 dept_name
|
|
query_stmt = (
|
|
select(
|
|
User.id,
|
|
User.username,
|
|
User.name,
|
|
User.avatar,
|
|
User.email,
|
|
User.mobile,
|
|
Dept.name.label("dept_name"),
|
|
)
|
|
.join(UserRole, UserRole.user_id == User.id)
|
|
.outerjoin(Dept, and_(Dept.id == User.dept_id, Dept.is_deleted == False)) # noqa: E712
|
|
.where(*base_conditions)
|
|
.order_by(User.sys_create_datetime.desc())
|
|
.offset((page - 1) * page_size)
|
|
.limit(page_size)
|
|
)
|
|
rows = (await db.execute(query_stmt)).all()
|
|
|
|
items = [
|
|
{
|
|
"id": row.id,
|
|
"username": row.username,
|
|
"name": row.name,
|
|
"avatar": row.avatar,
|
|
"email": row.email,
|
|
"mobile": row.mobile,
|
|
"dept_name": row.dept_name,
|
|
}
|
|
for row in rows
|
|
]
|
|
return items, total
|
|
|
|
@classmethod
|
|
async def add_users_to_role(
|
|
cls,
|
|
db: AsyncSession,
|
|
role_id: str,
|
|
user_ids: List[str]
|
|
) -> int:
|
|
"""将用户添加到角色(通过多对多关联表)"""
|
|
from core.user.user_role_model import UserRole
|
|
|
|
added_count = 0
|
|
for user_id in user_ids:
|
|
# 检查是否已存在关联(包括软删除的)
|
|
existing = await db.execute(
|
|
select(UserRole).where(
|
|
UserRole.user_id == user_id,
|
|
UserRole.role_id == role_id,
|
|
)
|
|
)
|
|
record = existing.scalar_one_or_none()
|
|
if record:
|
|
# 如果是软删除的,恢复
|
|
if record.is_deleted:
|
|
record.is_deleted = False
|
|
added_count += 1
|
|
else:
|
|
db.add(UserRole(user_id=user_id, role_id=role_id))
|
|
added_count += 1
|
|
|
|
if added_count > 0:
|
|
await db.commit()
|
|
|
|
return added_count
|
|
|
|
@classmethod
|
|
async def remove_users_from_role(
|
|
cls,
|
|
db: AsyncSession,
|
|
role_id: str,
|
|
user_ids: List[str]
|
|
) -> int:
|
|
"""从角色中移除用户(通过多对多关联表)"""
|
|
from core.user.user_role_model import UserRole
|
|
|
|
removed_count = 0
|
|
for user_id in user_ids:
|
|
result = await db.execute(
|
|
select(UserRole).where(
|
|
UserRole.user_id == user_id,
|
|
UserRole.role_id == role_id,
|
|
UserRole.is_deleted == False, # noqa: E712
|
|
)
|
|
)
|
|
record = result.scalar_one_or_none()
|
|
if record:
|
|
record.is_deleted = True
|
|
removed_count += 1
|
|
|
|
if removed_count > 0:
|
|
await db.commit()
|
|
|
|
return removed_count
|
|
|
|
@classmethod
|
|
async def update_menus_permissions(
|
|
cls,
|
|
db: AsyncSession,
|
|
role_id: str,
|
|
menu_ids: List[str],
|
|
permission_ids: List[str],
|
|
loaded_menu_ids: Optional[List[str]] = None
|
|
) -> bool:
|
|
"""更新角色的菜单和权限
|
|
|
|
增量更新策略:
|
|
- 菜单:全量替换(前端始终加载完整菜单树)
|
|
- 权限:只替换 loaded_menu_ids 对应菜单下的权限,保留未加载菜单的原有权限
|
|
如果 loaded_menu_ids 为空列表或 None,则全量替换权限(兼容旧逻辑)
|
|
"""
|
|
role = await cls.get_by_id_with_relations(db, role_id)
|
|
if not role:
|
|
return False
|
|
|
|
# 菜单始终全量替换
|
|
from core.menu.model import Menu
|
|
result = await db.execute(select(Menu).where(Menu.id.in_(menu_ids)))
|
|
menus = _filter_ai_agent_admin_role_menus(list(result.scalars().all()))
|
|
allowed_menu_ids = {menu.id for menu in menus}
|
|
role.menus = menus
|
|
|
|
from core.permission.model import Permission
|
|
|
|
if loaded_menu_ids:
|
|
# 增量更新:只替换已加载菜单下的权限
|
|
loaded_menu_set = set(loaded_menu_ids) & allowed_menu_ids
|
|
|
|
# 保留未加载菜单下的原有权限
|
|
kept_permissions = [
|
|
p for p in (role.permissions or [])
|
|
if p.menu_id in allowed_menu_ids and p.menu_id not in loaded_menu_set
|
|
]
|
|
|
|
# 获取前端提交的权限(已加载菜单下用户选择的权限)
|
|
new_permissions = []
|
|
if permission_ids:
|
|
result = await db.execute(
|
|
select(Permission).where(
|
|
Permission.id.in_(permission_ids),
|
|
Permission.menu_id.in_(allowed_menu_ids),
|
|
)
|
|
)
|
|
new_permissions = list(result.scalars().all())
|
|
|
|
# 合并:未加载菜单的旧权限 + 已加载菜单的新权限
|
|
role.permissions = kept_permissions + new_permissions
|
|
else:
|
|
# 兼容旧逻辑:全量替换
|
|
if permission_ids:
|
|
result = await db.execute(
|
|
select(Permission).where(
|
|
Permission.id.in_(permission_ids),
|
|
Permission.menu_id.in_(allowed_menu_ids),
|
|
)
|
|
)
|
|
permissions = list(result.scalars().all())
|
|
else:
|
|
permissions = []
|
|
role.permissions = permissions
|
|
|
|
await db.commit()
|
|
|
|
# 清除缓存,确保权限变更立即生效
|
|
await cls._invalidate_role_related_cache(role_id)
|
|
|
|
return True
|
|
|
|
@classmethod
|
|
async def copy_role(
|
|
cls,
|
|
db: AsyncSession,
|
|
source_role_id: str,
|
|
new_name: str,
|
|
new_code: str,
|
|
creator_id: Optional[str] = None
|
|
) -> Optional[Role]:
|
|
"""复制角色"""
|
|
source_role = await cls.get_by_id_with_relations(db, source_role_id)
|
|
if not source_role:
|
|
return None
|
|
|
|
# 创建新角色
|
|
new_role = Role(
|
|
name=new_name,
|
|
code=new_code,
|
|
role_type=1, # 复制的角色都是自定义角色
|
|
status=source_role.status,
|
|
data_scope=source_role.data_scope,
|
|
priority=source_role.priority,
|
|
description=source_role.description,
|
|
remark=f"复制自角色: {source_role.name}",
|
|
sys_creator_id=creator_id
|
|
)
|
|
db.add(new_role)
|
|
await db.flush()
|
|
|
|
# 复制关联关系
|
|
new_role.menus = source_role.menus
|
|
new_role.permissions = source_role.permissions
|
|
new_role.depts = source_role.depts
|
|
|
|
await db.commit()
|
|
await db.refresh(new_role)
|
|
return new_role
|
|
|
|
@classmethod
|
|
async def update_permissions(
|
|
cls,
|
|
db: AsyncSession,
|
|
role_id: str,
|
|
permission_ids: List[str]
|
|
) -> bool:
|
|
"""更新角色的权限"""
|
|
role = await cls.get_by_id_with_relations(db, role_id)
|
|
if not role:
|
|
return False
|
|
|
|
from core.permission.model import Permission
|
|
result = await db.execute(select(Permission).where(Permission.id.in_(permission_ids)))
|
|
permissions = list(result.scalars().all())
|
|
role.permissions = permissions
|
|
|
|
await db.commit()
|
|
|
|
# 清除缓存,确保权限变更立即生效
|
|
await cls._invalidate_role_related_cache(role_id)
|
|
|
|
return True
|
|
|
|
@classmethod
|
|
async def get_menu_permission_tree(cls, db: AsyncSession, role: Role) -> Dict[str, Any]:
|
|
"""获取角色的菜单权限树"""
|
|
from core.menu.model import Menu
|
|
from core.permission.model import Permission
|
|
|
|
# 获取所有菜单
|
|
result = await db.execute(select(Menu).where(Menu.is_deleted == False)) # noqa: E712
|
|
all_menus = _filter_ai_agent_admin_role_menus(list(result.scalars().all()))
|
|
allowed_menu_ids = {menu.id for menu in all_menus}
|
|
|
|
# 获取该角色已分配的权限ID和菜单ID
|
|
role_permission_ids = set(p.id for p in role.permissions) if role.permissions else set()
|
|
role_menu_ids = {m.id for m in (role.menus or []) if m.id in allowed_menu_ids}
|
|
|
|
# 获取所有启用的权限
|
|
result = await db.execute(
|
|
select(Permission).where(Permission.is_active == True, Permission.is_deleted == False) # noqa: E712
|
|
)
|
|
all_permissions = [
|
|
perm for perm in list(result.scalars().all()) if perm.menu_id in allowed_menu_ids
|
|
]
|
|
|
|
# 权限类型映射
|
|
PERMISSION_TYPE_MAP = {
|
|
0: '按钮权限',
|
|
1: 'API权限',
|
|
2: '数据权限',
|
|
3: '其他权限',
|
|
}
|
|
|
|
# 按菜单分组权限
|
|
permissions_by_menu = {}
|
|
for perm in all_permissions:
|
|
menu_id = perm.menu_id
|
|
if menu_id not in permissions_by_menu:
|
|
permissions_by_menu[menu_id] = []
|
|
|
|
permission_type = perm.permission_type if perm.permission_type is not None else 3
|
|
permission_type_display = PERMISSION_TYPE_MAP.get(permission_type, '其他权限')
|
|
|
|
permissions_by_menu[menu_id].append({
|
|
'id': perm.id,
|
|
'label': perm.name,
|
|
'name': perm.name,
|
|
'code': perm.code,
|
|
'permission_type': permission_type,
|
|
'permission_type_display': permission_type_display,
|
|
'checked': perm.id in role_permission_ids,
|
|
})
|
|
|
|
# 构建菜单树
|
|
menu_map = {}
|
|
root_menus = []
|
|
|
|
for menu in all_menus:
|
|
menu_node = {
|
|
'id': menu.id,
|
|
'label': menu.title or menu.name,
|
|
'name': menu.name,
|
|
'parent_id': menu.parent_id,
|
|
'checked': menu.id in role_menu_ids,
|
|
'children': [],
|
|
}
|
|
menu_map[menu.id] = menu_node
|
|
|
|
# 建立父子关系
|
|
for menu in all_menus:
|
|
if menu.parent_id and menu.parent_id in menu_map:
|
|
menu_map[menu.parent_id]['children'].append(menu_map[menu.id])
|
|
else:
|
|
root_menus.append(menu_map[menu.id])
|
|
|
|
# 为叶子菜单添加权限
|
|
for menu_id, menu_node in menu_map.items():
|
|
if not menu_node['children']:
|
|
menu_node['children'] = permissions_by_menu.get(menu_id, [])
|
|
|
|
return {
|
|
'menu_tree': root_menus,
|
|
'permission_tree': [],
|
|
'selected_menu_ids': list(role_menu_ids),
|
|
'selected_permission_ids': list(role_permission_ids),
|
|
}
|
|
|
|
@classmethod
|
|
async def get_role_menus(cls, db: AsyncSession, role: Role, application_id: Optional[str] = None) -> Dict[str, Any]:
|
|
"""获取角色的菜单列表
|
|
|
|
Args:
|
|
db: 数据库会话
|
|
role: 角色对象
|
|
application_id: 应用ID,如果指定则只返回该应用的菜单
|
|
"""
|
|
from core.menu.model import Menu
|
|
from core.permission.model import Permission
|
|
|
|
# 获取该角色已选中的菜单ID
|
|
role_menu_ids = set(m.id for m in role.menus) if role.menus else set()
|
|
|
|
# 获取菜单,支持按应用过滤
|
|
query = select(Menu).where(Menu.is_deleted == False) # noqa: E712
|
|
if application_id:
|
|
query = query.where(Menu.application_id == application_id)
|
|
result = await db.execute(query)
|
|
all_menus = _filter_ai_agent_admin_role_menus(list(result.scalars().all()))
|
|
allowed_menu_ids = {menu.id for menu in all_menus}
|
|
role_menu_ids = {menu_id for menu_id in role_menu_ids if menu_id in allowed_menu_ids}
|
|
|
|
# 统计每个菜单的权限数量
|
|
permission_counts = {}
|
|
for menu in all_menus:
|
|
count_result = await db.execute(
|
|
select(func.count(Permission.id)).where(
|
|
Permission.menu_id == menu.id,
|
|
Permission.is_active == True, # noqa: E712
|
|
Permission.is_deleted == False # noqa: E712
|
|
)
|
|
)
|
|
permission_counts[menu.id] = count_result.scalar() or 0
|
|
|
|
# 构建菜单树
|
|
menu_map = {}
|
|
root_menus = []
|
|
|
|
for menu in all_menus:
|
|
menu_node = {
|
|
'id': menu.id,
|
|
'label': menu.title or menu.name,
|
|
'name': menu.name,
|
|
'parent_id': menu.parent_id,
|
|
'application_id': menu.application_id,
|
|
'checked': menu.id in role_menu_ids,
|
|
'permission_count': permission_counts.get(menu.id, 0),
|
|
'children': [],
|
|
}
|
|
menu_map[menu.id] = menu_node
|
|
|
|
if not menu.parent_id:
|
|
root_menus.append(menu_node)
|
|
|
|
# 建立父子关系
|
|
for menu_id, menu_node in menu_map.items():
|
|
parent_id = menu_node['parent_id']
|
|
if parent_id and parent_id in menu_map:
|
|
menu_map[parent_id]['children'].append(menu_node)
|
|
|
|
return {
|
|
'menu_tree': root_menus,
|
|
'selected_menu_ids': list(role_menu_ids),
|
|
}
|
|
|
|
@classmethod
|
|
async def get_menu_permissions(cls, db: AsyncSession, role: Role, menu_id: str) -> Dict[str, Any]:
|
|
"""获取指定菜单的权限列表"""
|
|
from core.menu.model import Menu
|
|
from core.permission.model import Permission
|
|
|
|
# 获取该角色已选中的权限ID
|
|
role_permission_ids = set(p.id for p in role.permissions) if role.permissions else set()
|
|
menu_result = await db.execute(
|
|
select(Menu).where(Menu.id == menu_id, Menu.is_deleted == False) # noqa: E712
|
|
)
|
|
menu = menu_result.scalar_one_or_none()
|
|
if not menu or not _filter_ai_agent_admin_role_menus([menu]):
|
|
return {
|
|
'menu_id': menu_id,
|
|
'permissions': [],
|
|
}
|
|
|
|
# 权限类型映射
|
|
PERMISSION_TYPE_MAP = {
|
|
0: '按钮权限',
|
|
1: 'API权限',
|
|
2: '数据权限',
|
|
3: '其他权限',
|
|
}
|
|
|
|
# 获取该菜单的所有权限
|
|
result = await db.execute(
|
|
select(Permission).where(
|
|
Permission.menu_id == menu_id,
|
|
Permission.is_active == True, # noqa: E712
|
|
Permission.is_deleted == False # noqa: E712
|
|
)
|
|
)
|
|
permissions = list(result.scalars().all())
|
|
|
|
permission_list = []
|
|
for perm in permissions:
|
|
permission_type = perm.permission_type if perm.permission_type is not None else 3
|
|
permission_type_display = PERMISSION_TYPE_MAP.get(permission_type, '其他权限')
|
|
|
|
permission_list.append({
|
|
'id': perm.id,
|
|
'label': perm.name,
|
|
'name': perm.name,
|
|
'code': perm.code,
|
|
'permission_type': permission_type,
|
|
'permission_type_display': permission_type_display,
|
|
'checked': perm.id in role_permission_ids,
|
|
})
|
|
|
|
return {
|
|
'menu_id': menu_id,
|
|
'permissions': permission_list,
|
|
}
|