|
@@ -330,51 +330,94 @@ func (s *adminService) MenuDelete(ctx context.Context, id uint) error {
|
|
|
}
|
|
|
|
|
|
func (s *adminService) GetMenus(ctx context.Context, uid uint) (*v1.GetMenuResponseData, error) {
|
|
|
+ // 步骤 1: 从数据库中一次性获取所有菜单项。
|
|
|
menuList, err := s.adminRepository.GetMenuList(ctx)
|
|
|
if err != nil {
|
|
|
s.logger.WithContext(ctx).Error("GetMenuList error", zap.Error(err))
|
|
|
return nil, err
|
|
|
}
|
|
|
- data := &v1.GetMenuResponseData{
|
|
|
- List: make([]v1.MenuDataItem, 0),
|
|
|
- }
|
|
|
- // 获取权限的菜单
|
|
|
+
|
|
|
+ // 步骤 2: 获取当前用户的完整权限列表。
|
|
|
permissions, err := s.adminRepository.GetUserPermissions(ctx, uid)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
- menuPermMap := map[string]struct{}{}
|
|
|
+
|
|
|
+ // 步骤 3: 创建一个以菜单ID为键、菜单对象指针为值的映射(Map),用于快速查找。
|
|
|
+ // 这样可以避免在后续操作中反复遍历列表,提高效率。
|
|
|
+ allMenusMap := make(map[uint]*model.Menu)
|
|
|
+ for i := range menuList {
|
|
|
+ allMenusMap[menuList[i].ID] = &menuList[i]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 步骤 4: 创建一个集合(Set),存放用户被明确授权可以访问的菜单路径。
|
|
|
+ permittedMenuPaths := make(map[string]struct{})
|
|
|
for _, permission := range permissions {
|
|
|
- // 防呆设置,超管可以看到所有菜单
|
|
|
+ // 特殊处理:超级管理员(Admin)拥有所有菜单的权限。
|
|
|
if convertor.ToString(uid) == model.AdminUserID {
|
|
|
- menuPermMap[strings.TrimPrefix(permission[1], model.MenuResourcePrefix)] = struct{}{}
|
|
|
+ // Casbin返回的权限格式通常是 [role, resource, action],我们取 resource (permission[1])。
|
|
|
+ // 并移除菜单资源特有的前缀,得到干净的菜单路径。
|
|
|
+ permittedMenuPaths[strings.TrimPrefix(permission[1], model.MenuResourcePrefix)] = struct{}{}
|
|
|
} else {
|
|
|
+ // 普通用户的权限策略,只处理菜单相关的权限。
|
|
|
if len(permission) == 3 && strings.HasPrefix(permission[1], model.MenuResourcePrefix) {
|
|
|
- menuPermMap[strings.TrimPrefix(permission[1], model.MenuResourcePrefix)] = struct{}{}
|
|
|
+ permittedMenuPaths[strings.TrimPrefix(permission[1], model.MenuResourcePrefix)] = struct{}{}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 步骤 5: 构建最终的菜单列表,核心逻辑是确保所有有权限访问的子菜单,其父菜单也一定会被包含进来。
|
|
|
+ // 使用 Map (finalMenusMap) 来存放最终结果,可以巧妙地实现自动去重。
|
|
|
+ finalMenusMap := make(map[uint]v1.MenuDataItem)
|
|
|
for _, menu := range menuList {
|
|
|
- if _, ok := menuPermMap[menu.Path]; ok {
|
|
|
- data.List = append(data.List, v1.MenuDataItem{
|
|
|
- ID: menu.ID,
|
|
|
- Name: menu.Name,
|
|
|
- Title: menu.Title,
|
|
|
- Path: menu.Path,
|
|
|
- Component: menu.Component,
|
|
|
- Redirect: menu.Redirect,
|
|
|
- KeepAlive: menu.KeepAlive,
|
|
|
- HideInMenu: menu.HideInMenu,
|
|
|
- Locale: menu.Locale,
|
|
|
- Weight: menu.Weight,
|
|
|
- Icon: menu.Icon,
|
|
|
- ParentID: menu.ParentID,
|
|
|
- UpdatedAt: menu.UpdatedAt.Format("2006-01-02 15:04:05"),
|
|
|
- URL: menu.URL,
|
|
|
- })
|
|
|
+ // 检查当前遍历到的菜单,其路径是否在用户的权限集合内。
|
|
|
+ if _, ok := permittedMenuPaths[menu.Path]; ok {
|
|
|
+ // 如果用户有权访问此菜单,则启动一个循环,向上追溯并添加其所有的父级菜单。
|
|
|
+ curr := &menu // 从当前这个有权限的菜单开始追溯。
|
|
|
+ for curr != nil {
|
|
|
+ // 如果当前菜单(或其父菜单)已经存在于最终结果中,说明这条路径已经处理过,跳出循环以避免重复工作。
|
|
|
+ if _, exists := finalMenusMap[curr.ID]; exists {
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将当前菜单转换为API需要的数据结构,并添加到最终结果Map中。
|
|
|
+ finalMenusMap[curr.ID] = v1.MenuDataItem{
|
|
|
+ ID: curr.ID,
|
|
|
+ Name: curr.Name,
|
|
|
+ Title: curr.Title,
|
|
|
+ Path: curr.Path,
|
|
|
+ Component: curr.Component,
|
|
|
+ Redirect: curr.Redirect,
|
|
|
+ KeepAlive: curr.KeepAlive,
|
|
|
+ HideInMenu: curr.HideInMenu,
|
|
|
+ Locale: curr.Locale,
|
|
|
+ Weight: curr.Weight,
|
|
|
+ Icon: curr.Icon,
|
|
|
+ ParentID: curr.ParentID,
|
|
|
+ UpdatedAt: curr.UpdatedAt.Format("2006-01-02 15:04:05"),
|
|
|
+ URL: curr.URL,
|
|
|
+ }
|
|
|
+
|
|
|
+ // 移动到父菜单,准备下一次循环。
|
|
|
+ if curr.ParentID > 0 {
|
|
|
+ // 利用步骤3创建的快速索引Map,高效地找到父菜单对象。
|
|
|
+ curr = allMenusMap[curr.ParentID]
|
|
|
+ } else {
|
|
|
+ // 如果没有ParentID了,说明已经追溯到根节点,结束循环。
|
|
|
+ curr = nil
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // 步骤 6: 将Map形式的最终结果转换为列表(Slice),以符合API响应的格式。
|
|
|
+ data := &v1.GetMenuResponseData{
|
|
|
+ List: make([]v1.MenuDataItem, 0, len(finalMenusMap)),
|
|
|
+ }
|
|
|
+ for _, menuItem := range finalMenusMap {
|
|
|
+ data.List = append(data.List, menuItem)
|
|
|
+ }
|
|
|
+
|
|
|
return data, nil
|
|
|
}
|
|
|
func (s *adminService) GetAdminMenus(ctx context.Context) (*v1.GetMenuResponseData, error) {
|