7.2 - 在線教育平臺系統

1、簡介

在線教育開發平臺,仿:https://www.luffycity.com/homehtml

開發過程,詳細內容:地址
前端

vue + rest framework 先後端分離得項目vue

 

1. 用戶登陸,認證node

2. 查看課程頁面,課程詳細列表,查看文章頁面python

3. 購物車,支付中心,生成訂單...接口開發 linux

CC上傳視頻,點播(https://www.bokecc.com/)申請,上傳,本身就能夠寫頁面,播放視頻;git

支付寶支付,詳細內容:見   http://www.cnblogs.com/alice-bj/articles/9399334.htmlgithub

微信消息推送,詳細內容:見 http://www.cnblogs.com/alice-bj/articles/9401858.htmlredis

接口得開發:sql

  1. 查詢全部得課程

  2. 查詢課程詳細

  3. 用戶登陸

  4. 登陸以後,才能訪問; 權限組件,與文章相關得接口

  5. 對文章評論、點贊、收藏得接口

  6. 與購物車相關得接口 - 數據存在 - redis中

    post、get、delete、put請求

  7. 與支付中心相關得接口 - redis中

    post、get、patch請求

  8. 生成訂單得接口 - 存到數據庫中

    post請求

 

2、數據庫

 分類:

  與課程相關

  與文章相關

  與用戶相關

  與獎懲相關

  與優惠劵,支付相關

 

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.safestring import mark_safe
from django.db import models
import hashlib


# ######################## 課程相關 ########################

class CourseCategory(models.Model):
    """課程大類, e.g 前端  後端..."""
    name = models.CharField(max_length=64, unique=True)

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

    class Meta:
        verbose_name_plural = "01.課程大類"


class CourseSubCategory(models.Model):
    """課程子類, e.g python linux """
    category = models.ForeignKey("CourseCategory")
    name = models.CharField(max_length=64, unique=True)

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

    class Meta:
        verbose_name_plural = "02.課程子類"


class DegreeCourse(models.Model):
    """學位課程"""
    name = models.CharField(max_length=128, unique=True)
    course_img = models.CharField(max_length=255, verbose_name="縮略圖")
    brief = models.TextField(verbose_name="學位課程簡介", )
    total_scholarship = models.PositiveIntegerField(verbose_name="總獎學金(貝里)", default=40000)
    mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本課程的導師輔導費用(貝里)", default=15000)
    period = models.PositiveIntegerField(verbose_name="建議學習週期(days)", default=150)  # 爲了計算學位獎學金
    prerequisite = models.TextField(verbose_name="課程先修要求", max_length=1024)
    teachers = models.ManyToManyField("Teacher", verbose_name="課程講師")

    # 用於GenericForeignKey反向查詢, 不會生成表字段,切勿刪除
    coupon = GenericRelation("Coupon")

    # 用於GenericForeignKey反向查詢,不會生成表字段,切勿刪除
    degreecourse_price_policy = GenericRelation("PricePolicy")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "03.學位課"


class Teacher(models.Model):
    """講師、導師表"""
    name = models.CharField(max_length=32)
    role_choices = ((0, '講師'), (1, '導師'))
    role = models.SmallIntegerField(choices=role_choices, default=0)
    title = models.CharField(max_length=64, verbose_name="職位、職稱")
    signature = models.CharField(max_length=255, help_text="導師簽名", blank=True, null=True)
    image = models.CharField(max_length=128)
    brief = models.TextField(max_length=1024)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "04.導師或講師"


class Scholarship(models.Model):
    """學位課程獎學金"""
    degree_course = models.ForeignKey("DegreeCourse")
    time_percent = models.PositiveSmallIntegerField(verbose_name="獎勵檔位(時間百分比)", help_text="只填百分值,如80,表明80%")
    value = models.PositiveIntegerField(verbose_name="獎學金數額")

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

    class Meta:
        verbose_name_plural = "05.學位課獎學金"


class Course(models.Model):
    """課程"""
    name = models.CharField(max_length=128, unique=True)
    course_img = models.CharField(max_length=255)
    sub_category = models.ForeignKey("CourseSubCategory")
    course_type_choices = ((0, '付費'), (1, 'VIP專享'), (2, '學位課程'))
    course_type = models.SmallIntegerField(choices=course_type_choices)
    degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="如果學位課程,此處關聯學位表")
    brief = models.TextField(verbose_name="課程概述", max_length=2048)
    level_choices = ((0, '初級'), (1, '中級'), (2, '高級'))
    level = models.SmallIntegerField(choices=level_choices, default=1)
    pub_date = models.DateField(verbose_name="發佈日期", blank=True, null=True)
    period = models.PositiveIntegerField(verbose_name="建議學習週期(days)", default=7)
    order = models.IntegerField("課程順序", help_text="從上一個課程數字日後排")
    attachment_path = models.CharField(max_length=128, verbose_name="課件路徑", blank=True, null=True)
    status_choices = ((0, '上線'), (1, '下線'), (2, '預上線'))
    status = models.SmallIntegerField(choices=status_choices, default=0)
    template_id = models.SmallIntegerField("前端模板id", default=1)
    coupon = GenericRelation("Coupon")
    # 用於GenericForeignKey反向查詢,不會生成表字段,切勿刪除
    price_policy = GenericRelation("PricePolicy")

    asked_question = GenericRelation("OftenAskedQuestion")

    def __str__(self):
        return "%s(%s)" % (self.name, self.get_course_type_display())

    def save(self, *args, **kwargs):
        if self.course_type == 2:
            if not self.degree_course:
                raise ValueError("學位課程必須關聯對應的學位表")
        super(Course, self).save(*args, **kwargs)

    class Meta:
        verbose_name_plural = "06.專題課或學位課模塊"


class CourseDetail(models.Model):
    """課程詳情頁內容"""
    course = models.OneToOneField("Course")
    hours = models.IntegerField("課時")
    course_slogan = models.CharField(max_length=125, blank=True, null=True)
    video_brief_link = models.CharField(verbose_name='課程介紹', max_length=255, blank=True, null=True)
    why_study = models.TextField(verbose_name="爲何學習這門課程")
    what_to_study_brief = models.TextField(verbose_name="我將學到哪些內容")
    career_improvement = models.TextField(verbose_name="此項目如何有助於個人職業生涯")
    prerequisite = models.TextField(verbose_name="課程先修要求", max_length=1024)
    recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True)
    teachers = models.ManyToManyField("Teacher", verbose_name="課程講師")

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

    class Meta:
        verbose_name_plural = "07.課程或學位模塊詳細"


class OftenAskedQuestion(models.Model):
    """常見問題"""
    content_type = models.ForeignKey(ContentType,
                                     limit_choices_to={'model__contains': 'course'})  # 關聯course or degree_course
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    question = models.CharField(max_length=255)
    answer = models.TextField(max_length=1024)

    def __str__(self):
        return "%s-%s" % (self.content_object, self.question)

    class Meta:
        unique_together = ('content_type', 'object_id', 'question')
        verbose_name_plural = "08. 常見問題"


class CourseOutline(models.Model):
    """課程大綱"""
    course_detail = models.ForeignKey("CourseDetail")
    title = models.CharField(max_length=128)
    # 前端顯示順序
    order = models.PositiveSmallIntegerField(default=1)

    content = models.TextField("內容", max_length=2048)

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

    class Meta:
        unique_together = ('course_detail', 'title')
        verbose_name_plural = "09. 課程大綱"


class CourseChapter(models.Model):
    """課程章節"""
    course = models.ForeignKey("Course", related_name='coursechapters')
    chapter = models.SmallIntegerField(verbose_name="第幾章", default=1)
    name = models.CharField(max_length=128)
    summary = models.TextField(verbose_name="章節介紹", blank=True, null=True)
    pub_date = models.DateField(verbose_name="發佈日期", auto_now_add=True)

    class Meta:
        unique_together = ("course", 'chapter')
        verbose_name_plural = "10. 課程章節"

    def __str__(self):
        return "%s:(第%s章)%s" % (self.course, self.chapter, self.name)


class CourseSection(models.Model):
    """課時目錄"""
    chapter = models.ForeignKey("CourseChapter", related_name='coursesections')
    name = models.CharField(max_length=128)
    order = models.PositiveSmallIntegerField(verbose_name="課時排序", help_text="建議每一個課時之間空1至2個值,以備後續插入課時")
    section_type_choices = ((0, '文檔'), (1, '練習'), (2, '視頻'))
    section_type = models.SmallIntegerField(default=2, choices=section_type_choices)
    section_link = models.CharField(max_length=255, blank=True, null=True, help_text="如果video,填vid,如果文檔,填link")
    video_time = models.CharField(verbose_name="視頻時長", blank=True, null=True, max_length=32)  # 僅在前端展現使用
    pub_date = models.DateTimeField(verbose_name="發佈時間", auto_now_add=True)
    free_trail = models.BooleanField("是否可試看", default=False)

    class Meta:
        unique_together = ('chapter', 'section_link')
        verbose_name_plural = "11. 課時"

    def __str__(self):
        return "%s-%s" % (self.chapter, self.name)


class Homework(models.Model):
    chapter = models.ForeignKey("CourseChapter")
    title = models.CharField(max_length=128, verbose_name="做業題目")
    order = models.PositiveSmallIntegerField("做業順序", help_text="同一課程的每一個做業以前的order值間隔1-2個數")
    homework_type_choices = ((0, '做業'), (1, '模塊通關考覈'))
    homework_type = models.SmallIntegerField(choices=homework_type_choices, default=0)
    requirement = models.TextField(max_length=1024, verbose_name="做業需求")
    threshold = models.TextField(max_length=1024, verbose_name="踩分點")
    recommend_period = models.PositiveSmallIntegerField("推薦完成周期(天)", default=7)
    scholarship_value = models.PositiveSmallIntegerField("爲該做業分配的獎學金(貝里)")
    note = models.TextField(blank=True, null=True)
    enabled = models.BooleanField(default=True, help_text="本做業若是後期不須要了,不想讓學員看到,能夠設置爲False")

    class Meta:
        unique_together = ("chapter", "title")
        verbose_name_plural = "12. 章節做業"

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


class CourseReview(models.Model):
    """課程評價"""
    enrolled_course = models.OneToOneField("EnrolledCourse")
    about_teacher = models.FloatField(default=0, verbose_name="講師講解是否清晰")
    about_video = models.FloatField(default=0, verbose_name="內容實用")
    about_course = models.FloatField(default=0, verbose_name="課程內容通俗易懂")
    review = models.TextField(max_length=1024, verbose_name="評價")
    disagree_number = models.IntegerField(default=0, verbose_name="")
    agree_number = models.IntegerField(default=0, verbose_name="贊同數")
    tags = models.ManyToManyField("Tags", blank=True, verbose_name="標籤")
    date = models.DateTimeField(auto_now_add=True, verbose_name="評價日期")
    is_recommend = models.BooleanField("熱評推薦", default=False)
    hide = models.BooleanField("不在前端頁面顯示此條評價", default=False)

    def __str__(self):
        return "%s-%s" % (self.enrolled_course.course, self.review)

    class Meta:
        verbose_name_plural = "13. 課程評價(購買課程後才能評價)"


class DegreeCourseReview(models.Model):
    """學位課程評價
    爲了之後能夠定製單獨的評價內容,因此不與普通課程的評價混在一塊兒,單獨建表
    """
    enrolled_course = models.ForeignKey("EnrolledDegreeCourse")
    course = models.ForeignKey("Course", verbose_name="評價學位模塊", blank=True, null=True,
                               help_text="不填寫即表明評價整個學位課程", limit_choices_to={'course_type': 2})
    about_teacher = models.FloatField(default=0, verbose_name="講師講解是否清晰")
    about_video = models.FloatField(default=0, verbose_name="視頻質量")
    about_course = models.FloatField(default=0, verbose_name="課程")
    review = models.TextField(max_length=1024, verbose_name="評價")
    disagree_number = models.IntegerField(default=0, verbose_name="")
    agree_number = models.IntegerField(default=0, verbose_name="贊同數")
    tags = models.ManyToManyField("Tags", blank=True, verbose_name="標籤")
    date = models.DateTimeField(auto_now_add=True, verbose_name="評價日期")
    is_recommend = models.BooleanField("熱評推薦", default=False)
    hide = models.BooleanField("不在前端頁面顯示此條評價", default=False)

    def __str__(self):
        return "%s-%s" % (self.enrolled_course, self.review)

    class Meta:
        verbose_name_plural = "14. 學位課評價(購買課程後才能評價)"


class PricePolicy(models.Model):
    """價格與有課程效期表"""
    content_type = models.ForeignKey(ContentType)  # 關聯course or degree_course
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    # course = models.ForeignKey("Course")
    valid_period_choices = ((1, '1天'), (3, '3天'),
                            (7, '1周'), (14, '2周'),
                            (30, '1個月'),
                            (60, '2個月'),
                            (90, '3個月'),
                            (180, '6個月'), (210, '12個月'),
                            (540, '18個月'), (720, '24個月'),
                            )
    valid_period = models.SmallIntegerField(choices=valid_period_choices)
    price = models.FloatField()

    class Meta:
        unique_together = ("content_type", 'object_id', "valid_period")
        verbose_name_plural = "15. 價格策略"

    def __str__(self):
        return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)


# ######################## 深科技相關 ########################
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)
    content = models.TextField(verbose_name="文章正文")
    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="收藏數")

    tags = models.ManyToManyField("Tags", blank=True, 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 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. 通用評論表"


# ######################## 用戶帳戶相關 ########################

class Tags(models.Model):
    tag_type_choices = ((0, '文章標籤'), (1, '課程評價標籤'), (2, '用戶感興趣技術標籤'))
    tag_type = models.SmallIntegerField(choices=tag_type_choices)
    name = models.CharField(max_length=64, unique=True, db_index=True)

    class Meta:
        verbose_name_plural = "20. Tag標籤"

    def __str__(self):
        return self.name


class MentorGroup(models.Model):
    """導師組"""

    name = models.CharField(max_length=64, unique=True)
    brief = models.TextField(blank=True, null=True)
    mentors = models.ManyToManyField("Account", limit_choices_to={'role': 1})

    class Meta:
        verbose_name_plural = "21. 導師組"

    def __str__(self):
        return self.name


class Account(models.Model):
    username = models.CharField("用戶名", max_length=64, unique=True)
    email = models.EmailField(
        verbose_name='email address',
        max_length=255,
        unique=True,
        blank=True,
        null=True
    )

    uid = models.CharField(max_length=64, unique=True, help_text='微信用戶綁定和CC視頻統計')  # 與第3方交互用戶信息時,用這個uid,以免泄露敏感用戶信息
    mobile = models.BigIntegerField(verbose_name="手機", unique=True, help_text="用於手機驗證碼登陸")
    qq = models.CharField(verbose_name="QQ", max_length=64, blank=True, null=True, db_index=True)
    weixin = models.CharField(max_length=128, blank=True, null=True, db_index=True, verbose_name="微信")
    profession = models.ForeignKey("Profession", verbose_name="職位信息", blank=True, null=True)  # 職位相關信息,註冊時必選
    tags = models.ManyToManyField("Tags", blank=True, verbose_name="感興趣的標籤")
    city = models.ForeignKey("City", verbose_name="城市", blank=True, null=True)  # 所在城市,註冊時必填, 經過城市能找到對應的省份
    signature = models.CharField('我的簽名', blank=True, null=True, max_length=255)
    brief = models.TextField("我的介紹", blank=True, null=True)

    openid = models.CharField(max_length=128, blank=True, null=True)
    gender_choices = ((0, '保密'), (1, ''), (2, ''))
    gender = models.SmallIntegerField(choices=gender_choices, default=0, verbose_name="性別")
    degree_choices = ((0, "學歷"), (1, '高中如下'), (2, '中專/高中'), (3, '大專'), (4, '本科'), (5, '碩士'), (6, '博士'))
    degree = models.PositiveSmallIntegerField(choices=degree_choices, blank=True,
                                              null=True, default=0, verbose_name="學歷")
    birthday = models.DateField(blank=True, null=True, verbose_name="生日")
    id_card = models.CharField(max_length=32, blank=True, null=True, verbose_name="身份證號或護照號")
    password = models.CharField('password', max_length=128,
                                help_text=mark_safe('''<a class='btn-link' href='password'>重置密碼</a>'''))
    is_active = models.BooleanField(default=True, verbose_name="帳戶狀態")
    is_staff = models.BooleanField(verbose_name='staff status', default=False, help_text='決定着用戶是否可登陸管理後臺')
    name = models.CharField(max_length=32, default="", verbose_name="真實姓名")
    head_img = models.CharField(max_length=128, default='/static/frontend/head_portrait/logo@2x.png',
                                verbose_name="我的頭像")
    role_choices = ((0, '學員'), (1, '導師'), (2, '講師'), (3, '管理員'))
    role = models.SmallIntegerField(choices=role_choices, default=0, verbose_name="角色")

    # 貝里餘額 
    balance = models.PositiveIntegerField(default=0, verbose_name="可提現和使用餘額")

    memo = models.TextField('備註', blank=True, null=True, default=None)
    date_joined = models.DateTimeField(auto_now_add=True, verbose_name="註冊時間")

    class Meta:
        verbose_name_plural = "22. 帳戶信息"

    def save(self, *args, **kwargs):
        if not self.pk:
            # This code only happens if the objects is not in the database yet. Otherwise it would have pk
            m = hashlib.md5()
            m.update(self.username.encode(encoding="utf-8"))
            self.uid = m.hexdigest()
        super(Account, self).save(*args, **kwargs)

    def __str__(self):
        return self.username


class UserAuthToken(models.Model):
    """
    用戶Token表
    """
    user = models.OneToOneField(to="Account")
    token = models.CharField(max_length=40, unique=True)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "23. 用戶Token"

    def save(self, *args, **kwargs):
        import datetime
        # 根據用戶名和時間生成惟一標識

        self.token = self.generate_key()
        self.created = datetime.datetime.utcnow()
        return super(UserAuthToken, self).save(*args, **kwargs)

    def generate_key(self):
        import datetime

        """根據用戶名和時間生成惟一標識"""
        username = self.user.name
        now = str(datetime.datetime.now()).encode('utf-8')
        md5 = hashlib.md5(username.encode('utf-8'))
        md5.update(now)
        return md5.hexdigest()


class Province(models.Model):
    """
    省份表
    """
    code = models.IntegerField(verbose_name="省代碼", unique=True)
    name = models.CharField(max_length=64, verbose_name="省名稱", unique=True)

    def __str__(self):
        return "{} - {}".format(self.code, self.name)

    class Meta:
        verbose_name_plural = "24. 省"


class City(models.Model):
    """
    城市表
    """
    code = models.IntegerField(verbose_name="", unique=True)
    name = models.CharField(max_length=64, verbose_name="市名稱")  # 城市名可能有重複
    province = models.ForeignKey("Province")

    def __str__(self):
        return "{} - {}".format(self.code, self.name)

    class Meta:
        verbose_name_plural = "25. 市"


class Industry(models.Model):
    """
    行業表
    """
    code = models.IntegerField(verbose_name="行業代碼", unique=True)
    name = models.CharField(max_length=64, verbose_name="行業名稱")

    def __str__(self):
        return "{} - {}".format(self.code, self.name)

    class Meta:
        verbose_name_plural = "26. 行業信息"


class Profession(models.Model):
    """
    職位表,與行業表外鍵關聯
    """
    code = models.IntegerField(verbose_name="職位代碼")
    name = models.CharField(max_length=64, verbose_name="職位名稱")
    industry = models.ForeignKey("Industry")

    def __str__(self):
        return "{} - {}".format(self.code, self.name)

    class Meta:
        unique_together = ("code", "industry")
        verbose_name_plural = "27. 職位信息"


class Feedback(models.Model):
    """用戶反饋表"""
    name = models.CharField(max_length=32, blank=True, null=True)
    contact = models.CharField(max_length=64, blank=True, null=True)
    feedback_type_choices = ((0, '網站優化建議'), (1, '爛!我想吐槽'), (2, '網站bug反饋'))
    feedback_type = models.SmallIntegerField(choices=feedback_type_choices)
    content = models.TextField(max_length=1024)
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "28. 用戶反饋"

    def __str__(self):
        return self.name


# ######################## 獎懲相關 ########################

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 獎金餘額:%s" % (self.id, self.score_rule, self.account, self.score, self.balance)

    class Meta:
        verbose_name_plural = "30. 獎懲記錄"


# ######################## 購買課程相關 ########################

class Coupon(models.Model):
    """優惠券生成規則"""
    name = models.CharField(max_length=64, verbose_name="活動名稱")
    brief = models.TextField(blank=True, null=True, verbose_name="優惠券介紹")
    coupon_type_choices = ((0, '通用券'), (1, '滿減券'), (2, '折扣券'))
    coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券類型")

    money_equivalent_value = models.IntegerField(verbose_name="等值貨幣")
    off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只針對折扣券,例7.9折,寫79", blank=True, null=True)
    minimum_consume = models.PositiveIntegerField("最低消費", default=0, help_text="僅在滿減券時填寫此字段")

    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.PositiveIntegerField("綁定課程", blank=True, null=True, help_text="能夠把優惠券跟課程綁定")
    content_object = GenericForeignKey('content_type', 'object_id')

    quantity = models.PositiveIntegerField("數量(張)", default=1)
    open_date = models.DateField("優惠券領取開始時間")
    close_date = models.DateField("優惠券領取結束時間")
    valid_begin_date = models.DateField(verbose_name="有效期開始時間", blank=True, null=True)
    valid_end_date = models.DateField(verbose_name="有效結束時間", blank=True, null=True)
    coupon_valid_days = models.PositiveIntegerField(verbose_name="優惠券有效期(天)", blank=True, null=True,
                                                    help_text="自券被領時開始算起")
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "31. 優惠券生成記錄"

    def __str__(self):
        return "%s(%s)" % (self.get_coupon_type_display(), self.name)

    def save(self, *args, **kwargs):
        if not self.coupon_valid_days or (self.valid_begin_date and self.valid_end_date):
            if self.valid_begin_date and self.valid_end_date:
                if self.valid_end_date <= self.valid_begin_date:
                    raise ValueError("valid_end_date 有效期結束日期必須晚於 valid_begin_date ")
            if self.coupon_valid_days == 0:
                raise ValueError("coupon_valid_days 有效期不能爲0")
        if self.close_date < self.open_date:
            raise ValueError("close_date 優惠券領取結束時間必須晚於 open_date優惠券領取開始時間 ")

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


class CouponRecord(models.Model):
    """優惠券發放、消費紀錄"""
    coupon = models.ForeignKey("Coupon")
    number = models.CharField(max_length=64, unique=True)
    account = models.ForeignKey("Account", verbose_name="擁有者")
    status_choices = ((0, '未使用'), (1, '已使用'), (2, '已過時'))
    status = models.SmallIntegerField(choices=status_choices, default=0)
    get_time = models.DateTimeField(verbose_name="領取時間", help_text="用戶領取時間")
    used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用時間")
    order = models.ForeignKey("Order", blank=True, null=True, verbose_name="關聯訂單")  # 一個訂單能夠有多個優惠券

    class Meta:
        verbose_name_plural = "32. 用戶優惠券"

    def __str__(self):
        return '%s-%s-%s' % (self.account, self.number, self.status)


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 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")  # 使訂單購買後支持 課程評價

    # order = models.ForeignKey("Order",blank=True,null=True)

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

    class Meta:
        verbose_name_plural = "34. 報名專題課"


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


        
        
        
        
        
        
        
class Question(models.Model):
    """課程提問"""
    name = models.CharField(max_length=128, blank=True, null=True, verbose_name="問題概要", db_index=True)
    question_type_choices = ((0, '專題課程問題'), (1, '學位課程問題'))
    question_type = models.SmallIntegerField(choices=question_type_choices, default=0, verbose_name="來源")
    account = models.ForeignKey("Account", verbose_name="提問者")
    degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True)  # 如果針對整個學位課程的提問,關聯這個
    course_section = models.ForeignKey("CourseSection", blank=True, null=True)  # 針對整個學位課程的提問不需關聯特定課時
    content = models.TextField(max_length=1024, verbose_name="問題內容")
    enquiries_count = models.IntegerField(default=0, verbose_name="同問者計數")
    attachment_path = models.CharField(max_length=128, blank=True, null=True, verbose_name="附件路徑", help_text="問題記錄的截圖等")
    date = models.DateTimeField(auto_now_add=True)
    status_choices = ((0, '待解答'), (1, '已解答'), (2, '已關閉'))
    status = models.SmallIntegerField(choices=status_choices, default=0)

    class Meta:
        verbose_name_plural = "43. 討論區:課程提問"

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

    def save(self, *args, **kwargs):
        if self.degree_course is None and self.course_section is None:
            raise ValueError("提的問題必須關聯學位課程或具體課時!")

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

    
        
class Answer(models.Model):
    """問題解答"""
    question = models.ForeignKey("Question", verbose_name="問題")
    content = models.TextField(verbose_name="回答")
    account = models.ForeignKey("Account", verbose_name="回答者")
    agree_number = models.IntegerField(default=0, verbose_name="點贊數")
    disagree_number = models.IntegerField(default=0, verbose_name="點踩數")
    answer_date = models.DateTimeField(auto_now=True, verbose_name="日期")

    class Meta:
        verbose_name_plural = "44. 討論區:解答"

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


class AnswerComment(models.Model):
    """答案回覆評論"""
    answer = models.ForeignKey("Answer")
    reply_to = models.ForeignKey("self", blank=True, null=True, verbose_name="基於評論的評論")
    comment = models.TextField(max_length=512, verbose_name="評論內容")
    attachment_path = models.CharField(max_length=128, blank=True, null=True, verbose_name="附件路徑", help_text="跟進記錄的截圖等")
    account = models.ForeignKey("Account", verbose_name="評論者")
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "45. 討論區:評論"

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


class QACounter(models.Model):
    """ 問題和回答的贊同數量統計 """
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    data_type_choices = ((0, '點贊'), (1, ''), (2, '同問'))
    data_type = models.SmallIntegerField(choices=data_type_choices)
    account = models.ForeignKey("Account")
    date = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name_plural = "46. 問題和回答的贊同數量統計"
        unique_together = ("content_type", 'object_id', "account")
modles.py
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Q


# ######################## 課程相關 ########################

class CourseCategory(models.Model):
    """課程大類, e.g 前端  後端..."""
    name = models.CharField(max_length=64, unique=True)

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

    class Meta:
        verbose_name_plural = "01.課程大類"


class CourseSubCategory(models.Model):
    """課程子類, e.g python linux """
    category = models.ForeignKey("CourseCategory")
    name = models.CharField(max_length=64, unique=True)

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

    class Meta:
        verbose_name_plural = "02.課程子類"


class DegreeCourse(models.Model):
    """學位課程"""
    name = models.CharField(max_length=128, unique=True)
    course_img = models.CharField(max_length=255, verbose_name="縮略圖")
    brief = models.TextField(verbose_name="學位課程簡介", )
    total_scholarship = models.PositiveIntegerField(verbose_name="總獎學金(貝里)", default=40000)
    mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本課程的導師輔導費用(貝里)", default=15000)
    period = models.PositiveIntegerField(verbose_name="建議學習週期(days)", default=150)  # 爲了計算學位獎學金
    prerequisite = models.TextField(verbose_name="課程先修要求", max_length=1024)
    teachers = models.ManyToManyField("Teacher", verbose_name="課程講師")

    # 用於GenericForeignKey反向查詢, 不會生成表字段,切勿刪除
    # coupon = GenericRelation("Coupon")

    # 用於GenericForeignKey反向查詢,不會生成表字段,切勿刪除
    degreecourse_price_policy = GenericRelation("PricePolicy")

    # 查詢常見問題
    asked_question = GenericRelation("OftenAskedQuestion")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "03.學位課"


class Teacher(models.Model):
    """講師、導師表"""
    name = models.CharField(max_length=32)
    role_choices = ((0, '講師'), (1, '導師'))
    role = models.SmallIntegerField(choices=role_choices, default=0)
    title = models.CharField(max_length=64, verbose_name="職位、職稱")
    signature = models.CharField(max_length=255, help_text="導師簽名", blank=True, null=True)
    image = models.CharField(max_length=128)
    brief = models.TextField(max_length=1024)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "04.導師或講師"


class Scholarship(models.Model):
    """學位課程獎學金"""
    degree_course = models.ForeignKey("DegreeCourse")
    time_percent = models.PositiveSmallIntegerField(verbose_name="獎勵檔位(時間百分比)", help_text="只填百分值,如80,表明80%")
    value = models.PositiveIntegerField(verbose_name="獎學金數額")

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

    class Meta:
        verbose_name_plural = "05.學位課獎學金"


class Course(models.Model):
    """專題課程 OR 學位課模塊"""
    name = models.CharField(max_length=128, unique=True)  # Python基礎
    course_img = models.CharField(max_length=255)

    sub_category = models.ForeignKey("CourseSubCategory")

    course_type_choices = ((0, '付費'), (1, 'VIP專享'), (2, '學位課程'))
    course_type = models.SmallIntegerField(choices=course_type_choices)

    degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="如果學位課程,此處關聯學位表")

    brief = models.TextField(verbose_name="課程(模塊)概述", max_length=2048)
    level_choices = ((0, '初級'), (1, '中級'), (2, '高級'))
    level = models.SmallIntegerField(choices=level_choices, default=1)
    pub_date = models.DateField(verbose_name="發佈日期", blank=True, null=True)
    period = models.PositiveIntegerField(verbose_name="建議學習週期(days)", default=7)
    order = models.IntegerField("課程順序", help_text="從上一個課程數字日後排")
    attachment_path = models.CharField(max_length=128, verbose_name="課件路徑", blank=True, null=True)
    status_choices = ((0, '上線'), (1, '下線'), (2, '預上線'))
    status = models.SmallIntegerField(choices=status_choices, default=0)
    template_id = models.SmallIntegerField("前端模板id", default=1)

    # 若是是專題課時,獲取相關優惠券
    coupon = GenericRelation("Coupon")

    # 用於GenericForeignKey反向查詢,不會生成表字段,切勿刪除
    price_policy = GenericRelation("PricePolicy")

    # 查詢常見問題
    asked_question = GenericRelation("OftenAskedQuestion")

    class Meta:
        verbose_name_plural = "06.專題課or學位課模塊"

    def __str__(self):
        return self.name


class CourseDetail(models.Model):
    """課程詳情頁內容"""
    course = models.OneToOneField("Course")
    hours = models.IntegerField("課時")
    course_slogan = models.CharField(max_length=125, blank=True, null=True)
    video_brief_link = models.CharField(verbose_name='課程介紹', max_length=255, blank=True, null=True)
    why_study = models.TextField(verbose_name="爲何學習這門課程")
    what_to_study_brief = models.TextField(verbose_name="我將學到哪些內容")
    career_improvement = models.TextField(verbose_name="此項目如何有助於個人職業生涯")
    prerequisite = models.TextField(verbose_name="課程先修要求", max_length=1024)
    recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True)
    teachers = models.ManyToManyField("Teacher", verbose_name="課程講師")

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

    class Meta:
        verbose_name_plural = "07.課程或學位模塊詳細"


class OftenAskedQuestion(models.Model):
    """常見問題"""
    content_type = models.ForeignKey(ContentType)  # 關聯course or degree_course
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    question = models.CharField(max_length=255)
    answer = models.TextField(max_length=1024)

    def __str__(self):
        return "%s-%s" % (self.content_object, self.question)

    class Meta:
        unique_together = ('content_type', 'object_id', 'question')
        verbose_name_plural = "08. 常見問題"


class CourseOutline(models.Model):
    """課程大綱"""
    course_detail = models.ForeignKey("CourseDetail")

    # 前端顯示順序
    order = models.PositiveSmallIntegerField(default=1)

    title = models.CharField(max_length=128)
    content = models.TextField("內容", max_length=2048)

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

    class Meta:
        unique_together = ('course_detail', 'title')
        verbose_name_plural = "09. 課程大綱"


class CourseChapter(models.Model):
    """課程章節"""
    course = models.ForeignKey("Course", related_name='coursechapters')
    chapter = models.SmallIntegerField(verbose_name="第幾章", default=1)
    name = models.CharField(max_length=128)
    summary = models.TextField(verbose_name="章節介紹", blank=True, null=True)
    pub_date = models.DateField(verbose_name="發佈日期", auto_now_add=True)

    class Meta:
        unique_together = ("course", 'chapter')
        verbose_name_plural = "10. 課程章節"

    def __str__(self):
        return "%s:(第%s章)%s" % (self.course, self.chapter, self.name)


class CourseSection(models.Model):
    """課時目錄"""
    chapter = models.ForeignKey("CourseChapter", related_name='coursesections')
    name = models.CharField(max_length=128)
    order = models.PositiveSmallIntegerField(verbose_name="課時排序", help_text="建議每一個課時之間空1至2個值,以備後續插入課時")
    section_type_choices = ((0, '文檔'), (1, '練習'), (2, '視頻'))
    section_type = models.SmallIntegerField(default=2, choices=section_type_choices)
    # 59EE3275E977AADB9C33DC5901307461
    section_link = models.CharField(max_length=255, blank=True, null=True, help_text="如果video,填vid,如果文檔,填link")

    video_time = models.CharField(verbose_name="視頻時長", blank=True, null=True, max_length=32)  # 僅在前端展現使用
    pub_date = models.DateTimeField(verbose_name="發佈時間", auto_now_add=True)
    free_trail = models.BooleanField("是否可試看", default=False)

    class Meta:
        unique_together = ('chapter', 'section_link')
        verbose_name_plural = "11. 課時目錄"

    def __str__(self):
        return "%s-%s" % (self.chapter, self.name)


class Homework(models.Model):
    """章節做業"""
    chapter = models.ForeignKey("CourseChapter")
    title = models.CharField(max_length=128, verbose_name="做業題目")
    order = models.PositiveSmallIntegerField("做業順序", help_text="同一課程的每一個做業以前的order值間隔1-2個數")
    homework_type_choices = ((0, '做業'), (1, '模塊通關考覈'))
    homework_type = models.SmallIntegerField(choices=homework_type_choices, default=0)
    requirement = models.TextField(max_length=1024, verbose_name="做業需求")
    threshold = models.TextField(max_length=1024, verbose_name="踩分點")
    recommend_period = models.PositiveSmallIntegerField("推薦完成周期(天)", default=7)
    scholarship_value = models.PositiveSmallIntegerField("爲該做業分配的獎學金(貝里)")
    note = models.TextField(blank=True, null=True)
    enabled = models.BooleanField(default=True, help_text="本做業若是後期不須要了,不想讓學員看到,能夠設置爲False")

    class Meta:
        unique_together = ("chapter", "title")
        verbose_name_plural = "12. 章節做業"

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

#
# class CourseReview(models.Model):
#     """課程評價"""
#     enrolled_course = models.OneToOneField("EnrolledCourse")
#     about_teacher = models.FloatField(default=0, verbose_name="講師講解是否清晰")
#     about_video = models.FloatField(default=0, verbose_name="內容實用")
#     about_course = models.FloatField(default=0, verbose_name="課程內容通俗易懂")
#     review = models.TextField(max_length=1024, verbose_name="評價")
#     disagree_number = models.IntegerField(default=0, verbose_name="踩")
#     agree_number = models.IntegerField(default=0, verbose_name="贊同數")
#     tags = models.ManyToManyField("Tags", blank=True, verbose_name="標籤")
#     date = models.DateTimeField(auto_now_add=True, verbose_name="評價日期")
#     is_recommend = models.BooleanField("熱評推薦", default=False)
#     hide = models.BooleanField("不在前端頁面顯示此條評價", default=False)
#
#     def __str__(self):
#         return "%s-%s" % (self.enrolled_course.course, self.review)
#
#     class Meta:
#         verbose_name_plural = "13. 課程評價(購買課程後才能評價)"
#
#
# class DegreeCourseReview(models.Model):
#     """學位課程評價
#     爲了之後能夠定製單獨的評價內容,因此不與普通課程的評價混在一塊兒,單獨建表
#     """
#     enrolled_course = models.ForeignKey("EnrolledDegreeCourse")
#     course = models.ForeignKey("Course", verbose_name="評價學位模塊", blank=True, null=True,
#                                help_text="不填寫即表明評價整個學位課程", limit_choices_to={'course_type': 2})
#     about_teacher = models.FloatField(default=0, verbose_name="講師講解是否清晰")
#     about_video = models.FloatField(default=0, verbose_name="視頻質量")
#     about_course = models.FloatField(default=0, verbose_name="課程")
#     review = models.TextField(max_length=1024, verbose_name="評價")
#     disagree_number = models.IntegerField(default=0, verbose_name="踩")
#     agree_number = models.IntegerField(default=0, verbose_name="贊同數")
#     tags = models.ManyToManyField("Tags", blank=True, verbose_name="標籤")
#     date = models.DateTimeField(auto_now_add=True, verbose_name="評價日期")
#     is_recommend = models.BooleanField("熱評推薦", default=False)
#     hide = models.BooleanField("不在前端頁面顯示此條評價", default=False)
#
#     def __str__(self):
#         return "%s-%s" % (self.enrolled_course, self.review)
#
#     class Meta:
#         verbose_name_plural = "14. 學位課評價(購買課程後才能評價)"
#


class PricePolicy(models.Model):
    """價格與有課程效期表"""
    content_type = models.ForeignKey(ContentType)  # 關聯course or degree_course
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    # course = models.ForeignKey("Course")
    valid_period_choices = ((1, '1天'), (3, '3天'),
                            (7, '1周'), (14, '2周'),
                            (30, '1個月'),
                            (60, '2個月'),
                            (90, '3個月'),
                            (180, '6個月'), (210, '12個月'),
                            (540, '18個月'), (720, '24個月'),
                            )
    valid_period = models.SmallIntegerField(choices=valid_period_choices)
    price = models.FloatField()

    class Meta:
        unique_together = ("content_type", 'object_id', "valid_period")
        verbose_name_plural = "15. 價格策略"

    def __str__(self):
        return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)


# ######################## 深科技相關 ########################
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)
    content = models.TextField(verbose_name="文章正文")
    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="收藏數")

    # tags = models.ManyToManyField("Tags", blank=True, 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")
    collect = GenericRelation("Collection")

    class Meta:
        verbose_name_plural = "17. 文章"

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


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. 通用評論表"


# ########################### 3. 用戶相關 ################################

class Account(models.Model):
    """用戶表"""
    username = models.CharField("用戶名", max_length=64, unique=True)
    password = models.CharField("密碼", max_length=64)

    uid = models.CharField(max_length=64, unique=True, help_text='微信用戶綁定和CC視頻統計')  # 與第3方交互用戶信息時,用這個uid,以免泄露敏感用戶信息
    openid = models.CharField(max_length=128, blank=True, null=True)
    # 貝里餘額
    balance = models.PositiveIntegerField(default=0, verbose_name="可提現和使用餘額")

    def __str__(self):
        return self.username

    class Meta:
        verbose_name_plural = "20. 用戶表"


class UserAuthToken(models.Model):
    """
    用戶Token表
    """
    user = models.OneToOneField(to="Account")
    token = models.CharField(max_length=64, unique=True)

    def __str__(self):
        return '用戶{}Token'.format(self.user)

    class Meta:
        verbose_name_plural = "21. 用戶Token表"

# ########################### 4. 獎懲相關 ################################

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 獎金餘額:%s" % (self.id, self.score_rule, self.account, self.score, self.balance)

    class Meta:
        verbose_name_plural = "30. 獎懲記錄"

# ########################### 優惠券 ,支付相關 ################################


class Coupon(models.Model):
    """優惠券生成規則"""
    name = models.CharField(max_length=64, verbose_name="活動名稱")
    brief = models.TextField(blank=True, null=True, verbose_name="優惠券介紹")
    coupon_type_choices = ((0, '通用券'), (1, '滿減券'), (2, '折扣券'))
    coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券類型")

    """
    通用:
        money_equivalent_value=100
        off_percent=null
        minimum_consume=0
    滿減:
        money_equivalent_value=100
        off_percent=null
        minimum_consume=1000
    折扣:
        money_equivalent_value=0
        off_percent=79
        minimum_consume=0
    """
    money_equivalent_value = models.IntegerField(verbose_name="等值貨幣")
    off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只針對折扣券,例7.9折,寫79", blank=True, null=True)
    minimum_consume = models.PositiveIntegerField("最低消費", default=0, help_text="僅在滿減券時填寫此字段")

    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.PositiveIntegerField("綁定課程", blank=True, null=True, help_text="能夠把優惠券跟課程綁定")
    content_object = GenericForeignKey('content_type', 'object_id')

    quantity = models.PositiveIntegerField("數量(張)", default=1)
    open_date = models.DateField("優惠券領取開始時間")
    close_date = models.DateField("優惠券領取結束時間")
    valid_begin_date = models.DateField(verbose_name="有效期開始時間", blank=True, null=True)
    valid_end_date = models.DateField(verbose_name="有效結束時間", blank=True, null=True)
    coupon_valid_days = models.PositiveIntegerField(verbose_name="優惠券有效期(天)", blank=True, null=True,
                                                    help_text="自券被領時開始算起")
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "31. 優惠券生成記錄"

    def __str__(self):
        return "%s(%s)" % (self.get_coupon_type_display(), self.name)

    def save(self, *args, **kwargs):
        if not self.coupon_valid_days or (self.valid_begin_date and self.valid_end_date):
            if self.valid_begin_date and self.valid_end_date:
                if self.valid_end_date <= self.valid_begin_date:
                    raise ValueError("valid_end_date 有效期結束日期必須晚於 valid_begin_date ")
            if self.coupon_valid_days == 0:
                raise ValueError("coupon_valid_days 有效期不能爲0")
        if self.close_date < self.open_date:
            raise ValueError("close_date 優惠券領取結束時間必須晚於 open_date優惠券領取開始時間 ")

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


class CouponRecord(models.Model):
    """優惠券發放、消費紀錄"""
    coupon = models.ForeignKey("Coupon")
    number = models.CharField(max_length=64, unique=True)
    account = models.ForeignKey("Account", verbose_name="擁有者")
    status_choices = ((0, '未使用'), (1, '已使用'), (2, '已過時'))
    status = models.SmallIntegerField(choices=status_choices, default=0)
    get_time = models.DateTimeField(verbose_name="領取時間", help_text="用戶領取時間")
    used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用時間")
    # order = models.ForeignKey("Order", blank=True, null=True, verbose_name="關聯訂單")  # 一個訂單能夠有多個優惠券

    class Meta:
        verbose_name_plural = "32. 用戶優惠券"

    def __str__(self):
        return '%s-%s-%s' % (self.account, self.number, self.status)


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 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")  # 使訂單購買後支持 課程評價

    # order = models.ForeignKey("Order",blank=True,null=True)

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

    class Meta:
        verbose_name_plural = "34. 報名專題課"


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 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 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)
開發版本 - models.py

 

3、why? 流程

1. 爲何要開發「學城」?
    - 提升在線 完課率(學成率)。
    - 具體:
        - 購買時間週期
        - 闖關式學習
        - 考覈
        - 導師篩選
        - 導師監督(跟進記錄)
        - 答疑時間(12小時)
        - 獎懲措施
            - 時間 
            - 做業 

2. 開發週期和團隊?
    團隊:
        - 開發 
            - 導師後臺,stark組件+rbac : 1人
            - 管理後臺,stark組件+rbac : 1人
            - 主站
                - vue.js  1人 
                - api     村長+1/2文州+1/2Alex+其餘 + 村長
        - 運維(1人)
        - 測試(1人)
        - 產品經理(1人)
        - UI設計(1人)
        - 運營(1人)
        - 銷售(4人)
        - 全職導師(2人)
        - 簽約講師(...)
    週期:
        - 7月份
        - 11月份上線 
        - 11月份~次年5月份: 修Bug,活動支持,廣告。。。
        - 6月份:開發題庫系統
    
3. 購買流程 
    - 加入購物車
    - 去結算
    - 去支付 

 

4、知識點

詳細見:https://www.cnblogs.com/alice-bj/articles/9363920.html

1. 登陸、課程、文章相關得接口:

版本、分頁、路由、視圖 (ViewSetMinin  ModelViewSet  APIView)、

中間件、跨域、序列化(o2o fk m2m)、認證 (authentication_classes = [LuffyAuth, ])、

事務(with transaction.atomic(): ... ... )、異常處理 ( try ... except  異常捕獲)

 

2. 與購物車相關得接口

用戶挑選課程,添加到購物車,數據存到redis中:

使用redis緣由:中間狀態,可能會頻繁得修改數據,

 

POST請求:購物車中添加一條數據

GET請求:查看本身購物車中的全部數據

DELETE請求:刪除購物車中的數據

PUT/PATCH請求:更新價格策略

 

增刪改查:

合法性,必定要判斷,用戶傳過來數據,合法性。

 

3. 與支付中心相關得接口

用戶選中購物車得課程,點擊結算,跳到支付中心

涉及到 數據存在redis、認證、事務(redis) 

 

post請求: 用戶選中購物車得課程,去支付,只要是用戶傳得數據,都要判斷合法性,在加入以前先清空,刪除。

patch請求:修改課程或全局得優惠劵。

get請求:查看支付中心得數據

 

4. 與生成訂單相關得接口

用戶將redis中得支付中心得課程數據,點擊當即支付,發post請求,生成訂單,訂單詳情,存數據庫中。

 

1. 獲取用戶提交得數據(貝里數和實付金額),驗證數據得合法性。查看我的得帳戶餘額(貝里)是否足夠;

2. 去結算中心獲取課程信息,優惠劵,全站優惠劵,判斷優惠劵得類型:立減、滿減、折扣

  算出實際應該支付得錢數與前端傳來得實付金額是否相等。

3. 生成訂單,多條sql,使用事務:

  訂單表建立一條數據,訂單詳細根據課程建立多條記錄,

  使用貝里,貝里金額扣除,交易記錄,更新優惠劵得狀態。

 

5、code

https://github.com/alice-bj/luffy

相關文章
相關標籤/搜索