路飛項目學習

                  路飛項目

1、項目分析

1、Vue

2、restframework

3、路飛邏輯

  (1) 搭建環境    vue_cli     Django

      建立於課程相關的表結構

  (2)建立課程組件,展現課程信息(課程詳情)

  (3)登陸驗證

  (4)購物車(redis)

  (5)結算中心

  (6)支付寶接口

  (7)支付應用

2、思路

  利用先後端分離的思想開發javascript

  一、前端服務器       Vue_cli  (express ,  vue  ,  webpack(在VUE要build的時候使用))         css

      Vue_cli是一個腳手架,裏面內涵一套系統; html

      JS裏有個相似django的web框架,express,裏面有專門模塊來receive,send,可是中間的過程交給VUE來作,由於VUE是處理數據的框架前端

    做用: 請求數據,展現頁面vue

  二、後端口服務器    Django (restframework)java

3、搭建環境

  一、vue create Xxxxx,手動編輯時,須要明白的是最後一步將項目建立另外一名,是爲了後續建立項目時選中別名繼續引用其配置python

  二、須要明白  mysql

    (1)、後續開發大多數在src文件linux

    (2)、vue是單頁面開發,只有index.html一個html,其餘都是組件webpack

    (3)、而後到main.js

    (4)、App.vue

    (5)、父子通訊,一層嵌一層

  三、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>

  四、先後端通訊用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工程,中間件去解決跨域問題

一、APIView
二、跨域問題
  obj =
HttpResponse(json.dumps(['紅樓夢','西遊記'],ensure_ascii=False))
  obj["Access-Control-Allow-Origin"]="*"
  return obj

 4、項目實戰

一、跨域問題處理

  前端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"

二、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>

三、用戶進入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)

四、序列化組件覆蓋字段的功能

   有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

五、若想實現字體樣式變化,如居左顯示

  能夠再coursedetail.vue的

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

六、序列化組件的多對多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

 七、課程詳情之頁面切換,(在推薦課程裏,點下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>

  八、圖片添加,使得前端能訪問到圖片

  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,能夠在全局裏設置

                });



            }

        },

九、引入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);

 十、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()

十一、價格策略的展現

  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>

 十二、在作登錄頁面時候,遇到跨域複雜請求的問題

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 () {
// 發生錯誤執行函數
})

}
}

}

1三、持續完善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)

 1四、登陸驗證、登陸狀態保存-----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)

1五、登陸驗證優化

  通過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>

1六、爲何使用redis

  A、他比數據庫要快、靈活,像好比一些最終用戶未下單的存放在購物車裏的數據無需專門像mysql同樣建表存儲

  B、最終下單的數據支持持久化,不像memecache只能在內存裏操做,速度雖然快,可是沒法持久化

 redis
      
     -- 數據庫 (mysql)    
     -- 非關係型數據庫
     -- 持久化    
     
下載redis

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

 1七、登陸認證類

  搭建購物車時候,一個查看、一個提交表單數據均須要登陸認證,而後判斷數據提交是否合法、在數據庫內等等,

再而後保存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測試下是否登陸驗證作好

1八、購物車接口設計

   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")))

1九、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等其它平臺的接口和設計

 2一、結算接口設計

  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)

2二、python的短路現象

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

2三、公鑰和私鑰 ( rsa)    及摘要(即md5之類)

  公鑰和私鑰相互加密解密

  1、加密數據

    公鑰加密      公鑰 sdsdksfhskhlsfnsf7343bskjdbksdkds

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

    私鑰解密     私鑰 skjdgkasdgkas9834949aksjdbdakbs

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

  2、識別身份

    私鑰加密
    公鑰解密

參考網址

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

2四、支付寶支付接口

  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('支付成功')
相關文章
相關標籤/搜索