LingAdmin/Backend/Services/IdentityService/LingAdmin.IdentityService/Controllers/UsersController.cs

243 lines
7.4 KiB
C#
Raw Permalink Normal View History

2026-04-16 18:13:06 +08:00
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using LingAdmin.IdentityService.Data;
using LingAdmin.IdentityService.Services;
using LingAdmin.Shared.DTOs;
namespace LingAdmin.IdentityService.Controllers;
/// <summary>
/// 用户管理控制器 - 管理员操作
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Authorize(Roles = "Admin,SuperAdmin")]
public class UsersController : ControllerBase
{
private readonly IdentityDbContext _context;
private readonly IPasswordHasher _passwordHasher;
private readonly ILogger<UsersController> _logger;
public UsersController(
IdentityDbContext context,
IPasswordHasher passwordHasher,
ILogger<UsersController> logger)
{
_context = context;
_passwordHasher = passwordHasher;
_logger = logger;
}
/// <summary>
/// 获取用户列表(分页)
/// </summary>
[HttpGet]
public async Task<ActionResult<ApiResponse<PaginatedResponse<UserDto>>>> GetUsers(
[FromQuery] PaginationRequest pagination)
{
var query = _context.Users.AsQueryable();
// Search
if (!string.IsNullOrWhiteSpace(pagination.Search))
{
var search = pagination.Search.ToLower();
query = query.Where(u =>
u.Name.ToLower().Contains(search) ||
u.Email.ToLower().Contains(search));
}
// Sort
query = pagination.SortBy?.ToLower() switch
{
"name" => pagination.SortDescending ? query.OrderByDescending(u => u.Name) : query.OrderBy(u => u.Name),
"email" => pagination.SortDescending ? query.OrderByDescending(u => u.Email) : query.OrderBy(u => u.Email),
"createdat" => pagination.SortDescending ? query.OrderByDescending(u => u.CreatedAt) : query.OrderBy(u => u.CreatedAt),
_ => query.OrderByDescending(u => u.CreatedAt)
};
var totalCount = await query.CountAsync();
var users = await query
.Skip((pagination.Page - 1) * pagination.PageSize)
.Take(pagination.PageSize)
.Select(u => new UserDto
{
Id = u.Id,
Name = u.Name,
Email = u.Email,
Status = u.Status,
CreatedAt = u.CreatedAt,
LastLoginAt = u.LastLoginAt
})
.ToListAsync();
var response = new PaginatedResponse<UserDto>
{
Items = users,
TotalCount = totalCount,
Page = pagination.Page,
PageSize = pagination.PageSize
};
return Ok(ApiResponse<PaginatedResponse<UserDto>>.Ok(response));
}
/// <summary>
/// 获取单个用户
/// </summary>
[HttpGet("{id}")]
public async Task<ActionResult<ApiResponse<UserDto>>> GetUser(int id)
{
var user = await _context.Users.FindAsync(id);
if (user == null)
{
return NotFound(ApiResponse<UserDto>.NotFound("User not found"));
}
var userDto = new UserDto
{
Id = user.Id,
Name = user.Name,
Email = user.Email,
Status = user.Status,
CreatedAt = user.CreatedAt,
LastLoginAt = user.LastLoginAt
};
return Ok(ApiResponse<UserDto>.Ok(userDto));
}
/// <summary>
/// 更新用户状态
/// </summary>
[HttpPatch("{id}/status")]
public async Task<ActionResult<ApiResponse<UserDto>>> UpdateUserStatus(int id, [FromBody] UpdateStatusRequest request)
{
var user = await _context.Users.FindAsync(id);
if (user == null)
{
return NotFound(ApiResponse<UserDto>.NotFound("User not found"));
}
if (!new[] { "Active", "Inactive", "Suspended" }.Contains(request.Status))
{
return BadRequest(ApiResponse<UserDto>.Error("Invalid status. Must be Active, Inactive, or Suspended"));
}
user.Status = request.Status;
user.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
var userDto = new UserDto
{
Id = user.Id,
Name = user.Name,
Email = user.Email,
Status = user.Status,
CreatedAt = user.CreatedAt,
LastLoginAt = user.LastLoginAt
};
return Ok(ApiResponse<UserDto>.Ok(userDto, "User status updated"));
}
/// <summary>
/// 重置用户密码(管理员操作)
/// </summary>
[HttpPost("{id}/reset-password")]
public async Task<ActionResult<ApiResponse<object>>> ResetUserPassword(int id, [FromBody] AdminResetPasswordRequest request)
{
var user = await _context.Users.FindAsync(id);
if (user == null)
{
return NotFound(ApiResponse<object>.NotFound("User not found"));
}
user.PasswordHash = _passwordHasher.HashPassword(request.NewPassword);
user.UpdatedAt = DateTime.UtcNow;
// Revoke all refresh tokens
var tokens = await _context.RefreshTokens
.Where(rt => rt.UserId == id && rt.RevokedAt == null)
.ToListAsync();
foreach (var token in tokens)
{
token.RevokedAt = DateTime.UtcNow;
}
await _context.SaveChangesAsync();
_logger.LogInformation("Admin reset password for user {UserId}", id);
return Ok(ApiResponse<object>.Ok(new { }, "Password reset successfully"));
}
/// <summary>
/// 删除用户
/// </summary>
[HttpDelete("{id}")]
[Authorize(Roles = "SuperAdmin")]
public async Task<ActionResult<ApiResponse<object>>> DeleteUser(int id)
{
var user = await _context.Users.FindAsync(id);
if (user == null)
{
return NotFound(ApiResponse<object>.NotFound("User not found"));
}
// Delete all refresh tokens
var tokens = await _context.RefreshTokens.Where(rt => rt.UserId == id).ToListAsync();
_context.RefreshTokens.RemoveRange(tokens);
_context.Users.Remove(user);
await _context.SaveChangesAsync();
_logger.LogInformation("User {UserId} deleted", id);
return Ok(ApiResponse<object>.Ok(new { }, "User deleted successfully"));
}
/// <summary>
/// [开发用] 重置用户密码 - 仅在开发环境可用
/// </summary>
[HttpPost("{id}/dev-reset-password")]
[AllowAnonymous]
public async Task<ActionResult<ApiResponse<object>>> DevResetPassword(int id, [FromBody] AdminResetPasswordRequest request)
{
// Only allow in development
var env = HttpContext.RequestServices.GetService<IWebHostEnvironment>();
if (!env!.IsDevelopment())
{
return NotFound();
}
var user = await _context.Users.FindAsync(id);
if (user == null)
{
return NotFound(ApiResponse<object>.NotFound("User not found"));
}
user.PasswordHash = _passwordHasher.HashPassword(request.NewPassword);
user.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return Ok(ApiResponse<object>.Ok(new { hash = user.PasswordHash }, "Password reset successfully"));
}
}
public class UpdateStatusRequest
{
public required string Status { get; set; }
}
public class AdminResetPasswordRequest
{
public required string NewPassword { get; set; }
}