• luffy_08


    08支付和订单

    为了方便开发,和以后项目的维护,我们再次创建子应用orders来完成接下来的订单和订单支付功能。

    cd luffy/apps
    python ../../manage.py startapp orders
    

    注册子应用,settings/dev.py,代码:

    INSTALLED_APPS = [
        # 子应用
    	。。。
        
        'orders',
    ]
    

    订单模型

    from django.db import models
    
    # Create your models here.
    from luffy.utils.models import BaseModel
    from users.models import User
    from courses.models import Course
    class Order(BaseModel):
        """订单记录"""
        status_choices = (
            (0, '未支付'),
            (1, '已支付'),
            (2, '已取消'),
            (3, '超时取消'),
        )
        pay_choices = (
            (0, '支付宝'),
            (1, '微信支付')
        )
        order_title = models.CharField(max_length=150,verbose_name="订单标题")
        total_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="订单总价", default=0)
        real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="实付金额", default=0)
        order_number = models.CharField(max_length=64,verbose_name="订单号")
        order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="订单状态")
        pay_type = models.SmallIntegerField(choices=pay_choices, default=0, verbose_name="支付方式")
        use_coupon = models.BooleanField(default=False,verbose_name="是否使用优惠券")
        coupon = models.IntegerField(null=True, verbose_name="用户优惠券ID")
        order_desc = models.TextField(max_length=500,null=True,blank=True, verbose_name="订单描述")
        pay_time = models.DateTimeField(null=True, verbose_name="支付时间")
        user = models.ForeignKey(User, related_name='user_orders', on_delete=models.DO_NOTHING,verbose_name="下单用户")
    
        class Meta:
            db_table="ly_order"
            verbose_name= "订单记录"
            verbose_name_plural= "订单记录"
    
        def __str__(self):
            return "%s,总价: %s,实付: %s" % (self.order_title, self.total_price, self.real_price)
    
    from courses.models import CourseTime
    class OrderDetail(BaseModel):
        """订单详情"""
        order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, verbose_name="订单")
        course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.CASCADE, verbose_name="课程")
        expire = models.IntegerField(default='-1', verbose_name="有效期周期")
        price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价")
        real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程实价")
        discount_name = models.CharField(max_length=120,default="",verbose_name="优惠活动类型")
        class Meta:
            db_table="ly_order_detail"
            verbose_name= "订单详情"
            verbose_name_plural= "订单详情"
    

    makemigrations && migrate

    python manage.py makemigrations;
    python manage.py migrate;
    

    把当前子应用注册到xadmin中

    在当前子应用下创建adminx.py,代码:

    import xadmin
    
    from .models import Order
    class OrderModelAdmin(object):
        """订单模型管理类"""
        pass
    
    xadmin.site.register(Order, OrderModelAdmin)
    
    
    from .models import OrderDetail
    class OrderDetailModelAdmin(object):
        """订单详情模型管理类"""
        pass
    
    xadmin.site.register(OrderDetail, OrderDetailModelAdmin)
    

    后端实现生成订单的api接口

    from django.shortcuts import render
    from rest_framework.views import APIView
    from rest_framework.permissions import IsAuthenticated
    from django_redis import get_redis_connection
    from rest_framework.response import Response
    from .models import Order,OrderDetail
    from datetime import datetime
    from courses.models import Course, CourseTime
    import random
    from django.db import transaction
    from rest_framework import status
    
    import logging
    log = logging.getLogger("django")
    
    class OrderAPIView(APIView):
        permission_classes = [IsAuthenticated]
        def post(self,request):
            """生成订单"""
            # 获取用户ID
            # user_id = 1
            user_id = request.user.id
    
            # 订单号,必须保证唯一
            order_number =  datetime.now().strftime("%Y%m%d%H%M%S") + "%08d" % user_id + "%04d" % random.randint(0,9999)
    
            with transaction.atomic():
    
                # 数据库事务的回滚标记
                save_id = transaction.savepoint()
    
                # 生成空的订单
                try:
                    order = Order.objects.create(
                        order_title="路飞学城课程购买",
                        total_price=0,
                        real_price=0,
                        order_number= order_number,
                        user_id=user_id,
                    )
    
                    # 到redis获取购物车信息
                    redis = get_redis_connection("cart")
                    # 勾选状态
                    course_selects_set = redis.smembers("cart_selected_%s" % user_id )
                    print( course_selects_set )
                    # 购物车中商品课程列表
                    cart_course_list = redis.hgetall("cart_%s" % user_id )
                    print( cart_course_list )
                    # 通过购物车信息到数据中提取相关数据
                    # 计算订单总价
                    total_price = 0
    
                    # 开启redis的管道操作[事务操作]
                    pipeline = redis.pipeline()
                    pipeline.multi()
    
                    for course_id_byte,expire_byte in cart_course_list.items():
                        if course_id_byte in course_selects_set:
                            expire = expire_byte.decode()
                            course_id = course_id_byte.decode()
                            course = Course.objects.get(pk=course_id)
                            if expire == '-1':
                                """永久有效"""
                                course_price = course.get_course_price()
    
                            else:
                                """有购买周期"""
                                coursetime = CourseTime.objects.get(course=course_id,timer=expire)
                                course_price = coursetime.course.get_course_price(coursetime.price)
    
                            total_price += course_price
    
                            # 生成订单详情
                            OrderDetail.objects.create(
                                order_id=order.id,
                                course_id=course_id,
                                expire=expire,
                                price=course.price if expire=='-1' else coursetime.price,
                                real_price=course_price,
                                discount_name=course.get_course_discount_type()
                            )
    
                            # 从购物车中移除已经加入订单的商品
                            pipeline.srem("cart_selected_%s" % user_id, course_id )
                            pipeline.hdel("cart_%s" % user_id, course_id )
    
                    # 提交redis的事务操作
                    pipeline.execute()
                    # 补充订单的总价格
                    order.total_price=total_price
                    order.real_price=total_price
                    order.save()
    
                except Exception:
                    # 记录错误日志
                    log.error( "%s" % Exception )
                    # 回滚事务
                    transaction.savepoint_rollback(save_id)
                    # 响应结果
                    return Response({"message":"系统异常!"},status=status.HTTP_507_INSUFFICIENT_STORAGE)
    
            # 响应结果
            return Response({"message":"成功生成订单!","order":order.order_number})
    

    上面我们使用了redis的事务操作保证数据的一致性。但是mysql里面我们也是在进行多表操作,所以也是需要使用事务来保证数据的一致性的。

    事务有四大特性:

    原子性(Atomicity)
    一致性(Consistency)
    隔离性(Isolation)[事务隔离级别->幻读,脏读]
    持久性(Durability)
    

    django框架本身就提供了2种事务操作的用法。

    django的事务操作方法主要通过 django.db.transation模块完成的。

    启用事务用法1:

    from django.db import transaction
    from rest_framework.views import APIView
    class OrderAPIView(APIView):
    	@transaction.atomic          # 开启事务,当方法执行完成以后,自动提交事务
        def post(self,request):
            ....
    

    启用事务用法2:

    from django.db import transaction
    from rest_framework.views import APIView
    class OrderAPIView(APIView):
        def post(self,request):
            ....
            with transation.atomic(): # 开启事务,当with语句执行完成以后,自动提交事务
                # 数据库操作
    

    在使用事务过程中, 有时候会出现异常,当出现异常的时候,我们需要让程序停止下来,同时需要回滚事务。

    from django.db import transaction
    from rest_framework.views import APIView
    class OrderAPIView(APIView):
        def post(self,request):
            ....
            with transation.atomic():
                # 设置事务回滚的标记点
                sid = transation.savepoint()
    
                ....
    
                try:
                    ....
                except:
                    transation.savepoint_rallback(sid)
    

    使用django提供的mysql事务操作保证下单过程中的数据一致性

    视图代码:

    from django.shortcuts import render
    from rest_framework.views import APIView
    from rest_framework.permissions import IsAuthenticated
    from django_redis import get_redis_connection
    from rest_framework.response import Response
    from .models import Order,OrderDetail
    from datetime import datetime
    from courses.models import Course, CourseTime
    import random
    from django.db import transaction
    from rest_framework import status
    
    import logging
    log = logging.getLogger("django")
    
    class OrderAPIView(APIView):
        permission_classes = [IsAuthenticated]
        def post(self,request):
            """生成订单"""
            # 获取用户ID
            # user_id = 1
            user_id = request.user.id
    
            # 订单号,必须保证唯一
            order_number =  datetime.now().strftime("%Y%m%d%H%M%S") + "%08d" % user_id + "%04d" % random.randint(0,9999)
    
            with transaction.atomic():
    
                # 数据库事务的回滚标记
                save_id = transaction.savepoint()
    
                # 生成空的订单
                try:
                    order = Order.objects.create(
                        order_title="路飞学城课程购买",
                        total_price=0,
                        real_price=0,
                        order_number= order_number,
                        user_id=user_id,
                    )
    
                    # 到redis获取购物车信息
                    redis = get_redis_connection("cart")
                    # 勾选状态
                    course_selects_set = redis.smembers("cart_selected_%s" % user_id )
                    print( course_selects_set )
                    # 购物车中商品课程列表
                    cart_course_list = redis.hgetall("cart_%s" % user_id )
                    print( cart_course_list )
                    # 通过购物车信息到数据中提取相关数据
                    # 计算订单总价
                    total_price = 0
    
                    # 开启redis的管道操作[事务操作]
                    pipeline = redis.pipeline()
                    pipeline.multi()
    
                    for course_id_byte,expire_byte in cart_course_list.items():
                        if course_id_byte in course_selects_set:
                            expire = expire_byte.decode()
                            course_id = course_id_byte.decode()
                            course = Course.objects.get(pk=course_id)
                            if expire == '-1':
                                """永久有效"""
                                course_price = course.get_course_price()
    
                            else:
                                """有购买周期"""
                                coursetime = CourseTime.objects.get(course=course_id,timer=expire)
                                course_price = coursetime.course.get_course_price(coursetime.price)
    
                            total_price += course_price
    
                            # 生成订单详情
                            OrderDetail.objects.create(
                                order_id=order.id,
                                course_id=course_id,
                                expire=expire,
                                price=course.price if expire=='-1' else coursetime.price,
                                real_price=course_price,
                                discount_name=course.get_course_discount_type()
                            )
    
                            # 从购物车中移除已经加入订单的商品
                            pipeline.srem("cart_selected_%s" % user_id, course_id )
                            pipeline.hdel("cart_%s" % user_id, course_id )
    
                    # 提交redis的事务操作
                    pipeline.execute()
                    # 补充订单的总价格
                    order.total_price=total_price
                    order.real_price=total_price
                    order.save()
    
                except Exception:
                    # 记录错误日志
                    log.error( "%s" % Exception )
                    # 回滚事务
                    transaction.savepoint_rollback(save_id)
                    # 响应结果
                    return Response({"message":"系统异常!"},status=status.HTTP_507_INSUFFICIENT_STORAGE)
    
            # 响应结果
            return Response({"message":"成功生成订单!","order":order.order_number})
    

    **change the ** utils/models.py

    from django.db import models
    
    
    class BaseModel(models.Model):
        """公共字段模型"""
        orders = models.IntegerField(verbose_name='显示顺序', null=True, blank=True)
        is_show = models.BooleanField(verbose_name="是否上架", default=False)
        is_delete = models.BooleanField(verbose_name="逻辑删除", default=False)
        create_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
        update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
    
        class Meta:
            # 设置当前模型在数据迁移的时候不要为它创建表
            abstract = True
    

    the main urls.py

    from django.contrib import admin
    from django.urls import path, re_path, include
    import xadmin
    from xadmin.plugins import xversion
    from django.views.static import serve
    from django.conf import settings
    
    xadmin.autodiscover()
    xversion.register_models()  # version模块自动注册需要版本控制的 Model
    
    urlpatterns = [
        ......
        path('orders/', include("orders.urls")),
    ]
    
    

    orders/urls.py

    from django.urls import path, re_path
    from . import views
    
    
    urlpatterns = [
        path("", views.OrderAPIView.as_view()),
    ]
    

    前端请求生成订单

    <template>
      。。。
                <span class="go-pay" @click="gotopay">去结算</span>
      。。。
    </template>
    <script>
    import Header from "./common/Header"
    import Footer from "./common/Footer"
    export default {
        name: "Cart",
        。。。。
        methods:{
          。。。。
          gotopay(){
            // 提交结算,生成订单
            this.$axios.post(this.$settings.Host+"/orders/",{},{
                headers:{
                  // 注意:jwt后面必须有且只有一个空格!!!!
                  "Authorization":"jwt " + this.token
                }
            }).then(response=>{
              let _this = this;
              this.$alert(response.data.message,"提示",{
                callback(){
                  _this.$router.push(`orders/${response.data.order}`);
                }
              })
            }).catch(error=>{
              console.log(error.response)
            })
          }
        },
        components:{Header,Footer}
    }
    </script>
    

    create a Order.vue

    <template>
        
    </template>
    
    <script>
        export default {
            name: "Order"
        }
    </script>
    
    <style scoped>
    
    </style>
    

    router/index.js

    import Vue from 'vue'
    import Router from 'vue-router'
    import Home from '../components/Home'
    import Login from "../components/Login";
    import Register from "../components/Register";
    import Course from "../components/Course";
    import Detail from "../components/Detail";
    import Player from "../components/Player";
    import Cart from "../components/Cart";
    import Order from "../components/Order";
    
    Vue.use(Router);
    
    export default new Router({
      mode: 'history',
      routes: [
          ...
        {
          name: "Order",
          path: "/orders/:order",  // 地址栏的命名绑定参数, 在组件中可以通过 this.$router.param.order可以获取数据
          component: Order,
        }
      ]
    })
    
    

    显示结算页面

    Order.vue

    <template>
      <div class="cart">
        <Header/>
        <div class="cart-info">
            <h3 class="cart-top">购物车结算 <span>共1门课程</span></h3>
            <div class="cart-title">
               <el-row>
                 <el-col :span="2">&nbsp;</el-col>
                 <el-col :span="10">课程</el-col>
                 <el-col :span="8">有效期</el-col>
                 <el-col :span="4">价格</el-col>
               </el-row>
            </div>
              <div class="cart-item" v-for="item in course_list">
              <el-row>
                 <el-col :span="2" class="checkbox">&nbsp;&nbsp;</el-col>
                 <el-col :span="10" class="course-info">
                   <img :src="item.course.course_http_img" alt="">
                    <span>{{item.course.name}}</span>
                 </el-col>
                 <el-col :span="8"><span>永久有效</span></el-col>
                 <el-col :span="4" class="course-price">¥{{item.unit_price}}</el-col>
               </el-row>
            </div>
            <div class="calc">
                <el-row class="pay-row">
                  <el-col :span="4" class="pay-col"><span class="pay-text">支付方式:</span></el-col>
                  <el-col :span="8">
                    <span class="alipay"><img src="../../static/img/alipay2.png" alt=""></span>
                    <span class="alipay wechat"><img src="../../static/img/wechat.png" alt=""></span>
                  </el-col>
                  <el-col :span="8" class="count">实付款: <span>¥{{total}}</span></el-col>
                  <el-col :span="4" class="cart-pay"><span @click="payhander">支付宝支付</span></el-col>
                </el-row>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default {
        name:"Order",
        data(){
          return {
            token: localStorage.token || sessionStorage.token,
            id: localStorage.id || sessionStorage.id,
            order_id:sessionStorage.order_id || null,
            course_list:[],
            total:0,
          }
        },
        components:{
          Header,
          Footer,
        },
        created(){
    
        },
        methods: {
          payhander(){
    
          }
        }
      }
    </script>
    
    <style scoped>
    .cart{
      margin-top: 80px;
    }
    .cart-info{
      overflow: hidden;
       1200px;
      margin: auto;
    }
    .cart-top{
      font-size: 18px;
      color: #666;
      margin: 25px 0;
      font-weight: normal;
    }
    .cart-top span{
        font-size: 12px;
        color: #d0d0d0;
        display: inline-block;
    }
    .cart-title{
        background: #F7F7F7;
        height: 70px;
    }
    .calc{
      margin-top: 25px;
      margin-bottom: 40px;
    }
    
    .calc .count{
      text-align: right;
      margin-right: 10px;
      vertical-align: middle;
    }
    .calc .count span{
        font-size: 36px;
        color: #333;
    }
    .calc .cart-pay{
        margin-top: 5px;
         110px;
        height: 38px;
        outline: none;
        border: none;
        color: #fff;
        line-height: 38px;
        background: #ffc210;
        border-radius: 4px;
        font-size: 16px;
        text-align: center;
        cursor: pointer;
    }
    .cart-item{
      height: 120px;
      line-height: 120px;
    }
    .course-info img{
         175px;
        height: 115px;
        margin-right: 35px;
        vertical-align: middle;
    }
    .alipay{
      display: inline-block;
      height: 48px;
    }
    .alipay img{
      height: 100%;
      auto;
    }
    
    .pay-text{
      display: block;
      text-align: right;
      height: 100%;
      line-height: 100%;
      vertical-align: middle;
      margin-top: 20px;
    }
    </style>
    
    

    后端提供订单数据的api

    orders/serializers.py,序列化器,代码:

    from rest_framework import serializers
    from .models import Order,OrderDetail
    
    class OrderCourseSerializer(serializers.ModelSerializer):
        class Meta:
            model = OrderDetail
            fields = ("course_name","expire_text","price","real_price","discount_name","course_img")
    
    class OrderDetailModelSerializer(serializers.ModelSerializer):
        order_courses = OrderCourseSerializer(many=True)
        class Meta:
            model = Order
            fields = ("id", "total_price", "real_price","pay_type","use_coupon","coupon","order_courses")
    
    

    模型中新增返回课程图片的字段,代码:

    from courses.models import CourseTime
    class OrderDetail(BaseModel):
        """订单详情"""
        order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, verbose_name="订单")
        course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.CASCADE, verbose_name="课程")
        expire = models.IntegerField(default='-1', verbose_name="有效期周期")
        price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价")
        real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程实价")
        discount_name = models.CharField(max_length=120,default="",verbose_name="优惠活动类型")
        class Meta:
            db_table="ly_order_detail"
            verbose_name= "订单详情"
            verbose_name_plural= "订单详情"
    
        def course_img(self):
            # 返回图片的url地址
            return self.course.course_img.url
    
        def expire_text(self):
            if self.expire == -1:
                return "永久有效"
            else:
                print( self.expire )
                coursetime = CourseTime.objects.get(timer=self.expire,course=self.course)
                return coursetime.title
    
        def course_name(self):
            return self.course.name
    
    

    视图代码:

    from .serializers import OrderDetailModelSerializer
    class OrderDetailAPIView(APIView):
        permission_classes = [IsAuthenticated]
        def get(self,request,pk):
            try:
                order = Order.objects.get(order_number=pk)
            except Order.DoesNotExist:
                return Response({"message":"订单信息有误!"},status=status.HTTP_400_BAD_REQUEST)
    
            serializer = OrderDetailModelSerializer(instance=order)
    
            return Response(serializer.data, status=status.HTTP_200_OK)
    

    路由代码:

        re_path(r"(?P<pk>d+)/",views.OrderDetailAPIView.as_view()),
    
    

    前端请求后端的订单信息

    <template>
      <div class="cart">
        <Header/>
        <div class="cart-info">
            <h3 class="cart-top">购物车结算 <span>共1门课程</span></h3>
            <div class="cart-title">
               <el-row>
                 <el-col :span="2">&nbsp;</el-col>
                 <el-col :span="10">课程</el-col>
                 <el-col :span="8">有效期</el-col>
                 <el-col :span="4">价格</el-col>
               </el-row>
            </div>
            <div class="cart-item" v-for="course in order_info.order_courses">
              <el-row>
                 <el-col :span="2" class="checkbox">&nbsp;&nbsp;</el-col>
                 <el-col :span="10" class="course-info">
                   <img style="float: left;" :src="$settings.Host+course.course_img" alt="">
                   <span class="course_name">
                     <span>{{course.course_name}}</span><br>
                     <span class="discount_name">{{course.discount_name}}</span>
                   </span>
                 </el-col>
                 <el-col :span="8" class="lh"><span>{{course.expire_text}}</span></el-col>
                 <el-col :span="4">
                   <div class="course-price">
                      <p class="real_price">¥{{course.real_price}}</p>
                      <span class="original_price">原价: ¥{{course.price}}</span>
                   </div>
                 </el-col>
               </el-row>
            </div>
            <div>
            <div class="coupon">
              <div id="accordion">
                <div class="coupon-box">
                  <div class="coupon-title">
                    <span class="select-coupon">使用优惠劵:</span>
                    <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" style=" 20px; height: 20px" class="collapsed" aria-expanded="false">
                      <img class="sign" src="../../static/img/12.png" width="20" height="20" alt=""></a>
                      <span class="coupon-num">有0张可用</span>
                  </div>
                  <p class="sum-price-wrap" style="margin-right: 45px">商品总金额:<span class="sum-price">{{order_info.total_price}}元</span></p>
                </div>
                <div style="text-align: left; height: 0px;" id="collapseOne" class="panel-collapse out collapse" aria-expanded="false">
                  <ul class="coupon-list" style="display: none;">
    
                  </ul>
                  <div style="text-align: center;  100%; padding: 50px 0px; align-items: center; justify-content: center; border-bottom: 1px solid rgb(232, 232, 232);">
                    <span style="font-size: 16px; color: #9b9b9b">暂无可用优惠券</span>
                  </div>
                </div>
              </div>
              <div style="height: 30px; margin-top: 40px; display: flex; align-items: center; justify-content: flex-end">
                <input type="checkbox" class="ok" id="color-input-red">
                <label for="color-input-red"><img src=""alt=""></label>
                <p class="discount-num" style="color:#9B9B9B">使用我的贝里</p>
                <p class="discount-num" style="margin-right: 45px">
                  <span style="display: none;">可用0个已抵扣 ¥0</span>
                </p>
              </div>
              <p class="sun-coupon-num" style="margin-right: 45px;margin-bottom:43px">优惠券抵扣:<span>0元</span></p>
            </div>
            </div>
            <div class="calc">
                <el-row class="pay-row">
                  <el-col :span="4" class="pay-col"><span class="pay-text">支付方式:</span></el-col>
                  <el-col :span="8">
                    <span class="alipay"><img src="../../static/img/alipay2.png" alt=""></span>
                    <span class="alipay wechat"><img src="../../static/img/wechat.png" alt=""></span>
                  </el-col>
                  <el-col :span="8" class="count">实付款: <span>¥{{order_info.total_price}}</span></el-col>
                  <el-col :span="4" class="cart-pay"><span @click="payhander">支付宝支付</span></el-col>
                </el-row>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default {
        name:"Order",
        data(){
          return {
            order_info:{},
          }
        },
        components:{
          Header,
          Footer,
        },
        created(){
          // 判断用户是否已经登录
          let token = sessionStorage.token || localStorage.token;
          let _this = this;
          if(!token){
            this.$alert("对不起,您尚未登录!请登录!","警告",{
              callback(){
                _this.$router.push("/login");
              }
            })
          }
          // 获取地址栏上面的订单号
          let order_number = this.$route.params.order;
    
          // 发送请求获取数据
          this.$axios.get(this.$settings.Host+`/orders/${order_number}/`,{
              headers:{
                // 注意下方的空格!!!
                "Authorization":"jwt " + token,
              },
          }).then(response=>{
            this.order_info = response.data;
          }).catch(error=>{
            console.log(error.response);
          })
        },
        methods: {
          payhander(){
    
          }
        }
      }
    </script>
    
    <style scoped>
    .cart{
      margin-top: 80px;
    }
    .cart-info{
      overflow: hidden;
       1200px;
      margin: auto;
    }
    .cart-top{
      font-size: 18px;
      color: #666;
      margin: 25px 0;
      font-weight: normal;
    }
    .cart-top span{
        font-size: 12px;
        color: #d0d0d0;
        display: inline-block;
    }
    .cart-title{
        background: #F7F7F7;
        height: 70px;
    }
    .calc{
      margin-top: 25px;
      margin-bottom: 40px;
    }
    
    .calc .count{
      text-align: right;
      margin-right: 10px;
      vertical-align: middle;
    }
    .calc .count span{
        font-size: 36px;
        color: #333;
    }
    .calc .cart-pay{
        margin-top: 5px;
         110px;
        height: 38px;
        outline: none;
        border: none;
        color: #fff;
        line-height: 38px;
        background: #ffc210;
        border-radius: 4px;
        font-size: 16px;
        text-align: center;
        cursor: pointer;
    }
    .cart-item{
      height: 120px;
      /*line-height: 120px;*/
    }
    .cart-item .lh{
      line-height: 120px;
    }
    .course-info img{
         175px;
        height: 115px;
        margin-right: 35px;
        vertical-align: middle;
    }
    .course-price{
      margin-top: 40px;
    }
    .alipay{
      display: inline-block;
      height: 48px;
    }
    .alipay img{
      height: 100%;
      auto;
    }
    
    .pay-text{
      display: block;
      text-align: right;
      height: 100%;
      line-height: 100%;
      vertical-align: middle;
      margin-top: 20px;
    }
    .real_price{
        color: #333;
        margin-bottom: 10px;
    }
    .original_price{
        color: #9b9b9b;
        letter-spacing: .36px;
        text-decoration: line-through;
    }
    .coupon{
      margin-top: 30px;
    }
    .coupon-box{
      text-align: left;
      display: flex;
      padding-bottom: 22px;
      padding-left:30px;
      border-bottom: 1px solid #e8e8e8;
    }
    .coupon-title{
      display: flex;
    }
    .sum-price-wrap{
        display: inline-block;
        margin-left: auto;
        font-size: 16px;
        color: #4a4a4a;
    }
    .discount_name{
        color: #ffc210;
        margin-top: 24px;
        font-size: 14px;
        letter-spacing: .32px;
    }
    .course_name{
      margin-top: 40px;
      display: block;
    }
    </style>
    
    

    优惠券

    创建一个coupon子应用.

    cd luffy/apps
    python ../../manage.py startapp coupon
    

    注册子应用

    INSTALLED_APPS = [
     
    
        # 子应用
    	。。。
        'coupon',
    ]
    

    模型分析:

    1558574562894

    代码:

    from django.db import models
    from luffy.utils.models import BaseModel
    # 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, verbose_name="优惠券类型")
        timer = models.IntegerField(verbose_name="优惠券有效期", default=30, help_text="")
        condition = models.IntegerField(blank=True, default=0, verbose_name="满足使用优惠券的价格条件")
        sale = models.TextField(verbose_name="优惠公式", help_text="""
            *号开头表示折扣价,例如*0.82表示八二折;<br>
            -号开头表示减免价,例如-10表示在总价基础上减免10元<br>    
            """)
    
        class Meta:
            db_table = "ly_coupon"
            verbose_name="优惠券"
            verbose_name_plural="优惠券"
    
        def __str__(self):
            return "%s" % (self.name)
    
    from users.models import User
    class UserCoupon(BaseModel):
        user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="coupons", verbose_name="用户")
        coupon = models.ForeignKey(Coupon, on_delete=models.CASCADE, related_name="users", verbose_name="优惠券")
        start_time = models.DateTimeField(verbose_name="优惠策略的开始时间")
        is_use = models.BooleanField(default=False,verbose_name="优惠券是否使用过")
    
        class Meta:
            db_table = "ly_user_coupon"
            verbose_name = "用户的优惠券"
            verbose_name_plural = "用户的优惠券"
    
        def __str__(self):
            return "优惠券:%s,用户:%s" % (self.coupon.name, self.user.username)
    

    数据迁移

    cd ../../
    python manage.py makemigrations
    python manage.py migrate
    

    注册到xadmin,添加测试数据[1.添加优惠券,给用户发放优惠券]

    import xadmin
    from .models import Coupon
    class CouponModelAdmin(object):
        """优惠券模型管理类"""
        list_display = ["name","coupon_type","timer"]
    xadmin.site.register(Coupon, CouponModelAdmin)
    
    
    
    from .models import UserCoupon
    class UserCouponModelAdmin(object):
        """我的优惠券模型管理类"""
        list_display = ["user","coupon","start_time","is_use"]
    
    xadmin.site.register(UserCoupon, UserCouponModelAdmin)
    

    1558574889770

    在订单页面查询当前用户拥有的优惠券

    前端编写优惠券的样式代码:

    <template>
      <div class="cart">
        <Header/>
        <div class="cart-info">
            <h3 class="cart-top">购物车结算 <span>共1门课程</span></h3>
            <div class="cart-title">
               <el-row>
                 <el-col :span="2">&nbsp;</el-col>
                 <el-col :span="10">课程</el-col>
                 <el-col :span="8">有效期</el-col>
                 <el-col :span="4">价格</el-col>
               </el-row>
            </div>
            <div class="cart-item" v-for="course in order_info.order_courses">
              <el-row>
                 <el-col :span="2" class="checkbox">&nbsp;&nbsp;</el-col>
                 <el-col :span="10" class="course-info">
                   <img style="float: left;" :src="$settings.Host+course.course_img" alt="">
                   <span class="course_name">
                     <span>{{course.course_name}}</span><br>
                     <span class="discount_name">{{course.discount_name}}</span>
                   </span>
                 </el-col>
                 <el-col :span="8" class="lh"><span>{{course.expire_text}}</span></el-col>
                 <el-col :span="4">
                   <div class="course-price">
                      <p class="real_price">¥{{course.real_price}}</p>
                      <span class="original_price">原价: ¥{{course.price}}</span>
                   </div>
                 </el-col>
               </el-row>
            </div>
            <div>
            <div class="coupon">
              <div id="accordion">
                <div class="coupon-box">
                  <div class="coupon-title">
                    <span class="select-coupon">使用优惠劵:</span>
                    <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" style=" 20px; height: 20px" class="collapsed" aria-expanded="false">
                      <img class="sign" src="../../static/img/12.png" width="20" height="20" alt=""></a>
                      <span class="coupon-num">有0张可用</span>
                  </div>
                  <p class="sum-price-wrap" style="margin-right: 45px">商品总金额:<span class="sum-price">{{order_info.total_price}}元</span></p>
                </div>
                <div style="text-align: left;" id="collapseOne" class="panel-collapse out collapse" aria-expanded="false">
                  <ul class="coupon-list" style="">
                    <li class="coupon-item">
                      <span>优惠券</span>
                      <span>¥10元</span>
                      <span>开始使用时间: 2019-10-01</span>
                    </li>
                    <li class="coupon-item">
                      <span>优惠券</span>
                      <span>¥10元</span>
                      <span>开始使用时间: 2019-10-01</span>
                    </li>
                  </ul>
                  <div style="text-align: center;  100%; padding: 50px 0px; align-items: center; justify-content: center; border-bottom: 1px solid rgb(232, 232, 232);">
                    <span style="font-size: 16px; color: #9b9b9b">暂无可用优惠券</span>
                  </div>
                </div>
              </div>
              <div style="height: 30px; margin-top: 40px; display: flex; align-items: center; justify-content: flex-end">
                <input type="checkbox" class="ok" id="color-input-red">
                <label for="color-input-red"><img src="" alt=""></label>
                <p class="discount-num" style="color:#9B9B9B">使用我的贝里</p>
                <p class="discount-num" style="margin-right: 45px">
                  <span style="display: none;">可用0个已抵扣 ¥0</span>
                </p>
              </div>
              <p class="sun-coupon-num" style="margin-right: 45px;margin-bottom:43px">优惠券抵扣:<span>0元</span></p>
            </div>
            </div>
            <div class="calc">
                <el-row class="pay-row">
                  <el-col :span="4" class="pay-col"><span class="pay-text">支付方式:</span></el-col>
                  <el-col :span="8">
                    <span class="alipay"><img src="../../static/img/alipay2.png" alt=""></span>
                    <span class="alipay wechat"><img src="../../static/img/wechat.png" alt=""></span>
                  </el-col>
                  <el-col :span="8" class="count">实付款: <span>¥{{order_info.total_price}}</span></el-col>
                  <el-col :span="4" class="cart-pay"><span @click="payhander">支付宝支付</span></el-col>
                </el-row>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      。。。。
    </script>
    
    <style scoped>
    。。。
        
    .coupon-list{
      overflow: hidden;
    }
    .coupon-item{
      float: left;
      margin-left: 10px;
      margin-right: 10px;
       200px;
      height: 120px;
      border: 1px solid #000;
      padding: 10px;
    }
    .coupon-item span{
      display: block;
      font-size: 14px;
    }
    </style>
    

    后端提供查询当前用户拥有的优惠券api接口

    序列化器,代码:

    from rest_framework import serializers
    from .models import Coupon, UserCoupon
    class CouponModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = Coupon
            fields = ("name","coupon_type","timer","condition","sale")
    
    
    class UserCouponModelSerializer(serializers.ModelSerializer):
        coupon = CouponModelSerializer()
        class Meta:
            model = UserCoupon
            fields = ("id","start_time","coupon")
    

    视图,代码:

    from rest_framework.generics import ListAPIView
    from rest_framework.permissions import IsAuthenticated
    from django_filters.rest_framework import DjangoFilterBackend
    from .models import UserCoupon
    from .serializers import UserCouponModelSerializer
    class UserCouponAPIVew(ListAPIView):
        """我的优惠券"""
        queryset = UserCoupon.objects.filter(is_show=True,is_delete=False,is_use=False)
        serializer_class = UserCouponModelSerializer
        permission_classes = [IsAuthenticated]
        filter_backends = [DjangoFilterBackend]
        ordering_fields = ('user_id',)
    
    

    子应用路由,代码:

    from django.urls import path
    from . import views
    urlpatterns = [
        path(r"list/",views.UserCouponAPIVew.as_view()),
    ]
    

    总路由,代码:

        path('coupon/', include("coupon.urls")),
    

    前端展示当前用户拥有的优惠券并勾选使用优惠券以后,会自动调整订单实付价格

    代码:

    <template>
      <div class="cart">
        <Header/>
        <div class="cart-info">
            <h3 class="cart-top">购物车结算 <span>共1门课程</span></h3>
            <div class="cart-title">
               <el-row>
                 <el-col :span="2">&nbsp;</el-col>
                 <el-col :span="10">课程</el-col>
                 <el-col :span="8">有效期</el-col>
                 <el-col :span="4">价格</el-col>
               </el-row>
            </div>
            <div class="cart-item" v-for="course in order_info.order_courses">
              <el-row>
                 <el-col :span="2" class="checkbox">&nbsp;&nbsp;</el-col>
                 <el-col :span="10" class="course-info">
                   <img style="float: left;" :src="$settings.Host+course.course_img" alt="">
                   <span class="course_name">
                     <span>{{course.course_name}}</span><br>
                     <span class="discount_name">{{course.discount_name}}</span>
                   </span>
                 </el-col>
                 <el-col :span="8" class="lh"><span>{{course.expire_text}}</span></el-col>
                 <el-col :span="4">
                   <div class="course-price">
                      <p class="real_price">¥{{course.real_price}}</p>
                      <span class="original_price">原价: ¥{{course.price}}</span>
                   </div>
                 </el-col>
               </el-row>
            </div>
            <div>
            <div class="coupon">
              <div id="accordion">
                <div class="coupon-box">
                  <div class="coupon-title">
                    <span class="select-coupon">使用优惠劵:</span>
                    <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" style=" 20px; height: 20px" class="collapsed" aria-expanded="false">
                      <img class="sign" src="../../static/img/12.png" width="20" height="20" alt=""></a>
                      <span class="coupon-num">有0张可用</span>
                  </div>
                  <p class="sum-price-wrap" style="margin-right: 45px">商品总金额:<span class="sum-price">{{order_info.total_price}}元</span></p>
                </div>
                <div style="text-align: left;" id="collapseOne" class="panel-collapse out collapse" aria-expanded="false">
                  <ul class="coupon-list" v-if="coupon_list.length>0">
                    <li @click="use_coupon=item.id" v-for="item in coupon_list" class="coupon-item" :class="use_coupon==item.id?'coupon_selected':''">
                      <span>{{item.coupon.name}}</span>
                      <span v-if="item.coupon.type==1">¥{{item.coupon.sale}}元</span>
                      <span style="font-size: 12px;">开始使用时间: {{new Date(item.start_time).toLocaleString()}}</span>
                    </li>
                  </ul>
                  <div v-else style="text-align: center;  100%; padding: 50px 0px; align-items: center; justify-content: center; border-bottom: 1px solid rgb(232, 232, 232);">
                    <span style="font-size: 16px; color: #9b9b9b">暂无可用优惠券</span>
                  </div>
                </div>
              </div>
              <div style="height: 30px; margin-top: 40px; display: flex; align-items: center; justify-content: flex-end">
                <input type="checkbox" class="ok" id="color-input-red">
                <label for="color-input-red"><img src="" alt=""></label>
                <p class="discount-num" style="color:#9B9B9B">使用我的贝里</p>
                <p class="discount-num" style="margin-right: 45px">
                  <span style="display: none;">可用0个已抵扣 ¥0</span>
                </p>
              </div>
              <p class="sun-coupon-num" style="margin-right: 45px;margin-bottom:43px">优惠券抵扣:<span>0元</span></p>
            </div>
            </div>
            <div class="calc">
                <el-row class="pay-row">
                  <el-col :span="4" class="pay-col"><span class="pay-text">支付方式:</span></el-col>
                  <el-col :span="8">
                    <span class="alipay"><img src="../../static/img/alipay2.png" alt=""></span>
                    <span class="alipay wechat"><img src="../../static/img/wechat.png" alt=""></span>
                  </el-col>
                  <el-col :span="8" class="count">实付款: <span>¥{{order_info.real_price}}</span></el-col>
                  <el-col :span="4" class="cart-pay"><span @click="payhander">支付宝支付</span></el-col>
                </el-row>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default {
        name:"Order",
        data(){
          return {
            use_coupon: 0,
            order_info:{},
            coupon_list:[], // 用于展示优惠券列表
            coupon_list2:[],// 用于勾选优惠券功能
          }
        },
        components:{
          Header,
          Footer,
        },
        watch:{
          use_coupon(coupon_id){
            let data = this.coupon_list2[ coupon_id ];
            let sale = parseFloat( data.coupon.sale.slice(1) );
            if( data.coupon.coupon_type == 0 ){
              // 折扣优惠券
              this.order_info.real_price = this.order_info.total_price * sale;
            }else if(data.coupon.coupon_type == 1){
              // 减免优惠券
              this.order_info.real_price = this.order_info.total_price - sale;
            }
            this.order_info.real_price = this.order_info.real_price.toFixed(2);
    
            // 发送数据到后端,进行同步
          }
        },
        created(){
          // 判断用户是否已经登录
          let token = sessionStorage.token || localStorage.token;
          let _this = this;
          if(!token){
            this.$alert("对不起,您尚未登录!请登录!","警告",{
              callback(){
                _this.$router.push("/login");
              }
            })
          }
          // 获取地址栏上面的订单号
          let order_number = this.$route.params.order;
    
          // 发送请求获取数据
          this.$axios.get(this.$settings.Host+`/orders/${order_number}/`,{
              headers:{
                // 注意下方的空格!!!
                "Authorization":"jwt " + token,
              },
          }).then(response=>{
            this.order_info = response.data;
          }).catch(error=>{
            console.log(error.response);
          });
    
          // 获取用户的优惠券
          this.get_coupon_list()
        },
        methods: {
          get_coupon_list(){
            let user_id = localStorage.user_id || sessionStorage.user_id;
            let token = localStorage.token || sessionStorage.token;
            // 获取当前用户的优惠券
            this.$axios.get(this.$settings.Host+"/coupon/list/",{
              params:{
                user_id,
              },
              headers:{
                // 注意下方的空格!!!
                "Authorization":"jwt " + token,
              },
            }).then(response=>{
              // 调整获取到优惠券列表,以优惠券ID作为下标
              let data_list = [];
              response.data.forEach(row=>{
                data_list[row.id] = row;
              });
              this.coupon_list2= data_list;
              this.coupon_list = response.data;
            }).catch(error=>{
              console.log(error.response);
            })
          },
          payhander(){
    
          },
    
        }
      }
    </script>
    
    <style scoped>
    .cart{
      margin-top: 80px;
    }
    .cart-info{
      overflow: hidden;
       1200px;
      margin: auto;
    }
    .cart-top{
      font-size: 18px;
      color: #666;
      margin: 25px 0;
      font-weight: normal;
    }
    .cart-top span{
        font-size: 12px;
        color: #d0d0d0;
        display: inline-block;
    }
    .cart-title{
        background: #F7F7F7;
        height: 70px;
    }
    .calc{
      margin-top: 25px;
      margin-bottom: 40px;
    }
    
    .calc .count{
      text-align: right;
      margin-right: 10px;
      vertical-align: middle;
    }
    .calc .count span{
        font-size: 36px;
        color: #333;
    }
    .calc .cart-pay{
        margin-top: 5px;
         110px;
        height: 38px;
        outline: none;
        border: none;
        color: #fff;
        line-height: 38px;
        background: #ffc210;
        border-radius: 4px;
        font-size: 16px;
        text-align: center;
        cursor: pointer;
    }
    .cart-item{
      height: 120px;
      /*line-height: 120px;*/
    }
    .cart-item .lh{
      line-height: 120px;
    }
    .course-info img{
         175px;
        height: 115px;
        margin-right: 35px;
        vertical-align: middle;
    }
    .course-price{
      margin-top: 40px;
    }
    .alipay{
      display: inline-block;
      height: 48px;
    }
    .alipay img{
      height: 100%;
      auto;
    }
    
    .pay-text{
      display: block;
      text-align: right;
      height: 100%;
      line-height: 100%;
      vertical-align: middle;
      margin-top: 20px;
    }
    .real_price{
        color: #333;
        margin-bottom: 10px;
    }
    .original_price{
        color: #9b9b9b;
        letter-spacing: .36px;
        text-decoration: line-through;
    }
    .coupon{
      margin-top: 30px;
    }
    .coupon-box{
      text-align: left;
      display: flex;
      padding-bottom: 22px;
      padding-left:30px;
      border-bottom: 1px solid #e8e8e8;
    }
    .coupon-title{
      display: flex;
    }
    .sum-price-wrap{
        display: inline-block;
        margin-left: auto;
        font-size: 16px;
        color: #4a4a4a;
    }
    .discount_name{
        color: #ffc210;
        margin-top: 24px;
        font-size: 14px;
        letter-spacing: .32px;
    }
    .course_name{
      margin-top: 40px;
      display: block;
    }
    .coupon-list{
      overflow: hidden;
    }
    .coupon-item{
      float: left;
      margin-left: 10px;
      margin-right: 10px;
       200px;
      height: 60px;
      border: 1px solid #000;
      padding: 10px;
    }
    .coupon-item span{
      display: block;
      font-size: 14px;
    }
    .coupon_selected{
      border-color: red;
      color: indianred;
    }
    </style>
    

    发起支付-支付宝

    支付宝开发平台登录

    https://open.alipay.com/platform/home.htm

    1554189376336

    1554189403316

    1554189677144

    沙箱环境

    真实的支付宝网关:   https://openapi.alipay.com/gateway.do
    	
    沙箱的支付宝网关:   https://openapi.alipaydev.com/gateway.do
    

    支付宝开发者文档

    电脑网站支付流程

    1558585884855

    开发支付功能

    cd luffy/apps
    python ../../manage.py startapp payments
    

    注册子应用

    INSTALLED_APPS = [
    	。。。。
        'payments',
    ]
    

    配置秘钥

    1. 生成应用的私钥和公钥

    下载对应系统的秘钥生成工具: https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1

    1554192077808

    应用公钥复制粘贴到支付宝网站页面中.

    1554192111270

    1554192122648

    点击修改以后,粘贴进去

    1554192143494

    2. 保存应用私钥文件

    在payments应用中新建keys目录,用来保存秘钥文件。

    将应用私钥文件app_private_key.pem复制到payment/keys目录下。

    -----BEGIN RSA PRIVATE KEY-----
    私钥
    -----END RSA PRIVATE KEY-----
    

    3. 保存支付宝公钥

    在payment/keys目录下新建alipay_public_key.pem文件,用于保存支付宝的公钥文件。

    将支付宝的公钥内容复制到alipay_public_key.pem文件中

    支付宝公钥

    -----BEGIN PUBLIC KEY-----
    公钥
    -----END PUBLIC KEY-----
    

    1554200221417

    4. 使用支付宝的sdk开发支付接口

    SDK:https://docs.open.alipay.com/270/106291/

    python版本的支付宝SDK文档:https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

    安装命令:

    pip install python-alipay-sdk --upgrade
    

    后端提供发起支付的接口url地址

    from rest_framework.views import APIView
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.response import Response
    from rest_framework import status
    from orders.models import Order
    from coupon.models import UserCoupon
    from alipay import AliPay
    from django.conf import settings
    import os
    from django.db import transaction
    from decimal import Decimal
    
    class AlipayAPIView(APIView):
        # permission_classes = [IsAuthenticated]
    
        def get(self,request):
            """生成支付宝支付地址"""
            # 接受参数[优惠券,订单号]
            coupon_id = request.query_params.get("coupon_id")
            order_number  = request.query_params.get("order_number")
            try:
                order = Order.objects.get(order_number=order_number)
            except Order.DoesNotExist:
                return Response({"message":"对不起,当前订单信息不存在!无法进行支付"},status=status.HTTP_400_BAD_REQUEST)
    
            if coupon_id != None and coupon_id != "0":
                with transaction.atomic():
                    save_id = transaction.savepoint()
                    # 重新计算订单实际支付价格
                    try:
                        user_coupon = UserCoupon.objects.get(pk=coupon_id)
                    except UserCoupon.DoesNotExist:
                        return Response({"message": "对不起,当前订单使用的优惠券不存在!无法进行支付"}, status=status.HTTP_400_BAD_REQUEST)
    
                    if user_coupon.coupon.coupon_type == 0:
                        """折扣优惠"""
                        order.real_price = order.total_price * Decimal(user_coupon.coupon.sale[1:])
                    elif user_coupon.coupon.coupon_type == 1:
                        order.real_price = order.total_price - Decimal(user_coupon.coupon.sale[1:])
                    else:
                        return Response({"message": "当前优惠券无法使用!无法进行支付"}, status=status.HTTP_400_BAD_REQUEST)
    
                    try:
                        # 经过上面的计算,保存实付价格和使用的优惠券
                        order.use_coupon = True
                        order.coupon = user_coupon.id
                        order.save()
    
                        # 上面的优惠券已经被使用了,所以我们需要修改优惠券的状态
                        user_coupon.is_use = True
                        user_coupon.save()
    
                    except:
                        transaction.savepoint_rollback(save_id)
                        return Response({"message": "系统异常,无法进行支付"}, status=status.HTTP_400_BAD_REQUEST)
    
            # 构造支付宝支付链接地址
            alipay = AliPay(
                appid=settings.ALIPAY_APPID,
                app_notify_url=settings.APP_NOTIFY_URL,  # 默认回调url
                app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
                # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"),
                sign_type="RSA2",  # RSA 或者 RSA2
                debug=settings.ALIPAY_DEBUG
            )
    
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=order.order_number,
                total_amount= "%.2f" % order.real_price,
                subject=order.order_title,
                return_url=settings.ALIPAY_RETURN_URL,
                notify_url=settings.ALIPAY_NOTIFY_URL,
            )
    
            url = settings.APIPAY_GATEWAY + "?" + order_string
    
            return Response({"message":"发起支付成功","url":url})
    

    在配置文件中编辑支付宝的配置信息[实际的值根据自己的账号而定]

    # 支付宝
    ALIPAY_APP_ID="2016091600523592" # 应用ID
    APLIPAY_APP_NOTIFY_URL = None      # 应用回调地址[支付成功以后,支付宝返回结果到哪一个地址下面]
    ALIPAY_DEBUG = True
    # APIPAY_GATEWAY="https://openapi.alipay.com/gateway.do"
    APIPAY_GATEWAY="https://openapi.alipaydev.com/gateway.do"
    ALIPAY_RETURN_URL = "http://127.0.0.1:8080/success"
    ALIPAY_NOTIFY_URL = "http://127.0.0.1:8080/success"
    

    前端点击"支付宝支付",请求后端的发起支付api

    <template>
      <div class="cart">
        <Header/>
        <div class="cart-info">
            <h3 class="cart-top">购物车结算 <span>共1门课程</span></h3>
            <div class="cart-title">
               <el-row>
                 <el-col :span="2">&nbsp;</el-col>
                 <el-col :span="10">课程</el-col>
                 <el-col :span="8">有效期</el-col>
                 <el-col :span="4">价格</el-col>
               </el-row>
            </div>
            <div class="cart-item" v-for="course in order_info.order_courses">
              <el-row>
                 <el-col :span="2" class="checkbox">&nbsp;&nbsp;</el-col>
                 <el-col :span="10" class="course-info">
                   <img style="float: left;" :src="$settings.Host+course.course_img" alt="">
                   <span class="course_name">
                     <span>{{course.course_name}}</span><br>
                     <span class="discount_name">{{course.discount_name}}</span>
                   </span>
                 </el-col>
                 <el-col :span="8" class="lh"><span>{{course.expire_text}}</span></el-col>
                 <el-col :span="4">
                   <div class="course-price">
                      <p class="real_price">¥{{course.real_price}}</p>
                      <span class="original_price">原价: ¥{{course.price}}</span>
                   </div>
                 </el-col>
               </el-row>
            </div>
            <div>
            <div class="coupon">
              <div id="accordion">
                <div class="coupon-box">
                  <div class="coupon-title">
                    <span class="select-coupon">使用优惠劵:</span>
                    <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" style=" 20px; height: 20px" class="collapsed" aria-expanded="false">
                      <img class="sign" src="../../static/img/12.png" width="20" height="20" alt=""></a>
                      <span class="coupon-num">有0张可用</span>
                  </div>
                  <p class="sum-price-wrap" style="margin-right: 45px">商品总金额:<span class="sum-price">{{order_info.total_price}}元</span></p>
                </div>
                <div style="text-align: left;" id="collapseOne" class="panel-collapse out collapse" aria-expanded="false">
                  <ul class="coupon-list" v-if="coupon_list.length>0">
                    <li @click="use_coupon=item.id" v-for="item in coupon_list" class="coupon-item" :class="use_coupon==item.id?'coupon_selected':''">
                      <span>{{item.coupon.name}}</span>
                      <span v-if="item.coupon.type==1">¥{{item.coupon.sale}}元</span>
                      <span style="font-size: 12px;">开始使用时间: {{new Date(item.start_time).toLocaleString()}}</span>
                    </li>
                  </ul>
                  <div v-else style="text-align: center;  100%; padding: 50px 0px; align-items: center; justify-content: center; border-bottom: 1px solid rgb(232, 232, 232);">
                    <span style="font-size: 16px; color: #9b9b9b">暂无可用优惠券</span>
                  </div>
                </div>
              </div>
              <div style="height: 30px; margin-top: 40px; display: flex; align-items: center; justify-content: flex-end">
                <input type="checkbox" class="ok" id="color-input-red">
                <label for="color-input-red"><img src="" alt=""></label>
                <p class="discount-num" style="color:#9B9B9B">使用我的贝里</p>
                <p class="discount-num" style="margin-right: 45px">
                  <span style="display: none;">可用0个已抵扣 ¥0</span>
                </p>
              </div>
              <p class="sun-coupon-num" style="margin-right: 45px;margin-bottom:43px">优惠券抵扣:<span>0元</span></p>
            </div>
            </div>
            <div class="calc">
                <el-row class="pay-row">
                  <el-col :span="4" class="pay-col"><span class="pay-text">支付方式:</span></el-col>
                  <el-col :span="8">
                    <span class="alipay"><img src="../../static/img/alipay2.png" alt=""></span>
                    <span class="alipay wechat"><img src="../../static/img/wechat.png" alt=""></span>
                  </el-col>
                  <el-col :span="8" class="count">实付款: <span>¥{{order_info.real_price}}</span></el-col>
                  <el-col :span="4" class="cart-pay"><span @click="payhander">支付宝支付</span></el-col>
                </el-row>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default {
        name:"Order",
        data(){
          return {
            use_coupon: 0,  // 使用的优惠券ID
            order_info:{},
            coupon_list:[], // 用于展示优惠券列表
            coupon_list2:[],// 用于勾选优惠券功能
          }
        },
        components:{
          Header,
          Footer,
        },
        watch:{
          use_coupon(coupon_id){
            let data = this.coupon_list2[ coupon_id ];
            let sale = parseFloat( data.coupon.sale.slice(1) );
            if( data.coupon.coupon_type == 0 ){
              // 折扣优惠券
              this.order_info.real_price = this.order_info.total_price * sale;
            }else if(data.coupon.coupon_type == 1){
              // 减免优惠券
              this.order_info.real_price = this.order_info.total_price - sale;
            }
            this.order_info.real_price = this.order_info.real_price.toFixed(2);
    
            // 发送数据到后端,进行同步
          }
        },
        created(){
          // 判断用户是否已经登录
          let token = sessionStorage.token || localStorage.token;
          let _this = this;
          if(!token){
            this.$alert("对不起,您尚未登录!请登录!","警告",{
              callback(){
                _this.$router.push("/login");
              }
            })
          }
          // 获取地址栏上面的订单号
          let order_number = this.$route.params.order;
    
          // 发送请求获取数据
          this.$axios.get(this.$settings.Host+`/orders/${order_number}/`,{
              headers:{
                // 注意下方的空格!!!
                "Authorization":"jwt " + token,
              },
          }).then(response=>{
            this.order_info = response.data;
          }).catch(error=>{
            console.log(error.response);
          });
    
          // 获取用户的优惠券
          this.get_coupon_list()
        },
        methods: {
          get_coupon_list(){
            let user_id = localStorage.user_id || sessionStorage.user_id;
            let token = localStorage.token || sessionStorage.token;
            // 获取当前用户的优惠券
            this.$axios.get(this.$settings.Host+"/coupon/list/",{
              params:{
                user_id,
              },
              headers:{
                // 注意下方的空格!!!
                "Authorization":"jwt " + token,
              },
            }).then(response=>{
              // 调整获取到优惠券列表,以优惠券ID作为下标
              let data_list = [];
              response.data.forEach(row=>{
                data_list[row.id] = row;
              });
              this.coupon_list2= data_list;
              this.coupon_list = response.data;
            }).catch(error=>{
              console.log(error.response);
            })
          },
          payhander(){
    
            this.$confirm("您即将跳转到支付宝页面进行订单支付?","提示").then(()=>{
                let token = localStorage.token || sessionStorage.token;
                // 获取订单号
                let order_number = this.$route.params.order;
                // 发起支付请求
                this.$axios.get(this.$settings.Host+"/payments/alipay/url/",{
                  params:{
                    order_number: order_number,
                    coupon_id: this.use_coupon,
                  },
                  headers:{
                    // 注意下方的空格!!!
                    "Authorization":"jwt " + token,
                  },
                }).then(response=>{
                  let url = response.data.url;
                  console.log(url);
                  // 页面跳转
                  location.assign( url );
                }).catch(error=>{
                  console.log(error.response)
                })
    
            });
    
    
          },
    
        }
      }
    </script>
    
    <style scoped>
    .cart{
      margin-top: 80px;
    }
    .cart-info{
      overflow: hidden;
       1200px;
      margin: auto;
    }
    .cart-top{
      font-size: 18px;
      color: #666;
      margin: 25px 0;
      font-weight: normal;
    }
    .cart-top span{
        font-size: 12px;
        color: #d0d0d0;
        display: inline-block;
    }
    .cart-title{
        background: #F7F7F7;
        height: 70px;
    }
    .calc{
      margin-top: 25px;
      margin-bottom: 40px;
    }
    
    .calc .count{
      text-align: right;
      margin-right: 10px;
      vertical-align: middle;
    }
    .calc .count span{
        font-size: 36px;
        color: #333;
    }
    .calc .cart-pay{
        margin-top: 5px;
         110px;
        height: 38px;
        outline: none;
        border: none;
        color: #fff;
        line-height: 38px;
        background: #ffc210;
        border-radius: 4px;
        font-size: 16px;
        text-align: center;
        cursor: pointer;
    }
    .cart-item{
      height: 120px;
      /*line-height: 120px;*/
    }
    .cart-item .lh{
      line-height: 120px;
    }
    .course-info img{
         175px;
        height: 115px;
        margin-right: 35px;
        vertical-align: middle;
    }
    .course-price{
      margin-top: 40px;
    }
    .alipay{
      display: inline-block;
      height: 48px;
    }
    .alipay img{
      height: 100%;
      auto;
    }
    
    .pay-text{
      display: block;
      text-align: right;
      height: 100%;
      line-height: 100%;
      vertical-align: middle;
      margin-top: 20px;
    }
    .real_price{
        color: #333;
        margin-bottom: 10px;
    }
    .original_price{
        color: #9b9b9b;
        letter-spacing: .36px;
        text-decoration: line-through;
    }
    .coupon{
      margin-top: 30px;
    }
    .coupon-box{
      text-align: left;
      display: flex;
      padding-bottom: 22px;
      padding-left:30px;
      border-bottom: 1px solid #e8e8e8;
    }
    .coupon-title{
      display: flex;
    }
    .sum-price-wrap{
        display: inline-block;
        margin-left: auto;
        font-size: 16px;
        color: #4a4a4a;
    }
    .discount_name{
        color: #ffc210;
        margin-top: 24px;
        font-size: 14px;
        letter-spacing: .32px;
    }
    .course_name{
      margin-top: 40px;
      display: block;
    }
    .coupon-list{
      overflow: hidden;
    }
    .coupon-item{
      float: left;
      margin-left: 10px;
      margin-right: 10px;
       200px;
      height: 60px;
      border: 1px solid #000;
      padding: 10px;
    }
    .coupon-item span{
      display: block;
      font-size: 14px;
    }
    .coupon_selected{
      border-color: red;
      color: indianred;
    }
    </style>
    

    支付成功的模板

    <template>
      <div class="success">
        <Header :current_page="current_page"/>
        <div class="main">
            <div class="title">
              <img src="../../static/images/right.svg" alt="">
              <div class="success-tips">
                  <p class="tips1">您已成功购买 1 门课程!</p>
                  <p class="tips2">你还可以加入QQ群 <span>747556033</span> 学习交流</p>
              </div>
            </div>
            <div class="order-info">
                <p class="info1"><b>付款时间:</b><span>2019/04/02 10:27</span></p>
                <p class="info2"><b>付款金额:</b><span >0</span></p>
                <p class="info3"><b>课程信息:</b><span><span>《Pycharm使用秘籍》</span></span></p>
            </div>
            <div class="wechat-code">
              <img src="../../static/images/server.cf99f78.png" alt="" class="er">
              <p><img src="../../static/images/tan.svg" alt="">重要!微信扫码关注获得学习通知&amp;课程更新提醒!否则将严重影响学习进度和课程体验!</p>
            </div>
            <div class="study">
              <span>立即学习</span>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default{
        name:"Success",
        data(){
          return {
            current_page:0,
          };
        },
        components:{
          Header,
          Footer,
        }
      }
    </script>
    
    <style scoped>
    .success{
      padding-top: 80px;
    }
    .main{
        height: 100%;
        padding-top: 25px;
        padding-bottom: 25px;
        margin: 0 auto;
         1200px;
        background: #fff;
    }
    .main .title{
        display: flex;
        -ms-flex-align: center;
        align-items: center;
        padding: 25px 40px;
        border-bottom: 1px solid #f2f2f2;
    }
    .main .title .success-tips{
        box-sizing: border-box;
    }
    .title img{
        vertical-align: middle;
         60px;
        height: 60px;
        margin-right: 40px;
    }
    .title .success-tips{
        box-sizing: border-box;
    }
    .title .tips1{
        font-size: 22px;
        color: #000;
    }
    .title .tips2{
        font-size: 16px;
        color: #4a4a4a;
        letter-spacing: 0;
        text-align: center;
        margin-top: 10px;
    }
    .title .tips2 span{
        color: #ec6730;
    }
    .order-info{
        padding: 25px 48px;
        padding-bottom: 15px;
        border-bottom: 1px solid #f2f2f2;
    }
    .order-info p{
      font-family: PingFangSC-Regular;
        display: -ms-flexbox;
        display: flex;
        margin-bottom: 10px;
        font-size: 16px;
    }
    .order-info p b{
      font-weight: 400;
      color: #9d9d9d;
      white-space: nowrap;
    }
    .wechat-code{
        display: flex;
        -ms-flex-align: center;
        align-items: center;
        padding: 25px 40px;
        border-bottom: 1px solid #f2f2f2;
    }
    .wechat-code>img{
         100px;
        height: 100px;
        margin-right: 15px;
    }
    .wechat-code p{
        font-family: PingFangSC-Regular;
        font-size: 14px;
        color: #d0021b;
        display: -ms-flexbox;
        display: flex;
        -ms-flex-align: center;
        align-items: center;
    }
    .wechat-code p>img{
         16px;
        height: 16px;
        margin-right: 10px;
    }
    .study{
          padding: 25px 40px;
    }
    .study span{
      display: block;
       140px;
      height: 42px;
      text-align: center;
      line-height: 42px;
      cursor: pointer;
      background: #ffc210;
      border-radius: 6px;
      font-family: PingFangSC-Regular;
      font-size: 16px;
      color: #fff;
    }
    </style>
    
    
    

    后端接受支付结果

    支付宝会返回的参数如下列表:

    http://www.luffycity.cn:8080?
    charset=utf-8&
    out_trade_no=2019040217080000000010976&
    method=alipay.trade.page.pay.return&
    total_amount=1206.44&
    sign=XKJG5826fH%2F9%2B3jCWw2ODjlc%2FuGLfqmr5RnimSAqrh%2B5bFkWcbLDh5V6VYtMqCpwnYp3FuGPqEeUeRO6WK62Qz0Q5nQGOA394IdxPfTOzry7PXuwYf41PCbDq53yg7vCYrobz4Tt8uajeADJLJwIsL%2F%2B88vbDEISUDUujL4442kl3oLh3EDD8DxZc2LLsv1Z%2FEFGJMfcTA47A4T7qmjB%2BbLKJetZZBISdt9RDL0q8A%2BAfb8B3Ux1nq%2F0EiNGiwIlWC1pvUCHK2UXMJW3kmgU9P9Zoujrj4ER28oieQt6Rt4gQXeah5uYtAMkftWfZpiyu%2FjUkr6iRx%2B4mP5IFz4Uew%3D%3D&
    trade_no=2019040222001439881000005802&
    auth_app_id=2016091600523592&
    version=1.0&
    app_id=2016091600523592&
    sign_type=RSA2&
    seller_id=2088102175868026&
    timestamp=2019-04-02%2017%3A13%3A15
    

    后端完成 支付宝支付结果的处理并更新订单和购买记录

    users/models.py,模型代码:

    from django.contrib.auth.models import AbstractUser
    from django.db import models
    
    class User(AbstractUser):
        """用户模型类"""
        mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')
    
        class Meta:
            db_table = 'ly_users'
            verbose_name = '用户'
            verbose_name_plural = verbose_name
    
    
    from luffy.utils.models import BaseModel
    from courses.models import Course
    class UserCourse(BaseModel):
        pay_choices = (
            (0, '支付宝'),
            (1, '微信支付'),
            (2, '免费活动'),
            (3, '活动赠品'),
            (4, '系统赠送'),
        )
        user = models.ForeignKey(User, related_name='user_courses', on_delete=models.DO_NOTHING,verbose_name="用户")
        course = models.ForeignKey(Course, related_name='course_users', on_delete=models.DO_NOTHING, verbose_name="课程")
        buy_number = models.CharField(max_length=128, null=True, verbose_name="账单号")
        buy_type = models.SmallIntegerField(choices=pay_choices, default=0, verbose_name="购买方式")
        pay_time = models.DateTimeField(null=True, verbose_name="购买时间")
        out_time = models.DateTimeField(null=True, verbose_name="过期时间")
    
        class Meta:
            db_table = 'ly_user_course'
            verbose_name = '课程购买记录'
            verbose_name_plural = verbose_name
    

    payments/views.py,视图代码:

    from rest_framework.views import APIView
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.response import Response
    from rest_framework import status
    from orders.models import Order
    from coupon.models import UserCoupon
    from alipay import AliPay
    from django.conf import settings
    import os
    from django.db import transaction
    from decimal import Decimal
    import logging
    log = logging.getLogger("django")
    from datetime import datetime
    from users.models import UserCourse
    
    class AlipayAPIView(APIView):
        # permission_classes = [IsAuthenticated]
    
        def get(self,request):
            """生成支付宝支付地址"""
            # 接受参数[优惠券,订单号]
            coupon_id = request.query_params.get("coupon_id")
            order_number  = request.query_params.get("order_number")
            try:
                order = Order.objects.get(order_number=order_number)
            except Order.DoesNotExist:
                return Response({"message":"对不起,当前订单信息不存在!无法进行支付"},status=status.HTTP_400_BAD_REQUEST)
    
            if coupon_id != None and coupon_id != "0":
                with transaction.atomic():
                    save_id = transaction.savepoint()
                    # 重新计算订单实际支付价格
                    try:
                        user_coupon = UserCoupon.objects.get(pk=coupon_id)
                    except UserCoupon.DoesNotExist:
                        return Response({"message": "对不起,当前订单使用的优惠券不存在!无法进行支付"}, status=status.HTTP_400_BAD_REQUEST)
    
                    if user_coupon.coupon.coupon_type == 0:
                        """折扣优惠"""
                        order.real_price = order.total_price * Decimal(user_coupon.coupon.sale[1:])
                    elif user_coupon.coupon.coupon_type == 1:
                        order.real_price = order.total_price - Decimal(user_coupon.coupon.sale[1:])
                    else:
                        return Response({"message": "当前优惠券无法使用!无法进行支付"}, status=status.HTTP_400_BAD_REQUEST)
    
                    try:
                        # 经过上面的计算,保存实付价格和使用的优惠券
                        order.use_coupon = True
                        order.coupon = user_coupon.id
                        order.save()
    
                        # 上面的优惠券已经被使用了,所以我们需要修改优惠券的状态
                        user_coupon.is_use = True
                        user_coupon.save()
    
                    except:
                        transaction.savepoint_rollback(save_id)
                        return Response({"message": "系统异常,无法进行支付"}, status=status.HTTP_400_BAD_REQUEST)
    
            # 构造支付宝支付链接地址
            alipay = AliPay(
                appid=settings.ALIPAY_APPID,
                app_notify_url=settings.APP_NOTIFY_URL,  # 默认回调url
                app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
                # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"),
                sign_type="RSA2",  # RSA 或者 RSA2
                debug=settings.ALIPAY_DEBUG
            )
    
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=order.order_number,
                total_amount= "%.2f" % order.real_price,
                subject=order.order_title,
                return_url=settings.ALIPAY_RETURN_URL,
                notify_url=settings.ALIPAY_NOTIFY_URL,
            )
    
            url = settings.APIPAY_GATEWAY + "?" + order_string
    
            return Response({"message":"发起支付成功","url":url})
    
    
    class AlipayResult(APIView):
        def get(self,request):
            # 获取支付结果的所有参数,并转换成字典
            data = request.query_params.dict()
            # 在字典中移除sign签名
            signature = data.pop("sign")
    
            alipay = AliPay(
                appid=settings.ALIPAY_APPID,
                app_notify_url=settings.APP_NOTIFY_URL,  # 默认回调url
                app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
                # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"),
                sign_type="RSA2",  # RSA 或者 RSA2
                debug=settings.ALIPAY_DEBUG
            )
    
            success = alipay.verify(data, signature)
            if success:
                # 支付成功!
                # 更新订单
                order_number = data.get("out_trade_no")
                try:
                    order = Order.objects.get( order_number=order_number )
                except Order.DoesNotExist:
                    log.error("订单号:%s不存在!" % order_number )
                    return Response({"message": "无效的订单号"},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    
                with transaction.atomic():
                    save_id = transaction.savepoint()
                    try:
                        order.order_status = 1
                        order.pay_time = datetime.now()
                        # order.pay_time = data.get("timestamp")
                        order.save()
    
                        # 课程与用户之间添加一条购买记录
                        detail_list = order.order_courses.all()
                        course_list = []
                        for detail in detail_list:
    
                            if detail.expire=='-1':
                                out_time = "2099-01-01 00:00:00"
                            else:
                                out_time = datetime.now().timestamp() + detail.expire * 86400
                                # 日期时间对象 = fromtimestamp(数值时间戳)
                                out_time = datetime.fromtimestamp(out_time)
                                out_time = out_time.strftime("%Y-%m-%d %H:%M:%S")
    
                            UserCourse.objects.create(
                                user=order.user,
                                course=detail.course,
                                buy_number=data.get("trade_no"),
                                buy_type=0,
                                pay_time=data.get("timestamp"),
                                out_time=out_time
                            )
    
                            course_list.append(detail.course.name)
    
                        return Response({"message":{
                            "pay_time": order.pay_time.strftime("%Y-%m-%d %H:%M:%S"),
                            "real_price": order.real_price,
                            "course_list":course_list,
                        }})
    
                    except:
                        log.error("修改订单和购买记录发生异常!")
                        transaction.savepoint_rollback(save_id)
                        return Response({"message":"系统异常!"},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    

    payments/urls.py,路由代码:

    from django.urls import path,re_path
    from . import views
    urlpatterns = [
        path("alipay/url/",views.AlipayAPIView.as_view() ),
        path("alipay/result/",views.AlipayResult.as_view() ),
    ]
    

    前端把地址栏上面返回的同步支付结果转发给服务端

    Success.vue

    <template>
      <div class="success">
        <Header/>
        <div class="main">
            <div class="title">
    <!--          <img src="../../static/images/right.svg" alt="">-->
              <div class="success-tips">
                  <p class="tips1">您已成功购买 {{order_info.course_list.length}} 门课程!</p>
                  <p class="tips2">你还可以加入QQ群 <span>747556033</span> 学习交流</p>
              </div>
            </div>
            <div class="order-info">
                <p class="info1"><b>付款时间:</b><span>{{order_info.pay_time}}</span></p>
                <p class="info2"><b>付款金额:</b><span >¥{{order_info.real_price}}元</span></p>
                <p class="info3"><b>课程信息:</b><span><span>{{order_info.course_list2}}</span></span></p>
            </div>
            <div class="wechat-code">
    <!--          <img src="../../static/images/server.cf99f78.png" alt="" class="er">-->
    <!--          <p><img src="../../static/images/tan.svg" alt="">重要!微信扫码关注获得学习通知&amp;课程更新提醒!否则将严重影响学习进度和课程体验!</p>-->
            </div>
            <div class="study">
              <span>立即学习</span>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default{
        name:"Success",
        data(){
          return {
            order_info:{
              course_list:[]
            }
          };
        },
        created(){
            let token = localStorage.token || sessionStorage.token;
    
            if(!token){
              this.$alert("对不起,您尚未登录!请登录!","警告",{
                callback(){
                  _this.$router.push("/login");
                }
              })
            }
    
            // 转发支付结果到后端
            this.$axios.get(this.$settings.Host+"/payments/alipay/result/"+location.search,{
              headers:{
                // 注意下方的空格!!!
                "Authorization":"jwt " + token,
              },
            }).then(response=>{
              this.order_info = response.data.message;
              this.order_info.course_list2 = "";
              this.order_info.course_list.forEach(course=>{
                this.order_info.course_list2+=`《${course}》`;
              })
            }).catch(error=>{
    
              console.log(error.response);
            })
        },
        components:{
          Header,
          Footer,
        }
      }
    </script>
    
    <style scoped>
    .success{
      padding-top: 80px;
    }
    .main{
        height: 100%;
        padding-top: 25px;
        padding-bottom: 25px;
        margin: 0 auto;
         1200px;
        background: #fff;
    }
    .main .title{
        display: flex;
        -ms-flex-align: center;
        align-items: center;
        padding: 25px 40px;
        border-bottom: 1px solid #f2f2f2;
    }
    .main .title .success-tips{
        box-sizing: border-box;
    }
    .title img{
        vertical-align: middle;
         60px;
        height: 60px;
        margin-right: 40px;
    }
    .title .success-tips{
        box-sizing: border-box;
    }
    .title .tips1{
        font-size: 22px;
        color: #000;
    }
    .title .tips2{
        font-size: 16px;
        color: #4a4a4a;
        letter-spacing: 0;
        text-align: center;
        margin-top: 10px;
    }
    .title .tips2 span{
        color: #ec6730;
    }
    .order-info{
        padding: 25px 48px;
        padding-bottom: 15px;
        border-bottom: 1px solid #f2f2f2;
    }
    .order-info p{
      font-family: PingFangSC-Regular;
        display: -ms-flexbox;
        display: flex;
        margin-bottom: 10px;
        font-size: 16px;
    }
    .order-info p b{
      font-weight: 400;
      color: #9d9d9d;
      white-space: nowrap;
    }
    .wechat-code{
        display: flex;
        -ms-flex-align: center;
        align-items: center;
        padding: 25px 40px;
        border-bottom: 1px solid #f2f2f2;
    }
    .wechat-code>img{
         100px;
        height: 100px;
        margin-right: 15px;
    }
    .wechat-code p{
        font-family: PingFangSC-Regular;
        font-size: 14px;
        color: #d0021b;
        display: -ms-flexbox;
        display: flex;
        -ms-flex-align: center;
        align-items: center;
    }
    .wechat-code p>img{
         16px;
        height: 16px;
        margin-right: 10px;
    }
    .study{
          padding: 25px 40px;
    }
    .study span{
      display: block;
       140px;
      height: 42px;
      text-align: center;
      line-height: 42px;
      cursor: pointer;
      background: #ffc210;
      border-radius: 6px;
      font-family: PingFangSC-Regular;
      font-size: 16px;
      color: #fff;
    }
    </style>
    

    修改订单结果[新增接口接受异步支付结果]

    from rest_framework.views import APIView
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.response import Response
    from rest_framework import status
    from orders.models import Order
    from coupon.models import UserCoupon
    from alipay import AliPay
    from django.conf import settings
    import os
    from django.db import transaction
    from decimal import Decimal
    import logging
    log = logging.getLogger("django")
    from datetime import datetime
    from users.models import UserCourse
    
    class AlipayAPIView(APIView):
        ......
    
    
    class AlipayResult(APIView):
        def get(self,request):
            ......
    
       def post(self,request):
    
            data = request.data.dict()
            # 在字典中移除sign签名
            signature = data.pop("sign")
    
            alipay = AliPay(
                appid=settings.ALIPAY_APPID,
                app_notify_url=settings.APP_NOTIFY_URL,  # 默认回调url
                app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
                # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"),
                sign_type="RSA2",  # RSA 或者 RSA2
                debug=settings.ALIPAY_DEBUG
            )
    
            success = alipay.verify(data, signature)
            if success:
                # 支付成功!
                # 更新订单
                order_number = data.get("out_trade_no")
                try:
                    order = Order.objects.get( order_number=order_number )
                except Order.DoesNotExist:
                    log.error("订单号:%s不存在!" % order_number )
                    return Response({"message": "无效的订单号"},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    
                with transaction.atomic():
                    save_id = transaction.savepoint()
                    try:
                        order.order_status = 1
                        order.pay_time = datetime.now()
                        # order.pay_time = data.get("timestamp")
                        order.save()
    
                        # 课程与用户之间添加一条购买记录
                        detail_list = order.order_courses.all()
                        course_list = []
                        for detail in detail_list:
    
                            if detail.expire=='-1':
                                out_time = "2099-01-01 00:00:00"
                            else:
                                out_time = datetime.now().timestamp() + detail.expire * 86400
                                # 日期时间对象 = fromtimestamp(数值时间戳)
                                out_time = datetime.fromtimestamp(out_time)
                                out_time = out_time.strftime("%Y-%m-%d %H:%M:%S")
    
                            UserCourse.objects.create(
                                user=order.user,
                                course=detail.course,
                                buy_number=data.get("trade_no"),
                                buy_type=0,
                                pay_time=data.get("timestamp"),
                                out_time=out_time
                            )
    
                            course_list.append(detail.course.name)
    
                        return Response("success", content_type="text/html")
    
                    except:
                        log.error("修改订单和购买记录发生异常!")
                        transaction.savepoint_rollback(save_id)
                        return Response({"message":"系统异常!"},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    

    我的订单

    <template>
      <div class="user-order">
        <Header/>
        <div class="main">
            <div class="banner"></div>
              <div class="profile">
                  <div class="profile-info">
                      <div class="avatar"><img class="newImg" width="100%" alt="" src="/static/img/logo@2x.png"></div>
                      <span class="user-name">Mixtea</span>
                      <span class="user-job">深圳市 | 程序员</span>
                  </div>
                  <ul class="my-item">
                      <li>我的账户</li>
                      <li class="active">我的订单</li>
                      <li>个人资料</li>
                      <li>账号安全</li>
                  </ul>
                </div>
              <div class="user-data">
                <ul class="nav">
                  <li class="order-info">订单</li>
                  <li class="course-expire">有效期</li>
                  <li class="course-price">课程价格</li>
                  <li class="real-price">实付金额</li>
                  <li class="order-status">交易状态</li>
                  <li class="order-do">交易操作</li>
                </ul>
                <div class="my-order-item">
                    <div class="user-data-header">
                      <span class="order-time">2019-04-02 10:27:49</span>
                      <span class="order-num">订单号:
                          <span class="my-older-number">20190402102749606</span>
                      </span>
                    </div>
                    <ul class="nav user-data-list">
                  <li class="order-info">
                      <img src="../../static/course/1544059695.jpeg" alt="">
                      <div class="order-info-title">
                        <p class="course-title">Pycharm使用秘籍</p>
                        <p class="price-service">限时免费</p>
                      </div>
                  </li>
                  <li class="course-expire">永久有效</li>
                  <li class="course-price">977.00</li>
                  <li class="real-price">577.00</li>
                  <li class="order-status">交易成功</li>
                  <li class="order-do">
                    <span class="btn btn2">去学习</span>
                  </li>
                </ul>
                </div>
                <div class="my-order-item">
                    <div class="user-data-header">
                      <span class="order-time">2019-04-02 10:27:49</span>
                      <span class="order-num">订单号:
                          <span class="my-older-number">20190402102749606</span>
                      </span>
                    </div>
                    <ul class="nav user-data-list">
                  <li class="order-info">
                      <img src="../../static/course/1544059695.jpeg" alt="">
                      <div class="order-info-title">
                        <p class="course-title">Pycharm使用秘籍</p>
                        <p class="price-service">限时免费</p>
                      </div>
                  </li>
                  <li class="course-expire">永久有效</li>
                  <li class="course-price">977.00</li>
                  <li class="real-price">577.00</li>
                  <li class="order-status">交易成功</li>
                  <li class="order-do">
                    <span class="btn btn2">去学习</span>
                  </li>
                </ul>
                </div>
                <div class="my-order-item">
                    <div class="user-data-header">
                      <span class="order-time">2019-04-02 10:27:49</span>
                      <span class="order-num">订单号:
                          <span class="my-older-number">20190402102749606</span>
                      </span>
                    </div>
                    <ul class="nav user-data-list">
                  <li class="order-info">
                      <img src="../../static/course/1544059695.jpeg" alt="">
                      <div class="order-info-title">
                        <p class="course-title">Pycharm使用秘籍</p>
                        <p class="price-service">限时免费</p>
                      </div>
                  </li>
                  <li class="course-expire">永久有效</li>
                  <li class="course-price">977.00</li>
                  <li class="real-price">577.00</li>
                  <li class="order-status">交易成功</li>
                  <li class="order-do">
                    <span class="btn btn2">去学习</span>
                  </li>
                </ul>
                </div>
                <div class="my-order-item">
                    <div class="user-data-header">
                      <span class="order-time">2019-04-02 10:27:49</span>
                      <span class="order-num">订单号:
                          <span class="my-older-number">20190402102749606</span>
                      </span>
                    </div>
                    <ul class="nav user-data-list">
                  <li class="order-info">
                      <img src="../../static/course/1544059695.jpeg" alt="">
                      <div class="order-info-title">
                        <p class="course-title">Pycharm使用秘籍</p>
                        <p class="price-service">限时免费</p>
                      </div>
                  </li>
                  <li class="course-expire">永久有效</li>
                  <li class="course-price">977.00</li>
                  <li class="real-price">577.00</li>
                  <li class="order-status">交易成功</li>
                  <li class="order-do">
                    <span class="btn btn2">去学习</span>
                  </li>
                </ul>
                </div>
                <div class="my-order-item">
                    <div class="user-data-header">
                      <span class="order-time">2019-04-02 10:27:49</span>
                      <span class="order-num">订单号:
                          <span class="my-older-number">20190402102749606</span>
                      </span>
                    </div>
                    <ul class="nav user-data-list">
                  <li class="order-info">
                      <img src="../../static/course/1544059695.jpeg" alt="">
                      <div class="order-info-title">
                        <p class="course-title">Pycharm使用秘籍</p>
                        <p class="price-service">限时免费</p>
                      </div>
                  </li>
                  <li class="course-expire">永久有效</li>
                  <li class="course-price">977.00</li>
                  <li class="real-price">577.00</li>
                  <li class="order-status">交易成功</li>
                  <li class="order-do">
                    <span class="btn btn2">去学习</span>
                  </li>
                </ul>
                </div>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default{
        name:"UserOrder",
        data(){
          return {
          };
        },
        components:{
          Header,
          Footer,
        }
      }
    </script>
    
    <style scoped>
    .user-order{
      padding-top: 80px;
    }
    .main .banner{
         100%;
        height: 324px;
        background: url(/static/img/my_bkging.0648ebe.png) no-repeat;
        background-size: cover;
        z-index: 1;
    }
    .profile{
         1200px;
        margin: 0 auto;
    }
    .profile-info{
        text-align: center;
        margin-top: -80px;
    }
    .avatar{
         120px;
        height: 120px;
        border-radius: 60px;
        overflow: hidden;
        margin: 0 auto;
    }
    .user-name{
        display: block;
        font-size: 24px;
        color: #4a4a4a;
        margin-top: 14px;
    }
    .user-job{
        display: block;
        font-size: 11px;
        color: #9b9b9b;
     }
    .my-item{
        list-style: none;
        line-height: 1.42857143;
        color: #333;
         474px;
        height: 31px;
        display: -ms-flexbox;
        display: flex;
        cursor: pointer;
        margin: 41px auto 0;
        -ms-flex-pack: justify;
        justify-content: space-between;
    }
    .my-item .active{
        border-bottom: 1px solid #000;
    }
    .user-data{
         1200px;
        height: auto;
        margin: 0 auto;
        padding-top: 30px;
        border-top: 1px solid #e8e8e8;
        margin-bottom: 63px;
    }
    .nav{
         100%;
        height: 60px;
        background: #e9e9e9;
        display: -ms-flexbox;
        display: flex;
        -ms-flex-align: center;
        align-items: center;
    }
    .nav li{
        margin-left: 20px;
        margin-right: 28px;
        height: 60px;
        line-height: 60px;
        list-style: none;
        font-family: PingFangSC-Medium;
        font-size: 13px;
        color: #333;
        border-bottom: 1px solid #e9e9e9;
       160px;
    }
    .nav .order-info{  325px; }
    .nav .course-expire{  60px; }
    .nav .course-price{  130px; }
    .user-data-header{
        display: flex;
        height: 44px;
        color: #4a4a4a;
        font-size: 14px;
        background: #f3f3f3;
        -ms-flex-align: center;
        align-items: center;
        font-family: PingFangSC-Regular;
    }
    .order-time{
        font-size: 12px;
        display: inline-block;
        margin-left: 20px;
    }
    .order-num{
        font-size: 12px;
        display: inline-block;
        margin-left: 29px;
    }
    .user-data-list{
        height: 100%;
        display: flex;
    }
    .user-data-list{
      background: none;
    }
    .user-data-list li{
        height: 60px;
        line-height: 60px;
    }
    .user-data-list .order-info{
        display: flex;
        align-items: center;
        margin-right: 28px;
    }
    .user-data-list .order-info img{
        max- 100px;
        max-height: 75px;
        margin-right: 22px;
    }
    .course-title{
         203px;
        font-size: 13px;
        color: #333;
        line-height: 20px;
        font-family: PingFangSC-Medium;
        margin-top: -10px;
    }
    .order-info-title .price-service{
        line-height: 18px;
    }
    .price-service{
        font-size: 12px;
        color: #fa6240;
        padding: 0 5px;
        border: 1px solid #fa6240;
        border-radius: 4px;
        margin-top: 4px;
        position: absolute;
    }
    .order-info-title{
        margin-top: -10px;
    }
    .user-data-list .course-expire{
        font-size: 12px;
        color: #ff5502;
        font-family: PingFangSC-Medium;
         60px;
    
        text-align: center;
    }
    .btn {
       100px;
      height: 32px;
      font-size: 14px;
      color: #fff;
      background: #ffc210;
      border-radius: 4px;
      border: none;
      outline: none;
      font-family: PingFangSC-Medium;
      transition: all .25s ease;
      display: inline-block;
      line-height: 32px;
      text-align: center;
      cursor: pointer;
    }
    </style>
    

    路由注册:

    import Vue from "vue"
    import Router from "vue-router"
    
    // 导入页面组件
    。。。
    import User from "../components/User"
    import UserOrder from "../components/UserOrder"
    
    
    Vue.use(Router);
    
    export default new Router({
      // 设置路由模式为‘history’,去掉默认的#
      mode: "history",
      routes:[
       ....
        {
          name:"User",
          path:"/my",
          component: User,
          // children:[  // 设置子路由,在父级路由对应的组件中如果存在父子公用部分页面,可以使用router-view来实现子路由
          //   {
          //     name:"UserOrder",
          //     path:"/order",
          //     component: UserOrder,
          //   }
          // ]
        },
        {
          name:"UserOrder",
          path:"/my/order",
          component: UserOrder,
        },
      ]
    })
    
    

    后端提供查询当前登录用户的订单列表信息。

    orders/models.py,模型新增返回订单状态的文本格式

    class Order(BaseModel):
        """订单记录"""
        。。。。
        def order_status_text(self):
            return self.status_choices[self.order_status][1]
    
    

    users/serializers.py,序列化器,代码:

    """会员订单"""
    from orders.models import Order,OrderDetail
    class OrderDetailListModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = OrderDetail
            fields = ("price","real_price","discount_name","expire_text","course_img","course_name","course")
    
    class OrderListModelSerializer(serializers.ModelSerializer):
        order_courses = OrderDetailListModelSerializer(many=True)
        class Meta:
            model = Order
            fields = ("order_courses","id","create_time","pay_time","order_number","real_price","total_price","order_status","order_status_text","pay_type")
    
    class UserOrderModelSerializer(serializers.ModelSerializer):
        user_orders = OrderListModelSerializer(many=True)
        class Meta:
            model = User
            # fields = ("username","身份信息..")
            fields = ("username","user_orders")
    
    

    users/views.py,视图代码:

    from .serializers import UserOrderModelSerializer
    from rest_framework.generics import RetrieveAPIView
    from rest_framework.permissions import IsAuthenticated
    class UserOrderAPIView(RetrieveAPIView):
        permission_classes = [IsAuthenticated]
        serializer_class = UserOrderModelSerializer
        queryset = User.objects.all()
    
    

    users/urls.py,路由代码:

    re_path(r'(?P<pk>d+)/orders/',views.UserOrderAPIView.as_view()),
    
    

    前端请求获取当前登录用户的订单信息

    <template>
      <div class="user-order">
        <Header/>
        <div class="main">
            <div class="banner"></div>
              <div class="profile">
                  <div class="profile-info">
                      <div class="avatar"><img class="newImg" width="100%" alt="" src="/static/img/logo@2x.png"></div>
                      <span class="user-name">{{user_info.username}}</span>
                      <span class="user-job">深圳市 | 程序员</span>
                  </div>
                  <ul class="my-item">
                      <li>我的账户</li>
                      <li class="active">我的订单</li>
                      <li>个人资料</li>
                      <li>账号安全</li>
                  </ul>
                </div>
              <div class="user-data">
                <ul class="nav">
                  <li class="order-info">订单</li>
                  <li class="course-expire">有效期</li>
                  <li class="course-price">课程价格</li>
                  <li class="real-price">实付金额</li>
                  <li class="order-status">交易状态</li>
                  <li class="order-do">交易操作</li>
                </ul>
                <div class="my-order-item" v-for="order in user_info.user_orders">
                    <div class="user-data-header">
                      <div class="order-time">
                        下单时间: {{new Date(order.create_time).toLocaleString()}}
                        <span style="padding-left: 20px;" v-if="order.pay_time">付款时间: {{new Date(order.pay_time).toLocaleString()}}</span>
                      </div>
                      <span class="order-num">订单号:
                          <span class="my-older-number">{{order.order_number}}</span>
                      </span>
                    </div>
                  <ul class="nav user-data-list" v-for="course in order.order_courses">
                    <li class="order-info">
                        <img :src="$settings.Host+course.course_img" alt="">
                        <div class="order-info-title">
                          <p class="course-title">{{course.course_name}}</p>
                          <p v-if="course.discount_name" class="price-service">{{course.discount_name}}</p>
                        </div>
                    </li>
                    <li class="course-expire">{{course.expire_text}}</li>
                    <li class="course-price">{{course.price}}</li>
                    <li class="real-price">{{course.real_price}}</li>
                    <li class="order-status">{{order.order_status_text}}</li>
                    <li class="order-do">
                      <router-link v-if="order.order_status==1" to="/my/course" class="btn btn2">去学习</router-link>
                      <router-link v-if="order.order_status==0" :to="'/orders/'+order.order_number" class="btn btn2">去付款</router-link>
                    </li>
                  </ul>
    
                </div>
    
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default{
        name:"UserOrder",
        data(){
          return {
            user_info:{}
          };
        },
        created(){
            let token = localStorage.token || sessionStorage.token;
            let user_id = localStorage.user_id || sessionStorage.user_id;
            if(!token){
              this.$alert("对不起,您尚未登录!请登录!","警告",{
                callback(){
                  _this.$router.push("/login");
                }
              })
            }
    
            this.$axios.get(this.$settings.Host+`/users/${user_id}/orders/`,{
              headers:{
                // 注意下方的空格!!!
                "Authorization":"jwt " + token,
              },
            }).then(response=>{
              this.user_info = response.data;
              console.log(this.user_info);
            }).catch(error=>{
              console.log( error.response )
            })
        },
        components:{
          Header,
          Footer,
        }
      }
    </script>
    
    <style scoped>
    .user-order{
      padding-top: 80px;
    }
    .main .banner{
         100%;
        height: 324px;
        background: url(/static/img/my_bkging.0648ebe.png) no-repeat;
        background-size: cover;
        z-index: 1;
    }
    .profile{
         1200px;
        margin: 0 auto;
    }
    .profile-info{
        text-align: center;
        margin-top: -80px;
    }
    .avatar{
         120px;
        height: 120px;
        border-radius: 60px;
        overflow: hidden;
        margin: 0 auto;
    }
    .user-name{
        display: block;
        font-size: 24px;
        color: #4a4a4a;
        margin-top: 14px;
    }
    .user-job{
        display: block;
        font-size: 11px;
        color: #9b9b9b;
     }
    .my-item{
        list-style: none;
        line-height: 1.42857143;
        color: #333;
         474px;
        height: 31px;
        display: -ms-flexbox;
        display: flex;
        cursor: pointer;
        margin: 41px auto 0;
        -ms-flex-pack: justify;
        justify-content: space-between;
    }
    .my-item .active{
        border-bottom: 1px solid #000;
    }
    .user-data{
         1200px;
        height: auto;
        margin: 0 auto;
        padding-top: 30px;
        border-top: 1px solid #e8e8e8;
        margin-bottom: 63px;
    }
    .nav{
         100%;
        height: 60px;
        background: #e9e9e9;
        display: -ms-flexbox;
        display: flex;
        -ms-flex-align: center;
        align-items: center;
    }
    .nav li{
        margin-left: 20px;
        margin-right: 28px;
        height: 60px;
        line-height: 60px;
        list-style: none;
        font-family: PingFangSC-Medium;
        font-size: 13px;
        color: #333;
        border-bottom: 1px solid #e9e9e9;
       160px;
    }
    .nav .order-info{  325px; }
    .nav .course-expire{  60px; }
    .nav .course-price{  130px; }
    .user-data-header{
        display: flex;
        height: 44px;
        color: #4a4a4a;
        font-size: 14px;
        background: #f3f3f3;
        -ms-flex-align: center;
        align-items: center;
        font-family: PingFangSC-Regular;
    }
    .order-time{
        font-size: 12px;
        display: inline-block;
        margin-left: 20px;
    }
    .order-num{
        font-size: 12px;
        display: inline-block;
        margin-left: 29px;
    }
    .user-data-list{
        height: 100%;
        display: flex;
    }
    .user-data-list{
      background: none;
    }
    .user-data-list li{
        height: 60px;
        line-height: 60px;
    }
    .user-data-list .order-info{
        display: flex;
        align-items: center;
        margin-right: 28px;
    }
    .user-data-list .order-info img{
        max- 100px;
        max-height: 75px;
        margin-right: 22px;
    }
    .course-title{
         203px;
        font-size: 13px;
        color: #333;
        line-height: 20px;
        font-family: PingFangSC-Medium;
        margin-top: -10px;
    }
    .order-info-title .price-service{
        line-height: 18px;
    }
    .price-service{
        font-size: 12px;
        color: #fa6240;
        padding: 0 5px;
        border: 1px solid #fa6240;
        border-radius: 4px;
        margin-top: 4px;
        position: absolute;
    }
    .order-info-title{
        margin-top: -10px;
    }
    .user-data-list .course-expire{
        font-size: 12px;
        color: #ff5502;
        font-family: PingFangSC-Medium;
         60px;
    
        text-align: center;
    }
    .btn {
       100px;
      height: 32px;
      font-size: 14px;
      color: #fff;
      background: #ffc210;
      border-radius: 4px;
      border: none;
      outline: none;
      font-family: PingFangSC-Medium;
      transition: all .25s ease;
      display: inline-block;
      line-height: 32px;
      text-align: center;
      cursor: pointer;
    }
    </style>
    
    

    订单状态显示分析

    根据订单状态显示:
    1. 如果未支付[order.order_stauts=0],则显示"去支付"按钮
    2. 如果已支付[order.order_stauts=1],则显示"去学习"按钮
    3. 如果未支付,并超过指定时间[12个小时],则显示"已取消" [celery+RabbitMQ / Django-crontab 定时任务 ]
       用户下单在12小时以后自动判断订单状态如果是0,则直接改成3
    
    
  • 相关阅读:
    python设计模式
    tomcat在ubuntu下的配置
    排序算法
    python爬虫(一)
    python实现推荐系统(二)
    python实现k近邻
    python实现逻辑回归
    python实现推荐系统(一)
    SGD实现推荐系统
    pyqt4+chatterbot实现简单聊天机器人程序
  • 原文地址:https://www.cnblogs.com/pankypan/p/11300295.html
Copyright © 2020-2023  润新知