订单接口 多方式支付 订单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}]
"""

 

posted @ 2020-03-31 20:40  心慌得初夏  阅读(251)  评论(0编辑  收藏  举报
levels of contents