首页 > 其他分享 >课程动态价格策略

课程动态价格策略

时间:2022-10-31 12:13:18浏览次数:25  
标签:verbose models name course 课程 time 动态 True 策略

课程动态价格策略实现

动态价格策略设计

商城往往为了提高销量都会出现活动内容,商品因为参加了活动所以会产生价格的变动。

价格优惠活动类型名称: 限时免费, 限时折扣, 限时减免, 限时满减, 积分抵扣, 优惠券
​
针对单个商品的动态价格策略,公式:
限时免费       0
限时折扣       原价*0.8
限时减免       原价-减免价
​
针对单次下单的动态价格策略,公式:
限时满减      总价-(满减计算后换算价格) 
积分抵扣      总价-(积分计算后换算价格)       ->> 积分与现金换算比率
优惠券        总价-(优惠券计算后的优惠价格)   ->> 优惠券

 

模型创建

新增4个课程优惠相关的4个模型,courses/models.py,代码:

from django.utils import timezone as datetime
​
​
class Activity(BaseModel):
    start_time = models.DateTimeField(default=datetime.now, verbose_name="开始时间")
    end_time = models.DateTimeField(default=datetime.now, verbose_name="结束时间")
    description = RichTextUploadingField(blank=True, null=True, verbose_name="活动介绍")
    remark = models.TextField(blank=True, null=True, verbose_name="备注信息")
​
    class Meta:
        db_table = "fg_activity"
        verbose_name = "优惠活动"
        verbose_name_plural = verbose_name
​
    def __str__(self):
        return self.name
​
​
class DiscountType(BaseModel):
    remark = models.CharField(max_length=250, blank=True, null=True, verbose_name="备注信息")
​
    class Meta:
        db_table = "fg_discount_type"
        verbose_name = "优惠类型"
        verbose_name_plural = verbose_name
​
    def __str__(self):
        return self.name
​
​
class Discount(BaseModel):
    discount_type = models.ForeignKey("DiscountType", on_delete=models.CASCADE, related_name='discount_list', db_constraint=False, verbose_name="优惠类型")
    condition = models.IntegerField(blank=True, default=0, verbose_name="满足优惠的价格条件", help_text="设置享受优惠的价格条件,如果不填或0则没有优惠门槛")
    sale = models.TextField(verbose_name="优惠公式", help_text="""
    0表示免费;<br>
    *号开头表示折扣价,例如填写*0.82,则表示八二折;<br>
    -号开头表示减免价, 例如填写-100,则表示减免100;<br>""")
​
    class Meta:
        db_table = "fg_discount"
        verbose_name = "优惠公式"
        verbose_name_plural = verbose_name
​
    def __str__(self):
        return "价格优惠:%s,优惠条件:%s,优惠公式: %s" % (self.discount_type.name, self.condition, self.sale)
​
​
class CourseActivityPrice(BaseModel):
    activity = models.ForeignKey("Activity", on_delete=models.CASCADE, related_name='price_list', db_constraint=False, verbose_name="活动")
    course = models.ForeignKey("Course", on_delete=models.CASCADE, related_name='price_list', db_constraint=False, verbose_name="课程")
    discount = models.ForeignKey("Discount", on_delete=models.CASCADE, related_name='price_list', db_constraint=False, verbose_name="优惠")
​
    class Meta:
        db_table = "fg_course_activity_price"
        verbose_name = "课程参与活动的价格表"
        verbose_name_plural = verbose_name
​
    def __str__(self):
        return "活动:%s-课程:%s-优惠公式:%s" % (self.activity.name, self.course.name, self.discount.sale)
View Code

 

执行数据迁移

cd luffycityapi
python manage.py makemigrations
python manage.py migrate

 

提交代码版本

# 合并前面的course分支到master
git checkout master
git merge feature/course
​
cd /home/moluo/Desktop/luffycity/
git add .
git commit -m "feature: 动态价格策略模型的创建"
git push
​
git checkout -b feature/discount

 

 

admin站点配置活动相关模型管理器

courses/admin.py,代码:

from .models import Activity, DiscountType, Discount, CourseActivityPrice
​
class ActivityModelAdmin(admin.ModelAdmin):
    """优惠活动的模型管理器"""
    list_display = ["id", "name", "start_time", "end_time", "remark"]
​
admin.site.register(Activity, ActivityModelAdmin)
​
class DiscountTypeModelAdmin(admin.ModelAdmin):
    """优惠类型的模型管理器"""
    list_display = ["id", "name", "remark"]
​
admin.site.register(DiscountType, DiscountTypeModelAdmin)
​
​
class DiscountModelAdmin(admin.ModelAdmin):
    """优惠公式的模型管理器"""
    list_display = ["id", "name","discount_type","condition","sale"]
​
admin.site.register(Discount, DiscountModelAdmin)
​
​
class CourseActivityPriceModelAdmin(admin.ModelAdmin):
    """课程活动价格的模型管理器"""
    list_display = ["id", "activity", "course","discount"]
​
admin.site.register(CourseActivityPrice, CourseActivityPriceModelAdmin)
​
View Code

 

因为涉及到时间的转换计算,所以此处我们需要在settings/dev.py中设置时区相关的配置信息。

LANGUAGE_CODE = 'zh-hans'
​
TIME_ZONE = 'Asia/Shanghai'
​
USE_I18N = True
​
USE_L10N = True
​
USE_TZ = False # 关闭时区转换以后,django会默认使用TIME_ZONE作为时区。
View Code

 

添加测试数据

INSERT INTO luffycity.fg_activity (id, name, orders, is_show, is_deleted, created_time, updated_time, start_time, end_time, description, remark) VALUES (1, '路飞学城-5周年庆', 1, 1, 0, '2022-02-17 10:42:54.340893', '2022-02-17 10:42:54.340933', '2022-02-17 00:00:00', '2021-08-01 00:00:00', '<p>5周年庆,各种活动促销内容展示图片</p>', '负责人:
组织:
外勤:');
​
INSERT INTO luffycity.fg_discount_type (id, name, orders, is_show, is_deleted, created_time, updated_time, remark) VALUES (1, '免费', 1, 1, 0, '2022-02-17 10:43:38.546870', '2022-02-17 10:43:38.546901', null);
INSERT INTO luffycity.fg_discount_type (id, name, orders, is_show, is_deleted, created_time, updated_time, remark) VALUES (2, '折扣', 1, 1, 0, '2022-02-17 10:43:49.161997', '2022-02-17 11:19:58.799363', null);
INSERT INTO luffycity.fg_discount_type (id, name, orders, is_show, is_deleted, created_time, updated_time, remark) VALUES (3, '减免', 1, 1, 0, '2022-02-17 10:44:05.712935', '2022-02-17 11:41:16.504340', null);
INSERT INTO luffycity.fg_discount_type (id, name, orders, is_show, is_deleted, created_time, updated_time, remark) VALUES (4, '限时免费', 1, 1, 0, '2022-02-17 10:44:23.053845', '2022-02-17 10:44:23.053925', null);
INSERT INTO luffycity.fg_discount_type (id, name, orders, is_show, is_deleted, created_time, updated_time, remark) VALUES (5, '限时折扣', 1, 1, 0, '2022-02-17 10:44:31.999352', '2022-02-17 10:44:31.999382', null);
INSERT INTO luffycity.fg_discount_type (id, name, orders, is_show, is_deleted, created_time, updated_time, remark) VALUES (6, '限时减免', 1, 1, 0, '2022-02-17 10:44:39.100270', '2022-02-17 10:44:39.100305', null);
​
​
INSERT INTO luffycity.fg_discount (id, name, orders, is_show, is_deleted, created_time, updated_time, `condition`, sale, discount_type_id) VALUES (1, '免费购买', 1, 1, 0, '2022-02-17 10:45:54.027034', '2022-02-17 10:45:54.027079', 0, '0', 4);
INSERT INTO luffycity.fg_discount (id, name, orders, is_show, is_deleted, created_time, updated_time, `condition`, sale, discount_type_id) VALUES (2, '九折折扣', 1, 1, 0, '2022-02-17 10:47:12.855454', '2022-02-17 11:32:27.148655', 1, '*0.9', 2);
INSERT INTO luffycity.fg_discount (id, name, orders, is_show, is_deleted, created_time, updated_time, `condition`, sale, discount_type_id) VALUES (3, '课程减免100', 1, 1, 0, '2022-02-17 11:40:44.499026', '2022-02-17 11:40:44.499060', 300, '-100', 3);
​
​
INSERT INTO luffycity.fg_course_activity_price (id, name, orders, is_show, is_deleted, created_time, updated_time, activity_id, course_id, discount_id) VALUES (1, '九折-3天Typescript', 1, 1, 0, '2022-02-17 10:48:12.600755', '2022-02-17 10:48:12.600801', 1, 2, 2);
INSERT INTO luffycity.fg_course_activity_price (id, name, orders, is_show, is_deleted, created_time, updated_time, activity_id, course_id, discount_id) VALUES (2, '免费送课', 1, 1, 0, '2022-02-17 11:36:34.192896', '2022-02-17 11:36:34.192941', 1, 1, 1);
INSERT INTO luffycity.fg_course_activity_price (id, name, orders, is_show, is_deleted, created_time, updated_time, activity_id, course_id, discount_id) VALUES (3, '减免课程', 1, 1, 0, '2022-02-17 11:40:49.240245', '2022-02-17 11:40:49.240276', 1, 3, 3);
View Code

 

提交代码版本

cd /home/moluo/Desktop/luffycity/
git add .
git commit -m "feature: admin站点配置活动相关的模型管理器并添加测试数据"
git push --set-upstream origin feature/discount
 

 

在课程模型中计算课程优惠信息

courses/models.py,代码:

class Course(BaseModel):
    course_type = (
        (0, '付费购买'),
        (1, '会员专享'),
        (2, '学位课程'),
    )
    level_choices = (
        (0, '初级'),
        (1, '中级'),
        (2, '高级'),
    )
    status_choices = (
        (0, '上线'),
        (1, '下线'),
        (2, '预上线'),
    )
    # course_cover = models.ImageField(upload_to="course/cover", max_length=255, verbose_name="封面图片", blank=True, null=True)
    course_cover = StdImageField(variations={
        'thumb_1080x608': (1080, 608),   # 高清图
        'thumb_540x304': (540, 304),    # 中等比例,
        'thumb_108x61': (108, 61, True),  # 小图(第三个参数表示保持图片质量),
    }, max_length=255, delete_orphans=True, upload_to="course/cover", null=True, verbose_name="封面图片",blank=True)
    course_video = models.FileField(upload_to="course/video", max_length=255, verbose_name="封面视频", blank=True, null=True)
    course_type = models.SmallIntegerField(choices=course_type,default=0, verbose_name="付费类型")
    level = models.SmallIntegerField(choices=level_choices, default=1, verbose_name="难度等级")
    description = RichTextUploadingField(null=True, blank=True, verbose_name="详情介绍")
    pub_date = models.DateField(auto_now_add=True, verbose_name="发布日期")
    period = models.IntegerField(default=7, verbose_name="建议学习周期(day)")
    attachment_path = models.FileField(max_length=1000, blank=True, null=True, verbose_name="课件路径")
    attachment_link = models.CharField(max_length=1000, blank=True, null=True, verbose_name="课件链接")
    status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="课程状态")
    students = models.IntegerField(default=0, verbose_name="学习人数")
    lessons = models.IntegerField(default=0, verbose_name="总课时数量")
    pub_lessons = models.IntegerField(default=0, verbose_name="已更新课时数量")
    price = models.DecimalField(max_digits=10,decimal_places=2, verbose_name="课程原价",default=0)
    recomment_home_hot = models.BooleanField(default=False, verbose_name="是否推荐到首页新课栏目")
    recomment_home_top = models.BooleanField(default=False, verbose_name="是否推荐到首页必学栏目")
    direction = models.ForeignKey("CourseDirection", related_name="course_list", on_delete=models.DO_NOTHING, null=True, blank=True, db_constraint=False, verbose_name="学习方向")
    category = models.ForeignKey("CourseCategory", related_name="course_list", on_delete=models.DO_NOTHING, null=True, blank=True, db_constraint=False, verbose_name="课程分类")
    teacher = models.ForeignKey("Teacher", related_name="course_list", on_delete=models.DO_NOTHING, null=True, blank=True, db_constraint=False, verbose_name="授课老师")

    class Meta:
        db_table = "fg_course_info"
        verbose_name = "课程信息"
        verbose_name_plural = verbose_name

    def course_cover_small(self):
        if self.course_cover:
            return mark_safe(f'<img style="border-radius: 0%;" src="{self.course_cover.thumb_108x61.url}">')
        return ""

    course_cover_small.short_description = "封面图片(108x61)"
    course_cover_small.allow_tags = True
    course_cover_small.admin_order_field = "course_cover"

    def course_cover_medium(self):
        if self.course_cover:
            return mark_safe(f'<img style="border-radius: 0%;" src="{self.course_cover.thumb_540x304.url}">')
        return ""

    course_cover_medium.short_description = "封面图片(540x304)"
    course_cover_medium.allow_tags = True
    course_cover_medium.admin_order_field = "course_cover"

    def course_cover_large(self):
        if self.course_cover:
            return mark_safe(f'<img style="border-radius: 0%;" src="{self.course_cover.thumb_1080x608.url}">')
        return ""

    course_cover_large.short_description = "封面图片(1080x608)"
    course_cover_large.allow_tags = True
    course_cover_large.admin_order_field = "course_cover"

    @property
    def discount(self):
        """通过计算获取当前课程的折扣优惠相关的信息"""
        # 获取折扣优惠相关的信息
        now_time = datetime.now()  # 活动__结束时间 > 当前时间  and 活动__开始时间 < 当前时间(29)
        # 获取当前课程参与的最新活动记录
        last_activity_log = self.price_list.filter(
            activity__end_time__gt=now_time,
            activity__start_time__lt=now_time
        ).order_by("-id").first()

        type_text = ""  # 优惠类型的默认值
        price = -1      # 优惠价格
        expire = 0      # 优惠剩余时间

        if last_activity_log:
            # 获取优惠类型的提示文本
            type_text = last_activity_log.discount.discount_type.name

            # 获取限时活动剩余时间戳[单位:s]
            expire = last_activity_log.activity.end_time.timestamp() - now_time.timestamp()

            # 判断当前课程的价格是否满足优惠条件
            course_price = float(self.price)
            condition_price = float(last_activity_log.discount.condition)
            if course_price >= condition_price:
                # 计算本次课程参与了优惠以后的价格
                sale = last_activity_log.discount.sale
                print(f"{type_text}-{sale}")
                if sale == "0":
                    # 免费,则最终价格为0
                    price = 0
                elif sale[0] == "*":
                    # 折扣
                    price = course_price * float(sale[1:])
                elif sale[0] == "-":
                    # 减免
                    price = course_price - float(sale[1:])

                price = float(f"{price:.2f}")

        data = {}
        if type_text:
            data["type"] = type_text
        if expire > 0:
            data["expire"] = expire
        if price != -1:
            data["price"] = price

        return data

    def discount_json(self):
        # 必须转成字符串才能保存到es中。所以该方法提供给es使用的。
        return json.dumps(self.discount)

    @property
    def can_free_study(self):
        """是否允许试学"""
        lesson_list = self.lesson_list.filter(is_delete=False, is_show=True, free_trail=True).order_by("orders").all()
        return len(lesson_list) > 0
View Code

 

给Elasticsearch重建索引

cd /home/moluo/Desktop/luffycity/luffycityapi
python manage.py rebuild_index
 

 

客户端课程列表页展示课程优惠价格时增加免费的判断逻辑

views/Course.vue,代码:

                    <p class="two clearfix">
                        <span class="price l red bold" v-if="course_info.discount.price>=0">¥{{parseFloat(course_info.discount.price).toFixed(2)}}</span>
                        <span class="price l red bold" v-else>¥{{parseFloat(course_info.price).toFixed(2)}}</span>
                        <span class="origin-price l delete-line" v-if="course_info.discount.price>=0">¥{{parseFloat(course_info.price).toFixed(2)}}</span>
                        <span class="add-shop-cart r"><img class="icon imv2-shopping-cart" src="../assets/cart2.svg">加购物车</span>
                    </p>
View Code

 

客户端课程详情页展示真实课程的价格

views/Info.vue,代码:

            <p class="course-price" v-if="course.info.discount.price >= 0">
              <span>活动价</span>
              <span class="discount">¥{{parseFloat(course.info.discount.price).toFixed(2)}}</span>
              <span class="original">¥{{parseFloat(course.info.price).toFixed(2)}}</span>
            </p>
 
View Code

 

提交代码版本

git add .
git commit -m "feature: 课程优惠活动的实现"
git push
 

 

标签:verbose,models,name,course,课程,time,动态,True,策略
From: https://www.cnblogs.com/erhuoyuan/p/16843837.html

相关文章

  • KVM 动态调整 qcow2 硬盘
    动态扩容参考:https://cloud-atlas.readthedocs.io/zh_CN/latest/kvm/kvm_vdisk_live.html在宿主机器上使用qemu-imgresize命令调整磁盘大小,会提示不可操作#qemu-im......
  • vue源码分析-动态组件
    前面花了两节的内容介绍了组件,从组件的原理讲到组件的应用,包括异步组件和函数式组件的实现和使用场景。众所周知,组件是贯穿整个Vue设计理念的东西,并且也是指导我们开发的......
  • 1500套HTML+CSS+JS网页设计期末课程大作业 web前端开发技术 web课程设计 网页规划与设
    一、1500套HTML期末学生结课大作业作品(HTML+CSS+JS)这8年来做了1000多套(HTML+CSS+JS)网页设计的学生期末大作业,都是给学生定制的都符合学校或者学生考试期末作业的水平,都......
  • 案例4动态表格的删除和HTMLDOM的innerHTML
    案例4动态表格的删除2.删除:1.确定点击的是哪一个超链接<ahref="javascript:void(0);"onclick="delTr(this);">删除</......
  • 【ZJOI2007】捉迷藏(动态树分治)
    显然只有一次询问的话,可以用点分治来实现。但是现在我们有多组询问,还带有修改,我们只能通过动态点分治来做了。动态点分治的主要思想:省去每次点分治求重心的过程,直接预处......
  • 【atcoder 293 E - Sugoroku 4】【动态规划,递推】
    importjava.io.IOException;importjava.util.Arrays;importjava.util.Scanner;publicclassMain{staticintn,m,k;staticintMOD=998244353;......
  • P8818 CSP-S 2022 策略游戏
    P8818CSP-S2022策略游戏-洛谷|计算机科学教育新生态(luogu.com.cn)矩阵这个描述就是障眼法。翻译一下题目:A在\(a[l_1\cdotsr_1]\)中选择一个\(x\),然后B......
  • BIRT 动态隐藏行/列
    尝试了一种动态隐藏行的新方式:1、先把各行的单元格设置为 display=“none”;2、然后在单元格内的 data对象 onrender事件中判断显示的内容如果不为空,则将data对象的......
  • 策略模式
    旅游的出行方式有乘坐飞机旅行、乘火车旅行和自行车游,不同的旅游方式有不同的实现过程,客户可以根据自己的需要选择一种合适的旅行方式。  ......
  • #yyds干货盘点# 动态规划专题:计算字符串的编辑距离
    1.简述:描述Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删......