zoukankan      html  css  js  c++  java
  • pythondjango框架电商项目订单模块开发_20191125

    python-django框架-电商项目-订单模块开发

    提交订单页面:

    • 在购物车中点击提交订单,就应该到达提交订单页面了,
    • 显示:
    • 1,收获地址,
    • 2,支付方式
    • 3,用户购买的商品信息,数量,小计,
    • 4,总金额,运费,实际付多少,
    • 5,提交订单按钮,
    • 点击提交按钮,需要传递什么?
    • 注意:价格这个是给用户看的,不要传到后台,传了后端也不用,
    • 商品的id要传过去,另外商品的数量,我们也是从redis中拿的,不是页面上的
    • 我们可以把要传的信息放入一个表单,
    • 表单中的checkbox,只有被选中时,值才会被提交,
    • 后端增加一个提交订单的视图,
    • 1,接收数据,
    • 2,遍历用户购买的商品信息,
    • 总的数量,总的价格,
    • 运费,实际开发的时候,是一个子系统,现在我们把这个写死,
    • 实际支付,就是总价格+运费,
    • 获取用户的地址,只要是这个用户的,我们就从数据库查出来,注意页面流出来编辑收获地址的按钮,
    • 组织上下文,
    • 注意:没有登陆的时候是不能进入这个页面的,跳转到登陆页面,

    创建订单前端js:

    • 订单相关的有两张表,订单信息表,订单商品表,
    • 前端的订单提交页面,我们点击提交订单,应该提交哪些数据???
    • 商品的id,商品的运费,商品的收获地址,支付方式,
    • 至于总件数,总金额,这些传了也不会用,
    • 点击提交的时候,使用的是ajax请求,

     创建订单后端的操作

    • 前端有了js提交之后,我们后端就可以去接收这个请求了,
    • 创建订单的核心业务,
    • 1,用户下一个订单,我要往订单信息表中加入一条信息,这个时候的总数量和总金额,都是0,支付编号不用管,有默认值,订单状态一开始都是未支付,
    • 2,往订单商品表中加入多条记录,评论刚刚下订单是没有的,默认是空,
    • 3,下单成功了之后还需要更新商品的库存信息,因为之前都是0
    • 4,把购物车中的商品清除,  
    • 5,返回一个应答,下单成功, 

    订单生成-mysql的事物的概念:

    • 如果一共两个库存,但是两个用户都加了两个商品,一个客户先买了,第二个客户再次去购买,库存就是零了,这个时候怎么办?
    • 我们需要在生成订单的时候判断库存,如果大于库存,我们应该返回商品库存不足,
    • 所以整个创建订单的流程,需要做成一个事务,
    • 什么是mysql事务,要么全都执行,要么全部执行,
    • 特点:
    • 1,原子性,一组事务,要么成功,要么撤回
    • 2,稳定性,如果有非法的数据,可以撤回,
    • 3,事务的隔离性,一个事务的处理结果,如果影响了其他的事物,其他的也会撤回
    • 4,可靠性,事务会保存到日志里面,软硬件崩溃了,恢复之后可以做一个重新执行
    • mysql中事务控制的语句,
    • 1,开启事务, begin; # 开始事务,后面写的语句都是在事务里面,
    • 2,事务的提交 ,commit; # 提交事务
    • 3,事务的回滚,rollback; # 回滚,提交和回滚是事务的两个操作,
    • 还可以创建保存点,叫做标记点,
    • 设置保持点的命令:
    • SAVEPOINT savepoint_name; // 声明一个 savepoint
    • ROLLBACK TO savepoint_name; // 回滚到savepoint,这个点之前的没有回滚,之后的回滚了,
    • RELEASE SAVEPOINT savepoint_name;  // 删除指定保留点
    • 一旦开始了一个事务,
    • 要么是提交了,要么是回滚,才是结束,否则都不是结束,
    • 事务中可以设置保存点,

    django中使用事务,

    • 怎么把django中的是一系列操作放入一个事务里面呢?
    • 我们需要一个django的模块:from django.db import transaction
    • 然后使用    @transaction.atomic,这个装饰器,装饰我们的函数,
    • 把涉及到的数据库操作放入一个事务里面,
    • 你只要一装饰,这个函数就是一个事务,
    • 什么时候使用保存点呢?
    • 对数据库的操作分为两段,我们把核心也为设置一个保存点,
    • # 设置事务保存点,,save_id = transaction.savepoint()
    • # 商品不存在需要回滚,transaction.savepoint_rollback(save_id)
    • # 提交事务,否则不会提交,,transaction.savepoint_commit(save_id)

    订单生成-订单兵法的问题:

    • 举例:用户1要买一个鸡腿,这个鸡腿只有一个了,这是一个进程,
    • 1,要往数据库中添加一条记录, 
    • 2,查询商品鸡腿的信息,然后紧接着往订单商品表加入数据之前,我们进行一个库存的判断,
    • 3,判断没有问题,添加记录,
    • 4,进行商品库存的更新,
    • 如果在用户1买的时候,又有一个人去买了这个鸡腿,这是第二个进程,
    • 然后也执行这个函数,
    • 这两个进程是没有关系的,
    • 多于多进程多线程的时候,调哪一个进程是随机,
    • 假设先调的进程1,判断库存的时候假设是可以的,
    • 现在我们的电脑切换进程了,切换到了用户2,这个时候查询库存也是够的,
    • cpu再次进行切换,换进程1,那就更新库存为0 了
    • cpu再次进行切换,换进程2,去更新库存,这个时候你就会发现只有一个鸡腿,但是你卖出了两份,
    • 什么时候会发生, 就是大量的用户买一个商品的时候,比如秒杀,比如iPhone,比如小米,这时候就会有订单并发的问题,

    订单并发的处理:悲观锁,

    • 卖出的量超过了库存,这种并发问题怎么解决,
    • 有两种解决方法
    • 1,悲观锁,
    • 2,乐观锁。
    • 首先第一种悲观锁,什么是悲观锁,这个就是对应进程中锁的概念,谁拿到锁谁就可以去改,没有拿到的不能改,
    • 悲观锁就是这样,这个里面查询商品的时候我加一个锁,我拿到这个记录就把这条记录锁定,别的进程就拿不到了,拿不到就要等待,拿到了才可以处理,
    • 事务提交或者事务回滚的时候,也就是事务结束的时候,就会把这个锁释放,
    • 这样悲观锁就解决了这个问题,
    • 怎么加锁?
    • 就是查询的时候,加一个for update,
    • select * from df_goods_sku where id=sku_id for update;
    • 在django中怎么去写?
    • sku = GoodsSKU.objects.select_for_update().get(id=sku_id)
    • 在事务中可以写保存点:
    • # 设置事务保存点
    • save_id = transaction.savepoint()
    • transaction.savepoint_rollback(save_id)
    • # 提交事务,否则不会提交
    • transaction.savepoint_commit(save_id)

     订单并发-乐观锁:

    • 在查询数据的时候不加锁,在更新时进行判断,
    • 判断更新时的库存和之前的查出的库存是否一致,
    • 也就是说,我查到的库存是1,我更新的时候库存也是1,那就是没有人对这条数据操作,我就可以操作这个数据了,
    • # 返回受影响的行数,表示1更新成功,返回0表示更新失败
    • res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock) .update(stock=new_stock, sales=new_sales)
    • 根据这个条件查询,更新,要么是1,要么是0,1就是更新成功了,
    • 更新失败了,要回滚
    • 更新失败,说明确实之前被人改了,但是我还是尝试3次,再加一个for循环,
    • #######
    • 模拟两个用户操作同一个商品,使用悲观锁,
    • 1,两个都提交,都是第一次循环,
    • 2,第一个人提交了之后,查出来还是库存没有变这是怎么回事
    • 这是因为事务的隔离性,
    • 有四个隔离级别:
    • 1,读取未提交的内容,
    • 假设有两个事务,A和B,我在事务A中执行了插入语句,但是我还没有提交,但是这个隔离级别,事务B就可以查到事务A为提交的插入语句的内容改变,
    • 这是脏读,dirty read,这是读取未提交的内容,
    • 2,读取提交的内容,
    • 假设有两个事务,A和B,我在事务A中执行了插入语句,但是我还没有提交,这个时候事务B是拿不到内容的,只有事务A提交了,事务B才可以查看到,这是大多数数据库的默认隔离级别,但是不是mysql的
    • 3,可重复读,
    • 这是mysql的默认的隔离级别,假设有两个事务,A和B,我在事务A中执行了插入语句,但是我还没有提交,这个隔离级别,即使你提交了,我还是不拿你提交后的,还是拿到事务A更新之前的,这就是可重复读,
    • 这种会出现的问题就是幻读,在一个事务的两次查询中数据笔数不一致,什么意思?就是数据的列数不一致,
    • 举例:一个事务查询了3列数据,而另一个事务却在此时插入了几条新的数据,先前的事务在接下来的查询中,就会发现有几列数据是我之前没有查到的,这就是幻读,
    • 4,可串行化
    • 这是最高级别的隔离,强制事务进行排序,使事务之间不可能之间相互冲突,从而解决幻读的问题,但是会导致大量的超时现象和锁的竞争,处理效率低
    • ############
    • 之前我们出现的就是因为mysql默认是可重复读,这种我们拿不到上一个事务更新之后的库存,导致我更新是失败的,所以我们要更改mysql的事物隔离级别,改为读取提交的内容,
    • 怎么设置?
    • 找到mysql的日志文件,sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
    •  transaction-isolation = READ-COMMITTED
    • 重启mysql的服务,sudo service mysql restart
    • 这样就可以了,

    订单并发-总结:

    • 在冲突比较少的时候,建议使用乐观锁,我没有加锁,没有释放锁,就减少了开销,提交性能,
    • 冲突比较多的时候,使用悲观锁
    • 乐观锁重复操作的代价比较大,也使用悲观锁,

    用户中心-订单页面:

    • 提交订单页面,点击提交订单之后,提交成功,会进入用户中心的订单查看页面,
    • 用户中心有一个单独的用户中心订单页面,
    • 后端需要一个视图,用来返回订单的信息,
    • 我们在用户中心的订单页面,看到之前下的但是待支付的状态,点击去支付我们就可以支付了,
    • 点击支付会跳转到支付宝的二维码页面,然后登陆就可以支付了,
    • 需要研究一下支付宝支付的内容,

    订单支付代码:

    • 点击订单列表页面,点击去付款,采用post请求,给django网站传递参数,
    • 我们需要给支付宝传参数,我们要使用一个sdk,这样就不用我们自己传参数了,
    • 安装这个包,https://github.com/fzlee/alipay/blob/master/README.zh-hans.md
    • 先卸载一个包:pip uninstall pycrypto
    • 然后安装:pip install python-alipay-sdk --upgrade
    • 安装好了之后,你就有这个包了,你就可以使用了,
    • ########
    • 先搞清楚支付宝的沙箱环境,
    • 进入支付宝点开放平台网站,https://open.alipay.com/
    • 击“开放平台-开发者中心-沙箱环境”。进入沙箱环境页面,系统已经自动为你创建一个应用,在 信息配置 中可以看到应用信息。
    • 二.生成密钥文件
    • 1. 使用OpenSSL
    • openssl
    • 2. 生成私钥
    • genrsa -out app_private_key.pem 2048
    • 3. 生成公钥
    • rsa -in app_private_key.pem -pubout -out app_public_key.pem
    • 4. 退出OpenSSL
    • exit
    • ###########
    • 有了自己的公钥和私钥了,下一步就是要配置,在支付宝的沙箱环境,设置自己的公钥,支付宝就会反复支付宝的公钥,
    • 这个支付宝的公钥,是我们接收到支付宝的内容之后进行解密的,
    • 所以我们要把这个支付宝的公钥保存到我们的项目里面,
    • 我们在order应用下面,把支付宝的公钥,和我们私钥都放到这个文件夹下, 
    • 这样就配置玩了,
    • ##################
    • 怎么使用?
    • 前端点击支付的时候使用ajax+post请求,
    • 需要传递的参数:order_id,
    • 后端查看这个订单,有几个查询条件,订单号带上,是这个用户的,是支付宝支付方式,是待支付的状态的,能查到就是一个有效的订单,
    • 没有问题下一步就开始调用支付宝的接口了,
    • 1,初始化
    • 2,调接口,
    • 3,返回应答,这里就是方法pay_url = 'https://openapi.alipaydev.com/gateway.do?' + order_string
    • 报错:    raise ValueError("RSA key format is not supported")
    • 把接入支付宝的时候的参数改为:app_private_key_string----->  app_private_key_path
    • 报错:    raise ValueError("Not a valid PEM pre boundary")
    • 原来是我的前后的标记多谢了横杠,两边只能是五个,我写了7个,-----END RSA PRIVATE KEY-----注意这个标记的两边必须要是5个,不是5个就会报这个错,
    • 现在通了,
    • 现在登录沙箱环境,查看沙箱账号,买家账号密码

    订单支付-获取支付结果:

    • 因为我们现在不是公网环境,所以支付宝不能返回给我们支付结果,
    • 所以我们自己去查询交易的结果,
    • 获取了支付宝的交易结果之后,给用户一个支付的结果,支付成功,或者失败,
    • 用户浏览器访问我们的网站,然后看支付是否成功,
    • 用户的浏览器什么时候访问我们的地址,
    • 怎么设计??
    • 我们把用户引导到支付页面之后,
    • 检查支付状态,支付成功了,
    • 弹出支付成功,然后刷新页面,把数据库里面的订单状态改掉,

    订单评论:

    • 前端有单独的评论的页面,
    • 一个订单有多个商品,应该有多个评论框,
    • 评论之后提交,订单的状态从去评价变为完成,
    • 在商品详情页面就有一个评价,应该要显示评价, 
  • 相关阅读:
    去除测序reads中的接头:adaptor
    Python学习_13_继承和元类
    Matplotlib初体验
    Python学习_12_方法和类定制
    python+requests接口自动化测试框架实例详解教程
    typeof与instanceof运算符
    闭包与递归函数的区别
    添加SSH
    AndroidStudio常用快捷键总结
    git新建分支
  • 原文地址:https://www.cnblogs.com/andy0816/p/11928105.html
Copyright © 2011-2022 走看看