296 lines
10 KiB
C#
296 lines
10 KiB
C#
using Fengling.Console.Models.Dtos;
|
|
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
|
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
|
|
using Fengling.Platform.Infrastructure;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Security.Claims;
|
|
|
|
namespace Fengling.Console.Services;
|
|
|
|
public interface IUserService
|
|
{
|
|
Task<(IEnumerable<UserDto> Items, int TotalCount)> GetUsersAsync(int page, int pageSize, string? userName = null,
|
|
string? email = null, string? tenantCode = null);
|
|
|
|
Task<UserDto?> GetUserAsync(long id);
|
|
Task<UserDto> CreateUserAsync(CreateUserDto dto);
|
|
Task<UserDto> UpdateUserAsync(long id, UpdateUserDto dto);
|
|
Task ResetPasswordAsync(long id, ResetPasswordDto dto);
|
|
Task DeleteUserAsync(long id);
|
|
}
|
|
|
|
public class UserService(
|
|
ITenantManager tenantManager,
|
|
UserManager<ApplicationUser> userManager,
|
|
RoleManager<ApplicationRole> roleManager,
|
|
PlatformDbContext context,
|
|
IHttpContextAccessor httpContextAccessor)
|
|
: IUserService
|
|
{
|
|
public async Task<(IEnumerable<UserDto> Items, int TotalCount)> GetUsersAsync(int page, int pageSize,
|
|
string? userName = null, string? email = null,
|
|
string? tenantCode = null)
|
|
{
|
|
var query = context.Users.AsQueryable();
|
|
|
|
if (!string.IsNullOrEmpty(userName))
|
|
{
|
|
query = query.Where(u => u.UserName != null && u.UserName.Contains(userName));
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(email))
|
|
{
|
|
query = query.Where(u => u.Email != null && u.Email.Contains(email));
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(tenantCode))
|
|
{
|
|
query = query.Where(u => u.TenantInfo != null &&
|
|
u.TenantInfo.TenantCode != null &&
|
|
u.TenantInfo.TenantCode.Contains(tenantCode));
|
|
}
|
|
|
|
var totalCount = await query.CountAsync();
|
|
var users = await query
|
|
.OrderByDescending(u => u.CreatedTime)
|
|
.Skip((page - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.ToListAsync();
|
|
|
|
var userDtos = new List<UserDto>();
|
|
foreach (var user in users)
|
|
{
|
|
var roles = await userManager.GetRolesAsync(user);
|
|
var tenant = user.TenantInfo?.TenantId > 0
|
|
? await tenantManager.FindByIdAsync(user.TenantInfo.TenantId)
|
|
: null;
|
|
userDtos.Add(new UserDto
|
|
{
|
|
Id = user.Id,
|
|
UserName = user.UserName,
|
|
Email = user.Email,
|
|
RealName = user.RealName,
|
|
PhoneNumber = user.PhoneNumber,
|
|
TenantCode = user.TenantInfo?.TenantCode,
|
|
TenantName = tenant?.Name ?? "",
|
|
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<UserDto?> GetUserAsync(long id)
|
|
{
|
|
var user = await context.Users.FindAsync(id);
|
|
if (user == null) return null;
|
|
|
|
var roles = await userManager.GetRolesAsync(user);
|
|
var tenant = user.TenantInfo?.TenantId > 0 ? await tenantManager.FindByIdAsync(user.TenantInfo.TenantId) : null;
|
|
return new UserDto
|
|
{
|
|
Id = user.Id,
|
|
UserName = user.UserName,
|
|
Email = user.Email,
|
|
RealName = user.RealName,
|
|
PhoneNumber = user.PhoneNumber,
|
|
TenantCode = user.TenantInfo?.TenantCode,
|
|
TenantName = tenant?.Name ?? "",
|
|
Roles = roles.ToList(),
|
|
EmailConfirmed = user.EmailConfirmed,
|
|
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
|
|
CreatedAt = user.CreatedTime
|
|
};
|
|
}
|
|
|
|
public async Task<UserDto> CreateUserAsync(CreateUserDto dto)
|
|
{
|
|
Tenant? tenant = null;
|
|
|
|
if (dto.TenantId.HasValue && dto.TenantId.Value > 0)
|
|
{
|
|
tenant = await tenantManager.FindByIdAsync(dto.TenantId.Value);
|
|
if (tenant == null)
|
|
{
|
|
throw new InvalidOperationException("Invalid tenant ID");
|
|
}
|
|
}
|
|
|
|
var tenantInfo = tenant != null
|
|
? new TenantInfo(tenant.Id, tenant.TenantCode, tenant.Name)
|
|
: new TenantInfo(0, "", "");
|
|
|
|
var user = new ApplicationUser
|
|
{
|
|
UserName = dto.UserName,
|
|
Email = dto.Email,
|
|
RealName = dto.RealName,
|
|
PhoneNumber = dto.PhoneNumber,
|
|
TenantInfo = tenantInfo,
|
|
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.Count != 0)
|
|
{
|
|
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,
|
|
PhoneNumber = user.PhoneNumber,
|
|
TenantCode = user.TenantInfo.TenantId.ToString(),
|
|
TenantName = tenant?.Name ?? "",
|
|
Roles = roles.ToList(),
|
|
EmailConfirmed = user.EmailConfirmed,
|
|
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
|
|
CreatedAt = user.CreatedTime
|
|
};
|
|
}
|
|
|
|
public async Task<UserDto> UpdateUserAsync(long id, UpdateUserDto dto)
|
|
{
|
|
var user = await context.Users.FindAsync(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.PhoneNumber = dto.PhoneNumber;
|
|
user.EmailConfirmed = dto.EmailConfirmed;
|
|
user.UpdatedTime = DateTime.UtcNow;
|
|
|
|
await userManager.UpdateAsync(user);
|
|
|
|
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);
|
|
}
|
|
|
|
var tenant = user.TenantInfo?.TenantId > 0 ? await tenantManager.FindByIdAsync(user.TenantInfo.TenantId) : null;
|
|
|
|
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,
|
|
PhoneNumber = user.PhoneNumber,
|
|
TenantCode = user.TenantInfo?.TenantCode,
|
|
TenantName = tenant?.Name ?? "",
|
|
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 context.Users.FindAsync(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 tenantIdClaim = httpContext?.User?.FindFirstValue("TenantId");
|
|
|
|
var log = new AuditLog
|
|
{
|
|
Operator = userName,
|
|
TenantId = tenantIdClaim,
|
|
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();
|
|
}
|
|
} |