加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

day86:luffy:前端发送请求生成订单&结算页面优惠劵的实现

发布时间:2020-12-20 09:59:54 所属栏目:Python 来源:网络整理
导读:目录 1.前端发送请求生成订单 1.前端点击支付按钮生成订单 2.结算成功之后应该清除结算页面的数据 3.后端计算结算页面总原价格和总的真实价格并存到数据库订单表中 2.优惠劵 1.准备工作 2.前端展示优惠券信息-初始化 3.优惠券-前端获取优惠券数据+后端接口 4

目录

1.前端发送请求生成订单

  1.前端点击支付按钮生成订单

  2.结算成功之后应该清除结算页面的数据

  3.后端计算结算页面总原价格和总的真实价格并存到数据库订单表中

2.优惠劵

  1.准备工作

  2.前端展示优惠券信息-初始化

  3.优惠券-前端获取优惠券数据+后端接口

  4.结算页面计算真实总价格

  5.优惠券是否真的能够使用+优惠劵前端计算

    1.优惠劵的选中效果

    2.不可点击的不应该具有点击效果

    3.优惠券箭头收缩之后 取消优惠券的选中状态

    4.当选中的优惠劵发生变化时 重新计算总价

  6.优惠劵后端对优惠劵进行校验

1.前端发送请求生成订单

1.前端点击支付按钮生成订单

昨日讲到了后端如何生成订单(order/add_money)[传送门:后端生成订单的接口]? 但是前端的请求还没有发,现在来做一下前端发送请求来生成订单。

点击支付按钮,触发生成订单事件,生成一个订单

order.vue

<!-- html -->
 给支付按钮绑定一个事件 该事件向后端发起请求来生成订单 <el-col :span="4" class="cart-pay"><span @click="payhander">支付</span></el-col>
// js
// 生成订单
payhander(){
    let token = localStorage.token || sessionStorage.token;
    this.$axios.post(`${this.$settings.Host}/order/add_money/`,{
        
         生成订单需要提交的支付类型、优惠券、积分
        "pay_type":this.pay_type,"coupon":.current_coupon,"credit":0

    },{
        headers:{
            'Authorization':'jwt ' + token
        }
    }).then((res)=>{
        this.$message.success('订单已经生成,马上跳转支付页面')
    }).catch((error)=>.$message.error(error.response.data.msg);
    })



}

2.结算成功之后应该清除结算页面的数据

结算成功之后应该清除结算界面的数据,如果用户还想购买课程的话,应该去购物车去再次选中自己想要购买的商品再购买,然后再重新生成订单。

所以应该redis中删除用户选中的课程id,也就是selected_cart的数据

order/serializers.py

# serializers.py
    def create(self,validated_data):
        
         结算成功之后,再清除
        conn = get_redis_connection('cart')
        conn.delete(selected_cart_%s' % user_id)
        
        return order_obj

3.后端计算结算页面总原价格和总的真实价格并存到数据库订单表中

order/serializers.py

try:
         生成订单号  [日期,用户id,自增数据]

        total_price = 0   总原价
        total_real_price = 0   总真实价格

        with transaction.atomic():   添加事务
             生成订单,保存到数据库中
           ......
             生成订单详情
 计算所有课程的总原价
            total_price += course_obj.price

             计算所有课程的总真实价格
            total_real_price += course_obj.real_price(expire_id)

             将订单总原价和总真实价格存到数据库表中
            order_obj.total_price = total_price
            order_obj.real_price = total_real_price
            order_obj.save()

            except Exception:
                raise models.Order.DoesNotExist

            return order_obj

2.优惠劵

1.准备工作

1.创建coupon应用,并配置INSTALLAPP

python3 ../../manage.py startapp coupon

2.coupon/models.py

from django.db import models
from lyapi.utils.models  BaseModel
from users.models  User
from datetime  timedelta

 Create your models here.
class Coupon(BaseModel):
    """优惠券"""
    coupon_choices = (
        (0,折扣优惠),(1,减免优惠)
    )
    name = models.CharField(max_length=32,verbose_name="优惠券标题")
    coupon_type = models.SmallIntegerField(choices=coupon_choices,default=0,1)">优惠券类型)
    timer = models.IntegerField(verbose_name=优惠券有效期",default=7,help_text=默认当前优惠券7天有效,如果设置值为-1则表示当前优惠券永久有效)
    condition = models.IntegerField(blank=True,1)">满足使用优惠券的价格条件,如果设置值为0,则表示没有任何条件)
    sale = models.TextField(verbose_name=优惠公式
        *号开头表示折扣价,例如*0.82表示八二折;<br>
        -号开头表示减免价,例如-10表示在总价基础上减免10元<br>    
        )

     Meta:
        db_table = ly_coupon
        verbose_name=
        verbose_name_plural="

    def __str__(self):
        return %s" % (self.name)


 UserCoupon(BaseModel):
    user = models.ForeignKey(User,on_delete=models.CASCADE,related_name=coupons用户)
    coupon = models.ForeignKey(Coupon,1)">users)
    start_time = models.DateTimeField(verbose_name=优惠策略的开始时间)
    is_use = models.BooleanField(default=False,1)">优惠券是否使用过ly_user_coupon
        verbose_name = 用户的优惠券
        verbose_name_plural = 优惠券:%s,用户:%s (self.coupon.name,self.user.username)


    @property
     end_time(self):
        s_time = self.start_time
        timer = self.coupon.timer  天数

        return s_time + timedelta(days=timer)
优惠劵表结构设计

3.数据库迁移指令

python3 ../../manage.py makemigrations
python3 ../../manage.py migrate

4.adminx注册

coupon/adminx.py

 xadmin
from .models  Coupon
 CouponModelAdmin(object):
    优惠券模型管理类
    list_display = [namecoupon_typetimer]
xadmin.site.register(Coupon,CouponModelAdmin)


 UserCoupon
 UserCouponModelAdmin(object):
    我的优惠券模型管理类usercouponstart_timeis_use]

xadmin.site.register(UserCoupon,UserCouponModelAdmin)

5.插入一些数据

INSERT INTO `ly_coupon` VALUES (1,1); font-weight: bold">0,'2019-08-21 15:59:04.568037',1)">2019-08-21 15:59:04.568061十元优惠券30,1); font-weight: bold">10,1)">-10'),(2,1)">2019-08-21 15:59:33.7648072019-08-21 15:59:33.764830五十元优惠券50,1)">-503,1)">2019-08-21 16:00:10.0901002019-08-21 16:00:10.0901269折优惠券7,1)">*0.9);


INTO `ly_user_coupon` VALUES
(2019-08-21 16:00:40.8239772019-08-23 19:23:58.1176002019-08-21 01:00:00.00000012019-08-21 16:00:49.8685972019-08-22 09:37:46.0100372019-10-01 01:00:00.0000002019-08-21 16:01:09.0518622019-08-23 19:31:02.6052532019-08-21 01:01:00.0000004,1); font-weight: bold">5,1)">2019-08-22 08:48:56.4066712019-08-22 08:48:56.4066942019-08-22 17:48:00.0000001);

2.前端展示优惠券信息-初始化

div ="discount">
    id="accordion">
        ="coupon-box">
            ="icon-box">
                ="select-coupon">使用优惠劵:a ="select-icon unselect" :class="use_coupon?'is_selected':''" @click="use_coupon=!use_coupon"img ="sign is_show_select" src="../../static/img/12.png" alt=""a="coupon-num">有0张可用divp ="sum-price-wrap">商品总金额:="sum-price">0.00元p="collapSEOne" v-if="use_coupon"ul ="coupon-list"  v-if="coupon_list.length>0"li ="coupon-item"="select_coupon(index,coupon.id)" v-for="(coupon,index) in coupon_list"="change_coupon(index,coupon.id)">
                    ="coupon-name">8.5折优惠券="coupon-condition">满0元可以使用="coupon-time start_time">开始时间:="coupon-time end_time">过期时间:li>

            ul="no-coupon"="coupon_list.length<1"="no-coupon-tips">暂无可用优惠券="credit-box"label ="my_el_check_box"el-checkbox ="my_el_checkbox" v-model="use_credit"el-checkboxlabel="discount-num1"="!use_credit">使用我的贝里="discount-num2" v-else>总积分:100,已抵扣 ¥0.00,本次花费0积分="sun-coupon-num">优惠券抵扣:>
>
前端展示优惠劵信息-HTML

/* css */
.coupon-box{
  text-align: left;
  padding-bottom: 22px;
  padding-left:30px;
  border-bottom: 1px solid #e8e8e8;
}
.coupon-box::after{
  content: "";
  display: block;
  clear: both;
}
.icon-box{
  float: left;
}
.icon-box .select-coupon{
  color: #666;
  font-size: 16px;
}
.icon-box::after{"";both; block;
}
.select-icon{
  width: 20px;
  height:
.select-icon img{
  max-height:100%;
  max-width: 100%;
  margin-top: 2px;
  transform: rotate(-90deg);
  transition: transform .5s;
}
.is_show_select{ rotate(0deg)!important;
}
.coupon-num{
    height:
    line-height:
    padding: 0 5px;
    text-align: center;
    font-size: 12px;
    float:
    color: #fff;
    letter-spacing: .27px;
    background: #fa6240;
    border-radius:
    margin-left: 20px;
}
.sum-price-wrap{ right; 16px; #4a4a4a;
    margin-right: 45px;
}
.sum-price-wrap .sum-price{ 18px; #fa6240;
}

.no-coupon{
  padding: 50px 0px;
  align-items:
  justify-content: center;  文本两端对其 */ 1px solid rgb(232,232,232);
}
.no-coupon-tips{ #9b9b9b;
}
.credit-box{ 30px; 40px; flex; flex-end
}
.my_el_check_box{
  position: relative;
}
.my_el_checkbox{
  margin-right: 10px;
.discount{
    overflow: hidden;
}
.discount-num1{ #9b9b9b;
.discount-num2{ 45px; #4a4a4a;
}
.sun-coupon-num{
  margin-bottom:43px; inline-block; right;
}
.sun-coupon-num span{
.coupon-list{
  margin: 20px 0;
}
.coupon-list::after{
.coupon-item{ 15px 8px; 180px; 100px; 5px;
  background-color: #fa3030;
  cursor: pointer;
}
.coupon-list .active{ #fa9000;
}
.coupon-list .disable{ not-allowed; #fa6060;
}
.coupon-condition{ #fff;
}
.coupon-name{ 24px; center;
}
.coupon-time{ 12px;
}
.unselect{
  margin-left: 0px; rotate(-90deg);
}
.is_selected{ rotate(-1turn)!important;
}
  .coupon-item p{
    margin: 0; 0;
  }
前端展示优惠劵信息样式-CSS

3.优惠券-前端获取优惠券数据+后端接口

1.优惠劵后端接口

coupon/urls.py

 coupon/urls.py
from django.urls  path,re_path
from .  views


urlpatterns = [
    re_path(rlist/,views.CouponView.as_view(),),]

lyapi/urls.py

 lyapi/urls.py

from xadmin.plugins  xversion
xversion.register_models()

urlpatterns = [
    ......
    path(rcoupon/coupon.urls)),1)">coupon/views.py

 coupon/views.py
from django.shortcuts  render
from rest_framework.generics  ListAPIView
from rest_framework.permissions  IsAuthenticated

from .serializers  UserCouponModelSerializer


 CouponView(ListAPIView):

    serializer_class = UserCouponModelSerializer
    permission_classes = [IsAuthenticated,]
     get_queryset(self):

        return models.UserCoupon.objects.filter(is_show=True,is_deleted=False,is_use=False,user_id=self.request.user.id)

coupon/serializers.py

 coupon/serializers.py
from rest_framework  serializers
 Coupon,UserCoupon
 CouponModelSerializer(serializers.ModelSerializer):
     Meta:
        model = Coupon
        fields = (conditionsale)


 UserCouponModelSerializer(serializers.ModelSerializer):
    coupon = CouponModelSerializer()
     UserCoupon
        fields = (idend_time")

2.间接计算优惠劵的结束时间

 coupon/models.py
 UserCoupon(BaseModel):
    ......A
    @property  调用类中该方法时不需要加大括号 将其视作为属性
    天数
        return s_time + timedelta(days=timer)

3.前端发送请求获取优惠券数据

order.vue -js get_user_coupon(){ let token = localStorage.token ||this.$axios.get(`${this.$settings.Host}/coupon/list/`,1)">this.coupon_list = res.data; 获取到的优惠劵数据 }).this.$message.error('优惠券获取错误') }) },

 order.vue  html  -->
 >
     >
         >{{coupon.coupon.name}}>满{{coupon.coupon.condition}}元可以使用>开始时间:{{coupon.start_time.replace('T',' ')}}>过期时间:{{coupon.end_time.replace('T',1)">>

>

4.结算页面计算真实总价格

之前的结算页面只差真实的总价格没有计算了。现在通过后端计算真实总价格然后发送给前端。

1.后端计算好结算页面的总价格和总真实价格

cart/views.py

 cart/views.py

 结算页面数据
 show_pay_info(self,request):
    user_id = request.user.id
    conn = get_redis_connection()
    select_list = conn.smembers( user_id)
    data = []

    ret = conn.hgetall(cart_%s' % user_id)   dict {b'1': b'0',b'2': b'0'}

    total_price = 0
    total_real_price = 0
    
    for cid,eid in ret.items():
        expire_id = int(eid.decode(utf-8))
        if cid  select_list:

            course_id = int(cid.decode())
            course_obj = models.Course.objects.get(id=course_id)

            if expire_id > 0:
                expire_obj = models.CourseExpire.objects.get(id=expire_id)
                
                 查询到每个已勾选课程的真实价格
                course_real_price = course_obj.real_price(expire_id)
                
                 计算出所有课程的总真实价格
                total_real_price += course_real_price
                
                data.append({
                    course_id:course_obj.id,1)">:course_obj.name,1)">course_img':contains.SERVER_ADDR + course_obj.course_img.url, 结算页面的每条数据都显示为每个课程的真实价格
                    real_price:course_real_price,1)">expire_text:expire_obj.expire_text,})
                else:
                    course_real_price = course_obj.real_price(expire_id)
                    total_real_price += course_real_price
                    data.append({
                        : course_obj.id,1)">: course_obj.name,1)">': contains.SERVER_ADDR +: course_real_price,1)">': 永久有效return Response({data':data,1)">total_real_price':total_real_price})

2.前端发送请求 获取结算页面的数据、总价格、总真实价格

Order.vue get_order_data(){ let token = localStorage.token ||this.$settings.Host}/cart/expires/`,{ headers:{ 'Authorization':'jwt ' + 获取到课程名称、课程封面图、有效期信息等 this.course_list = res.data.data; 获取到后端发送过来的总真实价格 this.total_real_price = res.data.total_real_price; 获取到后端发送过来的总原价格 this.total_price = res.data.total_real_price; }) },

5.优惠券是否真的能够使用+优惠劵前端计算

1.优惠劵的选中效果

不在活动范围内的优惠劵应该设置不可选中的效果

如果选中了当期优惠劵,则应该给优惠劵设置为选中的效果

order.vue js select_coupon(index,coupon_id){ 拿到你当前点击的那条优惠劵数据 let current_c = .coupon_list[index] if (this.total_real_price < current_c.coupon.condition){ return 'disable' } '/1000'拿到时间戳 let current_time = new Date() / 1000; let s_time = new Date(current_c.start_time) / 1000 let e_time = new Date(current_c.end_time) / 1000 如果优惠劵不在活动时间范围内 则设置效果为不可选中 if (current_time < s_time || current_time > e_time){ } 如果优惠劵是当前被选中的优惠劵 则设置效果为选中 this.current_coupon === coupon_id){ return 'active' } return '' },1)"> order.vue html >

2.不可点击的不应该具有点击效果

change_coupon(index,coupon_id){
    let current_c = .coupon_list[index]
    
     如果优惠劵不符合条件 则优惠劵是不可点击的
     current_c.coupon.condition){
        return false
    }
    let current_time = ;
    let s_time = 
    let e_time = new Date(current_c.end_time) / 1000
    
     e_time){
        
    }

    this.current_coupon = coupon_id;
    this.coupon_obj = current_c;

},

3.优惠券箭头收缩之后 取消优惠券的选中状态

watch:{ use_coupon(){ 如果点击箭头收缩 那么就将优惠劵的选中状态取消 this.use_coupon === ){ this.current_coupon = 0; } },

4.当选中的优惠劵发生变化时 重新计算总价

order.vue

?

 当选中的优惠券发生变化时,重新计算总价
watch:{
    current_coupon(){
        .cal_total_price();
    }
}


 methods: {

      cal_total_price(){
         当用户选中了某个优惠劵
        this.current_coupon !== 0){
          
           1.获取用户使用优惠劵前的真实价格
          let tt = .total_real_price;
          
           2.获取当前优惠劵的优惠公式
          let sales = .coupon_obj.coupon.sale;
            
           3.取出优惠公式的数字部分
          let d = parseFloat(this.coupon_obj.coupon.sale.substr(1));
          if (sales[0] === '-'){
            tt = this.total_real_price - d
          }else if (sales[0] === '*'this.total_real_price * d
          }
           tt;

        }

      },

?

6.优惠劵后端对优惠劵进行校验

前端虽然对优惠劵进行校验,但是前端所显示的都是虚假的,后端也要对优惠劵进行校验。

 OrderModelSerializers:
     validate(self,attrs):

         验证支付方式是否合法
        pay_type = int(attrs.get(pay_type#

        if pay_type not in [i[0] for i  models.Order.pay_choices]:
            raise serializers.ValidationError(支付方式不对!)


          优惠券校验,看看是否过期了等等
        coupon_id = attrs.get(if coupon_id > 0:
            :
                user_conpon_obj = UserCoupon.objects.get(id=coupon_id)
            :
                订单创建失败,优惠券id不对)
             校验优惠劵是否在活动时间范围内
            now = datetime.datetime.now().timestamp()
            start_time = user_conpon_obj.start_time.timestamp()
            end_time = user_conpon_obj.end_time.timestamp()
            if now < start_time or now > end_time:
                订单创建失败,优惠券不在使用范围内,滚犊子 todo 积分上限校验



        return attrs

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读