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

如何在Django中设计满足业务规则的促销模型

Django 套餐促销模型实现方案

核心设计思路

完全匹配现有业务规则,不侵入已有的Plan模型原有结构,将促销逻辑独立封装在单独模型中,通过外键和套餐关联,避免修改原套餐数据导致的一致性问题。


促销模型代码实现

from django.db import models
from django.db.models import F
from django.utils import timezone

# 复用你现有代码中的Plan模型即可,导入路径替换为你的实际应用路径
# from your_app.models import Plan

class PlanPromotion(models.Model):
    # 基础信息字段
    promotionID = models.BigAutoField(primary_key=True, verbose_name="促销活动ID")
    promotionName = models.CharField(max_length=255, verbose_name="促销活动名称")
    # 关联对应套餐,一个套餐可以配置多个待生效、历史促销活动
    plan = models.ForeignKey(
        Plan,
        on_delete=models.CASCADE,
        related_name="promotions",
        verbose_name="关联套餐"
    )
    # 促销专属权益值:按照业务规则,促销仅调整权益比例
    promoBenefitPercentage = models.FloatField(default=0, verbose_name="促销权益比例")

    # 限制规则1:参与用户数限制
    userLimit = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name="可参与用户上限,留空表示不限制"
    )
    joinedUserCount = models.PositiveIntegerField(default=0, verbose_name="已参与用户数")

    # 限制规则2:活动时间范围限制
    startTime = models.DateTimeField(verbose_name="活动生效开始时间")
    endTime = models.DateTimeField(verbose_name="活动生效结束时间")

    # 状态字段
    isActive = models.BooleanField(default=True, verbose_name="是否启用活动")
    createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    updateTime = models.DateTimeField(auto_now=True, verbose_name="更新时间")

    def __str__(self):
        return f"{self.promotionName} | 关联套餐:{self.plan.planName}"

    def is_available(self):
        """判断当前促销是否处于可参与状态"""
        now = timezone.now()
        # 活动未启用直接返回不可用
        if not self.isActive:
            return False
        # 不在活动时间窗口内返回不可用
        if not (self.startTime <= now <= self.endTime):
            return False
        # 配置了用户上限且已达上限返回不可用
        if self.userLimit is not None and self.joinedUserCount >= self.userLimit:
            return False
        return True

    class Meta:
        verbose_name = "套餐促销活动"
        verbose_name_plural = verbose_name

字段与逻辑说明

  • 关联设计:通过外键绑定Plan模型,配置related_name="promotions"后,可以直接通过plan_instance.promotions.all()查询某个套餐下的所有促销配置。
  • 权益存储:独立设置promoBenefitPercentage字段存储促销期的权益比例,不直接修改原Plan模型的benefitPercentage字段。如果直接修改原套餐字段,促销结束后需要手动回滚数据,还可能出现时间差导致非活动时段的用户误享促销的问题,独立存储可以保证原套餐基础数据不受活动影响。
  • 人数限制:userLimit字段留空时表示不限制参与人数,搭配joinedUserCount统计实际参与量,判断是否达到参与上限。
  • 时间限制:通过startTime、endTime配置活动生效的时间窗口,精确到时分秒,支持任意时段的活动配置。
  • 状态判断:封装is_available()方法统一处理促销生效判断逻辑,业务层不需要重复编写判断条件,直接调用方法即可确认促销是否可参与。

业务使用参考

  • 配置促销:品牌合作方选择已有套餐后,传入促销名称、促销权益比例、用户上限、生效时间段即可完成促销创建。
  • 权益计算:用户访问套餐时,先查询对应套餐下所有启用的促销,调用is_available()筛选出当前生效的促销,如果存在有效促销则使用promoBenefitPercentage作为实际权益比例,没有有效促销时使用原Plan的benefitPercentage即可。
  • 参与计数:用户成功参与促销活动后,使用F表达式原子更新参与人数,避免并发场景下计数不准、超出人数限制的问题,示例代码:
    PlanPromotion.objects.filter(promotionID=target_promo_id).update(
        joinedUserCount=F('joinedUserCount') + 1
    )
    

如果业务要求同一套餐同一时间段只能存在一个生效促销,可以在模型保存时增加时间范围重叠校验,或者在Meta中添加对应的数据库约束即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:48:36