From 0268b0d0a66813891ececa229b6ea2e2e0fb74ca Mon Sep 17 00:00:00 2001
From: 李曜臣
Date: Wed, 18 Mar 2026 17:57:10 +0800
Subject: [PATCH] 菜单权限模块实现
---
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuCreateInputVo.cs | 5 ++++-
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs | 4 ++--
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuTreeDto.cs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IRbacMenuAppService.cs | 18 +++++++++++-------
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/MenuDbEntity.cs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------
6 files changed, 261 insertions(+), 60 deletions(-)
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuTreeDto.cs
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/MenuDbEntity.cs
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuCreateInputVo.cs
index ee5d658..1969e87 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuCreateInputVo.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuCreateInputVo.cs
@@ -7,7 +7,10 @@ public class RbacMenuCreateInputVo
{
public string MenuName { get; set; } = string.Empty;
- public Guid ParentId { get; set; }
+ ///
+ /// 父级ID(menu 表为字符串ID,可能是数字;根节点默认 0)
+ ///
+ public string ParentId { get; set; } = "0";
public int MenuType { get; set; }
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs
index 6970d00..4a59a70 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs
@@ -5,9 +5,9 @@ namespace FoodLabeling.Application.Contracts.Dtos.RbacMenu;
///
public class RbacMenuGetListOutputDto
{
- public Guid Id { get; set; }
+ public string Id { get; set; } = string.Empty;
- public Guid ParentId { get; set; }
+ public string ParentId { get; set; } = string.Empty;
public string MenuName { get; set; } = string.Empty;
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuTreeDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuTreeDto.cs
new file mode 100644
index 0000000..c0fbe0f
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuTreeDto.cs
@@ -0,0 +1,56 @@
+namespace FoodLabeling.Application.Contracts.Dtos.RbacMenu;
+
+///
+/// 权限树节点(返回菜单表全部字段)
+///
+public class RbacMenuTreeDto
+{
+ public string Id { get; set; } = string.Empty;
+
+ public bool IsDeleted { get; set; }
+
+ public DateTime CreationTime { get; set; }
+
+ public string? CreatorId { get; set; }
+
+ public string? LastModifierId { get; set; }
+
+ public DateTime? LastModificationTime { get; set; }
+
+ public int OrderNum { get; set; }
+
+ public bool State { get; set; }
+
+ public string MenuName { get; set; } = string.Empty;
+
+ public string? RouterName { get; set; }
+
+ public int MenuType { get; set; }
+
+ public string? PermissionCode { get; set; }
+
+ public string ParentId { get; set; } = string.Empty;
+
+ public string? MenuIcon { get; set; }
+
+ public string? Router { get; set; }
+
+ public bool IsLink { get; set; }
+
+ public bool IsCache { get; set; }
+
+ public bool IsShow { get; set; }
+
+ public string? Remark { get; set; }
+
+ public string? Component { get; set; }
+
+ public int MenuSource { get; set; }
+
+ public string? Query { get; set; }
+
+ public string? ConcurrencyStamp { get; set; }
+
+ public List? Children { get; set; }
+}
+
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IRbacMenuAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IRbacMenuAppService.cs
index 2eba979..cf0e7c7 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IRbacMenuAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IRbacMenuAppService.cs
@@ -1,6 +1,4 @@
using FoodLabeling.Application.Contracts.Dtos.RbacMenu;
-using FoodLabeling.Application.Contracts.Dtos.Common;
-using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace FoodLabeling.Application.Contracts.IServices;
@@ -11,14 +9,14 @@ namespace FoodLabeling.Application.Contracts.IServices;
public interface IRbacMenuAppService : IApplicationService
{
///
- /// 权限分页列表
+ /// 权限列表(不分页)
///
- Task> GetListAsync(RbacMenuGetListInputVo input);
+ Task> GetListAsync(RbacMenuGetListInputVo input);
///
/// 权限详情
///
- Task GetAsync(Guid id);
+ Task GetAsync(string id);
///
/// 新增权限
@@ -28,11 +26,17 @@ public interface IRbacMenuAppService : IApplicationService
///
/// 编辑权限
///
- Task UpdateAsync(Guid id, RbacMenuUpdateInputVo input);
+ Task UpdateAsync(string id, RbacMenuUpdateInputVo input);
///
/// 删除权限(逻辑删除)
///
- Task DeleteAsync(List ids);
+ Task DeleteAsync(List ids);
+
+ ///
+ /// 获取全部权限树(GET)
+ ///
+ /// 树状权限列表
+ Task> GetTreeAsync();
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/MenuDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/MenuDbEntity.cs
new file mode 100644
index 0000000..b5ed547
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/MenuDbEntity.cs
@@ -0,0 +1,58 @@
+using SqlSugar;
+
+namespace FoodLabeling.Application.Services.DbModels;
+
+///
+/// menu 表映射(兼容数字/字符串类型的 Id、ParentId)
+///
+[SugarTable("menu")]
+public class MenuDbEntity
+{
+ [SugarColumn(IsPrimaryKey = true)]
+ public string Id { get; set; } = string.Empty;
+
+ public bool IsDeleted { get; set; }
+
+ public DateTime CreationTime { get; set; }
+
+ public string? CreatorId { get; set; }
+
+ public string? LastModifierId { get; set; }
+
+ public DateTime? LastModificationTime { get; set; }
+
+ public int OrderNum { get; set; }
+
+ public bool State { get; set; }
+
+ public string MenuName { get; set; } = string.Empty;
+
+ public string? RouterName { get; set; }
+
+ public int MenuType { get; set; }
+
+ public string? PermissionCode { get; set; }
+
+ public string ParentId { get; set; } = "0";
+
+ public string? MenuIcon { get; set; }
+
+ public string? Router { get; set; }
+
+ public bool IsLink { get; set; }
+
+ public bool IsCache { get; set; }
+
+ public bool IsShow { get; set; }
+
+ public string? Remark { get; set; }
+
+ public string? Component { get; set; }
+
+ public int MenuSource { get; set; }
+
+ public string? Query { get; set; }
+
+ public string? ConcurrencyStamp { get; set; }
+}
+
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs
index f4f8a4a..e23abcd 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs
@@ -1,12 +1,10 @@
using FoodLabeling.Application.Contracts.Dtos.RbacMenu;
-using FoodLabeling.Application.Contracts.Dtos.Common;
using FoodLabeling.Application.Contracts.IServices;
+using FoodLabeling.Application.Services.DbModels;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Services;
-using Volo.Abp.Domain.Entities;
-using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace FoodLabeling.Application.Services;
@@ -16,57 +14,44 @@ namespace FoodLabeling.Application.Services;
///
public class RbacMenuAppService : ApplicationService, IRbacMenuAppService
{
- private readonly ISqlSugarRepository _menuRepository;
+ private readonly ISqlSugarDbContext _dbContext;
- public RbacMenuAppService(ISqlSugarRepository menuRepository)
+ public RbacMenuAppService(ISqlSugarDbContext dbContext)
{
- _menuRepository = menuRepository;
+ _dbContext = dbContext;
}
///
- public async Task> GetListAsync([FromQuery] RbacMenuGetListInputVo input)
+ public async Task> GetListAsync([FromQuery] RbacMenuGetListInputVo input)
{
- RefAsync total = 0;
-
- var query = _menuRepository._DbQueryable
+ var query = _dbContext.SqlSugarClient.Queryable()
.Where(x => x.IsDeleted == false)
.WhereIF(!string.IsNullOrWhiteSpace(input.MenuName), x => x.MenuName.Contains(input.MenuName!.Trim()))
.WhereIF(input.State is not null, x => x.State == input.State)
- .WhereIF(input.MenuSource is not null, x => x.MenuSource == (Yi.Framework.Rbac.Domain.Shared.Enums.MenuSourceEnum)input.MenuSource!.Value)
+ .WhereIF(input.MenuSource is not null, x => x.MenuSource == input.MenuSource!.Value)
.OrderBy(x => x.OrderNum, OrderByType.Desc);
- var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
+ var entities = await query.ToListAsync();
- var items = entities.Select(x => new RbacMenuGetListOutputDto
+ return entities.Select(x => new RbacMenuGetListOutputDto
{
Id = x.Id,
ParentId = x.ParentId,
MenuName = x.MenuName ?? string.Empty,
PermissionCode = x.PermissionCode,
- MenuType = (int)x.MenuType,
- MenuSource = (int)x.MenuSource,
+ MenuType = x.MenuType,
+ MenuSource = x.MenuSource,
OrderNum = x.OrderNum,
State = x.State
}).ToList();
-
- var pageSize = input.MaxResultCount <= 0 ? items.Count : input.MaxResultCount;
- var pageIndex = pageSize <= 0 ? 1 : (input.SkipCount / pageSize) + 1;
- var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize);
-
- return new PagedResultWithPageDto
- {
- PageIndex = pageIndex,
- PageSize = pageSize,
- TotalCount = total,
- TotalPages = totalPages,
- Items = items
- };
}
///
- public async Task GetAsync(Guid id)
+ public async Task GetAsync(string id)
{
- var entity = await _menuRepository.GetSingleAsync(x => x.Id == id && x.IsDeleted == false);
+ var entity = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.Id == id && x.IsDeleted == false)
+ .SingleAsync();
if (entity is null)
{
throw new UserFriendlyException("权限不存在");
@@ -78,8 +63,8 @@ public class RbacMenuAppService : ApplicationService, IRbacMenuAppService
ParentId = entity.ParentId,
MenuName = entity.MenuName ?? string.Empty,
PermissionCode = entity.PermissionCode,
- MenuType = (int)entity.MenuType,
- MenuSource = (int)entity.MenuSource,
+ MenuType = entity.MenuType,
+ MenuSource = entity.MenuSource,
OrderNum = entity.OrderNum,
State = entity.State
};
@@ -94,28 +79,35 @@ public class RbacMenuAppService : ApplicationService, IRbacMenuAppService
throw new UserFriendlyException("权限名称不能为空");
}
- var entity = new MenuAggregateRoot
+ var entity = new MenuDbEntity
{
+ Id = GuidGenerator.Create().ToString(),
MenuName = name,
- ParentId = input.ParentId,
- MenuType = (Yi.Framework.Rbac.Domain.Shared.Enums.MenuTypeEnum)input.MenuType,
- MenuSource = (Yi.Framework.Rbac.Domain.Shared.Enums.MenuSourceEnum)input.MenuSource,
+ ParentId = string.IsNullOrWhiteSpace(input.ParentId) ? "0" : input.ParentId.Trim(),
+ MenuType = input.MenuType,
+ MenuSource = input.MenuSource,
PermissionCode = input.PermissionCode?.Trim(),
Router = input.Router?.Trim(),
Component = input.Component?.Trim(),
OrderNum = input.OrderNum,
- State = input.State
+ State = input.State,
+ IsDeleted = false,
+ CreationTime = DateTime.Now,
+ IsCache = false,
+ IsLink = false,
+ IsShow = true,
+ ConcurrencyStamp = string.Empty
};
- EntityHelper.TrySetId(entity, () => GuidGenerator.Create());
-
- await _menuRepository.InsertAsync(entity);
+ await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
return await GetAsync(entity.Id);
}
///
- public async Task UpdateAsync(Guid id, [FromBody] RbacMenuUpdateInputVo input)
+ public async Task UpdateAsync(string id, [FromBody] RbacMenuUpdateInputVo input)
{
- var entity = await _menuRepository.GetSingleAsync(x => x.Id == id && x.IsDeleted == false);
+ var entity = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.Id == id && x.IsDeleted == false)
+ .SingleAsync();
if (entity is null)
{
throw new UserFriendlyException("权限不存在");
@@ -128,30 +120,118 @@ public class RbacMenuAppService : ApplicationService, IRbacMenuAppService
}
entity.MenuName = name;
- entity.ParentId = input.ParentId;
- entity.MenuType = (Yi.Framework.Rbac.Domain.Shared.Enums.MenuTypeEnum)input.MenuType;
- entity.MenuSource = (Yi.Framework.Rbac.Domain.Shared.Enums.MenuSourceEnum)input.MenuSource;
+ entity.ParentId = string.IsNullOrWhiteSpace(input.ParentId) ? "0" : input.ParentId.Trim();
+ entity.MenuType = input.MenuType;
+ entity.MenuSource = input.MenuSource;
entity.PermissionCode = input.PermissionCode?.Trim();
entity.Router = input.Router?.Trim();
entity.Component = input.Component?.Trim();
entity.OrderNum = input.OrderNum;
entity.State = input.State;
+ entity.LastModificationTime = DateTime.Now;
- await _menuRepository.UpdateAsync(entity);
+ await _dbContext.SqlSugarClient.Updateable(entity)
+ .Where(x => x.Id == entity.Id)
+ .ExecuteCommandAsync();
return await GetAsync(entity.Id);
}
///
- public async Task DeleteAsync([FromBody] List ids)
+ public async Task DeleteAsync([FromBody] List ids)
{
- var idList = ids?.Distinct().ToList() ?? new List();
+ var idList = ids?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList() ?? new List();
if (idList.Count == 0)
{
return;
}
- // 权限表为软删(ISoftDelete)
- await _menuRepository.DeleteAsync(x => idList.Contains(x.Id));
+ await _dbContext.SqlSugarClient.Updateable()
+ .SetColumns(x => new MenuDbEntity { IsDeleted = true })
+ .Where(x => idList.Contains(x.Id))
+ .ExecuteCommandAsync();
+ }
+
+ ///
+ public async Task> GetTreeAsync()
+ {
+ // 返回所有字段,但过滤逻辑删除数据
+ var menus = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.IsDeleted == false)
+ .OrderBy(x => x.OrderNum, OrderByType.Desc)
+ .ToListAsync();
+
+ var nodes = menus.Select(m => new RbacMenuTreeDto
+ {
+ Id = m.Id,
+ IsDeleted = m.IsDeleted,
+ CreationTime = m.CreationTime,
+ CreatorId = m.CreatorId,
+ LastModifierId = m.LastModifierId,
+ LastModificationTime = m.LastModificationTime,
+ OrderNum = m.OrderNum,
+ State = m.State,
+ MenuName = m.MenuName ?? string.Empty,
+ RouterName = m.RouterName,
+ MenuType = m.MenuType,
+ PermissionCode = m.PermissionCode,
+ ParentId = m.ParentId,
+ MenuIcon = m.MenuIcon,
+ Router = m.Router,
+ IsLink = m.IsLink,
+ IsCache = m.IsCache,
+ IsShow = m.IsShow,
+ Remark = m.Remark,
+ Component = m.Component,
+ MenuSource = m.MenuSource,
+ Query = m.Query,
+ ConcurrencyStamp = m.ConcurrencyStamp,
+ Children = new List()
+ }).ToList();
+
+ // TreeHelper 仅支持 Guid Id/ParentId,这里使用字符串 ParentId 自行构建树
+ var nodeById = nodes
+ .Where(x => !string.IsNullOrWhiteSpace(x.Id))
+ .GroupBy(x => x.Id)
+ .ToDictionary(g => g.Key, g => g.First());
+
+ foreach (var node in nodes)
+ {
+ node.Children ??= new List();
+ var parentId = string.IsNullOrWhiteSpace(node.ParentId) ? "0" : node.ParentId.Trim();
+ if (parentId == "0" || parentId == "00000000-0000-0000-0000-000000000000")
+ {
+ continue;
+ }
+
+ if (nodeById.TryGetValue(parentId, out var parent))
+ {
+ parent.Children ??= new List();
+ parent.Children.Add(node);
+ }
+ }
+
+ var roots = nodes
+ .Where(n =>
+ {
+ var pid = string.IsNullOrWhiteSpace(n.ParentId) ? "0" : n.ParentId.Trim();
+ return pid == "0" || pid == "00000000-0000-0000-0000-000000000000" || !nodeById.ContainsKey(pid);
+ })
+ .ToList();
+
+ SortTree(roots);
+ return roots;
+ }
+
+ private static void SortTree(List nodes)
+ {
+ nodes.Sort((a, b) => b.OrderNum.CompareTo(a.OrderNum));
+ foreach (var node in nodes)
+ {
+ if (node.Children is { Count: > 0 })
+ {
+ SortTree(node.Children);
+ }
+ }
}
}
--
libgit2 0.21.4