路飞项目

一、项目分析

1、Vue

2、restframework

3、路飞逻辑

  (1) 搭建环境    vue_cli     Django

      创建于课程相关的表结构

  (2)创建课程组件,展示课程信息(课程详情)

  (3)登录验证

  (4)购物车(redis)

  (5)结算中心

  (6)支付宝接口

  (7)支付应用

二、思路

  利用前后端分离的思想开发

  1、前端服务器       Vue_cli  (express ,  vue  ,  webpack(在VUE要build的时候使用))         

      Vue_cli是一个脚手架,里面内涵一套系统; 

      JS里有个类似django的web框架,express,里面有专门模块来receive,send,但是中间的过程交给VUE来做,因为VUE是处理数据的框架

    作用: 请求数据,展示页面

  2、后端口服务器    Django (restframework)

三、搭建环境

  1、vue create Xxxxx,手动编辑时,需要明白的是最后一步将项目创建另一名,是为了后续创建项目时选中别名继续引用其配置

  2、需要明白  

    (1)、后续开发大多数在src文件

    (2)、vue是单页面开发,只有index.html一个html,其他都是组件

    (3)、然后到main.js

    (4)、App.vue

    (5)、父子通信,一层嵌一层

  3、vue的一个组件简单的数据流展示

<template>
  <div class="degreeclass">
    <h1>This is an 学位课 page</h1>
    <div v-for="item in course_list">
       <p>{{item}}</p>
        </div>
  </div>
</template>

<script>

export default {
  name: 'degreeclass',
  data: function() {
     return {
        course_list: []
     }
  },
  mounted:function(){
     alert(123)
     this.init_course()
  },
  methods:{
     init_course:function(){
        this.course_list = ['python','linux','go']
     }

  }
}
</script>

  4、前后端通信用axios,前端用axios发信息,

    (1)、main.js

import axios from 'axios'
Vue.prototype.$http = axios

    (2)、course.vue,

 var  _this=this;注意此处
<template>
  <div class="course">
    <h3>课程列表</h3>
     <div v-for="item in course_list">
          <p>{{item}}</p>
     </div>

  </div>
</template>

<script>


export default {
  name: 'course',
  data:function () {
    return {
         course_list:[]
    }
  },
  mounted:function () {
       this.init_course()
  },
  methods:{
      init_course:function () {

         var  _this=this;
        // 发送请求
          this.$http.request({
             url:"http://127.0.0.1:8000/courses/",
             method:"get",
          }).then(function (response) {
              console.log(response);
              _this.course_list=response.data
          });

      }
  }

}
</script>

    (3)、新建个django drf工程,中间件去解决跨域问题

1、APIView
2、跨域问题
  obj =
HttpResponse(json.dumps(['红楼梦','西游记'],ensure_ascii=False))
  obj["Access-Control-Allow-Origin"]="*"
  return obj

 四、项目实战

1、跨域问题处理

  前端VUE发过来的请求,出现跨域问题

view.py  里response的数据也需要做跨域问题的处理

from rest_framework.views import  APIView
import json
from api.models import *

from api.serializers import CourseMiodelSerializers,CourseDetailMiodelSerializers
from rest_framework.response import Response

class coursesView(APIView):


    def get(self ,request ,*arg ,**kwargs):
        course_list=Course. objects.all()
        cs =CourseMiodelSerializers(course_list,many=True)

        return Response(cs.data)

    方法步骤a:

在api目录下新建个utils文件夹,在新建个cors.py,内容如下

from django.utils.deprecation import MiddlewareMixin

class CorsMiddleWare(MiddlewareMixin):

    def process_response(self,request,response):

        response["Access-Control-Allow-Origin"]="http://localhost:8081"   #这个url地址是前端的地址

        return response

  b:在settings.py里配置一条中间件的设定

 "api.utils.cors.CorsMiddleWare"

2、vue组件使用bootstrap

  a.组件界面引入css

在https://www.bootcdn.cn/twitter-bootstrap/下找到

将https://cdn.bootcss.com/twitter-bootstrap/4.2.1/css/bootstrap.css引入到index.html里的html的head里,link

  b.在course.vue里插入

     <div v-for="item in course_list">
            <div class="col-md-3">
                <div class="panel panel-info">
                    <div class="panel-heading">
                        <h3 class="panel-title">
                             <router-link :to="{name:'CourseDetail',params:{'id':item.id}}">{{item.name}}</router-link>
                        </h3>
                    </div>
                    <div class="panel-body">
                        {{item.brief.slice(0,100)}}          #固定取100个字符
                    </div>
                </div>
            </div>
        </div>

3、用户进入course页面后,当点击每段介绍的名字时,应该URL跳转,并且发送请求到drf,并获取数据显示在VUE上

  A、在course.vue界面写入

<h3 class="panel-title">
        <router-link :to="{name:'CourseDetail',params:{'id':item.id}}">{{item.name}}</router-link></h3>
#定义一个 CourseDetail的组件,参数为params

  B、定义一个 CourseDetail的组件

 {
      path: '/coursedetail/:id',
      name: 'CourseDetail',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: CourseDetail
    }

  C、新建个CourseDetail的组件,CourseDetail.vue

<template>

    <div class="course_detil">

重点一: <h4>课程名称:{{this.coursedetail_obj.course_name}}</h4>
        <h4>口号:{{this.coursedetail_obj.course_slogan}}</h4>
        <h4>推荐课时:{{this.coursedetail_obj.hours}}</h4>
        <h4>相关讲师:
            <span v-for="item in this.coursedetail_obj.teachers">{{item}},</span>
        </h4>
        <h4>
            <p> 推荐课程:</p>
            <ul>
                <li v-for="item in this.coursedetail_obj.recommend_courses">
                     <router-link :to="{name:'CourseDetail',params:{'id':item.pk}}">{{item.name}}</router-link>
                </li>
            </ul>

        </h4>
        <p>
            <img :src="src" alt="" width="300" height="200">
        </p>
        <hr>
    </div>
</template>

<script>

    export default {
        name: 'Detail',
        data: function () {
            return {

                id:this.$route.params.id,
                coursedetail_obj:"",
                src:""
            }
        },
        mounted: function () {
              this.init_course_detail()
        },
        methods: {
            init_course_detail:function () {
重点二:
                var _this = this;
                // 发送请求
                this.$http.request({
                    url: "http://127.0.0.1:8000/coursedetail/"+_this.id+"/",
                    method: "get",
                }).then(function (response) {
                    console.log("123",response);
                    _this.coursedetail_obj=response.data

                    _this.src="http://127.0.0.1:8000"+_this.coursedetail_obj.course_img

                });

  D:drf处理

  url(r'^coursedetail/(?P<pk>\d+)', CourseDetailView.as_view()),
class CourseDetailView(APIView):


    def get(self ,request,pk,*arg ,**kwargs):
        coursedetail_obj=CourseDetail.objects.filter(pk=pk).first()
        cds =CourseDetailMiodelSerializers(coursedetail_obj)

        return Response(cds.data)

4、序列化组件覆盖字段的功能

   有source属性使得可以扩展序列化组件的功能,以source里为主

class CourseDetailMiodelSerializers(serializers.ModelSerializer):
    class Meta:
        model=CourseDetail
        fields="__all__"

    course_name=serializers.CharField(source="course.name")
    course_img=serializers.CharField(source="course.course_img")

    teachers=serializers.SerializerMethodField()
    def get_teachers(self,obj):
        temp=[]
        for i in obj.teachers.all():
            temp.append(i.name)
        return temp

    recommend_courses = serializers.SerializerMethodField()

    def get_recommend_courses(self, obj):
        temp = []
        for i in obj.recommend_courses.all():
            temp.append({
                "name":i.name,
                "pk":i.pk
            })
        return temp

5、若想实现字体样式变化,如居左显示

  可以再coursedetail.vue的

<style>
      .course_detil{
          text-align: left;
      }
</style>

6、序列化组件的多对多field

   <h4>相关讲师:
            <span v-for="item in this.coursedetail_obj.teachers">{{item}},</span>
        </h4>
        <h4>
            <p> 推荐课程:</p>
            <ul>
                <li v-for="item in this.coursedetail_obj.recommend_courses">
                     <router-link :to="{name:'CourseDetail',params:{'id':item.pk}}">{{item.name}}</router-link>
                </li>
            </ul>

        </h4>

后端

class CourseDetailMiodelSerializers(serializers.ModelSerializer):
    class Meta:
        model=CourseDetail
        fields="__all__"

    course_name=serializers.CharField(source="course.name")
    course_img=serializers.CharField(source="course.course_img")

    teachers=serializers.SerializerMethodField()
    def get_teachers(self,obj):
        temp=[]
        for i in obj.teachers.all():
            temp.append(i.name)
        return temp

    recommend_courses = serializers.SerializerMethodField()

    def get_recommend_courses(self, obj):
        temp = []
        for i in obj.recommend_courses.all():
            temp.append({
                "name":i.name,
                "pk":i.pk
            })
        return temp

 7、课程详情之页面切换,(在推荐课程里,点下title可以跳转)   watch ----to\from

  A、params:{'id':item.pk}},前端能取到pk,后台一定要发过来

  <p> 推荐课程:</p>
            <ul>
                <li v-for="item in this.coursedetail_obj.recommend_courses">
                     <router-link :to="{name:'CourseDetail',params:{'id':item.pk}}">{{item.name}}</router-link>
                </li>
            </ul>

  B、需要 def get_recommend_courses里先赋个字典,带name,pk属性

class CourseDetailMiodelSerializers(serializers.ModelSerializer):
    class Meta:
        model=CourseDetail
        fields="__all__"

    course_name=serializers.CharField(source="course.name")
    course_img=serializers.CharField(source="course.course_img")

    teachers=serializers.SerializerMethodField()
    def get_teachers(self,obj):
        temp=[]
        for i in obj.teachers.all():
            temp.append(i.name)
        return temp

    recommend_courses = serializers.SerializerMethodField()

    def get_recommend_courses(self, obj):
        temp = []
        for i in obj.recommend_courses.all():
            temp.append({
                "name":i.name,
                "pk":i.pk
            })
        return temp

  总结:VUE其实是一个不断地销魂、创建的过程,一个页面,一旦被替换掉了,说明其生命周期结束了,会被另一个组件替换掉

    C、因为在同一个路由里再点击titte,会有新的路由变化,想要监听路由的变化,一旦跳转了让init_course_detail重新执行下,用watch

如下,watch里to和from分别是跳转到和来自于的链接,id需要变化,因为当前已经是课程里了,有id如3,点击推荐课程则新的title,id值变化了

watch监听路有变化,一旦路由变化,则重新发个请求,数据展示在页面上

  export default {
        name: 'Detail',
        data: function () {
            return {

                id:this.$route.params.id,
                coursedetail_obj:"",
                src:""
            }
        },
        mounted: function () {
              this.init_course_detail()
        },
        methods: {
            init_course_detail:function () {

                var _this = this;
                // 发送请求
                this.$http.request({
                    url: "http://127.0.0.1:8000/coursedetail/"+_this.id+"/",
                    method: "get",
                }).then(function (response) {
                    console.log("123",response);
                    _this.coursedetail_obj=response.data

                    _this.src="http://127.0.0.1:8000"+_this.coursedetail_obj.course_img

                });



            }

        },

        watch:{
            "$route":function (to,from) {
                 console.log("to",to)
                 console.log("from",from)

                 this.id=to.params.id
                 this.init_course_detail()
            }
        }

    }
</script>

  8、图片添加,使得前端能访问到图片

  A、在前端static里新建个img的文件夹,使得http:127.......+path 能访问到

  B、先在class类里添加图片字段,并补充数据,保存/static/img/python.png,等

class Course(models.Model):
    """专题课程"""
    name = models.CharField(max_length=128, unique=True)
    course_img = models.CharField(max_length=255)

  C、前端coursedetail的vue里添加访问url

  

  <p>
            <img :src="src" alt="" width="300" height="200">    #不能在这里面直接拼接地址属性
        </p>
 export default {
        name: 'Detail',
        data: function () {
            return {

                id:this.$route.params.id,
                coursedetail_obj:"",
                src:""
            }
        },
        mounted: function () {
              this.init_course_detail()
        },
        methods: {
            init_course_detail:function () {

                var _this = this;
                // 发送请求
                this.$http.request({
                    url: "http://127.0.0.1:8000/coursedetail/"+_this.id+"/",
                    method: "get",
                }).then(function (response) {
                    console.log("123",response);
                    _this.coursedetail_obj=response.data

                    _this.src="http://127.0.0.1:8000"+_this.coursedetail_obj.course_img  #http://+path,可以在全局里设置

                });



            }

        },

9、引入elemrnt-ui,类似bootstrap,

1 安装element-ui

   (1) npm install element-ui
   (2) main.js导入:
            import ElementUI from 'element-ui';
            import 'element-ui/lib/theme-chalk/index.css';
            Vue.use(ElementUI);

 10、table页面的展示,如课程章节的detail展示

  A、coursedetail.vue需要绑定click事件,用以切换,v-show为true的时候展示,false的时候隐藏

   <div class="tab-menu">
            <div>
                <a @click="changeTab('detail')">课程概述</a>
                <a @click="changeTab('chapter')">课程章节</a>
                <a @click="changeTab('questions')">常见问题</a>
                <a @click="changeTab('review')">课程评价</a>
            </div>
        </div>

        <div>
            <div v-show="tabs.detail">
                <div class="brief">
                     <p>{{coursedetail_obj.course_brief}}</p>
                </div>
            </div>
            <div v-show="tabs.chapter">课程章节的内容...</div>
            <div v-show="tabs.questions">课程常见问题的内容....</div>
            <div v-show="tabs.review">课程评价的内容...</div>
        </div>

  B、datas里为数据显示与否,method里绑定个changeTab事件

   export default {
        name: 'Detail',
        data: function () {
            return {

                id:this.$route.params.id,
                coursedetail_obj:"",
                src:"",
                tabs: {
                     detail:true,
                     chapter:false,
                     questions:false,
                     review:false,
                     }
            }
        },
        mounted: function () {
              this.init_course_detail()
        },
        methods: {
            init_course_detail:function () {

                var _this = this;
                // 发送请求
                this.$http.request({
                    url: "http://127.0.0.1:8000/coursedetail/"+_this.id+"/",
                    method: "get",
                }).then(function (response) {
                    console.log("123",response);
                    _this.coursedetail_obj=response.data

                    _this.src="http://127.0.0.1:8000"+_this.coursedetail_obj.course_img

                });



            },
            changeTab:function (name) {
                 for (let item in this.tabs){
                     if (item==name){
                         this.tabs[name]=true
                     }
                     else {
                         this.tabs[item]=false
                     }
                 }
            }
        },

  C、后台需要传递coursedetail_brief,则

class CourseDetailMiodelSerializers(serializers.ModelSerializer):
    class Meta:
        model=CourseDetail
        fields="__all__"

    course_name=serializers.CharField(source="course.name")
    course_img=serializers.CharField(source="course.course_img")
    course_brief=serializers.CharField(source="course.brief")
    teachers=serializers.SerializerMethodField()

11、价格策略的展示

  A、后台需要新增扩展价格,其实就是用contentType的GenericRelation的方法

class CourseDetailMiodelSerializers(serializers.ModelSerializer):
    class Meta:
        model=CourseDetail
        fields="__all__"

    course_name=serializers.CharField(source="course.name")
    course_img=serializers.CharField(source="course.course_img")
。。。。

    price_policy=serializers.SerializerMethodField()
    def get_price_policy(self,obj):

        print( obj.course.price_policy.all())
      return [{"price":item.price,"valid_period":item.valid_period,"pk":item.pk} for item in obj.course.price_policy.all()]

   B、前端展示

   <ul>
            <li v-for="item in coursedetail_obj.price_policy">
                价格:{{item.price}} 周期:{{item.valid_period}}
            </li>
        </ul>

 12、在做登陆页面时候,遇到跨域复杂请求的问题

1 CORS的简单请求和复杂请求
       只要同时满足以下两大条件,就属于简单请求:
               (1) 请求方法是以下三种方法之一:
                    HEAD
                    GET
                    POST
                (2)HTTP的头信息不超出以下几种字段:
                    Accept
                    Accept-Language
                    Content-Language
                    Last-Event-ID
                    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain


        一旦确认是复杂请求,会先发一次预检请求(option请求)
    2 axios请求默认发送json数据

  A、后端处理,加中间件

from django.utils.deprecation import MiddlewareMixin

class CorsMiddleWare(MiddlewareMixin):

    def process_response(self,request,response):

        if request.method=="OPTIONS":
            response["Access-Control-Allow-Headers"]="Content-Type"

        response["Access-Control-Allow-Origin"] = "http://localhost:8080"

        return response

  插入 "api.utils.cors.CorsMiddleWare"

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    "api.utils.cors.CorsMiddleWare"
]

  B、App.vue上加入登录按钮,同时新建Login.vue

  <span class="pull-right">

                <span v-if="!this.$store.state.username">
                       <router-link to="/login">登录</router-link>
                       <router-link to="">注册</router-link>
                </span>
                <span v-else="">
                      <span>{{this.$store.state.username}}</span>
                      <a href="javascript:void(0)" @click="logout">注销</a>
                </span>
<script>

  C、form里的button按钮触发事件,将绑定v-model的值post上传

  <div>
         <H4>登录页面</H4>
         <form action="">
            <p>用户名:<input type="text" id="user" v-model="username"></p>
            <p>密码:<input type="password" id="pwd" v-model="password"></p>
            <input type="button" value="submit" @click="Login">
         </form>
    </div>

<script>
export default {
name: 'course',
data: function () {
return {
username:"",
password:"",
}
},
mounted: function () {

},
methods: {
Login:function () {
let _this=this;
this.$http.request({
url:"http://127.0.0.1:8000/login/",
method:"post",
data:{
user:this.username,
pwd:this.password,
}
}).then(function (response) {

console.log(response);
if(response.data.code=="1000"){
_this.$store.commit("saveToken",response)

}

}).catch(function () {
// 发生错误执行函数
})

}
}

}

13、持续完善login.vue

  A、数据库迁移

class User(models.Model):
    user=models.CharField(max_length=32)
    pwd=models.CharField(max_length=32)
    type=models.IntegerField(choices=((1,"common"),(2,"VIP"),(3,"SVIP")),default= 1)

class UserToken(models.Model):
    user=models.OneToOneField("User")
    token=models.CharField(max_length=128)

  B、Login.vue

<template>
    <div>
         <H4>登录页面</H4>
         <form action="">
            <p>用户名:<input type="text" id="user" v-model="username"></p>
            <p>密码:<input type="password" id="pwd" v-model="password"></p>
            <input type="button" value="submit" @click="Login">
         </form>
    </div>
</template>

<script>


    export default {
        name: 'course',
        data: function () {
            return {
                  username:"",
                  password:"",
            }
        },
        mounted: function () {

        },
        methods: {
             Login:function () {
                 let _this=this;
                 this.$http.request({
                     url:"http://127.0.0.1:8000/login/",
                     method:"post",
                     data:{
                         user:this.username,
                         pwd:this.password,
                     }
                 }).then(function (response) {

                     console.log(response);
                     if(response.data.code=="1000"){


                         _this.$store.commit("saveToken",response)



                     }

                 }).catch(function () {
                     // 发生错误执行函数
                 })


             }
        }

    }
</script>

<style>

</style>

  C、后台新建个auth.py,专门做验证,uuid随机生成字符串,确保每次登录都是不同的token

from rest_framework.views import APIView

from django.shortcuts import HttpResponse

from api.models import *

import uuid

from django.http import JsonResponse

class LoginView(APIView):

    def post(self,request):
        username=request.data.get("user")
        password=request.data.get("pwd")

        try:
            response = {"code": 1000, "user": "", "msg": ""}
            user = User.objects.filter(user=username, pwd=password).first()
            if user:
                random_str = uuid.uuid4()
                UserToken.objects.update_or_create(user=user, defaults={"token": random_str})
                response["token"]=random_str
                response["user"]=user.user
            else:
                response["code"] = 2000
                response["msg"] = "用户名或者密码错误"

        except Exception as e:
            response["code"] = 3000
            response["msg"] = str(e)

        return JsonResponse(response)

 14、登录验证、登录状态保存-----vuex状态管理器,可以定义全局变量

    3 使用vuex方式
       (1)  设计 store.js
            import Vue from 'vue'
            import Vuex from 'vuex'
            import Cookie from "vue-cookies"

            Vue.use(Vuex);

            export default new Vuex.Store({
              state: {
                 username: Cookie.get("username"),
                 token:Cookie.get("token"),

              },
              mutations: {
                     saveToken: function (state,response) {

                          state.username = response.data.user;
                          state.token = response.data.token;
                          Cookie.set("username",response.data.user,"20min");
                          Cookie.set("token",response.data.token,"20min");

                        },

                     clearToken:function (state) {
                          state.username = "";
                          state.token = "";
                          Cookie.remove("username");
                          Cookie.remove("token");

                     }
              },
            })

            
        (2) main.js  
            import store from './store'
            new Vue({
                  router,
                  store,
                  render: h => h(App)
                }).$mount('#app')
     
         (3)   
            在任何组件中:
                     this.$stroe.state.username
                     this.$store.commit("函数名","参数")
            
            
            
        Django:
              1 
                response.set_cookie("","")
              2 
                request.COOKIES

  A、如上所示

  B、前端实现,用户登陆了则显示user在线的状态,

 <span class="pull-right">

                <span v-if="!this.$store.state.username">
                       <router-link to="/login">登录</router-link>
                       <router-link to="">注册</router-link>
                </span>
                <span v-else="">
                      <span>{{this.$store.state.username}}</span>
                      <a href="javascript:void(0)" @click="logout">注销</a>
                </span>

            </span>

  C、login.vue

<script>


export default {
name: 'course',
data: function () {
return {
username:"",
password:"",
}
},
mounted: function () {

},
methods: {
Login:function () {
let _this=this; #this代指方法,_this代指对象
this.$http.request({
url:"http://127.0.0.1:8000/login/",
method:"post",
data:{
user:this.username,
pwd:this.password,
}
}).then(function (response) {

console.log(response);
if(response.data.code=="1000"){
_this.$store.state.username = response.data.user
_this.$store.state.usertoken = response.data.token

// _this.$store.commit("saveToken",response)

}

}).catch(function () {
// 发生错误执行函数
})

}
}

}
</script>

  D、后台传递数据

class LoginView(APIView):

    def post(self,request):
        username=request.data.get("user")
        password=request.data.get("pwd")

        try:
            response = {"code": 1000, "user": "", "msg": ""}
            user = User.objects.filter(user=username, pwd=password).first()
            if user:
                random_str = uuid.uuid4()
                UserToken.objects.update_or_create(user=user, defaults={"token": random_str})
                response["token"]=random_str
                response["user"]=user.user
            else:
                response["code"] = 2000
                response["msg"] = "用户名或者密码错误"

        except Exception as e:
            response["code"] = 3000
            response["msg"] = str(e)

        return JsonResponse(response)

15、登录验证优化

  经过14之后,因为vue单页面,数据请求后在组件内是可以全局使用store.js里的变量,但是一旦刷新后,则数据需要重新登录请求访问,则又回到刚开始未登录界面。

需要cookie,否则只能重新登录

  A、前台第一次先提交登录,登陆成功后返回cookie

    <span class="pull-right">

                <span v-if="!this.$store.state.username">
                       <router-link to="/login">登录</router-link>
                       <router-link to="">注册</router-link>
                </span>
                <span v-else="">
                      <span>{{this.$store.state.username}}</span>
                      <a href="javascript:void(0)" @click="logout">注销</a>
                </span>



            </span>

后台处理

from rest_framework.views import APIView
from django.shortcuts import HttpResponse
from api.models import *
import uuid
from django.http import JsonResponse

class LoginView(APIView):

    def post(self,request):
        username=request.data.get("user")
        password=request.data.get("pwd")

        try:
            response = {"code": 1000, "user": "", "msg": ""}
            user = User.objects.filter(user=username, pwd=password).first()
            if user:
                random_str = uuid.uuid4()
                UserToken.objects.update_or_create(user=user, defaults={"token": random_str})
                response["token"]=random_str
                response["user"]=user.user
            else:
                response["code"] = 2000
                response["msg"] = "用户名或者密码错误"

        except Exception as e:
            response["code"] = 3000
            response["msg"] = str(e)

        return JsonResponse(response)

返回前台

<template>
    <div>
         <H4>登录页面</H4>
         <form action="">
            <p>用户名:<input type="text" id="user" v-model="username"></p>
            <p>密码:<input type="password" id="pwd" v-model="password"></p>
            <input type="button" value="submit" @click="Login">
         </form>
    </div>
</template>

<script>


    export default {
        name: 'course',
        data: function () {
            return {
                  username:"",
                  password:"",
            }
        },
        mounted: function () {

        },
        methods: {
             Login:function () {
                 let _this=this;
                 this.$http.request({
                     url:"http://127.0.0.1:8000/login/",
                     method:"post",
                     data:{
                         user:this.username,
                         pwd:this.password,
                     }
                 }).then(function (response) {

                     console.log(response);
                     if(response.data.code=="1000"){
                #        this.$store.state.username = response.data.user
                #        this.$store.state.usertoken = response.data.token

                        _this.$store.commit("saveToken",response)          #涉及到全局使用的变量时,提交的数据需要 
   

                     }

                 }).catch(function () {
                     // 发生错误执行函数
                 })


             }
        }

    }
</script>

<style>

</style>

赋值cookie信息到前台的vuex里

import Vue from 'vue'
import Vuex from 'vuex'
import Cookie from "vue-cookies"

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
     username: Cookie.get("username"),
     token:Cookie.get("token"),

  },
  mutations: {
         saveToken: function (state,response) {

              state.username = response.data.user;              #先赋值变量,再设置cookie
              state.token = response.data.token;
              Cookie.set("username",response.data.user,"20min");
              Cookie.set("token",response.data.token,"20min");

            },

         clearToken:function (state) {
              state.username = "";
              state.token = "";
              Cookie.remove("username");
              Cookie.remove("token");

         }
  },
})

之后在切换前端页面时,都带着cookie信息

然后再无论是否刷新vue页面,cookie都能在一定时间内保存,取决于设置的时间长短

  B、做logout处理

与上面一样的处理方式

App.vue里的logout绑定的click方法

<script>

    export default {
        name: 'App',
        methods: {
            logout:function () {
                   this.$store.commit("clearToken")

            }
        },
        

    }
</script>

16、为什么使用redis

  A、他比数据库要快、灵活,像比如一些最终用户未下单的存放在购物车里的数据无需专门像mysql一样建表存储

  B、最终下单的数据支持持久化,不像memecache只能在内存里操作,速度虽然快,但是无法持久化

 redis
      
     -- 数据库 (mysql)    
     -- 非关系型数据库
     -- 持久化    
     
下载redis

https://github.com/MicrosoftArchive/redis/releases

 17、登录认证类

  搭建购物车时候,一个查看、一个提交表单数据均需要登录认证,然后判断数据提交是否合法、在数据库内等等,

再然后保存redis数据到response返回回去

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api.models import UserToken

class LoginAuth(BaseAuthentication):

    def authenticate(self, request):
        token=request.GET.get("token")
        token_obj=UserToken.objects.filter(token=token).first()

        if token_obj:
            return token_obj.user,token_obj.token
        else:
            raise AuthenticationFailed("认证失败了")

shoppingcar.py


from rest_framework.views import APIView
from django.shortcuts import HttpResponse
class ShoppingCarView(APIView): 

  authentication_classes
= [LoginAuth,]
  def get(self,request):
    pass
  def post(self,request):
    pass

用Postman测试下是否登录验证做好

18、购物车接口设计

   A、shopping_car.py

from rest_framework.views import APIView
from django.shortcuts import HttpResponse

from api.utils.auth_class import LoginAuth
from api.models import *

from django.core.exceptions import ObjectDoesNotExist

from api.utils.response import BaseResponse
import json

from rest_framework.response import Response
from django.http import JsonResponse

from api.utils.exceptions import PriceException

from django_redis import get_redis_connection

class ShoppingCarView(APIView):

    authentication_classes = [LoginAuth,]
    response=BaseResponse()
    conn=get_redis_connection("default")

    def post(self,request):
        """
        购物车的添加课程请求
        :param request:
        :return:
        """
        print(request.user,request.auth)

        print("request.data",request.data) # {'course_id': 1, 'price_policy_id': 1}
        # 获取数据

        course_id=request.data.get("course_id")
        price_policy_id=request.data.get("price_policy_id")

        # 校验数据是否合法

        try:
            # (1) 校验课程是否存在
            course_obj=Course.objects.get(pk=course_id)

            # 查找课程关联的所有的价格策略

            price_policy_list=course_obj.price_policy.all()

            price_policy_dict={}
            for price_policy_item in price_policy_list:
                price_policy_dict[price_policy_item.pk]={
                          "price":price_policy_item.price,
                          "valid_period":price_policy_item.valid_period,
                          "valid_period_text":price_policy_item.get_valid_period_display()
                }

            print(price_policy_dict)

            '''
            price_policy_dict= {
                           1:{
                              "price":100,
                              "valid_period":7,
                              "valid_period_text":"一周"
                              },

                            2 :{
                              "price":200,
                              "valid_period":14,
                              "valid_period_text":"两周"
                              }

                        }

            '''

            if price_policy_id not in price_policy_dict:
                raise PriceException()


            # shopping_car的key

            shopping_car_key="ShoppingCarKey_%s_%s"

            user_id=request.user.pk
            shopping_car_key=shopping_car_key%(user_id,course_id)
            print(shopping_car_key)

            val={
                "course_name":course_obj.name,
                "course_img":course_obj.course_img,
                "prcie_policys":json.dumps(price_policy_dict),
                "default_prcie_policy_id":price_policy_id
            }

            self.conn.hmset(shopping_car_key,val)
            self.response.data = "success"


        except PriceException as e:
            self.response.code = "3000"
            self.response.error_msg = e.msg

        except ObjectDoesNotExist as e:
            print("该课程不存在!")
            self.response.code="2000"
            self.response.error_msg="该课程不存在!"

        return JsonResponse(self.response.dict)

    def get(self,request):
        """
        查看购物车列表请求
        :param request:
        :return:
        """
        pass

  B、response.py

class BaseResponse(object):

    def __init__(self):
        self.data=None
        self.error_msg=""
        self.code=1000

    @property
    def dict(self):

        return self.__dict__

  C、exception.py

class PriceException(Exception):

    def __init__(self):
        self.msg="价格策略有问题,你不是人!"

  D、auth_class.py

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api.models import UserToken

class LoginAuth(BaseAuthentication):

    def authenticate(self, request):
        token=request.GET.get("token")
        token_obj=UserToken.objects.filter(token=token).first()

        if token_obj:
            return token_obj.user,token_obj.token
        else:
            raise AuthenticationFailed("认证失败了")

  E、test.py,要想经过redis直接得到字典的方法

################################## 测试redis###################################

import redis

r=redis.Redis(host="127.0.0.1",port=6379)
#
# print(r.get("123"))
#
# print(r.hgetall("ShoppingCarKey_1_1"))


# r.hmset("k11",{"k22":{"k33":"v3"}})

# 查询k33 对应的值

# print(r.hgetall("k11"))  # 字典
#
# print(r.hget("k11","k22"))
#
# print(r.hget("k11","k22").decode("utf8"))
#
# import json
#
# s=json.dumps(r.hget("k11","k22").decode("utf8"))
#
# json.loads(s)


##############################
import json

r.hmset("k11",{"k22":json.dumps({"k33":"v3"})})

print(r.hget("k11","k22"))

print(json.loads(r.hget("k11","k22")))

19、django里使用redis

  A、安装django-redis,且settings里配置

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100}
            # "PASSWORD": "密码",
        }
    },
}

  B、views里

from django_redis import get_redis_connection

class ShoppingCarView(APIView):


    conn=get_redis_connection("default")

    def post(self,request):
       self.conn.hmset(shopping_car_key,val)
        self.response.data = "success"
。。。。。
        pass   

20、为什么选择前后端分离

  A、并行开发、提升效率

  B、解耦、前后端数据接口基本一致、适用于后续andriod和ios等其它平台的接口和设计

 21、结算接口设计

  A、models

# 支付功能相关表

class Coupon(models.Model):
    """优惠券生成规则"""
    name = models.CharField(max_length=64, verbose_name="活动名称")
    brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")
    coupon_type_choices = ((0, '立减券'), (1, '满减券'), (2, '折扣券'))
    coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")

    money_equivalent_value = models.IntegerField(verbose_name="等值货币",blank=True,null=True)
    off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
    minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段")

    content_type = models.ForeignKey(ContentType, blank=True, null=True,on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
    content_object = GenericForeignKey('content_type', 'object_id')

    quantity = models.PositiveIntegerField("数量(张)", default=1)
    open_date = models.DateField("优惠券领取开始时间")
    close_date = models.DateField("优惠券领取结束时间")
    valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True)
    valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True)
    coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,
                                                    help_text="自券被领时开始算起")
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "31. 优惠券生成规则"

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

class CouponRecord(models.Model):
    """优惠券发放、消费纪录"""
    coupon = models.ForeignKey("Coupon",on_delete=models.CASCADE)
    number = models.CharField(max_length=64)
    user = models.ForeignKey("User", verbose_name="拥有者",on_delete=models.CASCADE)
    status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期'))
    status = models.SmallIntegerField(choices=status_choices, default=0)
    get_time = models.DateTimeField(verbose_name="领取时间", help_text="用户领取时间")
    used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间")

    class Meta:
        verbose_name_plural = "32. 优惠券发放、消费纪录"

    def __str__(self):
        return '%s-%s-%s' % (self.user, self.number, self.status)

  B、此部分主要是业务逻辑了,即

前端点击结算按钮,将选择的课程信息发送到后台,后台将信息处理后(包括课程信息,用户优惠券信息等),存在redis里并返回前端

settings.py

LUFFY_SHOPPING_CAR="ShoppingCarKey_%s_%s"
LUFFY_PAYMENT="Payment_%s"

payment.py


from
rest_framework.views import APIView from django.shortcuts import HttpResponse from api.utils.auth_class import LoginAuth from api.models import * from django.core.exceptions import ObjectDoesNotExist from api.utils.response import BaseResponse import json from rest_framework.response import Response from django.http import JsonResponse from api.utils.exceptions import PriceException from django_redis import get_redis_connection from api.models import * from django.core.exceptions import ObjectDoesNotExist from django.conf import settings class PaymentView(APIView): authentication_classes = [LoginAuth,] response=BaseResponse() conn=get_redis_connection("default") def post(self,request): """ 结算课程的保存 :param request: :return: """ print(request.user,request.auth) print("request.data",request.data) # {course_id_list:[course_id, ....]} course_id_list=request.data.get("course_id_list") try: payment_key=settings.LUFFY_PAYMENT%(request.user.pk) payment_dict={} for course_id in course_id_list: # 校验课程是否存在购物车中 course_dict={} shopping_car_key = settings.LUFFY_SHOPPING_CAR shopping_car_key=shopping_car_key%(request.user.pk,course_id) if not self.conn.exists(shopping_car_key): self.response.error_msg = "购物城中不存在该课程" self.response.code = 2000 raise Exception # 获取循环的该课程的详细信息字典 course_detail=self.conn.hgetall(shopping_car_key) print("course_detail",course_detail) print("ok123") course_detail_dict={} for key,val in course_detail.items(): key=key.decode("utf8") val=val.decode("utf8") if key=="price_policys": print(val) val=json.loads(val) print(type(val)) course_detail_dict[key]=val print("----->",course_detail_dict) # 查询登录用户所有有效优惠券 import datetime now=datetime.datetime.now().date() coupon_record_list=CouponRecord.objects.filter(user=request.user,status=0,coupon__valid_begin_date__lt=now,coupon__valid_end_date__gt=now) # 构建数据结构,保存到redis中: course_coupons_dict = {} global_coupons_dict = {} for coupon_record in coupon_record_list: temp={ "name":coupon_record.coupon.name, "coupon_type": coupon_record.coupon.coupon_type, "money_equivalent_value": coupon_record.coupon.money_equivalent_value or "", "off_percent": coupon_record.coupon.off_percent or "", "minimum_consume": coupon_record.coupon.minimum_consume or "", "object_id":coupon_record.coupon.object_id or "" } # 判断该优惠券对象是通用优惠券还是课程优惠券 if coupon_record.coupon.object_id: # 课程优惠券 course_coupons_dict[coupon_record.pk]=json.dumps(temp) else: # 通用优惠券 global_coupons_dict[coupon_record.pk]=json.dumps(temp) course_dict["course_detail"]=json.dumps(course_detail_dict) course_dict["coupons"]=json.dumps(course_coupons_dict) payment_dict[course_id]=course_dict self.conn.hmset(payment_key,payment_dict) self.response.data="success" except Exception as e : print(e) return JsonResponse(self.response.dict) def get(self,request): """ :param request: :return: """ pass

参考真的

import json
from utils.auth import LoginAuth
from rest_framework.response import Response
from api.models import Coupon,CouponRecord
from django.conf import settings
from rest_framework.views import APIView
from utils.response import BaseResponse

from django_redis import get_redis_connection
LUffY_GLOBAL_COUPON_KEY = "luffy_global_coupons_%s"




class PaymentView(APIView):

    authentication_classes = [LoginAuth,]
    res = BaseResponse()
    conn = get_redis_connection("default")

    def post(self, request, *args, **kwargs):

        # 1  获取课程列表
        course_id_list = request.data.get("course_id_list")
        login_user_id = request.auth.user.pk

        luffy_payment_key = settings.LUFFY_PAYMENT_KEY % (login_user_id)
        payment_dict={}

        for course_id in course_id_list:

            course_dict={}

            shopping_car_key = settings.LUFFY_SHOPPING_CAR_KEY % (login_user_id, course_id)
            # 校验课程是否合法
            if not self.conn.exists(shopping_car_key):
                self.res.code = 2000
                self.res.error_msg = "课程不在购物车中!"
                return Response(self.res.dict)

            # 课程详细字典
            course_detail = self.conn.hgetall(shopping_car_key)
            course_detail_dict = {}
            for key, val in course_detail.items():
                if key == "pricepolicy":
                    val = json.loads(val.decode("utf8"))
                else:
                    val = val.decode("utf8")
                course_detail_dict[key.decode("utf8")] = val
            print("course_detail_dict", course_detail_dict)

            # 查询该用户的所有有效期的优惠券
            import datetime
            now = datetime.datetime.now()
            coupon_record_list = CouponRecord.objects.filter(user_id=login_user_id, status=0,
                                                             coupon__valid_begin_date__lt=now,
                                                             coupon__valid_end_date__gt=now)


            course_coupons_dict = {}
            global_coupons_dict = {}

            for coupon_record in coupon_record_list:
                object_id = coupon_record.coupon.object_id
                temp = {
                    "name": coupon_record.coupon.name,
                    "coupon_type": coupon_record.coupon.coupon_type,
                    "money_equivalent_value": coupon_record.coupon.money_equivalent_value or "",
                    "off_percent": coupon_record.coupon.off_percent or "",
                    "minimum_consume": coupon_record.coupon.minimum_consume or "",
                    "object_id": object_id or "",
                }

                # 判断通用优惠券
                if not object_id:
                    global_coupons_dict[coupon_record.pk] = json.dumps(temp, ensure_ascii=False)
                else:
                    course_coupons_dict[coupon_record.pk] = json.dumps(temp, ensure_ascii=False)

            # 该用户的通用优惠券写入redis
            name = LUffY_GLOBAL_COUPON_KEY % login_user_id
            self.conn.hmset(name,global_coupons_dict)

            # 构建循环的课程字典
            course_dict["course_detail"] = json.dumps(course_detail_dict, ensure_ascii=False)
            course_dict["coupons"] = json.dumps(course_coupons_dict, ensure_ascii=False)

            # 将课程字典写入到redis中
            payment_dict[course_id]=course_dict

        self.conn.hmset(luffy_payment_key,payment_dict)

        '''

        +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
           "shopping_car_1_1":{
                                    "title":course_obj.title,
                                    "img_src":course_obj.course_img,
                                    "pricepolicy":json.dumps(price_policy_dict,ensure_ascii=False),
                                    "default":pricepolicy_id
                               }

        +++++++++++++++++++++++++++++++++++++++++
          1 某用户的结算中心redis存储:
          luffy_payment_1:{

             course_id:{

                        course_detail:{
                           },
                        coupons:{
                                 1:{
                                            "name":coupon_record.coupon.name,
                                            "coupon_type":coupon_record.coupon.coupon_type,
                                            "money_equivalent_value":coupon_record.coupon.money_equivalent_value,
                                            "off_percent":coupon_record.coupon.off_percent,
                                            "minimum_consume":coupon_record.coupon.minimum_consume,
                                            "object_id":coupon_record.coupon.object_id,
                                        }
                                 }

                        },
             course_id:{

                  },

             ....

         }

         2 通用优惠券的redis存储:
         global_coupons_1:{
                 global_coupon_id:{}

                 }

        '''

        return Response("success")

    def get(self, request, *args, **kwargs):
        # 从结算中心拿到用户的所有结算数据
        try:
            # 获取用户id
            user_id = request.auth.user.pk
            # 获得该用户结算中心的所有的keys
            luffy_payment_key = settings.LUFFY_PAYMENT_KEY % (user_id)
            keys = self.conn.scan_iter(luffy_payment_key)
            global_coupon_key = LUffY_GLOBAL_COUPON_KEY % user_id
            global_coupon_dict = self.conn.hgetall(global_coupon_key)
            # print(global_coupon_dict)
            global_dict = {}
            for k, v in global_coupon_dict.items():
                global_dict[k.decode()] = json.loads(v.decode())
            # print(global_dict)
            data_list = []
            for key in keys:
                course_detail = self.conn.hgetall(key)
                course_detail_dict = {}
                for k, v in course_detail.items():
                    course_detail_dict[k.decode()] = json.loads(v.decode())
                    data_list.append(course_detail_dict)
            data_list.append(global_dict)
            print(data_list)
            self.res.data = data_list


        except Exception as e:
            self.res.code = 2000
            self.res.error_msg = "你有问题!!!"
        return Response(self.res.dict)

22、python的短路现象

# print(1 and 2)          #2
# print(1 or 2)            #1
# print(0 or 2)           #2
# print(0 and 2)          #0

23、公钥和私钥 ( rsa)    及摘要(即md5之类)

  公钥和私钥相互加密解密

  1、加密数据

    公钥加密      公钥 sdsdksfhskhlsfnsf7343bskjdbksdkds

           abc ------------------------------------------------------------------->  sdkjfkjfkdsjfhsd736384dbsbsj

    私钥解密     私钥 skjdgkasdgkas9834949aksjdbdakbs

     sdkjfkjfkdsjfhsd736384dbsbsj------------------------------------->  abc  

  2、识别身份

    私钥加密
    公钥解密

参考网址

    www.10tiao.com/html/619/201604/4052184643/3.html

24、支付宝支付接口

  A、引用python的支付接口

pay.py

from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from urllib.parse import quote_plus
from base64 import decodebytes, encodebytes
import json

class AliPay(object):
    """
    支付宝支付接口(PC端支付接口)
    """
    def __init__(self, appid, app_notify_url, app_private_key_path,
                 alipay_public_key_path, return_url, debug=False):
        self.appid = appid
        self.app_notify_url = app_notify_url
        self.app_private_key_path = app_private_key_path
        self.app_private_key = None
        self.return_url = return_url
        with open(self.app_private_key_path) as fp:
            self.app_private_key = RSA.importKey(fp.read())
        self.alipay_public_key_path = alipay_public_key_path
        with open(self.alipay_public_key_path) as fp:
            self.alipay_public_key = RSA.importKey(fp.read())

        if debug is True:
            self.__gateway = "https://openapi.alipaydev.com/gateway.do"
        else:
            self.__gateway = "https://openapi.alipay.com/gateway.do"

    def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
        biz_content = {
            "subject": subject,
            "out_trade_no": out_trade_no,
            "total_amount": total_amount,
            "product_code": "FAST_INSTANT_TRADE_PAY",
            # "qr_pay_mode":4
        }

        biz_content.update(kwargs)
        data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
        return self.sign_data(data)

    def build_body(self, method, biz_content, return_url=None):
        data = {
            "app_id": self.appid,
            "method": method,
            "charset": "utf-8",
            "sign_type": "RSA2",
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "version": "1.0",
            "biz_content": biz_content
        }

        if return_url is not None:
            data["notify_url"] = self.app_notify_url
            data["return_url"] = self.return_url

        return data

    def sign_data(self, data):
        data.pop("sign", None)
        # 排序后的字符串
        unsigned_items = self.ordered_data(data)
        unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
        sign = self.sign(unsigned_string.encode("utf-8"))
        # ordered_items = self.ordered_data(data)
        quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)

        # 获得最终的订单信息字符串
        signed_string = quoted_string + "&sign=" + quote_plus(sign)
        return signed_string

    def ordered_data(self, data):
        complex_keys = []
        for key, value in data.items():
            if isinstance(value, dict):
                complex_keys.append(key)

        # 将字典类型的数据dump出来
        for key in complex_keys:
            data[key] = json.dumps(data[key], separators=(',', ':'))

        return sorted([(k, v) for k, v in data.items()])

    def sign(self, unsigned_string):
        # 开始计算签名
        key = self.app_private_key
        signer = PKCS1_v1_5.new(key)
        signature = signer.sign(SHA256.new(unsigned_string))
        # base64 编码,转换为unicode表示并移除回车
        sign = encodebytes(signature).decode("utf8").replace("\n", "")
        return sign

    def _verify(self, raw_content, signature):
        # 开始计算签名
        key = self.alipay_public_key
        signer = PKCS1_v1_5.new(key)
        digest = SHA256.new()
        digest.update(raw_content.encode("utf8"))
        if signer.verify(digest, decodebytes(signature.encode("utf8"))):
            return True
        return False

    def verify(self, data, signature):
        if "sign_type" in data:
            sign_type = data.pop("sign_type")
        # 排序后的字符串
        unsigned_items = self.ordered_data(data)
        message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
        return self._verify(message, signature)

  B、沙箱环境测试

 沙箱环境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info
from django.shortcuts import render, redirect, HttpResponse
from utils.pay import AliPay
import json
import time
def ali():
    # 沙箱环境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info
    app_id = "2016091100486897"
    # POST请求,用于最后的检测
    notify_url = "http://47.94.172.250:8804/page2/"
    # notify_url = "http://www.wupeiqi.com:8804/page2/"
    # GET请求,用于页面的跳转展示
    return_url = "http://47.94.172.250:8804/page2/"
    # return_url = "http://www.wupeiqi.com:8804/page2/"
    merchant_private_key_path = "keys/app_private_2048.txt"
    alipay_public_key_path = "keys/alipay_public_2048.txt"

    alipay = AliPay(
        appid=app_id,
        app_notify_url=notify_url,
        return_url=return_url,
        app_private_key_path=merchant_private_key_path,
        alipay_public_key_path=alipay_public_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥
        debug=True,  # 默认False,
    )
    return alipay


def page1(request):
    if request.method == "GET":

        return render(request, 'page1.html')
    else:
        money = float(request.POST.get('money'))

        alipay = ali()
        # 生成支付的url
        query_params = alipay.direct_pay(
            subject="Django课程",  # 商品简单描述
            out_trade_no="x2" + str(time.time()),  # 商户订单号
            total_amount=money,  # 交易金额(单位: 元 保留俩位小数)
        )

        pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)

        return redirect(pay_url)


def page2(request):
    alipay = ali()
    if request.method == "POST":
        # 检测是否支付成功
        # 去请求体中获取所有返回的数据:状态/订单号
        from urllib.parse import parse_qs
        body_str = request.body.decode('utf-8')
        post_data = parse_qs(body_str)

        post_dict = {}
        for k, v in post_data.items():
            post_dict[k] = v[0]
        print(post_dict)

        sign = post_dict.pop('sign', None)
        status = alipay.verify(post_dict, sign)
        print('POST验证', status)
        return HttpResponse('POST返回')

    else:
        params = request.GET.dict()
        sign = params.pop('sign', None)
        status = alipay.verify(params, sign)
        print('GET验证', status)
        return HttpResponse('支付成功')