package service import ( "encoding/base64" "strings" "ems.agt/features/sys_menu/consts" "ems.agt/features/sys_menu/model" sysRoleService "ems.agt/features/sys_role/service" sysrolemenu "ems.agt/features/sys_role_menu" "ems.agt/lib/core/utils/parse" "ems.agt/lib/core/utils/regular" "ems.agt/lib/core/vo" ) // 实例化服务层 ServiceSysMenu 结构体 var NewServiceSysMenu = &ServiceSysMenu{ sysMenuRepository: NewRepoSysMenu, sysRoleMenuRepository: sysrolemenu.NewRepoSysRoleMenu, sysRoleRepository: sysRoleService.NewRepoSysRole, } // ServiceSysMenu 菜单 服务层处理 type ServiceSysMenu struct { // 菜单服务 sysMenuRepository *RepoSysMenu // 角色与菜单关联服务 sysRoleMenuRepository *sysrolemenu.RepoSysRoleMenu // 角色服务 sysRoleRepository *sysRoleService.RepoSysRole } // SelectMenuList 查询系统菜单列表 func (r *ServiceSysMenu) SelectMenuList(sysMenu model.SysMenu, userId string) []model.SysMenu { return r.sysMenuRepository.SelectMenuList(sysMenu, userId) } // SelectMenuPermsByUserId 根据用户ID查询权限 func (r *ServiceSysMenu) SelectMenuPermsByUserId(userId string) []string { return r.sysMenuRepository.SelectMenuPermsByUserId(userId) } // SelectMenuTreeByUserId 根据用户ID查询菜单 func (r *ServiceSysMenu) SelectMenuTreeByUserId(userId string) []model.SysMenu { sysMenus := r.sysMenuRepository.SelectMenuTreeByUserId(userId) return r.parseDataToTree(sysMenus) } // SelectMenuTreeSelectByUserId 根据用户ID查询菜单树结构信息 func (r *ServiceSysMenu) SelectMenuTreeSelectByUserId(sysMenu model.SysMenu, userId string) []vo.TreeSelect { sysMenus := r.sysMenuRepository.SelectMenuList(sysMenu, userId) menus := r.parseDataToTree(sysMenus) tree := make([]vo.TreeSelect, 0) for _, menu := range menus { tree = append(tree, sysMenuTreeSelect(menu)) } return tree } // sysMenuTreeSelect 使用给定的 SysMenu 对象解析为 TreeSelect 对象 func sysMenuTreeSelect(sysMenu model.SysMenu) vo.TreeSelect { t := vo.TreeSelect{} t.ID = sysMenu.MenuID t.Label = sysMenu.MenuName t.Title = sysMenu.MenuName if len(sysMenu.Children) > 0 { for _, menu := range sysMenu.Children { child := sysMenuTreeSelect(menu) t.Children = append(t.Children, child) } } else { t.Children = []vo.TreeSelect{} } return t } // SelectMenuListByRoleId 根据角色ID查询菜单树信息 TODO func (r *ServiceSysMenu) SelectMenuListByRoleId(roleId string) []string { roles := r.sysRoleRepository.SelectRoleByIds([]string{roleId}) if len(roles) > 0 { role := roles[0] if role.RoleID == roleId { return r.sysMenuRepository.SelectMenuListByRoleId( role.RoleID, role.MenuCheckStrictly == "1", ) } } return []string{} } // SelectMenuById 根据菜单ID查询信息 func (r *ServiceSysMenu) SelectMenuById(menuId string) model.SysMenu { if menuId == "" { return model.SysMenu{} } menus := r.sysMenuRepository.SelectMenuByIds([]string{menuId}) if len(menus) > 0 { return menus[0] } return model.SysMenu{} } // HasChildByMenuIdAndStatus 存在菜单子节点数量与状态 func (r *ServiceSysMenu) HasChildByMenuIdAndStatus(menuId, status string) int64 { return r.sysMenuRepository.HasChildByMenuIdAndStatus(menuId, status) } // CheckMenuExistRole 查询菜单是否存在角色 func (r *ServiceSysMenu) CheckMenuExistRole(menuId string) int64 { return r.sysRoleMenuRepository.CheckMenuExistRole(menuId) } // InsertMenu 新增菜单信息 func (r *ServiceSysMenu) InsertMenu(sysMenu model.SysMenu) string { return r.sysMenuRepository.InsertMenu(sysMenu) } // UpdateMenu 修改菜单信息 func (r *ServiceSysMenu) UpdateMenu(sysMenu model.SysMenu) int64 { return r.sysMenuRepository.UpdateMenu(sysMenu) } // DeleteMenuById 删除菜单管理信息 func (r *ServiceSysMenu) DeleteMenuById(menuId string) int64 { // 删除菜单与角色关联 r.sysRoleMenuRepository.DeleteMenuRole([]string{menuId}) return r.sysMenuRepository.DeleteMenuById(menuId) } // CheckUniqueMenuName 校验菜单名称是否唯一 func (r *ServiceSysMenu) CheckUniqueMenuName(menuName, parentId, menuId string) bool { uniqueId := r.sysMenuRepository.CheckUniqueMenu(model.SysMenu{ MenuName: menuName, ParentID: parentId, }) if uniqueId == menuId { return true } return uniqueId == "" } // CheckUniqueMenuPath 校验路由地址是否唯一(针对目录和菜单) func (r *ServiceSysMenu) CheckUniqueMenuPath(path, menuId string) bool { uniqueId := r.sysMenuRepository.CheckUniqueMenu(model.SysMenu{ Path: path, }) if uniqueId == menuId { return true } return uniqueId == "" } // BuildRouteMenus 构建前端路由所需要的菜单 func (r *ServiceSysMenu) BuildRouteMenus(sysMenus []model.SysMenu, prefix string) []vo.Router { routers := []vo.Router{} for _, item := range sysMenus { router := vo.Router{} router.Name = r.getRouteName(item) router.Path = r.getRouterPath(item) router.Component = r.getComponent(item) router.Meta = r.getRouteMeta(item) // 子项菜单 目录类型 非路径链接 cMenus := item.Children if len(cMenus) > 0 && item.MenuType == consts.TYPE_DIR && !regular.ValidHttp(item.Path) { // 获取重定向地址 redirectPrefix, redirectPath := r.getRouteRedirect( cMenus, router.Path, prefix, ) router.Redirect = redirectPath // 子菜单进入递归 router.Children = r.BuildRouteMenus(cMenus, redirectPrefix) } else if item.ParentID == "0" && item.IsFrame == "1" && item.MenuType == consts.TYPE_MENU { // 父菜单 内部跳转 菜单类型 menuPath := "/" + item.MenuID childPath := menuPath + r.getRouterPath(item) children := vo.Router{ Name: r.getRouteName(item), Path: childPath, Component: item.Component, Meta: r.getRouteMeta(item), } router.Meta.HideChildInMenu = true router.Children = append(router.Children, children) router.Name = item.MenuID router.Path = menuPath router.Redirect = childPath router.Component = consts.COMPONENT_LAYOUT_BASIC } else if item.ParentID == "0" && item.IsFrame == "1" && regular.ValidHttp(item.Path) { // 父菜单 内部跳转 路径链接 menuPath := "/" + item.MenuID childPath := menuPath + r.getRouterPath(item) children := vo.Router{ Name: r.getRouteName(item), Path: childPath, Component: consts.COMPONENT_LAYOUT_LINK, Meta: r.getRouteMeta(item), } router.Meta.HideChildInMenu = true router.Children = append(router.Children, children) router.Name = item.MenuID router.Path = menuPath router.Redirect = childPath router.Component = consts.COMPONENT_LAYOUT_BASIC } routers = append(routers, router) } return routers } // getRouteName 获取路由名称 路径英文首字母大写 func (r *ServiceSysMenu) getRouteName(sysMenu model.SysMenu) string { routerName := parse.FirstUpper(sysMenu.Path) // 路径链接 if regular.ValidHttp(sysMenu.Path) { return routerName[:5] + "Link" + sysMenu.MenuID } return routerName } // getRouterPath 获取路由地址 func (r *ServiceSysMenu) getRouterPath(sysMenu model.SysMenu) string { routerPath := sysMenu.Path // 显式路径 if routerPath == "" || strings.HasPrefix(routerPath, "/") { return routerPath } // 路径链接 内部跳转 if regular.ValidHttp(routerPath) && sysMenu.IsFrame == "1" { routerPath = regular.Replace(routerPath, `/^http(s)?:\/\/+/`, "") routerPath = base64.StdEncoding.EncodeToString([]byte(routerPath)) } // 父菜单 内部跳转 if sysMenu.ParentID == "0" && sysMenu.IsFrame == "1" { routerPath = "/" + routerPath } return routerPath } // getComponent 获取组件信息 func (r *ServiceSysMenu) getComponent(sysMenu model.SysMenu) string { // 内部跳转 路径链接 if sysMenu.IsFrame == "1" && regular.ValidHttp(sysMenu.Path) { return consts.COMPONENT_LAYOUT_LINK } // 非父菜单 目录类型 if sysMenu.ParentID != "0" && sysMenu.MenuType == consts.TYPE_DIR { return consts.COMPONENT_LAYOUT_BLANK } // 组件路径 内部跳转 菜单类型 if sysMenu.Component != "" && sysMenu.IsFrame == "1" && sysMenu.MenuType == consts.TYPE_MENU { // 父菜单套外层布局 if sysMenu.ParentID == "0" { return consts.COMPONENT_LAYOUT_BASIC } return sysMenu.Component } return consts.COMPONENT_LAYOUT_BASIC } // getRouteMeta 获取路由元信息 func (r *ServiceSysMenu) getRouteMeta(sysMenu model.SysMenu) vo.RouterMeta { meta := vo.RouterMeta{} if sysMenu.Icon == "#" { meta.Icon = "" } else { meta.Icon = sysMenu.Icon } meta.Title = sysMenu.MenuName meta.HideChildInMenu = false meta.HideInMenu = sysMenu.Visible == "0" meta.Cache = sysMenu.IsCache == "1" meta.Target = "" // 路径链接 非内部跳转 if regular.ValidHttp(sysMenu.Path) && sysMenu.IsFrame == "0" { meta.Target = "_blank" } return meta } // getRouteRedirect 获取路由重定向地址(针对目录) // // cMenus 子菜单数组 // routerPath 当前菜单路径 // prefix 菜单重定向路径前缀 func (r *ServiceSysMenu) getRouteRedirect(cMenus []model.SysMenu, routerPath string, prefix string) (string, string) { redirectPath := "" // 重定向为首个显示并启用的子菜单 var firstChild *model.SysMenu for _, item := range cMenus { if item.IsFrame == "1" && item.Visible == "1" { firstChild = &item break } } // 检查内嵌隐藏菜单是否可做重定向 if firstChild == nil { for _, item := range cMenus { if item.IsFrame == "1" && item.Visible == "1" && strings.Contains(item.Path, consts.PATH_INLINE) { firstChild = &item break } } } if firstChild != nil { firstChildPath := r.getRouterPath(*firstChild) if strings.HasPrefix(firstChildPath, "/") { redirectPath = firstChildPath } else { // 拼接追加路径 if !strings.HasPrefix(routerPath, "/") { prefix += "/" } prefix = prefix + routerPath redirectPath = prefix + "/" + firstChildPath } } return prefix, redirectPath } // parseDataToTree 将数据解析为树结构,构建前端所需要下拉树结构 func (r *ServiceSysMenu) parseDataToTree(sysMenus []model.SysMenu) []model.SysMenu { // 节点分组 nodesMap := make(map[string][]model.SysMenu) // 节点id treeIds := []string{} // 树节点 tree := []model.SysMenu{} for _, item := range sysMenus { parentID := item.ParentID // 分组 mapItem, ok := nodesMap[parentID] if !ok { mapItem = []model.SysMenu{} } mapItem = append(mapItem, item) nodesMap[parentID] = mapItem // 记录节点ID treeIds = append(treeIds, item.MenuID) } for key, value := range nodesMap { // 选择不是节点ID的作为树节点 found := false for _, id := range treeIds { if id == key { found = true break } } if !found { tree = append(tree, value...) } } for i, node := range tree { iN := r.parseDataToTreeComponet(node, &nodesMap) tree[i] = iN } return tree } // parseDataToTreeComponet 递归函数处理子节点 func (r *ServiceSysMenu) parseDataToTreeComponet(node model.SysMenu, nodesMap *map[string][]model.SysMenu) model.SysMenu { id := node.MenuID children, ok := (*nodesMap)[id] if ok { node.Children = children } if len(node.Children) > 0 { for i, child := range node.Children { icN := r.parseDataToTreeComponet(child, nodesMap) node.Children[i] = icN } } return node }