实战项目-美多商城(七)订单

发布时间 2023-04-21 17:25:38作者: 清安宁

订单建模,包含订单基本信息订单商品两张表

### orders.models
from django.db import models

from utils.models import BaseModel
from users.models import UserInfo,Address
from goods.models import SKU

class OrderInfo(BaseModel):
    '''
        - 订单基本信息
        
        - 用到 User 和 Address两个外键
    '''
    PAY_METHODS_ENUM = {
        "CASH": 1,
        "ALIPAY": 2
    }
    PAY_METHOD_CHOICES = (
        (1, "货到付款"),
        (2, "支付宝"),
    )
    ORDER_STATUS_ENUM = {
        "UNPAID": 1,
        "UNSEND": 2,
        "UNRECEIVED": 3,
        "UNCOMMENT": 4,
        "FINISHED": 5
    }
    ORDER_STATUS_CHOICES = (
        (1, "待支付"),
        (2, "待发货"),
        (3, "待收货"),
        (4, "待评价"),
        (5, "已完成"),
        (6, "已取消"),
    )
    # 不使用默认自动生成的ID字段,而是自定义
    order_id = models.CharField(max_length=64, primary_key=True, verbose_name="订单号")
    user = models.ForeignKey(UserInfo, on_delete=models.PROTECT, verbose_name="下单用户")
    address = models.ForeignKey(Address, on_delete=models.PROTECT, verbose_name="收货地址")
    # 正整数
    total_count = models.PositiveIntegerField(default=1, verbose_name="商品总数")
    # 设置了精度的十进制数字(推荐使用)
    total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="商品总金额")
    freight = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="运费")
    pay_method = models.SmallIntegerField(choices=PAY_METHOD_CHOICES, default=1, verbose_name="支付方式")
    status = models.SmallIntegerField(choices=ORDER_STATUS_CHOICES, default=1, verbose_name="订单状态")

    class Meta:
        db_table = "tb_order_info"
        verbose_name = '订单基本信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.order_id


class OrderGoods(BaseModel):
    """
        - 订单商品
        
        -两个外键: order & sku
    """
    SCORE_CHOICES = (
        (0, '0分'),
        (1, '20分'),
        (2, '40分'),
        (3, '60分'),
        (4, '80分'),
        (5, '100分'),
    )
    order = models.ForeignKey(OrderInfo, related_name='skus', on_delete=models.CASCADE, verbose_name="订单")
    sku = models.ForeignKey(SKU, on_delete=models.PROTECT, verbose_name="订单商品")
    count = models.PositiveIntegerField(default=1, verbose_name="数量")
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="单价")
    comment = models.TextField(default="", verbose_name="评价信息")
    score = models.SmallIntegerField(choices=SCORE_CHOICES, default=5, verbose_name='满意度评分')
    is_anonymous = models.BooleanField(default=False, verbose_name='是否匿名评价')
    is_commented = models.BooleanField(default=False, verbose_name='是否评价了')

    class Meta:
        db_table = "tb_order_goods"
        verbose_name = '订单商品'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.sku.name


功能 --- 提交订单展示页

  • 请求方式

    请求方法 请求地址
    GET http://127.0.0.1:8000/orders/settlement/
  • 请求参数

    请求头 key 类型 是否必传 说明
    headers Authorization 'JWT ' + token string JWT字符串一定要加空格(后端以空格进行拆分)
  • 响应成功结果:JSON

    {
        "freight":"100.00",
        "skus":[
            {
                "id":10,
                "name":'华为xxxx手机',
                "default_image_url":"https://www.dfsdfsf.xsdfsdss",
                "price":"3788.00",
                "count":1
            },
            {
                "id":11,
                "name":'苹果xxxx收集',
                "default_image_url":"https://www.xxxx.yyyyyy",
                "price":"6700.00",
                "count":2
            },
        ]
    }
    

  • 后端接口注意事项

    • 用户收货地址(已有)
    • 支付方式('货到付款'/'支付宝')
    • 商品信息数据(不再提供修改功能)
  • 前端要获取两块信息,当网页开始加载的时候,立即向后端发起请求

    • 获取用户的收货地址
    • 获取购物车结算的商品信息
    ......
    mounted: function(){
        // 获取地址信息
        axios.get(this.host + '/addresses/', {
                headers: {
                    'Authorization': 'JWT ' + this.token
                },
                responseType: 'json'
            })
            .then(response => {
                this.addresses = response.data.addresses;
                this.nowsite = response.data.default_address_id;
            })
            ......
    
            // 获取结算商品信息
            axios.get(this.host+'/orders/settlement/', {
                headers: {
                    'Authorization': 'JWT ' + this.token
                },
                responseType: 'json'
            })
            .then(response => {
                this.skus = response.data.skus;
                this.freight = response.data.freight;
                this.total_count = 0;
                this.total_amount = 0;
                for(var i=0; i<this.skus.length; i++){
                    var amount = parseFloat(this.skus[i].price)*this.skus[i].count;
                    this.skus[i].amount = amount.toFixed(2);
                    this.total_count += this.skus[i].count;
                    this.total_amount += amount;
                }
                this.payment_amount = parseFloat(this.freight) + this.total_amount;
                this.payment_amount = this.payment_amount.toFixed(2);
                this.total_amount = this.total_amount.toFixed(2);
            })
            ......
    
    
  • 构造类似下面的数据格式,用两个序列化器来搞定

{
    "freight":"100.00", # 订单序列化器
    "skus":[ # sku序列化器
        {
            ......
        },
        {
            ......
        },
    ]
}

### orders.serializers
from apps.goods.models import SKU
from rest_framework import serializers

class CartSKUSerializer(serializers.ModelSerializer):
    '''订单中,商品数据的序列化(提供给下面的序列化类使用)'''
    
    count = serializers.IntegerField(label='购买数量') # count值从redis取
    class Meta:
        model = SKU
        fields = ['id','name','price','default_image_url','count']


class OrderSettlementSerializer(serializers.Serializer):
    '''订单序列化器'''
    
    # 多条数据,要加上many,skus对应一个查询集
    skus = CartSKUSerializer(many=True)
    # 简单赋值
    freight = serializers.DecimalField(max_digits=10,decimal_places=2,label='运费')
  • 视图: 从redis取出购物车数据,构造数据,然后序列化返回给前端
from decimal import Decimal

from django_redis import get_redis_connection
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from .serializers import OrderSettlementSerializer
from apps.goods.models import SKU

class OrderSettlementView(APIView):

    authentication_classes = [JSONWebTokenAuthentication, ]
    permission_classes = [IsAuthenticated, ]

    def get(self,request):
        user = request.user # 代码能进来,说明验证无误,不用再try
        conn = get_redis_connection('cart')
        # 获取 redis 购物车数据,构造dict
        redis_cart_dict = conn.hgetall('cart_{}'.format(user.id))
        selected_ids = conn.smembers('selected_{}'.format(user.id))
        cart_dict = {} # 把sku_id和count丢里面,给后面的queryset使用
        for sku_id in selected_ids:
            cart_dict[int(sku_id)] = int(redis_cart_dict[sku_id]) # 获取count
            
        skus_queryset = SKU.objects.filter(id__in=cart_dict.keys())
        for sku in skus_queryset:
            sku.count = cart_dict[sku.id] # 把count加入queryset对象

        freight = Decimal('10.00')
        data_dict = { # 构造数据并序列化
            'freight':freight,
            'skus':skus_queryset
        }
        serializer = OrderSettlementSerializer(data_dict) # 单个对象,无需再加 many=True

        return Response(serializer.data)