using Model; using Model.Dto.System; using Model.Entity.System; using Model.Mapper; using ORM; using Service.Interface; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Service.Implement { /// /// 角色权限管理 服务实现 /// public class RoleService : IRoleService { public async Task>> GetPagedAsync(int pageIndex, int pageSize, RefAsync total, string? keyword = null) { try { var list = await SqlSugarContext.DbContext.Queryable() .Where(x => x.IsDel == 0) .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.Code.Contains(keyword!) || x.Name.Contains(keyword!)) .OrderBy(x => x.Sort).OrderBy(x => x.CreateTime, OrderByType.Desc) .ToPageListAsync(pageIndex, pageSize, total); var dtos = list.ToDtoList(); await FillPermissionsAsync(dtos); return Result>.Success(dtos); } catch (Exception ex) { return Result>.Error("查询角色列表失败", ex); } } public async Task> GetByIdAsync(long id) { try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("角色不存在或已被删除"); var dto = entity.ToDto(); await FillPermissionsAsync(new List { dto }); return Result.Success(dto); } catch (Exception ex) { return Result.Error("查询角色详情失败", ex); } } public async Task> AddAsync(RoleDto dto) { if (dto == null || string.IsNullOrWhiteSpace(dto.Code) || string.IsNullOrWhiteSpace(dto.Name)) return Result.Error("角色编码和名称不能为空"); try { var exists = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Code == dto.Code && x.IsDel == 0).AnyAsync(); if (exists) return Result.Error($"角色编码「{dto.Code}」已存在"); var entity = dto.ToEntity(); entity.Id = 0; entity.IsSystem = 0; // 新建角色一律非系统内置 entity.CreateTime = DateTime.Now; var id = await SqlSugarContext.DbContext.Insertable(entity).ExecuteReturnSnowflakeIdAsync(); entity.Id = id; if (dto.PermissionIds != null && dto.PermissionIds.Count > 0) { var permIds = dto.PermissionIds.Select(long.Parse).Where(p => p > 0).ToList(); await ReplaceRolePermissionsAsync(id, permIds); } var result = entity.ToDto(); await FillPermissionsAsync(new List { result }); return Result.Success(result); } catch (Exception ex) { return Result.Error("新增角色失败", ex); } } public async Task> UpdateAsync(RoleDto dto) { if (dto == null || !long.TryParse(dto.Id, out var id) || id <= 0) return Result.Error("角色Id无效"); try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("角色不存在或已被删除"); // 系统内置角色不允许改编码 if (entity.IsSystem == 1 && dto.Code != entity.Code) return Result.Error($"系统内置角色「{entity.Name}」不允许修改编码"); var codeExists = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Code == dto.Code && x.Id != id && x.IsDel == 0).AnyAsync(); if (codeExists) return Result.Error($"角色编码「{dto.Code}」已存在"); var newEntity = dto.ToEntity(); newEntity.Id = id; newEntity.IsSystem = entity.IsSystem; // 内置标记不可被前端覆盖 await SqlSugarContext.DbContext.Updateable(newEntity) .IgnoreColumns(x => new { x.CreateTime, x.IsDel, x.IsSystem }) .ExecuteCommandAsync(); // 权限变化时同步更新关联(传入 null 表示不改权限) if (dto.PermissionIds != null) { var permIds = dto.PermissionIds.Select(long.Parse).Where(p => p > 0).ToList(); var guard = await EnsureAdminRemainsAfterRoleChangeAsync(id, permIds); if (guard != null) return Result.Error(guard.Msg); await ReplaceRolePermissionsAsync(id, permIds); } var result = newEntity.ToDto(); await FillPermissionsAsync(new List { result }); return Result.Success(result); } catch (Exception ex) { return Result.Error("修改角色失败", ex); } } public async Task DeleteAsync(long id) { if (id <= 0) return Result.Error("角色Id无效"); try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("角色不存在或已被删除"); if (entity.IsSystem == 1) return Result.Error($"系统内置角色「{entity.Name}」不允许删除"); var usedByUser = await SqlSugarContext.DbContext.Queryable() .Where(x => x.RoleId == id).AnyAsync(); if (usedByUser) return Result.Error("该角色下存在用户,请先移除角色下的用户再删除"); await SqlSugarContext.DbContext.Updateable() .SetColumns(x => x.IsDel == 1) .Where(x => x.Id == id).ExecuteCommandAsync(); await SqlSugarContext.DbContext.Deleteable() .Where(x => x.RoleId == id).ExecuteCommandAsync(); return Result.Success(); } catch (Exception ex) { return Result.Error("删除角色失败", ex); } } public async Task AssignPermissionsAsync(long roleId, List permissionIds) { if (roleId <= 0) return Result.Error("角色Id无效"); try { var exists = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == roleId && x.IsDel == 0).AnyAsync(); if (!exists) return Result.Error("角色不存在或已被删除"); // 防锁死:移除角色的用户管理权限后,系统必须仍有其它可管理用户的管理员 var newIds = permissionIds ?? new List(); var guard = await EnsureAdminRemainsAfterRoleChangeAsync(roleId, newIds); if (guard != null) return guard; await ReplaceRolePermissionsAsync(roleId, newIds); return Result.Success(); } catch (Exception ex) { return Result.Error("分配权限失败", ex); } } public async Task>> GetOptionsAsync() { try { var list = await SqlSugarContext.DbContext.Queryable() .Where(x => x.IsDel == 0) .OrderBy(x => x.Sort).OrderBy(x => x.Code) .ToListAsync(); return Result>.Success(list.ToOptionDtoList()); } catch (Exception ex) { return Result>.Error("查询角色选项失败", ex); } } public async Task>> GetPermissionsAsync() { try { var list = await SqlSugarContext.DbContext.Queryable() .Where(x => x.IsDel == 0) .OrderBy(x => x.Sort).OrderBy(x => x.Code) .ToListAsync(); return Result>.Success(list.ToDtoList()); } catch (Exception ex) { return Result>.Error("查询权限列表失败", ex); } } /// 全量覆盖角色-权限关联 private static async Task ReplaceRolePermissionsAsync(long roleId, List permissionIds) { var db = SqlSugarContext.DbContext; await db.Deleteable().Where(x => x.RoleId == roleId).ExecuteCommandAsync(); if (permissionIds.Count > 0) { var now = DateTime.Now; var links = permissionIds.Distinct().Select(p => new RolePermissionEntity { RoleId = roleId, PermissionId = p, CreateTime = now }).ToList(); await db.Insertable(links).ExecuteCommandAsync(); } } /// /// 防锁死校验:当 roleId 将失去 user:manage 权限时,系统必须仍存在 /// 「已启用且通过其它角色拥有 user:manage 权限」的用户,否则返回 Error /// private async Task EnsureAdminRemainsAfterRoleChangeAsync(long roleId, List newPermissionIds) { var db = SqlSugarContext.DbContext; var managePermId = await db.Queryable() .Where(x => x.Code == "user:manage" && x.IsDel == 0) .Select(x => x.Id).FirstAsync(); if (managePermId == 0) return null; bool currentlyHas = await db.Queryable() .AnyAsync(x => x.RoleId == roleId && x.PermissionId == managePermId); bool willHave = newPermissionIds.Contains(managePermId); if (currentlyHas && !willHave) { var otherRoleIds = await db.Queryable() .Where(x => x.PermissionId == managePermId && x.RoleId != roleId) .Select(x => x.RoleId).ToListAsync(); var otherUserIds = otherRoleIds.Count > 0 ? await db.Queryable().Where(x => otherRoleIds.Contains(x.RoleId)).Select(x => x.UserId).ToListAsync() : new List(); var exists = otherUserIds.Count > 0 && await db.Queryable() .AnyAsync(x => otherUserIds.Contains(x.Id) && x.IsDel == 0 && x.IsEnabled == 1); if (!exists) return Result.Error("操作被拒绝:移除该角色的用户管理权限后,系统将没有任何可管理用户的管理员"); } return null; } /// 给角色 DTO 填充权限Id列表与权限编码列表 private static async Task FillPermissionsAsync(List dtos) { if (dtos == null || dtos.Count == 0) return; var db = SqlSugarContext.DbContext; var roleIds = dtos.Select(d => long.Parse(d.Id)).ToList(); var links = await db.Queryable() .Where(x => roleIds.Contains(x.RoleId)).ToListAsync(); var permIds = links.Select(l => l.PermissionId).Distinct().ToList(); var perms = permIds.Count > 0 ? await db.Queryable().Where(x => permIds.Contains(x.Id) && x.IsDel == 0).ToListAsync() : new List(); var permMap = perms.ToDictionary(p => p.Id, p => p.Code); foreach (var d in dtos) { var rid = long.Parse(d.Id); var myPermIds = links.Where(l => l.RoleId == rid).Select(l => l.PermissionId).ToList(); d.PermissionIds = myPermIds.Select(p => p.ToString()).ToList(); d.PermissionCodes = myPermIds.Where(p => permMap.ContainsKey(p)).Select(p => permMap[p]).ToList(); } } } }