Commit 0268b0d0a66813891ececa229b6ea2e2e0fb74ca
1 parent
557c9c0e
菜单权限模块实现
Showing
6 changed files
with
261 additions
and
60 deletions
美国版/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 |
| 7 | 7 | { |
| 8 | 8 | public string MenuName { get; set; } = string.Empty; |
| 9 | 9 | |
| 10 | - public Guid ParentId { get; set; } | |
| 10 | + /// <summary> | |
| 11 | + /// 父级ID(menu 表为字符串ID,可能是数字;根节点默认 0) | |
| 12 | + /// </summary> | |
| 13 | + public string ParentId { get; set; } = "0"; | |
| 11 | 14 | |
| 12 | 15 | public int MenuType { get; set; } |
| 13 | 16 | ... | ... |
美国版/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; |
| 5 | 5 | /// </summary> |
| 6 | 6 | public class RbacMenuGetListOutputDto |
| 7 | 7 | { |
| 8 | - public Guid Id { get; set; } | |
| 8 | + public string Id { get; set; } = string.Empty; | |
| 9 | 9 | |
| 10 | - public Guid ParentId { get; set; } | |
| 10 | + public string ParentId { get; set; } = string.Empty; | |
| 11 | 11 | |
| 12 | 12 | public string MenuName { get; set; } = string.Empty; |
| 13 | 13 | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuTreeDto.cs
0 → 100644
| 1 | +namespace FoodLabeling.Application.Contracts.Dtos.RbacMenu; | |
| 2 | + | |
| 3 | +/// <summary> | |
| 4 | +/// 权限树节点(返回菜单表全部字段) | |
| 5 | +/// </summary> | |
| 6 | +public class RbacMenuTreeDto | |
| 7 | +{ | |
| 8 | + public string Id { get; set; } = string.Empty; | |
| 9 | + | |
| 10 | + public bool IsDeleted { get; set; } | |
| 11 | + | |
| 12 | + public DateTime CreationTime { get; set; } | |
| 13 | + | |
| 14 | + public string? CreatorId { get; set; } | |
| 15 | + | |
| 16 | + public string? LastModifierId { get; set; } | |
| 17 | + | |
| 18 | + public DateTime? LastModificationTime { get; set; } | |
| 19 | + | |
| 20 | + public int OrderNum { get; set; } | |
| 21 | + | |
| 22 | + public bool State { get; set; } | |
| 23 | + | |
| 24 | + public string MenuName { get; set; } = string.Empty; | |
| 25 | + | |
| 26 | + public string? RouterName { get; set; } | |
| 27 | + | |
| 28 | + public int MenuType { get; set; } | |
| 29 | + | |
| 30 | + public string? PermissionCode { get; set; } | |
| 31 | + | |
| 32 | + public string ParentId { get; set; } = string.Empty; | |
| 33 | + | |
| 34 | + public string? MenuIcon { get; set; } | |
| 35 | + | |
| 36 | + public string? Router { get; set; } | |
| 37 | + | |
| 38 | + public bool IsLink { get; set; } | |
| 39 | + | |
| 40 | + public bool IsCache { get; set; } | |
| 41 | + | |
| 42 | + public bool IsShow { get; set; } | |
| 43 | + | |
| 44 | + public string? Remark { get; set; } | |
| 45 | + | |
| 46 | + public string? Component { get; set; } | |
| 47 | + | |
| 48 | + public int MenuSource { get; set; } | |
| 49 | + | |
| 50 | + public string? Query { get; set; } | |
| 51 | + | |
| 52 | + public string? ConcurrencyStamp { get; set; } | |
| 53 | + | |
| 54 | + public List<RbacMenuTreeDto>? Children { get; set; } | |
| 55 | +} | |
| 56 | + | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IRbacMenuAppService.cs
| 1 | 1 | using FoodLabeling.Application.Contracts.Dtos.RbacMenu; |
| 2 | -using FoodLabeling.Application.Contracts.Dtos.Common; | |
| 3 | -using Volo.Abp.Application.Dtos; | |
| 4 | 2 | using Volo.Abp.Application.Services; |
| 5 | 3 | |
| 6 | 4 | namespace FoodLabeling.Application.Contracts.IServices; |
| ... | ... | @@ -11,14 +9,14 @@ namespace FoodLabeling.Application.Contracts.IServices; |
| 11 | 9 | public interface IRbacMenuAppService : IApplicationService |
| 12 | 10 | { |
| 13 | 11 | /// <summary> |
| 14 | - /// 权限分页列表 | |
| 12 | + /// 权限列表(不分页) | |
| 15 | 13 | /// </summary> |
| 16 | - Task<PagedResultWithPageDto<RbacMenuGetListOutputDto>> GetListAsync(RbacMenuGetListInputVo input); | |
| 14 | + Task<List<RbacMenuGetListOutputDto>> GetListAsync(RbacMenuGetListInputVo input); | |
| 17 | 15 | |
| 18 | 16 | /// <summary> |
| 19 | 17 | /// 权限详情 |
| 20 | 18 | /// </summary> |
| 21 | - Task<RbacMenuGetListOutputDto> GetAsync(Guid id); | |
| 19 | + Task<RbacMenuGetListOutputDto> GetAsync(string id); | |
| 22 | 20 | |
| 23 | 21 | /// <summary> |
| 24 | 22 | /// 新增权限 |
| ... | ... | @@ -28,11 +26,17 @@ public interface IRbacMenuAppService : IApplicationService |
| 28 | 26 | /// <summary> |
| 29 | 27 | /// 编辑权限 |
| 30 | 28 | /// </summary> |
| 31 | - Task<RbacMenuGetListOutputDto> UpdateAsync(Guid id, RbacMenuUpdateInputVo input); | |
| 29 | + Task<RbacMenuGetListOutputDto> UpdateAsync(string id, RbacMenuUpdateInputVo input); | |
| 32 | 30 | |
| 33 | 31 | /// <summary> |
| 34 | 32 | /// 删除权限(逻辑删除) |
| 35 | 33 | /// </summary> |
| 36 | - Task DeleteAsync(List<Guid> ids); | |
| 34 | + Task DeleteAsync(List<string> ids); | |
| 35 | + | |
| 36 | + /// <summary> | |
| 37 | + /// 获取全部权限树(GET) | |
| 38 | + /// </summary> | |
| 39 | + /// <returns>树状权限列表</returns> | |
| 40 | + Task<List<RbacMenuTreeDto>> GetTreeAsync(); | |
| 37 | 41 | } |
| 38 | 42 | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/MenuDbEntity.cs
0 → 100644
| 1 | +using SqlSugar; | |
| 2 | + | |
| 3 | +namespace FoodLabeling.Application.Services.DbModels; | |
| 4 | + | |
| 5 | +/// <summary> | |
| 6 | +/// menu 表映射(兼容数字/字符串类型的 Id、ParentId) | |
| 7 | +/// </summary> | |
| 8 | +[SugarTable("menu")] | |
| 9 | +public class MenuDbEntity | |
| 10 | +{ | |
| 11 | + [SugarColumn(IsPrimaryKey = true)] | |
| 12 | + public string Id { get; set; } = string.Empty; | |
| 13 | + | |
| 14 | + public bool IsDeleted { get; set; } | |
| 15 | + | |
| 16 | + public DateTime CreationTime { get; set; } | |
| 17 | + | |
| 18 | + public string? CreatorId { get; set; } | |
| 19 | + | |
| 20 | + public string? LastModifierId { get; set; } | |
| 21 | + | |
| 22 | + public DateTime? LastModificationTime { get; set; } | |
| 23 | + | |
| 24 | + public int OrderNum { get; set; } | |
| 25 | + | |
| 26 | + public bool State { get; set; } | |
| 27 | + | |
| 28 | + public string MenuName { get; set; } = string.Empty; | |
| 29 | + | |
| 30 | + public string? RouterName { get; set; } | |
| 31 | + | |
| 32 | + public int MenuType { get; set; } | |
| 33 | + | |
| 34 | + public string? PermissionCode { get; set; } | |
| 35 | + | |
| 36 | + public string ParentId { get; set; } = "0"; | |
| 37 | + | |
| 38 | + public string? MenuIcon { get; set; } | |
| 39 | + | |
| 40 | + public string? Router { get; set; } | |
| 41 | + | |
| 42 | + public bool IsLink { get; set; } | |
| 43 | + | |
| 44 | + public bool IsCache { get; set; } | |
| 45 | + | |
| 46 | + public bool IsShow { get; set; } | |
| 47 | + | |
| 48 | + public string? Remark { get; set; } | |
| 49 | + | |
| 50 | + public string? Component { get; set; } | |
| 51 | + | |
| 52 | + public int MenuSource { get; set; } | |
| 53 | + | |
| 54 | + public string? Query { get; set; } | |
| 55 | + | |
| 56 | + public string? ConcurrencyStamp { get; set; } | |
| 57 | +} | |
| 58 | + | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs
| 1 | 1 | using FoodLabeling.Application.Contracts.Dtos.RbacMenu; |
| 2 | -using FoodLabeling.Application.Contracts.Dtos.Common; | |
| 3 | 2 | using FoodLabeling.Application.Contracts.IServices; |
| 3 | +using FoodLabeling.Application.Services.DbModels; | |
| 4 | 4 | using Microsoft.AspNetCore.Mvc; |
| 5 | 5 | using SqlSugar; |
| 6 | 6 | using Volo.Abp; |
| 7 | 7 | using Volo.Abp.Application.Services; |
| 8 | -using Volo.Abp.Domain.Entities; | |
| 9 | -using Yi.Framework.Rbac.Domain.Entities; | |
| 10 | 8 | using Yi.Framework.SqlSugarCore.Abstractions; |
| 11 | 9 | |
| 12 | 10 | namespace FoodLabeling.Application.Services; |
| ... | ... | @@ -16,57 +14,44 @@ namespace FoodLabeling.Application.Services; |
| 16 | 14 | /// </summary> |
| 17 | 15 | public class RbacMenuAppService : ApplicationService, IRbacMenuAppService |
| 18 | 16 | { |
| 19 | - private readonly ISqlSugarRepository<MenuAggregateRoot, Guid> _menuRepository; | |
| 17 | + private readonly ISqlSugarDbContext _dbContext; | |
| 20 | 18 | |
| 21 | - public RbacMenuAppService(ISqlSugarRepository<MenuAggregateRoot, Guid> menuRepository) | |
| 19 | + public RbacMenuAppService(ISqlSugarDbContext dbContext) | |
| 22 | 20 | { |
| 23 | - _menuRepository = menuRepository; | |
| 21 | + _dbContext = dbContext; | |
| 24 | 22 | } |
| 25 | 23 | |
| 26 | 24 | /// <inheritdoc /> |
| 27 | - public async Task<PagedResultWithPageDto<RbacMenuGetListOutputDto>> GetListAsync([FromQuery] RbacMenuGetListInputVo input) | |
| 25 | + public async Task<List<RbacMenuGetListOutputDto>> GetListAsync([FromQuery] RbacMenuGetListInputVo input) | |
| 28 | 26 | { |
| 29 | - RefAsync<int> total = 0; | |
| 30 | - | |
| 31 | - var query = _menuRepository._DbQueryable | |
| 27 | + var query = _dbContext.SqlSugarClient.Queryable<MenuDbEntity>() | |
| 32 | 28 | .Where(x => x.IsDeleted == false) |
| 33 | 29 | .WhereIF(!string.IsNullOrWhiteSpace(input.MenuName), x => x.MenuName.Contains(input.MenuName!.Trim())) |
| 34 | 30 | .WhereIF(input.State is not null, x => x.State == input.State) |
| 35 | - .WhereIF(input.MenuSource is not null, x => x.MenuSource == (Yi.Framework.Rbac.Domain.Shared.Enums.MenuSourceEnum)input.MenuSource!.Value) | |
| 31 | + .WhereIF(input.MenuSource is not null, x => x.MenuSource == input.MenuSource!.Value) | |
| 36 | 32 | .OrderBy(x => x.OrderNum, OrderByType.Desc); |
| 37 | 33 | |
| 38 | - var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total); | |
| 34 | + var entities = await query.ToListAsync(); | |
| 39 | 35 | |
| 40 | - var items = entities.Select(x => new RbacMenuGetListOutputDto | |
| 36 | + return entities.Select(x => new RbacMenuGetListOutputDto | |
| 41 | 37 | { |
| 42 | 38 | Id = x.Id, |
| 43 | 39 | ParentId = x.ParentId, |
| 44 | 40 | MenuName = x.MenuName ?? string.Empty, |
| 45 | 41 | PermissionCode = x.PermissionCode, |
| 46 | - MenuType = (int)x.MenuType, | |
| 47 | - MenuSource = (int)x.MenuSource, | |
| 42 | + MenuType = x.MenuType, | |
| 43 | + MenuSource = x.MenuSource, | |
| 48 | 44 | OrderNum = x.OrderNum, |
| 49 | 45 | State = x.State |
| 50 | 46 | }).ToList(); |
| 51 | - | |
| 52 | - var pageSize = input.MaxResultCount <= 0 ? items.Count : input.MaxResultCount; | |
| 53 | - var pageIndex = pageSize <= 0 ? 1 : (input.SkipCount / pageSize) + 1; | |
| 54 | - var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize); | |
| 55 | - | |
| 56 | - return new PagedResultWithPageDto<RbacMenuGetListOutputDto> | |
| 57 | - { | |
| 58 | - PageIndex = pageIndex, | |
| 59 | - PageSize = pageSize, | |
| 60 | - TotalCount = total, | |
| 61 | - TotalPages = totalPages, | |
| 62 | - Items = items | |
| 63 | - }; | |
| 64 | 47 | } |
| 65 | 48 | |
| 66 | 49 | /// <inheritdoc /> |
| 67 | - public async Task<RbacMenuGetListOutputDto> GetAsync(Guid id) | |
| 50 | + public async Task<RbacMenuGetListOutputDto> GetAsync(string id) | |
| 68 | 51 | { |
| 69 | - var entity = await _menuRepository.GetSingleAsync(x => x.Id == id && x.IsDeleted == false); | |
| 52 | + var entity = await _dbContext.SqlSugarClient.Queryable<MenuDbEntity>() | |
| 53 | + .Where(x => x.Id == id && x.IsDeleted == false) | |
| 54 | + .SingleAsync(); | |
| 70 | 55 | if (entity is null) |
| 71 | 56 | { |
| 72 | 57 | throw new UserFriendlyException("权限不存在"); |
| ... | ... | @@ -78,8 +63,8 @@ public class RbacMenuAppService : ApplicationService, IRbacMenuAppService |
| 78 | 63 | ParentId = entity.ParentId, |
| 79 | 64 | MenuName = entity.MenuName ?? string.Empty, |
| 80 | 65 | PermissionCode = entity.PermissionCode, |
| 81 | - MenuType = (int)entity.MenuType, | |
| 82 | - MenuSource = (int)entity.MenuSource, | |
| 66 | + MenuType = entity.MenuType, | |
| 67 | + MenuSource = entity.MenuSource, | |
| 83 | 68 | OrderNum = entity.OrderNum, |
| 84 | 69 | State = entity.State |
| 85 | 70 | }; |
| ... | ... | @@ -94,28 +79,35 @@ public class RbacMenuAppService : ApplicationService, IRbacMenuAppService |
| 94 | 79 | throw new UserFriendlyException("权限名称不能为空"); |
| 95 | 80 | } |
| 96 | 81 | |
| 97 | - var entity = new MenuAggregateRoot | |
| 82 | + var entity = new MenuDbEntity | |
| 98 | 83 | { |
| 84 | + Id = GuidGenerator.Create().ToString(), | |
| 99 | 85 | MenuName = name, |
| 100 | - ParentId = input.ParentId, | |
| 101 | - MenuType = (Yi.Framework.Rbac.Domain.Shared.Enums.MenuTypeEnum)input.MenuType, | |
| 102 | - MenuSource = (Yi.Framework.Rbac.Domain.Shared.Enums.MenuSourceEnum)input.MenuSource, | |
| 86 | + ParentId = string.IsNullOrWhiteSpace(input.ParentId) ? "0" : input.ParentId.Trim(), | |
| 87 | + MenuType = input.MenuType, | |
| 88 | + MenuSource = input.MenuSource, | |
| 103 | 89 | PermissionCode = input.PermissionCode?.Trim(), |
| 104 | 90 | Router = input.Router?.Trim(), |
| 105 | 91 | Component = input.Component?.Trim(), |
| 106 | 92 | OrderNum = input.OrderNum, |
| 107 | - State = input.State | |
| 93 | + State = input.State, | |
| 94 | + IsDeleted = false, | |
| 95 | + CreationTime = DateTime.Now, | |
| 96 | + IsCache = false, | |
| 97 | + IsLink = false, | |
| 98 | + IsShow = true, | |
| 99 | + ConcurrencyStamp = string.Empty | |
| 108 | 100 | }; |
| 109 | - EntityHelper.TrySetId(entity, () => GuidGenerator.Create()); | |
| 110 | - | |
| 111 | - await _menuRepository.InsertAsync(entity); | |
| 101 | + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); | |
| 112 | 102 | return await GetAsync(entity.Id); |
| 113 | 103 | } |
| 114 | 104 | |
| 115 | 105 | /// <inheritdoc /> |
| 116 | - public async Task<RbacMenuGetListOutputDto> UpdateAsync(Guid id, [FromBody] RbacMenuUpdateInputVo input) | |
| 106 | + public async Task<RbacMenuGetListOutputDto> UpdateAsync(string id, [FromBody] RbacMenuUpdateInputVo input) | |
| 117 | 107 | { |
| 118 | - var entity = await _menuRepository.GetSingleAsync(x => x.Id == id && x.IsDeleted == false); | |
| 108 | + var entity = await _dbContext.SqlSugarClient.Queryable<MenuDbEntity>() | |
| 109 | + .Where(x => x.Id == id && x.IsDeleted == false) | |
| 110 | + .SingleAsync(); | |
| 119 | 111 | if (entity is null) |
| 120 | 112 | { |
| 121 | 113 | throw new UserFriendlyException("权限不存在"); |
| ... | ... | @@ -128,30 +120,118 @@ public class RbacMenuAppService : ApplicationService, IRbacMenuAppService |
| 128 | 120 | } |
| 129 | 121 | |
| 130 | 122 | entity.MenuName = name; |
| 131 | - entity.ParentId = input.ParentId; | |
| 132 | - entity.MenuType = (Yi.Framework.Rbac.Domain.Shared.Enums.MenuTypeEnum)input.MenuType; | |
| 133 | - entity.MenuSource = (Yi.Framework.Rbac.Domain.Shared.Enums.MenuSourceEnum)input.MenuSource; | |
| 123 | + entity.ParentId = string.IsNullOrWhiteSpace(input.ParentId) ? "0" : input.ParentId.Trim(); | |
| 124 | + entity.MenuType = input.MenuType; | |
| 125 | + entity.MenuSource = input.MenuSource; | |
| 134 | 126 | entity.PermissionCode = input.PermissionCode?.Trim(); |
| 135 | 127 | entity.Router = input.Router?.Trim(); |
| 136 | 128 | entity.Component = input.Component?.Trim(); |
| 137 | 129 | entity.OrderNum = input.OrderNum; |
| 138 | 130 | entity.State = input.State; |
| 131 | + entity.LastModificationTime = DateTime.Now; | |
| 139 | 132 | |
| 140 | - await _menuRepository.UpdateAsync(entity); | |
| 133 | + await _dbContext.SqlSugarClient.Updateable(entity) | |
| 134 | + .Where(x => x.Id == entity.Id) | |
| 135 | + .ExecuteCommandAsync(); | |
| 141 | 136 | return await GetAsync(entity.Id); |
| 142 | 137 | } |
| 143 | 138 | |
| 144 | 139 | /// <inheritdoc /> |
| 145 | - public async Task DeleteAsync([FromBody] List<Guid> ids) | |
| 140 | + public async Task DeleteAsync([FromBody] List<string> ids) | |
| 146 | 141 | { |
| 147 | - var idList = ids?.Distinct().ToList() ?? new List<Guid>(); | |
| 142 | + var idList = ids?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList() ?? new List<string>(); | |
| 148 | 143 | if (idList.Count == 0) |
| 149 | 144 | { |
| 150 | 145 | return; |
| 151 | 146 | } |
| 152 | 147 | |
| 153 | - // 权限表为软删(ISoftDelete) | |
| 154 | - await _menuRepository.DeleteAsync(x => idList.Contains(x.Id)); | |
| 148 | + await _dbContext.SqlSugarClient.Updateable<MenuDbEntity>() | |
| 149 | + .SetColumns(x => new MenuDbEntity { IsDeleted = true }) | |
| 150 | + .Where(x => idList.Contains(x.Id)) | |
| 151 | + .ExecuteCommandAsync(); | |
| 152 | + } | |
| 153 | + | |
| 154 | + /// <inheritdoc /> | |
| 155 | + public async Task<List<RbacMenuTreeDto>> GetTreeAsync() | |
| 156 | + { | |
| 157 | + // 返回所有字段,但过滤逻辑删除数据 | |
| 158 | + var menus = await _dbContext.SqlSugarClient.Queryable<MenuDbEntity>() | |
| 159 | + .Where(x => x.IsDeleted == false) | |
| 160 | + .OrderBy(x => x.OrderNum, OrderByType.Desc) | |
| 161 | + .ToListAsync(); | |
| 162 | + | |
| 163 | + var nodes = menus.Select(m => new RbacMenuTreeDto | |
| 164 | + { | |
| 165 | + Id = m.Id, | |
| 166 | + IsDeleted = m.IsDeleted, | |
| 167 | + CreationTime = m.CreationTime, | |
| 168 | + CreatorId = m.CreatorId, | |
| 169 | + LastModifierId = m.LastModifierId, | |
| 170 | + LastModificationTime = m.LastModificationTime, | |
| 171 | + OrderNum = m.OrderNum, | |
| 172 | + State = m.State, | |
| 173 | + MenuName = m.MenuName ?? string.Empty, | |
| 174 | + RouterName = m.RouterName, | |
| 175 | + MenuType = m.MenuType, | |
| 176 | + PermissionCode = m.PermissionCode, | |
| 177 | + ParentId = m.ParentId, | |
| 178 | + MenuIcon = m.MenuIcon, | |
| 179 | + Router = m.Router, | |
| 180 | + IsLink = m.IsLink, | |
| 181 | + IsCache = m.IsCache, | |
| 182 | + IsShow = m.IsShow, | |
| 183 | + Remark = m.Remark, | |
| 184 | + Component = m.Component, | |
| 185 | + MenuSource = m.MenuSource, | |
| 186 | + Query = m.Query, | |
| 187 | + ConcurrencyStamp = m.ConcurrencyStamp, | |
| 188 | + Children = new List<RbacMenuTreeDto>() | |
| 189 | + }).ToList(); | |
| 190 | + | |
| 191 | + // TreeHelper 仅支持 Guid Id/ParentId,这里使用字符串 ParentId 自行构建树 | |
| 192 | + var nodeById = nodes | |
| 193 | + .Where(x => !string.IsNullOrWhiteSpace(x.Id)) | |
| 194 | + .GroupBy(x => x.Id) | |
| 195 | + .ToDictionary(g => g.Key, g => g.First()); | |
| 196 | + | |
| 197 | + foreach (var node in nodes) | |
| 198 | + { | |
| 199 | + node.Children ??= new List<RbacMenuTreeDto>(); | |
| 200 | + var parentId = string.IsNullOrWhiteSpace(node.ParentId) ? "0" : node.ParentId.Trim(); | |
| 201 | + if (parentId == "0" || parentId == "00000000-0000-0000-0000-000000000000") | |
| 202 | + { | |
| 203 | + continue; | |
| 204 | + } | |
| 205 | + | |
| 206 | + if (nodeById.TryGetValue(parentId, out var parent)) | |
| 207 | + { | |
| 208 | + parent.Children ??= new List<RbacMenuTreeDto>(); | |
| 209 | + parent.Children.Add(node); | |
| 210 | + } | |
| 211 | + } | |
| 212 | + | |
| 213 | + var roots = nodes | |
| 214 | + .Where(n => | |
| 215 | + { | |
| 216 | + var pid = string.IsNullOrWhiteSpace(n.ParentId) ? "0" : n.ParentId.Trim(); | |
| 217 | + return pid == "0" || pid == "00000000-0000-0000-0000-000000000000" || !nodeById.ContainsKey(pid); | |
| 218 | + }) | |
| 219 | + .ToList(); | |
| 220 | + | |
| 221 | + SortTree(roots); | |
| 222 | + return roots; | |
| 223 | + } | |
| 224 | + | |
| 225 | + private static void SortTree(List<RbacMenuTreeDto> nodes) | |
| 226 | + { | |
| 227 | + nodes.Sort((a, b) => b.OrderNum.CompareTo(a.OrderNum)); | |
| 228 | + foreach (var node in nodes) | |
| 229 | + { | |
| 230 | + if (node.Children is { Count: > 0 }) | |
| 231 | + { | |
| 232 | + SortTree(node.Children); | |
| 233 | + } | |
| 234 | + } | |
| 155 | 235 | } |
| 156 | 236 | } |
| 157 | 237 | ... | ... |