python-django框架-电商项目-订单模块开发_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个就会报这个错,
  • 现在通了,
  • 现在登录沙箱环境,查看沙箱账号,买家账号密码

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

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

订单评论:

  • 前端有单独的评论的页面,
  • 一个订单有多个商品,应该有多个评论框,
  • 评论之后提交,订单的状态从去评价变为完成,
  • 在商品详情页面就有一个评价,应该要显示评价, 
上一篇:C++之路进阶——bzoj1821(部落划分)


下一篇:CodeBlocks去掉拼写检查