Commit 0268b0d0a66813891ececa229b6ea2e2e0fb74ca

Authored by 李曜臣
1 parent 557c9c0e

菜单权限模块实现

美国版/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  
... ...