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
相关产品推荐
相关产品推荐

