311 lines
8.0 KiB
Go
311 lines
8.0 KiB
Go
package system
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"strconv"
|
||
)
|
||
|
||
// 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 bool) *MenuUsecase {
|
||
return &MenuUsecase{
|
||
repo: repo,
|
||
authRepo: authRepo,
|
||
useStrictAuth: 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
|
||
}
|