美多商城项目订单和支付模块总结

订单完成

  • 订单结算页面

    • 订单展示用的序列化器
    # 前端需要运费数据和商品信息数据的字典列表,这里使用嵌套序列化器返回数据
    class CartSKUSerializer(serializers.ModelSerializer):
        # 因为商品表没有count字段,所以要定义它
        count=serializers.IntegerField()
        
        class Meta:
            model=SKU
            fields=('id','name', 'default_image_url', 'price', 'count')
            
    class OrderSettlementSerializer(serializers.Serializer):
    	freight = serializers.DecimalField(max_digits=10,decimal_places=2)
        skus = CartSKUSerializer(many=True)
    
    • 视图实现业务逻辑
    class OrderSettlementView(APIView):
        def get(self, request):
            user=request.user
            conn=get_redis_connection('cart')
            cart_dict=conn.hgetall('cart_%s'%user.id)
            cart_list=conn.smembers('cart_selected_%s'%user.id)
            cart={}
            for sku_id in cart_list:
                cart[sku_id]=cart_dict[sku_id]['count']
            skus=SKU.objects.filter(id__in=cart.keys())
            for sku in skus:
                sku.count=cart[sku.id]['count']
            freight=Decimal(10.00)
            serializer=OrderSettlementSerializer({'freight':freight,'skus':skus})
            return Response(serializer.data)
    
  • 保存订单, 视图逻辑由CreateAPIView提供, 我们需要重写序列化器的保存操作

    class SaveOrderView(CreateAPIView):
        serializer_class= SaveOrderSerializer
    
    class SaveOrderSerializer(serializers.ModelSerializer):
        class Meta:
            model = OrderInfo
            fields=('order_id','address','pay_method')
            read_only_fields=('order_id',)
            extra_kwargs={
                'address':{
                    'write_only':True,
                    'required':True,
                },
                'pay_method':{
                    'write_only':True,
                    'required':True
                }
            }
    	def create(self, validated_data):
            # 获取当前下单用户
    		user=self.context['request'].user
        	# 生成订单编号
    		order_id = datetime.now().strftime('%Y%m%d%H%M%S')+('%09s'%user.id)
        	# 保存订单基本信息数据 OrderInfo
    		address=validated_data.get('address')
    		pay_method=validated_data['pay_method']
    		with transaction.atomic():
                save_id=transaction.savepoint()
                try:
                    order =OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal(0),
                    freight=Decimal(10.00),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH'] else OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                    )
        			# 从redis中获取购物车结算商品数据
    			    conn=get_redis_connection('cart')
                	 cart_dict=conn.hgetall('cart_%s'%user.id)
                     cart_list=conn.smembers('cart_selected_%s'%user.id)
                     cart={}
                     for sku_id in cart_list:
                         cart[int(sku_id)]=int(cart_dict[sku_id])
                     skus=SKU.objects.filter(id__in=cart.keys())          
        			# 遍历结算商品:
    				for sku in skus:
                    	 sku_count=cart[sku.id]
            			# 判断商品库存是否充足
    				    origin_stock = sku.stock
                	     origin_sales = sku.sales
                    	 if sku_count > origin_stock:
                             transcation.save_point_rollback(save_id)
                             raise serializers.ValidationError('商品库存不足')
                          
            			# 减少商品库存,增加商品销量
                        new_stock=origin_stock-sku_count
                        new_sales=origin_sales+sku_count
                        sku.stock=new_stock
                        sku.sales=new_sales
                        sku.save()
    				  # 累计商品的SPU 销量信息
                    	sku.goods.total_count+=sku_count
                        sku.goods.save()
                        # 累计订单基本信息的数据
                        order.total_count+=sku_count
                        order.total_amount+=(int(sku_count)*int(sku.price))
                        # 保存订单商品数据
                        OrderInfo.objects.create(
                            order=order,
                            sku=sku,
                            count=sku_count,
                            price=sku.price
                        )
                        order.total_amount+=order.freight
                        order.save()
                    except Exception as e:
                        logger.error(e)
                        transcation.savepoint_rollback(save_id)
                        raise
                    transaction.savepoint_commit(save_id)
                    # 删除的是fields列表
                    conn.hdel('cart_%s'%user.id,*cart_list)
                    conn.srem('cart_selected_%s'%user.id,*cart_list)
                    return order
    
  • 并发处理使用乐观锁, 因为资源竞争的问题, 多用户之前执行程序不具备完整性

    # 乐观锁的原理是死循环和验证数据
    # 在对数据库进行操作时
    for id in sku_list:
        while True:
            # 因为查询集具备缓存和惰性的原因, 不使用查询集
            sku=SKU.objects.get(id=id)
            ...
            # 查询语句的原子性, 先验证数据没有发生变化
            ret=SKU.objects.get(id=sku.id,stock=stock,sales=sales).update(stock=new_stock)
            if ret==0:
                continue
            ...
            # 没有出错就进行下一个sku_id
            break
    # 同时修改事务执行的隔离级别为Read committed
    
  • 发起支付

    class PaymentView(APIView):
        def get(self, request, order_id):
            try:
                order=OrderInfo.objects.get(order_id=order_id,user=request.user,
                                           pay_method=OrderInfo.PAY_METHODS_ENUM['ALIPY'],
                                           status=OrderInfoi.ORDERS_STATUS_ENUM['UNPAID'])
            except Exception as e:
                return Reaponse({'message':'订单信息有错误'})
            # 创建alipay对象
            alipay=AliPay(
                appid=settings.ALIPAY_APPID,
                app_notify_url=None,  # 默认回调url
                app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), 								"keys/app_private_key.pem"),								   alipay_public_key_path=
                os.path.join(os.path.dirname(os.path.abspath(__file__)), 					             "keys/alipay_public_key.pem"), 
                sign_type="RSA2",  # RSA 或者 RSA2
                debug=settings.ALIPAY_DEBUG  # 默认False
                )
            order_string=alipay.api_alipay_trade_page_pay(
                out_trade_no=order_id,
                total_amount=str(order.total_amount),
                subject="美多商城%s" % order_id,
                return_url="http://www.meiduo.site:8080/pay_success.html",
                )
            alipay_url=settings.ALIPAY_URL+order_string
            return Response({'alipay_url':alipay_url})
    
  • 保存支付结果, 回调链接携带从支付宝返回的查询字符串, 然后前端发给后端, 后端做保存操作, 并返回给前端支付宝流水编号.

    class PaymentStatusView(APIView):
        def put(self, request):
            # 将查询字符串转换为字典
            data = request.query_params.dict()
            sign=data.pop('sign')
            # 生成alipay对象
            alipay = AliPay(
                appid=settings.ALIPAY_APPID,
                app_notify_url=None,  # 默认回调url
                app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),             "keys/app_private_key.pem"),
                alipay_public_key_path=
                os.path.join(os.path.dirname(os.path.abspath(__file__)),
                "keys/alipay_public_key.pem"), 
                sign_type="RSA2",  # RSA 或者 RSA2
                debug=settings.ALIPAY_DEBUG  # 默认False
            )
            # 验证是否是支付宝返回的数据
            success=alipay.verify(data,sign)
            if success:
                order_id=data.get('out_trade_no')
                trade_id=data.get('trade_id')
                Payment.objects.create(
                order_id=order_id,
                trade_id=trade_id)	                                                               
                OrderInfo.objects.filter(order_id=order_id)
                .update(status=OrderInfo.ORDER_STATUS_ENUM["UNCOMMENT"])
                return Response({'trade_id': trade_id})
            else:
                return Response({'message': '非法请求'}, status=403)    
    

你可能感兴趣的:(项目学习,框架学习)