using FoodLabeling.Application.Contracts.Dtos.AuthSession; using FoodLabeling.Application.Contracts.IServices; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.Caching; using FoodLabeling.Application.Services.DbModels; using Yi.Framework.Rbac.Domain.Entities; using Yi.Framework.Rbac.Domain.Shared.Caches; using Yi.Framework.Rbac.Domain.Shared.Consts; using Yi.Framework.SqlSugarCore.Abstractions; namespace FoodLabeling.Application.Services; /// /// 当前登录会话:菜单权限与退出 /// [Authorize] public class AuthSessionAppService : ApplicationService, IAuthSessionAppService { private readonly IDistributedCache _userCache; private readonly ISqlSugarDbContext _dbContext; private readonly ISqlSugarRepository _userRepository; public AuthSessionAppService( ISqlSugarDbContext dbContext, ISqlSugarRepository userRepository, IDistributedCache userCache) { _dbContext = dbContext; _userRepository = userRepository; _userCache = userCache; } /// public virtual async Task GetMyMenusAsync() { if (!CurrentUser.Id.HasValue) { throw new UserFriendlyException("用户未登录"); } // 避免走 UserManager.GetInfoAsync -> UserRepository.GetUserAllInfoAsync 的导航加载 // 这里直接按 UserRole/RoleMenu/Menu 表关联查询当前用户可见菜单与权限码 var userId = CurrentUser.Id.Value; var user = await _userRepository.GetByIdAsync(userId); if (user is null || user.IsDeleted) { throw new UserFriendlyException("用户不存在"); } var userRoleIds = await _dbContext.SqlSugarClient.Queryable() .Where(x => x.UserId == userId) .Select(x => x.RoleId) .ToListAsync(); var distinctUserRoleIds = userRoleIds.Distinct().ToList(); List menus; if (UserConst.Admin.Equals(user.UserName)) { // MenuAggregateRoot(ParentId 为 Guid) 无法兼容 menu.ParentId=0/字符串:这里统一用 MenuDbEntity menus = await _dbContext.SqlSugarClient.Queryable() .Where(x => x.IsDeleted == false) .ToListAsync(); } else { var roleIdStrs = distinctUserRoleIds.Select(x => x.ToString()).Distinct().ToList(); if (roleIdStrs.Count == 0) { menus = new List(); } else { var menuIds = await _dbContext.SqlSugarClient.Queryable() .Where(x => roleIdStrs.Contains(x.RoleId)) .Select(x => x.MenuId) .Distinct() .ToListAsync(); menus = menuIds.Count == 0 ? new List() : await _dbContext.SqlSugarClient.Queryable() .Where(x => x.IsDeleted == false && menuIds.Contains(x.Id)) .ToListAsync(); } } var menuNodes = menus .Select(MapToNode) .OrderByDescending(x => x.OrderNum) .ThenBy(x => x.MenuName) .ToList(); // 注意:经仓储查询 RoleAggregateRoot 会触发 YiRbacDbContext 的 IDataPermission 过滤, // 其表达式包含 roleInfo.Select(...).Contains(...),在当前 SqlSugar 版本下会报“不支持 Select”。 // 角色展示名使用 RoleDbEntity 直查 Role 表;角色编码列表仍用 JWT(CurrentUser.Roles),与原先 RoleCodes 行为一致。 var roleCodes = CurrentUser.Roles?.ToList() ?? new List(); var roleDisplay = await BuildRoleDisplayAsync(distinctUserRoleIds, roleCodes); var permissionCodes = menuNodes .Where(x => !string.IsNullOrWhiteSpace(x.PermissionCode)) .Select(x => x.PermissionCode!.Trim()) .Distinct() .OrderBy(x => x) .ToList(); return new CurrentUserMenuPermissionsOutputDto { User = new CurrentUserBriefDto { Id = user.Id, UserName = user.UserName, Nick = user.Nick, Email = user.Email, Icon = user.Icon }, RoleCodes = roleCodes.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().OrderBy(x => x).ToList(), PermissionCodes = permissionCodes, Menus = BuildMenuTree(menuNodes), LastUpdated = user.LastModificationTime, Role = roleDisplay, FullName = BuildFullName(user) }; } /// [HttpPost] public virtual async Task LogoutAsync() { if (!CurrentUser.Id.HasValue) { return false; } await _userCache.RemoveAsync(new UserInfoCacheKey(CurrentUser.Id.Value)); return true; } private static string BuildFullName(UserAggregateRoot user) { if (!string.IsNullOrWhiteSpace(user.Name)) { return user.Name.Trim(); } if (!string.IsNullOrWhiteSpace(user.Nick)) { return user.Nick.Trim(); } return user.UserName ?? string.Empty; } private async Task BuildRoleDisplayAsync( IReadOnlyList userRoleIds, IReadOnlyList jwtRoleCodes) { var names = new List(); if (userRoleIds.Count > 0) { names = await _dbContext.SqlSugarClient.Queryable() .Where(r => !r.IsDeleted && userRoleIds.Contains(r.Id)) .OrderBy(r => r.RoleName) .Select(r => r.RoleName) .ToListAsync(); } if (names.Count == 0 && jwtRoleCodes.Count > 0) { var codes = jwtRoleCodes .Where(x => !string.IsNullOrWhiteSpace(x)) .Select(x => x.Trim()) .Distinct() .ToList(); names = await _dbContext.SqlSugarClient.Queryable() .Where(r => !r.IsDeleted && codes.Contains(r.RoleCode)) .OrderBy(r => r.RoleName) .Select(r => r.RoleName) .ToListAsync(); } var distinctNames = names .Where(n => !string.IsNullOrWhiteSpace(n)) .Select(n => n.Trim()) .Distinct() .ToList(); if (distinctNames.Count > 0) { return string.Join(", ", distinctNames); } var fallbackCodes = jwtRoleCodes .Where(x => !string.IsNullOrWhiteSpace(x)) .Select(x => x.Trim()) .Distinct() .ToList(); return fallbackCodes.Count > 0 ? string.Join(", ", fallbackCodes) : string.Empty; } private static List BuildMenuTree(List flat) { var nodes = flat .GroupBy(x => x.Id) .Select(g => g.First()) .ToList(); var byId = nodes.ToDictionary(n => n.Id, n => n); foreach (var n in nodes) { n.Children = new List(); } var roots = new List(); foreach (var n in nodes) { var pid = string.IsNullOrWhiteSpace(n.ParentId) ? "0" : n.ParentId.Trim(); if (pid == "0" || pid == "00000000-0000-0000-0000-000000000000") { roots.Add(n); continue; } if (byId.TryGetValue(pid, out var parent)) { parent.Children.Add(n); } else { roots.Add(n); } } SortMenuTree(roots); return roots; } private static CurrentUserMenuNodeDto MapToNode(MenuDbEntity m) { return new CurrentUserMenuNodeDto { Id = m.Id, ParentId = string.IsNullOrWhiteSpace(m.ParentId) ? "0" : m.ParentId.Trim(), MenuName = m.MenuName ?? string.Empty, RouterName = m.RouterName, Router = m.Router, PermissionCode = m.PermissionCode, MenuType = m.MenuType, MenuSource = m.MenuSource, OrderNum = m.OrderNum, State = m.State, MenuIcon = m.MenuIcon, Component = m.Component, IsLink = m.IsLink, IsCache = m.IsCache, IsShow = m.IsShow, Query = m.Query, Remark = m.Remark }; } private static void SortMenuTree(List level) { level.Sort((a, b) => { var o = b.OrderNum.CompareTo(a.OrderNum); return o != 0 ? o : string.Compare(a.MenuName, b.MenuName, StringComparison.Ordinal); }); foreach (var n in level) { if (n.Children.Count > 0) { SortMenuTree(n.Children); } } } }