课程动态价格策略实现
动态价格策略设计
商城往往为了提高销量都会出现活动内容,商品因为参加了活动所以会产生价格的变动。
价格优惠活动类型名称: 限时免费, 限时折扣, 限时减免, 限时满减, 积分抵扣, 优惠券 针对单个商品的动态价格策略,公式: 限时免费 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) > 0View 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