DAY 86 luffy05

1 课程详情页面
-播放器组件
2 课程章节接口
-所有课程章节全拿回来(过滤:按课程id过滤)
3 支付宝支付
-生成公钥私钥,把公钥配置在支付宝上---》生成一个支付宝公钥
   -支付宝公钥,自己的私钥,放到了项目中
   -API,没有提供sdk,第三方的sdk
   -安装,安装步骤扣代码
  -生成一个对象(一堆参数)
       -调用对象的方法生成支付连接(一堆参数),需要拼连接地址
       -拿到连接地址就可以打开支付宝,付款了
       -两个回调
      -get回调,调前端(传回很多参数),当vue挂载时候,也向后端发送一个请求,携带数据
           -post回调,以它为准,修改订单状态
          -如果刚支付成功,服务挂掉(24小时以内只要启动服务即可,支付宝会发送8次回调)
       -基于sdk封装包
      -

1 支付接口

1 post请求---》携带数据什么样
{courses:[1,2,3],subject:'订单标题',pay_type:'1',total_amount:'199'}
   
   
2 视图类
-自动生成路由,新增类
   -重写create方法
   -写序列化类
  -validate中校验
   -5个步骤(在serializer的validate)
  -校验钱是否正确
       -生成订单号(uuid,分布式id的生成)
       -获取当前登录用户(request.user)
       -生成支付链接(context)
       -入库前准备(把user,订单号放到字典中)
  -入库(重写create方法)
  -入两个库

1.1 视图类

class PayView(GenericViewSet, CreateModelMixin):
   # class PayView(ViewSetMixin,CreateAPIView):
   # 认证类,权限类
   authentication_classes = [JSONWebTokenAuthentication]
   permission_classes = [IsAuthenticated,]
   queryset = Order.objects.all()
   serializer_class = OrderModelSerializer

   def create(self, request, *args, **kwargs):
       serializer = self.get_serializer(data=request.data, context={'request': request})
       # OrderModelSerializer(data=request.data,context={'request':request})
       serializer.is_valid(raise_exception=True)
       self.perform_create(serializer)
       # serializer.save()
       pay_url = serializer.context['pay_url']
       return APIResponse(pay_url=pay_url)

 

 

1.2 序列化类

class OrderModelSerializer(serializers.ModelSerializer):
   # courses:[1,2,3]
   # 转换成courses:[obj1,obj2,obj3]
   courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), many=True)

   class Meta:
       model = Order
       fields = [
           'courses',
           'subject',
           'pay_type',
           'total_amount'
      ]

   def _check_money(self, attrs):
       # 校验钱是否合法
       total_amount = attrs.get('total_amount')
       total = 0
       for course in attrs.get('courses'):
           total += course.price
       if total_amount == total:
           return total_amount
       else:
           raise ValidationError('钱不对')

   def _get_trade_no(self):
       trade_no = str(uuid.uuid4()).replace('-', '')
       return trade_no

   def _get_user(self):
       return self.context['request'].user

   def _get_pay_url(self, subject, trade_no, total_amount):
       from luffyapi.libs.alpay import alipay

       res = alipay.api_alipay_trade_page_pay(
           subject=subject,
           out_trade_no=trade_no,
           total_amount=float(total_amount),  # 转成float类型
           return_url=settings.RETURN_URL,
           notify_url=settings.NOTIFY_URL
      )

       pay_url = pay_settings.GATEWAY + res  # 支付链接
       self.context['pay_url'] = pay_url

   def _pre_model(self, attrs, user, total_amount, trade_no):
       attrs['user'] = user
       attrs['total_amount'] = total_amount
       attrs['out_trade_no'] = trade_no
       # courses需要pop出来,但是不着急

   def validate(self, attrs):
       # -校验钱是否正确
       total_amount = self._check_money(attrs)
       # -生成订单号(uuid,分布式id的生成)
       trade_no = self._get_trade_no()
       # -获取当前登录用户(request.user)
       user = self._get_user()
       # -生成支付链接(context)
       self._get_pay_url(attrs['subject'], trade_no, total_amount)
       # -入库前准备(把user,订单号放到字典中)
       self._pre_model(attrs, user, total_amount, trade_no)
       return attrs

   def create(self, validated_data):
       courses = validated_data.pop('courses')
       # 存订单表
       order = Order.objects.create(**validated_data)

       # 存订单详情表
       for course in courses:
           OrderDetail.objects.create(order=order, course=course, price=course.price, real_price=course.price)

       return order

 

1.2 路由

router=SimpleRouter()

router.register('pay',views.PayView,'pay')
urlpatterns = [

   path('', include(router.urls)),

]

 

2 支付前端

2.2 get回调参数

charset=utf-8&
out_trade_no=36c931b5e7a44cf6a8bd5187862e8fca&
method=alipay.trade.page.pay.return&
total_amount=39.00&
sign=d4KoJ4LIPap%2BmqEGTsfjVMYbml3ndl21vDgkxL0J1Zc2K1LM2Qll7yecg6nuYziwPB0EAo4dnirIBZI7%2F%2BV0zF%2BgB0l0ck%2FIYm4JfMrC1FSAL6wlSHX%2FfxRskFAsUHx%2BIvQ%2FNR6%2FK01KJ7zThRLzn3aC8%2B8gYRn1i3UagZQgKpt%2FwTWnw3jG1Gqsg7C54uW%2Fon7%2FYM3yCTLtxTIhm5vNqSy6ommqLIM%2FkVD0RpU487hQI56Nw4j1t%2F%2FXhHS7TqqTnbg0xRXIY%2FBFbFrw7QwF0JZYpvo0zE5qDugGEWsk%2BiCOmIs0bh6VJPavTHL3%2BO5rEba4F79Lpq8RvxykdoptVw%3D%3D&
trade_no=2021051722001430480501699313&
auth_app_id=2016092000554611&version=1.0&
app_id=2016092000554611&sign_type=RSA2&
seller_id=2088102176466324&
timestamp=2021-05-17%2012%3A21%3A30

2.2 前端

            buy_now(course) {
               //判断是否登录了
               let token = this.$cookies.get('token')
               if (!token) {
                   this.$message({
                       message: '您没有登录',
                       type: 'warning',
                  })
                   return
              }
               //向后端发送请求
               // this.$http.post(this.$BASE_URL + 'order/pay/', data = {}, header = {})
               this.$http({
                       headers: {
                           'Authorization': `jwt ${token}`
                      },
                       method: 'post',
                       url: this.$BASE_URL + 'order/pay/',
                       data: {
                           "courses": [course.id], "subject": course.name, "pay_type": "1", "total_amount": course.price
                      }
                  }
              ).then(res => {
                   console.log(res.data)
                   if (res.data.code == 100) {
                       open(res.data.pay_url, '_self') //在当前页面中打开一个连接
                  }
              })


          },

2.3 支付成功前端

<template>
   <div class="pay-success">
       <!--如果是单独的页面,就没必要展示导航栏(带有登录的用户)-->
       <Header/>
       <div class="main">
           <div class="title">
               <div class="success-tips">
                   <p class="tips">您已成功购买 1 门课程!</p>
               </div>
           </div>
           <div class="order-info">
               <p class="info"><b>订单号:</b><span>{{ result.out_trade_no }}</span></p>
               <p class="info"><b>交易号:</b><span>{{ result.trade_no }}</span></p>
               <p class="info"><b>付款时间:</b><span><span>{{ result.timestamp }}</span></span></p>
           </div>
           <div class="study">
               <span>立即学习</span>
           </div>
       </div>
   </div>
</template>

<script>
   import Header from "@/components/Header"

   export default {
       name: "Success",
       data() {
           return {
               result: {},
          };
      },
       created() {
           // url后拼接的参数:?及后面的所有参数 => ?a=1&b=2
           // console.log(location.search);

           // 解析支付宝回调的url参数
           let params = location.search.substring(1);  // 去除? => a=1&b=2
           let items = params.length ? params.split('&') : [];  // ['a=1', 'b=2']
           //逐个将每一项添加到args对象中
           for (let i = 0; i < items.length; i++) {  // 第一次循环a=1,第二次b=2
               let k_v = items[i].split('=');  // ['a', '1']
               //解码操作,因为查询字符串经过编码的
               if (k_v.length >= 2) {
                   // url编码反解
                   let k = decodeURIComponent(k_v[0]);
                   this.result[k] = decodeURIComponent(k_v[1]);
                   // 没有url编码反解
                   // this.result[k_v[0]] = k_v[1];
              }

          }
           // 解析后的结果
           // console.log(this.result);


           // 把地址栏上面的支付结果,再get请求转发给后端
           this.$http({
               url: this.$BASE_URL + '/order/success/' + location.search,
               method: 'get',
          }).then(response => {
               console.log(response.data);
               alert(response.data.msg)
          }).catch(() => {
               console.log('支付结果同步失败');
          })
      },
       components: {
           Header,
      }
  }
</script>

<style scoped>
  .main {
       padding: 60px 0;
       margin: 0 auto;
       width: 1200px;
       background: #fff;
  }

  .main .title {
       display: flex;
       -ms-flex-align: center;
       align-items: center;
       padding: 25px 40px;
       border-bottom: 1px solid #f2f2f2;
  }

  .main .title .success-tips {
       box-sizing: border-box;
  }

  .title img {
       vertical-align: middle;
       width: 60px;
       height: 60px;
       margin-right: 40px;
  }

  .title .success-tips {
       box-sizing: border-box;
  }

  .title .tips {
       font-size: 26px;
       color: #000;
  }


  .info span {
       color: #ec6730;
  }

  .order-info {
       padding: 25px 48px;
       padding-bottom: 15px;
       border-bottom: 1px solid #f2f2f2;
  }

  .order-info p {
       display: -ms-flexbox;
       display: flex;
       margin-bottom: 10px;
       font-size: 16px;
  }

  .order-info p b {
       font-weight: 400;
       color: #9d9d9d;
       white-space: nowrap;
  }

  .study {
       padding: 25px 40px;
  }

  .study span {
       display: block;
       width: 140px;
       height: 42px;
       text-align: center;
       line-height: 42px;
       cursor: pointer;
       background: #ffc210;
       border-radius: 6px;
       font-size: 16px;
       color: #fff;
  }
</style>

 

3 支付成功两个回调接口

from rest_framework.views import  APIView
from .models import Order
class SuccessView(APIView):
def get(self,request,*args,**kwargs):
# 去订单表查询该订单的状态
out_trade_no=request.query_params.get('out_trade_no')
# 校验这个数据是什么状态
try:
Order.objects.get(out_trade_no=out_trade_no,order_status=1)
return APIResponse(msg='已经支付')
except Exception as e:
return APIResponse(code=101,msg='请稍后刷新该页面再看')


def post(self,request,*args,**kwargs):
# 支付宝回调,数据在哪?
# 取出支付宝回调返回的数据,在request.data中
# 验证签名
# 修改订单状态
# 告诉支付宝成功
try:
print(request.data)
result_data = request.data.dict()
out_trade_no = result_data.get('out_trade_no')
signature = result_data.pop('sign')
from luffyapi.libs import alpay
result = alpay.alipay.verify(result_data, signature)
if result and result_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):
# 完成订单修改:订单状态、流水号、支付时间
# Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1,trade_no=result_data.get('trade_no'),pay_time=result_data.get('timestamp'))
Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1)
# 完成日志记录
logger.warning('%s订单支付成功' % out_trade_no)
return Response('success')
else:
logger.error('%s订单支付失败' % out_trade_no)
except:
pass
return Response('failed')

 

4 阿里云服务器购买

1 内网穿透
-花钱的花生壳
-免费的。。。
-ngrok.exe http 8000
-生成一个域名,访问域名就能访问到你的本地的8000
-自己写
2 项目调整
-前端
-后端
-nginx+uwsgi+django
-uwsgi不能获取静态资源
-动静分离
3 宝塔
-python2+django写了一个项目

3 购买服务器
-使用ssh连接(xshell,finallshell。。。)
-http://www.hostbuf.com/t/988.html
-ssh root@地址 # 输入密码即可

4 上传前端项目的命令
-cd /
-mkdir soft
-cd soft
-yum install lrzsz -y # 上传下载软件
-从本地把前端项目拖到服务器 /soft
-yum install unzip -y # 解压zip的软件
-unzip dist.zip
-看到前端项目解压了

5 拉去后端项目
-yum update -y
-yum -y groupinstall "Development tools" # 包含了git
-yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel psmisc libffi-devel
-git clone https://gitee.com/liuqingzheng/luffy_api_s16.git
-在soft路径下,移动到 /soft/project/luffyapi
-mv luffy_api_s16 project/luffyapi
-到小luffyapi文件夹下创建static文件夹
-mkdir static

 

5 安装mysql

# 下载rpm包
wget http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm

# 安装mysql57
yum -y install mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql-community-server

# 3 启动mysql57并查看启动状态
systemctl start mysqld # 启动mysql服务端
systemctl status mysqld # 查看mysqld的状态

# 4查看默认密码并登录
grep "password" /var/log/mysqld.log
mysql -uroot -p
# 5 修改密码: ,Uhfd*<;f3hp
# 修改root用户密码为 lqz123?
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Lqz12345?';

 

6 安装redis

# 下载redis-5.0.5源码包
wget http://download.redis.io/releases/redis-5.0.5.tar.gz

# 解压
tar -xf redis-5.0.5.tar.gz

# 进入目标文件
cd redis-5.0.5

# 编译源码
make

# 复制环境到指定路径完成安装
cp -r redis-5.0.5 /usr/local/redis

# 配置redis可以后台启动:修改下方内容(你不加也可以)
vim /usr/local/redis/redis.conf

daemonize yes

# 完成配置修改
esc
:wq

# 建立软连接
ln -s /usr/local/redis/src/redis-server /usr/bin/redis-server
ln -s /usr/local/redis/src/redis-cli /usr/bin/redis-cli

# 后台运行redis
>: cd /usr/local/redis
>: redis-server ./redis.conf

# 客户端连接测试redis环境
>: redis-cli
ctrl + c

# 关闭redis服务
>: pkill -f redis -9

 

7 安装python(centos 7.9 可以不装了)

# 下载  Python3.6.7
wget https://www.python.org/ftp/python/3.6.7/Python-3.6.7.tar.xz

# 解压安装包
tar -xf Python-3.6.7.tar.xz

# 进入目标文件
cd Python-3.6.7

# 配置安装路径:/usr/local/python3
./configure --prefix=/usr/local/python3

# 编译并安装
make && make install

# 建立软连接:终端命令 python3,pip3
ln -s /usr/local/python3/bin/python3.6 /usr/bin/python3.6.7
ln -s /usr/local/python3/bin/pip3.6 /usr/bin/pip3.6.7

# 删除安装包与文件:
rm -rf Python-3.6.7
rm -rf Python-3.6.7.tar.xz

# centos 7.9默认自带3.6

 

8 安装虚拟环境

1 pip源的修改,如果是阿里云,就不用了
2 安装uwsgi
pip3.6.7 install uwsgi
ln -s /usr/local/python3/bin/uwsgi /usr/bin/uwsgi
ps aux|grep 进程名 # 查看进程


# 安装依赖
pip3.6.7 install virtualenv
pip3.6.7 install virtualenvwrapper

# 建立虚拟环境软连接
ln -s /usr/local/python3/bin/virtualenv /usr/bin/virtualenv

# 配置虚拟环境:填入下方内容(用户环境变量,root)
vim /root/.bash_profile
# vim ~/.bash_profile
VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3.6.7
source /usr/local/python3/bin/virtualenvwrapper.sh

# 退出编辑状态
esc

# 保存修改并退出
:wq

# 更新配置文件内容
source ~/.bash_profile

# 虚拟环境默认根目录:~/.virtualenvs
# 创建虚拟环境
mkvirtualenv luffy # 创建路飞虚拟环境
deactivate # 退出虚拟环境
workon # 列出所有虚拟环境
workon luffy # 进入到路飞虚拟环境

 

9 安装Nginx


# 下载nginx1.13.7
wget http://nginx.org/download/nginx-1.13.7.tar.gz

# 解压安装包
tar -xf nginx-1.13.7.tar.gz

# 进入目标文件
cd nginx-1.13.7

# 配置安装路径:/usr/local/nginx
./configure --prefix=/usr/local/nginx

# 编译并安装
make && make install

# 建立软连接:终端命令 nginx
ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx

# 删除安装包与文件:
>: cd ~
>: rm -rf nginx-1.13.7
>: rm -rf nginx-1.13.7.tar.xz

# 测试Nginx环境,服务器运行nginx,本地访问服务器ip
nginx
服务器绑定的域名 或 ip:80

# 相关命令
启动
nginx

关闭nginx
nginx -s stop

重启nginx
nginx -s reload

查看端口,强行关闭
ps -aux|grep nginx
kill <pid:进程编号>

 

10 前端部署

# 配置nginx配置文件(访问80端口,直接返回前端页面)
cd /usr/local/nginx/conf
mv nginx.conf nginx.conf.bak
vim nginx.conf

events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
server {
listen 80;
server_name 127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
charset utf-8;
location / {
root /soft/dist; # html访问路径
index index.html; # html文件名称
try_files $uri $uri/ /index.html; # 解决单页面应用刷新404问题
}
}
}

# 保存退出

# 重启nginx
nginx -s reload

 

11 后端部署

#1  进入到后端项目的路径
#2 把当前代码更新到最新
git pull origin master
#3 进入虚拟环境
workon luffy
#4 安装依赖
pip install -r requirements.txt
#5 如果装mysqlclient报错
yum install mysql-devel
yum install python-devel
#yum install python36-devel
pip install mysqlclient

#6 虚拟环境中也要装uwsgi
pip install uwsgi

# 7 使用uwsgi跑django项目(写一个uwsgi的配置文件),项目根路径下
vim luffyai.xml

<uwsgi>
<socket>127.0.0.1:8080</socket>
<chdir>/soft/project/luffyapi/</chdir>
<module>luffyapi.wsgi</module>
<processes>4</processes>
<daemonize>uwsgi.log</daemonize>
</uwsgi>

# 8 配置nginx 转发动态请求
vim /usr/local/nginx/conf/nginx.conf
server {
listen 8000;
server_name 127.0.0.1;
charset utf-8;
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:8080;
uwsgi_param UWSGI_SCRIPT luffyapi.wsgi;
uwsgi_param UWSGI_CHDIR /soft/project/luffyapi/;
}
}

# 数据库设置(创建用户,创建库,迁移数据)
mysql -uroot -p
# 创建库
create database luffy default charset=utf8;

# 创建用户,授权
grant all privileges on luffy.* to 'luffy'@'%' identified by 'Luffy123?';
grant all privileges on luffy.* to 'luffy'@'localhost' identified by 'Luffy123?';
flush privileges;

# 退出mysql
>: quit;

# 表迁移
# 必须在luffy环境下
python manage_pro.py makemigrations
python manage_pro.py migrate

python manage_pro.py createsuperuser
# 亚洲上海,在windows上没问题,linux上 Shanghai 必须大写
# 收集静态文件的配置,必须在迁移后在解开

# 把之前的数据导入(把之前的数据导入)

# 启动uwsgi,重启nginx
nginx -s reload
uwsgi -x luffy.xml

# 收集静态文件(取消配置文件中的static的配置--最后一行)
python manage_pro.py collectstatic
# 使用nginx 转发静态文件(动静分离)
# 动态请求---》uwsgi
# 静态请求---》直接取拿静态文件
vim /usr/local/nginx/conf/nginx.conf

location /static {
alias /soft/project/luffyapi/luffyapi/static;
}

nginx -s reload

# 后台管理的地址是
http://101.133.166.126:8000/admin/
posted @ 2021-06-17 16:21  DEJAVU_ERIC  阅读(26)  评论(0编辑  收藏  举报