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

如何在Django视图中实现等待Webhook回调或超时处理逻辑

方案选型判断

你最初设想的「每次等待回调时创建异步进程/线程」的方案完全不适配你的场景,尤其不适合Heroku部署环境,核心问题如下:

  • Heroku的Web dyno默认30分钟无请求就会休眠,运行中的后台线程/进程会被直接回收,数小时级别的超时等待逻辑根本无法完整执行
  • 每发送一条消息就占用一个独立线程/进程,并发稍高就会耗尽服务器内存、连接数资源,稳定性极差
  • 内存中运行的线程/进程没有持久化存储,一旦服务重启、版本部署,所有等待中的任务会全部丢失,提醒逻辑直接失效

正确的实现思路是用持久化状态存储+延迟任务调度+Webhook状态更新的架构,不需要常驻等待的进程/线程,资源占用极低,完全适配Heroku的部署规则。


具体实现步骤

1. 定义数据模型存储消息状态

首先在Django模型里记录每一条发出的WhatsApp消息的状态、超时时间、提醒内容,所有状态落库,不会因为服务重启丢失:

# models.py
from django.db import models
from django.utils import timezone

class WhatsAppMessage(models.Model):
    STATUS_CHOICES = [
        ("SENT", "已发送待回复"),
        ("REPLIED", "客户已回复"),
        ("REMINDER_SENT", "已发送提醒"),
    ]
    customer_phone = models.CharField(max_length=20, verbose_name="客户手机号")
    message_content = models.TextField(verbose_name="初始发送内容")
    sent_at = models.DateTimeField(default=timezone.now, verbose_name="发送时间")
    timeout_at = models.DateTimeField(verbose_name="超时触发提醒时间")
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="SENT")
    reminder_content = models.TextField(verbose_name="提醒内容")

2. 实现延迟提醒任务

用Celery做任务队列(Heroku有官方Redis插件作为任务存储,免费档足够支撑中小规模使用),提醒任务只在超时时间点触发,不需要提前占用资源等待:

# tasks.py
from celery import shared_task
from django.utils import timezone
from .models import WhatsAppMessage
# 替换成你自己封装的WhatsApp官方API发消息方法
from .utils import send_whatsapp_message

@shared_task
def send_timeout_reminder(message_id):
    # 任务触发时先校验消息存在性
    try:
        msg = WhatsAppMessage.objects.get(id=message_id)
    except WhatsAppMessage.DoesNotExist:
        return
    # 二次校验状态:如果客户已经回复,直接终止逻辑不发提醒
    if msg.status != "SENT":
        return
    # 校验时间避免任务提前执行导致误发
    if timezone.now() < msg.timeout_at:
        return
    # 调用API发提醒
    send_whatsapp_message(to=msg.customer_phone, content=msg.reminder_content)
    msg.status = "REMINDER_SENT"
    msg.save()

3. 初始消息发送视图

给客户发初始消息时,只需要落库+投递延迟任务,不需要开线程等待:

# views.py
from django.utils import timezone
from datetime import timedelta
from django.http import JsonResponse
from .models import WhatsAppMessage
from .tasks import send_timeout_reminder
from .utils import send_whatsapp_message

def send_customer_message(request):
    # 省略参数合法性校验逻辑
    customer_phone = request.POST.get("phone")
    initial_content = request.POST.get("content")
    reminder_content = request.POST.get("reminder_content")
    timeout_hours = int(request.POST.get("timeout_hours", 2)) # 默认2小时超时

    # 先调用WhatsApp API发初始消息
    send_whatsapp_message(to=customer_phone, content=initial_content)
    # 消息记录落库
    msg = WhatsAppMessage.objects.create(
        customer_phone=customer_phone,
        message_content=initial_content,
        timeout_at=timezone.now() + timedelta(hours=timeout_hours),
        reminder_content=reminder_content
    )
    # 投递延迟任务,到超时时间点自动执行
    send_timeout_reminder.apply_async(args=[msg.id], eta=msg.timeout_at)
    return JsonResponse({"code": 0, "message_id": msg.id})

4. WhatsApp Webhook接收视图

收到WhatsApp推送的客户回复事件时,直接把对应消息的状态更新为已回复即可,延迟任务触发时会自动跳过已回复的消息,不会发提醒:

# views.py
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from .models import WhatsAppMessage
import json

@csrf_exempt
def whatsapp_event_webhook(request):
    # 处理WhatsApp要求的Webhook校验逻辑
    if request.method == "GET":
        return HttpResponse(request.GET.get("hub.challenge"))
    
    # 解析客户回复的事件 payload
    payload = json.loads(request.body)
    # 省略官方payload解析逻辑,提取回复客户的手机号
    customer_phone = extract_customer_phone(payload)
    # 找到该客户最新的一条待回复消息,更新状态
    latest_pending = WhatsAppMessage.objects.filter(
        customer_phone=customer_phone,
        status="SENT"
    ).order_by("-sent_at").first()
    if latest_pending:
        latest_pending.status = "REPLIED"
        latest_pending.save()
    return HttpResponse(status=200)

Heroku部署注意事项

常规Celery方案配置

  • 开通Heroku Redis插件作为Celery的任务存储
  • 在Procfile中添加worker进程配置:worker: celery -A 你的项目名 worker --loglevel=info
  • 在Heroku后台手动开启worker dyno(默认是关闭状态)

轻量低成本方案(适合小流量场景)

如果不想维护Celery和Redis,可以直接用Heroku官方免费的Scheduler插件:

  • 设置每10分钟触发一次Django自定义管理命令
  • 管理命令直接扫描所有已到超时时间、状态仍为SENT的消息,发送提醒并更新状态
  • 这个方案不需要额外的组件资源,唯一的缺点是提醒触发时间最多有10分钟的误差,对于数小时级别的超时场景完全可以接受

对应的管理命令示例:

# management/commands/send_pending_reminders.py
from django.core.management.base import BaseCommand
from django.utils import timezone
from .models import WhatsAppMessage
from .utils import send_whatsapp_message

class Command(BaseCommand):
    def handle(self, *args, **options):
        overdue_msgs = WhatsAppMessage.objects.filter(
            status="SENT",
            timeout_at__lte=timezone.now()
        )
        for msg in overdue_msgs:
            send_whatsapp_message(to=msg.customer_phone, content=msg.reminder_content)
            msg.status = "REMINDER_SENT"
            msg.save()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 20:01:21