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("用户不存在");
}
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 roleIds = await _dbContext.SqlSugarClient.Queryable()
.Where(x => x.UserId == userId)
.Select(x => x.RoleId)
.ToListAsync();
var roleIdStrs = roleIds.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”。
// 这里直接使用 JWT 中的角色码(CurrentUser.Roles)返回,避免触发过滤器。
var roleCodes = CurrentUser.Roles?.ToList() ?? new List();
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)
};
}
///
[HttpPost]
public virtual async Task LogoutAsync()
{
if (!CurrentUser.Id.HasValue)
{
return false;
}
await _userCache.RemoveAsync(new UserInfoCacheKey(CurrentUser.Id.Value));
return true;
}
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);
}
}
}
}