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

在DRF中实现课程购买优惠券的使用次数与有效期限制

DRF优惠券系统的有效性限制实现方案

要实现基于使用人数(如前50名用户)和时间段(如未来一周)的优惠券有效性限制,你可以从以下几个核心环节入手:

一、数据库模型设计

首先需要在模型中存储优惠券的限制规则和使用状态:

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

class Coupon(models.Model):
    code = models.CharField(max_length=50, unique=True)
    max_usage = models.PositiveIntegerField(default=0, help_text="允许使用的最大用户数,0表示无限制")
    used_count = models.PositiveIntegerField(default=0, verbose_name="已使用次数")
    valid_from = models.DateTimeField(default=timezone.now, verbose_name="生效时间")
    valid_until = models.DateTimeField(verbose_name="失效时间")
    is_active = models.BooleanField(default=True, verbose_name="是否激活")

    def __str__(self):
        return self.code

# 记录用户与优惠券的关联,防止重复使用
class CouponUsage(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='coupon_usages')
    coupon = models.ForeignKey(Coupon, on_delete=models.CASCADE)
    used_at = models.DateTimeField(default=timezone.now)

    class Meta:
        unique_together = ('user', 'coupon')  # 确保同一用户无法重复使用同一优惠券

二、优惠券有效性验证

在序列化器中实现验证逻辑,检查优惠券是否满足时间段、使用人数限制,以及用户是否已使用过:

from rest_framework import serializers
from .models import Coupon
from django.utils import timezone

class CouponApplySerializer(serializers.Serializer):
    code = serializers.CharField(max_length=50)

    def validate_code(self, value):
        try:
            coupon = Coupon.objects.get(code=value, is_active=True)
        except Coupon.DoesNotExist:
            raise serializers.ValidationError("无效的优惠券码")
        
        # 验证时间段有效性
        now = timezone.now()
        if now < coupon.valid_from or now > coupon.valid_until:
            raise serializers.ValidationError("优惠券不在有效期内")
        
        # 验证使用人数限制
        if coupon.max_usage > 0 and coupon.used_count >= coupon.max_usage:
            raise serializers.ValidationError("优惠券已被抢光")
        
        # 验证当前用户未使用过该优惠券
        user = self.context['request'].user
        if user.coupon_usages.filter(coupon=coupon).exists():
            raise serializers.ValidationError("你已使用过该优惠券")
        
        return coupon

三、视图层处理逻辑

在视图中处理优惠券申请请求,验证通过后更新使用次数并记录使用记录:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import CouponApplySerializer
from .models import CouponUsage
from django.db import transaction

class ApplyCouponView(APIView):
    def post(self, request):
        serializer = CouponApplySerializer(data=request.data, context={'request': request})
        if serializer.is_valid():
            coupon = serializer.validated_data['code']
            
            # 原子更新使用次数,避免并发超卖
            with transaction.atomic():
                # 加锁防止并发修改
                locked_coupon = Coupon.objects.select_for_update().get(pk=coupon.pk)
                if locked_coupon.used_count >= locked_coupon.max_usage and locked_coupon.max_usage > 0:
                    return Response({"error": "优惠券已被抢光"}, status=status.HTTP_400_BAD_REQUEST)
                locked_coupon.used_count += 1
                locked_coupon.save()
            
            # 记录用户使用记录
            CouponUsage.objects.create(user=request.user, coupon=coupon)
            
            return Response({"message": "优惠券使用成功", "coupon_code": coupon.code}, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

四、关键注意事项

  • 并发安全:使用select_for_update或F()表达式确保优惠券使用次数的更新是原子操作,避免多个用户同时操作导致超卖。
  • 时间准确性:依赖Django的timezone.now()确保时间验证基于服务器时区,避免客户端时间误差。
  • 状态管理:通过is_active字段可以快速禁用优惠券,无需删除数据。

参考资料

  • Django官方文档:模型字段定义、数据库事务、F表达式
  • DRF官方文档:序列化器验证、API视图类

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:33:30