service.xpcool.com/internal/service/admin/system/menu/menu.go
夏犀麟 2ed31f012e FIX: RBAC 禁用角色权限隔离(Codes/HasPermission/Routes 过滤 r.status=1)
- 三处联表查询统一 LeftJoin admin_role 并加启用状态过滤
- 修复前 opuser(禁用角色) 仍能拿到权限码并访问受保护接口
2026-08-26 21:43:15 +08:00

93 lines
2.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package admin_system_menu
import (
"context"
"sort"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/text/gstr"
"service.xpcool.com/internal/dao"
"service.xpcool.com/internal/model/dto"
"service.xpcool.com/internal/model/entity"
)
type IAdminMenu interface {
// Routes 返回管理员的可见菜单树vben 路由项)。
Routes(context.Context, uint64) ([]*dto.RouteItem, error)
}
type adminMenu struct{}
func NewAdminMenu() IAdminMenu { return &adminMenu{} }
var localAdminMenu IAdminMenu
func AdminMenu() IAdminMenu {
if localAdminMenu == nil {
panic("AdminMenu implementation not registered")
}
return localAdminMenu
}
func RegisterAdminMenu(i IAdminMenu) { localAdminMenu = i }
// routeName 将权限码(如 "system:admin")转换为唯一的
// vben 路由名,如 "SystemAdmin"。
func routeName(permission string) string {
if permission == "" {
return ""
}
return gstr.CaseCamel(strings.ReplaceAll(permission, ":", "_"))
}
func (s *adminMenu) Routes(ctx context.Context, adminID uint64) ([]*dto.RouteItem, error) {
// 当前管理员所有启用角色r.status=1可见的菜单type=1含按钮行但不作为路由节点。
var list []entity.AdminMenu
if err := dao.AdminUserRole.Ctx(ctx).As("ur").
LeftJoin("admin_role r", "ur.role_id=r.id").
LeftJoin("admin_role_menu rm", "ur.role_id=rm.role_id").
LeftJoin("admin_menu m", "rm.menu_id=m.id").
Where("ur.admin_user_id", adminID).
Where("m.type", 1).
Where("m.status", 1).
Where("r.status", 1).
Where("m.deleted_at IS NULL").
Fields("m.*").
Scan(&list); err != nil {
return nil, gerror.Wrap(err, "query admin menu routes")
}
byID := make(map[uint64]*dto.RouteItem, len(list))
for i := range list {
m := &list[i]
byID[m.Id] = &dto.RouteItem{
Name: routeName(m.Permission),
Path: m.Path,
Component: m.Component,
Meta: dto.RouteMeta{Title: m.Name, Icon: m.Icon, Order: m.Sort, HideInMenu: m.Hidden},
}
}
var roots []*dto.RouteItem
for i := range list {
m := &list[i]
item := byID[m.Id]
if m.ParentId == 0 {
roots = append(roots, item)
} else if p, ok := byID[m.ParentId]; ok {
p.Children = append(p.Children, item)
}
}
sort.Slice(roots, func(i, j int) bool { return roots[i].Meta.Order < roots[j].Meta.Order })
for _, r := range roots {
sortRouteChildren(r)
}
return roots, nil
}
func sortRouteChildren(r *dto.RouteItem) {
sort.Slice(r.Children, func(i, j int) bool { return r.Children[i].Meta.Order < r.Children[j].Meta.Order })
for _, c := range r.Children {
sortRouteChildren(c)
}
}