📋 系统概述
本系统是一个基于角色权限控制的菜单管理系统,实现了菜单管理、角色管理、权限分配等核心功能。系统采用树形结构组织菜单,支持细粒度的权限控制。
🏗️ 系统架构图
📊 核心功能流程图
1. 菜单管理流程

2. 角色权限分配流程
🗂️ 数据模型
核心实体类
// 菜单实体
public class MenuEntity
{
public string Id { get; set; }
public string Name { get; set; }
public string Text { get; set; }
public string ParentId { get; set; }
public int Sort { get; set; }
public string Link { get; set; }
public string FileUrl { get; set; }
public bool IsHide { get; set; }
public bool IsPure { get; set; }
public bool Enable { get; set; }
}
// 角色实体
public class RoleEntity
{
public string Id { get; set; }
public string RoleName { get; set; }
public string MenuIdsList { get; set; } // JSON格式的菜单ID列表
public bool Enable { get; set; }
}
// 用户实体
public class UserEntity
{
public string Id { get; set; }
public string RoleId { get; set; }
}
🔧 核心功能实现
1. 菜单管理模块
获取菜单列表
🔧 核心功能实现
1. 菜单管理模块
获取菜单列表
csharp
[HttpGet]
public BaseResult<PagingResult<MenuOutput>> GetMenus([FromQuery] PagingRequest request)
{
var result = new BaseResult<PagingResult<MenuOutput>>();
try
{
// 获取所有启用的菜单
var allMenus = _dbService.Where<MenuEntity>(p => p.Enable)
.OrderBy(p => p.Sort)
.ToList();
// 搜索过滤
if (!string.IsNullOrEmpty(request.SearchKey))
{
allMenus = allMenus.Where(p =>
p.Name.Contains(request.SearchKey) ||
p.Text.Contains(request.SearchKey))
.ToList();
}
// 构建树形结构
var treeMenus = BuildMenuTree(allMenus, "");
// 分页处理
int totalCount = treeMenus.Count;
var pagedMenus = treeMenus
.Skip((request.PageIndex - 1) * request.PageSize)
.Take(request.PageSize)
.ToList();
// 转换为输出格式
var outputList = pagedMenus.Select(menu => new MenuOutput
{
Id = menu.Id,
Name = menu.Name,
Text = menu.Text,
Hide = menu.IsHide,
Pure = menu.IsPure,
Link = menu.Link,
File = menu.FileUrl,
ParentId = menu.ParentId,
Sort = menu.Sort,
Children = ConvertToSubMenuList(menu.Children)
}).ToList();
result.Data = new PagingResult<MenuOutput>
{
TotalCount = totalCount,
List = outputList
};
}
catch (Exception ex)
{
result.Code = ResultCode.Error;
result.Message = $"查询失败:{ex.Message}";
}
return result;
}
构建菜单树
private List<MenuTreeNode> BuildMenuTree(List<MenuEntity> allMenus, string parentId)
{
var treeNodes = allMenus
.Where(menu => menu.ParentId == parentId)
.Select(menu => new MenuTreeNode
{
Id = menu.Id,
Name = menu.Name,
Text = menu.Text,
IsHide = menu.IsHide,
IsPure = menu.IsPure,
Link = menu.Link,
FileUrl = menu.FileUrl,
ParentId = menu.ParentId,
Sort = menu.Sort,
Children = new List<MenuTreeNode>()
})
.OrderBy(node => node.Sort)
.ToList();
// 递归构建子菜单
foreach (var node in treeNodes)
{
node.Children = BuildMenuTree(allMenus, node.Id);
}
return treeNodes;
}
添加菜单
[HttpPost]
public BaseResult AddMenu(AddMenuRequest request)
{
var result = new BaseResult();
// 验证菜单名称唯一性
var existingMenu = _dbService.FirstOrDefault<MenuEntity>(p =>
p.Enable && p.Name == request.Name);
if (existingMenu != null)
{
result.Code = ResultCode.Error;
result.Message = $"菜单名称「{request.Name}」已存在";
return result;
}
// 验证父菜单存在性
if (!string.IsNullOrEmpty(request.ParentId))
{
var parentMenu = _dbService.FirstOrDefault<MenuEntity>(p =>
p.Enable && p.Id == request.ParentId);
if (parentMenu == null)
{
result.Code = ResultCode.Error;
result.Message = $"父菜单不存在";
return result;
}
}
try
{
var newMenu = new MenuEntity
{
Sort = request.Sort,
FileUrl = request.File,
IsHide = request.Hide,
IsPure = request.Pure,
Link = request.Link,
Name = request.Name,
Text = request.Text,
ParentId = request.ParentId,
Enable = true
};
_dbService.Add(newMenu);
}
catch (Exception ex)
{
result.Code = ResultCode.Error;
result.Message = $"新增失败:{ex.Message}";
}
return result;
}
2. 角色管理模块
获取角色列表
[HttpGet]
public BaseResult<PagingResult<RoleOutput>> GetRoles([FromQuery] PagingRequest request)
{
var result = new BaseResult<PagingResult<RoleOutput>>();
var pagingResult = new PagingResult<RoleOutput>();
try
{
var query = from role in _dbService.Where<RoleEntity>(p => p.Enable)
select new RoleOutput
{
Id = role.Id,
Key = role.Id,
MenuIdsJson = role.MenuIdsList,
Name = role.RoleName,
};
// 搜索过滤
if (!string.IsNullOrEmpty(request.SearchKey))
{
query = query.Where(p =>
p.Name.Contains(request.SearchKey) ||
p.Key.Contains(request.SearchKey));
}
// 非管理员不能查看admin角色
var currentUser = _dbService.FirstOrDefault<UserEntity>(p => p.Id == GetCurrentUserId());
if (currentUser.RoleId != "admin")
{
query = query.Where(p => p.Id != "admin");
}
// 分页处理
int totalCount = query.Count();
var data = query.OrderBy(p => p.Id)
.Skip((request.PageIndex - 1) * request.PageSize)
.Take(request.PageSize)
.ToList();
// 解析菜单树
foreach (var item in data)
{
if (!string.IsNullOrEmpty(item.MenuIdsJson))
{
var menuIds = JsonHelper.Deserialize<List<string>>(item.MenuIdsJson) ?? new List<string>();
item.Menus = BuildRoleMenus(menuIds);
}
}
pagingResult.TotalCount = totalCount;
pagingResult.List = data;
result.Data = pagingResult;
}
catch (Exception ex)
{
result.Code = ResultCode.Error;
result.Message = $"查询失败:{ex.Message}";
}
return result;
}
构建角色菜单树
private List<MenuModel> BuildRoleMenus(List<string> authorizedMenuIds)
{
var result = new List<MenuModel>();
if (!authorizedMenuIds.Any()) return result;
// 获取所有启用的菜单
var allMenus = _dbService.Where<MenuEntity>(p => p.Enable).ToList();
// 筛选授权菜单并补全父级
var authorizedMenus = new HashSet<MenuEntity>();
foreach (var menuId in authorizedMenuIds)
{
var menu = allMenus.FirstOrDefault(m => m.Id == menuId);
if (menu != null)
{
authorizedMenus.Add(menu);
AddParentMenus(menu, allMenus, authorizedMenus);
}
}
// 构建树形结构
var topLevelMenus = authorizedMenus
.Where(m => string.IsNullOrEmpty(m.ParentId))
.OrderBy(m => m.Sort)
.ToList();
foreach (var topMenu in topLevelMenus)
{
result.Add(ConvertToMenuModel(topMenu, allMenus, authorizedMenus));
}
return result;
}
添加角色
[HttpPost]
public BaseResult AddRole(AddRoleRequest request)
{
var result = new BaseResult();
// 禁止添加admin角色
if (request.Key == "admin")
{
result.Code = ResultCode.Error;
result.Message = "禁止新增admin角色";
return result;
}
// 检查角色Key唯一性
var existingRole = _dbService.FirstOrDefault<RoleEntity>(p =>
p.Enable && p.Id == request.Key);
if (existingRole != null)
{
result.Code = ResultCode.Error;
result.Message = $"角色Key已存在";
return result;
}
// 验证菜单ID有效性
var (isValid, errorMsg) = ValidateMenuIds(request.MenuIds);
if (!isValid)
{
result.Code = ResultCode.Error;
result.Message = errorMsg;
return result;
}
try
{
var newRole = new RoleEntity
{
Id = request.Key,
RoleName = request.Name,
MenuIdsList = JsonHelper.Serialize(request.MenuIds),
Enable = true
};
_dbService.Add(newRole);
}
catch (Exception ex)
{
result.Code = ResultCode.Error;
result.Message = $"新增失败:{ex.Message}";
}
return result;
}
菜单ID验证
private (bool IsValid, string ErrorMsg) ValidateMenuIds(List<string> menuIds)
{
if (!menuIds.Any())
return (false, "至少选择一个菜单");
// 获取所有启用的菜单ID
var validMenuIds = _dbService.Where<MenuEntity>(p => p.Enable)
.Select(p => p.Id)
.ToList();
// 检查无效的菜单ID
var invalidIds = menuIds.Except(validMenuIds).ToList();
if (invalidIds.Any())
return (false, $"无效的菜单ID:{string.Join(",", invalidIds)}");
return (true, "");
}
3. 权限验证模块
获取当前用户菜单
[HttpGet]
[Authorize]
public BaseResult<UserMenuOutput> GetUserMenus()
{
var result = new BaseResult<UserMenuOutput>();
var menuOutput = new UserMenuOutput();
try
{
var currentUser = GetCurrentUserInfo();
var role = _dbService.FirstOrDefault<RoleEntity>(p => p.Id == currentUser.RoleId);
if (role == null)
{
result.Code = ResultCode.Error;
result.Message = "未找到用户角色";
return result;
}
if (string.IsNullOrEmpty(role.MenuIdsList))
{
menuOutput.Menus = new List<MenuModel>();
result.Data = menuOutput;
return result;
}
// 解析用户有权限的菜单ID
var authorizedMenuIds = JsonHelper.Deserialize<List<string>>(role.MenuIdsList) ?? new List<string>();
// 构建用户菜单树
var allMenus = _dbService.Where<MenuEntity>(p => p.Enable).ToList();
var authorizedMenus = GetAuthorizedMenusWithParents(allMenus, authorizedMenuIds);
var menuTree = BuildMenuTree(authorizedMenus, "");
menuOutput.Menus = menuTree.Select(node => new MenuModel
{
Id = node.Id,
Name = node.Name,
Text = node.Text,
Hide = node.IsHide,
Pure = node.IsPure,
Link = node.Link,
File = node.FileUrl,
Sort = node.Sort,
Children = ConvertToMenuChildren(node.Children)
}).ToList();
result.Data = menuOutput;
}
catch (Exception ex)
{
result.Code = ResultCode.Error;
result.Message = $"获取菜单失败:{ex.Message}";
}
return result;
}
🔍 核心算法解析
1. 菜单树构建算法
// 递归构建菜单树
private List<MenuTreeNode> BuildMenuTree(List<MenuEntity> menus, string parentId)
{
return menus
.Where(m => m.ParentId == parentId)
.OrderBy(m => m.Sort)
.Select(m => new MenuTreeNode
{
// 设置节点属性
Children = BuildMenuTree(menus, m.Id) // 递归构建子节点
})
.ToList();
}
2. 权限菜单补全算法
// 补全父级菜单确保树形完整
private void AddParentMenus(MenuEntity menu, List<MenuEntity> allMenus, HashSet<MenuEntity> authorizedMenus)
{
if (string.IsNullOrEmpty(menu.ParentId)) return;
var parentMenu = allMenus.FirstOrDefault(m => m.Id == menu.ParentId);
if (parentMenu != null && !authorizedMenus.Contains(parentMenu))
{
authorizedMenus.Add(parentMenu);
AddParentMenus(parentMenu, allMenus, authorizedMenus); // 递归补全祖父级
}
}
⚠️ 安全与验证
1. 权限控制
-
非admin角色不能操作admin角色
-
用户只能查看和操作自己有权限的菜单
-
防止循环引用验证
2. 数据验证
-
菜单名称唯一性验证
-
父菜单存在性验证
-
菜单ID有效性验证
-
防止循环引用检查
📝 使用说明
1. 菜单管理
-
支持菜单的增删改查操作
-
菜单支持树形结构展示
-
支持菜单排序、隐藏等属性设置
2. 角色管理
-
创建角色并分配菜单权限
-
支持权限的批量分配
-
自动构建权限菜单树
3. 权限验证
-
用户登录后获取有权限的菜单
-
前端根据权限动态显示菜单
-
后端接口进行权限验证
🔄 更新日志
-
v1.0 基础菜单和角色管理功能
-
v1.1 增加树形菜单支持
-
v1.2 优化权限验证逻辑
-
v1.3 增加安全验证和错误处理
注意:本文档已进行脱敏处理,实际代码中请根据具体业务需求调整实体名称和业务逻辑。
1313

被折叠的 条评论
为什么被折叠?



