sys_menu.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. # -*- coding: utf-8 -*-
  2. # @Author : YY
  3. from typing import List, Literal, Optional, Set
  4. from ruoyi_common.constant import Constants, UserConstants
  5. from ruoyi_common.domain.entity import SysRole, TreeSelect
  6. from ruoyi_system.domain.vo import RouterMetaVo, RouterVo
  7. from ruoyi_system.mapper import SysMenuMapper
  8. from ruoyi_common.utils import security_util as SecurityUtil, StringUtil
  9. from ruoyi_common.domain.entity import SysMenu
  10. from ruoyi_system.mapper.sys_role import SysRoleMapper
  11. from ruoyi_system.mapper.sys_role_menu import SysRoleMenuMapper
  12. class SysMenuService:
  13. @classmethod
  14. def select_menu_list_by_user_id(cls, user_id) -> List[SysMenu]:
  15. '''
  16. 根据用户,查询菜单列表
  17. Args:
  18. user_id(int): 用户ID
  19. Returns:
  20. List[SysMenu]: 菜单列表
  21. '''
  22. return cls.select_menu_list(SysMenu(), user_id)
  23. @classmethod
  24. def select_menu_list(cls, menu:SysMenu, user_id:int) -> List[SysMenu]:
  25. '''
  26. 查询菜单列表
  27. Args:
  28. menu(SysMenu): 菜单实体
  29. user_id(int): 用户ID
  30. Returns:
  31. List[SysMenu]: 菜单列表
  32. '''
  33. if SecurityUtil.is_admin(user_id):
  34. eos: List[SysMenu] = SysMenuMapper.select_menu_list(menu)
  35. else:
  36. eos: List[SysMenu] = SysMenuMapper \
  37. .select_menu_list_by_user_id(menu, user_id)
  38. return eos
  39. @classmethod
  40. def select_menu_perms_by_user_id(cls, user_id:int) -> List[str]:
  41. '''
  42. 根据用户ID,查询菜单权限列表
  43. Args:
  44. user_id(int): 用户ID
  45. Returns:
  46. List[str]: 菜单权限列表
  47. '''
  48. perms: List[str] = SysMenuMapper.select_menu_perms_by_user_id(user_id)
  49. perm_set = set()
  50. for perm in perms:
  51. if perm:
  52. perm_set |= set(perm.strip().split(','))
  53. return list(perm_set)
  54. @classmethod
  55. def select_menu_perms_by_role_id(cls, role_id:int) -> Set[str]:
  56. '''
  57. 根据角色ID,查询菜单权限列表
  58. Args:
  59. role_id(int): 角色ID
  60. Returns:
  61. Set[str]: 菜单权限集合
  62. '''
  63. perms: List[str] = SysMenuMapper.select_menu_perms_by_role_id(role_id)
  64. perm_set = set()
  65. for perm in perms:
  66. if perm:
  67. perm_set |= set(perm.strip().split(','))
  68. return perm_set
  69. @classmethod
  70. def select_menu_tree_by_user_id(cls, user_id:int) -> List[SysMenu]:
  71. '''
  72. 根据角色ID,查询菜单树
  73. Args:
  74. user_id(int): 用户ID
  75. Returns:
  76. List[SysMenu]: 菜单树
  77. '''
  78. if SecurityUtil.is_admin(user_id):
  79. eos: List[SysMenu] = SysMenuMapper.select_menu_tree_all()
  80. else:
  81. eos: List[SysMenu] = SysMenuMapper.select_menu_tree_by_user_id(user_id)
  82. return cls.get_child_perms(eos, 0)
  83. @classmethod
  84. def select_menu_list_by_role_id(cls, role_id:int):
  85. '''
  86. 根据角色ID,查询菜单列表
  87. Args:
  88. role_id(int): 角色ID
  89. Returns:
  90. List[SysMenu]: 菜单列表
  91. '''
  92. eo:SysRole = SysRoleMapper.select_role_by_id(role_id)
  93. return SysMenuMapper.select_menu_list_by_role_id(role_id,eo.menu_check_strictly)
  94. @classmethod
  95. def build_menus(cls, menus:List[SysMenu]):
  96. '''
  97. 构建菜单
  98. Args:
  99. menus(List[SysMenu]): 菜单列表
  100. Returns:
  101. List[RouterVo]: 路由列表
  102. '''
  103. routers:List[RouterVo] = []
  104. for menu in menus:
  105. rv:RouterVo = RouterVo()
  106. rv.hidden = False if menu.visible == "0" else True
  107. rv.name = cls.get_route_name(menu)
  108. rv.path = cls.get_router_path(menu)
  109. rv.component = cls.get_component(menu)
  110. rv.query = menu.query
  111. rv.meta = RouterMetaVo(
  112. title=menu.menu_name,
  113. icon=menu.icon,
  114. no_cache=True if menu.is_cache == "1" else False,
  115. link=menu.path)
  116. menu_children = menu.children
  117. if menu_children and UserConstants.TYPE_DIR == menu.menu_type:
  118. rv.always_show = True
  119. rv.redirect = 'noRedirect'
  120. rv.children = cls.build_menus(menu_children)
  121. elif cls.is_menu_frame(menu):
  122. rv.meta = None
  123. rv_child = RouterVo()
  124. rv_child.path = menu.path
  125. rv_child.component = menu.component
  126. rv_child.name = menu.path.capitalize()
  127. rv_child.meta = RouterMetaVo(
  128. title=menu.menu_name,
  129. icon=menu.icon,
  130. no_cache=True if menu.is_cache == "1" else False,
  131. link=menu.path)
  132. rv_children = [rv_child]
  133. rv.children = rv_children
  134. elif menu.parent_id == 0 and cls.is_inner_link(menu):
  135. rv.meta = RouterMetaVo(
  136. title=menu.menu_name,
  137. icon=menu.icon
  138. )
  139. rv.path = "/"
  140. rv_child = RouterVo()
  141. rv_child.path = cls.inner_link_replace_each(menu.path)
  142. rv_child.component = UserConstants.INNER_LINK
  143. rv_child.name = menu.path.capitalize()
  144. rv_child.meta = RouterMetaVo(
  145. title=menu.menu_name,
  146. icon=menu.icon,
  147. link=menu.path
  148. )
  149. rv_children = [rv_child]
  150. rv.children = rv_children
  151. routers.append(rv)
  152. return routers
  153. @classmethod
  154. def get_route_name(cls, menu: SysMenu) -> str:
  155. '''
  156. 获取路由名称
  157. Args:
  158. menu(SysMenu): 菜单信息
  159. Returns:
  160. str: 路由名称
  161. '''
  162. if cls.is_menu_frame(menu):
  163. return ""
  164. else:
  165. return menu.path.capitalize()
  166. @classmethod
  167. def get_router_path(cls, menu: SysMenu) -> str:
  168. '''
  169. 获取路由路径
  170. Args:
  171. menu(SysMenu): 菜单信息
  172. Returns:
  173. str: 路由路径
  174. '''
  175. router_path = menu.path
  176. if menu.parent_id != 0 and cls.is_inner_link(menu):
  177. router_path = cls.inner_link_replace_each(router_path)
  178. if menu.parent_id == 0 and menu.menu_type == UserConstants.TYPE_DIR \
  179. and UserConstants.NO_FRAME == menu.is_frame:
  180. router_path = "/" + menu.path
  181. elif cls.is_menu_frame(menu):
  182. router_path = "/"
  183. return router_path
  184. @classmethod
  185. def is_menu_frame(cls, menu: SysMenu) -> bool:
  186. '''
  187. 是否为菜单内部跳转
  188. Args:
  189. menu(SysMenu): 菜单信息
  190. Returns:
  191. bool: 是否为菜单内部跳转
  192. '''
  193. return menu.parent_id == 0 and UserConstants.TYPE_MENU == menu.menu_type \
  194. and menu.is_frame == UserConstants.NO_FRAME
  195. @classmethod
  196. def is_inner_link(cls, menu: SysMenu) -> bool:
  197. '''
  198. 是否为内链
  199. Args:
  200. menu(SysMenu): 菜单信息
  201. Returns:
  202. bool: 是否为内链
  203. '''
  204. return menu.is_frame == UserConstants.NO_FRAME and StringUtil.ishttp(menu.path)
  205. @classmethod
  206. def is_parent_view(cls, menu: SysMenu) -> bool:
  207. '''
  208. 是否为parent_view
  209. Args:
  210. menu(SysMenu): 菜单信息
  211. Returns:
  212. bool: 是否为parent_view
  213. '''
  214. return menu.parent_id != 0 and UserConstants.TYPE_DIR == menu.menu_type
  215. @classmethod
  216. def build_menu_tree(cls, menus:List[SysMenu]) -> List[SysMenu]:
  217. '''
  218. 构建菜单树结构
  219. Args:
  220. menus(List[SysMenu]): 菜单列表
  221. Returns:
  222. List[SysMenu]: 菜单树结构
  223. '''
  224. return_list = []
  225. temp_list = []
  226. for menu in menus:
  227. temp_list.append(menu.menu_id)
  228. for menu in menus:
  229. if menu.parent_id not in temp_list:
  230. cls.recursion_fn(menus, menu)
  231. return_list.append(menu)
  232. if not return_list:
  233. return_list = menus
  234. return return_list
  235. @classmethod
  236. def build_menu_tree_select(cls, menus:List[SysMenu]):
  237. '''
  238. 构建菜单下拉树结构
  239. Args:
  240. menus(List[SysMenu]): 菜单列表
  241. Returns:
  242. List[TreeSelect]: 菜单下拉树结构
  243. '''
  244. menu_tree = cls.build_menu_tree(menus)
  245. return list(map(TreeSelect.from_menu, menu_tree))
  246. @classmethod
  247. def select_menu_by_id(cls, menu_id) -> Optional[SysMenu]:
  248. '''
  249. 根据菜单ID,查询菜单信息
  250. Args:
  251. menu_id(int): 菜单ID
  252. Returns:
  253. Optional[SysMenu]: 菜单信息
  254. '''
  255. return SysMenuMapper.select_menu_by_id(menu_id)
  256. @classmethod
  257. def has_child_by_menu_id(cls, menu_id) -> bool:
  258. '''
  259. 判断是否有子节点
  260. Args:
  261. menu_id(int): 菜单ID
  262. Returns:
  263. bool: 是否有子节点
  264. '''
  265. num = SysMenuMapper.has_child_by_menu_id(menu_id)
  266. return num > 0
  267. @classmethod
  268. def check_menu_exist_role(cls, menu_id:int) -> bool:
  269. '''
  270. 查询菜单使用数量
  271. Args:
  272. menu_id(int): 菜单ID
  273. Returns:
  274. bool: 是否有使用
  275. '''
  276. num = SysRoleMenuMapper.check_menu_exist_role(menu_id)
  277. return num > 0
  278. @classmethod
  279. def insert_menu(cls, menu:SysMenu) -> int:
  280. '''
  281. 新增菜单
  282. Args:
  283. menu(SysMenu): 菜单信息
  284. Returns:
  285. int: 新增菜单ID
  286. '''
  287. return SysMenuMapper.insert_menu(menu)
  288. @classmethod
  289. def update_menu(cls, menu:SysMenu) -> int:
  290. '''
  291. 修改菜单
  292. Args:
  293. menu(SysMenu): 菜单信息
  294. Returns:
  295. int: 修改菜单的数量
  296. '''
  297. return SysMenuMapper.update_menu(menu)
  298. @classmethod
  299. def delete_menu_by_id(cls, menu_id:int) -> int:
  300. '''
  301. 根据菜单ID,删除菜单
  302. Args:
  303. menu_id(int): 菜单ID
  304. Returns:
  305. int: 删除菜单的数量
  306. '''
  307. return SysMenuMapper.delete_menu_by_id(menu_id)
  308. @classmethod
  309. def check_menu_name_unique(cls, menu:SysMenu) -> Literal["0","1"]:
  310. '''
  311. 校验菜单名称是否唯一
  312. Args:
  313. menu(SysMenu): 菜单信息
  314. Returns:
  315. Literal["0","1"]: 是否唯一, 0-唯一, 1-不唯一
  316. '''
  317. eo:SysMenu = SysMenuMapper.check_menu_name_unique(menu.menu_name, menu.parent_id)
  318. if eo and eo.menu_id != menu.menu_id:
  319. return UserConstants.Not_UNIQUE
  320. else:
  321. return UserConstants.UNIQUE
  322. @classmethod
  323. def get_component(cls, menu: SysMenu) -> Literal['InnerLink','ParentView','Layout']:
  324. '''
  325. 获取组件类型
  326. Args:
  327. menu(SysMenu): 菜单信息
  328. Returns:
  329. Literal['InnerLink','ParentView','Layout']: 组件类型
  330. '''
  331. component = UserConstants.LAYOUT
  332. if menu.component and not cls.is_menu_frame(menu):
  333. component = menu.component
  334. elif not menu.component and menu.parent_id != 0 and cls.is_inner_link(menu):
  335. component = UserConstants.INNER_LINK
  336. elif not menu.component and cls.is_parent_view(menu):
  337. component = UserConstants.PARENT_VIEW
  338. return component
  339. @classmethod
  340. def get_child_perms(cls, menus:List[SysMenu], parent_id:int) -> List[SysMenu]:
  341. '''
  342. 获取子菜单权限列表
  343. Args:
  344. menus(List[SysMenu]): 菜单列表
  345. parent_id(int): 父菜单ID
  346. Returns:
  347. List[SysMenu]: 子菜单列表
  348. '''
  349. return_list = []
  350. for menu in menus:
  351. if menu.parent_id == parent_id:
  352. cls.recursion_fn(menus, menu)
  353. return_list.append(menu)
  354. return return_list
  355. @classmethod
  356. def recursion_fn(cls, list:List[SysMenu], t:SysMenu):
  357. """
  358. 递归菜单列表
  359. Args:
  360. list(List[SysMenu]): 菜单列表
  361. t(SysMenu): 父菜单信息
  362. """
  363. child_list = cls.get_child_list(list, t)
  364. t.children = child_list
  365. for t_child in child_list:
  366. if cls.has_child(list, t_child):
  367. cls.recursion_fn(list, t_child)
  368. @classmethod
  369. def get_child_list(cls, list:List[SysMenu], t:SysMenu) -> List[SysMenu]:
  370. """
  371. 子菜单列表
  372. Args:
  373. list(List[SysMenu]): 菜单列表
  374. t(SysMenu): 父菜单信息
  375. Returns:
  376. List[SysMenu]: 子菜单列表
  377. """
  378. tlist = []
  379. for n in list:
  380. if n.parent_id is not None and n.parent_id == t.menu_id:
  381. tlist.append(n)
  382. return tlist
  383. @classmethod
  384. def has_child(cls, list:List[SysMenu], t:SysMenu) -> bool:
  385. """
  386. 判断是否有子菜单
  387. Args:
  388. list(List[SysMenu]): 菜单列表
  389. t(SysMenu): 父菜单信息
  390. Returns:
  391. bool: 是否有子菜单
  392. """
  393. for n in list:
  394. if n.parent_id is not None and n.parent_id==t.menu_id:
  395. return True
  396. return False
  397. @classmethod
  398. def inner_link_replace_each(cls, path:str) -> str:
  399. '''
  400. 内链域名特殊字符替换
  401. Args:
  402. path(str): 路径
  403. Returns:
  404. str: 路径
  405. '''
  406. path = path.replace(Constants.HTTP, "")
  407. path = path.replace(Constants.HTTPS, "")
  408. path = path.replace(Constants.WWW, "")
  409. path = path.replace(Constants.DOT, "/")
  410. return path