zoukankan      html  css  js  c++  java
  • K12(在线学习的平台)

    项目:K12(在线学习的平台)

    一、背景

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

    K12:大目标是要取代线下班

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

    二、系统架构

    - 主站
        - 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张表
     1 # ######################## 深科技相关 ########################
     2 
     3 class ArticleSource(models.Model):
     4     """文章来源"""
     5     name = models.CharField(max_length=64, unique=True)
     6 
     7     def __str__(self):
     8         return self.name
     9 
    10 
    11 class Article(models.Model):
    12     """文章资讯"""
    13     title = models.CharField(max_length=255, unique=True, db_index=True, verbose_name="标题")
    14     source = models.ForeignKey("ArticleSource", verbose_name="来源")
    15     article_type_choices = ((0, '资讯'), (1, '视频'))
    16     article_type = models.SmallIntegerField(choices=article_type_choices, default=0)
    17     brief = models.TextField(max_length=512, verbose_name="摘要")
    18     head_img = models.CharField(max_length=255)
    19     content = models.TextField(verbose_name="文章正文")
    20     pub_date = models.DateTimeField(verbose_name="上架日期")
    21     offline_date = models.DateTimeField(verbose_name="下架日期")
    22     status_choices = ((0, '在线'), (1, '下线'))
    23     status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="状态")
    24     order = models.SmallIntegerField(default=0, verbose_name="权重", help_text="文章想置顶,可以把数字调大,不要超过1000")
    25     vid = models.CharField(max_length=128, verbose_name="视频VID", help_text="文章类型是视频, 则需要添加视频VID", blank=True, null=True)
    26     comment_num = models.SmallIntegerField(default=0, verbose_name="评论数")
    27     agree_num = models.SmallIntegerField(default=0, verbose_name="点赞数")
    28     view_num = models.SmallIntegerField(default=0, verbose_name="观看数")
    29     collect_num = models.SmallIntegerField(default=0, verbose_name="收藏数")
    30 
    31     tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签")
    32     date = models.DateTimeField(auto_now_add=True, verbose_name="创建日期")
    33 
    34     position_choices = ((0, '信息流'), (1, 'banner大图'), (2, 'banner小图'))
    35     position = models.SmallIntegerField(choices=position_choices, default=0, verbose_name="位置")
    36     comment = GenericRelation("Comment")  # 用于GenericForeignKey反向查询, 不会生成表字段,切勿删除,如有疑问请联系老村长
    37 
    38     def __str__(self):
    39         return "%s-%s" % (self.source, self.title)
    40 
    41 
    42 class Collection(models.Model):
    43     """收藏"""
    44     content_type = models.ForeignKey(ContentType)
    45     object_id = models.PositiveIntegerField()
    46     content_object = GenericForeignKey('content_type', 'object_id')
    47 
    48     account = models.ForeignKey("Account")
    49     date = models.DateTimeField(auto_now_add=True)
    50 
    51     class Meta:
    52         unique_together = ('content_type', 'object_id', 'account')
    53 
    54 
    55 # 深科技评论和视频评论
    56 class Comment(models.Model):
    57     """通用的评论表: 课程评论 """
    58     content_type = models.ForeignKey(ContentType, blank=True, null=True, verbose_name="类型")
    59     object_id = models.PositiveIntegerField(blank=True, null=True)
    60     content_object = GenericForeignKey('content_type', 'object_id')
    61 
    62     p_node = models.ForeignKey("self", blank=True, null=True, verbose_name="父级评论")
    63     content = models.TextField(max_length=1024)
    64     account = models.ForeignKey("Account", verbose_name="会员名")
    65     disagree_number = models.IntegerField(default=0, verbose_name="踩")
    66     agree_number = models.IntegerField(default=0, verbose_name="赞同数")
    67     date = models.DateTimeField(auto_now_add=True)
    68 
    69     def __str__(self):
    70         return self.content
    深科技相关
      1 # ######################## 购买课程相关 ########################
      2 
      3 class EnrolledCourse(models.Model):
      4     """已报名课程,不包括学位课程"""
      5     account = models.ForeignKey("Account")
      6     course = models.ForeignKey("Course", limit_choices_to=~Q(course_type=2))
      7     enrolled_date = models.DateTimeField(auto_now_add=True)
      8     valid_begin_date = models.DateField(verbose_name="有效期开始自")
      9     valid_end_date = models.DateField(verbose_name="有效期结束至")
     10     status_choices = ((0, '已开通'), (1, '已过期'))
     11     status = models.SmallIntegerField(choices=status_choices, default=0)
     12     order_detail = models.OneToOneField("OrderDetail")  # 使订单购买后支持 课程评价
     13 
     14     # order = models.ForeignKey("Order",blank=True,null=True)
     15 
     16     def __str__(self):
     17         return "%s:%s" % (self.account, self.course)
     18 
     19 
     20 class DegreeRegistrationForm(models.Model):
     21     """学位课程报名表"""
     22     enrolled_degree = models.OneToOneField("EnrolledDegreeCourse")
     23     current_company = models.CharField(max_length=64, )
     24     current_position = models.CharField(max_length=64, )
     25     current_salary = models.IntegerField()
     26     work_experience_choices = (
     27         (0, "应届生"),
     28         (1, "1年"),
     29         (2, "2年"),
     30         (3, "3年"),
     31         (4, "4年"),
     32         (5, "5年"),
     33         (6, "6年"),
     34         (7, "7年"),
     35         (8, "8年"),
     36         (9, "9年"),
     37         (10, "10年"),
     38         (11, "超过10年"),
     39     )
     40     work_experience = models.IntegerField()
     41     open_module = models.BooleanField("是否开通第1模块", default=True)
     42     stu_specified_mentor = models.CharField("学员自行指定的导师名", max_length=32, blank=True, null=True)
     43     study_plan_choices = ((0, "1-2小时/天"),
     44                           (1, "2-3小时/天"),
     45                           (2, "3-5小时/天"),
     46                           (3, "5小时+/天"),
     47                           )
     48     study_plan = models.SmallIntegerField(choices=study_plan_choices, default=1)
     49     why_take_this_course = models.TextField("报此课程原因", max_length=1024)
     50     why_choose_us = models.TextField("为何选路飞", max_length=1024)
     51     your_expectation = models.TextField("你的期待", max_length=1024)
     52     memo = models.CharField(max_length=255, blank=True, null=True)
     53 
     54     def __str__(self):
     55         return "%s" % self.enrolled_degree
     56 
     57 
     58 class EnrolledDegreeCourse(models.Model):
     59     """已报名的学位课程"""
     60     account = models.ForeignKey("Account")
     61     degree_course = models.ForeignKey("DegreeCourse")
     62     enrolled_date = models.DateTimeField(auto_now_add=True)
     63     valid_begin_date = models.DateField(verbose_name="有效期开始自", blank=True, null=True)  # 开通第一个模块时,再添加课程有效期,2年
     64     valid_end_date = models.DateField(verbose_name="有效期结束至", blank=True, null=True)
     65     status_choices = (
     66         (0, '在学中'),
     67         (1, '休学中'),
     68         (2, '已毕业'),
     69         (3, '超时结业'),
     70         (4, '未开始'),
     71         # (3, '其它'),
     72     )
     73     study_status = models.SmallIntegerField(choices=status_choices, default=0)
     74     mentor = models.ForeignKey("Account", verbose_name="导师", related_name='my_students',
     75                                blank=True, null=True, limit_choices_to={'role': 1})
     76     mentor_fee_balance = models.PositiveIntegerField("导师费用余额", help_text="这个学员的导师费用,每有惩罚,需在此字段同时扣除")
     77     order_detail = models.OneToOneField("OrderDetail")  # 使订单购买后支持填写报名表
     78 
     79     def __str__(self):
     80         return "%s:%s" % (self.account, self.degree_course)
     81 
     82     class Meta:
     83         unique_together = ('account', 'degree_course')
     84 
     85 
     86 class Coupon(models.Model):
     87     """优惠券生成规则"""
     88     name = models.CharField(max_length=64, verbose_name="活动名称")
     89     brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")
     90     coupon_type_choices = ((0, '通用券'), (1, '满减券'), (2, '折扣券'))
     91     coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")
     92 
     93     money_equivalent_value = models.IntegerField(verbose_name="等值货币")
     94     off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
     95     minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段")
     96 
     97     content_type = models.ForeignKey(ContentType, blank=True, null=True)
     98     object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
     99     content_object = GenericForeignKey('content_type', 'object_id')
    100     
    101     quantity = models.PositiveIntegerField("数量(张)", default=1)
    102     open_date = models.DateField("优惠券领取开始时间")
    103     close_date = models.DateField("优惠券领取结束时间")
    104     valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True)
    105     valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True)
    106     coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,
    107                                                     help_text="自券被领时开始算起")
    108     date = models.DateTimeField(auto_now_add=True)
    109 
    110     def __str__(self):
    111         return "%s(%s)" % (self.get_coupon_type_display(), self.name)
    112 
    113     def save(self, *args, **kwargs):
    114         if not self.coupon_valid_days or (self.valid_begin_date and self.valid_end_date):
    115             if self.valid_begin_date and self.valid_end_date:
    116                 if self.valid_end_date <= self.valid_begin_date:
    117                     raise ValueError("valid_end_date 有效期结束日期必须晚于 valid_begin_date ")
    118             if self.coupon_valid_days == 0:
    119                 raise ValueError("coupon_valid_days 有效期不能为0")
    120         if self.close_date < self.open_date:
    121             raise ValueError("close_date 优惠券领取结束时间必须晚于 open_date优惠券领取开始时间 ")
    122 
    123         super(Coupon, self).save(*args, **kwargs)
    124 
    125 
    126 class CouponRecord(models.Model):
    127     """优惠券发放、消费纪录"""
    128     coupon = models.ForeignKey("Coupon")
    129     #number = models.CharField(max_length=64, unique=True)
    130     account = models.ForeignKey("Account", verbose_name="拥有者")
    131     status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期'),)
    132     status = models.SmallIntegerField(choices=status_choices, default=0)
    133     get_time = models.DateTimeField(verbose_name="领取时间", help_text="用户领取时间")
    134     used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间")
    135     order = models.ForeignKey("Order", blank=True, null=True, verbose_name="关联订单")  # 一个订单可以有多个优惠券
    136 
    137 
    138 class Order(models.Model):
    139     """订单"""
    140     payment_type_choices = ((0, '微信'), (1, '支付宝'), (2, '优惠码'), (3, '贝里'))
    141     payment_type = models.SmallIntegerField(choices=payment_type_choices)
    142     payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True)
    143     order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True)  # 考虑到订单合并支付的问题
    144     account = models.ForeignKey("Account")
    145     actual_amount = models.FloatField(verbose_name="实付金额")
    146 
    147     status_choices = ((0, '交易成功'), (1, '待支付'), (2, '退费申请中'), (3, '已退费'), (4, '主动取消'), (5, '超时取消'))
    148     status = models.SmallIntegerField(choices=status_choices, verbose_name="状态")
    149     date = models.DateTimeField(auto_now_add=True, verbose_name="订单生成时间")
    150     pay_time = models.DateTimeField(blank=True, null=True, verbose_name="付款时间")
    151     cancel_time = models.DateTimeField(blank=True, null=True, verbose_name="订单取消时间")
    152 
    153     def __str__(self):
    154         return "%s" % self.order_number
    155 
    156 
    157 class OrderDetail(models.Model):
    158     """订单详情"""
    159     order = models.ForeignKey("Order")
    160 
    161     content_type = models.ForeignKey(ContentType)  # 可关联普通课程或学位
    162     object_id = models.PositiveIntegerField()
    163     content_object = GenericForeignKey('content_type', 'object_id')
    164 
    165     original_price = models.FloatField("课程原价")
    166     price = models.FloatField("折后价格")
    167     content = models.CharField(max_length=255, blank=True, null=True)  # ?
    168     valid_period_display = models.CharField("有效期显示", max_length=32)  # 在订单页显示
    169     valid_period = models.PositiveIntegerField("有效期(days)")  # 课程有效期
    170     memo = models.CharField(max_length=255, blank=True, null=True)
    171 
    172     # def __str__(self):
    173     #     return "%s - %s - %s" % (self.order, self.content_type, self.price)
    174 
    175     class Meta:
    176         # unique_together = ("order", 'course')
    177         unique_together = ("order", 'content_type', 'object_id')
    178 
    179 
    180 class TransactionRecord(models.Model):
    181     """贝里交易纪录"""
    182     account = models.ForeignKey("Account")
    183     amount = models.IntegerField("金额")
    184     balance = models.IntegerField("账户余额")
    185     transaction_type_choices = ((0, '收入'), (1, '支出'), (2, '退款'), (3, "提现"))  # 2 为了处理 订单过期未支付时,锁定期贝里的回退
    186     transaction_type = models.SmallIntegerField(choices=transaction_type_choices)
    187 
    188     content_type = models.ForeignKey(ContentType, blank=True, null=True)
    189     object_id = models.PositiveIntegerField(blank=True, null=True, verbose_name="关联对象")
    190     content_object = GenericForeignKey('content_type', 'object_id')
    191 
    192     transaction_number = models.CharField(unique=True, verbose_name="流水号", max_length=128)
    193     date = models.DateTimeField(auto_now_add=True)
    194     memo = models.CharField(max_length=128, blank=True, null=True)
    195 
    196     def __str__(self):
    197         return "%s" % self.transaction_number
    购买课程相关的8张表
      1 class Homework(models.Model):
      2     chapter = models.ForeignKey("CourseChapter")
      3     title = models.CharField(max_length=128, verbose_name="作业题目")
      4     order = models.PositiveSmallIntegerField("作业顺序", help_text="同一课程的每个作业之前的order值间隔1-2个数")
      5     homework_type_choices = ((0, '作业'), (1, '模块通关考核'))
      6     homework_type = models.SmallIntegerField(choices=homework_type_choices, default=0)
      7     requirement = models.TextField(max_length=1024, verbose_name="作业需求")
      8     threshold = models.TextField(max_length=1024, verbose_name="踩分点")
      9     recommend_period = models.PositiveSmallIntegerField("推荐完成周期(天)", default=7)
     10     scholarship_value = models.PositiveSmallIntegerField("为该作业分配的奖学金(贝里)")
     11     note = models.TextField(blank=True, null=True)
     12     enabled = models.BooleanField(default=True, help_text="本作业如果后期不需要了,不想让学员看到,可以设置为False")
     13 
     14     class Meta:
     15         unique_together = ("chapter", "title")
     16 
     17     def __str__(self):
     18         return "%s - %s" % (self.chapter, self.title)
     19 
     20 
     21 class Teacher(models.Model):
     22     """讲师、导师表"""
     23     name = models.CharField(max_length=32)
     24     role_choices = ((0, '讲师'), (1, '导师'))
     25     role = models.SmallIntegerField(choices=role_choices, default=0)
     26     title = models.CharField(max_length=64, verbose_name="职位、职称")
     27     signature = models.CharField(max_length=255, help_text="导师签名", blank=True, null=True)
     28     image = models.CharField(max_length=128)
     29     brief = models.TextField(max_length=1024)
     30 
     31     def __str__(self):
     32         return self.name
     33 
     34 
     35 # 普通课购买课程后,我的订单中去评价。
     36 class CourseReview(models.Model):
     37     """课程评价"""
     38     enrolled_course = models.OneToOneField("EnrolledCourse")
     39     about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰")
     40     about_video = models.FloatField(default=0, verbose_name="内容实用")
     41     about_course = models.FloatField(default=0, verbose_name="课程内容通俗易懂")
     42     review = models.TextField(max_length=1024, verbose_name="评价")
     43     disagree_number = models.IntegerField(default=0, verbose_name="踩")
     44     agree_number = models.IntegerField(default=0, verbose_name="赞同数")
     45     tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签")
     46     date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期")
     47     is_recommend = models.BooleanField("热评推荐", default=False)
     48     hide = models.BooleanField("不在前端页面显示此条评价", default=False)
     49 
     50     def __str__(self):
     51         return "%s-%s" % (self.enrolled_course.course, self.review)
     52 
     53 
     54 # 学位课购买课程后,我的订单中去天报名表,然后就编程去学习(以后去评价)。
     55 class DegreeCourseReview(models.Model):
     56     """学位课程评价
     57     为了以后可以定制单独的评价内容,所以不与普通课程的评价混在一起,单独建表
     58     """
     59     enrolled_course = models.ForeignKey("EnrolledDegreeCourse")
     60     course = models.ForeignKey("Course", verbose_name="评价学位模块", blank=True, null=True,
     61                                help_text="不填写即代表评价整个学位课程", limit_choices_to={'course_type': 2})
     62     about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰")
     63     about_video = models.FloatField(default=0, verbose_name="视频质量")
     64     about_course = models.FloatField(default=0, verbose_name="课程")
     65     review = models.TextField(max_length=1024, verbose_name="评价")
     66     disagree_number = models.IntegerField(default=0, verbose_name="踩")
     67     agree_number = models.IntegerField(default=0, verbose_name="赞同数")
     68     tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签")
     69     date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期")
     70     is_recommend = models.BooleanField("热评推荐", default=False)
     71     hide = models.BooleanField("不在前端页面显示此条评价", default=False)
     72 
     73     def __str__(self):
     74         return "%s-%s" % (self.enrolled_course, self.review)
     75 
     76 
     77         
     78 # 购买学位课后,为每个学生每个模块会立刻生成一条学习纪录(未开通)
     79 class StudyRecord(models.Model):
     80     """学位课程的模块学习进度
     81        报名学位课程后,每个模块会立刻生成一条学习纪录
     82     """
     83     enrolled_degree_course = models.ForeignKey("EnrolledDegreeCourse")
     84     course_module = models.ForeignKey("Course", verbose_name="学位模块", limit_choices_to={'course_type': 2})
     85     open_date = models.DateField(blank=True, null=True, verbose_name="开通日期")
     86     end_date = models.DateField(blank=True, null=True, verbose_name="完成日期")
     87     status_choices = ((2, '在学'), (1, '未开通'), (0, '已完成'))
     88     status = models.SmallIntegerField(choices=status_choices, default=1)
     89 
     90     class Meta:
     91         unique_together = ('enrolled_degree_course', 'course_module')
     92 
     93     def __str__(self):
     94         return '%s-%s' % (self.enrolled_degree_course, self.course_module)
     95 
     96     def save(self, *args, **kwargs):
     97         if self.course_module.degree_course_id != self.enrolled_degree_course.degree_course_id:
     98             raise ValueError("学员要开通的模块必须与其报名的学位课程一致!")
     99 
    100         super(StudyRecord, self).save(*args, **kwargs)
    101 
    102 
    103 # 为学生开通一个模块
    104 class CourseSchedule(models.Model):
    105     """课程进度计划表,针对学位课程,每开通一个模块,就为这个学员生成这个模块的推荐学习计划表,后面的奖惩均按此表进行"""
    106     study_record = models.ForeignKey("StudyRecord")
    107     homework = models.ForeignKey("Homework")
    108     recommend_date = models.DateField("推荐交作业日期")
    109 
    110     def __str__(self):
    111         return "%s - %s - %s " % (self.study_record, self.homework, self.recommend_date)
    112 
    113     class Meta:
    114         unique_together = ('study_record', 'homework')
    115 
    116 
    117 # 学生交作业
    118 class HomeworkRecord(models.Model):
    119     """学员作业记录及成绩"""
    120     homework = models.ForeignKey("Homework")
    121     student = models.ForeignKey("EnrolledDegreeCourse", verbose_name="学生")
    122     score_choices = (
    123         (100, 'A+'),
    124         (90, 'A'),
    125         (85, 'B+'),
    126         (80, 'B'),
    127         (70, 'B-'),
    128         (60, 'C+'),
    129         (50, 'C'),
    130         (40, 'C-'),
    131         (-1, 'D'),
    132         (0, 'N/A'),
    133         (-100, 'COPY'),
    134     )
    135     score = models.SmallIntegerField(verbose_name="分数", choices=score_choices, null=True, blank=True)
    136     mentor = models.ForeignKey("Account", related_name="my_stu_homework_record", limit_choices_to={'role': 1},
    137                                verbose_name="导师")
    138     mentor_comment = models.TextField(verbose_name="导师批注", blank=True, null=True)  # 导师
    139     status_choice = (
    140         (0, '待批改'),
    141         (1, '已通过'),
    142         (2, '不合格'),
    143     )
    144     status = models.SmallIntegerField(verbose_name='作业状态', choices=status_choice, default=0)
    145 
    146     submit_num = models.SmallIntegerField(verbose_name='提交次数', default=0)
    147     correct_date = models.DateTimeField('备注日期', blank=True, null=True)
    148     note = models.TextField(blank=True, null=True)
    149     date = models.DateTimeField("作业提交日期", auto_now_add=True)
    150 
    151     check_date = models.DateTimeField("批改日期", null=True, blank=True)
    152 
    153     update_time = models.DateTimeField(auto_now=True, verbose_name="提交日期")
    154 
    155     # homework_path = models.CharField(verbose_name='作业路径', max_length=256,blank=True,null=True) 作业路径可以动态拿到,没必要存
    156 
    157     reward_choice = (
    158         (0, '新提交'),
    159         (1, '按时提交'),
    160         (2, '未按时提交'),
    161         (3, '成绩已奖励'),
    162         (4, '成绩已处罚'),
    163         (5, '未作按时检测'),
    164     )
    165     reward_status = models.SmallIntegerField(verbose_name='作业记录奖惩状态', default=0)
    166 
    167     def __str__(self):
    168         return "%s %s" % (self.homework, self.student)
    169 
    170     class Meta:
    171         unique_together = ("homework", "student")
    172 
    173 
    174 # 导师跟进记录
    175 class StuFollowUpRecord(models.Model):
    176     """学员跟进记录"""
    177     enrolled_degree_course = models.ForeignKey("EnrolledDegreeCourse", verbose_name="学生")
    178     mentor = models.ForeignKey("Account", related_name='mentor', limit_choices_to={'role': 1}, verbose_name="导师")
    179     followup_tool_choices = ((0, 'QQ'), (1, '微信'), (2, '电话'), (3, '系统通知'))
    180     followup_tool = models.SmallIntegerField(choices=followup_tool_choices, default=1)
    181     record = models.TextField(verbose_name="跟进记录")
    182     attachment_path = models.CharField(max_length=128, blank=True, null=True, verbose_name="附件路径", help_text="跟进记录的截图等")
    183     date = models.DateTimeField(auto_now_add=True)
    184 
    185     def __str__(self):
    186         return "%s --%s --%s" % (self.enrolled_degree_course, self.record, self.date)
    学位课优惠相关的表
  • 相关阅读:
    Ionic Js十:加载动作
    Ionic Js九:列表操作
    Ionic Js八:头部和底部
    Ionic Js七:手势事件
    库文件
    驱动知识
    自启动总结
    学习笔记
    写脚本切换用户
    未找到arm-linux-gcc解决办法
  • 原文地址:https://www.cnblogs.com/morgana/p/8496399.html
Copyright © 2011-2022 走看看