night_购物车结算有效期相关

2. 实现课程详情页倒计时功能
模型返回当前课程优惠的剩余时间戳
# courses/models.py中,Course模型新增计算剩余时间的方法,代码: from ckeditor_uploader.fields import RichTextUploadingField class Course(BaseModel): """ 专题课程 """ 。。。 def has_time(self): """计算活动的剩余时间""" now = datetime.now() try: course_prices = self.prices.get(start_time__lte=now, end_time__gte=now, is_delete=False, is_show=True) # 把 活动结束时间 - 当前时间 = 剩余时间 return int( course_prices.end_time.timestamp() - now.timestamp() ) except: print("---活动过期了----") return 0 # 序列化器,新增返回字段 class CourseDetailModelSerializer(serializers.ModelSerializer): """课程详情页的序列化器""" teacher = TeacherDetailModelSerializer() class Meta: model = Course # fields = ("id","name", "video", "course_img", "students","lessons","pub_lessons","price","teacher","course_level","brief") fields = ("id","name", "course_img", "students","lessons","pub_lessons","price","teacher","course_level","brief","get_course_price","get_course_discount_type","has_time")
# 前端使用定时器setInterval完成倒计时功能 <template> <div class="detail"> <Header/> <div class="main"> <div class="course-info"> <div class="wrap-left"> 。。。。 <div class="sale-time"> <p class="sale-type">{{course.get_course_discount_type}}</p> <p class="expire">距离结束:仅剩 {{Math.floor(course.has_time/86400)}}天 {{Math.floor(course.has_time%86400/3600)}}小时 {{Math.floor(course.has_time%86400%3600/60)}}分 <span class="second">{{Math.floor(course.has_time%86400%3600%60)}}</span> 秒</p> </div> <p class="course-price"> <span>活动价</span> <span class="discount">¥{{course.get_course_price}}</span> <span class="original">¥{{course.price}}</span> </p> </div> <div v-else> <div class="sale-time"> <p class="sale-type">价格: ¥{{course.price}}</p> </div> </div> 。。。。 </template> <script> import Header from "./common/Header" import Footer from "./common/Footer" import {videoPlayer} from 'vue-video-player'; export default { name: "Detail", 。。。。 created(){ // 获取当前课程ID this.course_id = this.$route.query.id - 0; // 判断ID基本有效性 let _this = this; if( isNaN(this.course_id) || this.course_id < 1 ){ _this.$alert("无效的课程ID!","错误",{ callback(){ _this.$router.go(-1); }}); } // 发送请求获取后端课程数据 this.$axios.get(this.$settings.Host+`/courses/detail/${this.course_id}/`).then(response=>{ this.course = response.data; // 修改视频中的封面图片 this.playerOptions.poster = this.course.course_img; // 倒计时 if(this.course.has_time > 1){ let timer = setInterval(()=>{ if( this.course.has_time > 1 ){ this.course.has_time-=1; }else{ clearInterval(timer); location.reload(); } },1000); } }).catch(error=>{ console.log(error.response) }); }, 。。。。 } </script>
3.
# 模型代码: """课程有效期""" class CourseTime(BaseModel): """课程有效期表""" timer = models.IntegerField(verbose_name="购买周期",default=30,help_text="单位:天<br>建议按月书写,例如:1个月,则为30.") title = models.CharField(max_length=150, null=True, blank=True, verbose_name="购买周期的文本提示", default="1个月有效", help_text="要根据上面的购买周期,<br>声明对应的提示内容,<br>展示在购物车商品列表中") course = models.ForeignKey("Course", on_delete=models.CASCADE, related_name="prices", verbose_name="课程") price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价", default=0) class Meta: db_table = "ly_course_time" verbose_name = "课程有效期表" verbose_name_plural = "课程有效期表" def __str__(self): return "课程:%s,周期:%s,价格:%s" % (self.course, self.timer, self.price) # 数据建议 python manage.py makemigrations python manage.py migrate # 把模型注册到xadmin中 from .models import CourseTime class CourseTimeModelAdmin(object): """课程与价格优惠关系模型管理类""" list_display = ["course","title","timer","price"] xadmin.site.register(CourseTime, CourseTimeModelAdmin)
添加测试数据

# 购物车视图中,返回购买课程的周期列表,cart/views.py,代码 class CartAPIView(APIView): permission_classes = [IsAuthenticated] """购物车视图""" def get(self,request): """获取购物车商品课程列表""" # 获取当前用户ID # user_id = 1 user_id = request.user.id # 通过用户ID获取购物车中的商品信息 redis = get_redis_connection("cart") cart_goods_list = redis.hgetall("cart_%s" % user_id ) # 商品课程列表 cart_goods_selects = redis.smembers("cart_selected_%s" % user_id) # redis里面的所有数据最终都是以bytes类型的字符串保存的 # print( cart_goods_selects ) # 格式: {b'7', b'3', b'5'} # print( cart_goods_list ) # 格式: {b'7': b'-1', b'5': b'-1'} # 遍历购物车中的商品课程到数据库获取课程的价格, 标题, 图片 data_list = [] try: for course_id_bytes,expire_bytes in cart_goods_list.items(): course_id = int( course_id_bytes.decode() ) expire = expire_bytes.decode() course = Course.objects.get(pk=course_id) # 获取购买的课程的周期价格列表 expires = course.coursetimes.all() # 默认具有永久价格 expire_list = [{ "title": "永久有效", "timer": -1, "price": course.price }] for item in expires: expire_list.append({ "title":item.title, "timer":item.timer, "price":item.price, }) data_list.append({ "id": course_id, "expire":expire, "course_img": course.course_img.url, "name": course.name, "price": course.get_course_price(), "is_select": course_id_bytes in cart_goods_selects, "expire_list": expire_list, }) except: return Response(data_list,status=status.HTTP_500_INTERNAL_SERVER_ERROR) # print(data_list) # 返回查询结果 return Response(data_list,status=status.HTTP_200_OK)
# 前端展示购物车中每一个商品课程的购买周期,代码: <template> 。。。。 <el-table-column label="有效期" width="216"> <template slot-scope="scope"> <el-select @change="ChangeExpire(scope.row)" v-model="scope.row.expire" placeholder="请选择"> <el-option v-for="item in scope.row.expire_list" :key="item.timer" :label="item.title" :value="item.timer"></el-option> </el-select> </template> </el-table-column> 。。。。 </div> </template> <script> import Header from "./common/Header" import Footer from "./common/Footer" export default { name: "Cart", data(){ return{ // 注释掉原来的有效周期测试数据 // expire:3, // expire_list:[] courseData:[], // 购物车中的商品信息 selection:[], // 购物车中被勾选的商品信息 total_price:0.00, } }, 。。。。 created(){ // 判断是否登录 this.token = sessionStorage.token || localStorage.token; if( !this.token ){ this.$confirm("对不起,您尚未登录!请登录",'提示').then(() => { this.$router.push("/login"); }).catch(()=>{ this.$router.go(-1); }); }else{ // 获取购物车商品数据 this.$axios.get(this.$settings.Host+"/carts/course/",{ headers:{ // 注意下方的空格!!! "Authorization":"jwt " + this.token } }).then(response=>{ this.courseData = response.data; // 更新在vuex里面的数据 this.$store.state.cart.count = response.data.length; // 调整因为ajax数据请求导致勾选状态人没有出现的原因,使用定时器进行延时调用 setTimeout(()=>{ let expire_data = []; this.courseData.forEach(course=>{ course.expire_list.forEach(row=>{ expire_data[row.timer] = row.title; }); }); // row 就是字典数据[json] this.courseData.forEach(row => { // 设置商品课程的选中状态 if(row.is_select){ this.$refs.multipleTable.toggleRowSelection(row); } // 调整有效期选项中数值变成文本内容 row.expire = expire_data[row.expire]; }); },0) }).catch(error=>{ let status = error.response.status; if( status == 401 ){ this.token = null; sessionStorage.removeItem("token"); localStorage.removeItem("token"); let _this = this; this.$alert("您尚未登录或登录超时!请重新登录","警告",{ callback(){ _this.$router.push("/login"); } }); } }) } }, 。。。 } </script>
4. 当切换课程周期时,后端重新计算价格并返回
模型中计算真实价格时,增加一个原价字段,通过原价字段,判断本次计算是计算周期还是计算永久有效。
from ckeditor_uploader.fields import RichTextUploadingField class Course(BaseModel): """ 专题课程 """ 。。。。 def get_course_price(self,price=0): # 获取当前课程的真实价格 self.price = price if price != 0 else self.price # 判断调用当前方法时,是否定义了价格 。。。。
# 视图中修改patch方法,在用户切换购买课程周期时,重新计算真实课程价格,代码: def patch(self,request): """更新购物城中的商品信息[切换课程有效期]""" # 获取当前登录的用户ID # user_id = 1 user_id = request.user.id # 获取当前操作的课程ID course_id = request.data.get("course_id") # 获取新的有效期 expire = request.data.get("expire") # 获取redis链接 redis = get_redis_connection("cart") # 更新购物中商品课程的有效期 redis.hset("cart_%s" % user_id,course_id, expire) # 根据新的课程有效期获取新的课程原价 try: coursetime = CourseTime.objects.get(course=course_id, timer=expire) # 根据新的课程价格,计算真实课程价格 price = coursetime.course.get_course_price(coursetime.price) except: # 这里给price设置一个默认值,当值-1,则前段不许要对价格进行调整 course = Course.objects.get(pk=course_id) price = course.get_course_price() return Response({ "price": price, "message": "修改购物车信息成功!" }, status=status.HTTP_200_OK)
5. 前端获取课程有效期列表
// 更新课程的有效期 ChangeExpire(course){ // 获取课程ID和有效期 let course_id = course.id; let expire = course.expire; // 发送patch请求更新有效期 this.$axios.patch(this.$settings.Host+"/carts/course/",{ course_id, expire, // 这里是简写,相当于 expire:expire, },{ headers:{ // 注意下方的空格!!! "Authorization":"jwt " + this.token }, }).then(response=>{ // 更新购买的商品课程的价格 course.price = response.data.price; this.$message(response.data.message,"提示"); }); }, # 完成上面的步骤以后,切换购买周期时,价格就发生了变化,但是购物车页面刷新时,发现价格还原成"永久有效"的价格。所以我们需要在后端的购物车商品列表api接口中针对价格的购买周期进行判断。 # carts/views.py的CartAPIView def get(self,request): """获取购物车商品课程列表""" # 获取当前用户ID # user_id = 1 user_id = request.user.id # 通过用户ID获取购物车中的商品信息 redis = get_redis_connection("cart") cart_goods_list = redis.hgetall("cart_%s" % user_id ) # 商品课程列表 cart_goods_selects = redis.smembers("cart_selected_%s" % user_id) # redis里面的所有数据最终都是以bytes类型的字符串保存的 # print( cart_goods_selects ) # 格式: {b'7', b'3', b'5'} # print( cart_goods_list ) # 格式: {b'7': b'-1', b'5': b'-1'} # 遍历购物车中的商品课程到数据库获取课程的价格, 标题, 图片 data_list = [] try: for course_id_bytes,expire_bytes in cart_goods_list.items(): course_id = int( course_id_bytes.decode() ) expire = expire_bytes.decode() course = Course.objects.get(pk=course_id) # 获取购买的课程的周期价格列表 expires = course.coursetimes.all() # 默认具有永久价格 expire_list = [{ "title": "永久有效", "timer": -1, "price": course.price }] for item in expires: expire_list.append({ "title":item.title, "timer":item.timer, "price":item.price, }) try: # 根据课程有效期传入课程原价 coursetime = CourseTime.objects.get(course=course_id, timer=expire) # 根据新的课程价格,计算真实课程价格 price= coursetime.price except: price = 0 data_list.append({ "id": course_id, "expire":expire, "course_img": course.course_img.url, "name": course.name, "price": course.get_course_price(price), "is_select": course_id_bytes in cart_goods_selects, "expire_list": expire_list, }) except: return Response(data_list,status=status.HTTP_500_INTERNAL_SERVER_ERROR) # print(data_list) # 返回查询结果 return Response(data_list,status=status.HTTP_200_OK)
6. 最后,修复在用户换购买周期时,前端需要重新计算购物车中所有商品的总价格
// 更新课程的有效期 ChangeExpire(course){ // 获取课程ID和有效期 let course_id = course.id; let expire = course.expire; // 发送patch请求更新有效期 this.$axios.patch(this.$settings.Host+"/carts/course/",{ course_id, expire, // 这里是简写,相当于 expire:expire, },{ headers:{ // 注意下方的空格!!! "Authorization":"jwt " + this.token }, }).then(response=>{ // 更新购买的商品课程的价格 course.price = response.data.price; // 重新计算购物车中的商品总价 this.getTotalPrice(); this.$message(response.data.message,"提示"); }); },


浙公网安备 33010602011771号