package admin_menu import ( "context" "sort" "strconv" "ycc-server/app/main/api/internal/svc" "ycc-server/app/main/api/internal/types" "ycc-server/common/ctxdata" "ycc-server/common/xerr" "github.com/Masterminds/squirrel" "github.com/bytedance/sonic" "github.com/pkg/errors" "github.com/samber/lo" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/mr" ) type GetMenuAllLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetMenuAllLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMenuAllLogic { return &GetMenuAllLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetMenuAllLogic) GetMenuAll(req *types.GetMenuAllReq) (resp *[]types.GetMenuAllResp, err error) { userId, err := ctxdata.GetUidFromCtx(l.ctx) if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败, %+v", err) } // 使用MapReduceVoid并发获取用户角色 var roleIds []int64 var permissions []*struct { RoleId int64 } type UserRoleResult struct { RoleId int64 } err = mr.MapReduceVoid( func(source chan<- interface{}) { adminUserRoleBuilder := l.svcCtx.AdminUserRoleModel.SelectBuilder().Where(squirrel.Eq{"user_id": userId}) source <- adminUserRoleBuilder }, func(item interface{}, writer mr.Writer[*UserRoleResult], cancel func(error)) { builder := item.(squirrel.SelectBuilder) result, err := l.svcCtx.AdminUserRoleModel.FindAll(l.ctx, builder, "role_id DESC") if err != nil { cancel(errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户角色信息失败, %+v", err)) return } for _, r := range result { writer.Write(&UserRoleResult{RoleId: r.RoleId}) } }, func(pipe <-chan *UserRoleResult, cancel func(error)) { for item := range pipe { permissions = append(permissions, &struct{ RoleId int64 }{RoleId: item.RoleId}) } }, ) if err != nil { return nil, err } for _, permission := range permissions { roleIds = append(roleIds, permission.RoleId) } // 使用MapReduceVoid并发获取角色菜单 var menuIds []int64 var roleMenus []*struct { MenuId int64 } type RoleMenuResult struct { MenuId int64 } err = mr.MapReduceVoid( func(source chan<- interface{}) { getRoleMenuBuilder := l.svcCtx.AdminRoleMenuModel.SelectBuilder().Where(squirrel.Eq{"role_id": roleIds}) source <- getRoleMenuBuilder }, func(item interface{}, writer mr.Writer[*RoleMenuResult], cancel func(error)) { builder := item.(squirrel.SelectBuilder) result, err := l.svcCtx.AdminRoleMenuModel.FindAll(l.ctx, builder, "id DESC") if err != nil { cancel(errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取角色菜单信息失败, %+v", err)) return } for _, r := range result { writer.Write(&RoleMenuResult{MenuId: r.MenuId}) } }, func(pipe <-chan *RoleMenuResult, cancel func(error)) { for item := range pipe { roleMenus = append(roleMenus, &struct{ MenuId int64 }{MenuId: item.MenuId}) } }, ) if err != nil { return nil, err } for _, roleMenu := range roleMenus { menuIds = append(menuIds, roleMenu.MenuId) } // 使用MapReduceVoid并发获取菜单 type AdminMenuStruct struct { Id int64 Pid int64 Name string Path string Component string Redirect struct { String string Valid bool } Meta string Sort int64 Type int64 Status int64 } var menus []*AdminMenuStruct err = mr.MapReduceVoid( func(source chan<- interface{}) { adminMenuBuilder := l.svcCtx.AdminMenuModel.SelectBuilder().Where(squirrel.Eq{"id": menuIds}) source <- adminMenuBuilder }, func(item interface{}, writer mr.Writer[*AdminMenuStruct], cancel func(error)) { builder := item.(squirrel.SelectBuilder) result, err := l.svcCtx.AdminMenuModel.FindAll(l.ctx, builder, "sort ASC") if err != nil { cancel(errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取菜单信息失败, %+v", err)) return } for _, r := range result { menu := &AdminMenuStruct{ Id: r.Id, Pid: r.Pid, Name: r.Name, Path: r.Path, Component: r.Component, Redirect: r.Redirect, Meta: r.Meta, Sort: r.Sort, Type: r.Type, Status: r.Status, } writer.Write(menu) } }, func(pipe <-chan *AdminMenuStruct, cancel func(error)) { for item := range pipe { menus = append(menus, item) } }, ) if err != nil { return nil, err } // 转换为types.Menu结构并存储到映射表 menuMap := make(map[string]types.GetMenuAllResp) for _, menu := range menus { // 只处理状态正常的菜单 if menu.Status != 1 { continue } meta := make(map[string]interface{}) err = sonic.Unmarshal([]byte(menu.Meta), &meta) if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析菜单Meta信息失败, %+v", err) } redirect := func() string { if menu.Redirect.Valid { return menu.Redirect.String } return "" }() menuId := strconv.FormatInt(menu.Id, 10) menuMap[menuId] = types.GetMenuAllResp{ Name: menu.Name, Path: menu.Path, Redirect: redirect, Component: menu.Component, Sort: menu.Sort, Meta: meta, Children: make([]types.GetMenuAllResp, 0), } } // 按ParentId将菜单分组 menuGroups := lo.GroupBy(menus, func(item *AdminMenuStruct) int64 { return item.Pid }) // 递归构建菜单树 var buildMenuTree func(parentId int64) []types.GetMenuAllResp buildMenuTree = func(parentId int64) []types.GetMenuAllResp { children := make([]types.GetMenuAllResp, 0) childMenus, ok := menuGroups[parentId] if !ok { return children } // 按Sort排序 sort.Slice(childMenus, func(i, j int) bool { return childMenus[i].Sort < childMenus[j].Sort }) for _, childMenu := range childMenus { menuId := strconv.FormatInt(childMenu.Id, 10) if menu, exists := menuMap[menuId]; exists && childMenu.Status == 1 { // 递归构建子菜单 menu.Children = buildMenuTree(childMenu.Id) children = append(children, menu) } } return children } // 从根菜单开始构建(ParentId为0的是根菜单) menuTree := buildMenuTree(0) return &menuTree, nil }