This commit is contained in:
2026-07-24 12:42:51 +08:00 Unverified
commit 67905dfa16
56 changed files with 7630 additions and 0 deletions
@@ -0,0 +1,143 @@
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
using Jiaowu.Api.Domain.Identity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Jiaowu.Api.Controllers;
[ApiController]
[Authorize(Roles = SystemRoles.SuperAdmin)]
[Route("api/users")]
public sealed class UsersController(
UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<object>> GetUsers(CancellationToken cancellationToken)
{
var users = await userManager.Users.AsNoTracking()
.OrderBy(x => x.UserName)
.Select(x => new
{
x.Id, x.UserName, x.DisplayName, x.StaffNumber,
x.CollegeId, x.IsEnabled, x.LastLoginAt, x.CreatedAt
})
.ToListAsync(cancellationToken);
var result = new List<object>();
foreach (var user in users)
{
var identityUser = await userManager.FindByIdAsync(user.Id.ToString());
result.Add(new
{
user.Id, user.UserName, user.DisplayName, user.StaffNumber,
user.CollegeId, user.IsEnabled, user.LastLoginAt, user.CreatedAt,
Roles = identityUser is null
? []
: await userManager.GetRolesAsync(identityUser)
});
}
return Ok(result);
}
[HttpGet("roles")]
public async Task<ActionResult<object>> GetRoles(CancellationToken cancellationToken) =>
Ok(await roleManager.Roles.AsNoTracking()
.OrderBy(x => x.Name)
.Select(x => new { x.Name, x.Description, x.DataScope })
.ToListAsync(cancellationToken));
[HttpPost]
public async Task<ActionResult> Create(CreateUserRequest request)
{
var invalidRoles = request.Roles
.Except(SystemRoles.All, StringComparer.OrdinalIgnoreCase)
.ToArray();
if (invalidRoles.Length > 0)
return ValidationProblem($"无效角色:{string.Join("", invalidRoles)}");
var user = new ApplicationUser
{
UserName = request.UserName.Trim(),
DisplayName = request.DisplayName.Trim(),
StaffNumber = request.StaffNumber?.Trim(),
CollegeId = request.CollegeId,
LockoutEnabled = true,
IsEnabled = true
};
var result = await userManager.CreateAsync(user, request.Password);
if (!result.Succeeded)
return IdentityValidationProblem(result);
result = await userManager.AddToRolesAsync(user, request.Roles);
if (!result.Succeeded)
return IdentityValidationProblem(result);
return CreatedAtAction(nameof(GetUsers), new { id = user.Id }, new { user.Id });
}
[HttpPut("{id:guid}/status")]
public async Task<IActionResult> SetStatus(Guid id, SetUserStatusRequest request)
{
var user = await userManager.FindByIdAsync(id.ToString());
if (user is null) return NotFound();
if (User.FindFirstValue(ClaimTypes.NameIdentifier) == id.ToString() &&
!request.IsEnabled)
return ValidationProblem("不能停用当前登录账号。");
user.IsEnabled = request.IsEnabled;
var result = await userManager.UpdateAsync(user);
return result.Succeeded ? NoContent() : IdentityValidationProblem(result);
}
[HttpPut("{id:guid}/roles")]
public async Task<IActionResult> SetRoles(Guid id, SetRolesRequest request)
{
var user = await userManager.FindByIdAsync(id.ToString());
if (user is null) return NotFound();
var invalidRoles = request.Roles
.Except(SystemRoles.All, StringComparer.OrdinalIgnoreCase)
.ToArray();
if (invalidRoles.Length > 0)
return ValidationProblem($"无效角色:{string.Join("", invalidRoles)}");
var existing = await userManager.GetRolesAsync(user);
if (User.FindFirstValue(ClaimTypes.NameIdentifier) == id.ToString() &&
existing.Contains(SystemRoles.SuperAdmin) &&
!request.Roles.Contains(SystemRoles.SuperAdmin))
{
return ValidationProblem("不能移除当前账号的超级管理员角色。");
}
var removeResult = await userManager.RemoveFromRolesAsync(
user,
existing.Except(request.Roles));
if (!removeResult.Succeeded) return IdentityValidationProblem(removeResult);
var addResult = await userManager.AddToRolesAsync(
user,
request.Roles.Except(existing));
return addResult.Succeeded ? NoContent() : IdentityValidationProblem(addResult);
}
private ActionResult IdentityValidationProblem(IdentityResult result)
{
foreach (var error in result.Errors)
ModelState.AddModelError(error.Code, error.Description);
return ValidationProblem(ModelState);
}
}
public sealed record CreateUserRequest(
[Required, MaxLength(50)] string UserName,
[Required, MaxLength(50)] string DisplayName,
[Required, MinLength(8), MaxLength(100)] string Password,
[MaxLength(30)] string? StaffNumber,
Guid? CollegeId,
[MinLength(1)] string[] Roles);
public sealed record SetUserStatusRequest(bool IsEnabled);
public sealed record SetRolesRequest([MinLength(1)] string[] Roles);