:::tip 菜单管理 菜单管理是 RBAC 权限系统的基础,负责管理系统的导航菜单和权限节点。支持目录/菜单(type=0)和权限按钮节点(type=1)两种类型,通过树形结构组织。权限节点由系统根据前端勾选自动生成,采用 `sys:module:action` 三段式命名。 ::: ## 菜单类型 | type | 名称 | 说明 | 用途 | |------|------|------|------| | `0` | 目录/菜单 | 页面路由 | 前端侧边栏分组 + 路由生成 | | `1` | 节点 | 权限按钮 | 控制操作权限(由系统自动生成) | ## 菜单模型 ```python # src/modules/system/menu/models.py # ============================================================ # 菜单模型 # ============================================================ class Menu(BaseModel, BaseDB): """菜单模型类""" # ============================================================ # 表名配置 # ============================================================ __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 值与权限操作的映射关系: ```python # 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) ``` :::tip 权限节点生命周期 新增/编辑菜单时,`set_permission` 函数根据前端传入的 `checkedList` 数组,复用已有节点(保持 id 稳定,role_menu 授权不失效)或新建节点。本次未勾选的历史节点自动软删。删除菜单时,其下所有 type=1 权限节点一并软删,并清理 `role_menu` 关联。 ::: ## 菜单树构建 使用 children_map 字典实现非递归树构建,避免深层递归性能问题: ```python # 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 实现,仅有效角色(未删除、未禁用)的权限生效: ```python # 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 接口 | 接口 | 方法 | 权限节点 | 说明 | |------|------|---------|------| | `/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` | 删除菜单 | :::warning 父级校验 编辑菜单时会校验父级:不能设为自身、不能设为自己的下级(沿父链向上走检测环路),避免菜单树成环导致渲染死循环。 ::: ## 总结 菜单管理模块具备以下特点: ``` 1. 树形结构:通过 parent_id 形成多级菜单树,非递归构建 2. 权限节点:type=1 按钮节点由 set_permission 自动生成和维护 3. 权限格式:sys:module:action 三段式,sort 值映射操作类型 4. 缓存联动:菜单增删改后通过 invalidate_menu_change 清理全量权限缓存 5. 环路防护:编辑时检测父级不能是自身或下级 6. 级联删除:删除菜单时一并软删其下权限节点和 role_menu 关联 ```