4.6 KiB
4.6 KiB
找回密码邮件发送问题修复计划
问题描述
用户在找回密码页面输入邮箱后,页面显示发送成功,但实际未收到验证码邮件。
根因分析
当前代码流程
- 用户输入邮箱 → 前端调用
/api/forgot_password_send_code/ - 后端
ForgotPasswordSendCodeAPIView生成验证码并保存到缓存 - 调用
submit_task(send_reset_password_email_task, to_email, code)发送邮件 - 前端显示成功
发现的问题
1. Celery Worker 可能未运行
- 问题:
submit_task函数先尝试apply_async(异步),如果成功则直接返回 - 风险:如果 Redis 可达但无 worker 运行,任务会永远留在队列中不被执行
- 验证:本地测试显示
apply_async返回 PENDING,说明任务已入队但可能无 worker 消费 - fallback 机制:
submit_task只在apply_async抛异常或超时时才降级为同步apply
2. QQ Mail 发送限制
- 问题:QQ Mail 对同一地址发送频率有限制
- 风险:短时间内多次发送可能被 QQ Mail 服务器静默丢弃
- 特征:SMTP 连接成功,但邮件未投递
3. 邮件进入垃圾箱
- 问题:验证码邮件可能被收件方归类为垃圾邮件
- 风险:用户未检查垃圾邮件箱
修复方案
修复1:确保邮件同步发送(解决 Celery 不可用问题)
文件:utils/safe_task.py
修改:将 submit_task 改为默认同步发送,失败时降级为异步
理由:
- 验证码邮件需要立即发送,不能依赖 Celery worker 的可用性
- 同步发送确保邮件立即投递
- 如果同步失败,再尝试异步作为 fallback
def submit_task(task, *args, **kwargs):
"""发送任务,优先同步执行,失败时降级为异步"""
try:
# 优先同步执行,确保立即发送
return task.apply(*args, **kwargs)
except Exception as exc:
logger.warning('sync send failed, fallback to async: %s', exc)
try:
return task.apply_async(args, kwargs, kwargs.get('_timeout', SUBMIT_TIMEOUT))
except Exception as exc2:
logger.error('async fallback also failed: %s', exc2)
return None
修复2:增加邮件发送状态反馈
文件:user/views/user.py → ForgotPasswordSendCodeAPIView
修改:
- 在
submit_task调用后检查返回结果 - 如果同步发送失败,返回更明确的错误信息
- 添加日志记录发送状态
result = submit_task(send_reset_password_email_task, to_email, code)
if result is None:
logger.warning(f'Reset password email failed to send: {to_email}')
# 不暴露内部错误给用户,但记录日志
修复3:前端增加提示
文件:src/pages/ForgotPassword/ForgotPassword.tsx
修改:
- 发送成功后提示用户检查垃圾邮件箱
- 如果发送失败,提示用户稍后重试
if (res?.code === 10009) {
message.success(t('forgotPassword.successCodeSent'));
message.info(t('forgotPassword.checkSpam')); // 新增:提示检查垃圾邮件
// ...
}
修复4:QQ Mail 发送频率优化(可选)
文件:utils/safe_task.py
修改:
- 添加发送间隔限制,避免被 QQ Mail 拦截
- 对同一邮箱,短时间内不重复发送
涉及文件
| 文件 | 修改类型 | 说明 |
|---|---|---|
utils/safe_task.py |
修改 | 改为同步优先发送 |
user/views/user.py |
修改 | 增加发送状态日志 |
src/pages/ForgotPassword/ForgotPassword.tsx |
修改 | 增加垃圾邮件提示 |
src/locales/zh.json |
修改 | 添加提示文本 |
验证步骤
-
本地测试
- 启动 Django 开发服务器
- 访问找回密码页面
- 输入已注册邮箱
- 检查是否收到邮件
- 检查 Django 日志是否有发送记录
-
Celery 未运行场景
- 确保本地无 Celery worker 运行
- 测试邮件发送是否正常
-
同一邮箱多次发送
- 连续发送3次验证码
- 检查是否每次都能收到
- 检查是否有频率限制
-
垃圾邮件检查
- 检查 QQ Mail 垃圾邮件箱
- 确认邮件是否被归类为垃圾邮件
风险评估
- 同步发送会增加请求响应时间(约1-2秒),但验证码邮件场景下可接受
- 如果 SMTP 服务器响应慢,可能导致请求超时
- 需要监控邮件发送失败率
备选方案
如果同步发送影响性能,可以考虑:
- 使用数据库记录发送状态
- 前端轮询检查邮件是否发送成功
- 提供"重新发送"按钮