You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django仅针对新生成的告警发送邮件提醒的实现问题

Alert告警仅新创建时发送邮件的实现方案

原有代码核心问题

  • 仅查询单条告警:使用latest('pk')仅获取最新一条告警,完全无法覆盖多告警同时触发的场景,会出现漏发
  • 标记位逻辑错误:使用created_on作为邮件发送判断依据不合理,该字段本质是告警创建时间,天然应该在告警生成时就赋值;且逻辑中额外调用Alert.create()会产生冗余的无效告警记录
  • 无并发控制:未加锁或事务控制,高并发场景下会出现同一条告警重复发送邮件的问题

注:latest('pk')本身语法没有问题,只是不符合当前批量处理未发送告警的业务需求。

正确实现方案

1. 模型层调整:新增专用发送状态标记

给Alert表新增独立的邮件发送状态字段,保证字段职责单一,不要复用created_on做业务状态判断:

from django.db import models

class Alert(models.Model):
    # 原有字段保持不变
    kiosk = models.ForeignKey(Kiosk, on_delete=models.CASCADE)
    description = models.TextField()
    alert_type = models.IntegerField()
    created_on = models.DateTimeField(auto_now_add=True) # 告警创建时自动赋值,无需手动修改
    # 新增邮件发送状态字段,默认未发送
    is_email_sent = models.BooleanField(default=False, help_text="告警邮件是否已发送")

2. 业务逻辑调整:批量处理未发送告警+并发控制

通过事务+行级锁避免并发重复发送,同时批量处理所有未发送的告警,覆盖多告警同时触发的场景:

from django.db import transaction
from django.utils import timezone

@transaction.atomic
def send_alert_emails_for_kiosk(kiosk):
    # 加行级锁,避免并发请求重复获取同一条未发送告警
    unsent_alerts = Alert.objects.select_for_update().filter(kiosk=kiosk, is_email_sent=False)
    # 无未发送告警直接返回
    if not unsent_alerts.exists():
        return []
    
    # 预计算时间,避免循环中重复计算消耗性能
    current_dt = timezone.now().astimezone(kiosk.location.timezone)
    current_time = current_dt.strftime('%I:%M %p')
    current_date = current_dt.strftime('%m/%d/%Y')
    sent_alert_pks = []

    for alert in unsent_alerts:
        # 构造邮件参数
        email_props = {
            'email': 'john@example.com',
            'data': {
                'facility': kiosk.location.name,
                'description': alert.description,
                'pk': alert.pk,
                'time': current_time,
                'date': current_date,
                'kioskName': kiosk.name,
                'alert_type_display': alert.alert_type_display
            }
        }
        # 发送邮件
        _send_email(
            [email_props['email']],
            {'data': email_props['data']},
            ALERT_TEMPLATE_ID
        )
        # 标记为已发送,仅更新指定字段提升性能
        alert.is_email_sent = True
        alert.save(update_fields=['is_email_sent'])
        sent_alert_pks.append(alert.pk)
    
    return sent_alert_pks

可选优化点

  • 如果需要合并多告警为单封邮件,可在循环中收集所有告警信息,循环结束后统一调用一次_send_email即可
  • 可新增email_sent_time字段记录邮件发送时间,方便后续问题排查
  • 若告警量较大,可改用异步任务(如Celery)处理邮件发送,避免阻塞主业务流程

内容的提问来源于stack exchange,提问作者Sog t

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 23:45:03