python 全栈开发,Day105(路飞其他数据库表结构,立即结算需求)

考试第三部分:Django

16.  列列举你熟悉的Http协议头以及作用。(1分)

Accept-Charset:  用于告诉浏览器,客户机采用的编码
Host: 客户机通过这个头告诉服务器,想访问的主机名
User-Agent: 客户机通过这个头告诉服务器,客户机的软件环境
Cookie: 客户机通过这个头可以向服务器带数据
View Code

 

17. 状态码含义:200、301、302、304、404、500。(2分)

200 请求已成功
301 永久重定向
302 临时重定向
304 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源
404 资源找不到
500 服务器内部错误,无法完成请求
View Code

 

18. 简述cookie和session?(2分)

cookie是保存在客户端浏览器的,每次请求网页,会携带cookie
session是保存在服务器上面的,用来判断请求客户端的cookie是否合法
View Code

 

19. django中get和filter的区别?(1分)

get 返回一个object。当返回结果,有且只有一条时,才会返回。否则报错
filter 返回一个queryset对象。即使没有匹配到条件,不会报错,返回一个空的queryset对象
View Code

 

20.  django的中间件在1.7和1.11版本间有什什么区别?(1分)

1. 路由编写
2. ORM中,外键一定要有on_delete属性
3. 中间件
4. 模板配置
View Code

 

21. django中contenttypes组件的作⽤用?(1分)

用来解决一个对和多个表做外键关联,并且关联的字段,不会在表中产生新的字段
View Code

 

22.   django中Q的作⽤用?(2分)

用来做一些复杂查询,比如or
View Code

 

23. 将如下SQL语句句使⽤用Django的ORM实现:(3分) 

select * from order where id >= 12

order.object.filter(id__gte=12)
View Code

select * from order where id != 12 

order.object.filter(id__exclude=12)
View Code

select * from order where id in [1,3,4]

order.object.filter(id__in=[1,3,4])
View Code

select * from order where id between 20 and 100

order.object.filter(id__range([20,100])
View Code

select * from order where id > 20 and (num < 60 or num > 70 ) 

# 注意:Q(id__gt=20)是一组查询,&表示and,| 表示or
# Q(Q(num__lt=60)|Q(num__gt=70)) 是另外一组查询
order.object.filter(Q(id__gt=20) & Q(Q(num__lt=60)|Q(num__gt=70)))
View Code

select * from order order by id desc,age asc

# 默认使用asc(升序),-id表示以id字段,进行desc(降序)来排序
order.object.all().order_by('-id','age')
View Code

 

24.    编写查询语句句:(5分,前2个每个1分,最后⼀一题3分)

•  查看所有学⽣生,并打印 姓名、班级名称

# 注意:一定要以Student为基准表
# 当filter字段有其他表时,使用inner join。当使用values时,它是left join
# 一个班级在创建的时候,是没有学生的。所以以classes为基础表时,会造成数据不准确
Student.objects.all().values('name','classes__name')
View Code

• 查看班级名称为"全栈12期"的所有学⽣生

Classes.object.filter(name="全栈12期").value("student__name")
View Code

• 查看没有学⽣生的所有班级ID、班级名称

# 当班级表的学生记录为空时,使用student__isnull=True
Classes.objects.filter(student__isnull=True).values('id','name')
View Code

 

25. django中遇到复杂的SQL时ORM⽆无法完成,如何使⽤用原⽣生SQL执⾏行行?(2分)

1. 根据connections['default'].cursor(),最接近于pymysql操作
2. 使用extra
View Code

cursor = connection.cursor() 表示连接默认的数据库,看settings.py中的数据库配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
View Code

defalut表示默认的数据库。django可以同时连接多个数据库,可以这样

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },
    'default2': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
View Code

 

extra是django自带的,默认就可以使用。而connection使用时,需要导入模块!

 

详情请参考链接:

http://www.cnblogs.com/wupeiqi/articles/5246483.html

搜索 "其他操作",展开里面的代码,就可以看到了!

 

一、路飞其他数据库表结构

深科技

先来看深科技的相关表

# ############深科技相关 ###################
class ArticleSource(models.Model):
    """文章来源"""
    name = models.CharField(max_length=64, unique=True)

    class Meta:
        verbose_name_plural = "16. 文章来源"

    def __str__(self):
        return self.name


class Article(models.Model):
    """文章资讯"""
    title = models.CharField(max_length=255, unique=True, db_index=True, verbose_name="标题")
    source = models.ForeignKey("ArticleSource", verbose_name="来源")
    article_type_choices = ((0, '资讯'), (1, '视频'))
    article_type = models.SmallIntegerField(choices=article_type_choices, default=0)
    brief = models.TextField(max_length=512, verbose_name="摘要")
    head_img = models.CharField(max_length=255)

    pub_date = models.DateTimeField(verbose_name="上架日期")
    offline_date = models.DateTimeField(verbose_name="下架日期")
    status_choices = ((0, '在线'), (1, '下线'))
    status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="状态")
    order = models.SmallIntegerField(default=0, verbose_name="权重", help_text="文章想置顶,可以把数字调大,不要超过1000")
    vid = models.CharField(max_length=128, verbose_name="视频VID", help_text="文章类型是视频, 则需要添加视频VID", blank=True, null=True)
    comment_num = models.SmallIntegerField(default=0, verbose_name="评论数")
    agree_num = models.SmallIntegerField(default=0, verbose_name="点赞数")
    view_num = models.SmallIntegerField(default=0, verbose_name="观看数")
    collect_num = models.SmallIntegerField(default=0, verbose_name="收藏数")

    date = models.DateTimeField(auto_now_add=True, verbose_name="创建日期")

    position_choices = ((0, '信息流'), (1, 'banner大图'), (2, 'banner小图'))
    position = models.SmallIntegerField(choices=position_choices, default=0, verbose_name="位置")
    comment = GenericRelation("Comment")  # 用于GenericForeignKey反向查询, 不会生成表字段,切勿删除,如有疑问请联系老村长

    class Meta:
        verbose_name_plural = "17. 文章"

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


class ArticleDetail(models.Model):
    """文章详细"""
    article = models.OneToOneField(to='Article')
    content = models.TextField(verbose_name="文章正文")


class Collection(models.Model):
    """通用收藏表"""

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    account = models.ForeignKey("Account")
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ('content_type', 'object_id', 'account')
        verbose_name_plural = "18. 通用收藏表"


class Comment(models.Model):
    """通用的评论表"""
    content_type = models.ForeignKey(ContentType, blank=True, null=True, verbose_name="类型")
    object_id = models.PositiveIntegerField(blank=True, null=True)
    content_object = GenericForeignKey('content_type', 'object_id')

    p_node = models.ForeignKey("self", blank=True, null=True, verbose_name="父级评论")
    content = models.TextField(max_length=1024)
    account = models.ForeignKey("Account", verbose_name="会员名")
    disagree_number = models.IntegerField(default=0, verbose_name="")
    agree_number = models.IntegerField(default=0, verbose_name="赞同数")
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.content

    class Meta:
        verbose_name_plural = "19. 通用评论表"
View Code

打开网页: https://www.luffycity.com/news

Article

中间的 斯嘉丽约翰逊 就是banner大图,对应Article表中position_choices类型

banner大图右边的罗胖子,是banner小图

banner大图下面的文章列表,表示信息流

ArticleDetail

文章详情表,存放了文章详情。里面包含了大量的html标签!占用空间比较大。它需要单独拆分。

它和文章表,做了一对一关联。也就是外键+唯一索引

Collection

通用评论表,看下面这几行代码

content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')

如果要使用django ContentType组件,上面这3行,是必须要有的!不要问我为什么,这是约定俗成的!

例如:文章,课程,视频...,这些都需要评论。通常的做法是给这些表做外键,关联到评论表。那么这些表在数据库中会产生一个关联字段!

使用 ContentType组件后,只需要在这些表加一个属性等于GenericRelation("Collection"),那么就可以建立关联了。而且数据库不会产生新的字段!所以说,它是一个通用的评论表

 

 

订单相关

再来看订单相关的表

# ################# 订单相关 #################
class EnrolledCourse(models.Model):
    """已报名课程,不包括学位课程"""
    account = models.ForeignKey("Account")
    course = models.ForeignKey("Course", limit_choices_to=~Q(course_type=2))
    enrolled_date = models.DateTimeField(auto_now_add=True)
    valid_begin_date = models.DateField(verbose_name="有效期开始自")
    valid_end_date = models.DateField(verbose_name="有效期结束至")
    status_choices = ((0, '已开通'), (1, '已过期'))
    status = models.SmallIntegerField(choices=status_choices, default=0)
    order_detail = models.OneToOneField("OrderDetail")  # 使订单购买后支持 课程评价

    def __str__(self):
        return "%s:%s" % (self.account, self.course)

    class Meta:
        verbose_name_plural = "34. 报名专题课"


class ScoreRule(models.Model):
    """积分规则"""
    score_rule_choices = (
        (0, '未按时交作业'),
        (1, '未及时批改作业'),
        (2, '作业成绩'),
        (3, '未在规定时间内对学员进行跟进'),
        (4, '未在规定时间内回复学员问题'),
        (5, '收到学员投诉'),
        (6, '导师相关'),
        (7, '学位奖学金'),
    )
    rule = models.SmallIntegerField(choices=score_rule_choices, verbose_name="积分规则")
    score_type_choices = ((0, '奖励'), (1, '惩罚'), (2, '初始分配'))
    score_type = models.SmallIntegerField(choices=score_type_choices, verbose_name="奖惩", default=0)
    score = models.IntegerField(help_text="扣分数与贝里相等,若为0则代表规则的值可以从别处取得")
    # maturity_days = models.IntegerField("成熟周期", help_text="自纪录创建时开始计算")
    memo = models.TextField(blank=True, null=True)

    def __str__(self):
        return "%s-%s:%s" % (self.get_rule_display(), self.get_score_type_display(), self.score)

    class Meta:
        unique_together = ('rule', 'score_type')
        verbose_name_plural = "29. 奖惩规则"


class ScoreRecord(models.Model):
    """积分奖惩记录"""
    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.PositiveIntegerField(blank=True, null=True)
    content_object = GenericForeignKey('content_type', 'object_id')

    degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, verbose_name="关联学位课程")

    score_rule = models.ForeignKey("ScoreRule", verbose_name="关联规则")
    account = models.ForeignKey("Account", verbose_name="被执行人")
    score = models.IntegerField(
        verbose_name="金额(贝里)")  # 这里单独有一个字段存积分而不是从score_rule里引用的原因是考虑到如果引用的话, # 一旦score_rule里的积分有变更,那么所有用户的历史积分也会被影响

    received_score = models.IntegerField("实际到账金额贝里)", help_text="仅奖励用", default=0)
    # balance = models.PositiveIntegerField(verbose_name="奖金余额(贝里)")

    maturity_date = models.DateField("成熟日期(可提现日期)")

    applied = models.BooleanField(default=False, help_text="奖赏纪录是否已被执行", verbose_name="是否已被执行")
    applied_date = models.DateTimeField(blank=True, null=True, verbose_name="事件生效日期")
    date = models.DateTimeField(auto_now_add=True, verbose_name="事件触发日期")
    memo = models.TextField(blank=True, null=True)

    def __str__(self):
        return "%s-%s - %s - %s" % (self.id, self.score_rule, self.account, self.score,)

    class Meta:
        verbose_name_plural = "30. 奖惩记录"


class CourseSchedule(models.Model):
    """课程进度计划表,针对学位课程,每开通一个模块,就为这个学员生成这个模块的推荐学习计划表,后面的奖惩均按此表进行"""
    study_record = models.ForeignKey("StudyRecord")
    homework = models.ForeignKey("Homework")
    recommend_date = models.DateField("推荐交作业日期")

    def __str__(self):
        return "%s - %s - %s " % (self.study_record, self.homework, self.recommend_date)

    class Meta:
        unique_together = ('study_record', 'homework')
        verbose_name_plural = "33. 课程模块计划表(学位课)"


class StudyRecord(models.Model):
    """学位课程的模块学习进度,报名学位课程后,每个模块会立刻生成一条学习纪录"""
    enrolled_degree_course = models.ForeignKey("EnrolledDegreeCourse")
    course_module = models.ForeignKey("Course", verbose_name="学位模块", limit_choices_to={'course_type': 2})
    open_date = models.DateField(blank=True, null=True, verbose_name="开通日期")
    end_date = models.DateField(blank=True, null=True, verbose_name="完成日期")
    status_choices = ((2, '在学'), (1, '未开通'), (0, '已完成'))
    status = models.SmallIntegerField(choices=status_choices, default=1)

    class Meta:
        verbose_name_plural = "39. 学习记录表(报名学位课程后,每个模块会立刻生成一条学习纪录)"
        unique_together = ('enrolled_degree_course', 'course_module')

    def __str__(self):
        return '%s-%s' % (self.enrolled_degree_course, self.course_module)

    def save(self, *args, **kwargs):
        if self.course_module.degree_course_id != self.enrolled_degree_course.degree_course_id:
            raise ValueError("学员要开通的模块必须与其报名的学位课程一致!")

        super(StudyRecord, self).save(*args, **kwargs)


class DegreeRegistrationForm(models.Model):
    """学位课程报名表"""
    enrolled_degree = models.OneToOneField("EnrolledDegreeCourse")
    current_company = models.CharField(max_length=64, )
    current_position = models.CharField(max_length=64, )
    current_salary = models.IntegerField()
    work_experience_choices = ((0, "应届生"),
                               (1, "1年"),
                               (2, "2年"),
                               (3, "3年"),
                               (4, "4年"),
                               (5, "5年"),
                               (6, "6年"),
                               (7, "7年"),
                               (8, "8年"),
                               (9, "9年"),
                               (10, "10年"),
                               (11, "超过10年"),
                               )
    work_experience = models.IntegerField()
    open_module = models.BooleanField("是否开通第1模块", default=True)
    stu_specified_mentor = models.CharField("学员自行指定的导师名", max_length=32, blank=True, null=True)
    study_plan_choices = ((0, "1-2小时/天"),
                          (1, "2-3小时/天"),
                          (2, "3-5小时/天"),
                          (3, "5小时+/天"),
                          )
    study_plan = models.SmallIntegerField(choices=study_plan_choices, default=1)
    why_take_this_course = models.TextField("报此课程原因", max_length=1024)
    why_choose_us = models.TextField("为何选路飞", max_length=1024)
    your_expectation = models.TextField("你的期待", max_length=1024)
    memo = models.CharField(max_length=255, blank=True, null=True)

    class Meta:
        verbose_name_plural = "35. 报名表(学位课)"

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


class EnrolledDegreeCourse(models.Model):
    """已报名的学位课程"""
    account = models.ForeignKey("Account")
    degree_course = models.ForeignKey("DegreeCourse")
    enrolled_date = models.DateTimeField(auto_now_add=True)
    valid_begin_date = models.DateField(verbose_name="有效期开始自", blank=True, null=True)  # 开通第一个模块时,再添加课程有效期,2年
    valid_end_date = models.DateField(verbose_name="有效期结束至", blank=True, null=True)
    status_choices = (
        (0, '在学中'),
        (1, '休学中'),
        (2, '已毕业'),
        (3, '超时结业'),
        (4, '未开始'),
        # (3, '其它'),
    )
    study_status = models.SmallIntegerField(choices=status_choices, default=0)
    mentor = models.ForeignKey("Account", verbose_name="导师", related_name='my_students',
                               blank=True, null=True, limit_choices_to={'role': 1})
    mentor_fee_balance = models.PositiveIntegerField("导师费用余额", help_text="这个学员的导师费用,每有惩罚,需在此字段同时扣除")
    order_detail = models.OneToOneField("OrderDetail")  # 使订单购买后支持填写报名表

    def __str__(self):
        return "%s:%s" % (self.account, self.degree_course)

    class Meta:
        unique_together = ('account', 'degree_course')
        verbose_name_plural = "36. 报名学位课"


class Order(models.Model):
    """订单"""
    payment_type_choices = ((0, '微信'), (1, '支付宝'), (2, '优惠码'), (3, '贝里'))
    payment_type = models.SmallIntegerField(choices=payment_type_choices)

    payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True)

    order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True)  # 考虑到订单合并支付的问题
    account = models.ForeignKey("Account")
    actual_amount = models.FloatField(verbose_name="实付金额")

    status_choices = ((0, '交易成功'), (1, '待支付'), (2, '退费申请中'), (3, '已退费'), (4, '主动取消'), (5, '超时取消'))
    status = models.SmallIntegerField(choices=status_choices, verbose_name="状态")
    date = models.DateTimeField(auto_now_add=True, verbose_name="订单生成时间")
    pay_time = models.DateTimeField(blank=True, null=True, verbose_name="付款时间")
    cancel_time = models.DateTimeField(blank=True, null=True, verbose_name="订单取消时间")

    class Meta:
        verbose_name_plural = "37. 订单表"

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


class OrderDetail(models.Model):
    """订单详情"""
    order = models.ForeignKey("Order")

    content_type = models.ForeignKey(ContentType)  # 可关联普通课程或学位
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    original_price = models.FloatField("课程原价")
    price = models.FloatField("折后价格")
    content = models.CharField(max_length=255, blank=True, null=True)  #
    valid_period_display = models.CharField("有效期显示", max_length=32)  # 在订单页显示
    valid_period = models.PositiveIntegerField("有效期(days)")  # 课程有效期
    memo = models.CharField(max_length=255, blank=True, null=True)

    def __str__(self):
        return "%s - %s - %s" % (self.order, self.content_type, self.price)

    class Meta:
        verbose_name_plural = "38. 订单详细"
        unique_together = ("order", 'content_type', 'object_id')


class TransactionRecord(models.Model):
    """贝里交易纪录"""
    account = models.ForeignKey("Account")
    amount = models.IntegerField("金额")
    balance = models.IntegerField("账户余额")
    transaction_type_choices = ((0, '收入'), (1, '支出'), (2, '退款'), (3, "提现"))  # 2 为了处理 订单过期未支付时,锁定期贝里的回退
    transaction_type = models.SmallIntegerField(choices=transaction_type_choices)

    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.PositiveIntegerField(blank=True, null=True, verbose_name="关联对象")
    content_object = GenericForeignKey('content_type', 'object_id')

    transaction_number = models.CharField(unique=True, verbose_name="流水号", max_length=128)
    date = models.DateTimeField(auto_now_add=True)
    memo = models.CharField(max_length=128, blank=True, null=True)

    class Meta:
        verbose_name_plural = "40. 贝里交易记录"

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


class HomeworkRecord(models.Model):
    """学员作业记录及成绩"""
    homework = models.ForeignKey("Homework")
    student = models.ForeignKey("EnrolledDegreeCourse", verbose_name="学生")
    score_choices = ((100, 'A+'),
                     (90, 'A'),
                     (85, 'B+'),
                     (80, 'B'),
                     (70, 'B-'),
                     (60, 'C+'),
                     (50, 'C'),
                     (40, 'C-'),
                     (-1, 'D'),
                     (0, 'N/A'),
                     (-100, 'COPY'),
                     )
    score = models.SmallIntegerField(verbose_name="分数", choices=score_choices, null=True, blank=True)
    mentor = models.ForeignKey("Account", related_name="my_stu_homework_record", limit_choices_to={'role': 1},
                               verbose_name="导师")
    mentor_comment = models.TextField(verbose_name="导师批注", blank=True, null=True)  # 导师
    status_choice = (
        (0, '待批改'),
        (1, '已通过'),
        (2, '不合格'),
    )
    status = models.SmallIntegerField(verbose_name='作业状态', choices=status_choice, default=0)

    submit_num = models.SmallIntegerField(verbose_name='提交次数', default=0)
    correct_date = models.DateTimeField('备注日期', blank=True, null=True)
    note = models.TextField(blank=True, null=True)
    date = models.DateTimeField("作业提交日期", auto_now_add=True)

    check_date = models.DateTimeField("批改日期", null=True, blank=True)

    update_time = models.DateTimeField(auto_now=True, verbose_name="提交日期")

    # homework_path = models.CharField(verbose_name='作业路径', max_length=256,blank=True,null=True) 作业路径可以动态拿到,没必要存

    reward_choice = ((0, '新提交'),
                     (1, '按时提交'),
                     (2, '未按时提交'),
                     (3, '成绩已奖励'),
                     (4, '成绩已处罚'),
                     (5, '未作按时检测'),
                     )
    reward_status = models.SmallIntegerField(verbose_name='作业记录奖惩状态', default=0)

    def __str__(self):
        return "%s %s" % (self.homework, self.student)

    class Meta:
        verbose_name_plural = "41. 作业"
        unique_together = ("homework", "student")


class StuFollowUpRecord(models.Model):
    """学员跟进记录"""
    enrolled_degree_course = models.ForeignKey("EnrolledDegreeCourse", verbose_name="学生")
    mentor = models.ForeignKey("Account", related_name='mentor', limit_choices_to={'role': 1}, verbose_name="导师")
    followup_tool_choices = ((0, 'QQ'), (1, '微信'), (2, '电话'), (3, '系统通知'))
    followup_tool = models.SmallIntegerField(choices=followup_tool_choices, default=1)
    record = models.TextField(verbose_name="跟进记录")
    attachment_path = models.CharField(max_length=128, blank=True, null=True, verbose_name="附件路径", help_text="跟进记录的截图等")
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "42. 学员跟进记录"

    def __str__(self):
        return "%s --%s --%s" % (self.enrolled_degree_course, self.record, self.date)
View Code

 

假设一个订单号20242359346940369,里面包含了10个课程。要如何体现一个订单号有10个课程呢?

需要这样设计

订单表

+----+-----------+----------+
| id | 订单号    | 用户id   |
+----+-----------+----------+
|  1 | 335446557 |      234 |
+----+-----------+----------+

订单详细表

+----+-----------+----------+--------+--------+--------+
| id | 订单id    | 课程id   | 原价   | 价格   | 周期   |
+----+-----------+----------+--------+--------+--------+
|  1 | 335446557 |       12 | 20     | 15     | 7      |
...
+----+-----------+----------+--------+--------+--------+

 

Order

订单表,payment_number表示支付第3方订单号。比如:用户使用支付宝付款后,支付宝会发送一条POST请求,访问你的服务器,它会携带一个支付宝产生的订单号。这个就是payment_number的作用,用来进行后续的查询是否到账了!

actual_amount 表示抛开使用优惠券,贝里之后,实际付款的金额!

OrderDetail

订单详细表,content表示备注,用来给运营人员来加一些备注信息!

TransactionRecord

贝里交易纪录,当付款使用贝里时,这里会产生一条记录!

DegreeRegistrationForm

学位课程报名表,当用户购买学位课程后,是不能直接观看视频的。需要添加报名表才行,需要填写一些相关信息。有了这些信息后,才能给用户分配导师,促进学习!

ScoreRule

积分规则,主要约束导师的。初始分配:学员分配到某位导师后,这位导师的账户就会多一些积分,比如1500贝里。

如果学员未交作业,导师没有批改作业,受到学员投诉.... 都是要扣贝里的。这些贝里,到一定时间是可以提现的!

成熟周期这里注释掉了,意思就是学员毕业,就可以提现了!

在linux上面做了一个任务计划,每天凌晨2点跑一个脚本,用来更新成熟周期。

EnrolledCourse

已报名课程,不包括学位课程。也就是说,它只负责专题课。

当用户购买一个专题课后,需要在EnrolledCourse,Order,OrderDetail。这3个表产生记录即可

如果使用贝里支付,还需要操作TransactionRecord表。

设计到优惠券,还需要操作优惠券相关的表

 

如果是购买了学位课,除了EnrolledCourse表之外,其他表都要操作!

 

二、立即结算需求

看结算页面

当点击立即支付时,会向后端发送一些数据。发送哪些数据呢?

将页面的课程相关信息都发送过去?没有必要!为什么呢?

因为结算中心的数据,是存放在redis中的。所以发送时,只需要发送2个数据。

 

使用的贝里数,以及经过计算后的金额。后端接收到这2个数据时,再根据结算中心的数据,做计算。

当前端发送的金额和后端计算的金额一致时,跳转到支付宝页面,否则提示计算错误!

为什么呢?因为用户页面看到的是498,结果跳转到支付宝页面时,要付款510块,用户肯定不干了!

 

计算规则是这样的,先计算使用绑定课程的优惠券,然后将所有计算后的所有课程,做一个总价。

最后使用未绑定课程的优惠券,对总价进行计算。再使用贝里抵扣,最终得到实际付款金额!

 

注意:如果优惠券的金额大于实际课程的金额,则按照实际扣除。那么这个优惠券就不能使用了!没有返现的功能!

多个表操作时,要基于事务来做

 

总结:

后端接收:
    贝里的数量和付款金额 

a. 去结算中心获取要支付课程

b. 生成订单

c. 生成 订单详细,专题课购买表(如果优惠券>实际金额,则按照实际扣款)

d. 如果使用了贝里,在贝里交易表中,记录一下;
    
e. 优惠券表更新

f. 计算规则:
    前端发送的金额和后端计算的金额 --> 相同,跳转到支付宝支付;
    
    后端计算规则: 
    先计算绑定课程的优惠券
    总价再使用未绑定课程优惠券
    贝里抵扣
    
    最终得到,实际付款金额
    
注意:多个表操作,使用事务 
View Code

 

作业:

1.继续完成结算中心逻辑

2.熟悉今天的表结构

 

结算中心逻辑

修改views目录下的payment.py

create和list代码如下:

import json
import redis
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
from rest_framework.response import Response
from api.utils.auth import LuffyAuthentication
from api import models
from api.utils.response import BaseResponse

from django_redis import get_redis_connection

CONN = get_redis_connection("default")  # 使用redis连接池


class PaymentView(ViewSetMixin, APIView):
    authentication_classes = [LuffyAuthentication, ]

    def create(self, request, *args, **kwargs):
        """
        在结算中添加课程
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        # 1.接收用户选择的要结算的课程ID列表
        choice_list = request.data.get('course_id')
        # print(choice_list)

        # 2.清空当前用户request.user.id结算中心的数据
        #   key = payment_1*
        CONN.delete('payment_1*')

        # 3.循环要加入结算中的所有课程ID列表

        """
        for course_id in 用户提交课程ID列表:
            3.1 根据course_id,request.user.id去购物车中获取商品信息:商品名称、图片、价格(id,周期,显示周期,价格)
            3.2 根据course_id,request.user.id获取 
                    - 当前用户
                    - 当前课程
                    - 可用的优惠券

            加入结算中心

            提示:可以使用contenttypes
        """
        '''
        # 2.1 课程是否存在?
        temp = {
                    'id': CONN.hget(key, 'id').decode('utf-8'),
                    'name': CONN.hget(key, 'name').decode('utf-8'),
                    'img':CONN.hget(key, 'img').decode('utf-8'),
                    'default_price_id':CONN.hget(key, 'default_price_id').decode('utf-8'),
                    'price_policy_dict': json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))
                }
        '''
        user_id = request.user.id
        print('用户id', user_id)

        # 从购物车中获取数据
        pattern = "shopping_car_%s_%s" % (request.user.id, '*',)
        # print(pattern)
        user_key_list = CONN.keys(pattern)

        for course_id in choice_list:  # 用户选择的要结算的课程ID列表
            for key in user_key_list:  # 当前用户购物车列表
                id = CONN.hget(key, 'id').decode('utf-8')  # 获取购物车课程id
                if id == course_id:  # 判断用户选择课程id和购物车课程id相等
                    name = CONN.hget(key, 'name').decode('utf-8')  # 课程名
                    default_price_id = CONN.hget(key, 'default_price_id').decode('utf-8')  # 默认价格策略id
                    # 所有价格策略
                    price_policy_dict = json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))

                    print('课程id',id)
                    print('课程名',name)
                    print('默认价格策略',default_price_id)
                    valid_period = price_policy_dict[default_price_id].get('valid_period_display')
                    print('有效期', valid_period)
                    print('原价',price_policy_dict[default_price_id].get('price'))
                    print('折后价', price_policy_dict[default_price_id].get('price'))
                    print('所有价格策略',price_policy_dict)

                    # 加入结算中心redis
                    j_key = "payment_%s_%s" % (user_id, id,)

                    CONN.hset(j_key, 'id', course_id)
                    CONN.hset(j_key, 'name', name)
                    CONN.hset(j_key, 'price_id', default_price_id)
                    CONN.hset(j_key, 'price', price_policy_dict[default_price_id].get('price'))
                    CONN.hset(j_key, 'valid_period', valid_period)
                    CONN.hset(j_key, 'discount_price', price_policy_dict[default_price_id].get('price'))

                    # 查询当前课程的 绑定课程优惠券
                    obj1 = models.Course.objects.filter(id=id).first()
                    if obj1.coupon.all():  # 反向查询该课程的所有优惠券
                        print("绑定课程优惠券#########################")
                        for i in obj1.coupon.all():  # 循环每一个优惠券
                            print('绑定课程优惠券个数',len(obj1.coupon.all()))
                            coupon_dict = {}  # 空字典
                            for j in range(len(obj1.coupon.all())):  # for循环长度
                                if i.coupon_type == 0:  # 类型为立减
                                    coupon_dict[j] = '{}{}'.format(i.get_coupon_type_display(), i.money_equivalent_value)
                                    # 增加到redis中
                                    CONN.hset(j_key, 'coupon_dict', coupon_dict)
                                    # print(111)
                                    print(
                                        '{}{}'.format(i.get_coupon_type_display(), i.money_equivalent_value))
                                elif i.coupon_type == 1:
                                    coupon_dict[j] = '满{}减{}'.format(i.minimum_consume, i.money_equivalent_value)
                                    CONN.hset(j_key, 'coupon_dict', coupon_dict)
                                    print('满{}减{}'.format(i.minimum_consume, i.money_equivalent_value))
                                else:
                                    # print(i.id)
                                    coupon_dict[j] = '{}折'.format(i.off_percent)
                                    CONN.hset(j_key, 'coupon_dict', coupon_dict)
                                    print('{}折'.format(i.off_percent))


        # 绑定课程的优惠券


        # obj = models.CouponRecord.objects.filter(account=user_id, coupon__object_id__isnull=False)
        # print('绑定课程优惠券#################')
        # if obj:
        #     for i in obj:
        #         if i.coupon.coupon_type == 0:
        #             print('{}{}'.format(i.coupon.get_coupon_type_display(), i.coupon.money_equivalent_value))
        #         elif i.coupon.coupon_type == 1:
        #             print('满{}减{}'.format(i.coupon.minimum_consume, i.coupon.money_equivalent_value))
        #         else:
        #             # print(i.coupon.id)
        #             print('{}折'.format(i.coupon.off_percent))


        # 4.获取当前用户所有未绑定课程优惠券
        #       - 未使用
        #       - 有效期内
        #       - 加入结算中心:glocal_coupon_用户ID

        # 当前用户未绑定课程的优惠券
        obj2 = models.CouponRecord.objects.filter(account=user_id, coupon__object_id__isnull=True)
        print('未绑定课程优惠券#################')
        if obj2:
            # 通用优惠券redis key
            coupon_key = "general_coupon_%s" % (user_id)
            for i in obj2:
                general_coupon_dict = {}  # 空字典
                print('未绑定课程优惠券个数 %s' % (len(obj2)))
                for j in range(len(obj2)):
                    if i.coupon.coupon_type == 0:  # 类型为立减
                        general_coupon_dict[j] = '{}{}'.format(i.coupon.get_coupon_type_display(), i.coupon.money_equivalent_value)
                        CONN.hset(coupon_key, 'coupon_dict', general_coupon_dict)
                        print('{}{}'.format(i.coupon.get_coupon_type_display(), i.coupon.money_equivalent_value))
                    elif i.coupon.coupon_type == 1:
                        general_coupon_dict[j] = '满{}减{}'.format(i.coupon.minimum_consume, i.coupon.money_equivalent_value)
                        CONN.hset(coupon_key, 'coupon_dict', general_coupon_dict)
                        print('满{}减{}'.format(i.coupon.minimum_consume, i.coupon.money_equivalent_value))
                    else:
                        general_coupon_dict[j] = '{}折'.format(i.coupon.off_percent)
                        CONN.hset(coupon_key, 'coupon_dict', general_coupon_dict)
                        print('{}折'.format(i.coupon.off_percent))


        return Response('ok')

    def list(self, request, *args, **kwargs):
        """
        查看结算中心
        :param request:
        :param args:
        :param kwargs:
        :return:
        """

        # 1. 根据用户ID去结算中心获取该用户所有要结算课程
        course_id = request.query_params.get('course_id')
        print('课程id',course_id)
        obj = models.Course.objects.filter(id=course_id).first()
        print('结算课程',obj.name)
        # 2. 根据用户ID去结算中心获取该用户所有可用未绑定课程的优惠券
        user_id =request.user.id
        print('用户id', user_id)
        obj2 = models.CouponRecord.objects.filter(account=user_id, coupon__object_id__isnull=True)
        if obj2:
            for i in obj2:
                if i.coupon.coupon_type == 0:
                    print('{}{}'.format(i.coupon.get_coupon_type_display(), i.coupon.money_equivalent_value))
                elif i.coupon.coupon_type == 1:
                    print('满{}减{}'.format(i.coupon.minimum_consume, i.coupon.money_equivalent_value))
                else:
                    print(i.coupon.id)
                    print('{}折'.format(i.coupon.off_percent))

        # 3. 用户表中获取贝里余额
        beili = models.Account.objects.filter(id=user_id).first()
        print('用户贝里',beili.balance)

        # 4. 以上数据构造成一个字典

        return Response('...')

    def update(self, request, *args, **kwargs):
        """
        更新优惠券
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        # 1. 获取用户提交:
        #       course_id=1,coupon_id=3
        #       course_id=0,coupon_id=6

        # 2. course_id=1 --> 去结算中心获取当前用户所拥有的绑定当前课程优惠,并进行校验
        #       - 成功:defaul_coupon_id=3
        #       - 否则:非法请求

        # 3. course_id=0 --> 去结算中心获取当前用户所拥有的未绑定课程优惠,并进行校验
        #       - 成功:defaul_coupon_id=3
        #       - 否则:非法请求
View Code

使用postman测试 create

查看Pycharm控制台输出:

用户id 1
课程id 1
课程名 Python开发入门7天特训营
默认价格策略 1
有效期 1周
原价 10.0
折后价 10.0
所有价格策略 {'2': {'valid_period': 30, 'id': 2, 'price': 50.0, 'valid_period_display': '1个月'}, '1': {'valid_period': 7, 'id': 1, 'price': 10.0, 'valid_period_display': '1周'}}
绑定课程优惠券#########################
绑定课程优惠券个数 2
8折
8折
绑定课程优惠券个数 2
满50减10
满50减10
未绑定课程优惠券#################
未绑定课程优惠券个数 1
立减10
View Code

 

使用postman测试 list

查看Pycharm控制台输出:

课程id 1
结算课程 Python开发入门7天特训营
用户id 1
立减10
用户贝里 100.0
View Code

说明:

结算中心有2个key

 一个是结算中心key

结算中心 = {
    'payment_用户id_课程id':{
        id:课程id,
        mame:课程名,
        price_id:价格策略id,
        price:原价,
        valid_period:有效期,
        discount_price:折后价,
        coupon_dict: {  # 绑定课程优惠券
            1:'优惠券1',
            2:'优惠券2',
            3:'优惠券3',
        }
    },
}

一个是用户通用优惠券,也就是未绑定课程优惠券

用户通用优惠券 = {
    'general_coupon_用户id':{
        coupon_dict: {  # 未绑定课程优惠券
            1:'优惠券1',
            2:'优惠券2',
            3:'优惠券3',
        }
    },
}

 

我没有把通用优惠放在结算中心key里面,为什么呢?

因为结算中心的key,都是用户id_课程id。如果用户购买了10个课程,那么就会产生10个key。

而每个key都用存储通用优惠券,那么这个通用优惠券重复了10次,完全没有必要!

所以我单独分离出来了!

 

完整代码请参考github

https://github.com/987334176/luffycity/archive/v1.6.zip

 

posted @ 2018-08-15 16:03  肖祥  阅读(679)  评论(0编辑  收藏  举报