路飞项目接口

路飞项目接口

轮播图接口

# model.py
class Banner(BaseModel):
    name = models.CharField(max_length=32, verbose_name='图片名字')
    img = models.ImageField(upload_to='banner', verbose_name='图片', blank=True)
    link = models.CharField(max_length=32, verbose_name='跳转链接')
    info = models.TextField(verbose_name='图片介绍')

    class Meta:
        verbose_name = '轮播图'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name
    
    
# ser.py
class BannerModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Banner
        fields = ['name', 'img', 'link']
       
#views.py
class BannerView(GenericViewSet, ListModelMixin):
    queryset = Banner.objects.filter(is_delete=False, is_show=True).order_by('display_order')[:settings.BANNER_COUNTER]
    serializer_class = BannerModelSerializer

    # 把data的数据加到缓存
    # 1. 先去缓存拿数据库
    def list(self, request, *args, **kwargs):
        banner_list = cache.get('banner_list')
        if not banner_list:
            print('走数据库了')
            # 缓存中没有,去数据库拿
            respnose = super().list(request, *args, **kwargs)
            cache.set('banner_list', respnose.data, 60 * 60 * 24)
            return respnose

        return Response(data=banner_list)

我们可以把常用的接口放到redis中,有到redis里面去找,没有到mysql去找,在用celery做个定时任务,每隔一天到缓存中查找一下,刷新

登录接口

ser.py

class UserModelSerializer(serializers.ModelSerializer):
    username = serializers.CharField()

    class Meta:
        model = User
        fields = ['username', 'password']

    def validate(self, attrs):
        # 多种方式登录
        user = self._get_user(attrs)
        token = self._get_token(user)
        self.context['token'] = token
        self.context['user'] = user
        return attrs

    def _get_user(self, attrs):

        username = attrs.get('username')
        password = attrs.get('password')

        # user = User.objects.filter(username=username).first()
        if re.match('0?(13|14|15|16|17|18|19)[0-9]{9}$', username):
            user = User.objects.filter(telephone=username).first()
        elif re.match('\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}$', username):
            user = User.objects.filter(email=username).first()
        else:
            user = User.objects.filter(username=username).first()
        if user:
            if user.check_password(password):
                return user
            else:
                raise ValidationError('密码错误')
        else:
            raise ValidationError('账号不存在')

    def _get_token(self, user):

        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token

    
#views.py

class LoginView(ViewSet):

    @action(methods=['POST'], detail=False)
    def login(self, request, *args, **kwargs):
        user_ser = ser.UserModelSerializer(data=request.data)
        if user_ser.is_valid():
            token = user_ser.context.get('token')
            username = user_ser.context.get('user').username
            return APIResponse(token=token, username=username)

        else:
            return APIResponse(code=0, msg=user_ser.errors)

手机登录接口

# ser.py

class CodeLogin(serializers.ModelSerializer):
    code = serializers.CharField()

    class Meta:
        model = User
        fields = ['telephone', 'code', ]

    def validate(self, attrs):
        user = self._get_user(attrs)
        token = self._get_token(user)
        self.context['token'] = token
        self.context['user'] = user
        return attrs

    def _get_user(self, attrs):
        telephone = attrs.get('telephone')
        code = attrs.get('code')
        if re.match('0?(13|14|15|16|17|18|19)[0-9]{9}$', telephone):
            user = User.objects.filter(telephone=telephone).first()
            if user:
                from django.core.cache import cache
                from django.conf import settings
                # 从缓存中取出telephone
                cache_code = cache.get(settings.PHONE_CACHE_KEY % telephone)
                if code == cache_code:
                    # 存在取出code比较,相等把过期的验证码删除
                    cache.set(settings.PHONE_CACHE_KEY % telephone, code)
                    return user
                else:
                    raise ValidationError('验证码不正确')
            else:
                raise ValidationError('用户不存在')

        else:
            raise ValidationError('手机号不合法')

    def _get_token(self, user):
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token
    
#view.py
 @action(methods=['POST'], detail=False)
    def code_login(self, request, *args, **kwargs):
        user_ser = ser.CodeLogin(data=request.data)
        if user_ser.is_valid():
            token = user_ser.context.get('token')
            username = user_ser.context.get('user').username
            return APIResponse(token=token, username=username)

        else:
            return APIResponse(code=0, msg=user_ser.errors)

    # 验证手机是否存在
    @action(methods=['GET'], detail=False)
    def check_telephone(self, request, *args, **kwargs):

        telephone = request.query_params.get('telephone')
        import re
        if not re.match('0?(13|14|15|16|17|18|19)[0-9]{9}$', telephone):
            return APIResponse(code=0, msg='手机号不合法')
        try:
            User.objects.get(telephone=telephone)
            return APIResponse(code=1)
        except:
            return APIResponse(code=0, msg='手机号不存在')

    # 发送验证码
    @action(methods=['GET'], detail=False, throttle_classes=[SMSThrottling])
    def send(self, request, *args, **kwargs):
        import re

        from django.core.cache import cache
        from django.conf import settings
        telephone = request.query_params.get('telephone')
        if not re.match('0?(13|14|15|16|17|18|19)[0-9]{9}$', telephone):
            return APIResponse(code=0, msg='手机号不合法')
        code = get_code()
        result = send_single_sms(code, telephone)

        # 验证码保存(保存到哪?)
        cache.set(settings.PHONE_CACHE_KEY % telephone, code, 180)
        print(result)

        if result:

            return APIResponse(code=1, msg='验证码发送成功')
        else:

            return APIResponse(code=0, msg='验证码发送失败')


注册接口

#ser.py
class RegisterModelSerializer(serializers.ModelSerializer):
    code = serializers.CharField(write_only=True)

    class Meta:
        model = User
        fields = ['code', 'password', 'telephone', 'username']
        extra_kwargs = {
            'password': {'max_length': 16, 'min_length': 8},
            'username': {'read_only': True}
        }

    def validate(self, attrs):
        telephone = attrs.get('telephone')
        code = attrs.get('code')

        if re.match('0?(13|14|15|16|17|18|19)[0-9]{9}$', telephone):
            user = User.objects.filter(telephone=telephone).first()
            if not user:

                # 从缓存中取出telephone
                cache_code = cache.get(settings.PHONE_CACHE_KEY % telephone)
                if code == cache_code:
                    # 存在取出code比较,相等把过期的验证码删除
                    cache.set(settings.PHONE_CACHE_KEY % telephone, code)
                    attrs['username'] = telephone
                    attrs.pop('code')
                    return attrs
                else:
                    raise ValidationError('验证码不正确')
            else:
                raise ValidationError('此用户已注册存在')

        else:
            raise ValidationError('手机号不合法')

    def create(self, validated_data):
        user = User.objects.create_user(**validated_data)
        return user
    
#view.py
class RegisterView(GenericViewSet,CreateModelMixin):
    queryset = User.objects.all()
    serializer_class = ser.RegisterModelSerializer

    def create(self, request, *args, **kwargs):
        response = super().create(request, *args, **kwargs)
        username = response.data.get('username')
        return APIResponse(code=1,msg='注册成功',username=username)


云片网接口

import json
import requests
from luffyapi.utils.logger import log

def get_code():
    import random
    s_code = ''
    for i in range(4):
        s_code += str(random.randint(0,9))
    print(s_code)
    return s_code


def send_single_sms(code, mobile):
    from . import settings
    # 发送单条短信
    url = settings.url
    text = f"您的验证码是{code}。如非本人操作,请忽略本短信"
    apikey = settings.apikey
    try:
        res = requests.post(url, data={
            "apikey": apikey,
            "mobile": mobile,
            "text": text
        })

        re_json = json.loads(res.text)
        print(re_json)
        re = re_json['code']
        if re == 53:
            return False
        else:
            return True
    except Exception as e:
        log.error('手机号:%s,短信发送失败,错误为:%s' % (mobile, str(e)))

轮播图接口缓存redis缓存

# 首页轮播图数据缓存到redis中
def list(self, request, *args, **kwargs):

    # response=super().list(request, *args, **kwargs)
    # 把data的数据加缓存
    # 1 先去缓存拿数据
    banner_list=cache.get('banner_list')
    if not banner_list:
        print('走数据库了')
        # 缓存中没有,去数据库拿
        response = super().list(request, *args, **kwargs)
        # 加到缓存
        cache.set('banner_list',response.data,60*60*24)
        return response

    return Response(data=banner_list)

轮播图定时任务刷新

# celery_task/celery.py
# 三步走,加载django的环境,执行定时任务,任务的定时配置,
from celery import Celery


# 加载django的环境
import os
import django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev')
django.setup()

broker = 'redis://127.0.0.1:6379/1'
banckend = 'redis://127.0.0.1:6379/2'


app = Celery(__name__,broker=broker,banckend=banckend,include=['celery_task.home_task',])

# 执行定时任务 设置时区

app.conf.timezone='Asia/Shanghai'

# UTC时间
app.conf.enable_utc=False

# 任务的定时配置

from datetime import timedelta
from celery.schedules import crontab

app.conf.beat_schedule = {
    'add_task': {
        'task': 'celery_task.home_task.banner_update',
        # 'schedule': timedelta(seconds=30),

    }
}



# celery_task/home_task.py
from .celery import app
from home import ser
    from home import models
    from django.conf import settings
    from django.core.cache import cache

@app.task
def banner_update():
    queryset_banner = models.Banner.objects.filter(is_show=True, is_delete=False).order_by('orders')[
                      0:settings.BANNER_COUNTER]
    serializer_banner = ser.BannerModelSerializer(instance=queryset_banner,many=True)
    for banner in serializer_banner.data:
        banner['img'] = 'http://127.0.0.1:8000'+banner['img']
    cache.set('banner_list',serializer_banner.data)
    return True

必须建立一个celery.py的文件

课程分类群查接口

class CourseCategoryView(GenericViewSet, ListModelMixin):
    queryset = models.CourseCategory.objects.filter(is_delete=False, is_show=True).order_by('orders')
    serializer_class = serializer.CourseCategoryModelSerializer

课程群查接口

class CourseView(GenericViewSet, ListModelMixin,RetrieveModelMixin):
    queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by('orders')
    serializer_class = serializer.CourseModelSerializer
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    ordering_fields = ['id', 'price', 'students']
    filter_fields = ['course_category', 'students']
    
    
# ser.py
class TeacherModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Teacher
        fields = ['name', 'role_name', 'title', 'signature', 'image', 'brief']


# 展示课程
class CourseModelSerializer(serializers.ModelSerializer):
    teacher = TeacherModelSerializer()


    class Meta:
        model = models.Course
        fields = [
            'id',
            'name',
            'course_img',
            'brief',
            'attachment_path',
            'pub_sections',
            'price',
            'students',
            'period',
            'sections',
            'course_type_name',
            'level_name',
            'status_name',
            'teacher',
            'section_list',
        ]
        
# models.py
    def course_type_name(self):
        return self.get_course_type_display()

    def level_name(self):
        return self.get_level_display()

    def status_name(self):
        return self.get_status_display()

    @property
    def section_list(self):
        ll = []

        # 根据课程取出所有章节(正向查询,字段名.all())
        course_chapter_list = self.coursechapters.all()
        for course_chapter in course_chapter_list:
            # 通过章节对象,取到章节下所有的课时(反向查询)
            # course_chapter.表名小写_set.all() 现在变成了course_chapter.coursesections.all()
            course_sections_list = course_chapter.coursesections.all()
            for course_section in course_sections_list:
                ll.append({
                    'name': course_section.name,
                    'section_link': course_section.section_link,
                    'duration': course_section.duration,
                    'free_trail': course_section.free_trail,
                })
                if len(ll) >= 4:
                    return ll


        return ll

搜索组件和过滤组件

排序:
按id正序倒叙排序,按price正序倒叙排列
使用:http://127.0.0.1:8000/course/free/?ordering=-id
配置类:
    filter_backends=[OrderingFilter]
配置字段:
    ordering_fields=['id','price']
    
    
内置过滤:
使用:http://127.0.0.1:8000/course/free/?search=39
按照price过滤(表自有的字段直接过滤)
配置类:
    filter_backends=[SearchFilter]
配置字段:
    search_fields=['price']
    
扩展:django-filter
安装:
支持自由字段的过滤还支持外键字段的过滤
http://127.0.0.1:8000/course/free/?course_category=1   # 过滤分类为1 (python的所有课程)
配置类:
    filter_backends=[DjangoFilterBackend]
配置字段:
    filter_fields=['course_category']

自定义过滤组件

# filters.py
# 自定义过滤规则
from rest_framework.filters import BaseFilterBackend

class MyFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # 真正的过滤规则
        # params=request.GET.get('teacher')
        # queryset.filter('''''')
        return queryset[:1]
 
# 使用:在视图类中配置
filter_backends=[DjangoFilterBackend,OrderingFilter,MyFilter]

区间过滤

# 借助django-filter实现区间过滤
# 实现区间过滤
##########1 filters.py
class CourseFilterSet(FilterSet):
    # 课程的价格范围要大于min_price,小于max_price
    min_price = filters.NumberFilter(field_name='price', lookup_expr='gt')
    max_price = filters.NumberFilter(field_name='price', lookup_expr='lt')
    class Meta:
        model=models.Course
        fields=['course_category']
#####2 视图类中配置
     -filter_backends=[DjangoFilterBackend]
	 # 配置类:(自己写的类)
     -filter_class = CourseFilterSet

课程单查接口

class CouresView(GenericViewSet,ListModelMixin,RetrieveModelMixin):
    queryset = models.Course.objects.filter(is_delete=False,is_show=True).order_by('orders')
    serializer_class = serializer.CourseModelSerializer
    pagination_class = PageNumberPagination

    # 过滤和排序
    # filter_backends=[DjangoFilterBackend,OrderingFilter,SearchFilter]
    # filter_backends=[DjangoFilterBackend,OrderingFilter,MyFilter]
    filter_backends=[DjangoFilterBackend,OrderingFilter]
    # # filter_backends=OrderingFilter
    ordering_fields=['id', 'price', 'students']
    # # search_fields=['course_category']
    filter_fields=['course_category']

只需要在课程视图上继承RetrieveModelMixin就可完成单查

章节分类接口

#1 urls.py
router.register('chapters', views.CourseChapterView, 'coursechapter')

# 2 views.py
class CourseChapterView(GenericViewSet,ListModelMixin):
    queryset = models.CourseChapter.objects.filter(is_delete=False,is_show=True)
    serializer_class = serializer.CourseChapterSerializer

    # 可以按照课程id来查
    filter_backends = [DjangoFilterBackend]
    filter_fields = ['course']
    
    
    
# 3 serializer.py
class CourseSectionSerializer(serializers.ModelSerializer):
    class Meta:
        model=models.CourseSection
        fields=['name','orders','duration','free_trail','section_link','section_type_name']
class CourseChapterSerializer(serializers.ModelSerializer):
    # 子序列化的方式
    coursesections=CourseSectionSerializer(many=True)
    class Meta:
        model=models.CourseChapter
        fields=['name','summary','chapter','coursesections']

后台搜索接口

# urls.py
router.register('search', views.CouresSearchView, 'search')
# views.py
class CouresSearchView(GenericViewSet,ListModelMixin):
    queryset = models.Course.objects.filter(is_delete=False,is_show=True)
    serializer_class = serializer.CourseModelSerializer
    pagination_class = PageNumberPagination
    filter_backends=[SearchFilter]
    search_fields=['name']

支付宝接口

#libs/alipay/pem/ali_pulic_key.pem和ali_pulic_key.pem这里配置你的私钥和支付宝的公钥

# __init__.py这样容易导
from .ali_pay import alipay, gateway

# alipay/settings.py
import os

APP_PRIVATE_KEY_STRING = open(os.path.join(os.path.dirname(__file__), 'pem', 'private_key.pem')).read()
ALIPAY_PUBLIC_KEY_STRING = open(os.path.join(os.path.dirname(__file__), 'pem', 'ali_pulic_key.pem')).read()
APPID = 2021000116665495
SIGN_TYPE = 'RSA2'
DEBUG = True
GATEWAY = 'https://openapi.alipaydev.com/gateway.do?' if DEBUG else 'https://openapi.alipay.com/gateway.do?'


# alipay/ali_pay.py
from alipay import AliPay
from . import settings


alipay = AliPay(
    appid=settings.APPID,
    app_notify_url='http://127.0.0.1:8000/home/',
    app_private_key_string=settings.APP_PRIVATE_KEY_STRING,
    alipay_public_key_string=settings.ALIPAY_PUBLIC_KEY_STRING,
    sign_type=settings.SIGN_TYPE,
    debug=settings.DEBUG
)

gateway = settings.GATEWAY

订单接口

from rest_framework.mixins import CreateModelMixin
from . import models
from . import serializer
from utils.apitoken import JWTJSONWebTokenAuthentication
from rest_framework.response import Response
from rest_framework.views import APIView

# Create your views here.


class PayView(GenericViewSet, CreateModelMixin):
    authentication_classes = [JWTJSONWebTokenAuthentication]
    queryset = models.Order.objects.all()
    serializer_class = serializer.OrderModelSerializer

    """因为卖家要付款,我们要在request.data里面取出user,重写create方法"""

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)

        self.perform_create(serializer)
        return Response(serializer.context.get('pay_url'))

    

serializer.py

from . import models
from course.models import Course
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
import uuid
from django.conf import settings

class OrderModelSerializer(serializers.ModelSerializer):
    course = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), many=True, write_only=True)
    '''
    前端传过来的数据形式:{course:[1,2,3],total_amount:1111,out_trade_no:1223334,pay_type:1}
    这里前端传过来的是课程的一个个对象,我们需要处理course:[obj1,obj2]
    '''

    class Meta:
        model = models.Order
        fields = ['total_amount', 'subject', 'pay_type', 'course']
        extra_kwargs = {
            'pay_type': {'required': True},
            'total_amount': {'required': True}
        }

    def validate(self, attrs):
        total_amount = self._check_price(attrs)  # 校验价钱
        out_trade_no = self._get_out_trade_no()  # 生成订单号
        user = self._get_user()  # 需要request对象(需要视图通过context把request对象传入。重写create方法)
        pay_url = self._get_pay_url(out_trade_no, total_amount, attrs.get('subject'))
        self._before_create(attrs, user, pay_url, out_trade_no)
        return attrs

    def _check_price(self, attrs):
        total_amount = attrs.get('total_amount')  # 总价钱

        course_list = attrs.get('course')

        course_price = 0
        for course in course_list:  # 取出课程里面的价钱相加
            course_price += course.price
        if total_amount != course_price:
            raise ValidationError('价钱不一致')  # 返回错误信息
        # print(attrs)
        return total_amount

    def _get_out_trade_no(self):  # 生成订单号
        out_trade_no = uuid.uuid4()
        return str(out_trade_no).replace('-', '')

    def _get_user(self):

        user = self.context.get('request').user
        return user

    def _get_pay_url(self, out_trade_no, total_amount, subject):
        from libs.alipay import alipay, gateway

        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no=out_trade_no,
            total_amount=float(total_amount),
            subject=subject,
            return_url=settings.RETURN_URL,
            notify_url=settings.NOTIFY_URL
        )
        return gateway + order_string

    def _before_create(self,attrs,user,pay_url,out_trade_no):
        attrs['user'] = user
        self.context['pay_url'] = pay_url
        attrs['out_trade_no'] = out_trade_no


    def create(self, validated_data):

        course_list = validated_data.pop('course')

        order = models.Order.objects.create(**validated_data)
        for course in course_list:
            models.OrderDetail.objects.create(order=order,course=course,price=course.price,real_price=course.price)
        print(order)
        return order

总结:

  • 前端传过来的订单里面的course是一个个课程的对象,前端传过来的数据形式:{course:[1,2,3],total_amount:1111,out_trade_no:1223334,pay_type:1}这里前端传过来的是课程的一个个对象,我们需要处理course:[obj1,obj2],我们只需要用PrimaryKeyRelatedField指定关联的Course表就可以了
  • 生成订单里面需要传入user字段,订单和user是一对多,我们需要在view视图里面重新写create方法,把request传过来,取出user
  • 支付接口五步走:
    • 校验价钱
    • 生成订单号
    • 需要request对象(需要视图通过context把request对象传入。重写create方法)
    • 生成支付宝支付url
    • 为保存做准备,把支付宝的支付的url传给view
    • 把课程pop出来,保存两张表,订单详情表和订单表

支付宝回调接口

# 支付宝回调地址
class  PaySuccessView(APIView):
    def get(self,request, *args,**kwargs):
        # 接收支付宝发送到后端的get请求,改变支付状态,查询订单号是否一致
        out_trade_no = request.query_params.get('out_trade_no')
        # 查询订单的状态
        order = models.Order.objects.filter(out_trade_no=out_trade_no).first()
        if order.order_status ==1:
            return Response(True)
        return Response(False)

    def post(self, request, *args, **kwargs):
        from libs.alipay.ali_pay import alipay
        from utils.logger import log
        '''支付宝调回接口'''
        data = request.data
        out_trade_no = data.get('out_trade_no', None)
        gmt_payment = data.get('gmt_payment', None)
        signature = data.pop('sign')
        success = alipay.verify(data, signature)
        if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):
            models.Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1, pay_time=gmt_payment)
            log.info('%s订单支付成功' % out_trade_no)
            return Response('success')
        else:
            log.info('%s订单有问题' % out_trade_no)
            return Response('error')
posted @ 2020-08-01 15:11  小子,你摊上事了  阅读(82)  评论(0编辑  收藏  举报