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",
},
# ...
# ...
}
}