# -*- coding: utf-8 -*- # @Author : YY from typing import List, Literal, Optional, Set from ruoyi_common.constant import Constants, UserConstants from ruoyi_common.domain.entity import SysRole, TreeSelect from ruoyi_system.domain.vo import RouterMetaVo, RouterVo from ruoyi_system.mapper import SysMenuMapper from ruoyi_common.utils import security_util as SecurityUtil, StringUtil from ruoyi_common.domain.entity import SysMenu from ruoyi_system.mapper.sys_role import SysRoleMapper from ruoyi_system.mapper.sys_role_menu import SysRoleMenuMapper class SysMenuService: @classmethod def select_menu_list_by_user_id(cls, user_id) -> List[SysMenu]: ''' 根据用户,查询菜单列表 Args: user_id(int): 用户ID Returns: List[SysMenu]: 菜单列表 ''' return cls.select_menu_list(SysMenu(), user_id) @classmethod def select_menu_list(cls, menu:SysMenu, user_id:int) -> List[SysMenu]: ''' 查询菜单列表 Args: menu(SysMenu): 菜单实体 user_id(int): 用户ID Returns: List[SysMenu]: 菜单列表 ''' if SecurityUtil.is_admin(user_id): eos: List[SysMenu] = SysMenuMapper.select_menu_list(menu) else: eos: List[SysMenu] = SysMenuMapper \ .select_menu_list_by_user_id(menu, user_id) return eos @classmethod def select_menu_perms_by_user_id(cls, user_id:int) -> List[str]: ''' 根据用户ID,查询菜单权限列表 Args: user_id(int): 用户ID Returns: List[str]: 菜单权限列表 ''' perms: List[str] = SysMenuMapper.select_menu_perms_by_user_id(user_id) perm_set = set() for perm in perms: if perm: perm_set |= set(perm.strip().split(',')) return list(perm_set) @classmethod def select_menu_perms_by_role_id(cls, role_id:int) -> Set[str]: ''' 根据角色ID,查询菜单权限列表 Args: role_id(int): 角色ID Returns: Set[str]: 菜单权限集合 ''' perms: List[str] = SysMenuMapper.select_menu_perms_by_role_id(role_id) perm_set = set() for perm in perms: if perm: perm_set |= set(perm.strip().split(',')) return perm_set @classmethod def select_menu_tree_by_user_id(cls, user_id:int) -> List[SysMenu]: ''' 根据角色ID,查询菜单树 Args: user_id(int): 用户ID Returns: List[SysMenu]: 菜单树 ''' if SecurityUtil.is_admin(user_id): eos: List[SysMenu] = SysMenuMapper.select_menu_tree_all() else: eos: List[SysMenu] = SysMenuMapper.select_menu_tree_by_user_id(user_id) return cls.get_child_perms(eos, 0) @classmethod def select_menu_list_by_role_id(cls, role_id:int): ''' 根据角色ID,查询菜单列表 Args: role_id(int): 角色ID Returns: List[SysMenu]: 菜单列表 ''' eo:SysRole = SysRoleMapper.select_role_by_id(role_id) return SysMenuMapper.select_menu_list_by_role_id(role_id,eo.menu_check_strictly) @classmethod def build_menus(cls, menus:List[SysMenu]): ''' 构建菜单 Args: menus(List[SysMenu]): 菜单列表 Returns: List[RouterVo]: 路由列表 ''' routers:List[RouterVo] = [] for menu in menus: rv:RouterVo = RouterVo() rv.hidden = False if menu.visible == "0" else True rv.name = cls.get_route_name(menu) rv.path = cls.get_router_path(menu) rv.component = cls.get_component(menu) rv.query = menu.query rv.meta = RouterMetaVo( title=menu.menu_name, icon=menu.icon, no_cache=True if menu.is_cache == "1" else False, link=menu.path) menu_children = menu.children if menu_children and UserConstants.TYPE_DIR == menu.menu_type: rv.always_show = True rv.redirect = 'noRedirect' rv.children = cls.build_menus(menu_children) elif cls.is_menu_frame(menu): rv.meta = None rv_child = RouterVo() rv_child.path = menu.path rv_child.component = menu.component rv_child.name = cls.path_to_component_name(menu.path) rv_child.meta = RouterMetaVo( title=menu.menu_name, icon=menu.icon, no_cache=True if menu.is_cache == "1" else False, link=menu.path) rv_children = [rv_child] rv.children = rv_children elif menu.parent_id == 0 and cls.is_inner_link(menu): rv.meta = RouterMetaVo( title=menu.menu_name, icon=menu.icon ) rv.path = "/" rv_child = RouterVo() rv_child.path = cls.inner_link_replace_each(menu.path) rv_child.component = UserConstants.INNER_LINK rv_child.name = cls.path_to_component_name(menu.path) rv_child.meta = RouterMetaVo( title=menu.menu_name, icon=menu.icon, link=menu.path ) rv_children = [rv_child] rv.children = rv_children routers.append(rv) return routers @classmethod def path_to_component_name(cls, path: str) -> str: ''' 将path字段转换为组件名称(只将首字母大写) path字段存储的是路由名称,不是组件路径 例如: recruitInfo -> RecruitInfo Args: path(str): 路由名称(path字段) Returns: str: 组件名称 ''' if not path: return "" # 只将首字母大写,保留其他字符不变(包括驼峰命名中的大写字母) # 例如: recruitInfo -> RecruitInfo return path[0].upper() + path[1:] if path else "" @classmethod def get_route_name(cls, menu: SysMenu) -> str: ''' 获取路由名称 Args: menu(SysMenu): 菜单信息 Returns: str: 路由名称 ''' if cls.is_menu_frame(menu): return "" else: return cls.path_to_component_name(menu.path) @classmethod def get_router_path(cls, menu: SysMenu) -> str: ''' 获取路由路径 Args: menu(SysMenu): 菜单信息 Returns: str: 路由路径 ''' router_path = menu.path if menu.parent_id != 0 and cls.is_inner_link(menu): router_path = cls.inner_link_replace_each(router_path) if menu.parent_id == 0 and menu.menu_type == UserConstants.TYPE_DIR \ and UserConstants.NO_FRAME == menu.is_frame: router_path = "/" + menu.path elif cls.is_menu_frame(menu): router_path = "/" return router_path @classmethod def is_menu_frame(cls, menu: SysMenu) -> bool: ''' 是否为菜单内部跳转 Args: menu(SysMenu): 菜单信息 Returns: bool: 是否为菜单内部跳转 ''' return menu.parent_id == 0 and UserConstants.TYPE_MENU == menu.menu_type \ and menu.is_frame == UserConstants.NO_FRAME @classmethod def is_inner_link(cls, menu: SysMenu) -> bool: ''' 是否为内链 Args: menu(SysMenu): 菜单信息 Returns: bool: 是否为内链 ''' return menu.is_frame == UserConstants.NO_FRAME and StringUtil.ishttp(menu.path) @classmethod def is_parent_view(cls, menu: SysMenu) -> bool: ''' 是否为parent_view Args: menu(SysMenu): 菜单信息 Returns: bool: 是否为parent_view ''' return menu.parent_id != 0 and UserConstants.TYPE_DIR == menu.menu_type @classmethod def build_menu_tree(cls, menus:List[SysMenu]) -> List[SysMenu]: ''' 构建菜单树结构 Args: menus(List[SysMenu]): 菜单列表 Returns: List[SysMenu]: 菜单树结构 ''' return_list = [] temp_list = [] for menu in menus: temp_list.append(menu.menu_id) for menu in menus: if menu.parent_id not in temp_list: cls.recursion_fn(menus, menu) return_list.append(menu) if not return_list: return_list = menus return return_list @classmethod def build_menu_tree_select(cls, menus:List[SysMenu]): ''' 构建菜单下拉树结构 Args: menus(List[SysMenu]): 菜单列表 Returns: List[TreeSelect]: 菜单下拉树结构 ''' menu_tree = cls.build_menu_tree(menus) return list(map(TreeSelect.from_menu, menu_tree)) @classmethod def select_menu_by_id(cls, menu_id) -> Optional[SysMenu]: ''' 根据菜单ID,查询菜单信息 Args: menu_id(int): 菜单ID Returns: Optional[SysMenu]: 菜单信息 ''' return SysMenuMapper.select_menu_by_id(menu_id) @classmethod def has_child_by_menu_id(cls, menu_id) -> bool: ''' 判断是否有子节点 Args: menu_id(int): 菜单ID Returns: bool: 是否有子节点 ''' num = SysMenuMapper.has_child_by_menu_id(menu_id) return num > 0 @classmethod def check_menu_exist_role(cls, menu_id:int) -> bool: ''' 查询菜单使用数量 Args: menu_id(int): 菜单ID Returns: bool: 是否有使用 ''' num = SysRoleMenuMapper.check_menu_exist_role(menu_id) return num > 0 @classmethod def insert_menu(cls, menu:SysMenu) -> int: ''' 新增菜单 Args: menu(SysMenu): 菜单信息 Returns: int: 新增菜单ID ''' return SysMenuMapper.insert_menu(menu) @classmethod def update_menu(cls, menu:SysMenu) -> int: ''' 修改菜单 Args: menu(SysMenu): 菜单信息 Returns: int: 修改菜单的数量 ''' return SysMenuMapper.update_menu(menu) @classmethod def delete_menu_by_id(cls, menu_id:int) -> int: ''' 根据菜单ID,删除菜单 Args: menu_id(int): 菜单ID Returns: int: 删除菜单的数量 ''' return SysMenuMapper.delete_menu_by_id(menu_id) @classmethod def check_menu_name_unique(cls, menu:SysMenu) -> Literal["0","1"]: ''' 校验菜单名称是否唯一 Args: menu(SysMenu): 菜单信息 Returns: Literal["0","1"]: 是否唯一, 0-唯一, 1-不唯一 ''' eo:SysMenu = SysMenuMapper.check_menu_name_unique(menu.menu_name, menu.parent_id) if eo and eo.menu_id != menu.menu_id: return UserConstants.Not_UNIQUE else: return UserConstants.UNIQUE @classmethod def get_component(cls, menu: SysMenu) -> Literal['InnerLink','ParentView','Layout']: ''' 获取组件类型 Args: menu(SysMenu): 菜单信息 Returns: Literal['InnerLink','ParentView','Layout']: 组件类型 ''' component = UserConstants.LAYOUT if menu.component and not cls.is_menu_frame(menu): component = menu.component elif not menu.component and menu.parent_id != 0 and cls.is_inner_link(menu): component = UserConstants.INNER_LINK elif not menu.component and cls.is_parent_view(menu): component = UserConstants.PARENT_VIEW return component @classmethod def get_child_perms(cls, menus:List[SysMenu], parent_id:int) -> List[SysMenu]: ''' 获取子菜单权限列表 Args: menus(List[SysMenu]): 菜单列表 parent_id(int): 父菜单ID Returns: List[SysMenu]: 子菜单列表 ''' return_list = [] for menu in menus: if menu.parent_id == parent_id: cls.recursion_fn(menus, menu) return_list.append(menu) return return_list @classmethod def recursion_fn(cls, list:List[SysMenu], t:SysMenu): """ 递归菜单列表 Args: list(List[SysMenu]): 菜单列表 t(SysMenu): 父菜单信息 """ child_list = cls.get_child_list(list, t) t.children = child_list for t_child in child_list: if cls.has_child(list, t_child): cls.recursion_fn(list, t_child) @classmethod def get_child_list(cls, list:List[SysMenu], t:SysMenu) -> List[SysMenu]: """ 子菜单列表 Args: list(List[SysMenu]): 菜单列表 t(SysMenu): 父菜单信息 Returns: List[SysMenu]: 子菜单列表 """ tlist = [] for n in list: if n.parent_id is not None and n.parent_id == t.menu_id: tlist.append(n) return tlist @classmethod def has_child(cls, list:List[SysMenu], t:SysMenu) -> bool: """ 判断是否有子菜单 Args: list(List[SysMenu]): 菜单列表 t(SysMenu): 父菜单信息 Returns: bool: 是否有子菜单 """ for n in list: if n.parent_id is not None and n.parent_id==t.menu_id: return True return False @classmethod def inner_link_replace_each(cls, path:str) -> str: ''' 内链域名特殊字符替换 Args: path(str): 路径 Returns: str: 路径 ''' path = path.replace(Constants.HTTP, "") path = path.replace(Constants.HTTPS, "") path = path.replace(Constants.WWW, "") path = path.replace(Constants.DOT, "/") return path