using Fengling.AuthService.Data; using Fengling.AuthService.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Security.Claims; namespace Fengling.AuthService.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] public class UsersController : ControllerBase { private readonly ApplicationDbContext _context; private readonly UserManager _userManager; private readonly RoleManager _roleManager; private readonly ILogger _logger; public UsersController( ApplicationDbContext context, UserManager userManager, RoleManager roleManager, ILogger logger) { _context = context; _userManager = userManager; _roleManager = roleManager; _logger = logger; } [HttpGet] public async Task> GetUsers( [FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string? userName = null, [FromQuery] string? email = null, [FromQuery] string? tenantId = null) { var query = _context.Users.AsQueryable(); if (!string.IsNullOrEmpty(userName)) { query = query.Where(u => u.UserName!.Contains(userName)); } if (!string.IsNullOrEmpty(email)) { query = query.Where(u => u.Email != null && u.Email.Contains(email)); } if (!string.IsNullOrEmpty(tenantId)) { query = query.Where(u => u.TenantInfo.Id.ToString() == tenantId); } var totalCount = await query.CountAsync(); var users = await query .OrderByDescending(u => u.CreatedTime) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); var result = users.Select(async u => new { id = u.Id, userName = u.UserName, email = u.Email, realName = u.RealName, phone = u.Phone, tenantId = u.TenantInfo.Id, roles = (await _userManager.GetRolesAsync(u)).ToList(), emailConfirmed = u.EmailConfirmed, isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow, createdAt = u.CreatedTime, }); return Ok(new { items = await Task.WhenAll(result), totalCount, page, pageSize }); } [HttpGet("{id}")] public async Task> GetUser(long id) { var user = await _context.Users.FindAsync(id); if (user == null) { return NotFound(); } var roles = await _userManager.GetRolesAsync(user); return Ok(new { id = user.Id, userName = user.UserName, email = user.Email, realName = user.RealName, phone = user.Phone, tenantId = user.TenantInfo.Id, roles, emailConfirmed = user.EmailConfirmed, isActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, createdAt = user.CreatedTime, }); } [HttpPost] public async Task> CreateUser(CreateUserDto dto) { var tenantId = dto.TenantId ?? 0; Tenant tenant = null; if (tenantId != 0) { tenant = await _context.Tenants.FindAsync(tenantId); if (tenant == null) { return BadRequest("Invalid tenant ID"); } } var user = new ApplicationUser { UserName = dto.UserName, Email = dto.Email, RealName = dto.RealName, Phone = dto.Phone, TenantInfo = new TenantInfo(tenantId, tenant?.TenantId ?? "default", tenant?.Name ?? "默认租户"), EmailConfirmed = dto.EmailConfirmed, CreatedTime = DateTime.UtcNow, }; var result = await _userManager.CreateAsync(user, dto.Password); if (!result.Succeeded) { return BadRequest(result.Errors); } 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, SerializeToJson(dto)); return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user); } [HttpPut("{id}")] public async Task UpdateUser(long id, UpdateUserDto dto) { var user = await _context.Users.FindAsync(id); if (user == null) { return NotFound(); } 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)); return NoContent(); } [HttpPut("{id}/password")] public async Task ResetPassword(long id, ResetPasswordDto dto) { var user = await _userManager.FindByIdAsync(id.ToString()); if (user == null) { return NotFound(); } var token = await _userManager.GeneratePasswordResetTokenAsync(user); var result = await _userManager.ResetPasswordAsync(user, token, dto.NewPassword); if (!result.Succeeded) { return BadRequest(result.Errors); } await CreateAuditLog("user", "reset_password", "User", user.Id, user.UserName); return NoContent(); } [HttpDelete("{id}")] public async Task DeleteUser(long id) { var user = await _context.Users.FindAsync(id); if (user == null) { return NotFound(); } 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); return NoContent(); } private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null) { var userName = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.Identity?.Name ?? "system"; var tenantId = 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, }; _context.AuditLogs.Add(log); await _context.SaveChangesAsync(); } private string SerializeToJson(object obj) { return System.Text.Json.JsonSerializer.Serialize(obj, new System.Text.Json.JsonSerializerOptions { WriteIndented = false }); } } public class CreateUserDto { public string UserName { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; public string RealName { get; set; } = string.Empty; public string? Phone { get; set; } public long? TenantId { get; set; } public List RoleIds { get; set; } = new(); public string Password { get; set; } = string.Empty; public bool EmailConfirmed { get; set; } public bool IsActive { get; set; } = true; } public class UpdateUserDto { public string Email { get; set; } = string.Empty; public string RealName { get; set; } = string.Empty; public string? Phone { get; set; } public bool EmailConfirmed { get; set; } public bool IsActive { get; set; } = true; } public class ResetPasswordDto { public string NewPassword { get; set; } = string.Empty; }