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 cache = await GetAuthButtonCodeList(_authManager.UserId); var output = cache.FirstOrDefault(x => x.Code.Contains(code, StringComparison.CurrentCultureIgnoreCase)); return output?.Access ?? true; } /// /// 获取指定用户的访问权限集合 /// /// 系统用户id /// [NonAction] public async Task> GetAuthButtonCodeList(long userId) { var cache = await _easyCachingProvider.GetAsync($"{CacheConst.PermissionButtonCodeKey}{userId}", async () => { var queryable = _sysMenuRepository.AsSugarClient().Queryable() .InnerJoin((role, userRole) => role.Id == userRole.RoleId) .InnerJoin((role, userRole, roleMenu) => role.Id == roleMenu.RoleId) .Where(role => role.Status == AvailabilityStatus.Enable) .Select((role, userRole, roleMenu) => roleMenu); var list = await _sysMenuRepository.AsQueryable().LeftJoin(queryable, (menu, roleMenu) => menu.Id == roleMenu.MenuId) .Where(menu => menu.Type == MenuType.Button || menu.Type == MenuType.DataScope) .Select((menu, roleMenu) => new CheckPermissionOutput { Code = menu.Code, Access = SqlFunc.IIF(SqlFunc.IsNull(roleMenu.Id, 0) > 0 || menu.Status == AvailabilityStatus.Disable, true, false) }).ToListAsync(); return list.Distinct().ToList(); }, TimeSpan.FromDays(1)); return cache.Value; } /// /// 移除菜单中的按钮 /// /// 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); } } } }