fengling-console/Services/RoleService.cs
movingsam 0d64b61169 refactor(console): migrate tenant management to TenantManager pattern
- Replace TenantRepository with TenantManager (ASP.NET Identity style)
- Change TenantId from long to int (auto-increment)
- Add TenantStore with CRUD operations
- Update TenantService, UserService, RoleService to use TenantManager
- Add Tenant entity with TenantStatus enum
- Update DTOs and controllers for int tenant IDs
2026-02-19 21:43:24 +08:00

303 lines
10 KiB
C#

using Fengling.Console.Models.Dtos;
using Fengling.Console.Repositories;
using Fengling.Console.Managers;
using Microsoft.AspNetCore.Identity;
using System.Security.Claims;
using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
namespace Fengling.Console.Services;
public interface IRoleService
{
Task<(IEnumerable<RoleDto> Items, int TotalCount)> GetRolesAsync(int page, int pageSize, string? name = null,
string? tenantId = null);
Task<RoleDto?> GetRoleAsync(long id);
Task<IEnumerable<UserDto>> GetRoleUsersAsync(long id);
Task<RoleDto> CreateRoleAsync(CreateRoleDto dto);
Task<RoleDto> UpdateRoleAsync(long id, UpdateRoleDto dto);
Task DeleteRoleAsync(long id);
Task AddUserToRoleAsync(long roleId, long userId);
Task RemoveUserFromRoleAsync(long roleId, long userId);
}
public class RoleService : IRoleService
{
private readonly IRoleRepository _repository;
private readonly ITenantManager _tenantManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly ApplicationDbContext _context;
private readonly IHttpContextAccessor _httpContextAccessor;
public RoleService(
IRoleRepository repository,
ITenantManager tenantManager,
UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager,
ApplicationDbContext context,
IHttpContextAccessor httpContextAccessor)
{
_repository = repository;
_tenantManager = tenantManager;
_userManager = userManager;
_roleManager = roleManager;
_context = context;
_httpContextAccessor = httpContextAccessor;
}
public async Task<(IEnumerable<RoleDto> 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<RoleDto>();
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<RoleDto?> 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<IEnumerable<UserDto>> 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<UserDto>();
foreach (var user in users)
{
var roles = await _userManager.GetRolesAsync(user);
var tenant = user.TenantId > 0 ? await _tenantManager.FindByIdAsync(user.TenantId) : null;
userDtos.Add(new UserDto
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
RealName = user.RealName,
TenantCode = user.TenantId.ToString(),
TenantName = tenant?.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<RoleDto> CreateRoleAsync(CreateRoleDto dto)
{
var role = new ApplicationRole
{
Name = dto.Name,
DisplayName = dto.DisplayName,
Description = dto.Description,
TenantId = dto.TenantId.HasValue ? (int?)dto.TenantId.Value : null,
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<RoleDto> 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 AddUserToRoleAsync(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.AddToRoleAsync(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}");
}
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();
}
}