在線課程的總結

1、開發背景:

 

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
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 = "課程大類"
        verbose_name_plural = "課程大類"


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 = "課程子類"
        verbose_name_plural = "課程子類"


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)
    # 忽略,用於GenericForeignKey反向查詢, 不會生成表字段,切勿刪除
    coupon = GenericRelation("Coupon")

    # 爲了計算學位獎學金
    period = models.PositiveIntegerField(verbose_name="建議學習週期(days)", default=150)
    prerequisite = models.TextField(verbose_name="課程先修要求", max_length=1024)
    teachers = models.ManyToManyField("Teacher", verbose_name="課程講師")

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

    def __str__(self):
        return self.name


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

    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 PricePolicy(models.Model):
    """價格與有課程效期表"""
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

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

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



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


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


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

    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)
    # vid: cc視頻,惟一標識:Dfadljfosdfjlsdfjs
    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')


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

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

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 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="贊同數")
    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 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="贊同數")

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

# 學生交做業
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:
        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)

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

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

    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反向查詢, 不會生成表字段,切勿刪除,若有疑問請聯繫老村長

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


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')
    # FK(Article)
    article = models.ForeignKey('Article')
    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 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 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)

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

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)

    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 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="訂單取消時間")

    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:
        # unique_together = ("order", 'course')
        unique_together = ("order", 'content_type', 'object_id')

class TransactionRecord(models.Model):
    """貝里交易紀錄"""
    account = models.ForeignKey("Account")
    amount = models.IntegerField("金額")
    balance = models.IntegerField("帳戶餘額")
    transaction_type_choices = ((0, '收入'), (1, '支出'), (2, '退款'), (3, "提現"))  # 2 爲了處理 訂單過時未支付時,鎖按期貝里的回退
    transaction_type = models.SmallIntegerField(choices=transaction_type_choices)

    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.PositiveIntegerField(blank=True, null=True, verbose_name="關聯對象")
    content_object = GenericForeignKey('content_type', 'object_id')

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

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


# ######################## 用戶 ########################
class Account(models.Model):
    username = models.CharField("用戶名", max_length=64, unique=True)
    password = models.CharField('password', max_length=128)

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

 

整個支付流程

1、添加到購物車

當用戶選擇須要購買的課程和價格策略後,有兩種方式購買課程:javascript

一:直接購買,將課程id和選擇的價格策略放到redis中,跳到去支付頁面,從redis中獲取購買課程的id和價格策略id,若是該用戶要使用優惠券和貝利,則選擇當前用戶所擁有而且未使用和沒過時的優惠券,獲得折後價格,點擊去支付完成支付,前端

2、添加到購物車中完成支付(post)java

獲取到所選課程的id和所選擇的價格策略id傳到後端,在後端獲取到所選課程的id和所選擇的價格策略id,根據課程id獲取到當前課程,而後根據當前課程獲取到當前課程全部的價格策略,判斷在後端獲取到的價格策略id在不在當前課程的價格策略中,不在的話則拋出異常價格策略不存在,在的話繼續執行,將該課程全部價格策略的id,時間週期和價格存放到price_policy_list中node

 price_policy_objs = course_obj.price_policy.all()
            for item in price_policy_objs:
                if item.id == price_policy_id:
                    flag = True
                price_policy_list.append({'id':item.id,
                                          'valid_period':item.get_valid_period_display(),
                                          'price':item.price})

繼續將該課程的全部信息主要包括:課程id,課程圖片地址,課程標題,全部價格策略,默認價格策略封裝在course_dict中python

course_dict = {
                'id':course_obj.id,
                'img':course_obj.course_img,
                'title':course_obj.name,
                'price_policy_list':price_policy_list,
                'default_policy_id':price_policy_id
            }

判斷當前用戶的購物車中是否有東西,沒有的話則把當前課程放入到購物車中,若是購物車中已經有課程課程,則將該課程添加到購物車中,最後封裝好放到redis中:linux

  nothing = CONN.hget(settings.LUFFY_SHOPPING_CAR,request.user.id)
            if not nothing:
                data = {course_obj.id: course_dict}
            else:
                data = json.loads(nothing.decode('utf-8'))
                data[course_obj.id] = course_dict

            CONN.hset(settings.LUFFY_SHOPPING_CAR,request.user.id, json.dumps(data))

封裝在redis中的數據結構以下:redis

  chopping_car = {
            request.user.id:{
                course.id:{
                        title:'xx',
                        img:'xx',
                        choice_policy_id:1,
                        price_policy_list:[
                            {id:1,price:'9.9', period:'1個月'},
                            {id:2,price:'19.9',period:'3個月'},
                            {id:3,price:'59.9',period:'8個月'},
                        ],
                    }
                },
                course.id:[
                        title:'xx',
                        img:'xx',
                        choice_policy_id:1,
                        price_policy_list:[
                            {id:1,price:'9.9', period:'1個月'},
                            {id:2,price:'19.9',period:'3個月'},
                            {id:3,price:'59.9',period:'8個月'},
                        ],
                    ]
                }
            }
        }
from rest_framework.views import APIView
from rest_framework.response import Response
from django.core.exceptions import ObjectDoesNotExist
from api import models
from api.utils.auth.api_view import AuthAPIView
from api.utils.exception import PricePolicyDoesNotExist
from pool import POOL
import json
from django.conf import settings

import redis
CONN = redis.Redis(connection_pool=POOL)

class ShoppingCarView(AuthAPIView,APIView):

    def get(self,request,*args,**kwargs):
        """
        查看購物車
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        course = CONN.hget(settings.LUFFY_SHOPPING_CAR,request.user.id)
        course_dict = json.loads(course.decode('utf-8'))
        return Response(course_dict)

    def post(self,request,*args,**kwargs):
        """
        獲取課程ID和價格策略ID,放入redis
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = {'code':1000,'msg':None}
        try:
            course_id = request.data.get('course_id')

            price_policy_id = request.data.get('price_policy_id')

            # 1. 獲取課程
            course_obj = models.Course.objects.get(id=course_id)
            # 2. 獲取當前課程的全部價格策略: id, 有效期,價格
            price_policy_list = []
            flag = False
            price_policy_objs = course_obj.price_policy.all()
            for item in price_policy_objs:
                if item.id == price_policy_id:
                    flag = True
                price_policy_list.append({'id':item.id,
                                          'valid_period':item.get_valid_period_display(),
                                          'price':item.price})
            if not flag:
                raise PricePolicyDoesNotExist()

            # 3. 課程和價格策略均沒有問題,將課程和價格策略放到redis中
            # 課程id,課程圖片地址,課程標題,全部價格策略,默認價格策略
            course_dict = {
                'id':course_obj.id,
                'img':course_obj.course_img,
                'title':course_obj.name,
                'price_policy_list':price_policy_list,
                'default_policy_id':price_policy_id
            }

            # a. 獲取當前用戶購物車中的課程 car = {1: {,,,}, 2:{....}}
            # b. car[course_obj.id] = course_dict
            # c. conn.hset('luffy_shopping_car',request.user.id,car)
            nothing = CONN.hget(settings.LUFFY_SHOPPING_CAR,request.user.id)
            if not nothing:
                data = {course_obj.id: course_dict}
            else:
                data = json.loads(nothing.decode('utf-8'))
                data[course_obj.id] = course_dict

            CONN.hset(settings.LUFFY_SHOPPING_CAR,request.user.id, json.dumps(data))

        except ObjectDoesNotExist as e:
            ret['code'] = 1001
            ret['msg'] = "課程不存在"
        except PricePolicyDoesNotExist as e:
            ret['code'] = 1002
            ret['msg'] = "價格策略不存在"
        except Exception as e:
            ret['code'] = 1003
            ret['msg'] = "添加購物車異常"
        print()
        return Response(ret)


    def patch(self,request,*args,**kwargs):
        '''
        修改價格策略,課程id不變,價格策略id變化
        :param reques:
        :param args:
        :param kwargs:
        :return:
        '''
        ret = {'code': 1000, 'msg': None}
        try:
            #課程id
            course_id = request.data.get('course_id')
            course_id=str(course_id)
            #修改後的價格策略id
            price_policy_id = request.data.get('price_policy_id')
            #當前用戶的購物車
            cart=CONN.hget(settings.LUFFY_SHOPPING_CAR,request.user.id)
            print(cart)
            if not cart:
                raise  Exception('訪問的購物車不存在')


            cart=json.loads(cart.decode('utf-8'))
            #判斷課程師傅在當前用戶的購物車中
            if course_id not in cart:
                raise Exception('該課程在購物車中不存在')

            #當前課程全部的價格策略
            currentCourse_price_police= cart[course_id]['price_policy_list']

            policy_exit=False
            #若是當前選擇的價格策略在當前的課程策略中
            for price_policy in currentCourse_price_police:
                if price_policy['id']==price_policy_id:
                    policy_exit=True
                    break

            if not policy_exit:
                raise PricePolicyDoesNotExist()
            #將購物車中的當前課程的價格策略設置爲當前的價格策略
            cart[course_id]['default_policy_id']=price_policy_id
            CONN.hset(settings.LUFFY_SHOPPING_CAR,request.user.id,json.dumps(cart))
            print(cart)

        except PricePolicyDoesNotExist as e:
            ret['code'] = 1001
            ret['msg'] = '價格策略不存在'

        except Exception as e:
            ret['code']=1002
            ret['msg']=str(e)
        return Response(ret)


    def delete(self,request,*args,**kwargs):
        ret = {'code': 1000, 'msg': None}
        try:
            course_id=request.data.get('course_id')
            course_id=str(course_id)
            #獲取當前用戶的購物車
            cart=CONN.hget(settings.LUFFY_SHOPPING_CAR,request.user.id)
            print(cart)
            #若是沒有購物車
            if not cart:
                raise  Exception('購物車裏面沒有東西')

            cart=json.loads(cart.decode('utf-8'))
            #當前用戶購物車中沒有該課程
            if course_id not in cart:
                raise Exception('所刪除的課程沒在購物車中')

            #刪除該課程
            del cart[course_id]

            #寫入到redis中
            CONN.hset(settings.LUFFY_SHOPPING_CAR,request.user.id,json.dumps(cart))
        except Exception as e:
            ret['code']=1001
            ret['msg']=str(e)

        return Response(ret)
shopping_car

修改購物車總的價格策略(patch):數據庫

向後臺發課程id和要修改成的價格策略id,判斷課程是否在購物車中,判斷傳遞過來的價格策略是否在當前課程的價格策略中,在的話將 redis中的當前課程的默認價格策略修改成當前的價格策略具體將上面代碼django

2、去結算

前端向後端傳遞要去結算的課程的id列表如:[1,2]編程

1.獲取用戶提交的課程id, [1,2],

判斷是否選擇要結算的課程,沒選擇則拋出異常

 course_id_list = request.data.get('course_list')
            if not course_id_list or not isinstance(course_id_list, list):
                raise Exception('請選擇要結算的課程')

2.檢測購物車中檢查是否已經有課程(應該有課程的)

 product_dict = redis_pool.conn.hget(settings.LUFFY_SHOPPING_CAR, request.user.id)
            if not product_dict:
                raise Exception('購物車無課程')

3.檢測購物車中是否有用戶要購買的課程

            product_dict = json.loads(product_dict.decode('utf-8'))

            # ###### 課程、價格和優惠券 #######
            policy_course_dict = {}

            # 循環用戶傳遞過來的要結算的課程ID列表
            for course_id in course_id_list:
                course_id = str(course_id)
                product = product_dict.get(course_id)
                if not product:
                    raise Exception('購買的課程必須先加入購物車')

若是所結算的課程在購物車中,4.獲取選中價格策略的價格詳細,

選擇購物車中當前課程下的全部價格策略和當前課程的所選擇的價格策略相等的價格策略,獲取其信息,

            # c. 購物車中是否有用戶要購買的課程
            product_dict = json.loads(product_dict.decode('utf-8'))

            # ###### 課程、價格和優惠券 #######
            policy_course_dict = {}

            # 循環用戶傳遞過來的要結算的課程ID列表
            for course_id in course_id_list:
                course_id = str(course_id)
                product = product_dict.get(course_id)
                if not product:
                    raise Exception('購買的課程必須先加入購物車')

                # 獲取選中價格策略的價格詳細
                policy_exist = False
                for policy in product['price_policy_list']:
                    if policy['id'] == product['choice_policy_id']:
                        policy_price = policy['price']
                        policy_period = policy['period']
                        policy_valid_period = policy['valid_period']
                        policy_exist = True
                        break
                if not policy_exist:
                    raise Exception('購物車中的課程無此價格')
複製代碼

將上面咱們獲取的課程信息和價格策略信息封裝在policy_course中

複製代碼
                policy_course = {
                    'course_id': course_id,
                    'course_name': product['name'],
                    'course_img': product['course_img'],
                    'policy_id': product['choice_policy_id'],
                    'policy_price': policy_price,
                    'policy_period': policy_period,
                    'policy_valid_period': policy_valid_period,
                    'default_coupon_id': 0,
                    'coupon_record_list': {
                        0:{'id': 0, 'text': '請選擇優惠券'},
                    },
                }

5.獲取當前用戶全部的優惠券

  user_coupon_list = models.CouponRecord.objects.filter(account=request.user,status=0)

6.區分用戶的優惠券種類,課程優惠券添加到課程中;全局優惠券添加到全局

  # ###### 全局優惠券 #######
            global_coupon_record_dict = {}

循環遍歷當前用戶的全部優惠券,判斷他們是否過時

                begin_date = record.coupon.valid_begin_date
                end_date = record.coupon.valid_end_date
                if begin_date:
                    if current_date < begin_date:
                        continue
                if end_date:
                    if current_date > end_date:
                        continue

 

,若是沒過時,判斷他們是全局優惠券仍是針對某個課程的優惠券,區分好是什麼優惠券之後還的區分該優惠券是什麼類型,

若是是通用券

 if record.coupon.coupon_type == 0:
                        temp = {'type': 0, 'text': "通用優惠券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'money_equivalent_value': record.coupon.money_equivalent_value}

若是是滿減券:

  elif record.coupon.coupon_type == 1:
                        temp = {'type': 1, 'text': "滿減券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'minimum_consume': record.coupon.minimum_consume,
                                'money_equivalent_value': record.coupon.money_equivalent_value}

若是是折扣券:

 elif record.coupon.coupon_type == 2:
                        temp = {'type': 2, 'text': "折扣券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'off_percent': record.coupon.off_percent}

若是是全局優惠券,則

global_coupon_record_dict[record.id] = temp

若是可是針對課程的優惠券:

 policy_course_dict[cid]['coupon_record_list'][record.id] = temp

最後將全部數據封裝在user_pay中放到redis上

複製代碼
user_pay = {
                'policy_course_dict': policy_course_dict,
                'global_coupon_record_dict': global_coupon_record_dict,
                'default_global_coupon_id': 0,
            }
redis_pool.conn.hset(settings.LUFFY_PAYMENT, request.user.id, json.dumps(user_pay))
複製代碼

 user_pay數據結構

結算中心 =  {
    用戶ID: {
        policy_course_dict:{
            1:{
                'course_id': course_id,
                'course_name': product['name'],
                'course_img': product['course_img'],
                'policy_id': product['choice_policy_id'],
                'policy_price': policy_price,
                'policy_': policy_period, # 30/
                'default_coupon_id': 1,
                'coupon_record_list': {
                    0:{'id': 0, 'text': '請選擇優惠券'},
                    1:{'id': 1, 'type':1, 'text': '優惠券1', ..},
                    2:{'id': 2, 'type':2, 'text': '優惠券1', ..},
                    3: {'id': 3, 'type':3, 'text': '優惠券1', ..},
                },
            },
            2:{
                'course_id': course_id,
                'course_name': product['name'],
                'course_img': product['course_img'],
                'policy_id': product['choice_policy_id'],
                'policy_price': policy_price,
                'policy_': policy_period,
                'default_coupon_id': 0,
                'coupon_record_list': {
                    0:{'id': 0, 'text': '請選擇優惠券'},
                    1:{'id': 1, 'type':1, 'text': '優惠券1', ..},
                    2:{'id': 2, 'type':2, 'text': '優惠券1', ..},
                    3: {'id': 3, 'type':3, 'text': '優惠券1', ..},
                },
            }
        },
        global_coupon_dict:{
            1:{'type': 0, 'text': "通用優惠券", 'id': 1, ..},
            2:{'type': 0, 'text': "通用優惠券", 'id': 2, ..},
            3:{'type': 0, 'text': "通用優惠券", 'id': 3, ...},
            4:{'type': 0, 'text': "通用優惠券", 'id': 4, ...},
        },
        choice_global_coupon:3
    }         
}
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
import datetime
from django.conf import settings

from rest_framework.views import APIView
from rest_framework.response import Response

from api.utils import redis_pool
from api.utils.auth.api_view import AuthAPIView
from api import models


class PaymentView(AuthAPIView,APIView):
    """
    去結算接口
    """
    def get(self, request, *args, **kwargs):
        """
        獲取結算列表
        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """
        response = {'code': 1000}
        try:
            # 結算商品列表
            payment_list = redis_pool.conn.hget(settings.LUFFY_PAYMENT, request.user.id)
            if not payment_list:
                raise Exception()

            response['data'] = {
                'payment_list': json.loads(payment_list.decode('utf-8')),  # 結算信息(課程、價格和優惠券)
                "balance": request.user.balance  # 我的貝里帳戶,可以使用貝里金額
            }
        except Exception as e:
            response['code'] = 1001
            response['msg'] = "結算列表爲空"

        return Response(response)

    def post(self, request, *args, **kwargs):
        """
        去結算
            方案一(示例):用戶提交課程id,去redis購物車中獲取其選好的價格策略,再次檢測課程和價格策略的合法性。
                   PS: 直接購買時,須要先加入購物車,再當即去結算

            方案二:用戶提交課程id和價格策略id,去數據庫驗證其合法性。
                   PS: 直接購買時,直接去結算
            
            user.id: {
                policy_course_dict:{
                    課程ID:{
                        'course_id': course_id,
                        'course_name': product['name'],
                        'course_img': product['course_img'],
                        'policy_id': product['choice_policy_id'],
                        'policy_price': policy_price,
                        'policy_': policy_period,
                        'coupon_record_list': {
                            0:{'id': 0, 'text': '請選擇優惠券'},
                            1:{'id': 1, 'type':1, 'text': '優惠券1', ..},
                            2:{'id': 2, 'type':2, 'text': '優惠券1', ..},
                            3: {'id': 3, 'type':3, 'text': '優惠券1', ..},
                        },
                    },
                    課程ID:{
                        'course_id': course_id,
                        'course_name': product['name'],
                        'course_img': product['course_img'],
                        'policy_id': product['choice_policy_id'],
                        'policy_price': policy_price,
                        'policy_': policy_period,
                        'coupon_record_list': {
                            0:{'id': 0, 'text': '請選擇優惠券'},
                            1:{'id': 1, 'type':1, 'text': '優惠券1', ..},
                            2:{'id': 2, 'type':2, 'text': '優惠券1', ..},
                            3: {'id': 3, 'type':3, 'text': '優惠券1', ..},
                        },
                    }
                },
                global_coupon_dict:{
                    1:{'type': 0, 'text': "通用優惠券", 'id': 1, ..},
                    2:{'type': 0, 'text': "通用優惠券", 'id': 2, ..},
                    3:{'type': 0, 'text': "通用優惠券", 'id': 3, ...},
                    4:{'type': 0, 'text': "通用優惠券", 'id': 4, ...},
                },
                choice_global_coupon:0
            }  
                   
            
        :param request: 
        :param args: 
        :param kwargs: 
        :return: 
        """
        response = {'code': 1001}
        try:

            # a. 獲取用戶提交的課程id, [1,2]
            course_id_list = request.data.get('course_list')
            if not course_id_list or not isinstance(course_id_list, list):
                raise Exception('請選擇要結算的課程')

            # b. 購物車中檢查是否已經有課程(應該有課程的)
            product_dict = redis_pool.conn.hget(settings.LUFFY_SHOPPING_CAR, request.user.id)
            if not product_dict:
                raise Exception('購物車無課程')

            # c. 購物車中是否有用戶要購買的課程
            product_dict = json.loads(product_dict.decode('utf-8'))

            # ###### 課程、價格和優惠券 #######
            policy_course_dict = {}

            # 循環用戶傳遞過來的要結算的課程ID列表
            for course_id in course_id_list:
                course_id = str(course_id)
                product = product_dict.get(course_id)
                if not product:
                    raise Exception('購買的課程必須先加入購物車')

                # 獲取選中價格策略的價格詳細
                policy_exist = False
                for policy in product['price_policy_list']:
                    if policy['id'] == product['choice_policy_id']:
                        policy_price = policy['price']
                        policy_period = policy['period']
                        policy_valid_period = policy['valid_period']
                        policy_exist = True
                        break
                if not policy_exist:
                    raise Exception('購物車中的課程無此價格')


                policy_course = {
                    'course_id': course_id,
                    'course_name': product['name'],
                    'course_img': product['course_img'],
                    'policy_id': product['choice_policy_id'],
                    'policy_price': policy_price,
                    'policy_period': policy_period,
                    'policy_valid_period': policy_valid_period,
                    'default_coupon_id': 0,
                    'coupon_record_list': {
                        0:{'id': 0, 'text': '請選擇優惠券'},
                    },
                }
                policy_course_dict[course_id] = policy_course

            # 獲取當前全部優惠券
            user_coupon_list = models.CouponRecord.objects.filter(account=request.user,status=0)
            # ###### 全局優惠券 #######
            global_coupon_record_dict = {}

            # 課程優惠券添加到課程中;全局優惠券添加到全局
            current_date = datetime.datetime.now().date()
            for record in user_coupon_list:
                # 檢查優惠券是否已通過期
                begin_date = record.coupon.valid_begin_date
                end_date = record.coupon.valid_end_date
                if begin_date:
                    if current_date < begin_date:
                        continue
                if end_date:
                    if current_date > end_date:
                        continue
                # 全局優惠券
                if not record.coupon.content_type:
                    if record.coupon.coupon_type == 0:
                        temp = {'type': 0, 'text': "通用優惠券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'money_equivalent_value': record.coupon.money_equivalent_value}
                    elif record.coupon.coupon_type == 1:
                        temp = {'type': 1, 'text': "滿減券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'minimum_consume': record.coupon.minimum_consume,
                                'money_equivalent_value': record.coupon.money_equivalent_value}
                    elif record.coupon.coupon_type == 2:
                        temp = {'type': 2, 'text': "折扣券", 'id': record.id,
                                'begin_date': begin_date, 'end_date': end_date,
                                'off_percent': record.coupon.off_percent}
                    else:
                        continue

                    global_coupon_record_dict[record.id] = temp
                # 課程優惠券
                else:
                    cid = record.coupon.object_id
                    if record.coupon.content_type.model == 'course' and cid in policy_course_dict:
                        # 課程價格:滿減,打折,通用
                        if record.coupon.coupon_type == 0:
                            temp = {'type': 0, 'text': "通用優惠券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'money_equivalent_value': record.coupon.money_equivalent_value}
                        elif record.coupon.coupon_type == 1 and policy_course_dict[cid]['policy_price'] >= record.coupon.minimum_consume:
                            temp = {'type': 1, 'text': "滿減券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'minimum_consume': record.coupon.minimum_consume,
                                    'money_equivalent_value': record.coupon.money_equivalent_value}
                        elif record.coupon.coupon_type == 2:
                            temp = {'type': 2, 'text': "折扣券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'off_percent': record.coupon.off_percent}
                        else:
                            continue
                        policy_course_dict[cid]['coupon_record_list'][record.id] = temp

            user_pay = {
                'policy_course_dict': policy_course_dict,
                'global_coupon_record_dict': global_coupon_record_dict,
                'default_global_coupon_id': 0,
            }
            redis_pool.conn.hset(settings.LUFFY_PAYMENT, request.user.id, json.dumps(user_pay))

        except Exception as e:
            response['code'] = 1002
            response['msg'] = str(e)

        return Response(response)

    def patch(self,request, *args, **kwargs):
        """
        選擇優惠券
        1. 獲取用戶提交數據
            {
                course_id: '課程id',
                coupon_id: '優惠券ID'
            }
            {
                course_id: 0',
                coupon_id: '全局優惠券ID'
            }

        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        return Response('...')
payment

 三.去支付

1.去結算中心獲取要結算的全部課程和優惠券

2.循環遍歷每個課程

開始總價格totalprice和折扣價totaldiscount都爲0,

總價 = 0
總折扣 = 0

2.1.若是該課程沒有使用優惠券,則總價格=totalprice+課程原價,totaldiscount=0,

b. 循環購買的全部課程
    
    當前時間 = datetime.datetime.now()
    當前日期 = datetime.datetime.now().date
    ****課程信息 = [] *****
    ****使用的優惠券ID列表 = [] *****
    for course_id,values in policy_course_dict.items():
        課程原價 = values['policy_price']
        使用的優惠券ID = values['default_coupon_id']
        discount = 0
        
        # 未使用優惠券
            temp = {
                課程ID: 1,
                原價: 10,
                折扣價:10,
                有效期:30
            }
            課程信息.append(temp)
            
            總價 += 課程原價
            折扣 += discount

2.3.若是使用了優惠券,則須要去判斷所使用的優惠券是否已通過期,或是否已經被使用,若是過時了或者被使用了就拋出異常,不然繼續往下執行,判斷該優惠券的類型

若是使用了優惠券:
            去數據庫查詢:指定優惠券是否已經使用、是否已通過期
            若是優惠券可不用:
                raise Exception('優惠券不可用')
            
            
            若是是通用優惠券:
                discount = 通用優惠券(若是大於課程原價,課程原價)
            elif 若是是滿減優惠券:
                if 價格是否大於最小滿減要求:
                    discount = 通用優惠券(若是大於課程原價,課程原價)
            elif 若是是折扣優惠券:
                discount = 課程原價 * (1-折扣)
            使用的優惠券ID列表.append(綁定能夠的優惠券ID)
            
            temp = {
                課程ID: 1,
                原價: 10,
                折扣價:9,
                有效期:30
            }
            課程信息.append(temp)
            
            總價 += 課程原價
            折扣 += discount

到此爲止 

pay = 總價 - 總折扣

3.繼續計算看是否使用全局優惠券

全站優惠券ID = choice_global_coupon
    數據庫獲取並檢查是否可用(優惠券是否已經使用、是否已通過期)
    若是優惠券可不用:
        raise('全站優惠券不可用')
        
    g_discount = 0
    若是是通用優惠券:
        g_discount = 通用優惠券(若是大於pay,pay)
    elif 若是是滿減優惠券:
        if 價格是否大於最小滿減要求:
            g_discount = 通用優惠券(若是大於pay,pay)
    elif 若是是折扣優惠券:
        g_discount = pay * (1- 折扣)
    
    總折扣 += g_discount
    使用的優惠券ID列表.append(全站優惠券ID)

4.貝利支付

    if balance <= request.user.balance:
        總折扣 += balance

5.總結算

總價 - 總折扣 = alipay
if alipay ==0:
        貝里&優惠券 
        pay_type = 0
    else:
        支付寶支付
        pay_type = 1

若是最後支付=0,就直接修改支付狀態爲已支付,不然改成待支付

 

6.點擊當即支付之後 進行數據庫操做

事務:
        
        # 1. 建立訂單表
             order_obj = models.Order.objects.create(....status=0) # pay_type = 0
             或
             order_obj = models.Order.objects.create(....status=1) # pay_type = 1
             
        # 2. 生成訂單詳細
            
            for item in 課程信息:
                detail_obj = models.OrderDetail.objects.create(order_obj,課程ID,原價和折扣價)
                models.EnrolledCourse.objects.create(...當前時間,當前時間+30,status=1)
            
        # 3. 處理優惠券
            models.CouponRecord.objects.filter(account=request.user,status=0,id__in=使用的優惠券ID列表]).update(status=1,order=order_obj)
        
        # 4. 處理貝里交易
            models.Account.objects.filter(id=reuqest.user.id).update(balance=F(balance)-balance)
            models.TransactionRecord.objects.create(amount=balance,balance=request.user.balance,transaction_type=1,content_object=order_obj)
            

點擊當即支付要生成訂單,訂單根據前面支付是否等於0來判斷支付狀態是否要修改成已支付或者待支付;生成訂單詳情,循環課程信息,寫入課程的原價,折後價,視頻的有效期,即訂單的有效期,對優惠券進行處理,把該用戶已使用的優惠券狀態改成已使用;:修改貝里,該帳戶的貝里減去使用的貝里,並更新貝里消費記錄表,顯示帳戶的消費金額,帳戶的餘額等。

最後

if pay_type==1:
        生成支付寶連接(自動生成本身的訂單號),並返回給前端Vue
# ##################################### 支付寶的回調 ######################################
def callback(request,*args,**kwargs):
models.Order.objects.filter(訂單號).update(status=0)
相關文章
相關標籤/搜索