day100-Exception继承定义错误信息&结算接口&结算数据结构

2020-03-30 15:29:33 浏览数 (1)

1.Exception的继承

代码语言:javascript复制
# 自定义继承类继承 Exception
class MyException(Exception):
    def __init__(self, code, msg):
        self.code = code
        self.msg = msg


# 自定义相应信息
class BaseResponse:
    def __init__(self):
        self.code = ''
        self.error = ''
        self.msg = ''

    @property
    def dict(self):
        return self.__dict__


# 实例化响应对象
my_res = BaseResponse()
try:
    if not 1:
        raise MyException(1000, 'test_my_exception')
    """其他代码"""
    """ll = int(s)"""
    """其他代码"""
    
# 这个对应的是 MyException 的错误
except MyException as e:
    my_res.code = e.code
    my_res.msg = e.msg
    
# 这个对应的是 try 对应的其他代码的未知错误
except Exception as e:
    my_res.msg = str(e)

print(my_res.dict)

2.接口

2.1models的代码,新加了 Coupon 和 CouponRecord

代码语言:javascript复制
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey
from django.contrib.contenttypes.models import ContentType

# Create your models here.
__all__ = ["Category", "Course", "CourseDetail", "Teacher",
           "DegreeCourse", "CourseChapter",
           "CourseSection", "PricePolicy",
           "OftenAskedQuestion", "Comment",
           "Account", "CourseOutline",
           "Coupon", "CouponRecord"]


# 首先上面有课程分类
class Category(models.Model):
    """课程分类表"""
    title = models.CharField(max_length=32, unique=True, verbose_name="课程的分类")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "01-课程分类表"
        db_table = verbose_name
        verbose_name_plural = verbose_name


# 每一条课程分类下面有课程
class Course(models.Model):
    """课程表"""
    title = models.CharField(max_length=128, unique=True, verbose_name="课程的名称")

    course_img = models.ImageField(upload_to="course/%Y-%m", verbose_name='课程的图片')
    # media/course/2018-11/xxx.png

    category = models.ForeignKey(to="Category", verbose_name="课程的分类")
    COURSE_TYPE_CHOICES = ((0, "付费"), (1, "vip专享"), (2, "学位课程"))
    course_type = models.SmallIntegerField(choices=COURSE_TYPE_CHOICES)
    degree_course = models.ForeignKey(to="DegreeCourse", blank=True, null=True, help_text="如果是学位课程,必须关联学位表")
    # course_type    degree_course_id
    #  0                null
    #  1                null
    #  2                2

    brief = models.CharField(verbose_name="课程简介", max_length=1024)
    level_choices = ((0, '初级'), (1, '中级'), (2, '高级'))
    level = models.SmallIntegerField(choices=level_choices, default=1)
    status_choices = ((0, '上线'), (1, '下线'), (2, '预上线'))
    status = models.SmallIntegerField(choices=status_choices, default=0)
    pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True)
    order = models.IntegerField(verbose_name="课程顺序", help_text="从上一个课程数字往后排, 建议中间空几个数字")
    study_num = models.IntegerField(verbose_name="学习人数", help_text="只要有人买课程,订单表加入数据的同时给这个字段 1")
    is_free = models.BooleanField(default=False)

    # order_details = GenericRelation("OrderDetail", related_query_name="course")
    # coupon = GenericRelation("Coupon")
    # 只用于反向查询不生成字段
    price_policy = GenericRelation("PricePolicy")
    often_ask_questions = GenericRelation("OftenAskedQuestion")
    course_comments = GenericRelation("Comment")

    def save(self, *args, **kwargs):
        if self.course_type == 2:
            if not self.degree_course:
                raise ValueError("学位课必须关联学位课程表")
        super(Course, self).save(*args, **kwargs)

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "02-课程表"
        db_table = verbose_name
        verbose_name_plural = verbose_name


# 每一条课程里面都有课程详情
class CourseDetail(models.Model):
    """课程详细表"""
    course = models.OneToOneField(to="Course")
    hours = models.IntegerField(verbose_name="课时")
    course_slogan = models.CharField(max_length=125, blank=True, null=True, verbose_name="课程口号")
    # 视频简介链接
    video_brief_link = models.CharField(max_length=255, blank=True, null=True)
    summary = models.TextField(max_length=2048, verbose_name="课程概述")
    why_study = models.TextField(verbose_name="为什么学习这门课程")
    service = models.TextField(verbose_name="你将获得哪些服务")
    what_to_study_brief = models.TextField(verbose_name="我将学到哪些内容")
    career_improvement = models.TextField(verbose_name="此项目如何有助于我的职业生涯")
    prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024)
    recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True)
    teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师")

    def __str__(self):
        return self.course.title

    class Meta:
        verbose_name = "03-课程详细表"
        db_table = verbose_name
        verbose_name_plural = verbose_name


class Teacher(models.Model):
    """讲师表"""
    name = models.CharField(max_length=32, verbose_name="讲师名字")
    brief = models.TextField(max_length=1024, verbose_name="讲师介绍")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = "04-教师表"
        db_table = verbose_name
        verbose_name_plural = verbose_name


# 只是一个单独的深度学习的title
class DegreeCourse(models.Model):
    """
    字段大体跟课程表相同,哪些不同根据业务逻辑去区分
    """
    title = models.CharField(max_length=32, verbose_name="学位课程名字")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "05-学位课程表"
        db_table = verbose_name
        verbose_name_plural = verbose_name


# 课程章节表,课程有多个章节
class CourseChapter(models.Model):
    """课程章节表"""
    course = models.ForeignKey(to="Course", related_name="course_chapters")
    # 排序用的
    chapter = models.SmallIntegerField(default=1, verbose_name="第几章")
    title = models.CharField(max_length=32, verbose_name="课程章节名称")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "06-课程章节表"
        db_table = verbose_name
        verbose_name_plural = verbose_name
        unique_together = ("course", "chapter")


# 课时表,一章课程有多个课程
class CourseSection(models.Model):
    """课时表"""
    chapter = models.ForeignKey(to="CourseChapter", related_name="course_sections")
    title = models.CharField(max_length=32, verbose_name="课时")
    section_order = models.SmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时")
    section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频'))
    free_trail = models.BooleanField("是否可试看", default=False)
    section_type = models.SmallIntegerField(default=2, choices=section_type_choices)
    # 视频链接
    section_link = models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link")

    def __str__(self):
        return "%s-%s" % (self.chapter, self.title)

    class Meta:
        verbose_name = "07-课程课时表"
        db_table = verbose_name
        verbose_name_plural = verbose_name
        unique_together = ('chapter', 'section_link')


# 价格策略表
class PricePolicy(models.Model):
    """价格策略表"""
    content_type = models.ForeignKey(ContentType)  # 关联course or degree_course
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    valid_period_choices = ((1, '1天'), (3, '3天'),
                            (7, '1周'), (14, '2周'),
                            (30, '1个月'),
                            (60, '2个月'),
                            (90, '3个月'),
                            (120, '4个月'),
                            (180, '6个月'), (210, '12个月'),
                            (540, '18个月'), (720, '24个月')
                            )
    valid_period = models.SmallIntegerField(choices=valid_period_choices)
    price = models.FloatField()

    def __str__(self):
        return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)

    class Meta:
        verbose_name = "08-价格策略表"
        db_table = verbose_name
        verbose_name_plural = verbose_name
        unique_together = ("content_type", 'object_id', "valid_period")


# 常见问题
class OftenAskedQuestion(models.Model):
    """常见问题"""
    content_type = models.ForeignKey(ContentType)  # 关联course or degree_course
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    question = models.CharField(max_length=255)
    answer = models.TextField(max_length=1024)

    def __str__(self):
        return "%s-%s" % (self.content_object, self.question)

    class Meta:
        verbose_name = "09-常见问题表"
        db_table = verbose_name
        verbose_name_plural = verbose_name
        unique_together = ('content_type', 'object_id', 'question')


# 用户评论表
class Comment(models.Model):
    """通用的评论表"""
    # 定位表
    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    # 定位对象的id
    object_id = models.PositiveIntegerField(blank=True, null=True)
    # 定位对象
    content_object = GenericForeignKey('content_type', 'object_id')

    content = models.TextField(max_length=1024, verbose_name="评论内容")
    account = models.ForeignKey("Account", verbose_name="会员名")
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.content

    class Meta:
        verbose_name = "10-评价表"
        db_table = verbose_name
        verbose_name_plural = verbose_name


# 用户表
class Account(models.Model):
    username = models.CharField(max_length=32, verbose_name="用户姓名")
    pwd = models.CharField(max_length=128)

    create_token_time = models.DateTimeField(auto_now=True)

    token = models.UUIDField(null=True, blank=True)

    def __str__(self):
        return self.username

    class Meta:
        verbose_name = "11-用户表"
        db_table = verbose_name
        verbose_name_plural = verbose_name


# 课程大纲,在课程详情里面
class CourseOutline(models.Model):
    """课程大纲"""
    course_detail = models.ForeignKey(to="CourseDetail", related_name="course_outline")
    title = models.CharField(max_length=128)
    order = models.PositiveSmallIntegerField(default=1)
    # 前端显示顺序

    content = models.TextField("内容", max_length=2048)

    def __str__(self):
        return "%s" % self.title

    class Meta:
        verbose_name = "12-课程大纲表"
        db_table = verbose_name
        verbose_name_plural = verbose_name
        unique_together = ('course_detail', 'title')


#################################################################

class Coupon(models.Model):
    """优惠券生成规则"""
    name = models.CharField(max_length=64, verbose_name="活动名称")
    brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")
    coupon_type_choices = ((0, '立减券'), (1, '满减券'), (2, '折扣券'))
    coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")

    money_equivalent_value = models.IntegerField(verbose_name="等值货币", blank=True, null=True)
    off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
    minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段")

    content_type = models.ForeignKey(ContentType, blank=True, null=True, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
    content_object = GenericForeignKey('content_type', 'object_id')

    quantity = models.PositiveIntegerField("数量(张)", default=1)
    open_date = models.DateField("优惠券领取开始时间")
    close_date = models.DateField("优惠券领取结束时间")
    valid_begin_date = models.DateTimeField(verbose_name="有效期开始时间", blank=True, null=True)
    valid_end_date = models.DateTimeField(verbose_name="有效结束时间", blank=True, null=True)
    coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,
                                                    help_text="自券被领时开始算起")
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "13-优惠券生成规则"

    def __str__(self):
        return "%s(%s)" % (self.get_coupon_type_display(), self.name)


class CouponRecord(models.Model):
    """优惠券发放、消费纪录"""
    coupon = models.ForeignKey("Coupon", on_delete=models.CASCADE)
    user = models.ForeignKey("Account", verbose_name="拥有者", on_delete=models.CASCADE)
    status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期'))
    status = models.SmallIntegerField(choices=status_choices, default=0)
    get_time = models.DateTimeField(verbose_name="领取时间", help_text="用户领取时间", null=True, blank=True)
    used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间")

    class Meta:
        verbose_name_plural = "14-优惠券发放、消费纪录"

    def __str__(self):
        return '%s-%s-%s' % (self.user, self.coupon, self.get_status_display())

2.2结算接口,注意 # 时间属性必须转换格式才能被 json 序列化

代码语言:javascript复制
class AccountView(APIView):
    authentication_classes = [authentication.MyAuthentication, ]

    @staticmethod
    def get_user_coupons_dict(request, course_id=None):
        print('id', course_id)
        coupon_dict = {}
        coupons_query_set = models.CouponRecord.objects.filter(
            # 首先指定表id
            coupon__content_type_id=10,
            # 指定课程id
            coupon__object_id=course_id,
            status=0,
            user=request.user,
            coupon__valid_begin_date__lt=now(),
            coupon__valid_end_date__gt=now(),
        )
        for coupon_record in coupons_query_set:
            coupon_dict[coupon_record.id] = {
                'name': coupon_record.coupon.name,
                'brief': coupon_record.coupon.brief,
                '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,
                # 注意时间属性只有被转换格式才可以被序列化
                'valid_begin_date': coupon_record.coupon.valid_begin_date.strftime('%Y-%m-%d'),
                'valid_end_date': coupon_record.coupon.valid_end_date.strftime('%Y-%m-%d'),
            }
        return coupon_dict

    @staticmethod
    def post(request):
        """本接口仅仅对购物车进行结算"""
        # {course_list: [XX, XX, ...]}
        test_dict = {}
        my_res = BaseResponse()
        try:
            """1.以最新的结算为准,清除Redis里面该用户原来的结算数据"""
            user_id = request.user.pk
            account_key = ACCOUNT_KEY.format(user_id, '*')
            delete_list = REDIS_CONN.keys(ACCOUNT_KEY)
            if delete_list:
                REDIS_CONN.delete(*account_key)

            """2.判断课程列表里的课程是否合法"""
            course_list = request.data.get('course_list', '')
            # 类型是否为列表
            if type(course_list) is not list:
                raise MyException(601, '参数类型非法!')
            # 是否存在相同课程
            course_list = [str(emp).strip() for emp in course_list]
            if len(course_list) != len(set(course_list)):
                raise MyException('602', '课程id不能相同!')
            # 是否存在非法课程
            for course_id in course_list:
                if not REDIS_CONN.exists(SHOPPING_CAR.format(user_id, course_id)):
                    raise MyException(602, '不存在相关课程!')

            for course_id in course_list:
                """3.拿到课程信息字典"""
                course_info = REDIS_CONN.get(SHOPPING_CAR.format(user_id, course_id))
                test_dict['course_info_{}'.format(course_id)] = json.loads(course_info)

                """3.5.根据课程id拿到课程优惠券字典"""
                test_dict[ACCOUNT_KEY.format(user_id, course_id)] = AccountView.get_user_coupons_dict(
                    request, course_id=course_id
                )

            """4.拿到该用户的公共优惠券"""
            global_coupons_dict = AccountView.get_user_coupons_dict(request)
            test_dict['global_coupons_dict'] = global_coupons_dict
            my_res.code = 600
            my_res.msg = 'SUCCESS'
            my_res.data = test_dict

            """5.写进 Redis """
            """ ... """

        except MyException as e:
            my_res.code = e.code
            my_res.msg = e.msg
        except Exception as e:
            my_res.code = -600
            my_res.msg = str(e)
        return Response(my_res.dict)

2.3结算数据结构

代码语言:javascript复制
account = {
        # 结算接口数据结构
        "account_{}_{}": {
            "course_info": {
                "id": "XXX",
                "title": "XXX",
                "course_img": "XXX",
                "price_policy_dict": {
                    "valid_period": "XXX",
                    "price": "XXX"
                },
                "default_price_policy_id": "XXX"
            },
            # ...
            # ...

            "course_coupons": {
                "1": {
                    'course_id': "XXX",
                    'name': "XXX",
                    'coupon_type': "XXX",
                    'money_equivalent_value': "XXX",
                    'off_percent': "XXX",
                    'minimum_consume': "XXX",
                    'valid_begin_date': "XXX",
                    'valid_end_date': "XXX",
                },
                # ...
                # ...
            }
        },

        "global_coupon": {
            "1": {
                'name': "XXX",
                'coupon_type': "XXX",
                'money_equivalent_value': "XXX",
                'off_percent': "XXX",
                'minimum_consume': "XXX",
                'valid_begin_date': "XXX",
                'valid_end_date': "XXX",
            },
            # ...
            # ...
        }

    }

0 人点赞