【Django 天天生鲜项目05】订单(Mysql事务、并发处理、支付宝支付、评论)

本部分涉及订单的生成、并发处理、支付、评论等

关键:MySQL事务、并发处理的悲观锁/乐观锁、支付宝SDK 的使用......

仅作为个人笔记

目录

2.创建订单

3.订单生成

3.1. MySQL事务

3.2. Django中使用事务

3.3. 订单并发问题

5.订单支付

5.1.支付宝开放平台

5.2.网站对接支付宝流程


 

订单

在购物车中点击去结算后,会将选中的商品id、数量、价格等传到订单页。实际上,只需要传用户要购买的商品的id即可,数量等会从redis数据库中获取。

订单页面大致如下:

 

1.订单页面显示的视图

表单中的checkbox只有被选中时值才会被提交。request.POST提交的类型为QueryDict,可以一个名字对应多个值;在视图中request.POST.getlist()来获。

 

 

前端注意对收货地址的默认选中:

        <dl>
            <dt>寄送到:</dt>
            {% for addr in addrs %}
                <dd><input type="radio" name="addr_id" {% if addr.is_default %}checked{% endif %}>{
  {addr.addr}} ({
  {addr.receviver}} 收) {
  {addr.phone}}</dd>
            {% endfor %}
        </dl>

 

2.创建订单

用户点击提交订单时,创建订单。前端必须要传递的参数有:收货地址、支付方式、商品id等。

订单相关的MySQL表有:订单信息表:df_order_info、订单商品表:df_order_goods,二者为一对多的关系;

用户每下一个订单,就向df_order_info表中加入一条记录;用户的订单中有几个商品,就需要向df_order_goods表中加入几条记录。

 

前端js

提交订单使用Ajax提交,关键的js如下:

v
$('#order_btn').click(function() {
            // 获取用户选择的地址id, 支付方式, 要购买的商品id字符串
            addr_id = $('input[name="addr_id"]:checked').val()  // 选择器获取被选中的checkbox的值(收货地址
            pay_method = $('input[name="pay_style"]:checked').val()
            sku_ids = $(this).attr('sku_ids')
            csrf = $('input[name="csrfmiddlewaretoken"]').val()
            params = {'addr_id':addr_id, 'pay_method':pay_method, 'sku_ids':sku_ids,
                        'csrfmiddlewaretoken':csrf}
            // 发起ajax post请求,访问/order/commit, 传递的参数: addr_id pay_method, sku_ids
            $.post('/order/commit', params, function (data) {
                if (data.res==5){
                    alert('创建成功')
                }
                else {
                    alert(data.errmsg)
                }
            })
        });

注意,Ajax使用cdrf的方法:在html中加上:{% csrf_token %};在js中加上:csrf = $('input[name="csrfmiddlewaretoken"]').val()  ;

 

后台view

关键:用户每下一个订单,就向df_order_info表中加入一条记录;用户的订单中有几个商品,就需要向df_order_goods表中加入几条记录。

除了从前端和数据库中能直接获取的参数,还有些参数需要自己组织:订单id order_id(年月日时分秒+用户id)、总数total_count、总价total_price、运费transit_price等。

部分code如下,但下面的code还存在很多待完善的地方(库存判断等):

    def post(self, request):
        ...        
        # 组织参数
        # 订单id: 20171122181630+用户id (年月日时分秒+用户id)
        order_id = datetime.now().strftime('%Y%m%d%H%M%S') + str(user.id)
        transit_price = 10
        # 总数目和总金额(先假设为0存进去,添加完df_order_goods后再更新
        total_count = 0
        total_price = 0

        # todo: 向df_order_info表中添加一条记录
        order = OrderInfo.objects.create(order_id=order_id, ...)

        # todo: 用户的订单中有几个商品,需要向df_order_goods表中加入几条记录
        conn = get_redis_connection('default')
        cart_key = 'cart_%d'%user.id

        sku_ids = sku_ids.split(',')  # 将字符串转回列表,用于遍历
        for sku_id in sku_ids:
            try:
                sku = GoodsSKU.objects.get(id=sku_id)
            except:
                return JsonResponse({'res': 4, 'errmsg': '商品不存在'})

            # 从redis中获取用户所要购买的商品的数量
            count = conn.hget(cart_key, sku_id)

            # todo: 向df_order_goods表中添加一条记录
            OrderGoods.objects.create(order=order, ...)

            # 更新商品的库存和销量
            sku.stock -= int(count)
            sku.sales += int(count)
            sku.save()

            # 累加计算订单商品的总数量和总价格
            amount = sku.price * int(count)
            total_count += int(count)
            total_price += amount

        # 更新订单信息表中的商品的总数量和总价格
        order.total_count = total_count
        order.total_price = total_price
        order.save()

        # 从redis清除用户购物车中对应的记录(注意对列表拆包*sku_ids,如将[1,3]拆为 1,3 传递 )
        conn.hdel(cart_key, *sku_ids)

        # 返回应答
        return JsonResponse({'res': 5, 'message': '创建成功'})

 

3.订单生成

在上面创建订单的视图中,还可能存在库存不足,特别是当两个用户都将商品加入购物车,先后提交了订单,但后提交时库存不足的现象。这时,不应该再为后者创建订单相关的数据表。这是就可以使用MySQL的事务来处理。

3.1. MySQL事务

mysql事务: 一组sql操作,要么都成功,要么都失败。即一组mysql语句,要么执行,要么全部不执行。创建订单的一系列操作,要么都成功,要么都失败。MySQL 事务    https://www.mysqlzh.com/

 

事务的特点

  • 原子性:一组事务,要么成功;要么撤回。
  • 稳定性 :有非法数据(外键约束之类),事务撤回。
  • 隔离性事务独立运行。一个事务处理后的结果,影响了其他事务,那么其他事务会撤回。事务的100%隔离,需要牺牲速度。
  • 可靠性:软、硬件崩溃后,InnoDB数据表驱动会利用日志文件重构修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit 选项 决定什么时候吧事务保存到日志里。

 

事务控制语句

  • BEGIN 或 START TRANSACTION;显式地开启一个事务;
  • COMMITCOMMIT WORK 等价。COMMIT会提交事务,并使已对数据库进行的所有修改称为永久性的;
  • ROLLBACK:与ROLLBACK WORK 等价。回滚会结束用户的务,并撤销正在进行的所有未提交的修改;
  • SAVEPOINT identifier(SAVEPOINT 保存点名);SAVEPOINT允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT
  • RELEASE SAVEPOINT identifier;删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
  • ROLLBACK TO identifier;把事务回滚到标记点;

 

mysql事务隔离级别

SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

  • Read Uncommitted(读取未提交内容)
    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值