using Fengling.AuthService.Data; using Fengling.AuthService.Models; using Fengling.Console.Models.Dtos; using Fengling.Console.Repositories; using Microsoft.AspNetCore.Identity; using System.Security.Claims; namespace Fengling.Console.Services; public interface IRoleService { Task<(IEnumerable Items, int TotalCount)> GetRolesAsync(int page, int pageSize, string? name = null, string? tenantId = null); Task GetRoleAsync(long id); Task> GetRoleUsersAsync(long id); Task CreateRoleAsync(CreateRoleDto dto); Task UpdateRoleAsync(long id, UpdateRoleDto dto); Task DeleteRoleAsync(long id); Task RemoveUserFromRoleAsync(long roleId, long userId); } public class RoleService : IRoleService { private readonly IRoleRepository _repository; private readonly UserManager _userManager; private readonly RoleManager _roleManager; private readonly ApplicationDbContext _context; private readonly IHttpContextAccessor _httpContextAccessor; public RoleService( IRoleRepository repository, UserManager userManager, RoleManager roleManager, ApplicationDbContext context, IHttpContextAccessor httpContextAccessor) { _repository = repository; _userManager = userManager; _roleManager = roleManager; _context = context; _httpContextAccessor = httpContextAccessor; } public async Task<(IEnumerable Items, int TotalCount)> GetRolesAsync(int page, int pageSize, string? name = null, string? tenantId = null) { var roles = await _repository.GetPagedAsync(page, pageSize, name, tenantId); var totalCount = await _repository.CountAsync(name, tenantId); var roleDtos = new List(); foreach (var role in roles) { var users = await _userManager.GetUsersInRoleAsync(role.Name!); roleDtos.Add(new RoleDto { Id = role.Id, Name = role.Name, DisplayName = role.DisplayName, Description = role.Description, TenantId = role.TenantId, IsSystem = role.IsSystem, Permissions = role.Permissions, UserCount = users.Count, CreatedAt = role.CreatedTime }); } return (roleDtos, totalCount); } public async Task GetRoleAsync(long id) { var role = await _repository.GetByIdAsync(id); if (role == null) return null; return new RoleDto { Id = role.Id, Name = role.Name, DisplayName = role.DisplayName, Description = role.Description, TenantId = role.TenantId, IsSystem = role.IsSystem, Permissions = role.Permissions, UserCount = (await _userManager.GetUsersInRoleAsync(role.Name!)).Count, CreatedAt = role.CreatedTime }; } public async Task> GetRoleUsersAsync(long id) { var role = await _repository.GetByIdAsync(id); if (role == null) { throw new KeyNotFoundException($"Role with ID {id} not found"); } var users = await _userManager.GetUsersInRoleAsync(role.Name!); var userDtos = new List(); foreach (var user in users) { var roles = await _userManager.GetRolesAsync(user); userDtos.Add(new UserDto { Id = user.Id, UserName = user.UserName, Email = user.Email, RealName = user.RealName, TenantId = user.TenantInfo.Id, TenantName = user.TenantInfo.Name, Roles = roles.ToList(), EmailConfirmed = user.EmailConfirmed, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, CreatedAt = user.CreatedTime }); } return userDtos; } public async Task CreateRoleAsync(CreateRoleDto dto) { var role = new ApplicationRole { Name = dto.Name, DisplayName = dto.DisplayName, Description = dto.Description, TenantId = dto.TenantId, Permissions = dto.Permissions, IsSystem = false, CreatedTime = DateTime.UtcNow }; var result = await _roleManager.CreateAsync(role); if (!result.Succeeded) { throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description))); } await CreateAuditLog("role", "create", "Role", role.Id, role.DisplayName, null, System.Text.Json.JsonSerializer.Serialize(dto)); return new RoleDto { Id = role.Id, Name = role.Name, DisplayName = role.DisplayName, Description = role.Description, TenantId = role.TenantId, IsSystem = role.IsSystem, Permissions = role.Permissions, UserCount = 0, CreatedAt = role.CreatedTime }; } public async Task UpdateRoleAsync(long id, UpdateRoleDto dto) { var role = await _repository.GetByIdAsync(id); if (role == null) { throw new KeyNotFoundException($"Role with ID {id} not found"); } if (role.IsSystem) { throw new InvalidOperationException("系统角色不能修改"); } var oldValue = System.Text.Json.JsonSerializer.Serialize(role); role.DisplayName = dto.DisplayName; role.Description = dto.Description; role.Permissions = dto.Permissions; await _repository.UpdateAsync(role); await CreateAuditLog("role", "update", "Role", role.Id, role.DisplayName, oldValue, System.Text.Json.JsonSerializer.Serialize(role)); var users = await _userManager.GetUsersInRoleAsync(role.Name!); return new RoleDto { Id = role.Id, Name = role.Name, DisplayName = role.DisplayName, Description = role.Description, TenantId = role.TenantId, IsSystem = role.IsSystem, Permissions = role.Permissions, UserCount = users.Count, CreatedAt = role.CreatedTime }; } public async Task DeleteRoleAsync(long id) { var role = await _repository.GetByIdAsync(id); if (role == null) { throw new KeyNotFoundException($"Role with ID {id} not found"); } if (role.IsSystem) { throw new InvalidOperationException("系统角色不能删除"); } var oldValue = System.Text.Json.JsonSerializer.Serialize(role); var users = await _userManager.GetUsersInRoleAsync(role.Name!); foreach (var user in users) { await _userManager.RemoveFromRoleAsync(user, role.Name!); } await _repository.DeleteAsync(role); await CreateAuditLog("role", "delete", "Role", role.Id, role.DisplayName, oldValue); } public async Task RemoveUserFromRoleAsync(long roleId, long userId) { var role = await _repository.GetByIdAsync(roleId); if (role == null) { throw new KeyNotFoundException($"Role with ID {roleId} not found"); } var user = await _userManager.FindByIdAsync(userId.ToString()); if (user == null) { throw new KeyNotFoundException($"User with ID {userId} not found"); } var result = await _userManager.RemoveFromRoleAsync(user, role.Name!); if (!result.Succeeded) { throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description))); } await CreateAuditLog("role", "update", "UserRole", null, $"{role.Name} - {user.UserName}"); } private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null) { var httpContext = _httpContextAccessor.HttpContext; var userName = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? httpContext?.User?.Identity?.Name ?? "system"; var tenantId = httpContext?.User?.FindFirstValue("TenantId"); var log = new AuditLog { Operator = userName, TenantId = tenantId, Operation = operation, Action = action, TargetType = targetType, TargetId = targetId, TargetName = targetName, IpAddress = httpContext?.Connection?.RemoteIpAddress?.ToString() ?? "unknown", Status = "success", OldValue = oldValue, NewValue = newValue, CreatedAt = DateTime.UtcNow }; _context.AuditLogs.Add(log); await _context.SaveChangesAsync(); } }