using Dpz.Core.MessageQueue.Abstractions;
using Dpz.Core.MessageQueue.Models;
using Dpz.Core.Public.ViewModel.Messages;
using Dpz.Core.Web.Jobs.Services;
using ZiggyCreatures.Caching.Fusion;
namespace Dpz.Core.Web.Jobs.MessageHandlers;
/// <summary>
/// 发送邮件验证码消息处理器
/// 负责发送邮箱验证码并将验证码存储到缓存中
/// </summary>
public class SendEmailVerifyCodeHandler(
EmailSenderHelper emailSenderHelper,
IFusionCache fusionCache,
ILogger<SendEmailVerifyCodeHandler> logger
) : IMessageHandler<SendEmailVerifyCodeMessage>
{
/// <summary>
/// 验证码缓存key前缀
/// </summary>
private const string VerifyCodeCachePrefix = "EmailVerifyCode:";
/// <summary>
/// 验证码有效期
/// </summary>
private static readonly TimeSpan VerifyCodeExpiration = TimeSpan.FromMinutes(30);
public async Task<MessageHandlerResult> HandleAsync(
SendEmailVerifyCodeMessage message,
CancellationToken cancellationToken
)
{
try
{
// 1. 存储验证码到缓存
await StoreVerifyCodeAsync(message.Email, message.VerifyCode, cancellationToken);
// 2. 发送验证码邮件(使用 EmailSenderHelper 统一处理)
await emailSenderHelper.SendVerifyCodeEmailAsync(
message.Email,
message.VerifyCode,
VerifyCodeExpiration,
cancellationToken
);
logger.LogInformation(
"邮件验证码发送成功: Email={Email}, Purpose={Purpose}",
message.Email,
message.Purpose ?? "未指定"
);
return MessageHandlerResult.Ok();
}
catch (Exception ex)
{
logger.LogError(
ex,
"邮件验证码发送失败: Email={Email}, Purpose={Purpose}",
message.Email,
message.Purpose
);
return MessageHandlerResult.Fail($"邮件验证码发送失败: {ex.Message}");
}
}
/// <summary>
/// 存储验证码到缓存
/// </summary>
private async Task StoreVerifyCodeAsync(
string email,
string verifyCode,
CancellationToken cancellationToken
)
{
var cacheKey = $"{VerifyCodeCachePrefix}{email.ToLower()}";
await fusionCache.SetAsync(
cacheKey,
verifyCode,
options => options.SetDuration(VerifyCodeExpiration),
token: cancellationToken
);
logger.LogDebug(
"验证码已存储到缓存: Email={Email}, ExpiresIn={Minutes}分钟",
email,
VerifyCodeExpiration.TotalMinutes
);
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个邮件验证码消息处理器的实现,负责处理发送邮箱验证码的异步消息。
核心功能
该类实现了 IMessageHandler<SendEmailVerifyCodeMessage> 接口,用于:
- 接收验证码发送请求消息
- 将验证码缓存到 Redis/内存
- 通过邮件服务发送验证码
类结构分析
1. 依赖注入(构造函数)
public class SendEmailVerifyCodeHandler(
EmailSenderHelper emailSenderHelper, // 邮件发送辅助类
IFusionCache fusionCache, // 缓存服务(支持多级缓存)
ILogger<SendEmailVerifyCodeHandler> logger // 日志记录器
)
使用 C# 12 主构造函数语法,简洁地注入三个依赖项。
2. 常量配置
private const string VerifyCodeCachePrefix = "EmailVerifyCode:"; // 缓存键前缀
private static readonly TimeSpan VerifyCodeExpiration = TimeSpan.FromMinutes(30); // 30分钟有效期
- 缓存键格式:
EmailVerifyCode:{email}(例如:EmailVerifyCode:user@example.com) - 有效期:验证码 30 分钟后自动过期
3. 主处理方法 HandleAsync
处理流程:
接收消息 → 存储验证码到缓存 → 发送邮件 → 返回结果
关键步骤:
步骤 1:缓存验证码
await StoreVerifyCodeAsync(message.Email, message.VerifyCode, cancellationToken);
- 将验证码存入缓存(FusionCache)
- 用于后续验证时对比
步骤 2:发送邮件
await emailSenderHelper.SendVerifyCodeEmailAsync(
message.Email,
message.VerifyCode,
VerifyCodeExpiration,
cancellationToken
);
- 调用邮件服务发送验证码
- 邮件内容包含验证码和有效期提示
步骤 3:日志记录
logger.LogInformation("邮件验证码发送成功: Email={Email}, Purpose={Purpose}", ...);
- 记录发送成功日志
Purpose可能用于标识验证码用途(注册/重置密码等)
4. 私有方法 StoreVerifyCodeAsync
private async Task StoreVerifyCodeAsync(string email, string verifyCode, CancellationToken cancellationToken)
{
var cacheKey = $"{VerifyCodeCachePrefix}{email.ToLower()}"; // 生成缓存键
await fusionCache.SetAsync(
cacheKey,
verifyCode,
options => options.SetDuration(VerifyCodeExpiration), // 设置 30 分钟过期
token: cancellationToken
);
}
关键点:
- 邮箱统一小写:
email.ToLower()避免大小写导致的键冲突 - 自动过期:利用 FusionCache 的 TTL 机制,30 分钟后自动删除
异常处理
catch (Exception ex)
{
logger.LogError(ex, "邮件验证码发送失败: Email={Email}, Purpose={Purpose}", ...);
return MessageHandlerResult.Fail($"邮件验证码发送失败: {ex.Message}");
}
- 捕获所有异常(网络错误、邮件服务故障等)
- 记录详细错误日志
- 返回失败结果(消息队列可根据此结果决定是否重试)
技术亮点
| 技术 | 作用 |
|---|---|
| FusionCache | 多级缓存(内存+分布式),提高性能 |
| 异步消息处理 | 解耦邮件发送逻辑,提高系统响应速度 |
| 日志结构化 | 使用占位符记录关键信息,便于日志分析 |
| CancellationToken | 支持优雅取消操作 |
使用场景
典型的使用流程:
用户请求验证码
↓
API 发布 SendEmailVerifyCodeMessage 消息
↓
消息队列调用此 Handler
↓
缓存验证码 → 发送邮件
↓
用户收到邮件 → 输入验证码 → 系统从缓存验证
潜在改进点
- 幂等性保护:防止重复发送(可增加请求 ID 去重)
- 频率限制:防止恶意攻击(建议在上层 API 实现)
- 验证码格式:可考虑加密存储验证码
整体来说,这是一个标准的异步消息处理实现,代码清晰、职责单一、易于维护。
AI 正在分析代码…
评论加载中...