电商项目订单模块以及高并发的处理

版权声明:FatPuffer https://blog.csdn.net/qq_42517220/article/details/88781974

当用户点击去结算时,返回订单页面

cart/cart.html

<form method="post" action="{% url 'order:place' %}">
    {% for sku in skus %}
	<ul class="cart_list_td clearfix">
		<li class="col01"><input type="checkbox" name="sku_ids" value="{{ sku.id }}"></li>
		<li class="col02"><img src="{{ sku.image.url }}"></li>
		<li class="col03">{{ sku.name }}<br><em>{{ sku.price }}元/{{ sku.unit }}</em></li>
		<li class="col04">{{ sku.unit }}</li>
		<li class="col05">{{ sku.price }}元</li>
		<li class="col06">
			<div class="num_add">
				<a href="javascript:;" class="add fl">+</a>
				<input type="text" sku_id={{ sku.id }} class="num_show fl" value="{{ sku.count }}">
				<a href="javascript:;" class="minus fl">-</a>	
			</div>
		</li>
		<li class="col07">{{ sku.amount }}元</li>
		<li class="col08"><a href="javascript:;">删除</a></li>
	</ul>
    {% endfor %}

	<ul class="settlements">
        {% csrf_token %}
		<li class="col01"><input type="checkbox" name="" checked=""></li>
		<li class="col02">全选</li>
		<li class="col03">合计(不含运费):<span>¥</span><em>{{ total_price }}</em><br>共计<b>{{ total_count }}</b>件商品</li>
		<li class="col04"><input type="submit" value="去结算"></li>
	</ul>
	</form>

order/urls.py

from django.conf.urls import url
from order.views import OrderPlaceView
urlpatterns = [
    url(r'^place$', OrderPlaceView.as_view(), name='place'),  # 提交订单
]

order/views.py

from django.shortcuts import render, redirect
from django.views.generic import View
from django.core.urlresolvers import reverse
from django.http import JsonResponse

from goods.models import GoodsSKU
from user.models import Address

from django_redis import get_redis_connection
from utils.mixin import LoginRequiredMixin


# /order/palce
class OrderPlaceView(LoginRequiredMixin, View):
    """提价订单显示页面"""
    def post(self, request):
        # 获取当前用户
        user = request.user
        # 获取参数sku_ids
        sku_ids = request.POST.getlist('sku_ids')
        print(sku_ids)
        # 校验参数
        if not sku_ids:
            # 跳转到购物车页面
            return redirect(reverse('cart:show'))

        conn = get_redis_connection('default')
        cart_key = 'cart_%d' % user.id

        # 保存用户购买商的所有品信息
        skus = []

        # 记录用户购买商品的总件数和总价格
        total_count = 0
        total_price = 0

        # 遍历sku_ids获取用户要购买的商品id信息
        for sku_id in sku_ids:
            # 根据商品的id获取商品对象
            sku = GoodsSKU.objects.get(id=sku_id)
            # 获取用户要购买的商品的数量
            count = conn.hget(cart_key, sku_id)
            # 计算商品小计
            amount = sku.price*int(count)
            # 动态给商品对象添加购买数量和小计属性
            sku.amount = amount
            sku.count = count
            skus.append(sku)
            # 累加计算用户购买商品总件数和总价格
            total_count += int(count)
            total_price += amount

        # 商品运费 实际开发中,运费会新建一个子系统
        transit_price = 10

        # 实付款
        total_pay = total_price + transit_price

        # 获取用户收件地址
        addrs = Address.objects.filter(user=user)

        # 组织上下文
        sku_ids = ','.join(sku_ids)
        context = {
            "skus": skus,
            "total_count": total_count,
            "total_price": total_price,
            "transit_price": transit_price,
            "total_pay": total_pay,
            "addrs": addrs,
            "sku_ids": sku_ids
        }

        # 使用模板
        return render(request, 'place_order.html', context)

当用户点击提交订单时,会创建订单:

order/place_order.html

<h3 class="common_title">确认收货地址</h3>

	<div class="common_list_con clearfix">
		<dl>
			<dt>寄送到:</dt>
            {% for addr in addrs %}
			<dd><input type="radio" name="" checked="">{{ addr.addr }} ({{ addr.receiver }} 收) {{ addr.phone }}</dd>
		    {% endfor %}
        </dl>
		<a href="{% url 'user:address' %}" class="edit_site">编辑收货地址</a>

	</div>
	
	<h3 class="common_title">支付方式</h3>	
	<div class="common_list_con clearfix">
		<div class="pay_style_con clearfix">
			<input type="radio" name="pay_style" value="1" checked>
			<label class="cash">货到付款</label>
			<input type="radio" name="pay_style" value="2">
			<label class="weixin">微信支付</label>
			<input type="radio" name="pay_style" value="3">
			<label class="zhifubao"></label>
			<input type="radio" name="pay_style" value="4">
			<label class="bank">银行卡支付</label>
		</div>
	</div>

	<h3 class="common_title">商品列表</h3>
	
	<div class="common_list_con clearfix">
		<ul class="goods_list_th clearfix">
			<li class="col01">商品名称</li>
			<li class="col02">商品单位</li>
			<li class="col03">商品价格</li>
			<li class="col04">数量</li>
			<li class="col05">小计</li>		
		</ul>
        {% for sku in skus %}
		<ul class="goods_list_td clearfix">
			<li class="col01">{{ forloop.counter }}</li>
			<li class="col02"><img src="{{ sku.image.url }}"></li>
			<li class="col03">{{ sku.name }}</li>
			<li class="col04">{{ sku.unite }}</li>
			<li class="col05">{{ sku.price }}元</li>
			<li class="col06">{{ sku.count }}</li>
			<li class="col07">{{ sku.amount }}元</li>
		</ul>
        {% endfor %}
	</div>

	<h3 class="common_title">总金额结算</h3>

	<div class="common_list_con clearfix">
		<div class="settle_con">
			<div class="total_goods_count"><em>{{ total_count }}</em>件商品,总金额<b>{{ total_price }}元</b></div>
			<div class="transit">运费:<b>{{ transit_price }}元</b></div>
			<div class="total_pay">实付款:<b>{{ total_pay }}元</b></div>
		</div>
	</div>

	<div class="order_submit clearfix">
        {% csrf_token %}
		<a href="javascript:;" sku_ids="{{ sku_ids }}" id="order_btn">提交订单</a>

	</div>	

{% block bottom %}
	<div class="popup_con">
		<div class="popup">
			<p>订单提交成功!</p>
		</div>
		
		<div class="mask"></div>
	</div>
		<div class="mask"></div>
	</div>
{% endblock bottom %}


	<script type="text/javascript" src="js/jquery-1.12.2.js"></script>
	<script type="text/javascript">
		$('#order_btn').click(function() {
		    // 获取用户选择的地址,支付方式,购买商品id字符串
            addr_id = $('inpiut[name="addr_id"]:checked').val()
            pay_method = $('input[name="pay_style"]').val()
            sku_ids = $(this).attr('sku_ids')
            csrf = $('input[name="csrfmiddlewaretolen"]').val()

            // 组织参数
            params = {"addr_id": addr_id, "pay_method": pay_method, "sku_ids": sku_ids, "csrfmiddlewaretolen": csrf}

            // 发送ajax post请求,访问/order/commit,传递参数:用户地址,支付方式,购买商品id
            $.post('{% url "order:commit" %}', params, function (data) {
                if(data.res == 7){
                    // 创建成功
                    alert("创建成功")
                }else{
                    // 创建失败,返回错误信息
                    alert(data.errmsg)
                }
            })

			localStorage.setItem('order_finish',2);

			$('.popup_con').fadeIn('fast', function() {

				setTimeout(function(){
					$('.popup_con').fadeOut('fast',function(){
						window.location.href = 'index.html';
					});	
				},3000)
				
			});
		});
	</script>

order/urls.py

from django.conf.urls import url
from order.views import OrderCommitView
urlpatterns = [
    url(r'^place$', OrderCommitView.as_view(), name='place'),  # 提交订单
]

order/views.py

使用悲观锁处理订单高并发

from django.shortcuts import render, redirect
from django.views.generic import View
from django.core.urlresolvers import reverse
from django.db import transaction
from django.http import JsonResponse
from datetime import datetime

from goods.models import GoodsSKU
from user.models import Address
from order.models import OrderInfo, OrderGoods

from django_redis import get_redis_connection


# /order/commit
# 前端传递参数:订单地址(addr_id),支付方式(pay_method),用户购买商品id字符串
# mysql事物,一组sql操作,要么都成功,要么都失败
# 高并发:秒杀
# 支付宝支付
# 悲观锁处理
class OrderCommitView1(View):
    """订单创建"""
    # mysql事物操作装饰器
    @transaction.atomic
    def post(self, request):
        # 判断用户是否登录
        user = request.user
        if not user.is_authenticated():
            return JsonResponse({"res": 0, "errmsg": "请登录"})

        # 接收参数
        addr_id = request.POST.get('addr_id')
        pay_method = request.POST.get('pay_method')
        sku_ids = request.POST.get('sku_ids')

        # 校验参数
        if not all([addr_id, pay_method, sku_ids]):
            return JsonResponse({"res": 1, "errmsg": "参数不完整"})

        # 校验支付方式
        if pay_method not in OrderInfo.PAY_METHODS.keys():
            return JsonResponse({"res": 2, "errmsg": "非法的支付方式"})

        # 校验地址
        try:
            addr = Address.objects.get(id=addr_id)
        except Address.DoesNotExist:
            return JsonResponse({"res": 3, "errmsg": "地址不存在"})

        # todo: 创建订单核心业务

        # 组织参数
        # 订单id:20190217203835+用户id
        order_id = datetime.now().strftime('%Y%m%d%H%M%S') + str(user.id)

        # 运费
        transit_price = 10

        # 商品总数目和总价格
        total_count = 0
        total_price = 0

        # 设置事物保存点
        save_id = transaction.savepoint()

        try:
            # todo: 向订单信息表(df_order_info)中添加一条数据
            order = OrderInfo.objects.create(order_id=order_id,
                                             user=user,
                                             pay_method=pay_method,
                                             total_price=total_price,
                                             total_count=total_count,
                                             transit_price=transit_price)

            # 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:
                    # select * from df_goods_sku where id=sku_id for update;
                    sku = GoodsSKU.objects.select_for_update().get(id=sku_id)
                except GoodsSKU.DoesNotExist:
                    # 商品不存在,进行回滚到事物点
                    transaction.savepoint_rollback(save_id)
                    return JsonResponse({"res": 4, "errmsg": "商品不存在"})

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

                # todo: 判断商品库存
                if int(count) > sku.stock:
                    # 商品库存不足,进行回滚到事物点
                    transaction.savepoint_rollback(save_id)
                    return JsonResponse({"res": 5, "errmsg": "商品库存不足"})

                # todo: 向订单商品表(order_goods)中添加一条数据
                OrderGoods.objects.create(order=order,
                                          sku=sku,
                                          count=count,
                                          price=sku.price)

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

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

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

        except Exception as e:
            transaction.savepoint_rollback(save_id)
            return JsonResponse({"res": 6, "errmsg": "下单失败"})

        # 提交事物
        transaction.savepoint_commit(save_id)

        # todo: 清除用户购物车中对应的记录
        conn.hdel(cart_key, *sku_ids)

        # 返回应答
        return JsonResponse({"res": 7, "errmsg": "创建订单成功"})

使用乐观锁处理订单高并发

from django.shortcuts import render, redirect
from django.views.generic import View
from django.core.urlresolvers import reverse
from django.db import transaction
from django.http import JsonResponse
from datetime import datetime

from goods.models import GoodsSKU
from user.models import Address
from order.models import OrderInfo, OrderGoods

from django_redis import get_redis_connection


# 乐观锁处理
class OrderCommitView(View):
    """订单创建"""
    # mysql事物操作装饰器
    @transaction.atomic
    def post(self, request):
        # 判断用户是否登录
        user = request.user
        if not user.is_authenticated():
            return JsonResponse({"res": 0, "errmsg": "请登录"})

        # 接收参数
        addr_id = request.POST.get('addr_id')
        pay_method = request.POST.get('pay_method')
        sku_ids = request.POST.get('sku_ids')

        # 校验参数
        if not all([addr_id, pay_method, sku_ids]):
            return JsonResponse({"res": 1, "errmsg": "参数不完整"})

        # 校验支付方式
        if pay_method not in OrderInfo.PAY_METHODS.keys():
            return JsonResponse({"res": 2, "errmsg": "非法的支付方式"})

        # 校验地址
        try:
            addr = Address.objects.get(id=addr_id)
        except Address.DoesNotExist:
            return JsonResponse({"res": 3, "errmsg": "地址不存在"})

        # todo: 创建订单核心业务

        # 组织参数
        # 订单id:20190217203835+用户id
        order_id = datetime.now().strftime('%Y%m%d%H%M%S') + str(user.id)

        # 运费
        transit_price = 10

        # 商品总数目和总价格
        total_count = 0
        total_price = 0

        # 设置事物保存点
        save_id = transaction.savepoint()

        try:
            # todo: 向订单信息表(df_order_info)中添加一条数据
            order = OrderInfo.objects.create(order_id=order_id,
                                             user=user,
                                             pay_method=pay_method,
                                             total_price=total_price,
                                             total_count=total_count,
                                             transit_price=transit_price)

            # 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:
                for i in range(3):
                    # 获取商品信息
                    try:
                        sku = GoodsSKU.objects.get(id=sku_id)
                    except GoodsSKU.DoesNotExist:
                        # 商品不存在,进行回滚到事物点
                        transaction.savepoint_rollback(save_id)
                        return JsonResponse({"res": 4, "errmsg": "商品不存在"})

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

                    # todo: 判断商品库存
                    if int(count) > sku.stock:
                        # 商品库存不足,进行回滚到事物点
                        transaction.savepoint_rollback(save_id)
                        return JsonResponse({"res": 5, "errmsg": "商品库存不足"})

                    # todo: 更新商品的库存和销量
                    orgin_stock = sku.stock
                    new_stock = orgin_stock - int(count)
                    new_sales = sku.sales + int(count)

                    # 更新商品库存
                    # update df_goods_sku set stock=new_stock, sales=new_sales where id=sku_id and stock=orgin_stock
                    # 返回受影响的行数
                    res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock).update(stock=new_stock, sales=new_sales)
                    if res == 0:
                        # 尝试的第三次
                        if i == 2:
                            transaction.savepoint_rollback(save_id)
                            return JsonResponse({"res": 8, "errmsg": "下单失败"})
                        continue

                    # todo: 向订单商品表(order_goods)中添加一条数据
                    OrderGoods.objects.create(order=order,
                                              sku=sku,
                                              count=count,
                                              price=sku.price)

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

                    # 跳出循环
                    break

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

        except Exception as e:
            transaction.savepoint_rollback(save_id)
            return JsonResponse({"res": 6, "errmsg": "下单失败"})

        # 提交事物
        transaction.savepoint_commit(save_id)

        # todo: 清除用户购物车中对应的记录
        conn.hdel(cart_key, *sku_ids)

        # 返回应答
        return JsonResponse({"res": 7, "errmsg": "创建订单成功"})

猜你喜欢

转载自blog.csdn.net/qq_42517220/article/details/88781974