using YD_Event.Application.Auth; using YD_Event.Application.Menu.Dtos; using YD_Event.Core.Options; using Microsoft.Extensions.Options; using Microsoft.OpenApi; namespace YD_Event.Application.Menu; /// /// 系统菜单管理 /// public class SysMenuService : BaseService, ITransient { private readonly ISqlSugarRepository _sysMenuRepository; private readonly IEasyCachingProvider _easyCachingProvider; private readonly AuthManager _authManager; private readonly ISqlSugarRepository _sysUserRoleRepository; private readonly IOptionsMonitor _roleOptionsMonitor; public SysMenuService(ISqlSugarRepository sysMenuRepository, IEasyCachingProvider easyCachingProvider, AuthManager authManager, ISqlSugarRepository sysUserRoleRepository, IOptionsMonitor roleOptionsMonitor) : base(sysMenuRepository) { _sysMenuRepository = sysMenuRepository; _easyCachingProvider = easyCachingProvider; _authManager = authManager; _sysUserRoleRepository = sysUserRoleRepository; _roleOptionsMonitor = roleOptionsMonitor; } /// /// 菜单列表查询 /// /// /// [DisplayName("菜单列表查询")] [HttpGet] public async Task> Page([FromQuery] string name) { if (_authManager.IsSuperAdmin) { var q1 = _sysMenuRepository.AsQueryable() .OrderBy(x => x.Sort) .OrderBy(x => x.Id); if (!string.IsNullOrWhiteSpace(name)) { var list = await q1.Where(x => x.Name.Contains(name)).ToListAsync(); return list.Adapt>(); } var menus = await q1.ToTreeAsync(x => x.Children, x => x.ParentId, null); return menus.Adapt>(); } else { long userId = _authManager.UserId; var q2 = _sysMenuRepository.AsQueryable().InnerJoin((menu, roleMenu) => menu.Id == roleMenu.MenuId) .InnerJoin((menu, roleMenu, role) => roleMenu.RoleId == role.Id) .InnerJoin((menu, roleMenu, role, userRole) => role.Id == userRole.RoleId) .Where((menu, roleMenu, role, userRole) => role.Status == AvailabilityStatus.Enable && userRole.UserId == userId); if (!string.IsNullOrWhiteSpace(name)) { var list = await q2.Where(menu => menu.Name.Contains(name)).Distinct().OrderBy(menu => menu.Sort).OrderBy(menu => menu.Id).ToListAsync(); return list.Adapt>(); } var menuIdList = await q2.Select(menu => menu.Id).ToListAsync(); var array = menuIdList.Select(x => x as object).ToArray(); var menus = await _sysMenuRepository.AsQueryable() .Where(x => x.Status == AvailabilityStatus.Enable) .OrderBy(x => x.Sort) .OrderBy(x => x.Id) .ToTreeAsync(x => x.Children, x => x.ParentId, null, array); return menus.Adapt>(); } } /// /// 添加菜单/按钮 /// /// [DisplayName("添加菜单/按钮")] [HttpPost("add")] public async Task AddMenu(AddSysMenuInput dto) { SysMenu sysMenu = dto.Adapt(); if (sysMenu.Type == MenuType.Button || sysMenu.Type == MenuType.DataScope) { sysMenu.Link = sysMenu.Icon = sysMenu.Component = sysMenu.Path = sysMenu.Redirect = sysMenu.RouteName = null; } else { if (await _sysMenuRepository.IsAnyAsync(x => x.RouteName.ToLower() == dto.RouteName.ToLower())) { throw Oops.Bah("路由名称已存在"); } sysMenu.Code = null; } await _sysMenuRepository.InsertAsync(sysMenu); await _easyCachingProvider.RemoveByPrefixAsync(CacheConst.PermissionKey); } /// /// 修改菜单/按钮 /// /// /// [DisplayName("更新菜单/按钮")] [HttpPut("edit")] public async Task UpdateMenu(UpdateSysMenuInput dto) { SysMenu sysMenu = await _sysMenuRepository.GetByIdAsync(dto.Id); if (sysMenu == null) { throw Oops.Bah("无线参数"); } if (dto.Type != MenuType.Button && await _sysMenuRepository.IsAnyAsync(x => x.RouteName.ToLower() == dto.RouteName.ToLower() && x.Id != dto.Id)) { throw Oops.Bah("路由名称已存在"); } //检查菜单父子关系是否存在循环引用 if (dto.ParentId.HasValue && dto.ParentId != sysMenu.ParentId) { List list = await _sysMenuRepository.AsQueryable().ToChildListAsync(x => x.Children, dto.Id); if (list.Any(x => x.Id == dto.ParentId)) { throw Oops.Bah($"请勿将当前{dto.Type.Description()}的父级菜单设置为它的子级"); } } dto.Adapt(sysMenu); if (sysMenu.Type == MenuType.Button || sysMenu.Type == MenuType.DataScope) { sysMenu.Link = sysMenu.Icon = sysMenu.Component = sysMenu.Path = sysMenu.Redirect = sysMenu.RouteName = null; } else { sysMenu.Code = null; } await _sysMenuRepository.UpdateAsync(sysMenu); await _easyCachingProvider.RemoveByPrefixAsync(CacheConst.PermissionKey); } /// /// 根据菜单Id获取系统菜单详情 /// /// /// [DisplayName("系统菜单详情")] [HttpGet] public async Task Detail([FromQuery] long id) { return await _sysMenuRepository.AsQueryable().Where(x => x.Id == id) .Select(x => new SysMenuDetailOutput { Id = x.Id, Name = x.Name, ParentId = x.ParentId, Status = x.Status, Code = x.Code, Sort = x.Sort, Component = x.Component, Icon = x.Icon, IsFixed = x.IsFixed, IsIframe = x.IsIframe, IsKeepAlive = x.IsKeepAlive, IsVisible = x.IsVisible, Link = x.Link, Remark = x.Remark, Path = x.Path, Redirect = x.Redirect, RouteName = x.RouteName, Type = x.Type }).FirstAsync(); } /// /// 菜单下拉树 /// /// [DisplayName("菜单下拉树")] [HttpGet] public async Task> TreeSelect() { var list = await _sysMenuRepository.AsQueryable() .OrderBy(x => x.Sort) .OrderBy(x => x.Id) .WithCache() .ToTreeAsync(x => x.Children, x => x.ParentId, null); // 检查当前用户是否是管理员,如果不是管理员则过滤指定菜单 var isAdmin = await IsCurrentUserAdminAsync(); if (!isAdmin) { var hiddenMenuIds = _roleOptionsMonitor.CurrentValue.HiddenMenuIds ?? new List(); if (hiddenMenuIds.Any()) { FilterMenus(list, hiddenMenuIds); } } return list.Adapt>(); } /// /// 删除菜单/按钮 /// /// /// [DisplayName("删除菜单/按钮"), HttpDelete("delete")] public override async Task Delete(KeyDto dto) { await base.Delete(dto); await _easyCachingProvider.RemoveByPrefixAsync(CacheConst.PermissionKey); } /// /// 修改菜单/按钮状态 /// /// /// [DisplayName("修改菜单/按钮状态"), HttpPut("setStatus")] public override async Task SetStatus(AvailabilityDto dto) { await base.SetStatus(dto); await _easyCachingProvider.RemoveByPrefixAsync(CacheConst.PermissionKey); } /// /// 获取当前登录用户可用菜单 /// /// [DisplayName("获取当前登录用户可用菜单")] [HttpGet] public async Task> PermissionMenus() { long userId = _authManager.UserId; var value = await _easyCachingProvider.GetAsync($"{CacheConst.PermissionMenuKey}{userId}", async () => { var queryable = _sysMenuRepository.AsQueryable() .Where(x => x.Status == AvailabilityStatus.Enable) .OrderBy(x => x.Sort) .OrderBy(x => x.Id); List list; if (_authManager.IsSuperAdmin) { list = await queryable .Where(x => x.Type != MenuType.Button && x.Type != MenuType.DataScope) .ToTreeAsync(x => x.Children, x => x.ParentId, null); } else { List menuIdList = await _sysMenuRepository.AsSugarClient().Queryable().InnerJoin((role, userRole) => userRole.RoleId == role.Id) .InnerJoin((role, userRole, roleMenu) => role.Id == roleMenu.RoleId) .InnerJoin((role, userRole, roleMenu, menu) => roleMenu.MenuId == menu.Id) .Where((role, userRole, roleMenu, menu) => role.Status == AvailabilityStatus.Enable && userRole.UserId == userId && menu.Status == AvailabilityStatus.Enable) .Select((role, userRole, roleMenu) => roleMenu.MenuId).ToListAsync(); var array = menuIdList.Select(x => x as object).ToArray(); list = await queryable .ToTreeAsync(x => x.Children, x => x.ParentId, null, array); RemoveButton(list); } return list.Adapt>(); }, TimeSpan.FromDays(1)); return value.Value ?? new List(); } /// /// 检查当前登录用户是否是管理员 /// /// private async Task IsCurrentUserAdminAsync() { var userId = _authManager.UserId; // 首先检查是否是超级管理员 if (_authManager.IsSuperAdmin) { return true; } // 检查用户是否拥有管理员角色 return await _sysUserRoleRepository.IsAnyAsync(x => x.UserId == userId && x.RoleId == _roleOptionsMonitor.CurrentValue.AdminRoleId); } /// /// 过滤菜单,移除配置中指定的菜单ID及其子菜单 /// /// 菜单列表 /// 需要隐藏的菜单ID列表 private void FilterMenus(List menus, List hiddenMenuIds) { for (int i = menus.Count - 1; i >= 0; i--) { var menu = menus[i]; // 如果当前菜单在隐藏列表中,移除它 if (hiddenMenuIds.Contains(menu.Id)) { menus.RemoveAt(i); continue; } // 递归处理子菜单 if (menu.Children != null && menu.Children.Any()) { FilterMenus(menu.Children, hiddenMenuIds); } } } /// /// 菜单按钮树 - 仅在角色管理页面使用此接口时应用隐藏规则 /// /// [DisplayName("菜单按钮树")] [HttpGet] public async Task> TreeMenuButton() { long userId = _authManager.UserId; List menus; if (_authManager.IsSuperAdmin)//超级管理员 { menus = await _sysMenuRepository.AsQueryable().ToTreeAsync(x => x.Children, x => x.ParentId, null); } else { menus = await _sysMenuRepository.AsQueryable() .InnerJoin((menu, roleMenu) => menu.Id == roleMenu.MenuId) .InnerJoin((menu, roleMenu, role) => roleMenu.RoleId == role.Id) .InnerJoin((menu, roleMenu, role, userRole) => role.Id == userRole.RoleId) .Where((menu, roleMenu, role, userRole) => menu.Status == AvailabilityStatus.Enable && role.Status == AvailabilityStatus.Enable && userRole.UserId == userId) .Select(menu => menu) .ToTreeAsync(x => x.Children, x => x.ParentId, null); } // 检查当前用户是否是管理员,如果不是管理员则过滤指定菜单 var isAdmin = await IsCurrentUserAdminAsync(); if (!isAdmin) { var hiddenMenuIds = _roleOptionsMonitor.CurrentValue.HiddenMenuIds ?? new List(); if (hiddenMenuIds.Any()) { FilterMenus(menus, hiddenMenuIds); } } return menus.Adapt>(); } /// /// 校验权限 /// /// 权限标识 /// [NonAction] public async Task CheckPermission(string code) { if (_authManager.IsSuperAdmin) return true; // 兼容历史接口:未配置权限码的接口仅要求已登录,不做菜单权限拦截 var hasConfiguredPermission = await _sysMenuRepository.IsAnyAsync(x => x.Code == code); if (!hasConfiguredPermission) { return true; } var authCodes = await GetAuthPermissionCodeList(_authManager.UserId); return authCodes.Any(x => string.Equals(x, code, StringComparison.CurrentCultureIgnoreCase)); } /// /// 获取指定用户的访问权限集合 /// /// 系统用户id /// [NonAction] public async Task> GetAuthButtonCodeList(long userId) { var cacheKey = $"{CacheConst.PermissionButtonCodeKey}v2.{userId}"; var cache = await _easyCachingProvider.GetAsync(cacheKey, async () => { var list = await _sysMenuRepository.AsSugarClient() .Queryable() .InnerJoin((userRole, role) => userRole.RoleId == role.Id) .InnerJoin((userRole, role, roleMenu) => role.Id == roleMenu.RoleId) .InnerJoin((userRole, role, roleMenu, menu) => roleMenu.MenuId == menu.Id) .Where((userRole, role, roleMenu, menu) => userRole.UserId == userId && role.Status == AvailabilityStatus.Enable && menu.Status == AvailabilityStatus.Enable && (menu.Type == MenuType.Button || menu.Type == MenuType.DataScope)) .Select((userRole, role, roleMenu, menu) => new CheckPermissionOutput { Code = menu.Code, Access = true }) .ToListAsync(); return list .Where(x => !string.IsNullOrWhiteSpace(x.Code)) .GroupBy(x => x.Code) .Select(g => new CheckPermissionOutput { Code = g.Key, Access = true }) .ToList(); }, TimeSpan.FromDays(1)); return cache.Value; } [NonAction] private async Task> GetAuthPermissionCodeList(long userId) { var cacheKey = $"{CacheConst.PermissionKey}code.v2.{userId}"; var cache = await _easyCachingProvider.GetAsync(cacheKey, async () => { var list = await _sysMenuRepository.AsSugarClient() .Queryable() .InnerJoin((userRole, role) => userRole.RoleId == role.Id) .InnerJoin((userRole, role, roleMenu) => role.Id == roleMenu.RoleId) .InnerJoin((userRole, role, roleMenu, menu) => roleMenu.MenuId == menu.Id) .Where((userRole, role, roleMenu, menu) => userRole.UserId == userId && role.Status == AvailabilityStatus.Enable && menu.Status == AvailabilityStatus.Enable && !string.IsNullOrEmpty(menu.Code)) .Select((userRole, role, roleMenu, menu) => menu.Code) .ToListAsync(); return list .Where(x => !string.IsNullOrWhiteSpace(x)) .Distinct() .ToList(); }, TimeSpan.FromDays(1)); return cache.Value ?? new List(); } /// /// 移除菜单中的按钮 /// /// void RemoveButton(List menus) { for (int i = menus.Count - 1; i >= 0; i--) { if (menus[i].Type == MenuType.Button || menus[i].Type == MenuType.DataScope) { menus.Remove(menus[i]); continue; } if (menus[i].Children.Any()) { RemoveButton(menus[i].Children); } } } }