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

Django Python中如何实现延时触发函数自动取消超时未处理订单

Django 订单24小时未处理自动取消实现方案

1. 调整订单模型字段

首先需要在订单表中补充必要的状态和时间字段,用于后续的超时判断:

from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model

User = get_user_model()

class Order(models.Model):
    STATUS_CHOICES = (
        ("pending", "待卖家接单"),
        ("accepted", "卖家已接单待处理"),
        ("processed", "卖家已完成处理"),
        ("cancelled", "已取消"),
    )
    buyer = models.ForeignKey(User, on_delete=models.CASCADE, related_name="purchase_orders")
    seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sale_orders")
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="pending")
    # 卖家接单时间,仅当状态变为accepted时赋值
    accepted_at = models.DateTimeField(null=True, blank=True)
    # 可选:存储对应Celery延时任务ID,用于提前撤销任务
    cancel_task_id = models.CharField(max_length=64, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def accept(self):
        """卖家接单方法"""
        self.status = "accepted"
        self.accepted_at = timezone.now()
        self.save()

2. 两种主流实现方式

根据你的项目规模可以选择以下任意一种方案:

方案一:Celery延时任务(适合中小项目)

这是最常用的实现方式,每个订单接单后单独触发一个24小时后执行的取消任务,实时性高。

  • 首先确保你的项目已经完成Celery的基础配置
  • 编写超时取消任务,在对应app的tasks.py中新增:
from celery import shared_task
from django.utils import timezone
from .models import Order

@shared_task
def cancel_overdue_order(order_id):
    try:
        order = Order.objects.get(id=order_id, status="accepted")
        # 二次校验时间,避免异常场景下任务提前执行
        if timezone.now() - order.accepted_at >= timezone.timedelta(hours=24):
            order.status = "cancelled"
            order.save()
            # 此处可扩展业务逻辑:退还用户预付款、推送订单取消通知等
    except Order.DoesNotExist:
        return
  • 改造订单的accept方法,接单后提交延时任务:
from celery.result import AsyncResult

def accept(self):
    self.status = "accepted"
    self.accepted_at = timezone.now()
    # 提交24小时后执行的取消任务,存储任务ID
    task = cancel_overdue_order.apply_async((self.id,), countdown=24 * 3600)
    self.cancel_task_id = task.id
    self.save()

def process(self):
    """卖家处理完成订单的方法,提前取消延时任务"""
    self.status = "processed"
    # 撤销未执行的取消任务
    if self.cancel_task_id:
        AsyncResult(self.cancel_task_id).revoke(terminate=True)
    self.save()

方案二:定时任务轮询(适合中大型项目)

如果你的订单量很大,维护大量延时任务会增加额外开销,可以选择定时扫描批量处理超时订单,稳定性更高,仅会有最多一个扫描周期的误差,一般业务都可接受。

  • 首先编写Django自定义命令,在对应app下新建management/commands/cancel_overdue_orders.py:
from django.core.management.base import BaseCommand
from django.utils import timezone
from orders.models import Order

class Command(BaseCommand):
    help = "批量取消超过24小时未处理的已接单订单"

    def handle(self, *args, **options):
        overdue_time = timezone.now() - timezone.timedelta(hours=24)
        # 批量更新所有符合条件的超时订单
        update_count = Order.objects.filter(
            status="accepted",
            accepted_at__lte=overdue_time
        ).update(status="cancelled")
        self.stdout.write(self.style.SUCCESS(f"共取消{update_count}个超时订单"))
  • 配置定时任务:你可以用Linux系统自带的crontab,或者Celery Beat,设置每10分钟执行一次python manage.py cancel_overdue_orders即可,扫描周期可以根据你的业务对超时时间精度的要求调整。

3. 注意事项

  • 所有时间操作都要使用Django提供的timezone.now(),不要使用Python原生的datetime.now(),避免时区不一致导致的时间计算错误
  • 任何修改订单状态的操作都要带上状态条件校验,避免把已经处理完成的订单错误取消
  • 如果涉及资金操作,建议给订单取消的逻辑加分布式锁,避免重复操作引发的资金问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 17:45:03