day84:luffy:优惠活动策略&用户认证&购物车商品的勾选/结算
阅读原文时间:2023年07月09日阅读:2

1.课程列表页活动和真实价格计算

  1.优惠活动策略的model表结构

  2.课程列表页显示优惠类型名称

  3.课程列表页显示真实价格

  4.将优惠类型名称和真实价格显示到前端页面上

  5.课程列表页显示具体结束时间

2.添加购物车/查看购物车的用户认证

3.购物车商品价格的勾选/结算

  1.每个课程的真实价格显示到购物车页面上

  2.勾选/非勾选应在redis中实时存储-后端接口

  3.勾选/非勾选应该在前端页面重新计算价格

  4.购物车列表显示-后端接口

既然提到了活动,那与之对应的肯定是优惠策略、优惠活动等等。

所以我们要为优惠活动策略单独建立表结构

1.优惠活动策略的model表结构

class CourseDiscountType(BaseModel):
"""课程优惠类型"""
name = models.CharField(max_length=32, verbose_name="优惠类型名称")
remark = models.CharField(max_length=250, blank=True, null=True, verbose_name="备注信息")

class Meta:  
    db\_table = "ly\_course\_discount\_type"  
    verbose\_name = "课程优惠类型"  
    verbose\_name\_plural = "课程优惠类型"

def \_\_str\_\_(self):  
    return "%s" % (self.name)

class CourseDiscount(BaseModel):
"""课程优惠模型"""
discount_type = models.ForeignKey("CourseDiscountType", on_delete=models.CASCADE, related_name='coursediscounts', verbose_name="优惠类型")
condition = models.IntegerField(blank=True, default=0, verbose_name="满足优惠的价格条件",help_text="设置参与优惠的价格门槛,表示商品必须在xx价格以上的时候才参与优惠活动,
如果不填,则不设置门槛") #因为有的课程不足100,你减免100,还亏钱了
sale = models.TextField(verbose_name="优惠公式",blank=True,null=True, help_text="""
不填表示免费;

*号开头表示折扣价,例如*0.82表示八二折;

-号开头则表示减免,例如-20表示原价-20;

如果需要表示满减,则需要使用 原价-优惠价格,例如表示课程价格大于100,优惠10;大于200,优惠25,格式如下:

            满100-10

            满200-25

""")

class Meta:  
    db\_table = "ly\_course\_discount"  
    verbose\_name = "价格优惠策略"  
    verbose\_name\_plural = "价格优惠策略"

def \_\_str\_\_(self):  
    return "价格优惠:%s,优惠条件:%s,优惠值:%s" % (self.discount\_type.name, self.condition, self.sale)

class Activity(BaseModel):
"""优惠活动"""
name = models.CharField(max_length=150, verbose_name="活动名称")
start_time = models.DateTimeField(verbose_name="优惠策略的开始时间")
end_time = models.DateTimeField(verbose_name="优惠策略的结束时间")
remark = models.CharField(max_length=250, blank=True, null=True, verbose_name="备注信息")

class Meta:  
    db\_table = "ly\_activity"  
    verbose\_name="商品活动"  
    verbose\_name\_plural="商品活动"

def \_\_str\_\_(self):  
    return self.name

class CoursePriceDiscount(BaseModel):
"""课程与优惠策略的关系表"""
course = models.ForeignKey("Course",on_delete=models.CASCADE, related_name="activeprices",verbose_name="课程")
active = models.ForeignKey("Activity",on_delete=models.DO_NOTHING, related_name="activecourses",verbose_name="活动")
discount = models.ForeignKey("CourseDiscount",on_delete=models.CASCADE,related_name="discountcourse",verbose_name="优惠折扣")

class Meta:  
    db\_table = "ly\_course\_price\_dicount"  
    verbose\_name="课程与优惠策略的关系表"  
    verbose\_name\_plural="课程与优惠策略的关系表"

def \_\_str\_\_(self):  
    return "课程:%s,优惠活动: %s,开始时间:%s,结束时间:%s" % (self.course.name, self.active.name, self.active.start\_time,self.active.end\_time)

优惠活动策略的表结构设计

2.课程列表页显示优惠类型名称

1.course/models.py

在模型类中写入discount_name 让课程列表页页面显示优惠类型名称

class Course:

def activity(self):  
    import datetime  
    now = datetime.datetime.now()

    # 获取课程参加的活动名称  
    activity\_list = self.activeprices.filter(is\_show=True, is\_deleted=False, active\_\_start\_time\_\_lte=now, active\_\_end\_time\_\_gte=now)  
    return activity\_list

# 优惠类型名称  
def discount\_name(self):  
    dis\_name = ''  
    a = self.activity()  
    if a:  
        discount\_n\_list = \[\]  
        for i in a:  
            # 获取课程的折扣类型名称  
            discount\_n = i.discount.discount\_type.name  
            discount\_n\_list.append(discount\_n)  
        dis\_name = discount\_n\_list\[0\]

    return dis\_name

2.course/serializers.py

序列化器加入该字段

class CourseModelSerializer:
field = [,discount_name]

class CourseDetailModelSerializer:
field = [,discount_name]

3.drf测试:course/courses

3.课程列表页显示真实价格

1.dev.py

USE_TZ = False # 修改时区

2.course/models.py

class Course:
def real_price(self):
price = float(self.price) # 获取真实价格
r_price = price

    a = self.activity() # 获取课程对应的活动名称  
    if a: # 如果课程参加了活动  
        sale = a\[0\].discount.sale # 查看活动对应的优惠公式  
        condition\_price = a\[0\].discount.condition # 查看活动对应的满足优惠的价格条件  
        # 限时免费  
        if not sale.strip():  
            r\_price = 0

        # 限时折扣  \*0.5  
        elif '\*' in sale.strip():  
            if price >= condition\_price:  
                \_, d = sale.split('\*')  
                r\_price = price \* float(d)  
        # 限时减免  -100  
        elif sale.strip().startswith('-'):  
            if price >= condition\_price:  
                \_, d = sale.split('-')  
                r\_price = price - float(d)  
        # 满减 满100-15  
        elif '满' in sale:  
            if price >= condition\_price: # 只有价格满足优惠条件价格才能满减  
                l1 = sale.split('\\r\\n')  
                dis\_list = \[\]  #10 50  25  
                for i in l1:  
                    a, b = i\[1:\].split('-')

                    # 当商品价格(400) 满足100-200-300 应该选择满300那个优惠  
                    if price >= float(a):  
                        dis\_list.append(float(b))

                max\_dis = max(dis\_list) # 取到最大的那个满减优惠  
                r\_price = price - max\_dis # 原价格-满减价格=真实价格

    return r\_price

3.course/serializers.py

class CourseModelSerializer:
field = [,discount_name,real_price]

class CourseDetailModelSerializer:
field = [,discount_name,real_price]

4.drf测试:course/courses

4.将优惠类型名称和真实价格显示到前端页面上

1.课程列表页前端

{{course.discount\_name}} ¥{{course.real\_price}}元 原价:{{course.price}}元 立即购买

2.课程详情页前端

{{course\_data.discount\_name}}

距离结束:仅剩 567 天 12小时 52分 32

活动价 ¥{{course\_data.real\_price}} ¥{{course\_data.price}}

5.课程列表页显示具体结束时间

1.course/models.py

class Course:
def left_time(self):
import datetime
now = datetime.datetime.now().timestamp() # 获取当前时间戳
left_t = 0
a = self.activity() # 获取当前课程参加的活动
if a: # 如果当前课程有参加活动
end_time = a[0].active.end_time.timestamp() # 获取当前课程活动的结束时间
left_t = end_time - now # 剩余时间=结束时间-当前时间

        return left\_t

2.序列化器放入left_time

course/serializers.py

class CourseDetailModelSerializer:
field = [,discount_name,real_price,left_time]

3.前端渲染距离结束时间

Detail.vue

{{course\_data.discount\_name}}

距离结束:仅剩 {{course\_data.left\_time/60/60/24 | pInt}}天 {{course\_data.left\_time/60/60 % 24| pInt}}小时 {{course\_data.left\_time/60 % 60 | pInt}}分 {{course\_data.left\_time % 60 | pInt}}

在js部分需要设置一个定时器,让时间能够一直减1s

Detail.vue

get_course_data(){
this.$axios.get(`${this.$settings.Host}/course/detail/${this.course_id}/`)
.then((res)=>{
//console.log(res.data);
this.course_data = res.data;
this.playerOptions.sources[0].src = res.data.course_video
this.playerOptions.poster = res.data.course_img

     // 设置计时器  
      setInterval(()=>{  
        this.course\_data.left\_time--;

      },1000)

    })  
  },

补充:让页面可以显示出0x分0x秒的效果→过滤器

Detail.vue


距离结束:仅剩 {{course_data.left_time/60/60/24 | pInt}}天 {{course_data.left_time/60/60 % 24| pInt}}小时 {{course_data.left_time/60 % 60 | pInt}}分 {{course_data.left_time % 60 | pInt}}

// Detail.vue--js
filters:{
pInt(val){
let a = parseInt(val);
if (a < 10){
a = `0${a}`;
}
return a
}
}

为视图添加IsAuthenticated用户认证

# cart/views.py
class AddCartView:

# 请求头必须携带着token  
permission\_classes = \[IsAuthenticated,\] # 添加用户认证  
def add:  
    user\_id = request.user.id # 获取真实的用户id  
    '''  
    request = user\_obj  
    '''

drf接口: cart/add_cart 获取不到数据,因为加了IsAuthenticated认证

添加购物车时需要携带token 否则不能添加购物车

// Detail.vue 添加购物车
methods: {

  addCart(){

    let token = localStorage.token || sessionStorage.token;

    if (token){  
      this.$axios.post(\`${this.$settings.Host}/users/verify/\`,{  
          token:token,  
        }).then((res)=>{  
this.$axios.post(\`${this.$settings.Host}/cart/add\_cart/\`,{  
            course\_id:this.course\_id,  
          },{  
          // 向后端提交数据需要加上header项,将token也要提交过去  
          headers:{  
            'Authorization':'jwt ' + token  
          }  
        }).then((res)=>{  
            this.$message.success(res.data.msg);  
            console.log('>>>>>',this.$store)  
            this.$store.commit('add\_cart', res.data.cart\_length) ;  
            console.log(this.$store.state);  
          })

        }).catch((error)=>{  
         ......  
        })

    } else {  
      ......  
          })  
    }

  },

查看购物车时需要携带token 否则不能添加购物车

// Cart.vue 购物车页面
created() {
let token = sessionStorage.token || localStorage.token;
if (token){

  this.$axios.get(\`${this.$settings.Host}/cart/add\_cart/\`,{  
     // 查看购物车页面也要携带token 否则不能查看  
     headers:{  
            'Authorization':'jwt ' + token  
          }  
  })  
  .then((res)=>{  
    this.cart\_data\_list = res.data.cart\_data\_list  
  })  
  .catch((error)=>{  
    this.$message.error(error.response.data.msg);  
  })

1.每个课程的真实价格显示到购物车页面上

# cart/views.py

class AddCartView:
def cart_list(self, request):
……
cart_data_list.append({
……
'real_price': course_obj.real_price(),
……
})
……

¥{{cart.real\_price}}

2.勾选/非勾选应在redis中实时存储-后端接口

# cart/views.py
class AddCartView:
def change_select(self, request):

    # 拿到课程id  
    course\_id = request.data.get('course\_id')

    # 校验课程id合法性  
    try:  
        models.Course.objects.get(id=course\_id)  
    except:  
        return Response({'msg': '课程不存在,不要乱搞!'}, status=status.HTTP\_400\_BAD\_REQUEST)

    # 拿到user\_id  
    user\_id = request.user.id

    # 去redis数据库:cart  
    conn = get\_redis\_connection('cart')

    # redis存数据-用集合存:用户id:勾选课程id  
    conn.sadd('selected\_cart\_%s' % user\_id, course\_id)

    return Response({'msg':'勾选成功'})   

   def cancel\_select(self, request):  
    course\_id = request.data.get('course\_id')

    try:  
        models.Course.objects.get(id=course\_id)  
    except:  
        return Response({'msg': '课程不存在,不要乱搞!'}, status=status.HTTP\_400\_BAD\_REQUEST)

    user\_id = request.user.id  
    conn = get\_redis\_connection('cart')  # 1:{1,3}  
    conn.srem('selected\_cart\_%s' % user\_id, course\_id)

    return Response({'msg': '恭喜你!少花钱了,但是你真的不学习了吗!'}) 

为两个函数配置路由

# cart/urls.py
from django.urls import path,re_path
from . import views

urlpatterns = [
path('add_cart/', views.AddCartView.as_view({'post':'add',
'get':'cart_list',
'patch':'change_select',
'put':'cancel_select'})) # 不同的请求方法走不同函数

]

3.勾选/非勾选应该在前端页面重新计算价格


// Cartitem.vue
watch:{

  'cart.selected':function (){

    // 添加选中  
    let token = localStorage.token || sessionStorage.token;  
    if (this.cart.selected){  
      this.$axios.patch(\`${this.$settings.Host}/cart/add\_cart/\`,{  
        course\_id: this.cart.course\_id,

      },{  
        headers:{  
          'Authorization':'jwt ' + token  
        }  
      }).then((res)=>{  
        this.$message.success(res.data.msg);  
        this.$emit('cal\_t\_p') // 触发cart组件计算商品总价格的方法  
      }).catch((error)=>{  
        this.$message.error(res.data.msg);  
      })  
    }  
    else {

      // 取消选中  
      this.$axios.put(\`${this.$settings.Host}/cart/add\_cart/\`,{  
        course\_id: this.cart.course\_id,

      },{  
        headers:{  
          'Authorization':'jwt ' + token  
        }  
      }).then((res)=>{  
        this.$message.success(res.data.msg);  
        this.$emit('cal\_t\_p') // 触发cart组件计算商品总价格的方法  
      }).catch((error)=>{  
        this.$message.error(res.data.msg);  
      })  
    }  
  },

}

触发Cart组件(父组件)的计算商品总价格的方法

// Cart.vue
methods:{
cal_total_price(){

    let t\_price = 0  
    this.cart\_data\_list.forEach((v,k)=>{ // v是值 k是索引  
      if (v.selected){  
        t\_price += v.real\_price  
      }  
    })  
    this.total\_price = t\_price

  }  

}

4.购物车列表显示-后端接口

# cart/views.py
def cart_list(self, request):

    user\_id = request.user.id

    conn = get\_redis\_connection('cart')

    conn.delete('selected\_cart\_%s' % user\_id)  
    ret = conn.hgetall('cart\_%s' % user\_id)  # dict {b'1': b'0', b'2': b'0'}  
    cart\_data\_list = \[\]

    try:  
        for cid, eid in ret.items():  
            course\_id = cid.decode('utf-8') # 取出用户购物车里的课程id(redis中存着呢)  
            expire\_id = eid.decode('utf-8') # 取出用户购物车里的有效期id(redis中存着呢)

            course\_obj = models.Course.objects.get(id=course\_id) # 根据课程id,通过ORM查询得到课程对象,在下面就可以通过课程对象.字段 取到对应课程的参数信息

            cart\_data\_list.append({  
                'course\_id': course\_obj.id,  
                'name': course\_obj.name,  
                'course\_img': constants.SERVER\_ADDR + course\_obj.course\_img.url,  
                'price': course\_obj.price,  
                'real\_price': course\_obj.real\_price(),  
                'expire\_id': expire\_id,  
                'selected': False,  # 默认没有勾选  
            })  
    except Exception:  
        logger.error('获取购物车数据失败')  
        return Response({'msg': '后台数据库出问题了,请联系管理员'}, status=status.HTTP\_507\_INSUFFICIENT\_STORAGE)

    return Response({'msg': 'xxx', 'cart\_data\_list': cart\_data\_list})

BUG:勾选两个课程 刷新页面 redis中仍然存着两个课程id

# cart/views.py
def cart_list(self, request):
……
conn = get_redis_connection('cart')
# 用户刷新页面时,从redis中删除用户对应的课程id
conn.delete('selected_cart_%s' % user_id)
ret = conn.hgetall('cart_%s' % user_id)
……

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章