设计的订单相关的表如下所示:
由于每一个订单中的商品种类与数量都不定,因此单独将订单商品提出为一个表,为一对多的关系。
订单的提交
从购物车页面提交是通过form形式提交的,在checkbox元素中定义参数value并设为对应的商品id,则传递到后端的为一个由选中商品id组成的列表,在后端中的业务流程为:
①获取参数并校验,表单中的checkbox只有被选中时其value才会被提交,若不选中则不提交,若有多个name相同,则使用POST.getlist获取到一个由多个value(checkbox的value)组成的列表;
②从redis中获取该用户购物车的信息,从mysql中获取商品id对应的信息,计算数量与总价格,并将其动态添加到查询到的sku模型类实例中;
③处理运费与实付款;
④组织参数,渲染订单创建的页面。
订单的提交并不复杂,其中重点在于如何将选中的商品id传入后端,以及传入订单创建页面的参数选择。
订单的创建
订单创建的前端页面
共分为三部分:
①收货地址的选择,此处将默认收货地址默认选中,可以在该用户已有的收货地址中选择,也可以跳转到收货地址的编辑页面;
②支付方式的选择,因为此处只做了支付宝的接口因此其他支付方式暂时无法支付,但是可以提交;
③将要提交的商品按条目显示(在订单提交后端中传入),并显示其数量、单价、小计、单位等信息,显示总价、运费、总数量信息;
注:①在订单创建页面不使用form进行post提交,使用ajax post提交,订单创建成功时跳转到订单页面,创建失败时不跳转,显示后端传递的具体信息;
②由于订单中的信息是从购物车中查询到的,这个信息可能与真实情况不符(如商品下架,库存卖完等),因此在订单创建时必须要进行多次校验之后才可以确定订单是否可以创建成功。
订单创建的参数传递
从购物车提交至订单创建页面再到订单创建后端,其传递选中的商品都是通过传递sku_id然后通过查询redis中的用户购物车信息来进行的(并不直接传递具体的商品信息),但由于订单创建功能不与购物车页面交互,因此将被选中商品的sku_ids重构为一个字符串,直接从购物车传递到订单创建页面,并在订单创建页面中动态添加一个属性为sku_ids(此方法常用于在页面中获取变量)并使用ajax post请求发回后端,在订单创建流程中使用。
订单创建的注意事项
(1)关于订单提交失败:由于可能因为各种原因导致的订单无法完成,但订单记录会在数据库中生成,因此使用mysql事务来对订单提交(即把在两个表中的创建打包为一组事务,不允许出现空订单的情况);
(2)关于并发订单的处理:
①悲观锁:在某一条ORM查询语句上加锁(objects.select_for_update()),则多个用户同时进行订单提交时,哪个线程先执行到该语句则获取锁,待事务结束后才会释放,其他线程在执行至此时阻塞等待获取锁,保证了同一时刻只有一个事务在运行;
②乐观锁:查询时不加锁,在变更时对比原数据与当前重新查询的数据,若不一致则失败(即乐观的认为当前没有其他线程在同时进行此过程),一般采用3次循环重复此过程,但由于数据库事务的隔离性,查询到的原数据可能不会更新,因此需要修正数据库事务隔离的级别为read committed(在Django2.0版本中已经自动将所有数据库的事务隔离级别修改为read-committed,因此无需专门修改mysql数据库隔离级别),此时乐观锁可执行;
③使用方式:在冲突较少时使用乐观锁(