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);
}
}
}
}