package system import ( "context" "errors" "strconv" "kra/internal/conf" ) // BaseMenu 基础菜单实体 type BaseMenu struct { ID uint ParentId uint Path string Name string Hidden bool Component string Sort int ActiveName string KeepAlive bool DefaultMenu bool Title string Icon string CloseTab bool TransitionType string Parameters []*MenuParameter MenuBtn []*MenuBtn Children []*BaseMenu } // MenuParameter 菜单参数 type MenuParameter struct { ID uint SysBaseMenuID uint Type string Key string Value string } // MenuBtn 菜单按钮 type MenuBtn struct { ID uint SysBaseMenuID uint Name string Desc string } // Menu 菜单(带权限) type Menu struct { BaseMenu AuthorityId uint MenuId uint Btns map[string]uint Children []*Menu } // AuthorityBtn 角色按钮权限 type AuthorityBtn struct { AuthorityId uint SysMenuID uint SysBaseMenuBtnID uint BtnName string } // MenuRepo 菜单仓储接口 type MenuRepo interface { CreateBaseMenu(ctx context.Context, menu *BaseMenu) error UpdateBaseMenu(ctx context.Context, menu *BaseMenu) error DeleteBaseMenu(ctx context.Context, id uint) error FindBaseMenuByID(ctx context.Context, id uint) (*BaseMenu, error) FindBaseMenuByName(ctx context.Context, name string) (*BaseMenu, error) FindAllBaseMenus(ctx context.Context) ([]*BaseMenu, error) FindBaseMenusByIds(ctx context.Context, ids []string) ([]*BaseMenu, error) FindAuthorityMenuIds(ctx context.Context, authorityId uint) ([]string, error) FindAuthorityBtns(ctx context.Context, authorityId uint) ([]*AuthorityBtn, error) SetMenuAuthority(ctx context.Context, authorityId uint, menuIds []uint) error CheckMenuInAuthority(ctx context.Context, authorityId uint, menuName string) (bool, error) } // MenuUsecase 菜单用例 type MenuUsecase struct { repo MenuRepo authRepo AuthorityRepo useStrictAuth bool } // NewMenuUsecase 创建菜单用例 func NewMenuUsecase(repo MenuRepo, authRepo AuthorityRepo, useStrictAuth conf.UseStrictAuth) *MenuUsecase { return &MenuUsecase{ repo: repo, authRepo: authRepo, useStrictAuth: bool(useStrictAuth), } } // AddBaseMenu 添加基础菜单 func (uc *MenuUsecase) AddBaseMenu(ctx context.Context, menu *BaseMenu) error { return uc.repo.CreateBaseMenu(ctx, menu) } // UpdateBaseMenu 更新基础菜单 func (uc *MenuUsecase) UpdateBaseMenu(ctx context.Context, menu *BaseMenu) error { return uc.repo.UpdateBaseMenu(ctx, menu) } // DeleteBaseMenu 删除基础菜单 func (uc *MenuUsecase) DeleteBaseMenu(ctx context.Context, id uint) error { return uc.repo.DeleteBaseMenu(ctx, id) } // GetBaseMenuById 根据ID获取基础菜单 func (uc *MenuUsecase) GetBaseMenuById(ctx context.Context, id uint) (*BaseMenu, error) { return uc.repo.FindBaseMenuByID(ctx, id) } // GetMenuTree 获取动态菜单树 func (uc *MenuUsecase) GetMenuTree(ctx context.Context, authorityId uint) ([]*Menu, error) { treeMap, err := uc.getMenuTreeMap(ctx, authorityId) if err != nil { return nil, err } menus := treeMap[0] for i := range menus { uc.getChildrenList(menus[i], treeMap) } return menus, nil } func (uc *MenuUsecase) getMenuTreeMap(ctx context.Context, authorityId uint) (map[uint][]*Menu, error) { treeMap := make(map[uint][]*Menu) // 获取角色的菜单ID menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityId) if err != nil { return nil, err } // 获取基础菜单 baseMenus, err := uc.repo.FindBaseMenusByIds(ctx, menuIds) if err != nil { return nil, err } // 获取按钮权限 btns, err := uc.repo.FindAuthorityBtns(ctx, authorityId) if err != nil { return nil, err } // 构建按钮map btnMap := make(map[uint]map[string]uint) for _, b := range btns { if btnMap[b.SysMenuID] == nil { btnMap[b.SysMenuID] = make(map[string]uint) } btnMap[b.SysMenuID][b.BtnName] = authorityId } // 构建菜单树 for _, base := range baseMenus { menu := &Menu{ BaseMenu: *base, AuthorityId: authorityId, MenuId: base.ID, Btns: btnMap[base.ID], } treeMap[base.ParentId] = append(treeMap[base.ParentId], menu) } return treeMap, nil } func (uc *MenuUsecase) getChildrenList(menu *Menu, treeMap map[uint][]*Menu) { menu.Children = treeMap[menu.MenuId] for i := range menu.Children { uc.getChildrenList(menu.Children[i], treeMap) } } // GetBaseMenuTree 获取基础菜单树 func (uc *MenuUsecase) GetBaseMenuTree(ctx context.Context, authorityID uint) ([]*BaseMenu, error) { treeMap, err := uc.getBaseMenuTreeMap(ctx, authorityID) if err != nil { return nil, err } menus := treeMap[0] for i := range menus { uc.getBaseChildrenList(menus[i], treeMap) } return menus, nil } func (uc *MenuUsecase) getBaseMenuTreeMap(ctx context.Context, authorityID uint) (map[uint][]*BaseMenu, error) { parentAuthorityID, err := uc.authRepo.GetParentAuthorityID(ctx, authorityID) if err != nil { return nil, err } var allMenus []*BaseMenu treeMap := make(map[uint][]*BaseMenu) // 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选 if uc.useStrictAuth && parentAuthorityID != 0 { menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityID) if err != nil { return nil, err } allMenus, err = uc.repo.FindBaseMenusByIds(ctx, menuIds) if err != nil { return nil, err } } else { allMenus, err = uc.repo.FindAllBaseMenus(ctx) if err != nil { return nil, err } } for _, m := range allMenus { treeMap[m.ParentId] = append(treeMap[m.ParentId], m) } return treeMap, nil } func (uc *MenuUsecase) getBaseChildrenList(menu *BaseMenu, treeMap map[uint][]*BaseMenu) { menu.Children = treeMap[menu.ID] for i := range menu.Children { uc.getBaseChildrenList(menu.Children[i], treeMap) } } // GetInfoList 获取菜单列表(树形) func (uc *MenuUsecase) GetInfoList(ctx context.Context, authorityID uint) ([]*BaseMenu, error) { return uc.GetBaseMenuTree(ctx, authorityID) } // AddMenuAuthority 为角色增加菜单权限 func (uc *MenuUsecase) AddMenuAuthority(ctx context.Context, menus []*BaseMenu, adminAuthorityID, authorityId uint) error { // 检查权限 parentAuthorityID, err := uc.authRepo.GetParentAuthorityID(ctx, adminAuthorityID) if err != nil { return err } // 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选 if uc.useStrictAuth && parentAuthorityID != 0 { menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, adminAuthorityID) if err != nil { return err } for _, menu := range menus { hasMenu := false idStr := strconv.Itoa(int(menu.ID)) for _, mid := range menuIds { if idStr == mid { hasMenu = true break } } if !hasMenu { return errors.New("添加失败,请勿跨级操作") } } } // 设置菜单权限 menuIds := make([]uint, len(menus)) for i, m := range menus { menuIds[i] = m.ID } return uc.repo.SetMenuAuthority(ctx, authorityId, menuIds) } // GetMenuAuthority 获取角色的菜单权限 func (uc *MenuUsecase) GetMenuAuthority(ctx context.Context, authorityId uint) ([]*Menu, error) { menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityId) if err != nil { return nil, err } baseMenus, err := uc.repo.FindBaseMenusByIds(ctx, menuIds) if err != nil { return nil, err } menus := make([]*Menu, len(baseMenus)) for i, base := range baseMenus { menus[i] = &Menu{ BaseMenu: *base, AuthorityId: authorityId, MenuId: base.ID, } } return menus, nil } // UserAuthorityDefaultRouter 用户角色默认路由检查 // 如果用户的默认路由不在其权限菜单中,则设置为404 func (uc *MenuUsecase) UserAuthorityDefaultRouter(ctx context.Context, authorityId uint, defaultRouter string) string { if defaultRouter == "" { return "404" } // 检查默认路由是否在用户的权限菜单中 exists, err := uc.repo.CheckMenuInAuthority(ctx, authorityId, defaultRouter) if err != nil || !exists { return "404" } return defaultRouter }