ContentType组件
ContentType是Django的内置的一个应用,可以追踪项目中所有的APP和model的对应关系,并记录在ContentType表中。
当项目做数据迁移后,会有很多django自带的表,其中就有django_content_type表
ContentType组件应用
- 在model中定义ForeignKey字段,并关联到ContentType表,通常这个字段命名为content-type
- 在model中定义PositiveIntergerField字段, 用来存储关联表中的主键,通常用object_id
- 在model中定义GenericForeignKey字段,传入上面两个字段的名字
- 方便反向查询可以定义GenericRelation字段
- postman
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
# Create your models here.
class Food(models.Model):
"""
id title
1 面包
2 牛奶
"""
title = models.CharField(max_length=32)
# 不会生成字段 只用于反向查询
coupons = GenericRelation(to="Coupon")
class Fruit(models.Model):
"""
id title
1 苹果
2 香蕉
"""
title = models.CharField(max_length=32)
# 如果有40张表
# class Coupon(models.Model):
# """
# id title food_id fruit_id
# 1 面包九五折 1 null
# 2 香蕉满10元减5元 null 2
# """
# title = models.CharField(max_length=32)
# food = models.ForeignKey(to="Food")
# fruit = models.ForeignKey(to="Fruit")
# class Coupon(models.Model):
# """
# id title table_id object_id
# 1 面包九五折 1 1
# 2 香蕉满10元减5元 2 2
# """
# title = models.CharField(max_length=32)
# table = models.ForeignKey(to="Table")
# object_id = models.IntegerField()
#
#
# class Table(models.Model):
# """
# id app_name table_name
# 1 demo food
# 2 demo fruit
# """
# app_name = models.CharField(max_length=32)
# table_name = models.CharField(max_length=32)
class Coupon(models.Model):
title = models.CharField(max_length=32)
# 第一步:注意没有引号因为是导入的
content_type = models.ForeignKey(to=ContentType, on_delete=None)
# 第二步
object_id = models.IntegerField()
# 第三步 不会生成字段,用来操作增删改查
content_object = GenericForeignKey("content_type", "object_id")
models.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Food, Coupon
from django.contrib.contenttypes.models import ContentType
# Create your views here.
class DemoView(APIView):
def get(self, request):
# 给面包创建一个优惠券
food_obj = Food.objects.filter(id=1).first()
# Coupon.objects.create(title="面包九五折", content_type_id=8, object_id=1)
# Coupon.objects.create(title="双十一面包九折促销", content_object=food_obj)
#查询食物都有哪些优惠券
#定义了反向查询
coupons = food_obj.coupons.all()
print(coupons)
# 如果没定义反向查询
content = ContentType.objects.filter(app_label="app01", model="food").first()
coupons = Coupon.objects.filter(content_type=content, object_id=1).all()
print(coupons)
# 优惠券查对象
# 查询优惠券id=1绑定了哪个商品
coupon_obj = Coupon.objects.filter(id=1).first()
content_obj = coupon_obj.content_object
print(coupon_obj.title,content_obj.title)
# 通过ContentType表找表模型
content = ContentType.objects.filter(app_label="app01", model="food").first()
# content=food 获取food表的表模型用model_class()
model_class = content.model_class()
ret = model_class.objects.all()
print(ret)
return Response("ContentType测试")
views.py
media的配置
#静态文件
STATIC_URL = '/static/'
STATICFILES_DIRS=(
os.path.join(BASE_DIR,'static'),
)
# Media配置
MEDIA_URL = "media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
settings.py
from django.conf.urls import url, include
from django.contrib import admin
from django.views.static import serve
from new_luffy import settings
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/course/', include("course.urls")),
# media路径配置
url(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT})
]
urls.py
项目路由配置
from django.contrib import admin
from django.urls import path, include, re_path
from django.views.static import serve
from LuffyCity import settings
from Login.views import GeetestView
urlpatterns = [
path('admin/', admin.site.urls),
path('api/course/', include("Course.urls")),
path('api/shop/', include("shopping.urls")),
path('api/', include("Login.urls")),
path('pc-geetest/register', GeetestView.as_view()),
path('pc-geetest/ajax_validate', GeetestView.as_view()),
# media路径配置
# path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
]
项目urls.py
一、课程模块
- 课程模块,包括了免费课程以及专题课程
- 主要是课程的展示,点击课程进入课程详细页面
- 课程详细页面展示,课程的概述,课程的价格策略,课程章节,评价以及常见问题
1、设计表结构
from django.db import models
# Create your models here.
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
# Create your models here.
__all__ = ["Category", "Course", "CourseDetail", "Teacher", "DegreeCourse", "CourseChapter",
"CourseSection", "PricePolicy", "OftenAskedQuestion", "Comment", "Account", "CourseOutline"]
class Category(models.Model):
"""课程分类表"""
title = models.CharField(max_length=32, unique=True, verbose_name="课程的分类")
def __str__(self):
return self.title
class Meta:
verbose_name = "01-课程分类表"
db_table = verbose_name
verbose_name_plural = verbose_name
class Course(models.Model):
"""课程表"""
title = models.CharField(max_length=128, unique=True, verbose_name="课程的名称")
course_img = models.ImageField(upload_to="course/%Y-%m", verbose_name='课程的图片')
category = models.ForeignKey(to="Category", verbose_name="课程的分类", on_delete=None)
COURSE_TYPE_CHOICES = ((0, "付费"), (1, "vip专享"), (2, "学位课程"))
course_type = models.SmallIntegerField(choices=COURSE_TYPE_CHOICES)
degree_course = models.ForeignKey(to="DegreeCourse", blank=True, null=True, help_text="如果是学位课程,必须关联学位表", on_delete=None)
brief = models.CharField(verbose_name="课程简介", max_length=1024)
level_choices = ((0, '初级'), (1, '中级'), (2, '高级'))
level = models.SmallIntegerField(choices=level_choices, default=1)
status_choices = ((0, '上线'), (1, '下线'), (2, '预上线'))
status = models.SmallIntegerField(choices=status_choices, default=0)
pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True)
order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排")
study_num = models.IntegerField(verbose_name="学习人数", help_text="只要有人买课程,订单表加入数据的同时给这个字段+1")
# order_details = GenericRelation("OrderDetail", related_query_name="course")
# coupon = GenericRelation("Coupon")
# 只用于反向查询不生成字段
price_policy = GenericRelation("PricePolicy")
often_ask_questions = GenericRelation("OftenAskedQuestion")
course_comments = GenericRelation("Comment")
def save(self, *args, **kwargs):
if self.course_type == 2:
if not self.degree_course:
raise ValueError("学位课必须关联学位课程表")
super(Course, self).save(*args, **kwargs)
def __str__(self):
return self.title
class Meta:
verbose_name = "02-课程表"
db_table = verbose_name
verbose_name_plural = verbose_name
class CourseDetail(models.Model):
"""课程详细表"""
course = models.OneToOneField(to="Course", on_delete=None)
hours = models.IntegerField(verbose_name="课时", default=7)
course_slogan = models.CharField(max_length=125, blank=True, null=True, verbose_name="课程口号")
video_brief_link = models.CharField(max_length=255, blank=True, null=True)
summary = models.TextField(max_length=2048, verbose_name="课程概述")
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 self.course.title
class Meta:
verbose_name = "03-课程详细表"
db_table = verbose_name
verbose_name_plural = verbose_name
class Teacher(models.Model):
"""讲师表"""
name = models.CharField(max_length=32, verbose_name="讲师名字")
brief = models.TextField(max_length=1024, verbose_name="讲师介绍")
def __str__(self):
return self.name
class Meta:
verbose_name = "04-教师表"
db_table = verbose_name
verbose_name_plural = verbose_name
class DegreeCourse(models.Model):
"""
字段大体跟课程表相同,哪些不同根据业务逻辑去区分
"""
title = models.CharField(max_length=32, verbose_name="学位课程名字")
def __str__(self):
return self.title
class Meta:
verbose_name = "05-学位课程表"
db_table = verbose_name
verbose_name_plural = verbose_name
class CourseChapter(models.Model):
"""课程章节表"""
course = models.ForeignKey(to="Course", related_name="course_chapters", on_delete=None)
chapter = models.SmallIntegerField(default=1, verbose_name="第几章")
title = models.CharField(max_length=32, verbose_name="课程章节名称")
def __str__(self):
return self.title
class Meta:
verbose_name = "06-课程章节表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ("course", "chapter")
class CourseSection(models.Model):
"""课时表"""
chapter = models.ForeignKey(to="CourseChapter", related_name="course_sections", on_delete=None)
title = models.CharField(max_length=32, verbose_name="课时")
section_order = models.SmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时")
section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频'))
free_trail = models.BooleanField("是否可试看", default=False)
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")
def course_chapter(self):
return self.chapter.chapter
def course_name(self):
return self.chapter.course.title
def __str__(self):
return "%s-%s" % (self.chapter, self.title)
class Meta:
verbose_name = "07-课程课时表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ('chapter', 'section_link')
class PricePolicy(models.Model):
"""价格策略表"""
content_type = models.ForeignKey(ContentType, on_delete=None) # 关联course or degree_course
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个月'),
(120, '4个月'),
(180, '6个月'), (210, '12个月'),
(540, '18个月'), (720, '24个月'),
(722, '24个月'), (723, '24个月'),
)
valid_period = models.SmallIntegerField(choices=valid_period_choices)
price = models.FloatField()
def __str__(self):
return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)
class Meta:
verbose_name = "08-价格策略表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ("content_type", 'object_id', "valid_period")
class OftenAskedQuestion(models.Model):
"""常见问题"""
content_type = models.ForeignKey(ContentType, on_delete=None) # 关联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:
verbose_name = "09-常见问题表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ('content_type', 'object_id', 'question')
class Comment(models.Model):
"""通用的评论表"""
content_type = models.ForeignKey(ContentType, blank=True, null=True, on_delete=None)
object_id = models.PositiveIntegerField(blank=True, null=True)
content_object = GenericForeignKey('content_type', 'object_id')
content = models.TextField(max_length=1024, verbose_name="评论内容")
account = models.ForeignKey("Account", verbose_name="会员名", on_delete=None)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.content
class Meta:
verbose_name = "10-评价表"
db_table = verbose_name
verbose_name_plural = verbose_name
class Account(models.Model):
username = models.CharField(max_length=32, verbose_name="用户姓名")
pwd = models.CharField(max_length=32, verbose_name="密文密码")
# head_img = models.CharField(max_length=256, default='/static/frontend/head_portrait/logo@2x.png',
# verbose_name="个人头像")
balance = models.IntegerField(verbose_name="贝里余额", default=0)
def __str__(self):
return self.username
class Meta:
verbose_name = "11-用户表"
db_table = verbose_name
verbose_name_plural = verbose_name
class CourseOutline(models.Model):
"""课程大纲"""
course_detail = models.ForeignKey(to="CourseDetail", related_name="course_outline", on_delete=None)
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:
verbose_name = "12-课程大纲表"
db_table = verbose_name
verbose_name_plural = verbose_name
unique_together = ('course_detail', 'title')
Course中models.py
2、接口的编写
- 课程这个模块,所有的功能都是展示,基于数据展示的,通常称为数据接口
- 课程页面:有课程所有分类这个接口,有展示课程的接口
- 课程详情页面:详情页面的数据接口
- 详情页面下的子路由对应子组件的数据接口:课程章节课时、课程的评论、课程的常见问题
from django.urls import path
from .views import CategoryView, CourseView, CourseDetailView, CourseChapterView, CourseCommentView, QuestionView
from .video_view import PolyvView
urlpatterns = [
path('category', CategoryView.as_view()),
path('list', CourseView.as_view()),
path('detail/<int:pk>', CourseDetailView.as_view()),
path('chapter/<int:pk>', CourseChapterView.as_view()),
path('comment/<int:pk>', CourseCommentView.as_view()),
path('question/<int:pk>', QuestionView.as_view()),
path('polyv', PolyvView.as_view()),
]
Course中urls.py
from rest_framework import serializers
from . import models
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = models.Category
fields = "__all__"
class CourseSerializer(serializers.ModelSerializer):
level = serializers.CharField(source="get_level_display")
price = serializers.SerializerMethodField()
def get_price(self, obj):
print(obj.price_policy.all())
return obj.price_policy.all().order_by("price").first().price
class Meta:
model = models.Course
fields = ["id", "title", "course_img", "brief", "level", "study_num", "price"]
class CourseDetailSerializer(serializers.ModelSerializer):
level = serializers.CharField(source="course.get_level_display")
study_num = serializers.IntegerField(source="course.study_num")
recommend_courses = serializers.SerializerMethodField()
teachers = serializers.SerializerMethodField()
price_policy = serializers.SerializerMethodField()
course_outline = serializers.SerializerMethodField()
def get_course_outline(self, obj):
return [{"id": outline.id, "title": outline.title, "content": outline.content} for outline in obj.course_outline.all().order_by("order")]
def get_price_policy(self, obj):
return [{"id": price.id, "valid_price_display": price.get_valid_period_display(), "price": price.price} for price in obj.course.price_policy.all()]
def get_teachers(self, obj):
return [{"id": teacher.id, "name": teacher.name} for teacher in obj.teachers.all()]
def get_recommend_courses(self, obj):
return [{"id": course.id, "title": course.title} for course in obj.recommend_courses.all()]
class Meta:
model = models.CourseDetail
fields = ["id", "hours", "summary", "level", "study_num", "recommend_courses", "teachers",
"price_policy", "course_outline"]
class CourseChapterSerializer(serializers.ModelSerializer):
sections = serializers.SerializerMethodField()
def get_sections(self, obj):
return [{"id": section.id, "title": section.title, "free_trail": section.free_trail} for section in obj.course_sections.all().order_by("section_order")]
class Meta:
model = models.CourseChapter
fields = ["id", "title", "sections"]
class CourseCommentSerializer(serializers.ModelSerializer):
account = serializers.CharField(source="account.username")
class Meta:
model = models.Comment
fields = ["id", "account", "content", "date"]
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = models.OftenAskedQuestion
fields = ["id", "question", "answer"]
Course中serializers.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from . import models
from .serializers import CategorySerializer, CourseSerializer, CourseDetailSerializer, CourseChapterSerializer
from .serializers import CourseCommentSerializer, QuestionSerializer
# Create your views here.
class CategoryView(APIView):
"""课程分类接口"""
def get(self, request):
# 通过ORM操作获取所有分类数据
queryset = models.Category.objects.all()
# 利用序列化器去序列化我们的数据
ser_obj = CategorySerializer(queryset, many=True)
# 返回
return Response(ser_obj.data)
class CourseView(APIView):
"""查看所有免费课程的接口"""
def get(self, request):
# 获取过滤条件中的分类ID
category_id = request.query_params.get("category", 0)
# 根据分类获取课程
if category_id == 0:
# 证明没有分类,可以拿所有的课程数据
queryset = models.Course.objects.all().order_by("order")
else:
queryset = models.Course.objects.filter(category_id=category_id).all().order_by("order")
# 序列化课程数据
ser_obj = CourseSerializer(queryset, many=True)
# 返回
return Response(ser_obj.data)
class CourseDetailView(APIView):
"""课程详情页面"""
def get(self, request, pk):
# 根据pk获取到课程详情对象
course_detail_obj = models.CourseDetail.objects.filter(course__id=pk).first()
if not course_detail_obj:
return Response({"code": 1001, "error": "查询的课程详情不存在"})
# 序列化课程详情
ser_obj = CourseDetailSerializer(course_detail_obj)
# 返回
return Response(ser_obj.data)
class CourseChapterView(APIView):
"""课程章节接口"""
def get(self, request, pk):
# 数据结构["第一章": {课时一, 课时二}]
queryset = models.CourseChapter.objects.filter(course_id=pk).all().order_by("chapter")
# 序列化章节对象
ser_obj = CourseChapterSerializer(queryset, many=True)
# 返回
return Response(ser_obj.data)
class CourseCommentView(APIView):
def get(self, request, pk):
# 通过课程id找到课程所有的评论
queryset = models.Course.objects.filter(id=pk).first().course_comments.all()
# 序列化
ser_obj = CourseCommentSerializer(queryset, many=True)
# 返回
return Response(ser_obj.data)
class QuestionView(APIView):
def get(self, request, pk):
queryset = models.Course.objects.filter(id=pk).first().often_ask_questions.all()
ser_obj = QuestionSerializer(queryset, many=True)
return Response(ser_obj.data)
Course中views.py
from django.contrib import admin
# Register your models here.
from . import models
for table in models.__all__:
admin.site.register(getattr(models, table))
Course中admin.py
二、登录认证模块(token存Redis)
- 以前前后端不分离用cookie,session解决,现在前后端分离使用token令牌。
- 用户登录成功后,生成一个随机字符串token给前端返回
- 前端以后都携带这个token来访问,这样后端只需要鉴别这个token就可以做认证
import redis
POOL = redis.ConnectionPool(host="127.0.0.1", port=6379, decode_responses=True, max_connections=10)
utils中redis_pool.py
class BaseResponse(object):
def __init__(self):
self.code = 1000
self.data = None
self.error = None
@property
def dict(self):
return self.__dict__
utils中base_response.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .redis_pool import POOL
from Course.models import Account
import redis
CONN = redis.Redis(connection_pool=POOL)
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 从请求头中获取前端带过来的token
token = request.META.get("HTTP_AUTHENTICATION", "")
if not token:
raise AuthenticationFailed("没有携带token")
# 去redis比对
user_id = CONN.get(str(token))
if user_id == None:
raise AuthenticationFailed("token过期")
user_obj = Account.objects.filter(id=user_id).first()
return user_obj, token
utils中my_auth.py
urlpatterns = [
path('register', RegisterView.as_view()),
path('login', LoginView.as_view()),
path('test_auth', TestView.as_view()),
]
Login中urls.py
from rest_framework import serializers
from Course.models import Account
import hashlib
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = "__all__"
def create(self, validated_data):
pwd = validated_data["pwd"]
pwd_salt = "luffy_password" + pwd
md5_str = hashlib.md5(pwd_salt.encode()).hexdigest()
user_obj = Account.objects.create(username=validated_data["username"], pwd=md5_str)
return user_obj
Login中serializers.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import RegisterSerializer
from utils.base_response import BaseResponse
from Course.models import Account
from utils.redis_pool import POOL
import redis
import uuid
from utils.my_auth import LoginAuth
from utils.geetest import GeetestLib
from django.http import HttpResponse
import json
# Create your views here.
class RegisterView(APIView):
def post(self, request):
res = BaseResponse()
# 用序列化器做校验
ser_obj = RegisterSerializer(data=request.data)
if ser_obj.is_valid():
ser_obj.save()
res.data = ser_obj.data
else:
res.code = 1020
res.error = ser_obj.errors
return Response(res.dict)
class LoginView(APIView):
def post(self, request):
res = BaseResponse()
username = request.data.get("username", "")
pwd = request.data.get("pwd", "")
user_obj = Account.objects.filter(username=username, pwd=pwd).first()
if not user_obj:
res.code = 1030
res.error = "用户名或密码错误"
return Response(res.dict)
# 用户登录成功生成一个token写入redis
# 写入redis token : user_id
conn = redis.Redis(connection_pool=POOL)
try:
token = uuid.uuid4()
# conn.set(str(token), user_obj.id, ex=10)
conn.set(str(token), user_obj.id)
res.data = token
except Exception as e:
print(e)
res.code = 1031
res.error = "创建令牌失败"
return Response(res.dict)
class TestView(APIView):
authentication_classes = [LoginAuth, ]
def get(self, request):
return Response("认证测试")
pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4"
REDIS_CONN = redis.Redis(connection_pool=POOL)
class GeetestView(APIView):
def get(self, request):
user_id = 'test'
gt = GeetestLib(pc_geetest_id, pc_geetest_key)
status = gt.pre_process(user_id)
# request.session[gt.GT_STATUS_SESSION_KEY] = status
REDIS_CONN.set(gt.GT_STATUS_SESSION_KEY, status)
# request.session["user_id"] = user_id
REDIS_CONN.set("gt_user_id", user_id)
response_str = gt.get_response_str()
return HttpResponse(response_str)
def post(self, request):
# print(request.session.get("user_id"))
print(request.META.get("HTTP_AUTHENTICATION"))
print(request.data)
gt = GeetestLib(pc_geetest_id, pc_geetest_key)
challenge = request.data.get(gt.FN_CHALLENGE, '')
validate = request.data.get(gt.FN_VALIDATE, '')
seccode = request.data.get(gt.FN_SECCODE, '')
# username
# pwd
# status = request.session.get(gt.GT_STATUS_SESSION_KEY)
# print(status)
# user_id = request.session.get("user_id")
# print(user_id)
status = REDIS_CONN.get(gt.GT_STATUS_SESSION_KEY)
user_id = REDIS_CONN.get("gt_user_id")
if status:
result = gt.success_validate(challenge, validate, seccode, user_id)
else:
result = gt.failback_validate(challenge, validate, seccode)
result = {"status": "success"} if result else {"status": "fail"}
# if result:
# # 证明验证码通过
# # 判断用户名和密码
# else:
# # 返回验证码错误
return HttpResponse(json.dumps(result))
Login中views.py
三、登录认证模块(token存mysql)
# 拓展之前课程模块下的用户表
class Account(models.Model):
username = models.CharField(max_length=32, verbose_name="用户姓名", unique=True)
password = models.CharField(max_length=32, verbose_name="用户密码")
# head_img = models.CharField(max_length=256, default='/static/frontend/head_portrait/logo@2x.png',
# verbose_name="个人头像")
token = models.UUIDField(null=True, blank=True)
def __str__(self):
return self.username
class Meta:
verbose_name = "11-用户表"
db_table = verbose_name
verbose_name_plural = verbose_name
models.py 扩展之前功能模块的用户表
class BaseResponse(object):
def __init__(self):
self.code = 1000
self.data = None
self.error = None
@property
def dict(self):
return self.__dict__
utils中base_response.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from Course.models import Account
# django 提供的拿时间的接口 提供的是根据django配置的时区拿到的当前时间
from django.utils.timezone import now
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 从请求头中获取前端带过来的token
token = request.META.get("HTTP_AUTHENTICATION", "")
if not token:
raise AuthenticationFailed("没有携带token")
# 去redis比对
user_obj = Account.objects.filter(token=token).first()
if not user_obj:
raise AuthenticationFailed("token过期")
else:
old_time = user_obj.create_token_time
if (now() - old_time).days > 7:
raise AuthenticationFailed({"code": 1020, "error": "无效的token"})
return user_obj, token
utils中my_auth.py
urlpatterns = [
path('register', RegisterView.as_view()),
path('login', LoginView.as_view()),
path('test_auth', TestView.as_view()),
]
Login中urls.py
from rest_framework import serializers
from Course.models import Account
import hashlib
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = "__all__"
def create(self, validated_data):
pwd = validated_data["pwd"]
pwd_salt = "luffy_password" + pwd
md5_str = hashlib.md5(pwd_salt.encode()).hexdigest()
user_obj = Account.objects.create(username=validated_data["username"], pwd=md5_str)
return user_obj
Login中serializers.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import RegisterSerializer
from utils.base_response import BaseResponse
from Course.models import Account
import uuid
# Create your views here.
class RegisterView(APIView):
def post(self, request):
res = BaseResponse()
# 用序列化器做校验
ser_obj = RegisterSerializer(data=request.data)
if ser_obj.is_valid():
ser_obj.save()
res.data = ser_obj.data
else:
res.code = 1020
res.error = ser_obj.errors
return Response(res.dict)
class LoginView(APIView):
def post(self, request):
res = BaseResponse()
username = request.data.get("username", "")
pwd = request.data.get("pwd", "")
user_obj = Account.objects.filter(username=username, pwd=pwd).first()
if not user_obj:
res.code = 1030
res.error = "用户名或密码错误"
return Response(res.dict)
try:
token = uuid.uuid4()
###
user_obj.update(token=token)
res.data = token
except Exception as e:
print(e)
res.code = 1031
res.error = "创建令牌失败"
return Response(res.dict)
# 所有这是一个需要认证的接口
class TestView(APIView):
authentication_classes = [LoginAuth, ]
def get(self, request):
return Response("认证测试")
Login中views.py
四、购物车模块
- 用户点击商品加入购物车,个人中心可以查看自己所有购物车中数据
- 在购物车中可以删除课程,还可以更新购物车中课程的价格策略
- 所以接口应该有四种请求方式, get,post,patch,delete
- 因为购物车是属于中间状态数据而且很多时候需要过期时间所以选择存储到redis
from django.urls import path
from .views import ShoppingCarView
from .settlement_view import SettlementView
from .payment_view import PaymentView
urlpatterns = [
path('shopping_car', ShoppingCarView.as_view()),
path('settlement', SettlementView.as_view()),
path('payment', PaymentView.as_view()),
]
shopping中urls.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .redis_pool import POOL
from Course.models import Account
import redis
CONN = redis.Redis(connection_pool=POOL)
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 从请求头中获取前端带过来的token
token = request.META.get("HTTP_AUTHENTICATION", "")
if not token:
raise AuthenticationFailed("没有携带token")
# 去redis比对
user_id = CONN.get(str(token))
if user_id == None:
raise AuthenticationFailed("token过期")
user_obj = Account.objects.filter(id=user_id).first()
return user_obj, token
utils中my_auth.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.base_response import BaseResponse
from utils.my_auth import LoginAuth
from utils.redis_pool import POOL
from Course.models import Course
import json
import redis
# Create your views here.
# 前端传过来 course_id price_policy_id
# 把购物车数据放入redis
"""
{
SHOPPINGCAR_USERID_COURSE_ID: {
"id", 课程id
"title", 课程标题
"course_img",
"price_policy_dict": {
price_policy_id: "{valid_period, price, valid_period_display}"
price_policy_id2: "{valid_period, price, valid_period_display}"
price_policy_id3: "{valid_period, price, valid_period_display}"
},
"default_price_policy_id": 1 默认选中的价格id
}
}
"""
SHOPPINGCAR_KEY = "SHOPPINGCAR_%s_%s"
CONN = redis.Redis(connection_pool=POOL)
class ShoppingCarView(APIView):
authentication_classes = [LoginAuth, ]
# 给购物车增加商品
def post(self, request):
res = BaseResponse()
try:
# 1, 获取前端传过来的数据以及user_id
course_id = request.data.get("course_id", "")
price_policy_id = request.data.get("price_policy_id", "")
user_id = request.user.pk
# 2, 校验数据的合法性
# 2.1 校验课程id合法性
course_obj = Course.objects.filter(id=course_id).first()
if not course_obj:
res.code = 1040
res.error = "课程id不合法"
return Response(res.dict)
# 2.2 校验价格策略id是否合法
price_policy_queryset = course_obj.price_policy.all()
price_policy_dict = {}
for price_policy in price_policy_queryset:
price_policy_dict[price_policy.id] = {
"price": price_policy.price,
"valid_period": price_policy.valid_period,
"valid_period_display": price_policy.get_valid_period_display()
}
if price_policy_id not in price_policy_dict:
res.code = 1041
res.error = "价格策略id不合法"
return Response(res.dict)
# 3,构建redisKEY
key = SHOPPINGCAR_KEY % (user_id, course_id)
# 4,构建数据结构
course_info = {
"id": course_obj.id,
"title": course_obj.title,
"course_img": str(course_obj.course_img),
"price_policy_dict": json.dumps(price_policy_dict, ensure_ascii=False),
"default_price_policy_id": price_policy_id
}
# 5 写入redis
CONN.hmset(key, course_info)
res.data = "加入购物车成功"
except Exception as e:
res.code = 1012
res.error = "加入购物车失败"
return Response(res.dict)
def get(self, request):
res = BaseResponse()
try:
# 1, 拼接redis key
user_id = request.user.pk
shopping_car_key = SHOPPINGCAR_KEY % (user_id, "*")
# 2, 去redis中读取数据
# 2.1 模糊匹配所有的keys
# 3,构建数据结构展示
all_keys = CONN.scan_iter(shopping_car_key)
ret = []
for key in all_keys:
ret.append(CONN.hgetall(key))
res.data = ret
except Exception as e:
res.code = 1013
res.error = "获取购物车失败"
return Response(res.dict)
def put(self, request):
# 前端 course_id price_policy_id
res = BaseResponse()
try:
# 1, 获取前端传过来的数据以及user_id
course_id = request.data.get("course_id", "")
price_policy_id = request.data.get("price_policy_id", "")
user_id = request.user.pk
# 2, 校验数据的合法性
# 2.1 course_id是否合法
key = SHOPPINGCAR_KEY % (user_id, course_id)
if not CONN.exists(key):
res.code = 1043
res.error = "课程id不合法"
return Response(res.dict)
# 2,2 price_policy_id是否合法
price_policy_dict = json.loads(CONN.hget(key, "price_policy_dict"))
if str(price_policy_id) not in price_policy_dict:
res.code = 1044
res.error = "价格策略不合法"
return Response(res.dict)
# 3, 更新redis default_price_policy_id
CONN.hset(key, "default_price_policy_id", price_policy_id)
res.data = "更新成功"
except Exception as e:
res.code = 1014
res.error = "更新购物车失败"
return Response(res.dict)
def delete(self, request):
# course_list = [course_id, ]
res = BaseResponse()
try:
# 1 获取前端传来的数据以及user_id
course_list = request.data.get("course_list", "")
user_id = request.user.pk
# 2 校验course_id是否合法
for course_id in course_list:
key = SHOPPINGCAR_KEY % (user_id, course_id)
if not CONN.exists(key):
res.code = 1045
res.error = "课程ID不合法"
return Response(res.dict)
# 3, 删除redis数据
CONN.delete(key)
res.data = "删除成功"
except Exception as e:
res.code = 1014
res.error = "删除购物车失败"
return Response(res.dict)
shopping中views.py
五、结算中心模块
结算中心要开始选择优惠券了,有单独的课程优惠券还有全局优惠券。
from django.db import models
# Create your models here.
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from Course.models import Account
# Create your models here.
__all__ = ["Coupon", "CouponRecord", "Order", "OrderDetail", "TransactionRecord"]
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="等值货币", null=True, blank=True, default=0)
off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True, default=100)
minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段", null=True, blank=True)
content_type = models.ForeignKey(ContentType, blank=True, null=True, on_delete=None)
object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
# 不绑定代表全局优惠券
content_object = GenericForeignKey('content_type', 'object_id')
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 = "13. 优惠券生成规则记录"
db_table = verbose_name_plural
verbose_name = verbose_name_plural
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", on_delete=None)
number = models.CharField(max_length=64, unique=True, verbose_name="用户优惠券记录的流水号")
account = models.ForeignKey(to=Account, verbose_name="拥有者", on_delete=None)
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="关联订单", on_delete=None) # 一个订单可以有多个优惠券
class Meta:
verbose_name_plural = "14. 用户优惠券领取使用记录表"
db_table = verbose_name_plural
verbose_name = verbose_name_plural
def __str__(self):
return '%s-%s-%s' % (self.account, self.number, self.status)
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(to=Account, on_delete=None)
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 = "15. 订单表"
db_table = verbose_name_plural
verbose_name = verbose_name_plural
def __str__(self):
return "%s" % self.order_number
class OrderDetail(models.Model):
"""订单详情"""
order = models.ForeignKey("Order", on_delete=None)
content_type = models.ForeignKey(ContentType, on_delete=None) # 可关联普通课程或学位
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
original_price = models.FloatField("课程原价")
price = models.FloatField("折后价格")
valid_period_display = models.CharField("有效期显示", max_length=32) # 在订单页显示
valid_period = models.PositiveIntegerField("有效期(days)") # 课程有效期
memo = models.CharField(max_length=255, blank=True, null=True, verbose_name="备忘录")
def __str__(self):
return "%s - %s - %s" % (self.order, self.content_type, self.price)
class Meta:
verbose_name_plural = "16. 订单详细"
db_table = verbose_name_plural
verbose_name = verbose_name_plural
class TransactionRecord(models.Model):
"""贝里交易纪录"""
account = models.ForeignKey(to=Account, on_delete=None)
amount = models.IntegerField("金额")
balance = models.IntegerField("账户余额")
transaction_type_choices = ((0, '收入'), (1, '支出'), (2, '退款'), (3, "提现")) # 2 为了处理 订单过期未支付时,锁定期贝里的回退
transaction_type = models.SmallIntegerField(choices=transaction_type_choices)
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, verbose_name="备忘录")
class Meta:
verbose_name_plural = "17. 贝里交易记录"
db_table = verbose_name_plural
verbose_name = verbose_name_plural
def __str__(self):
return "%s" % self.transaction_number
shopping中models.py
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.base_response import BaseResponse
from utils.redis_pool import POOL
from django.utils.timezone import now
from utils.my_auth import LoginAuth
import redis
from .views import SHOPPINGCAR_KEY
from .models import CouponRecord
import json
CONN = redis.Redis(connection_pool=POOL)
SETTLEMENT_KEY = "SETTLEMENT_%s_%s"
GLOBAL_COUPON_KEY = "GLOBAL_COUPON_%s"
"""
结算中心
在购物车里选择了商品以及价格策略点击结算 才进入结算中心
在结算中心用户可以选择优惠券
前端传过来数据 course_list
redis = {
settlement_userid_courseid: {
id, 课程id,
title,
course_img,
valid_period_display,
price,
course_coupon_dict: {
coupon_id: {优惠券信息}
coupon_id2: {优惠券信息}
coupon_id3: {优惠券信息}
}
# 默认不给你选 这个字段只有更新的时候才添加
default_coupon_id: 1
}
global_coupon_userid: {
coupon_id: {优惠券信息}
coupon_id2: {优惠券信息}
coupon_id3: {优惠券信息},
# 这个字段只有更新的时候才添加
# 在用户进入结算中心选择优惠券的时候 也就是更新请求的时候更改
default_global_coupon_id: 1
}
}
"""
class SettlementView(APIView):
authentication_classes = [LoginAuth, ]
def post(self, request):
res = BaseResponse()
try:
# 1 获取前端的数据以及user_id
course_list = request.data.get("course_list", "")
user_id = request.user.pk
# 2 校验数据的合法性
for course_id in course_list:
# 2.1 判断course_id 是否在购物车中
shopping_car_key = SHOPPINGCAR_KEY % (user_id, course_id)
if not CONN.exists(shopping_car_key):
res.code = 1050
res.error = "课程ID不合法"
return Response(res.dict)
# 3 构建数据结构
# 3.1 获取用户的所有合法优惠券
user_all_coupons = CouponRecord.objects.filter(
account_id=user_id,
status=0,
coupon__valid_begin_date__lte=now(),
coupon__valid_end_date__gte=now(),
).all()
print(user_all_coupons)
# 3.2 构建优惠券dict
course_coupon_dict = {}
global_coupon_dict = {}
for coupon_record in user_all_coupons:
coupon = coupon_record.coupon
if coupon.object_id == course_id:
course_coupon_dict[coupon.id] = {
"id": coupon.id,
"name": coupon.name,
"coupon_type": coupon.get_coupon_type_display(),
"object_id": coupon.object_id,
"money_equivalent_value": coupon.money_equivalent_value,
"off_percent": coupon.off_percent,
"minimum_consume": coupon.minimum_consume
}
elif coupon.object_id == "":
global_coupon_dict[coupon.id] = {
"id": coupon.id,
"name": coupon.name,
"coupon_type": coupon.get_coupon_type_display(),
"money_equivalent_value": coupon.money_equivalent_value,
"off_percent": coupon.off_percent,
"minimum_consume": coupon.minimum_consume
}
# 3.3 构建写入redis的数据结构
course_info = CONN.hgetall(shopping_car_key)
price_policy_dict = json.loads(course_info["price_policy_dict"])
default_policy_id = course_info["default_price_policy_id"]
valid_period = price_policy_dict[default_policy_id]["valid_period_display"]
price = price_policy_dict[default_policy_id]["price"]
settlement_info = {
"id": course_info["id"],
"title": course_info["title"],
"course_img": course_info["course_img"],
"valid_period": valid_period,
"price": price,
"course_coupon_dict": json.dumps(course_coupon_dict, ensure_ascii=False)
}
# 4 写入redis
settlement_key = SETTLEMENT_KEY % (user_id, course_id)
global_coupon_key = GLOBAL_COUPON_KEY % user_id
CONN.hmset(settlement_key, settlement_info)
if global_coupon_dict:
CONN.hmset(global_coupon_key, global_coupon_dict)
# 5 删除购物车中的数据
CONN.delete(shopping_car_key)
res.data = "加入结算中心成功"
except Exception as e:
res.code = 1020
res.error = "结算失败"
return Response(res.dict)
def get(self, request):
res = BaseResponse()
try:
# 1, 获取user_id
user_id = request.user.pk
# 2, 拼接所有key
# 3, 去redis取数据
settlement_key = SETTLEMENT_KEY % (user_id, "*")
global_coupon_key = GLOBAL_COUPON_KEY % user_id
all_keys = CONN.scan_iter(settlement_key)
ret = []
for key in all_keys:
ret.append(CONN.hgetall(key))
global_coupon_info = CONN.hgetall(global_coupon_key)
res.data = {
"settlement_info": ret,
"global_coupon_dict": global_coupon_info
}
except Exception as e:
res.code = 1024
res.error = "获取结算中心失败"
return Response(res.dict)
def put(self, request):
# course_id course_coupon_id global_coupon_id
res = BaseResponse()
try:
# 1, 获取前端传过来数据
course_id = request.data.get("course_id", "")
course_coupon_id = request.data.get("course_coupon_id", "")
global_coupon_id = request.data.get("global_coupon_id", "")
user_id = request.user.pk
# 2, 校验数据合法性
# 2.1 校验course_id
key = SETTLEMENT_KEY % (user_id, course_id)
if course_id:
if not CONN.exists(key):
res.code = 1060
res.error = "课程ID不合法"
return Response(res.dict)
# 2.2 校验 course_coupon_id
if course_coupon_id:
course_coupon_dict = json.loads(CONN.hget(key, "course_coupon_dict"))
if str(course_coupon_id) not in course_coupon_dict:
res.code = 1061
res.error = "课程优惠券ID不合法"
return Response(res.dict)
# 2.3 校验global_coupon_id
if global_coupon_id:
global_coupon_key = GLOBAL_COUPON_KEY % user_id
if not CONN.exists(global_coupon_key):
res.code = 1062
res.error = "全局优惠券ID不合法"
return Response(res.dict)
CONN.hset(global_coupon_key, "default_global_coupon_id", global_coupon_id)
# 3,修改redis中数据
CONN.hset(key, "default_coupon_id", course_coupon_id)
res.data = "更新优惠券成功"
except Exception as e:
res.code = 1026
res.error = "更改优惠券失败"
return Response(res.dict)
shopping中settlement_view.py
六、支付中心模块
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.my_auth import LoginAuth
from utils.base_response import BaseResponse
from .settlement_view import SETTLEMENT_KEY, GLOBAL_COUPON_KEY
from utils.redis_pool import POOL
import redis
from Course.models import Course
from .models import Coupon
from django.utils.timezone import now
COON = redis.Redis(connection_pool=POOL)
# price balance
class PaymentView(APIView):
authentication_classes = [LoginAuth, ]
def post(self, request):
res = BaseResponse()
# 1 获取数据
balance = request.data.get("balance", 0)
price = request.data.get("price", "")
user_id = request.user.pk
# 2 校验数据的合法性
# 2.1 校验贝里数是否合法
if int(balance) > request.user.balance:
res.code = 1070
res.error = "抵扣的贝里错误"
return Response(res.dict)
# 2.2 从用户的结算中心拿数据 跟数据库比对是否合法
settlement_key = SETTLEMENT_KEY % (user_id, "*")
all_keys = COON.scan_iter(settlement_key)
# 课程id是否合法
course_rebate_total_price = 0
for key in all_keys:
settlement_info = COON.hgetall(key)
course_id = settlement_info["id"]
course_obj = Course.objects.filter(id=course_id).first()
if not course_obj or course_obj.status == 1:
res.code = 1071
res.error = "课程id不合法"
return Response(res.dict)
# 课程优惠券是否过期
course_coupon_id = settlement_info.get("default_coupon_id", 0)
if course_coupon_id:
coupon_dict = Coupon.objects.filter(
id=course_coupon_id,
couponrecord__status=0,
couponrecord__account_id=user_id,
object_id=course_id,
valid_begin_date__lte=now(),
valid_end_date__gte=now(),
).values("coupon_type", "money_equivalent_value", "off_percent", "minimum_consume")
if not coupon_dict:
res.code = 1072
res.error = "优惠券不合法"
return Response(res.dict)
# 2.3 校验price
# 得到所有的课程的折后价格和
course_pirce = settlement_info["price"]
course_rebate_price = self.account_price(coupon_dict, course_pirce)
if course_rebate_price == -1:
res.code = 1074
res.error = "课程优惠券不符合要求"
return Response(res.dict)
course_rebate_total_price += course_rebate_price
# 跟全局优惠券做折扣
# 校验全局优惠券是否合法
global_coupon_key = GLOBAL_COUPON_KEY % user_id
global_coupon_id = int(COON.hget(global_coupon_key, "default_global_coupon_id"))
if global_coupon_id:
global_coupon_dict = Coupon.objects.filter(
id=global_coupon_id,
couponrecord__status=0,
couponrecord__account_id=user_id,
valid_begin_date__lte=now(),
valid_end_date__gte=now(),
).values("coupon_type", "money_equivalent_value", "off_percent", "minimum_consume")
if not global_coupon_dict:
res.code = 1073
res.error = "全局优惠券id不合法"
return Response(res.dict)
global_rebate_price = self.account_price(global_coupon_dict, course_rebate_total_price)
if global_rebate_price == -1:
res.code = 1076
res.error = "全局优惠券不符合要求"
return Response(res.dict)
# 抵扣贝里
balance_money = balance / 100
balance_rebate_price = global_rebate_price - balance
if balance_rebate_price < 0:
balance_rebate_price = 0
# 终极校验price
if balance_rebate_price != price:
res.code = 1078
res.error = "价格不合法"
return Response(res.dict)
# 先去创建订单 订单状态未支付状态
# 3 调用支付宝接口支付
# 如果成功支付支付宝会给我们发回调
# 改变订单的状态
# 注意订单详情表有多个记录
# 更改优惠券的使用状态
# 更改用户表里的贝里 贝里要添加交易记录
def account_price(self, coupon_dict, price):
coupon_type = coupon_dict["coupon_type"]
if coupon_type == 0:
# 通用优惠券
money_equivalent_value = coupon_dict["money_equivalent_value"]
if price - money_equivalent_value >=0:
rebate_price = price - money_equivalent_value
else:
rebate_price = 0
elif coupon_type == 1:
# 满减
money_equivalent_value = coupon_dict["money_equivalent_value"]
minimum_consume = coupon_dict["minimum_consume"]
if price >= minimum_consume:
rebate_price = price - money_equivalent_value
else:
return -1
elif coupon_type == 2:
# 折扣
minimum_consume = coupon_dict["minimum_consume"]
off_percent = coupon_dict["off_percent"]
if price >= minimum_consume:
rebate_price = price * (off_percent / 100)
else:
return -1
return rebate_price
shopping中payment_view.py