• 订单接口 多方式支付 订单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)
    View Code

    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
    View Code

    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)
    View Code

    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
    View Code

    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)
    View Code

     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}]
    """
  • 相关阅读:
    python基础总结二
    HTTP和HTTPS的区别
    通过HTTP请求响应过程了解HTTP协议
    稳定性测试+易用性测试
    容错测试点
    功能测试思考点
    功能测试-UI测试思考点
    字符编码-11
    字典+再识函数-8
    web API的概念
  • 原文地址:https://www.cnblogs.com/tfzz/p/12608135.html
Copyright © 2020-2023  润新知