项目:路飞学城(在线学习的平台)

一、背景

目的是做一个在线的学习平台,提高学生的课程完成度

路飞学城:大目标是要取代线下班

  • - 录制专门的视频
  • - 导师的监管:如果没有主动和那个学生聊天,就扣钱
  • - 学生的激励:如果提前学完了,学习能力很强,可以免学费

二、系统架构

- 主站
    - rest api 
    - vue 
- 导师后台  (stark)
    - django 
    - stark 
- 管理后台  (stark)
    - django 
    - stark 
    

三、人员和部门

- 后端开发(5人)
    - 主站(3- 导师后台(1- 管理后台(1- 前端开发(1- UI (1- 测试(1- 运维(部署:上线)(1- 运营(推广出去)(2- 销售 (2- 导师 (2- 产品经理 (1- 人事
- 财务

四、需求分析

主站
    - 课程
        - 普通课程、学位课
            - 普通课程:比较小的课程
            - 学位课:从开始+项目+就业 
                - 分配一对一的导师
                - 导师对你进行监管,督促你学习
                - 保就业
                - 如果你%50的时间学完了,退一般的学费,等。
        - 课程列表、课程详细
        - 课程周期的不同定价格
        - 个人的课程中心
    - 支付
        - 银联- 支付宝
        - 微信
        - 优惠券策略(满减(满多少减多少),立减,打折)
        - 贝里:可直接抵现金
        - 个人购物车

    - 深科技
        - 文章或者爬取的别人的文章(运营需要审核)
     - 深刻有哪些功能?
        - 点赞、评论、查看、收藏
- 登录 、注册 导师后台 管理后台

具体进一步谈业务需求

a、课程:
            业务相关:
                1、学位课,如果你学的快;最多返指定金额的65%
                2、作业做的好,会把额外的35%返给你
                3、普通课程购买课程后,在我的订单中去评价
                4、购物车的数据没有数据表。保存到redis里面去了
            
                两大部分操作
                #redis操作
                    在课程详细页面,选择价格策略,点击加入购物车,吧视频和价格策略放到了redis中
                    在购物车页面,
                        可以对redis中保存的购买的视频数据和价格数据可以删除,修改,添加,查看(刷新)
                        选中,去结算中心
                #数据库操作
                #pip freeze > requirement.txt #生成文件
                #pip install -r requirement.txt  #下载所有的版本
                    在结算页面:
                    优惠券
                        - 选择单一的优惠券(通用、满减、折扣)
                            - 课程优惠券:绑定课程的优惠券
                        - 选择全站的优惠券(通用、满减、折扣)
                          #单一的优惠券用完,再计算,看是否满足全站的通用卷
                        - 选择贝里
                        - 选择支付宝,点击立即支付
                             - 在数据库中生成一个订单
                                这个订单如果用了优惠券,优惠券状态改为已使用
                                如果用了贝里,贝里的金额减少
                                如果取消订单,优惠券才不是使用状态
                    在支付页面,扫码状态为已支付

                    在我的订单中应该去数据库中读数据
                    在购物车或者结算中心的时候去redis里面读数据
            课程列表和课程详细等等等
            如果购买课程
                - 普通课程
                - 学位课程
                    - 每个模块生成记录(人和课程模块的记录)  - 买完之后自动生成
                    - 开通模块(后台)
                    - 导师跟进(导师)
                    - 交作业(主站)
        b、深科技相关
            来源
            文章
            收藏
            通用评论表
        c、购买支付流程
            课程id和价格策略id
            传值的时候可以传两个值
                - 通过策略id找到买的那个课程
                - 通过课程id 获取所有的价格策略
                处理请求
                    1、检测课程是否上线,下线
                    2、通过课程id 获取所有的价格策略,和传过来的进行对比
                    3、检测用户提交的价格策略是否在所有的价格策略中
                    4、将购物的视频添加到购物车(redis) 
                        - 同一个课程只能买一个,不允许一下加入两个
                        吧数据放到redis中,那么怎么存?
                            一个人就有一个购物车。可以通过用户id来存
                            所有的购物车= {
                                1:{
                                    课程id1:{'prices':[],'price':9,'title':'python','img':'课程图片'},
                                    课程id2:{'prices':[]}
                                }  
                            }
                            #如果看到课程已经在里面了,就不在里面添加了,这也就是用redis的原因
                            class ShoppingCat(APIView):
                                def post():
                                    #点击加入购物车保存数据到redis
                                def get():
                                    #查看购物车列表
                                def delete():
                                    #删除
                                def patch():
                                    #局部更新
                                    pass
                    5、去结算(先简单的说一下)
                        买的所有的课程
                            课程1  价格策略  
                            课程2  价格策略  
                            课程3  价格策略  
                        点击了去结算
                            优惠券
                            贝里

五、表结构

课程相关的13张表
  课程大类表:e.g 前端,后端   课程子类表:e.g python ,linux   普通课程表   学位课表   
##学位课可以有如下好处   学位奖学金表
  
##当然学位课和普通课程共有的   价格策略表   优惠券表
  课程详细表   课程章节表   课程目录表   课程问答表   课程评价(先不讨论)
  课程大纲
  
##   老师表   contenttype   contentType 可以一下关联两张表,django自己提供了,不用自己创建

深科技相关的4张表
  文章来源
  文章资讯
  收藏(既能收藏课程,也能收藏文章,可以用contenttype)
  评论(可以用contenttype的形式也可以用ForeignKey的形式)

用户相关的两张表
  用户信息表account
  用户认证表userauthtoken
    - 如果用户初次登录,给用户创建一个token,如果已经有token了,下次用户访问的时候直接带上token,如果没有token就说明没有登录,就没有访问其他页面的权限

购买支付相关的7张表
  已报名课程表EnrolledCourse(
谁购买了那个课程
  学位课报名表
  已报名的学位课程表EnrolledDegreeCourse
  优惠券发放、消费记录
  订单表
  订单详情
  贝里交易记录

其他相关的6张表
  课程评价
  学位课程评价
  学位课程的模块学习进度
  为学生开通一个模块
  学生作业记录及成绩
  学员跟进记录
  
  
  
  
  
  

1、简单表结构设计分析:

陆飞学城项目(项目背景以及表结构设计)

需要知道的知识点:

  现在学位课和普通课程都有自己的价格策略和优惠券,现在是学位课已经有了自己的价格策略,那么如果普通课程也要加价格策略,怎么弄呢?

一下有三种方法:

方法一:再单独创建一个表

陆飞学城项目(项目背景以及表结构设计)

方法二:不用单独创建一个表,可以多添加一个字段,其中的一个为NULL。相比方法一省了一张表

陆飞学城项目(项目背景以及表结构设计)

陆飞学城项目(项目背景以及表结构设计)

方法三:可以通过ContentType ,这个表里面存的是应用的名字以及表名。但是在我们makemigrations和migrate的时候django已经帮我们创建好了,如下图。就不用我们自己创建了

陆飞学城项目(项目背景以及表结构设计)

 这样关联的好处:可以ForeignKey任何一张表。里面的xid是关联表的某一行数据,陆飞学城项目(项目背景以及表结构设计)

这样关联以后,如果想在价格策略里面在添加一行数据,怎么添加呢?

有两种方式:

方式一:手动插入

  • 自己手动查出content_type_id 和关联表里的某一行数据xid。查出来添加到价格策略表里面去

方式二:利用GenericForeignKey

  • 可以通过导入 GenericForeignKey(GenericForeignKey有自动查找的功能,协助你快速做操作,没有也可以,自己手动查询)
from django.contrib.contenttypes.fields import GenericForeignKey

使用:

content_object = GenericForeignKey('content_type', 'object_id') #一下可以查到两个表的数据,自动插入到价格策略表里了,
优惠券表也是如此。只是为了查询方便

另外补充一个GenericRelation

from django.contrib.contenttypes.fields import GenericRelation

GenericRelation:也是帮助做快速查询的,就像反向关联的releated_name=‘xx’。以后查找的时候通可以通过点字段名的方式去查找

 # 用于GenericForeignKey反向查询, 不会生成表字段,切勿删除
    coupon = GenericRelation("Coupon")  #也是帮助做快速查询的,可以直接查出所有的学位课的价格策略等

2、具体表结构设计如下:

  1 from django.db import models
  2 from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
  3 #GenericForeignKey有自动查找的功能,协助你快速做操作,没有也可以,自己手动查询
  4 #GenericRelation:也是帮助做快速查询的,可以直接查出所有的学位课的价格策略等
  5 
  6 from django.contrib.contenttypes.models import ContentType
  7 from django.db.models import Q
  8 from django.utils.safestring import mark_safe
  9 from django.db import models
 10 import hashlib
 11 
 12 class CourseCategory(models.Model):
 13     """课程大类, e.g 前端  后端..."""
 14     name = models.CharField(max_length=64, unique=True)
 15 
 16     def __str__(self):
 17         return "%s" % self.name
 18 
 19     class Meta:
 20         verbose_name = "课程大类"   #单数
 21         verbose_name_plural = "课程大类"  #复数
 22 
 23 class CourseSubCategory(models.Model):
 24     """课程子类, e.g python linux """
 25     category = models.ForeignKey("CourseCategory",verbose_name='所属类')
 26     name = models.CharField(max_length=64, unique=True)
 27 
 28     def __str__(self):
 29         return "%s" % self.name
 30 
 31     class Meta:
 32         verbose_name = "课程子类"
 33         verbose_name_plural = "课程子类"
 34 
 35 class DegreeCourse(models.Model):
 36     """学位课程"""
 37     name = models.CharField(max_length=128, unique=True)
 38     course_img = models.CharField(max_length=255, verbose_name="缩略图")
 39     brief = models.TextField(verbose_name="学位课程简介", )
 40     total_scholarship = models.PositiveIntegerField(verbose_name="总奖学金(贝里)", default=40000)
 41     mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本课程的导师辅导费用(贝里)", default=15000)
 42     # 用于GenericForeignKey反向查询, 不会生成表字段,切勿删除
 43     coupon = GenericRelation("Coupon")  #也是帮助做快速查询的,可以直接查出所有的学位课的价格策略等
 44     # 为了计算学位奖学金
 45     period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=150)
 46     prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024)
 47     teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师")
 48     # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除
 49     degreecourse_price_policy = GenericRelation("PricePolicy")
 50 
 51     def __str__(self):
 52         return self.name
 53 
 54 class Course(models.Model):
 55     """课程"""
 56     name = models.CharField(max_length=128, unique=True)
 57     course_img = models.CharField(max_length=255)
 58     sub_category = models.ForeignKey("CourseSubCategory")
 59     course_type_choices = ((0, '付费'), (1, 'VIP专享'), (2, '学位课程'))
 60     course_type = models.SmallIntegerField(choices=course_type_choices)
 61     degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="若是学位课程,此处关联学位表")
 62     brief = models.TextField(verbose_name="课程概述", max_length=2048)
 63     level_choices = ((0, '初级'), (1, '中级'), (2, '高级'))
 64     level = models.SmallIntegerField(choices=level_choices, default=1)
 65     pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True)
 66     period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=7)
 67     order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排")
 68     attachment_path = models.CharField(max_length=128, verbose_name="课件路径", blank=True, null=True)
 69     status_choices = ((0, '上线'), (1, '下线'), (2, '预上线'))
 70     status = models.SmallIntegerField(choices=status_choices, default=0)
 71     template_id = models.SmallIntegerField("前端模板id", default=1)
 72     coupon = GenericRelation("Coupon")
 73     # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除
 74     price_policy = GenericRelation("PricePolicy")
 75 
 76     def __str__(self):
 77         return "%s(%s)" % (self.name, self.get_course_type_display())
 78 
 79     def save(self, *args, **kwargs):
 80         if self.course_type == 2:
 81             if not self.degree_course:
 82                 raise ValueError("学位课程必须关联对应的学位表")
 83         super(Course, self).save(*args, **kwargs)
 84 
 85 class CourseDetail(models.Model):
 86     """课程详情页内容"""
 87     course = models.OneToOneField("Course")
 88     hours = models.IntegerField("课时")
 89     course_slogan = models.CharField(max_length=125, blank=True, null=True)
 90     video_brief_link = models.CharField(verbose_name='课程介绍', max_length=255, blank=True, null=True)
 91     why_study = models.TextField(verbose_name="为什么学习这门课程")
 92     what_to_study_brief = models.TextField(verbose_name="我将学到哪些内容")
 93     career_improvement = models.TextField(verbose_name="此项目如何有助于我的职业生涯")
 94     prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024)
 95     recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True)
 96     teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师")
 97 
 98     def __str__(self):
 99         return "%s" % self.course
100 
101 class OftenAskedQuestion(models.Model):
102     """常见问题"""
103     content_type = models.ForeignKey(ContentType,
104                                      limit_choices_to={'model__contains': 'course'})  # 关联course or degree_course
105     object_id = models.PositiveIntegerField()
106     content_object = GenericForeignKey('content_type', 'object_id')
107 
108     question = models.CharField(max_length=255)
109     answer = models.TextField(max_length=1024)
110 
111     def __str__(self):
112         return "%s-%s" % (self.content_object, self.question)
113 
114     class Meta:
115         unique_together = ('content_type', 'object_id', 'question')
116 
117 class CourseOutline(models.Model):
118     """课程大纲"""
119     course_detail = models.ForeignKey("CourseDetail")
120     title = models.CharField(max_length=128)
121     # 前端显示顺序
122     order = models.PositiveSmallIntegerField(default=1)
123 
124     content = models.TextField("内容", max_length=2048)
125 
126     def __str__(self):
127         return "%s" % self.title
128 
129     class Meta:
130         unique_together = ('course_detail', 'title')
131 
132 class CourseChapter(models.Model):
133     """课程章节"""
134     course = models.ForeignKey("Course", related_name='coursechapters')
135     chapter = models.SmallIntegerField(verbose_name="第几章", default=1)
136     name = models.CharField(max_length=128)
137     summary = models.TextField(verbose_name="章节介绍", blank=True, null=True)
138     pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
139 
140     class Meta:
141         unique_together = ("course", 'chapter')
142 
143     def __str__(self):
144         return "%s:(第%s章)%s" % (self.course, self.chapter, self.name)
145 
146 class Teacher(models.Model):
147     """讲师、导师表"""
148     name = models.CharField(max_length=32)
149     role_choices = ((0, '讲师'), (1, '导师'))
150     role = models.SmallIntegerField(choices=role_choices, default=0)
151     title = models.CharField(max_length=64, verbose_name="职位、职称")
152     signature = models.CharField(max_length=255, help_text="导师签名", blank=True, null=True)
153     image = models.CharField(max_length=128)
154     brief = models.TextField(max_length=1024)
155 
156     def __str__(self):
157         return self.name
158 
159 class PricePolicy(models.Model):
160     """价格与有课程效期表"""
161     content_type = models.ForeignKey(ContentType)  # 关联course or degree_course
162     object_id = models.PositiveIntegerField()
163     content_object = GenericForeignKey('content_type', 'object_id')
164 
165     # course = models.ForeignKey("Course")
166     valid_period_choices = ((1, '1天'), (3, '3天'),
167                             (7, '1周'), (14, '2周'),
168                             (30, '1个月'),
169                             (60, '2个月'),
170                             (90, '3个月'),
171                             (180, '6个月'), (210, '12个月'),
172                             (540, '18个月'), (720, '24个月'),
173                             )
174     valid_period = models.SmallIntegerField(choices=valid_period_choices)
175     price = models.FloatField()
176 
177     class Meta:
178         unique_together = ("content_type", 'object_id', "valid_period")
179 
180     def __str__(self):
181         return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)
182 
183 class CourseSection(models.Model):
184     """课时目录"""
185     chapter = models.ForeignKey("CourseChapter", related_name='coursesections')
186     name = models.CharField(max_length=128)
187     order = models.PositiveSmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时")
188     section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频'))
189     section_type = models.SmallIntegerField(default=2, choices=section_type_choices)
190 
191 
192     section_link = models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link")
193     # vid:cc视频,唯一标识:sdfsgdfgdgfhgfh
194     video_time = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32)  # 仅在前端展示使用
195     pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True)
196     free_trail = models.BooleanField("是否可试看", default=False)
197 
198 
199     class Meta:
200         unique_together = ('chapter', 'section_link')
201 
202 
203     def __str__(self):
204         return "%s-%s" % (self.chapter, self.name)
205 
206 class Coupon(models.Model):
207     """优惠券生成规则"""
208     name = models.CharField(max_length=64, verbose_name="活动名称")
209     brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")
210     coupon_type_choices = ((0, '通用券'), (1, '满减券'), (2, '折扣券'))
211     coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")
212 
213     money_equivalent_value = models.IntegerField(verbose_name="等值货币")
214     off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
215     minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段")
216 
217     content_type = models.ForeignKey(ContentType, blank=True, null=True)
218     object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
219     content_object = GenericForeignKey('content_type', 'object_id')
220 
221     quantity = models.PositiveIntegerField("数量(张)", default=1)
222     open_date = models.DateField("优惠券领取开始时间")
223     close_date = models.DateField("优惠券领取结束时间")
224     valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True)
225     valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True)
226     coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,
227                                                     help_text="自券被领时开始算起")
228     date = models.DateTimeField(auto_now_add=True)
229 
230     def __str__(self):
231         return "%s(%s)" % (self.get_coupon_type_display(), self.name)
232 
233     def save(self, *args, **kwargs):
234         if not self.coupon_valid_days or (self.valid_begin_date and self.valid_end_date):
235             if self.valid_begin_date and self.valid_end_date:
236                 if self.valid_end_date <= self.valid_begin_date:
237                     raise ValueError("valid_end_date 有效期结束日期必须晚于 valid_begin_date ")
238             if self.coupon_valid_days == 0:
239                 raise ValueError("coupon_valid_days 有效期不能为0")
240         if self.close_date < self.open_date:
241             raise ValueError("close_date 优惠券领取结束时间必须晚于 open_date优惠券领取开始时间 ")
242 
243         super(Coupon, self).save(*args, **kwargs)
课程相关的13张表

相关文章:

  • 2021-06-13
  • 2022-12-23
  • 2022-12-23
  • 2021-12-21
  • 2021-12-20
  • 2021-07-16
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-12-19
  • 2022-12-23
  • 2022-12-23
  • 2021-08-10
  • 2021-12-29
  • 2022-02-19
相关资源
相似解决方案