using Fengling.Console.Models.Dtos; using Fengling.Console.Repositories; using Fengling.Platform.Infrastructure.Repositories; using Microsoft.AspNetCore.Identity; using System.Security.Claims; using Fengling.Console.Datas; using Fengling.Console.Models.Entities; using Fengling.Platform.Domain.AggregatesModel.TenantAggregate; namespace Fengling.Console.Services; public interface IUserService { Task<(IEnumerable Items, int TotalCount)> GetUsersAsync(int page, int pageSize, string? userName = null, string? email = null, string? tenantId = null); Task GetUserAsync(long id); Task CreateUserAsync(CreateUserDto dto); Task UpdateUserAsync(long id, UpdateUserDto dto); Task ResetPasswordAsync(long id, ResetPasswordDto dto); Task DeleteUserAsync(long id); } public class UserService( IUserRepository repository, ITenantRepository tenantRepository, UserManager userManager, RoleManager roleManager, ApplicationDbContext context, IHttpContextAccessor httpContextAccessor) : IUserService { public async Task<(IEnumerable Items, int TotalCount)> GetUsersAsync(int page, int pageSize, string? userName = null, string? email = null, string? tenantId = null) { var users = await repository.GetPagedAsync(page, pageSize, userName, email, tenantId); var totalCount = await repository.CountAsync(userName, email, tenantId); 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, Phone = user.Phone, TenantCode = user.TenantInfo.TenantCode, TenantName = user.TenantInfo.TenantName, Roles = roles.ToList(), EmailConfirmed = user.EmailConfirmed, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, CreatedAt = user.CreatedTime }); } return (userDtos, totalCount); } public async Task GetUserAsync(long id) { var user = await repository.GetByIdAsync(id); if (user == null) return null; var roles = await userManager.GetRolesAsync(user); return new UserDto { Id = user.Id, UserName = user.UserName, Email = user.Email, RealName = user.RealName, Phone = user.Phone, TenantCode = user.TenantInfo.TenantCode, TenantName = user.TenantInfo.TenantName, Roles = roles.ToList(), EmailConfirmed = user.EmailConfirmed, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, CreatedAt = user.CreatedTime }; } public async Task CreateUserAsync(CreateUserDto dto) { var tenantId = dto.TenantId ?? 0; Tenant? tenant = null; if (tenantId != 0) { tenant = await tenantRepository.GetByIdAsync(tenantId); if (tenant == null) { throw new InvalidOperationException("Invalid tenant ID"); } } var user = new ApplicationUser { UserName = dto.UserName, Email = dto.Email, RealName = dto.RealName, Phone = dto.Phone, TenantInfo = new TenantInfo(tenant!), EmailConfirmed = dto.EmailConfirmed, CreatedTime = DateTime.UtcNow }; var result = await userManager.CreateAsync(user, dto.Password); if (!result.Succeeded) { throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description))); } if (dto.RoleIds != null && dto.RoleIds.Any()) { foreach (var roleId in dto.RoleIds) { var role = await roleManager.FindByIdAsync(roleId.ToString()); if (role != null) { await userManager.AddToRoleAsync(user, role.Name!); } } } if (!dto.IsActive) { await userManager.SetLockoutEnabledAsync(user, true); await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); } await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, System.Text.Json.JsonSerializer.Serialize(dto)); var roles = await userManager.GetRolesAsync(user); return new UserDto { Id = user.Id, UserName = user.UserName, Email = user.Email, RealName = user.RealName, Phone = user.Phone, TenantCode = user.TenantInfo.TenantCode, TenantName = user.TenantInfo.TenantName, Roles = roles.ToList(), EmailConfirmed = user.EmailConfirmed, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, CreatedAt = user.CreatedTime }; } public async Task UpdateUserAsync(long id, UpdateUserDto dto) { var user = await repository.GetByIdAsync(id); if (user == null) { throw new KeyNotFoundException($"User with ID {id} not found"); } var oldValue = System.Text.Json.JsonSerializer.Serialize(user); user.Email = dto.Email; user.RealName = dto.RealName; user.Phone = dto.Phone; user.EmailConfirmed = dto.EmailConfirmed; user.UpdatedTime = DateTime.UtcNow; if (dto.IsActive) { await userManager.SetLockoutEnabledAsync(user, false); await userManager.SetLockoutEndDateAsync(user, null); } else { await userManager.SetLockoutEnabledAsync(user, true); await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); } await context.SaveChangesAsync(); await CreateAuditLog("user", "update", "User", user.Id, user.UserName, oldValue, System.Text.Json.JsonSerializer.Serialize(user)); var roles = await userManager.GetRolesAsync(user); return new UserDto { Id = user.Id, UserName = user.UserName, Email = user.Email, RealName = user.RealName, Phone = user.Phone, TenantCode = user.TenantInfo.TenantCode, TenantName = user.TenantInfo.TenantName, Roles = roles.ToList(), EmailConfirmed = user.EmailConfirmed, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, CreatedAt = user.CreatedTime }; } public async Task ResetPasswordAsync(long id, ResetPasswordDto dto) { var user = await userManager.FindByIdAsync(id.ToString()); if (user == null) { throw new KeyNotFoundException($"User with ID {id} not found"); } var token = await userManager.GeneratePasswordResetTokenAsync(user); var result = await userManager.ResetPasswordAsync(user, token, dto.NewPassword); if (!result.Succeeded) { throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description))); } await CreateAuditLog("user", "reset_password", "User", user.Id, user.UserName); } public async Task DeleteUserAsync(long id) { var user = await repository.GetByIdAsync(id); if (user == null) { throw new KeyNotFoundException($"User with ID {id} not found"); } var oldValue = System.Text.Json.JsonSerializer.Serialize(user); user.IsDeleted = true; user.UpdatedTime = DateTime.UtcNow; await context.SaveChangesAsync(); await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue); } 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(); } }