sys_menu.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  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 = cls.path_to_component_name(menu.path)
  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 = cls.path_to_component_name(menu.path)
  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 path_to_component_name(cls, path: str) -> str:
  155. '''
  156. 将path字段转换为组件名称(只将首字母大写)
  157. path字段存储的是路由名称,不是组件路径
  158. 例如: recruitInfo -> RecruitInfo
  159. Args:
  160. path(str): 路由名称(path字段)
  161. Returns:
  162. str: 组件名称
  163. '''
  164. if not path:
  165. return ""
  166. # 只将首字母大写,保留其他字符不变(包括驼峰命名中的大写字母)
  167. # 例如: recruitInfo -> RecruitInfo
  168. return path[0].upper() + path[1:] if path else ""
  169. @classmethod
  170. def get_route_name(cls, menu: SysMenu) -> str:
  171. '''
  172. 获取路由名称
  173. Args:
  174. menu(SysMenu): 菜单信息
  175. Returns:
  176. str: 路由名称
  177. '''
  178. if cls.is_menu_frame(menu):
  179. return ""
  180. else:
  181. return cls.path_to_component_name(menu.path)
  182. @classmethod
  183. def get_router_path(cls, menu: SysMenu) -> str:
  184. '''
  185. 获取路由路径
  186. Args:
  187. menu(SysMenu): 菜单信息
  188. Returns:
  189. str: 路由路径
  190. '''
  191. router_path = menu.path
  192. if menu.parent_id != 0 and cls.is_inner_link(menu):
  193. router_path = cls.inner_link_replace_each(router_path)
  194. if menu.parent_id == 0 and menu.menu_type == UserConstants.TYPE_DIR \
  195. and UserConstants.NO_FRAME == menu.is_frame:
  196. router_path = "/" + menu.path
  197. elif cls.is_menu_frame(menu):
  198. router_path = "/"
  199. return router_path
  200. @classmethod
  201. def is_menu_frame(cls, menu: SysMenu) -> bool:
  202. '''
  203. 是否为菜单内部跳转
  204. Args:
  205. menu(SysMenu): 菜单信息
  206. Returns:
  207. bool: 是否为菜单内部跳转
  208. '''
  209. return menu.parent_id == 0 and UserConstants.TYPE_MENU == menu.menu_type \
  210. and menu.is_frame == UserConstants.NO_FRAME
  211. @classmethod
  212. def is_inner_link(cls, menu: SysMenu) -> bool:
  213. '''
  214. 是否为内链
  215. Args:
  216. menu(SysMenu): 菜单信息
  217. Returns:
  218. bool: 是否为内链
  219. '''
  220. return menu.is_frame == UserConstants.NO_FRAME and StringUtil.ishttp(menu.path)
  221. @classmethod
  222. def is_parent_view(cls, menu: SysMenu) -> bool:
  223. '''
  224. 是否为parent_view
  225. Args:
  226. menu(SysMenu): 菜单信息
  227. Returns:
  228. bool: 是否为parent_view
  229. '''
  230. return menu.parent_id != 0 and UserConstants.TYPE_DIR == menu.menu_type
  231. @classmethod
  232. def build_menu_tree(cls, menus:List[SysMenu]) -> List[SysMenu]:
  233. '''
  234. 构建菜单树结构
  235. Args:
  236. menus(List[SysMenu]): 菜单列表
  237. Returns:
  238. List[SysMenu]: 菜单树结构
  239. '''
  240. return_list = []
  241. temp_list = []
  242. for menu in menus:
  243. temp_list.append(menu.menu_id)
  244. for menu in menus:
  245. if menu.parent_id not in temp_list:
  246. cls.recursion_fn(menus, menu)
  247. return_list.append(menu)
  248. if not return_list:
  249. return_list = menus
  250. return return_list
  251. @classmethod
  252. def build_menu_tree_select(cls, menus:List[SysMenu]):
  253. '''
  254. 构建菜单下拉树结构
  255. Args:
  256. menus(List[SysMenu]): 菜单列表
  257. Returns:
  258. List[TreeSelect]: 菜单下拉树结构
  259. '''
  260. menu_tree = cls.build_menu_tree(menus)
  261. return list(map(TreeSelect.from_menu, menu_tree))
  262. @classmethod
  263. def select_menu_by_id(cls, menu_id) -> Optional[SysMenu]:
  264. '''
  265. 根据菜单ID,查询菜单信息
  266. Args:
  267. menu_id(int): 菜单ID
  268. Returns:
  269. Optional[SysMenu]: 菜单信息
  270. '''
  271. return SysMenuMapper.select_menu_by_id(menu_id)
  272. @classmethod
  273. def has_child_by_menu_id(cls, menu_id) -> bool:
  274. '''
  275. 判断是否有子节点
  276. Args:
  277. menu_id(int): 菜单ID
  278. Returns:
  279. bool: 是否有子节点
  280. '''
  281. num = SysMenuMapper.has_child_by_menu_id(menu_id)
  282. return num > 0
  283. @classmethod
  284. def check_menu_exist_role(cls, menu_id:int) -> bool:
  285. '''
  286. 查询菜单使用数量
  287. Args:
  288. menu_id(int): 菜单ID
  289. Returns:
  290. bool: 是否有使用
  291. '''
  292. num = SysRoleMenuMapper.check_menu_exist_role(menu_id)
  293. return num > 0
  294. @classmethod
  295. def insert_menu(cls, menu:SysMenu) -> int:
  296. '''
  297. 新增菜单
  298. Args:
  299. menu(SysMenu): 菜单信息
  300. Returns:
  301. int: 新增菜单ID
  302. '''
  303. return SysMenuMapper.insert_menu(menu)
  304. @classmethod
  305. def update_menu(cls, menu:SysMenu) -> int:
  306. '''
  307. 修改菜单
  308. Args:
  309. menu(SysMenu): 菜单信息
  310. Returns:
  311. int: 修改菜单的数量
  312. '''
  313. return SysMenuMapper.update_menu(menu)
  314. @classmethod
  315. def delete_menu_by_id(cls, menu_id:int) -> int:
  316. '''
  317. 根据菜单ID,删除菜单
  318. Args:
  319. menu_id(int): 菜单ID
  320. Returns:
  321. int: 删除菜单的数量
  322. '''
  323. return SysMenuMapper.delete_menu_by_id(menu_id)
  324. @classmethod
  325. def check_menu_name_unique(cls, menu:SysMenu) -> Literal["0","1"]:
  326. '''
  327. 校验菜单名称是否唯一
  328. Args:
  329. menu(SysMenu): 菜单信息
  330. Returns:
  331. Literal["0","1"]: 是否唯一, 0-唯一, 1-不唯一
  332. '''
  333. eo:SysMenu = SysMenuMapper.check_menu_name_unique(menu.menu_name, menu.parent_id)
  334. if eo and eo.menu_id != menu.menu_id:
  335. return UserConstants.Not_UNIQUE
  336. else:
  337. return UserConstants.UNIQUE
  338. @classmethod
  339. def get_component(cls, menu: SysMenu) -> Literal['InnerLink','ParentView','Layout']:
  340. '''
  341. 获取组件类型
  342. Args:
  343. menu(SysMenu): 菜单信息
  344. Returns:
  345. Literal['InnerLink','ParentView','Layout']: 组件类型
  346. '''
  347. component = UserConstants.LAYOUT
  348. if menu.component and not cls.is_menu_frame(menu):
  349. component = menu.component
  350. elif not menu.component and menu.parent_id != 0 and cls.is_inner_link(menu):
  351. component = UserConstants.INNER_LINK
  352. elif not menu.component and cls.is_parent_view(menu):
  353. component = UserConstants.PARENT_VIEW
  354. return component
  355. @classmethod
  356. def get_child_perms(cls, menus:List[SysMenu], parent_id:int) -> List[SysMenu]:
  357. '''
  358. 获取子菜单权限列表
  359. Args:
  360. menus(List[SysMenu]): 菜单列表
  361. parent_id(int): 父菜单ID
  362. Returns:
  363. List[SysMenu]: 子菜单列表
  364. '''
  365. return_list = []
  366. for menu in menus:
  367. if menu.parent_id == parent_id:
  368. cls.recursion_fn(menus, menu)
  369. return_list.append(menu)
  370. return return_list
  371. @classmethod
  372. def recursion_fn(cls, list:List[SysMenu], t:SysMenu):
  373. """
  374. 递归菜单列表
  375. Args:
  376. list(List[SysMenu]): 菜单列表
  377. t(SysMenu): 父菜单信息
  378. """
  379. child_list = cls.get_child_list(list, t)
  380. t.children = child_list
  381. for t_child in child_list:
  382. if cls.has_child(list, t_child):
  383. cls.recursion_fn(list, t_child)
  384. @classmethod
  385. def get_child_list(cls, list:List[SysMenu], t:SysMenu) -> List[SysMenu]:
  386. """
  387. 子菜单列表
  388. Args:
  389. list(List[SysMenu]): 菜单列表
  390. t(SysMenu): 父菜单信息
  391. Returns:
  392. List[SysMenu]: 子菜单列表
  393. """
  394. tlist = []
  395. for n in list:
  396. if n.parent_id is not None and n.parent_id == t.menu_id:
  397. tlist.append(n)
  398. return tlist
  399. @classmethod
  400. def has_child(cls, list:List[SysMenu], t:SysMenu) -> bool:
  401. """
  402. 判断是否有子菜单
  403. Args:
  404. list(List[SysMenu]): 菜单列表
  405. t(SysMenu): 父菜单信息
  406. Returns:
  407. bool: 是否有子菜单
  408. """
  409. for n in list:
  410. if n.parent_id is not None and n.parent_id==t.menu_id:
  411. return True
  412. return False
  413. @classmethod
  414. def inner_link_replace_each(cls, path:str) -> str:
  415. '''
  416. 内链域名特殊字符替换
  417. Args:
  418. path(str): 路径
  419. Returns:
  420. str: 路径
  421. '''
  422. path = path.replace(Constants.HTTP, "")
  423. path = path.replace(Constants.HTTPS, "")
  424. path = path.replace(Constants.WWW, "")
  425. path = path.replace(Constants.DOT, "/")
  426. return path