• 路飞项目学习


                      路飞项目

    一、项目分析

    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 ----tofrom

      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("
    ", "")
            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('支付成功')
  • 相关阅读:
    取三级分销上下级用户id
    Map集合
    Log4j
    异常
    逻辑运算符
    变量
    变量名命名规则
    命名法
    Nessus
    Nmap扫描工具
  • 原文地址:https://www.cnblogs.com/di2wu/p/10166596.html
Copyright © 2020-2023  润新知