订单结算页面
# 前端需要运费数据和商品信息数据的字典列表,这里使用嵌套序列化器返回数据
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)