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

时间:2023-03-08 16:59:54

考试第三部分:Django

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

select * from order where id >= 12

order.object.filter(id__gte=12)

select * from order where id != 12 

order.object.filter(id__exclude=12)

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

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

select * from order where id between 20 and 100

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

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)))

select * from order order by id desc,age asc

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

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

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

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

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

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

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

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

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

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

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

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

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'),
}
}

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. 通用评论表"

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

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

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)

假设一个订单号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表之外,其他表都要操作!

二、立即结算需求

看结算页面

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

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

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

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

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

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

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

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

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

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

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

总结:

后端接收:
贝里的数量和付款金额 a. 去结算中心获取要支付课程 b. 生成订单 c. 生成 订单详细,专题课购买表(如果优惠券>实际金额,则按照实际扣款) d. 如果使用了贝里,在贝里交易表中,记录一下; e. 优惠券表更新 f. 计算规则:
前端发送的金额和后端计算的金额 --> 相同,跳转到支付宝支付; 后端计算规则:
先计算绑定课程的优惠券
总价再使用未绑定课程优惠券
贝里抵扣 最终得到,实际付款金额 注意:多个表操作,使用事务

作业:

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
# - 否则:非法请求

使用postman测试 create

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

查看Pycharm控制台输出:

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

使用postman测试 list

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

查看Pycharm控制台输出:

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

说明:

结算中心有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