Become a sponsor

菜单管理
菜单管理是 RBAC 权限系统的基础,负责管理系统的导航菜单和权限节点。支持目录/菜单(type=0)和权限按钮节点(type=1)两种类型,通过树形结构组织。权限节点由系统根据前端勾选自动生成,采用 sys:module:action 三段式命名。
| type | 名称 | 说明 | 用途 |
|---|---|---|---|
0 | 目录/菜单 | 页面路由 | 前端侧边栏分组 + 路由生成 |
1 | 节点 | 权限按钮 | 控制操作权限(由系统自动生成) |
# src/modules/system/menu/models.py
# ============================================================
# 菜单模型
# ============================================================
class Menu(base_model, base_db):
"""菜单模型类"""
# ============================================================
# 表名配置
# ============================================================
__tablename__ = DB_PREFIX + "menu"
__table_comment__ = "菜单表"
# ============================================================
# 字段定义
# ============================================================
# 菜单名称
name = Column(String(150), nullable=False, index=True, comment="菜单名称")
# 菜单图标
icon = Column(String(50), nullable=True, comment="菜单图标")
# 菜单图标(备用)
icon2 = Column(String(50), nullable=True, comment="菜单图标")
# 上级ID
parent_id = Column(Integer, default=0, server_default=text('0'), index=True, comment="上级ID")
# 菜单路径
path = Column(String(255), nullable=True, comment="菜单路径")
# 菜单组件
component = Column(String(255), nullable=True, comment="菜单组件")
# 权限节点
permission = Column(String(150), nullable=True, comment="权限节点")
# 打开方式:0-组件 1-内链 2-外链
target = Column(Integer, default=1, server_default=text('1'), comment="打开方式:0-组件 1-内链 2-外链")
# 菜单类型:0-菜单 1-节点
type = Column(Integer, default=0, server_default=text('0'), index=True, comment="菜单类型:0-菜单 1-节点")
# 状态选项
status = Column(Integer, default=0, server_default=text('0'), index=True, comment="菜单状态:0-正常 1-停用")
# 是否可见:0-可见 1-不可见
hide = Column(Integer, default=1, server_default=text('1'), comment="是否可见:0-可见 1-不可见")
# 排序
sort = Column(Integer, default=0, server_default=text('0'), comment="排序")
# 备注
note = Column(String(255), nullable=True, comment="备注")
# ============================================================
# 内置方法
# ============================================================
def __str__(self):
"""返回菜单ID作为字符串表示"""
return "菜单{}".format(self.id)当菜单类型为目录(type=0)且勾选了权限按钮时,系统自动在该菜单下生成 type=1 的权限节点。按钮 sort 值与权限操作的映射关系:
# src/modules/system/menu/service.py
_BUTTON_SORT_VALUES = (1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65)
# sort 值与权限操作的映射
1 → 查询(sys:{module}:page)
5 → 添加(sys:{module}:add)
10 → 修改(sys:{module}:update)
15 → 删除(sys:{module}:delete)
20 → 详情(sys:{module}:detail)
25 → 设置状态(sys:{module}:status)
30 → 批量删除(sys:{module}:batchDelete)
50 → 导出(sys:{module}:export)
55 → 导入(sys:{module}:import)
60 → 分配权限(sys:{module}:permission)
65 → 重置密码(sys:{module}:resetPwd)权限节点生命周期
新增/编辑菜单时,set_permission 函数根据前端传入的 checkedList 数组,复用已有节点(保持 id 稳定,role_menu 授权不失效)或新建节点。本次未勾选的历史节点自动软删。删除菜单时,其下所有 type=1 权限节点一并软删,并清理 role_menu 关联。
使用 children_map 字典实现非递归树构建,避免深层递归性能问题:
# src/modules/system/menu/service.py
def get_tree(data, parent_id):
"""获取树状结构(非递归,children_map 分组)"""
children_map = {}
for item in data:
pid = item["parentId"]
if pid not in children_map:
children_map[pid] = []
children_map[pid].append(item)
def build(pid):
result = []
for item in children_map.get(pid, []):
item["children"] = build(item["id"])
result.append(item)
return result
return build(parent_id)权限查询通过 ORM join 实现,仅有效角色(未删除、未禁用)的权限生效:
# src/modules/system/menu/service.py
def get_permissions_list_sync(user_id):
"""同步查询用户权限节点"""
if user_id == 1:
menu_list = menu_repo.get_all(type=1) # 超管拥有全部权限
else:
menu_list = (
menu_repo.filter()
.join(RoleMenu, RoleMenu.menu_id == Menu.id)
.join(UserRole, UserRole.role_id == RoleMenu.role_id)
.join(Role, Role.id == UserRole.role_id)
.filter(
and_(
UserRole.user_id == user_id,
or_(Menu.type == 1, and_(Menu.type == 0, Menu.permission != "")),
Menu.status == 0,
Menu.is_delete == 0,
Role.is_delete == 0,
)
)
.distinct()
.all()
)
return [item.permission for item in (menu_list or [])]| 接口 | 方法 | 权限节点 | 说明 |
|---|---|---|---|
/api/v1/menu/list | GET | sys:menu:list | 菜单列表 |
/api/v1/menu/detail/{id} | GET | sys:menu:detail | 菜单详情(含权限节点 checkedList) |
/api/v1/menu/add | POST | sys:menu:add | 新增菜单 |
/api/v1/menu/update | PUT | sys:menu:update | 编辑菜单 |
/api/v1/menu/delete/{id} | DELETE | sys:menu:delete | 删除菜单 |
父级校验
编辑菜单时会校验父级:不能设为自身、不能设为自己的下级(沿父链向上走检测环路),避免菜单树成环导致渲染死循环。
菜单管理模块具备以下特点:
1. 树形结构:通过 parent_id 形成多级菜单树,非递归构建
2. 权限节点:type=1 按钮节点由 set_permission 自动生成和维护
3. 权限格式:sys:module:action 三段式,sort 值映射操作类型
4. 缓存联动:菜单增删改后通过 invalidate_menu_change 清理全量权限缓存
5. 环路防护:编辑时检测父级不能是自身或下级
6. 级联删除:删除菜单时一并软删其下权限节点和 role_menu 关联