Luffy:后端接口设计(django)

接口设计

一、登录认证接口

 

二、课程接口设计

分析:

  课程和课程详情接口,是展示为主,为get请求,是通过serializer来实现的,

  接口返回的数据结构中,展示的内容是通过serializer中的字段来确定的。

{
    "code": 0,
    "data": [{
        "id": 1,
        "name": "Python开发21天入门",
        "course_img": "https://luffycity.com/static/frontend/course/5/Python21天入门必备_1509095274.4902391.png",
        "brief": "Python 以其简洁、优雅、高效的特点,成为目前最流行的4大主流开发语言之一,其应用广泛,易学易用,让很多人爱不释手。本套课程为初学者量身打造,是你入门 Python 的必修课程。\\r\\nPS. 此课程特别适合之前完全无任何开发经验的小白白学习!知名网红讲师亲自肉身上阵,课程内容幽默风趣不枯燥,深入浅出,包君满意。",
        "level": "中级",
        "coursedetail_id": null,
        "is_free": false,
        "price": "¥0",
        "origin_price": null
    }, {
        "id": 2,
        "name": "Django框架学习",
        "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png",
        "brief": "Django是Python语言中最流行、最强大的WEB框架,可快速构建稳定强大的WEB项目,大大提高开发效率,很多知名项目都是基于Django开发,如Disqus、Pinterest、Instagram、Bitbucket等, Django官方Slogan是The framework for perfectionist with deadline! 一个为完美主义者且又开发工期很紧的人设计的框架,事实确实如此,Django自身集成了丰富的WEB开发通用组件,如用户认证、分页、中间件、缓存、session等,可以避免浪费大量时间重复造轮子。人生苦短,join the course now and start to build your first web program based on Django.",
        "level": "中级",
        "coursedetail_id": "1",
        "is_free": false,
        "price": "¥100.0",
        "origin_price": null
    }, {
        "id": 3,
        "name": "Git入门",
        "course_img": "https://luffycity.com/static/frontend/course/42/github_1528713275.7115595.jpeg",
        "brief": "Git是一个开源的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理,GitHub就是基于git作为唯一的版本库格式的一个面向开源及私有软件项目的托管平台,除了git代码仓库托管及基本的Web管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器等功能。此课程将一步步带你熟悉git的使用以及如何将代码托管到GitHub平台以及日后工作与他人如何使用git进行协同开发。",
        "level": "中级",
        "coursedetail_id": null,
        "is_free": false,
        "price": "¥0",
        "origin_price": null
    }, {
        "id": 4,
        "name": "Linux系统基础5周入门精讲",
        "course_img": "https://luffycity.com/static/frontend/course/12/Linux5周入门_1509589530.6144893.png",
        "brief": "Linux世界上使用最多的系统之一,这位幕后英雄支持着各大网站的运行。\\r\\nLinux就像一个高冷的贵妇,你很难打动她。但是只要你打动她,她就对你死心塌地了。所以如何征服Linux这位贵妇,即Linux入门就很重要了。\\r\\n自学可能需要半年甚至更久,但观看我们这个专题课程五周让你熟练使用命令和常用服务以及排除常见故障。\\r\\nPS. 此课程特别适合之前完全无任何Linux使用或运维经验的小白白学习!知名网红讲师亲自肉身上阵,课程内容幽默风趣不枯燥,深入浅出,包君满意。",
        "level": "中级",
        "coursedetail_id": "2",
        "is_free": false,
        "price": "¥1000.0",
        "origin_price": null
    }]
}
接口返回的数据结构

 

from api import models
from api.utils import serializer

from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from api.utils.auth import ExpiringTokenAuthentication
from api.utils.filter import CourseFilter


class CourseView(ModelViewSet):
    authentication_classes = [ExpiringTokenAuthentication,]
    queryset = models.Course.objects.all()
    serializer_class = serializer.CourseSerializer
    filter_backends = [CourseFilter,]
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)

        return Response({"code": 0, "data": serializer.data})

class CourseDetailView(ModelViewSet):
    queryset = models.CourseDetail.objects.all()
    serializer_class = serializer.CourseDetailSerializer

class CourseCategoryView(ModelViewSet):
    queryset = models.CourseCategory.objects.all()
    serializer_class = serializer.CourseCategorySerializer

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        serializer = self.get_serializer(queryset, many=True)
        return Response({"error_no":0,"data":serializer.data})
course.py

 

from rest_framework import serializers
from api import models

class CourseCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Course
        fields = (
           "id",
           "name",
        )

class CourseSerializer(serializers.ModelSerializer):
    level = serializers.CharField(source="get_level_display")
    coursedetail_id=serializers.CharField(source="coursedetail.pk")
    class Meta:
        model = models.Course
        fields = (
            'id',
            'name',
            'course_img',
            'brief',
            'level',
            "coursedetail_id"
        )

    def to_representation(self, instance):

        data = super(CourseSerializer, self).to_representation(instance)
        # 购买人数
        # data["people_buy"] = instance.order_details.all().count()
        # 价格套餐列表
        price_policies = instance.price_policy.all().order_by("price").only("price")

        price = getattr(price_policies.first(), "price", 0)

        if price_policies and price == 0:
            is_free = True
            price = "免费"
            origin_price = "原价¥{}".format(price_policies.last().price)
        else:
            is_free = False
            price = "¥{}".format(price)
            origin_price = None

        # 是否免费
        data["is_free"] = is_free
        # 展示价格
        data["price"] = price
        # 原价
        data["origin_price"] = origin_price

        return data


class CourseDetailSerializer(serializers.ModelSerializer):

    name=serializers.CharField(source="course.name")
    prices = serializers.SerializerMethodField()
    brief = serializers.StringRelatedField(source='course.brief')
    study_all_time = serializers.StringRelatedField(source='hours')
    level = serializers.CharField(source='course.get_level_display')

    teachers_info = serializers.SerializerMethodField()
    is_online = serializers.SerializerMethodField()
    recommend_coursesinfo = serializers.SerializerMethodField()
    # learnnumber = serializers.SerializerMethodField()
    # OftenAskedQuestion = serializers.SerializerMethodField()

    class Meta:
        model = models.CourseDetail
        fields="__all__"

    def get_prices(self, obj):
        return PricePolicySerializer(
            obj.course.price_policy.all(), many=True, context=self.context
        ).data

    def get_study_all_time(self, obj):
        return "30小时"


    def get_recommend_coursesinfo(self, obj):
        courses = RecommendCourseSerializer(obj.recommend_courses.all(), many=True)
        return courses.data

    def get_teachers_info(self, obj):
        teachers = TeacherSerializer(obj.teachers.all(), many=True)
        return teachers.data

    def get_is_online(self, obj):
        if obj.course.status == 0:
            return True
        elif obj.course.status == 2:
            return False
        else:
            return ''

    # def get_learnnumber(self, obj):
    #     return obj.course.order_details.all().count()


    # def get_OftenAskedQuestion(self, obj):
    #     question_queryset = models.OftenAskedQuestion.objects.filter(content_type__model='Course',
    #                                                        object_id=obj.course.id)
    #     serializer = OftenAskedQuestionSerializer(question_queryset, many=True)
    #     return serializer.data
    #


class RecommendCourseSerializer(serializers.ModelSerializer):

    course_id = serializers.CharField(source="pk")
    course_name = serializers.CharField(source="name")

    class Meta:
        model = models.Course
        fields = ('course_id', 'course_name',)


class PricePolicySerializer(serializers.ModelSerializer):
    valid_period_name = serializers.StringRelatedField(source="get_valid_period_display")

    class Meta:
        model = models.PricePolicy
        fields = ('id', 'valid_period', 'valid_period_name', 'price',)


class CourseChapterSerializer(serializers.ModelSerializer):
    chapter_name = serializers.SerializerMethodField()
    chapter_symbol = serializers.SerializerMethodField()

    class Meta:
        model = models.CourseChapter
        fields = (
            'id',
            'chapter_name',
            'chapter_symbol',
        )

    def get_chapter_name(self, instance):
        return '第%s章·%s' % (instance.chapter, instance.name)

    def get_chapter_symbol(self, instance):
        return "chapter_%s_%s" % (self.context.get('enrolled_course_id', 1), instance.id)

    def to_representation(self, instance):

        data = super(CourseChapterSerializer, self).to_representation(instance)

        queryset = instance.coursesections.all().order_by("order")
        # 获取章节对应的课时数量
        data["section_of_count"] = queryset.count()
        data["free_trail"] = queryset.filter(free_trail=True).exists()
        data["coursesections"] = SectionSerializer(
            queryset, many=True, read_only=True, context=self.context
        ).data

        return data


class OftenAskedQuestionSerializer(serializers.ModelSerializer):
    question_tittle = serializers.SerializerMethodField()
    question_answer = serializers.SerializerMethodField()

    class Meta:
        model = models.OftenAskedQuestion
        fields = ('question_tittle', 'question_answer')

    def get_question_tittle(self, obj):
        return obj.question

    def get_question_answer(self, obj):
        return obj.answer


class TeacherSerializer(serializers.ModelSerializer):

    teacher_id = serializers.CharField(source="pk")
    teacher_name = serializers.CharField(source="name")
    teacher_brief = serializers.CharField(source="brief")
    teacher_image = serializers.CharField(source="image")

    class Meta:
        model = models.Teacher
        fields = ('teacher_id', 'teacher_name', 'title', 'signature', 'teacher_image', 'teacher_brief')

class UserInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model=models.UserInfo
        fields="__all__"
utils/serializer.py

三、购物车接口

购物车接口包含加入购物车和显示购物车

1、加入购物车(post)

  加入购物车主要是存入redis

  是post请求,携带的数据如下:

                    

  • 获取课程相关数据
  • 验证数据合法性
    • 校验course_id是否合法,判断是否存在接口,用 .get()
    • 校验价格策略是否合法,首先求出该课程的所有价格策略,判断传入id,是否在所求的价格策略里,存在则合法
  • 写入redis缓存中
    • key:shoppingcat_1_1,
      # shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, course_id)
  • post请求加入购物车主要是存入redis,只返回编码和信息,不需要返回数据
{"code":1000,"msg":"加入购物车成功!","data":null}

2、查看购物车 (get)

  get请求

  • 取到当前登录的 user_id
  • 拼接购物车的key,# shopping_car_1_* 
    • shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, "*")  匹配所有该用户的课程
  • 根据key,从redis中获取所有加入购物车的课程
    • 先模糊查询匹配出所有符合要求的key
    • 循环所有key,然后得到每一个课程,存入到shopping_car_list中,
    • 构建结构数据结构,作为data
  • 最终将获取的购物车中的信息,传给前端 ,返回数据前前面

3、修改购物车价格策略(put)

  • 获取前端传过来的course_id,以及price_policy_id
  • 校验数据合法性
    • 课程是否存在,价格策略是否合法
  • 修改redis中的price_policy_id
  • 修改后的信息写入redis中即可

4、删除购物车中的课程

  • 获取前端传过来的course_id
  • 判断课程 id 是否合法,判断该课程是否在redis中存储,首先要拼接key值,
  • 删除 redis 中数据

 

购物车完整代码:

{
    "code": 1000,
    "msg": "",
    "data": {
        "shopping_car_list": [{
            "id": 2,
            "name": "Django框架学习",
            "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png",
            "relate_price_policy": {
                "1": {
                    "prcie": 100.0,
                    "valid_period": 7,
                    "valid_period_text": "1周",
                    "default": false
                },
                "2": {
                    "prcie": 200.0,
                    "valid_period": 14,
                    "valid_period_text": "2周",
                    "default": false
                },
                "3": {
                    "prcie": 300.0,
                    "valid_period": 30,
                    "valid_period_text": "1个月",
                    "default": true
                }
            },
            "default_price": 300.0,
            "default_price_period": 30,
            "default_price_policy_id": 3
        }],
        "total": 1
    }
}
返回的数据结构(get请求)

 

from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin, ModelViewSet

from api.utils.response import BaseResponse

from rest_framework.response import Response

from api.models import Course, CourseDetail, PricePolicy
from django.core.exceptions import ObjectDoesNotExist
from api.utils.exceptions import CommonException
# from django_redis import get_redis_connection
from django.conf import settings
from api.utils.auth import ExpiringTokenAuthentication

import json
from api.utils.permission import LoginUserPermission
import redis

REDIS_CONN = redis.Redis(decode_responses=True)


class ShoppingCarView(APIView):
    """
    1030 加入购物车失败
    """
    authentication_classes = [ExpiringTokenAuthentication, ]

    def post(self, request):
        res = BaseResponse()
        try:
            # 1 获取前端传过来的course_id 以及price_policy_id user_id
            course_id = request.data.get("course_id", "")
            price_policy_id = request.data.get("price_policy_id", "")
            user_id = request.user.id
            # 2 验证数据的合法性
            # 2.1 验证course_id是否合法
            course_obj = Course.objects.get(pk=course_id)
            # 2.2 校验价格策略是否能合法

            # 查找课程关联的价格策略
            price_policy_list = course_obj.price_policy.all()
            price_policy_dict = {}
            for price_policy in price_policy_list:
                price_policy_dict[price_policy.pk] = {
                    "prcie": price_policy.price,
                    "valid_period": price_policy.valid_period,
                    "valid_period_text": price_policy.get_valid_period_display(),
                    "default": price_policy.pk == price_policy_id
                }

            if price_policy_id not in price_policy_dict:
                raise CommonException(1001, "价格策略异常!")

                # 3 构建我们想要的数据结构
                # 价格策略对象
            pp = PricePolicy.objects.get(pk=price_policy_id)
            course_info = {
                "id": course_id,
                "name": course_obj.name,
                "course_img": course_obj.course_img,
                "relate_price_policy": price_policy_dict,
                "default_price": pp.price,
                "default_price_period": pp.valid_period,
                "default_price_policy_id": pp.pk
            }
            # 4 写入redis
            # 4.1 先拼接购物车的key
            shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, course_id)
            # 4.2 写入redis
            REDIS_CONN.set(shopping_car_key, json.dumps(course_info))
            res.msg = "加入购物车成功!"

        except CommonException as e:
            res.code = e.code
            res.error = e.error
        except Exception as e:
            res.code = 1030
            res.error = "加入购物车失败!"

        return Response(res.dict)

    def get(self, request):
        res = BaseResponse()
        try:
            # 1 取到user_id
            user_id = request.user.id
            # 2 拼接购物车的key
            shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, "*")
            print("shopping_car_key",shopping_car_key)
            # shopping_car_1_*
            # shopping_car_1_asdgnlaksdj
            # 3 去redis读取该用户的所有加入购物车的课程
            # 3.1 先去模糊匹配出所有符合要求的key
            all_keys = REDIS_CONN.keys(shopping_car_key)
            print("all_keys",all_keys)
            # 3.2 循环所有的keys 得到每个可以
            shopping_car_list = []
            for key in all_keys:
                print("key",key)
                course_info = json.loads(REDIS_CONN.get(key))
                shopping_car_list.append(course_info)

            res.data = {"shopping_car_list": shopping_car_list, "total": len(shopping_car_list)}

        except Exception as e:
            res.code = 1033
            res.error = "获取购物车失败"
        print("res.dict",res.dict)
        return Response(res.dict)

    def put(self, request):
        res = BaseResponse()
        try:
            # 1 获取前端传过来的course_id 以及price_policy_id
            course_id = request.data.get("course_id", "")
            price_policy_id = request.data.get("price_policy_id", "")
            user_id = request.user.id
            # 2 校验数据的合法性
            # 2.1 校验course_id是否合法
            shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, course_id)
            if not REDIS_CONN.exists(shopping_car_key):
                res.code = 1035
                res.error = "课程不存在"
                return Response(res.dict)
            # 2.2 判断价格策略是否合法
            course_info = REDIS_CONN.hgetall(shopping_car_key)
            price_policy_dict = json.loads(course_info["price_policy_dict"])
            if str(price_policy_id) not in price_policy_dict:
                res.code = 1036
                res.error = "所选的价格策略不存在"
                return Response(res.dict)
            # 3 修改redis中的default_policy_id
            course_info["default_policy_id"] = price_policy_id
            # 4 修改信息后写入redis
            REDIS_CONN.hmset(shopping_car_key, course_info)
            res.data = "更新成功"
        except Exception as e:
            res.code = 1034
            res.error = "更新价格策略失败"
        return Response(res.dict)

    def delete(self, request):
        res = BaseResponse()
        try:
            # 获取前端传过来的course_id
            course_id = request.data.get("course_id", "")
            user_id = request.user.id
            # 判断课程id是否合法
            shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, course_id)
            if not REDIS_CONN.exists(shopping_car_key):
                res.code = 1039
                res.error = "删除的课程不存在"
                return Response(res.dict)
            # 删除redis中的数据
            REDIS_CONN.delete(shopping_car_key)
            res.data = "删除成功"
        except Exception as e:
            res.code = 1037
            res.error = "删除失败"
        return Response(res.dict)


'''
1 post接口构建数据结构:
    {
        "id": 2,
        "default_price_period": 14,
        "relate_price_policy": {
            "1": {
                "valid_period": 7,
                "valid_period_text": "1周",
                "default": false,
                "prcie": 100.0
            },
            "2": {
                "valid_period": 14,
                "valid_period_text": "2周",
                "default": true,
                "prcie": 200.0
            },
            "3": {
                "valid_period": 30,
                "valid_period_text": "1个月",
                "default": false,
                "prcie": 300.0
            }
        },
        "name": "Django框架学习",
        "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png",
        "default_price": 200.0
    }



2 get接口查询数据结构:

{
    "data": {
        "total": 2,
        "shopping_car_list": [
            {
                "id": 2,
                "default_price_period": 14,
                "relate_price_policy": {
                    "1": {
                        "valid_period": 7,
                        "valid_period_text": "1周",
                        "default": false,
                        "prcie": 100
                    },
                    "2": {
                        "valid_period": 14,
                        "valid_period_text": "2周",
                        "default": true,
                        "prcie": 200
                    },
                    "3": {
                        "valid_period": 30,
                        "valid_period_text": "1个月",
                        "default": false,
                        "prcie": 300
                    }
                },
                "name": "Django框架学习",
                "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png",
                "default_price": 200
            },
            {
                "id": 4,
                "default_price_period": 30,
                "relate_price_policy": {
                    "4": {
                        "valid_period": 30,
                        "valid_period_text": "1个月",
                        "default": true,
                        "prcie": 1000
                    },
                    "5": {
                        "valid_period": 60,
                        "valid_period_text": "2个月",
                        "default": false,
                        "prcie": 1500
                    }
                },
                "name": "Linux系统基础5周入门精讲",
                "course_img": "https://luffycity.com/static/frontend/course/12/Linux5周入门_1509589530.6144893.png",
                "default_price": 1000
            }
        ]
    },
    "code": 1000,
    "msg": ""
}

'''
shoppingcart.py

四、结算接口

account

1、去结算(post)

点击去结算按钮,发送post请求,前台发送的数据为:

course_list = [{
    "course_id": 1,
    "price_policy_id": 2
}, ]
  • 获取数据
  • 创建数据结构,先判断 redis 中是否存在 key 值,先清空,
    找到所有以account_userid_*,全部清空
  • 校验课程是否存在,查找关联的价格策略,将课程信息加入到每一个课程结算字典中
  • 课程价格加入到价格列表中
  • 查询当前用户拥有未使用的,在有效期的且与当前课程相关的优惠券
  • 存储结算信息
  • 获取通用优惠券,加入redis中

返回

{"code":1000,"msg":"","data":null}

 

2、展示结算页面(get)

  • 取到user_id
  • 拼接购物车 key  
    # shopping_car_1_*
    # shopping_car_1_asdgnlaksdj
  • 去redis读取该用户的所有加入购物车的课程,
  • 先去模糊匹配出所有符合要求的key
  • 循环所有的keys 从redis中得到每个课程

返回

 

3、修改结算中的价格策略(put)

 

结算完整代码:

{
    "code": 1000,
    "msg": "",
    "data": {
        "account_course_list": [{
            "id": 2,
            "name": "Django框架学习",
            "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png",
            "relate_price_policy": {
                "1": {
                    "prcie": 100.0,
                    "valid_period": 7,
                    "valid_period_text": "1周",
                    "default": false
                },
                "2": {
                    "prcie": 200.0,
                    "valid_period": 14,
                    "valid_period_text": "2周",
                    "default": false
                },
                "3": {
                    "prcie": 300.0,
                    "valid_period": 30,
                    "valid_period_text": "1个月",
                    "default": true
                }
            },
            "default_price": 300.0,
            "rebate_price": 300.0,
            "default_price_period": 30,
            "default_price_policy_id": 3,
            "coupon_list": [{
                "pk": 3,
                "name": "清明节活动",
                "coupon_type": "折扣券",
                "money_equivalent_value": 0.0,
                "off_percent": 80,
                "minimum_consume": 0
            }]
        }],
        "total": 1,
        "global_coupons": [{
            "pk": 2,
            "name": "国庆节活动通用券",
            "coupon_type": "满减券",
            "money_equivalent_value": 50.0,
            "off_percent": null,
            "minimum_consume": 100
        }],
        "total_price": "300.0"
    }
}
返回的数据结构

 

from rest_framework.views import APIView
from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication
from rest_framework.response import Response
import json
import datetime
from django.conf import settings
from api.models import Coupon, CouponRecord,Course,PricePolicy
# from django_redis import get_redis_connection
from api.utils.exceptions import CommonException
from django.core.exceptions import ObjectDoesNotExist
import redis

REDIS_CONN = redis.Redis(decode_responses=True)


class AccountView(APIView):
    '''
    结算接口
    '''
    authentication_classes = [ExpiringTokenAuthentication, ]

    def get_coupon_list(self, request, course_id=None):

        now = datetime.datetime.utcnow()

        coupon_record_list = CouponRecord.objects.filter(
            account=request.user,
            status=0,
            coupon__valid_begin_date__lte=now,
            coupon__valid_end_date__gt=now,
            coupon__content_type_id=14,
            coupon__object_id=course_id

        )

        coupon_list = []

        for coupon_record in coupon_record_list:
            coupon_list.append({

                "pk": coupon_record.pk,
                "name": coupon_record.coupon.name,
                "coupon_type": coupon_record.coupon.get_coupon_type_display(),
                "money_equivalent_value": coupon_record.coupon.money_equivalent_value,
                "off_percent": coupon_record.coupon.off_percent,
                "minimum_consume": coupon_record.coupon.minimum_consume,
            })

        return coupon_list

    def post(self, request, *args, **kwargs):
        # 1 获取数据
        '''
        course_list=[{
                          "course_id":1,
                          "price_policy_id":2
                        },
                    ]

        :param request:
        :param args:
        :param kwargs:
        :return:
        '''
        user = request.user
        course_list = request.data
        print("course_list",course_list)
        print("course_list",type(course_list))
        response = BaseResponse()
        try:
            # 2 创建数据结构
            # 清空操作
            # 找到所有以account_userid_*,全部清空
            del_list = REDIS_CONN.keys(settings.ACCOUNT_KEY % (user.pk, "*"))
            if del_list:
                REDIS_CONN.delete(*del_list)

            price_list=[]
            for course_dict in course_list:

                course_id=course_dict.get("course_id")
                price_policy_id=course_dict.get("price_policy_id")
                # 校验课程是否存在
                course_obj=Course.objects.get(pk=course_id)
                # 查找课程关联的价格策略
                price_policy_list = course_obj.price_policy.all()
                price_policy_dict = {}
                for price_policy in price_policy_list:
                    price_policy_dict[price_policy.pk] = {
                        "prcie": price_policy.price,
                        "valid_period": price_policy.valid_period,
                        "valid_period_text": price_policy.get_valid_period_display(),
                        "default": price_policy.pk == price_policy_id
                    }

                if price_policy_id not in price_policy_dict:
                    raise CommonException(1001, "价格策略异常!")
                pp=PricePolicy.objects.get(pk=price_policy_id)
                # 将课程信息加入到每一个课程结算字典中
                account_dict = {
                    "id": course_id,
                    "name": course_obj.name,
                    "course_img": course_obj.course_img,
                    "relate_price_policy": price_policy_dict,
                    "default_price": pp.price,
                    "rebate_price": pp.price,
                    "default_price_period": pp.valid_period,
                    "default_price_policy_id": pp.pk
                }
                # 课程价格加入到价格列表
                price_list.append(float(pp.price))

                # 查询当前用户拥有未使用的,在有效期的且与当前课程相关的优惠券
                account_dict["coupon_list"] = self.get_coupon_list(request, course_id)

                # 存储结算信息
                account_key = settings.ACCOUNT_KEY % (user.pk, course_id)
                REDIS_CONN.set(account_key, json.dumps(account_dict))

            # 获取通用优惠券,加入redis中

            REDIS_CONN.set("global_coupon_%s" % user.pk, json.dumps(self.get_coupon_list(request)))
            REDIS_CONN.set("total_price",sum(price_list))

        except ObjectDoesNotExist as e:
            response.code = 1001
            response.error = "课程不存在!"
        except CommonException as e:
            response.code = e.code
            response.error = e.error

        # except Exception as e:
        #     response.code = 500
        #     response.error = str(e)

        return Response(response.dict)

    def get(self, request, *args, **kwargs):

        res = BaseResponse()
        try:
            # 1 取到user_id
            user_id = request.user.id
            # 2 拼接购物车的key
            account_key = settings.ACCOUNT_KEY % (user_id, "*")
            # shopping_car_1_*
            # shopping_car_1_asdgnlaksdj
            # 3 去redis读取该用户的所有加入购物车的课程
            # 3.1 先去模糊匹配出所有符合要求的key
            all_keys = REDIS_CONN.scan_iter(account_key)

            # 3.2 循环所有的keys 得到每个课程
            account_course_list = []
            for key in all_keys:
                account_course = json.loads(REDIS_CONN.get(key))
                account_course_list.append(account_course)

            global_coupons = json.loads(REDIS_CONN.get("global_coupon_%s" % request.user.pk))
            total_price = REDIS_CONN.get("total_price")
            res.data = {
                "account_course_list": account_course_list,
                "total": len(account_course_list),
                "global_coupons": global_coupons,
                "total_price": total_price
            }

        except Exception as e:
            res.code = 1033
            res.error = "获取购物车失败"

        return Response(res.dict)

    def cal_coupon_price(self,price,coupon_info):

        print("coupon_info",coupon_info)
        coupon_type=coupon_info["coupon_type"]
        money_equivalent_value=coupon_info.get("money_equivalent_value")
        off_percent=coupon_info.get("off_percent")
        minimum_consume=coupon_info.get("minimum_consume")
        rebate_price=0
        if coupon_type == "立减券": # 立减券
            rebate_price=price-money_equivalent_value
            if rebate_price <= 0:
                rebate_price=0
        elif coupon_type == "满减券": # 满减券
             if minimum_consume > price:
                 raise CommonException(3000,"优惠券未达到最低消费")
             else:
                 rebate_price=price-money_equivalent_value
        elif coupon_type == "折扣券":
            rebate_price=price*off_percent/100

        return rebate_price

    def put(self,request, *args, **kwargs):
        '''
        choose_coupons:
            {
            choose_coupons={"1":2,"2":3,"global_coupon_id":5}
            is_beli:true
            }
        '''
        res=BaseResponse()
        # try:

        # 1 获取数据
        choose_coupons=request.data.get("choose_coupons")
        is_beli=request.data.get("is_beli")
        user_pk=request.user.pk

        # 2 获取结算课程列表
        cal_price={}
        data=self.get(request).data.get("data")
        account_course_list=data.get("account_course_list")
        print("account_course_list",account_course_list)
        '''
           account_course_list=[{
                'id': 4,
                'coupon_list': [{
                    'off_percent': None,
                    'pk': 4,
                    'money_equivalent_value': 300.0,
                    'coupon_type': '立减券',
                    'minimum_consume': 0,
                    'name': '51劳动节'
                }],
                'course_img': 'https://luffycity.com/static/frontend/course/12/Linux5周入门_1509589530.6144893.png',
                'default_price': 1500.0,
                'default_price_period': 60,
                'relate_price_policy': {
                    '5': {
                        'valid_period_text': '2个月',
                        'default': True,
                        'valid_period': 60,
                        'prcie': 1500.0
                    },
                    '4': {
                        'valid_period_text': '1个月',
                        'default': False,
                        'valid_period': 30,
                        'prcie': 1000.0
                    }
                },
                'default_price_policy_id': 5,
                'name': 'Linux系统基础5周入门精讲'
            }, {
                'id': 2,
                'coupon_list': [{
                    'off_percent': 80,
                    'pk': 3,
                    'money_equivalent_value': 0.0,
                    'coupon_type': '折扣券',
                    'minimum_consume': 0,
                    'name': '清明节活动'
                }],
                'course_img': 'https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png',
                'default_price': 300.0,
                'default_price_period': 30,
                'relate_price_policy': {
                    '3': {
                        'valid_period_text': '1个月',
                        'default': True,
                        'valid_period': 30,
                        'prcie': 300.0
                    },
                    '1': {
                        'valid_period_text': '1周',
                        'default': False,
                        'valid_period': 7,
                        'prcie': 100.0
                    },
                    '2': {
                        'valid_period_text': '2周',
                        'default': False,
                        'valid_period': 14,
                        'prcie': 200.0
                    }
                },
                'default_price_policy_id': 3,
                'name': 'Django框架学习'
            }]
        '''
        account_courses_info={}
        for account_course in account_course_list:
            temp={
                "coupon":{},
                "default_price":account_course["default_price"]
            }
            account_courses_info[account_course["id"]]=temp

            for item in account_course["coupon_list"]:

                print("choose_coupons",choose_coupons) # {'4': 4}
                print(str(account_course["id"]))

                coupon_id=choose_coupons.get(str(account_course["id"]))
                if coupon_id == item["pk"]:
                    temp["coupon"]=item

        print("account_course_info",account_courses_info)
        price_list=[]
        total_price=0
        '''
           {
                2: {
                    'coupon': {
                        'money_equivalent_value': 0.0,
                        'name': '清明节活动',
                        'pk': 3,
                        'off_percent': 80,
                        'coupon_type': '折扣券',
                        'minimum_consume': 0
                    },
                    'default_price': 200.0
                }
            }
        '''
        for key,val in account_courses_info.items():
            if not val.get("coupon"):
                price_list.append(val["default_price"])
                cal_price[key]=val["default_price"]
            else:
                coupon_info=val.get("coupon")
                default_price=val["default_price"]
                rebate_price=self.cal_coupon_price(default_price,coupon_info)
                price_list.append(rebate_price)
                cal_price[key]=rebate_price

        print("课程优惠券后价格列表price_list",price_list)
        total_price=sum(price_list)
        # 3 计算通用优惠券的价格
        global_coupon_id=choose_coupons.get("global_coupon_id")
        if global_coupon_id:

            global_coupons=data.get("global_coupons")
            print("global_coupons",global_coupons)
            global_coupon_dict={}
            for item in global_coupons:
                global_coupon_dict[item["pk"]]=item
            total_price=self.cal_coupon_price(total_price,global_coupon_dict[global_coupon_id])
            print("通用优惠券",global_coupon_dict[global_coupon_id]["coupon_type"])
            print("计算后total_price=",total_price)

        # 计算贝里
        if json.loads(is_beli):
            print("request.user.beli",request.user.beli)
            total_price=total_price-request.user.beli/10
            if total_price<0:
                total_price=0
            print("贝里数计算后",total_price)

        cal_price["total_price"]=total_price
        res.data=cal_price

        # except Exception as e:
        #     res.code=500
        #     res.msg="结算错误!"+str(e)

        return Response(res.dict)


'''
1 结算中心post添加接口数据结构:
        {
        "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png",
        "coupon_list": [{
            "name": "清明节活动",
            "minimum_consume": 0,
            "money_equivalent_value": 0.0,
            "off_percent": 80,
            "pk": 3,
            "coupon_type": "折扣券"
        }],
        "relate_price_policy": {
            "1": {
                "valid_period": 7,
                "valid_period_text": "1周",
                "prcie": 100.0,
                "default": true
            },
            "2": {
                "valid_period": 14,
                "valid_period_text": "2周",
                "prcie": 200.0,
                "default": false
            },
            "3": {
                "valid_period": 30,
                "valid_period_text": "1个月",
                "prcie": 300.0,
                "default": false
            }
        },
        "name": "Django框架学习",
        "default_price": 100.0,
        "id": 2,
        "default_price_period": 7,
        "default_price_policy_id": 1
    }

2 结算中心get查询接口:
{
    "data": {
        "total": 1,
        "global_coupons": [
            {
                "name": "国庆节活动通用券",
                "coupon_type": "满减券",
                "minimum_consume": 100,
                "money_equivalent_value": 50,
                "off_percent": null,
                "pk": 2
            }
        ],
        "total_price": "100.0",
        "account_course_list": [
            {
                "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png",
                "name": "Django框架学习",
                "relate_price_policy": {
                    "1": {
                        "valid_period": 7,
                        "prcie": 100,
                        "valid_period_text": "1周",
                        "default": true
                    },
                    "2": {
                        "valid_period": 14,
                        "prcie": 200,
                        "valid_period_text": "2周",
                        "default": false
                    },
                    "3": {
                        "valid_period": 30,
                        "prcie": 300,
                        "valid_period_text": "1个月",
                        "default": false
                    }
                },
                "coupon_list": [
                    {
                        "name": "清明节活动",
                        "coupon_type": "折扣券",
                        "minimum_consume": 0,
                        "money_equivalent_value": 0,
                        "off_percent": 80,
                        "pk": 3
                    }
                ],
                "default_price": 100,
                "id": 2,
                "default_price_period": 7,
                "default_price_policy_id": 1
            }
        ]
    },
    "code": 1000,
    "msg": ""
}
'''
account.py

 

 

 

 

 

五、支付接口

payment

 

 

六、订单接口

 order

 

posted @ 2018-12-21 17:41  葡萄想柠檬  Views(604)  Comments(0)    收藏  举报
目录代码