def post(self, request): user = request.user # 用户是否登录 if not user.is_authenticated: return json_status.params_error(message='请先登录') # 接受参数 order_id = request.POST.get('order_id') # 校验参数 if not order_id: return json_status.params_error(message='无效的订单id') try: order = OrderInfo.objects.get(order_id=order_id, user=user, pay_method=2, order_status=1) except OrderInfo.DoesNotExist: return json_status.params_error(message='订单错误') # 调用支付,使用python sdk调用支付宝的支付接口 # 使用https://github.com/fzlee/alipay/ # 初始化 alipay = AliPay( appid="2016092900624781", # 应用appid app_notify_url= "http://www.qmpython.com:8000/user/order/notify/", # 默认回调url app_private_key_path=os.path.join( settings.BASE_DIR, 'shop/payment/app_private_key.pem'), # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥, alipay_public_key_path=os.path.join( settings.BASE_DIR, 'shop/payment/alipay_public_key.pem'), sign_type="RSA2", # RSA 或者 RSA2 debug=True # 默认False,沙箱环境为True ) # 调用支付接口 # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string total_pay = order.total_price + order.transit_price order_string = alipay.api_alipay_trade_page_pay( out_trade_no=order_id, # 订单id total_amount=str(total_pay), # 支付总金额 subject='全民杂货店%s' % order_id, return_url='http://www.qmpython.com:8000/user/order/return/', # 同步通知 notify_url= 'http://www.qmpython.com:8000/user/order/notify/' # 异步通知,可选, 不填则使用默认notify url ) #对于PC网站支付的交易,在用户支付完成之后,支付宝会根据API中商户传入的notify_url, # 通过POST请求的形式将支付结果作为参数通知到商户系统。 # 返回应答 pay_url = "https://openapi.alipaydev.com/gateway.do?" + order_string return json_status.result(data={'pay_url': pay_url})
def post(self, request): '''购物车记录添加''' # 此类视图不用min,直接在这判断,因为如果不登陆用了min则直接跳转去了 if not request.user.is_authenticated: return json_status.params_error(message='请先登录') # 接收数据 sku_id = request.POST.get('sku_id') count = request.POST.get('count') # 数据校验 if not all([sku_id, count]): return json_status.params_error(message='数据不完整') # 校验商品数量 try: count = int(count) except Exception as e: # 数目错误 return json_status.params_error(message='商品数目出错') # 校验商品是否存在 try: sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: # 数目错误 return json_status.params_error(message='商品不存在') # 添加购物车记录 conn = get_redis_connection('default') cart_key = 'cart_{}'.format(request.user.id) # 用hash类型 # 先尝试获取sku_id的值 ->hget cart_key属性 # 如果sku_id在hash中不存在,hget返回None sku_count = conn.hget(cart_key, sku_id) # 获取sku_id的值, 同一个sku_id有多少数量,属于一个购物车 if sku_count: # 累加购物车中商品sku_id的数目 count += int(sku_count) # 校验商品库存 if count > sku.stock: return json_status.params_error(message='商品库存不足') # 设置hash中sku_id对应值 # hset->如果sku_id已经存在,更新数据,如果sku_id不存在,添加数据 conn.hset(cart_key, sku_id, count) # 计算用户购物车商品的条目数 cart_count = conn.hlen(cart_key) # 获取cart_key中多少个sku_id return json_status.result(data={'cart_count': cart_count})
def put(self, request): '''购物车记录添加''' # 此类视图不用min,直接在这判断,因为如果不登陆用了min则直接跳转去了 if not request.user.is_authenticated: return json_status.params_error(message='请先登录') # 接收数据 # 接收数据 json_data = request.body dict_data = json.loads(json_data.decode('utf-8')) sku_id = dict_data.get('sku_id') count = dict_data.get('count') # 数据校验 if not all([sku_id, count]): return json_status.params_error(message='数据不完整') # 校验商品数量 try: count = int(count) except Exception as e: # 数目错误 return json_status.params_error(message='商品数目出错') # 校验商品是否存在 try: sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: # 数目错误 return json_status.params_error(message='商品不存在') # 添加购物车记录 conn = get_redis_connection('default') cart_key = 'cart_{}'.format(request.user.id) # 校验商品库存 if count > sku.stock: return json_status.params_error(message='商品库存不足') conn.hset(cart_key, sku_id, count) return json_status.result()
def delete(self, request): if not request.user.is_authenticated: return json_status.params_error(message='请先登录') # 接收数据 json_data = request.body dict_data = json.loads(json_data.decode('utf-8')) sku_id = dict_data.get('sku_id', '') # 数据校验 # if not sku_id: # return json_status.params_error(message='数据不完整') # 业务处理,删除购物车记录 conn = get_redis_connection('default') cart_key = 'cart_{}'.format(request.user.id) cart_count = conn.hlen(cart_key) if not cart_count: json_status.params_error(message='购物车无数据') if sku_id: # hdel(name,*keys):将name对应的hash中指定key的键值对删除 conn.hdel(cart_key, sku_id) else: # delete(*name) 根据name删除redis中的任意数据类型 conn.delete(cart_key) # 计算用户购物车中商品的总件数{'1': 5, '2':6} ==> 5+6=11 # total_count = 0 # vals = conn.hvals(cart_key) # for val in vals: # total_count += int(val) # 计算sku个数 total_sku = conn.hlen(cart_key) return json_status.result(data={'total_sku': total_sku})
def post(self, request): user = request.user # 用户是否登录 if not user.is_authenticated: return json_status.params_error(message='请先登录') # 接受参数 order_id = request.POST.get('order_id') # 校验参数 if not order_id: return json_status.params_error(message='无效的订单id') try: order = OrderInfo.objects.get(order_id=order_id, user=user, pay_method=2, order_status=1) except OrderInfo.DoesNotExist: return json_status.params_error(message='订单错误') # 调用支付,使用python sdk调用支付宝的支付接口 # 使用https://github.com/fzlee/alipay/ # 初始化 alipay = AliPay( appid="2016092900624781", # 应用appid app_notify_url= "http://www.qmpython.com:8000/user/order/notify/", # 默认回调url app_private_key_path=os.path.join( settings.BASE_DIR, 'shop/payment/app_private_key.pem'), # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥, alipay_public_key_path=os.path.join( settings.BASE_DIR, 'shop/payment/alipay_public_key.pem'), sign_type="RSA2", # RSA 或者 RSA2 debug=True # 默认False,沙箱环境为True ) while True: # 调用支付宝查询接口 result = alipay.api_alipay_trade_query(order_id) #print(result) code = result.get('code') # 请求结果 stauts = result.get('trade_status') # 支付结果 #print(code, type(code),stauts) if code == '10000' and stauts == 'TRADE_SUCCESS': # 支付成功 # 获取支付宝交易号 trade_no = result.get('trade_no') # 更新订单状态 order.trade_no = trade_no order.order_status = 4 # 待评价 #order.save() #print(order.trade_no) #print(order.order_status) order.save(update_fields=['trade_no', 'order_status']) # 返回结果 return json_status.result(message='支付成功') elif code == '40004' or (code == '10000' and stauts == 'WAIT_BUYER_PAY'): # 等待买家付款 import time # 休眠几秒,再次去查询,所以用while循环去调用 time.sleep(10) continue else: # 支付出错 return json_status.params_error(message='支付失败')
def post(self, request): '''订单创建''' user = request.user if not user.is_authenticated: return json_status.params_error(message='请先登录') # 获取参数 json_data = request.body # 校验参数 if not json_data: return json_status.params_error(message='参数错误') dict_data = json.loads(json_data.decode('utf-8')) addr_id = dict_data.get('addr_id') pay_method = dict_data.get('pay_method') sku_ids = dict_data.get('sku_ids') if not all([addr_id, pay_method, sku_ids]): return json_status.params_error(message='数据不完整') from collections import OrderedDict # 创建有序字典 paymethod_dict = OrderedDict(OrderInfo.PAY_METHOD_CHOICES) # 校验支付方式 if pay_method not in paymethod_dict.keys(): # in 判断key是否存在 return json_status.params_error(message='非法支付方式') # 校验地址 try: addr = AccountAddress.objects.get(id=addr_id) except AccountAddress.DoesNotExist: return json_status.params_error(message='地址非法') # 创建订单核心业务 # 订单id: 20190510193640+用户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: # 向tb_order_info表中添加一条记录 order = OrderInfo.objects.create(order_id=order_id, user=user, address=addr, pay_method=pay_method, total_count=total_count, total_price=total_price, transit_price=transit_price) # 用户的订单中有几个商品,需要向tb_order_goods表中加入几条记录 conn = get_redis_connection('default') cart_key = 'cart_{}'.format(user.id) sku_ids = sku_ids.split(',') # split 将字符串分割为列表 for sku_id in sku_ids: for i in range(3): # 获取商品的信息 try: sku = GoodsSKU.objects.get(id=sku_id) # 不加锁查询 # select * from tb_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 json_status.params_error(message='商品不存在') # 从redis中获取用户所要购买的商品的数量 count = conn.hget(cart_key, sku_id) # 判断商品的库存 if int(count) > sku.stock: # 回滚事务 transaction.savepoint_rollback(save_id) return json_status.params_error(message='商品库存不足') # 更新商品库存和销量 # sku.stock -= int(count) # sku.sales += int(count) # 原库存 (数据库隔离级别必须是Read Committed;如果是Repeatable Read,那么多次尝试读取的原库存都是一样的,读不到其他线程提交更新后的数据。 orgin_stock = sku.stock new_stock = orgin_stock - int(count) new_sales = sku.sales + int(count) # update tb_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: # 如果更新失败则返回0条记录,说明有问题 if i == 2: # 尝试的第三次, range(3)==> 0,1,2 transaction.savepoint_rollback(save_id) return json_status.params_error(message='下单失败') continue # 把判断提到前面是防止重复向tb_order_goods表添加记录 # 向tb_order_goods表中添加一条记录 OrderGoods.objects.create(order_id=order.order_id, sku=sku, count=count, price=sku.price) # 累加计算订单商品的总数量和总价格 amount = sku.price * int(count) total_count += int(count) total_price += amount # 如果执行成功,那直接跳转循环,不用再循环 break # 更新商品订单信息表中的总数量和总价格, 前面直接设置的为0是为了方便创建订单信息给订单商品使用外键 order.total_price = total_price order.total_count = total_count order.save() except Exception as e: # 回滚事务 transaction.savepoint_rollback(save_id) return json_status.params_error(message='下单失败') # 提交事务 transaction.savepoint_commit(save_id) # 清除用户购物车中对应的记录 conn.hdel(cart_key, *sku_ids) # 将[1,3,4]拆包为1,3,4 # 返回应答 return json_status.result()
def post(self, request): ''' 1. 获取数据; client_data = request.data 2. 序列化数据; verified_data = ArticleSerializer(data=client_data) 3. 校验数据; if verified_data.is_valid(): article = verified_data.save() return Response(verified_data.data) else: return Response(verified_data.errors) 模拟请求数据: { 'sku_id':1, 'count':2 } Request请求对象: REST framework 引入了一个扩展HttpRequest的请求对象,提供了更灵活的请求解析, Request对象的核心功能是request.data属性,类似于request.POST,但是对于Web APIs更实用 request.POST # 仅可处理表单数据,仅仅用于post请求. request.data # 处理任意数据, 可供 'POST', 'PUT' and 'PATCH' 请求使用. Response响应对象: REST framework 也引入了一个response对象,它是一种TemplateResponse类型,它渲染文本内容, 并根据内容决定返回给客户端的数据类型 return Response(data) # 通过客户端请求返回渲染的内容 ''' # 1. 获取请求数据 sku_id = request.data.get('sku_id') count = request.data.get('count') user_id = request.user.pk # 2. 校验数据 if not all([sku_id, count]): logging.error('数据不完整') return json_status.params_error(message='数据不完整') # 校验商品数量 try: count = int(count) except Exception as e: # 数目错误 logging.error('商品数目出错') return json_status.params_error(message='商品数目出错') # 校验商品是否存在 try: sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: # 数目错误 logging.error('商品不存在') return json_status.params_error(message='商品不存在') # 添加购物车记录 ''' 方案1: redis={ cart_key_useid:{ { sku: sku1, count:100 }, { sku: sku2, count:200 }, } } 这个可以用hash来做,但是嵌套太深 方案2: redis={ cart_user1_sku_1:{ { sku: sku1, count:100 }, }, cart_user1_sku_2:{ { sku: sku, count:200 }, }, } 可以直接用string实现 ''' conn = get_redis_connection('default') cart_key = 'cart_{}'.format(user_id) # 用hash类型 # 先尝试获取sku_id的值 ->hget cart_key属性 # 如果sku_id在hash中不存在,hget返回None sku_count = conn.hget(cart_key, sku_id) # 获取sku_id的值, 同一个sku_id有多少数量,属于一个购物车 if sku_count: # 累加购物车中商品sku_id的数目 count += int(sku_count) # 校验商品库存 if count > sku.stock: logging.error('商品库存不足') return json_status.params_error(message='商品库存不足') # 设置hash中sku_id对应值 # hset->如果sku_id已经存在,更新数据,如果sku_id不存在,添加数据 conn.hset(cart_key, sku_id, count) # 计算用户购物车商品的条目数 cart_count = conn.hlen(cart_key) # 获取cart_key中多少个sku_id return Response(data={'cart_count': cart_count})