Browse Source

refactor: 重构角色管理模块,分离命令和查询处理器

web
hyh 3 months ago
parent
commit
c4cdd28411
  1. 17
      src/CellularManagement.Application/Features/Auth/Commands/RefreshToken/RefreshTokenCommand.cs
  2. 122
      src/CellularManagement.Application/Features/Auth/Commands/RefreshToken/RefreshTokenCommandHandler.cs
  3. 22
      src/CellularManagement.Application/Features/Auth/Commands/RefreshToken/RefreshTokenResponse.cs
  4. 1
      src/CellularManagement.Application/Features/Auth/Commands/RegisterUser/RegisterUserCommandHandler.cs
  5. 13
      src/CellularManagement.Application/Features/Auth/Common/UserInfo.cs
  6. 34
      src/CellularManagement.Application/Features/Roles/Commands/RoleCommandHandler.cs
  7. 19
      src/CellularManagement.Application/Features/Roles/Queries/GetAllRolesQuery.cs
  8. 87
      src/CellularManagement.Application/Features/Roles/Queries/RoleQueryHandler.cs
  9. 1
      src/CellularManagement.Domain/Repositories/IUserRoleRepository.cs
  10. 4
      src/CellularManagement.Infrastructure/DependencyInjection.cs
  11. 50
      src/CellularManagement.Presentation/Controllers/AuthController.cs
  12. 45
      src/CellularManagement.Presentation/Controllers/RolesController.cs

17
src/CellularManagement.Application/Features/Auth/Commands/RefreshToken/RefreshTokenCommand.cs

@ -0,0 +1,17 @@
using MediatR;
using System.ComponentModel.DataAnnotations;
using CellularManagement.Application.Features.Auth.Commands.AuthenticateUser;
namespace CellularManagement.Application.Features.Auth.Commands.RefreshToken;
/// <summary>
/// 刷新令牌命令
/// </summary>
public class RefreshTokenCommand : IRequest<OperationResult<AuthenticateUserResponse>>
{
/// <summary>
/// 刷新令牌
/// </summary>
[Required(ErrorMessage = "刷新令牌不能为空")]
public string RefreshToken { get; set; } = string.Empty;
}

122
src/CellularManagement.Application/Features/Auth/Commands/RefreshToken/RefreshTokenCommandHandler.cs

@ -0,0 +1,122 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Application.Common;
using CellularManagement.Application.Services;
using System.Security.Claims;
using CellularManagement.Application.Features.Auth.Commands.AuthenticateUser;
using CellularManagement.Application.Features.Auth.Common;
using CellularManagement.Domain.Repositories;
namespace CellularManagement.Application.Features.Auth.Commands.RefreshToken;
/// <summary>
/// 刷新令牌命令处理器
/// </summary>
public class RefreshTokenCommandHandler : IRequestHandler<RefreshTokenCommand, OperationResult<AuthenticateUserResponse>>
{
private readonly IJwtProvider _jwtProvider;
private readonly ILogger<RefreshTokenCommandHandler> _logger;
private readonly IUserRoleRepository _userRoleRepository;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="jwtProvider">JWT提供者</param>
/// <param name="logger">日志记录器</param>
/// <param name="userRoleRepository">用户角色仓储</param>
public RefreshTokenCommandHandler(
IJwtProvider jwtProvider,
ILogger<RefreshTokenCommandHandler> logger,
IUserRoleRepository userRoleRepository)
{
_jwtProvider = jwtProvider;
_logger = logger;
_userRoleRepository = userRoleRepository;
}
/// <summary>
/// 处理刷新令牌请求
/// </summary>
/// <param name="request">刷新令牌命令</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>刷新令牌结果</returns>
public async Task<OperationResult<AuthenticateUserResponse>> Handle(
RefreshTokenCommand request,
CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("开始处理刷新令牌请求");
// 验证刷新令牌
if (!_jwtProvider.ValidateToken(request.RefreshToken))
{
_logger.LogWarning("刷新令牌验证失败");
return OperationResult<AuthenticateUserResponse>.CreateFailure("无效的刷新令牌");
}
// 获取令牌中的声明
var claims = _jwtProvider.GetClaimsFromToken(request.RefreshToken);
if (!claims.Any())
{
_logger.LogWarning("刷新令牌中未找到声明");
return OperationResult<AuthenticateUserResponse>.CreateFailure("无效的刷新令牌");
}
// 检查令牌类型
var tokenType = _jwtProvider.GetTokenType(request.RefreshToken);
if (tokenType != "refresh_token")
{
_logger.LogWarning("令牌类型不是刷新令牌");
return OperationResult<AuthenticateUserResponse>.CreateFailure("无效的刷新令牌");
}
// 检查令牌是否在黑名单中
if (_jwtProvider.GetAllClaims(request.RefreshToken).ContainsKey("blacklisted"))
{
_logger.LogWarning("刷新令牌已被列入黑名单");
return OperationResult<AuthenticateUserResponse>.CreateFailure("刷新令牌已被撤销");
}
// 获取用户ID
var userId = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userId))
{
_logger.LogWarning("刷新令牌中未找到用户ID");
return OperationResult<AuthenticateUserResponse>.CreateFailure("无效的刷新令牌");
}
// 获取用户角色
var roles = await _userRoleRepository.GetUserRolesAsync(userId, cancellationToken);
// 生成新的访问令牌
var accessToken = _jwtProvider.GenerateAccessToken(claims);
// 生成新的刷新令牌
var refreshToken = _jwtProvider.GenerateRefreshToken(claims);
// 撤销旧的刷新令牌
_jwtProvider.RevokeToken(request.RefreshToken);
// 获取令牌过期时间
var expiresAt = _jwtProvider.GetTokenExpiration(accessToken);
var permissions = new Dictionary<string, bool>();
// 创建用户信息
var userInfo = new UserInfo(
userId,
claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value ?? string.Empty,
claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value ?? string.Empty,
claims.FirstOrDefault(c => c.Type == ClaimTypes.MobilePhone)?.Value,
roles, permissions);
_logger.LogInformation("刷新令牌成功");
return OperationResult<AuthenticateUserResponse>.CreateSuccess(
new AuthenticateUserResponse(accessToken, refreshToken, expiresAt, userInfo));
}
catch (Exception ex)
{
_logger.LogError(ex, "刷新令牌时发生异常");
return OperationResult<AuthenticateUserResponse>.CreateFailure("刷新令牌失败,请稍后重试");
}
}
}

22
src/CellularManagement.Application/Features/Auth/Commands/RefreshToken/RefreshTokenResponse.cs

@ -0,0 +1,22 @@
namespace CellularManagement.Application.Features.Auth.Commands.RefreshToken;
/// <summary>
/// 刷新令牌响应
/// </summary>
public class RefreshTokenResponse
{
/// <summary>
/// 访问令牌
/// </summary>
public string AccessToken { get; set; } = string.Empty;
/// <summary>
/// 刷新令牌
/// </summary>
public string RefreshToken { get; set; } = string.Empty;
/// <summary>
/// 过期时间(分钟)
/// </summary>
public int ExpiresIn { get; set; }
}

1
src/CellularManagement.Application/Features/Auth/Commands/RegisterUser/RegisterUserCommandHandler.cs

@ -91,6 +91,7 @@ public sealed class RegisterUserCommandHandler : IRequestHandler<RegisterUserCom
// 添加用户角色关系
await _userRoleRepository.AddAsync(userRole, cancellationToken);
await _unitOfWork.SaveChangesAsync(cancellationToken); // 必须有
}, cancellationToken: cancellationToken);
_logger.LogInformation("用户 {UserName} 注册成功", request.UserName);

13
src/CellularManagement.Application/Features/Auth/Common/UserInfo.cs

@ -27,4 +27,15 @@ public sealed record UserInfo(
/// <summary>
/// 用户角色列表
/// </summary>
IList<string> Roles);
IList<string> Roles,
/// <summary>
/// 用户权限字典
/// 键为权限标识符,值为是否拥有该权限
/// 例如:
/// {
/// "dashboard.view": true,
/// "users.view": true
/// }
/// </summary>
IDictionary<string, bool> Permissions);

34
src/CellularManagement.Application/Features/Roles/Commands/RoleCommandHandler.cs

@ -2,10 +2,8 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using MediatR;
using CellularManagement.Domain.Entities;
using CellularManagement.Domain.Repositories;
using CellularManagement.Application.Features.Roles.Commands.CreateRole;
using CellularManagement.Application.Features.Roles.Commands.DeleteRole;
using CellularManagement.Application.Features.Roles.Queries.GetRole;
namespace CellularManagement.Application.Features.Roles.Commands;
@ -14,8 +12,7 @@ namespace CellularManagement.Application.Features.Roles.Commands;
/// </summary>
public sealed class RoleCommandHandler :
IRequestHandler<CreateRoleCommand, OperationResult<CreateRoleResponse>>,
IRequestHandler<DeleteRoleCommand, OperationResult<DeleteRoleResponse>>,
IRequestHandler<GetRoleQuery, OperationResult<GetRoleResponse>>
IRequestHandler<DeleteRoleCommand, OperationResult<DeleteRoleResponse>>
{
private readonly RoleManager<AppRole> _roleManager;
private readonly ILogger<RoleCommandHandler> _logger;
@ -112,33 +109,4 @@ public sealed class RoleCommandHandler :
return OperationResult<DeleteRoleResponse>.CreateFailure("删除角色失败,请稍后重试");
}
}
/// <summary>
/// 处理获取角色请求
/// </summary>
public async Task<OperationResult<GetRoleResponse>> Handle(
GetRoleQuery request,
CancellationToken cancellationToken)
{
try
{
// 查找角色
var role = await _roleManager.FindByIdAsync(request.RoleId);
if (role == null)
{
_logger.LogWarning("角色 {RoleId} 不存在", request.RoleId);
return OperationResult<GetRoleResponse>.CreateFailure("角色不存在");
}
_logger.LogInformation("获取角色 {RoleId} 成功", request.RoleId);
return OperationResult<GetRoleResponse>.CreateSuccess(
new GetRoleResponse(role.Id, role.Name, role.Description));
}
catch (Exception ex)
{
_logger.LogError(ex, "获取角色 {RoleId} 失败", request.RoleId);
return OperationResult<GetRoleResponse>.CreateFailure("获取角色失败,请稍后重试");
}
}
}

19
src/CellularManagement.Application/Features/Roles/Queries/GetAllRolesQuery.cs

@ -0,0 +1,19 @@
using MediatR;
using CellularManagement.Application.Common;
namespace CellularManagement.Application.Features.Roles.Queries;
/// <summary>
/// 获取所有角色的查询
/// </summary>
public record GetAllRolesQuery : IRequest<OperationResult<GetAllRolesResponse>>;
/// <summary>
/// 获取所有角色的响应
/// </summary>
public record GetAllRolesResponse(IEnumerable<RoleDto> Roles);
/// <summary>
/// 角色数据传输对象
/// </summary>
public record RoleDto(string Id, string Name, string Description);

87
src/CellularManagement.Application/Features/Roles/Queries/RoleQueryHandler.cs

@ -0,0 +1,87 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using MediatR;
using CellularManagement.Domain.Entities;
using CellularManagement.Application.Features.Roles.Queries.GetRole;
using CellularManagement.Application.Features.Roles.Queries;
namespace CellularManagement.Application.Features.Roles.Queries;
/// <summary>
/// 角色查询处理器
/// </summary>
public sealed class RoleQueryHandler :
IRequestHandler<GetRoleQuery, OperationResult<GetRoleResponse>>,
IRequestHandler<GetAllRolesQuery, OperationResult<GetAllRolesResponse>>
{
private readonly RoleManager<AppRole> _roleManager;
private readonly ILogger<RoleQueryHandler> _logger;
/// <summary>
/// 初始化处理器
/// </summary>
public RoleQueryHandler(
RoleManager<AppRole> roleManager,
ILogger<RoleQueryHandler> logger)
{
_roleManager = roleManager;
_logger = logger;
}
/// <summary>
/// 处理获取角色请求
/// </summary>
public async Task<OperationResult<GetRoleResponse>> Handle(
GetRoleQuery request,
CancellationToken cancellationToken)
{
try
{
// 查找角色
var role = await _roleManager.FindByIdAsync(request.RoleId);
if (role == null)
{
_logger.LogWarning("角色 {RoleId} 不存在", request.RoleId);
return OperationResult<GetRoleResponse>.CreateFailure("角色不存在");
}
_logger.LogInformation("获取角色 {RoleId} 成功", request.RoleId);
return OperationResult<GetRoleResponse>.CreateSuccess(
new GetRoleResponse(role.Id, role.Name, role.Description));
}
catch (Exception ex)
{
_logger.LogError(ex, "获取角色 {RoleId} 失败", request.RoleId);
return OperationResult<GetRoleResponse>.CreateFailure("获取角色失败,请稍后重试");
}
}
/// <summary>
/// 处理获取所有角色请求
/// </summary>
public async Task<OperationResult<GetAllRolesResponse>> Handle(
GetAllRolesQuery request,
CancellationToken cancellationToken)
{
try
{
var roles = _roleManager.Roles.ToList();
var roleDtos = roles.Select(role => new RoleDto(
role.Id,
role.Name,
role.Description
));
_logger.LogInformation("成功获取所有角色,共 {Count} 个", roles.Count);
return OperationResult<GetAllRolesResponse>.CreateSuccess(
new GetAllRolesResponse(roleDtos));
}
catch (Exception ex)
{
_logger.LogError(ex, "获取所有角色失败");
return OperationResult<GetAllRolesResponse>.CreateFailure("获取角色列表失败,请稍后重试");
}
}
}

1
src/CellularManagement.Domain/Repositories/IUserRoleRepository.cs

@ -65,4 +65,5 @@ public interface IUserRoleQueryRepository : IQueryRepository<UserRole>
/// </summary>
public interface IUserRoleRepository : IUserRoleCommandRepository, IUserRoleQueryRepository
{
}

4
src/CellularManagement.Infrastructure/DependencyInjection.cs

@ -12,6 +12,7 @@ using Scrutor;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using CellularManagement.Infrastructure.Configurations;
namespace CellularManagement.Infrastructure;
@ -111,6 +112,9 @@ public static class DependencyInjection
// 注册缓存服务
services.AddScoped<ICacheService, CacheService>();
// 配置认证设置
services.Configure<AuthConfiguration>(configuration.GetSection("Auth"));
// 自动注册服务
services
.Scan(action =>

50
src/CellularManagement.Presentation/Controllers/AuthController.cs

@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc;
using MediatR;
using CellularManagement.Application.Features.Auth.Commands.AuthenticateUser;
using CellularManagement.Application.Features.Auth.Commands.RegisterUser;
using CellularManagement.Application.Features.Auth.Commands.RefreshToken;
using CellularManagement.Application.Common;
using Microsoft.Extensions.Logging;
using CellularManagement.Application.Services;
@ -170,4 +171,53 @@ public class AuthController : ApiController
OperationResult<RegisterUserResponse>.CreateFailure("系统错误,请稍后重试"));
}
}
/// <summary>
/// 刷新令牌
/// </summary>
/// <remarks>
/// 示例请求:
///
/// POST /api/auth/refresh-token
/// {
/// "refreshToken": "刷新令牌"
/// }
///
/// </remarks>
/// <param name="command">刷新令牌命令</param>
/// <returns>
/// 刷新结果,包含:
/// - 成功:返回用户信息和新的访问令牌
/// - 失败:返回错误信息
/// </returns>
/// <response code="200">刷新成功,返回用户信息和新的令牌</response>
/// <response code="400">刷新失败,返回错误信息</response>
[HttpPost()]
[ProducesResponseType(typeof(OperationResult<AuthenticateUserResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(OperationResult<AuthenticateUserResponse>), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<OperationResult<AuthenticateUserResponse>>> RefreshToken([FromBody] RefreshTokenCommand command)
{
try
{
var result = await mediator.Send(command);
if (result.IsSuccess)
{
_logger.LogInformation("令牌刷新成功");
}
else
{
_logger.LogWarning("令牌刷新失败: {Error}",
result.ErrorMessages?.FirstOrDefault());
}
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "刷新令牌时发生异常");
return StatusCode(StatusCodes.Status500InternalServerError,
OperationResult<AuthenticateUserResponse>.CreateFailure("系统错误,请稍后重试"));
}
}
}

45
src/CellularManagement.Presentation/Controllers/RolesController.cs

@ -4,6 +4,7 @@ using MediatR;
using CellularManagement.Application.Features.Roles.Commands.CreateRole;
using CellularManagement.Application.Features.Roles.Commands.DeleteRole;
using CellularManagement.Application.Features.Roles.Queries.GetRole;
using CellularManagement.Application.Features.Roles.Queries;
using CellularManagement.Application.Common;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
@ -198,4 +199,48 @@ public class RolesController : ApiController
OperationResult<GetRoleResponse>.CreateFailure("系统错误,请稍后重试"));
}
}
/// <summary>
/// 获取所有角色
/// </summary>
/// <remarks>
/// 示例请求:
///
/// GET /api/roles
///
/// </remarks>
/// <returns>
/// 所有角色信息列表
/// </returns>
/// <response code="200">获取成功,返回角色列表</response>
/// <response code="500">服务器错误</response>
[HttpGet]
[ProducesResponseType(typeof(OperationResult<GetAllRolesResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(OperationResult<GetAllRolesResponse>), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<OperationResult<GetAllRolesResponse>>> GetAllRoles()
{
try
{
var query = new GetAllRolesQuery();
var result = await mediator.Send(query);
if (result.IsSuccess)
{
_logger.LogInformation("成功获取所有角色");
}
else
{
_logger.LogWarning("获取所有角色失败: {Error}",
result.ErrorMessages?.FirstOrDefault());
}
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取所有角色时发生异常");
return StatusCode(StatusCodes.Status500InternalServerError,
OperationResult<GetAllRolesResponse>.CreateFailure("系统错误,请稍后重试"));
}
}
}
Loading…
Cancel
Save