订单接口 多方式支付 订单celery回退
1.前端数据
2.后端接口
1.
#订单号 import time ,random def get_order_id(): st="012345679qwertyui" order_id=str(time.strftime("%Y%m%d%h%M%S"))+"".join(random.sample(st,5)) return order_id from datetime import datetime from pro_celery.celery import del_order def add_task(order_id,seconds=5): ctime = datetime.now() #获取当前时间 utc_ctime = datetime.utcfromtimestamp(ctime.timestamp()) from datetime import timedelta time_delay = timedelta(seconds=seconds) #延迟执行任务时间 task_time = utc_ctime + time_delay #执行延迟任务 result = del_order.apply_async(args=[order_id, ], eta=task_time)
2.
import time from app01.wx import settings class Wxpay: def pay(self,order_data): self.order_id = order_data["order_id"] self.open_id = order_data['open_id'] self.ip = order_data['ip'] #自定义函数获取支付信息字符串 data_body = self.get_body_data() #向微信服务器发送支付信息 import requests url = "https://api.mch.weixin.qq.com/pay/unifiedorder" response = requests.post(url, data_body.encode("utf-8"), headers={'content-type': "application/xml"}) #自定义函处理返回数据 res_dict = self.xml_to_dic(response.content) timeStamp = str(int(time.time())) #二次签名 paySign = self.get_pay_sign(res_dict, timeStamp) data_dic = { 'timeStamp': timeStamp, 'nonceStr': res_dict['nonce_str'], 'package': f"prepay_id={res_dict['prepay_id']}", 'signType': 'MD5', "paySign": paySign, } return data_dic #二次签名 def get_pay_sign(self, res_dict, timeStamp): data_dic = { 'appId': res_dict['appid'], 'timeStamp': timeStamp, #时间戳 'nonceStr': res_dict['nonce_str'], #随机字符串 'package': f"prepay_id={res_dict['prepay_id']}", #数据包 "signType": "MD5" #签名类型 } sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)]) sign_str = f"{sign_str}&key={settings.pay_apikey}" import hashlib md5 = hashlib.md5() md5.update(sign_str.encode("utf-8")) sign = md5.hexdigest() return sign.upper() #处理微信返回xml数据 def xml_to_dic(self, xml_data): import xml.etree.ElementTree as ET ''' xml to dict :param xml_data: :return: ''' xml_dict = {} root = ET.fromstring(xml_data) for child in root: xml_dict[child.tag] = child.text return xml_dict #随机字符串 def get_random(self): import random data = "123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP" nonce_str = "".join(random.sample(data, 30)) return nonce_str #签名 def get_sign(self): data_dic = { "nonce_str": self.nonce_str, "out_trade_no": self.out_trade_no, "spbill_create_ip": self.spbill_create_ip, "notify_url": self.notify_url, "openid": self.open_id, "body": self.body, "trade_type": "JSAPI", "appid": self.appid, "total_fee": "1", "mch_id": self.mch_id } sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)]) sign_str = f"{sign_str}&key={settings.pay_apikey}" import hashlib md5 = hashlib.md5() md5.update(sign_str.encode("utf-8")) sign = md5.hexdigest() return sign.upper() #支付信息字符串 def get_body_data(self): self.appid = settings.AppId self.mch_id = str(settings.pay_mchid) #商户号 self.nonce_str = self.get_random() #随机字符串 self.out_trade_no = self.order_id #订单号 self.spbill_create_ip = self.ip #终端ip self.notify_url = "https://www.test.com" #异步回调地址 self.body = "老男孩学费" #商品描述 self.sign = self.get_sign() #签名(自定义函数) #格式替换 body_data = f""" <xml> <appid>{self.appid}</appid> <mch_id>{self.mch_id}</mch_id> <nonce_str>{self.nonce_str}</nonce_str> <sign>{self.sign}</sign> <body>{self.body}</body> <out_trade_no>{self.out_trade_no}</out_trade_no> <total_fee>1</total_fee> <spbill_create_ip>{ self.spbill_create_ip}</spbill_create_ip> <notify_url>{self.notify_url}</notify_url> <openid>{self.open_id}</openid> <trade_type>JSAPI</trade_type> </xml>""" return body_data
3.
from rest_framework.views import APIView from rest_framework.response import Response from django.core.cache import cache from app01 import models import hashlib,time from django.db import transaction from app01.comment import func from django import forms import importlib #form组件(校验前端订单数据数据) class OrderForm(forms.Form): phone = forms.CharField( error_messages={ "required": "手机号不能为空" }, # 调用Form组件中的验证器来校验手机号 # validators=[RegexValidator(r'1[1-9][0-9]{9}', '手机号格式不正确')], ) token = forms.CharField( error_messages={ "required": "token不能为空" }) province=forms.CharField( error_messages={ "required": "省份不能为空" }) city = forms.CharField(error_messages={ "required": "城市不能为空" }) county = forms.CharField(error_messages={ "required": "县/区不能为空" }) address = forms.CharField(error_messages={ "required": "详细地址不能为空" }) name = forms.CharField(error_messages={ "required": "姓名不能为空" }) #创建订单接口 class Creat(APIView): @transaction.atomic def post(self,request): #获取前端数据form组件校验 param=request.data form_obj=OrderForm(param) if form_obj.is_valid() and param['buy_list']: if request.META.get("HTTP_X_FORWARDED_FOR"): host_ip = request.META["HTTP_X_FROWARDED_FOR"] else: host_ip = request.META["REMOTE_ADDR"] #获取redis获取openID的拼接数据 user_cache=cache.get(param['token']) if user_cache: #获取openid openid=user_cache.split("&")[0] user_data=models.Wxuser.objects.filter(openid=openid).first() #创建订单字典 order_data = {"consignee_mobile": param['phone'], 'consignee_name': param['name'], 'wxuser_id': user_data.id, "memo": param['remark'], "consignee_area":f"{param['province']},{param['city']},{param['county']}", "consignee_address":param['address'] , } #获取前端商品字典,去数据库查出所有商品 buy_list=param['buy_list'] goods_key=list(buy_list.keys()) all_product=models.Product.objects.filter(product_id__in=goods_key) #调用自定义函数产生订单号,同时也是主键 order_data['order_id']=func.get_order_id() order_data['order_total']=0 #总价 order_data['quantity']=0 #商品数 #开启库存 sid=transaction.savepoint() #循环每个商品对象 for product in all_product: product.product_id=str(product.product_id) #主键 order_data['order_total']+=product.price*buy_list[product.product_id] #总价 order_data['quantity']+=buy_list[product.product_id] #商品数 #创建子订单 for i in range(3): #跨库存表,查询当前库存 stock=product.stock.quantity #当前减轻购买的数量,等于剩余的库存 new_stock=stock-buy_list[product.product_id] #判断库存是否足够 if new_stock<0: #回滚 transaction.savepoint_rollback(sid) #如果库存不住够,我们直接返回 return Response({"code":203,"msg":f"{product.name}库存不足"}) #乐观锁更新库存表库存 res=models.Stock.objects.filter(quantity=stock,stock_id=product.stock.stock_id).update(quantity=new_stock) #判断影响行数 if not res: if i==2: transaction.savepoint_rollback(sid) return Response({"code": 203, "msg": f"创建订单失败"}) else: continue else: break #更新商品表商品销量 new_buy_count = product.buy_count + buy_list[product.product_id] models.Product.objects.filter(product_id=product.product_id).update(buy_count=new_buy_count) #子订单创建 order_item_data={'order_id': order_data['order_id'], 'product_id': product.product_id, \ "name": product.name, "image": product.image, "price": product.price, \ "nums": buy_list[product.product_id], "brief": product.brief} models.Order_items.objects.create(**order_item_data) #创建订单 models.Order.objects.create(**order_data) #多种方式支付 pay_methon="Wxpay" try: pay_file=importlib.import_module(f"app01.Pay.{pay_methon}") #动态导入,路径名拿到相关py文件模块 pay_class=getattr(pay_file,pay_methon) #反射取出支付类 order_data['open_id'] = openid order_data['ip']=host_ip data=pay_class().pay(order_data) #支付类实例化传入订单数据。调用支付方法 except: transaction.savepoint_rollback(sid) return Response({"code": 200, "msg": "未知的支付方式" }) transaction.savepoint_commit(sid) #celer延迟任务订单回退 func.add_task(order_data['order_id']) #返回支付链接 return Response({"code": 200, "msg": "ok" ,"data":data}) else: return Response({"code":202,"msg":"token已过期"}) else: return Response({"code":201,"msg":"缺少参数"}) #多种方式支付回调 class Notity(APIView): def post(self,request,paymethon): ##动态导入,路径名拿到相关py文件模块 pay_file = importlib.import_module(f"app01.Pay.{paymethon}") # 反射取出支付类 pay_class = getattr(pay_file, paymethon) # 支付类实例化。调用回调方法 data=pay_class().notity(request.data) #更新订单状态 if data['status']=="success": models.Order.objects.filter(order_id=data['order']).updata(pay_status=1)
4.
1.settings.py AppId="" AppSecret="" code2Session="https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code" pay_mchid ='' pay_apikey = '' 2.wx_login.py import requests from app01.wx import settings def login(code): code_url = settings.code2Session.format(settings.AppId, settings.AppSecret, code) response = requests.get(code_url) json_response = response.json() if json_response.get('session_key'): return json_response else: return False
5.
import celery import time # broker='redis://127.0.0.1:6379/2' 不加密码 backend='redis://127.0.0.1:6379/1' broker='redis://127.0.0.1:6379/2' cel=celery.Celery('test',backend=backend,broker=broker) import os, sys import django BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # 定位到你的django根目录 # sys.path.append(os.path.join(BASE_DIR, "app01")) sys.path.append(os.path.abspath(BASE_DIR)) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wxshop.settings") django.setup() from django.db import transaction @cel.task @transaction.atomic def del_order(order_id): ''' 1 拿订单查询,订单号,是否支付,活跃 2 判断data是否有 :param order_id: :return: ''' from app01 import models data=models.Order.objects.filter(order_id=order_id,pay_status=0,status="active").first() #主订单 if data: item_data=models.Order_items.objects.filter(order_id=order_id).values("product","nums") #子订单们 # [{product:1,nums:3}] all_product_dict = { k['product']:k['nums'] for k in item_data} #子订单的商品id和数量{1:3,2:1} all_product_id =list(all_product_dict.keys()) #所有商品id products_all=models.Product.objects.filter(product_id__in=all_product_id) #所有商品们 sid=transaction.savepoint() #循环出每个商品 for product in products_all: for i in range(3): stock=product.stock.quantity #数据库商品库存 new_stock=stock+all_product_dict[product.product_id] #数据库商品库存+订单每个商品数量 new_buy_count=product.buy_count-all_product_dict[product.product_id] #商品销量——订单每个商品数量 res=models.Stock.objects.filter(quantity=stock,stock_id=product.stock).update(quantity=new_stock) #跟新库存 if not res: if i==2: from app01.comment import func transaction.savepoint_rollback(sid) func.add_task(order_id,1) return else: continue else: break models.Product.objects.filter(product_id=product.product_id).update(buy_count=new_buy_count) #更新商品销量 row=models.Order.objects.filter(order_id=order_id,pay_status=0).update(status="dead") #更新订单状态为作废 if row: transaction.savepoint_commit(sid) else: transaction.savepoint_rollback(sid)
1.查看所有某分类下的所有商品
1.路由 path("goods/list",Goods.List.as_view()), 2.表 #分类表 class Category(models.Model): cat_id=models.AutoField(primary_key=True) category_name=models.CharField(max_length=50) #自关联 parent=models.ForeignKey(to='Category', to_field='cat_id', related_name="Category", on_delete=models.CASCADE, db_constraint=False,blank=True,null=True) #排序 p_order=models.IntegerField(default=0) is_show =models.BooleanField(default=1) image = models.OneToOneField(to='Images', to_field='image_id', on_delete=models.CASCADE, db_constraint=False,null=True) creat_time = models.DateTimeField(auto_now_add=True) update_time = models.DateTimeField(auto_now=True) def __str__(self): return self.category_name #商品表 class Product(models.Model): product_id=models.AutoField(primary_key=True) name=models.CharField(max_length=200) price=models.DecimalField(max_digits=10, decimal_places=2) weight=models.IntegerField(default=0) #分类 cat = models.ForeignKey(to="Category", to_field="cat_id", related_name="Product", db_constraint=False, on_delete=models.CASCADE) intor = models.TextField(max_length=250)#详细介绍 brief = models.TextField(max_length=250)#商品简介 image=models.OneToOneField(to='Images',to_field='image_id',on_delete=models.CASCADE,db_constraint=False) #库存 stock = models.OneToOneField(to="Stock", to_field="stock_id", db_constraint=False, on_delete=models.CASCADE) buy_count=models.IntegerField(default=0)#购买量 disabled = models.BooleanField(default=1)#是否显示 w_order=models.IntegerField(default=0)#权重 creat_time = models.DateTimeField(auto_now_add=True) update_time = models.DateTimeField(auto_now=True) def __str__(self): return self.name 3.序列化 from rest_framework import serializers from app01 import models class Category_ser(serializers.ModelSerializer): image_url=serializers.ImageField(source="image.image_url") parent_id=serializers.SerializerMethodField() def get_parent_id(self,obj): if obj.parent_id is None: return 0 else: return obj.parent_id class Meta: model=models.Category fields="__all__" from rest_framework import serializers from app01 import models class Goods_ser(serializers.ModelSerializer): image_url=serializers.ImageField(source="image.image_url") stock=serializers.IntegerField(source="stock.quantity") class Meta: model=models.Product fields="__all__" 4.cbv #商品列表 class List(APIView): def post(self,request): param=request.data #1.获取分类主键id:category_id为某个分类的id if param.get('category_id'): #2.查出所有分类对象并序列化 data=models.Category.objects.filter(is_show=True) data=Category_ser.Category_ser(instance=data,many=True,context={"request":request}).data #3.将自身和子孙孙分类主键添加到列表中:从date列表中找出所有category_id的子分类孙分类包括自身的 all_id=func.get_son_id(data,param['category_id']) #4.把自身和孙分类的商品查出序列化 data=models.Product.objects.filter(disabled=True,cat_id__in=all_id).order_by("-w_order") data = Goods_ser.Goods_ser(instance=data, many=True, context={"request": request}).data return Response({"code": 200, "msg": "ok", "data": data}) else: return Response({"code": 201, "msg":"缺少参数" }) 5.函数 res_id=[] def get_son_id(data,parent_id=0,is_clear=True): if is_clear: res_id.clear() if parent_id : res_id.append(parent_id) for item in data: if item['parent_id']==parent_id: res_id.append(item['cat_id']) get_son_id(data,parent_id=item['cat_id'],is_clear=False) return res_id """ print(get_son_id(data,1)) [1, 3, 4, 5] """
2.所有分类
1.路由 path("category/all",Category.All.as_view()), 2.表 #分类表 class Category(models.Model): cat_id=models.AutoField(primary_key=True) category_name=models.CharField(max_length=50) #自关联 parent=models.ForeignKey(to='Category', to_field='cat_id', related_name="Category", on_delete=models.CASCADE, db_constraint=False,blank=True,null=True) #排序 p_order=models.IntegerField(default=0) is_show =models.BooleanField(default=1) image = models.OneToOneField(to='Images', to_field='image_id', on_delete=models.CASCADE, db_constraint=False,null=True) creat_time = models.DateTimeField(auto_now_add=True) update_time = models.DateTimeField(auto_now=True) def __str__(self): return self.category_name 3.序列化 from rest_framework import serializers from app01 import models class Category_ser(serializers.ModelSerializer): image_url=serializers.ImageField(source="image.image_url") parent_id=serializers.SerializerMethodField() def get_parent_id(self,obj): if obj.parent_id is None: return 0 else: return obj.parent_id class Meta: model=models.Category fields="__all__" 4.cbv class All(APIView): def post(self,request): #1.查出所有分类 data=models.Category.objects.filter(is_show=True) #2.分类对象序列化 data=Category_ser.Category_ser(instance=data,many=True,context={"request":request}).data # data=func.get_son(data) return Response({ "code":200, "msg":"ok", "data":data }) 5.函数 res=[] def get_son(data,level=0,parent_id=0,is_clear=True): if is_clear: res.clear() for item in data: if item['parent_id']==parent_id: item['level']=level res.append(item) get_son(data,level=level+1,parent_id=item['cat_id'],is_clear=False) return res """ son=get_son(data) print(son) [{'cat_id': 1, 'name': '北京', 'parent_id': 0, 'level': 0}, {'cat_id': 3, 'name': '沙河', 'parent_id': 1, 'level': 1}, {'cat_id': 4, 'name': 'sb镇', 'parent_id': 3, 'level': 2}, {'cat_id': 5, 'name': '昌平', 'parent_id': 1, 'level': 1}, {'cat_id': 2, 'name': '上海', 'parent_id': 0, 'level': 0}, {'cat_id': 6, 'name': '青浦', 'parent_id': 2, 'level': 1}] """