luffy后台

"""
├── luffyapi
    ├── logs/                # 项目运行时/开发时日志目录 - 包
    ├── manage.py            # 脚本文件
    ├── luffyapi/              # 项目主应用,开发时的代码保存 - 包
        ├── apps/              # 开发者的代码保存目录,以模块[子应用]为目录保存 - 包
        ├── libs/              # 第三方类库的保存目录[第三方组件、模块] - 包
        ├── settings/          # 配置目录 - 包
            ├── dev.py       # 项目开发时的本地配置
            └── prod.py      # 项目上线时的运行配置
        ├── urls.py            # 总路由
        └── utils/             # 多个模块[子应用]的公共函数类库[自己开发的组件]
    └── scripts/               # 保存项目运营时的脚本文件 - 文件夹
"""

 

 

 

 

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'  #broker任务队列
backend='redis://127.0.0.1:6379/2'   # 结构存储,执行完的结果存在这

app=Celery(__name__,broker=broker,backend=backend,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),
        # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
        # 'args': (300, 150),
    }
}

# 一定要启动beat
# celery beat -A celery_task -l info
celery_task/celery.py
from .celery import app

# cache
# model,serilizer

@app.task
def banner_update():
    from home import serializer
    from home import models
    from django.conf import settings
    from django.core.cache import cache
    queryset_banner = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('orders')[
               :settings.BANNER_COUNTER]
    serializer_banner=serializer.BannerModelSerilaizer(instance=queryset_banner,many=True)
    # print(serializer_banner.data)
    for banner in serializer_banner.data:
        banner['img']='http://127.0.0.1:8000'+banner['img']
    cache.set('banner_list',serializer_banner.data)
    # import time
    # time.sleep(1)
    # banner_list=cache.get('banner_list')
    # print(banner_list)
    return True
celery_task/home_task.py

.......

import xadmin
from . import models
xadmin.site.register(models.CourseCategory)
xadmin.site.register(models.Course)
xadmin.site.register(models.Teacher)
xadmin.site.register(models.CourseChapter)
xadmin.site.register(models.CourseSection)
luffyapi/apps/course/adminx.py
from django.apps import AppConfig


class CourseConfig(AppConfig):
    name = 'course'
luffyapi/apps/course/apps.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]



from django_filters.filterset import FilterSet
from django_filters import filters

from . import models
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']
luffyapi/apps/course/filters.py
from django.db import models

# Create your models here.


# 分析过程
# class Course(models.Model):
#     name = models.CharField(max_length=64)
#     title = models.CharField(max_length=64)
#
#     level = models.IntegerField(choices=((0, '入门'), (1, '进阶')), default=0)
#
#     detail = models.TextField()  # 可以关联详情表
#     type = models.IntegerField(choices=((0, 'Python'), (1, 'Linux')), default=0)
#     is_show = models.BooleanField(default=False)
#
#     # 优化字段
#     # 总学生
#     students = models.IntegerField(default=0)
#     # 总学时
#     time = models.IntegerField(default=0)
#
#     class Meta:
#         abstract = True
#
# # 免费课
# class FreeCourse(Course):
#     image = models.ImageField(upload_to='course/free')
#     attachment = models.FileField(upload_to='attachment')
#
# # 实战课
# class ActualCourse(Course):
#     image = models.ImageField(upload_to='course/actual')
#     price = models.DecimalField(max_digits=7, decimal_places=2)
#     # cost = models.DecimalField(max_digits=7, decimal_places=2)
#
# # 轻课
# class LightCourse(Course):
#     image = models.ImageField(upload_to='course/light')
#     price = models.DecimalField(max_digits=7, decimal_places=2)
#     # cost = models.DecimalField(max_digits=7, decimal_places=2)
#     period = models.IntegerField(verbose_name='学习建议周期(month)', default=0)

from luffyapi.utils.models import BaseModel
# 实际路飞课程相关表,以免费课为例
class CourseCategory(BaseModel):
    """分类
    python,linux,go, 网络安全
    跟课程是一对多的关系

    """
    name = models.CharField(max_length=64, unique=True, verbose_name="分类名称")
    class Meta:
        db_table = "luffy_course_category"
        verbose_name = "分类"
        verbose_name_plural = verbose_name

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


class Course(BaseModel):
    """课程"""
    course_type = (
        (0, '付费'),
        (1, 'VIP专享'),
        (2, '学位课程')
    )
    level_choices = (
        (0, '初级'),
        (1, '中级'),
        (2, '高级'),
    )
    status_choices = (
        (0, '上线'),
        (1, '下线'),
        (2, '预上线'),
    )
    # 原始字段
    name = models.CharField(max_length=128, verbose_name="课程名称")
    course_img = models.ImageField(upload_to="courses", max_length=255, verbose_name="封面图片", blank=True, null=True)
    course_type = models.SmallIntegerField(choices=course_type, default=0, verbose_name="付费类型")
    # 使用这个字段的原因
    brief = models.TextField(max_length=2048, verbose_name="详情介绍", null=True, blank=True)
    level = models.SmallIntegerField(choices=level_choices, default=0, verbose_name="难度等级")
    pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
    period = models.IntegerField(verbose_name="建议学习周期(day)", default=7)
    attachment_path = models.FileField(upload_to="attachment", max_length=128, verbose_name="课件路径", blank=True, null=True)
    status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="课程状态")
    price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价", default=0)

    # 优化字段
    students = models.IntegerField(verbose_name="学习人数", default=0)
    sections = models.IntegerField(verbose_name="总课时数量", default=0)
    pub_sections = models.IntegerField(verbose_name="课时更新数量", default=0)

    # 关联字段
    teacher = models.ForeignKey("Teacher", on_delete=models.DO_NOTHING, null=True, blank=True, verbose_name="授课老师",db_constraint=False)
    course_category = models.ForeignKey("CourseCategory", on_delete=models.SET_NULL, db_constraint=False, null=True, blank=True,verbose_name="课程分类")
    class Meta:
        db_table = "luffy_course"
        verbose_name = "课程"
        verbose_name_plural = "课程"

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

    @property
    def course_type_name(self):
        return self.get_course_type_display()
    @property
    def level_name(self):
        return self.get_level_display()
    @property
    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



class Teacher(BaseModel):
    """导师
    跟课程一对多,关联字段写在课程表中
    """
    role_choices = (
        (0, '讲师'),
        (1, '导师'),
        (2, '班主任'),
    )
    name = models.CharField(max_length=32, verbose_name="导师名")
    role = models.SmallIntegerField(choices=role_choices, default=0, verbose_name="导师身份")
    title = models.CharField(max_length=64, verbose_name="职位、职称")
    signature = models.CharField(max_length=255, verbose_name="导师签名", help_text="导师签名", blank=True, null=True)
    image = models.ImageField(upload_to="teacher", null=True, verbose_name="导师封面")
    brief = models.TextField(max_length=1024, verbose_name="导师描述")

    class Meta:
        db_table = "luffy_teacher"
        verbose_name = "导师"
        verbose_name_plural = verbose_name

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

    def role_name(self):
        # 返回角色的中文
        return self.get_role_display()



class CourseChapter(BaseModel):
    """章节
    章节跟课程是一(课程)对多(章节多)
    """
    course = models.ForeignKey("Course", related_name='coursechapters', on_delete=models.CASCADE, verbose_name="课程名称",db_constraint=False)
    chapter = models.SmallIntegerField(verbose_name="第几章", default=1)
    name = models.CharField(max_length=128, verbose_name="章节标题")
    summary = models.TextField(verbose_name="章节介绍", blank=True, null=True)
    pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)

    class Meta:
        db_table = "luffy_course_chapter"
        verbose_name = "章节"
        verbose_name_plural = verbose_name

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



class CourseSection(BaseModel):
    """课时
        章节和课时是一对多的关系,关联字段写在多的一方,课时
    """
    section_type_choices = (
        (0, '文档'),
        (1, '练习'),
        (2, '视频')
    )
    chapter = models.ForeignKey("CourseChapter", related_name='coursesections', on_delete=models.CASCADE,
                                verbose_name="课程章节",db_constraint=False)
    name = models.CharField(max_length=128, verbose_name="课时标题")
    orders = models.PositiveSmallIntegerField(verbose_name="课时排序")
    section_type = models.SmallIntegerField(default=2, choices=section_type_choices, verbose_name="课时种类")
    section_link = models.CharField(max_length=255, blank=True, null=True, verbose_name="课时链接",
                                    help_text="若是video,填vid,若是文档,填link")
    duration = 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(verbose_name="是否可试看", default=False)

    class Meta:
        db_table = "luffy_course_Section"
        verbose_name = "课时"
        verbose_name_plural = verbose_name

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

    @property
    def section_type_name(self):
        return self.get_section_type_display()
luffyapi/apps/course/models.py
from rest_framework.pagination import  PageNumberPagination as DRFPageNumberPagination

class PageNumberPagination(DRFPageNumberPagination):
    page_size=1
    page_query_param = 'page'
    max_page_size = 10
    page_size_query_param='page_size'
luffyapi/apps/course/paginations.py
from rest_framework import serializers
from . import models


class CourseCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.CourseCategory
        fields = ['id', 'name']


class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Teacher
        fields = ('name', 'role_name', 'title', 'signature', 'image', 'brief')


class CourseModelSerializer(serializers.ModelSerializer):
    # 子序列化的方式
    teacher = TeacherSerializer()

    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',
        ]
        # fields = ['id', 'name',
        #           'course_img',
        #           'brief',
        #           'teacher',
        #           'course_type_name',
        #           'status_name',
        #           'level_name',
        #           'course_sections'
        #           ]




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']
luffyapi/apps/course/serializer.py
from django.urls import path, include
from . import views
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('categories', views.CourseCategoryView, 'categories')
router.register('free', views.CouresView, 'free')
router.register('chapters', views.CourseChapterView, 'coursechapter')
router.register('search', views.CouresSearchView, 'search')
urlpatterns = [
    path('', include(router.urls)),

]
luffyapi/apps/course/urls.py
from django.shortcuts import render

# Create your views here.


from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,RetrieveModelMixin
from . import models
from . import serializer
class CourseCategoryView(GenericViewSet,ListModelMixin):
    queryset = models.CourseCategory.objects.filter(is_delete=False,is_show=True).order_by('orders')
    serializer_class = serializer.CourseCategorySerializer


from .paginations import PageNumberPagination
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter,SearchFilter
from .filters import MyFilter,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=['name']

    # filter_fields=['course_category']


    # # g同过django-filter扩展过滤
    # filter_backends = [DjangoFilterBackend]
    # # 原来是配置字段,现在配置类
    filter_class = CourseFilterSet


'''
django-filters指定以某个字段过滤有两种方式
第一种:
    配置类:
    filter_backends=[DjangoFilterBackend]
    配置字段:
    filter_fields=['course_category']
第二种:
    配置类:
    filter_backends=[DjangoFilterBackend]
    配置类:(自己写的类)
    class CourseFilterSet(FilterSet):
        class Meta:
            model=models.Course
            fields=['course_category']
    filter_class = CourseFilterSet
    
第三种:实现区间过滤
    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']
    配置类:
        filter_backends=[DjangoFilterBackend]
    配置类:(自己写的类)
        -filter_class = CourseFilterSet
'''




'''
排序:
按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']


'''

#如何自定义排序或者过滤类




# 课程章节接口
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']




# 搜索接口
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']
luffyapi/apps/course/views.py

.......

import xadmin

# 注册banner表
from . import models

xadmin.site.register(models.Banner)
luffyapi/apps/home/adminx.py
from django.db import models

# from luffyapi.utils.models import BaseModel
from utils.models import BaseModel


class Banner(BaseModel):
    name = models.CharField(max_length=32, verbose_name='图片名字')
    img = models.ImageField(upload_to='banner', verbose_name='轮播图', help_text='图片尺寸必须是:3840*800', null=True)
    link = models.CharField(max_length=32, verbose_name='跳转连接')
    info = models.TextField(verbose_name='图片简介')

    # type=models.IntegerField(choices=)

    def __str__(self):
        return self.name
luffyapi/apps/home/models.py
from rest_framework import serializers
from . import models


class BannerModelSerilaizer(serializers.ModelSerializer):
    class Meta:
        model = models.Banner
        fields = ['name', 'link', 'img']
luffyapi/apps/home/serializer.py
from django.urls import path, re_path, include
from . import views

from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('banner', views.BannerView, 'banner')
urlpatterns = [
    # path('banner/', views.BannerView.as_view()),
    path('', include(router.urls)),

]
luffyapi/apps/home/urls.py
from django.shortcuts import render

from rest_framework.views import APIView
from luffyapi.utils.response import APIResponse
from luffyapi.utils.logger import log

from rest_framework.mixins import ListModelMixin
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import GenericViewSet
from . import models
from . import serializer

# class BannerView(GenericAPIView,ListModelMixin):  # 这个路由配置  path('banner/', views.BannerView.as_view()),

# 路由位置
'''
from rest_framework.routers import  SimpleRouter
router=SimpleRouter()
router.register('banner',views.BannerView,'banner')
path('', include(router.urls)),
'''
from django.conf import settings
from django.core.cache import cache
from rest_framework.response import Response
class BannerView(GenericViewSet, ListModelMixin):
    # 无论有多少条待展示的数据,最多就展示3条
    queryset = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('orders')[
               :settings.BANNER_COUNTER]
    serializer_class = serializer.BannerModelSerilaizer

    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)
luffyapi/apps/home/views.py

.......

from django.db import models

# Create your models here.


from django.db import models
from user.models import User
from course.models import Course


class Order(models.Model):
    """订单模型"""
    status_choices = (
        (0, '未支付'),
        (1, '已支付'),
        (2, '已取消'),
        (3, '超时取消'),
    )
    pay_choices = (
        (1, '支付宝'),
        (2, '微信支付'),
    )
    subject = models.CharField(max_length=150, verbose_name="订单标题")
    total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="订单总价", default=0)
    out_trade_no = models.CharField(max_length=64, verbose_name="订单号", unique=True)
    trade_no = models.CharField(max_length=64, null=True, verbose_name="流水号")  # 支付宝生成回来的
    order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="订单状态")
    pay_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name="支付方式")
    pay_time = models.DateTimeField(null=True, verbose_name="支付时间")
    # 一个用户可以下多个订单,一个订单只属于一个用户,一对多的关系,关联字段写在多个一方,写在order方
    user = models.ForeignKey(User, related_name='order_user', on_delete=models.DO_NOTHING, db_constraint=False, verbose_name="下单用户")
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
    class Meta:
        db_table = "luffy_order"
        verbose_name = "订单记录"
        verbose_name_plural = "订单记录"

    def __str__(self):
        return "%s - ¥%s" % (self.subject, self.total_amount)

    @property
    def courses(self):
        data_list = []
        for item in self.order_courses.all():
            data_list.append({
                "id": item.id,
                "course_name": item.course.name,
                "real_price": item.real_price,
            })
        return data_list


# 订单和详情是一对多,关联字段写在多个的一方,写在订单详情表中

class OrderDetail(models.Model):
    """订单详情"""
    order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, db_constraint=False, verbose_name="订单")
    course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.SET_NULL, db_constraint=False, verbose_name="课程",null=True)
    price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价")
    real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程实价")

    class Meta:
        db_table = "luffy_order_detail"
        verbose_name = "订单详情"
        verbose_name_plural = "订单详情"

    def __str__(self):
        try:
            return "%s的订单:%s" % (self.course.name, self.order.out_trade_no)
        except:
            return super().__str__()
luffyapi/apps/order/models.py
from rest_framework import serializers
from . import models
from rest_framework.exceptions import ValidationError
from course.models import Course

from django.conf import settings
class OrderSerializer(serializers.ModelSerializer):
    # 前端传什么数据过来{course:[1,2,3],total_amount:100,subject:xx商品,pay_type:1,}
    # user字段需要,但是不是传的,使用了jwt


    # 需要把course:[1,2,3] 处理成 course:[obj1,obj2,obj3]

    # 课时:[1,4,6,]===>课时:[obj1,obj4,obj6,]
    # course=serializers.CharField()
    course=serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), write_only=True, many=True)

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


    def _check_price(self,attrs):
        total_amount=attrs.get('total_amount')
        course_list=attrs.get('course')
        total_price=0
        for course in course_list:
            total_price+=course.price
        if total_price!=total_amount:
            raise ValidationError('价格不合法')
        return total_amount

    def _gen_out_trade_no(self):
        import uuid
        return str(uuid.uuid4()).replace('-','')

    def _get_user(self):
        # 需要request对象(需要视图通过context把reuqest对象传入。重写create方法)
        request=self.context.get('request')
        return request.user

    def _gen_pay_url(self,out_trade_no,total_amout,subject):
        # total_amout是Decimal类型,识别不了,需要转换成float类型
        from luffyapi.libs.al_pay import alipay,gateway
        order_string = alipay.api_alipay_trade_page_pay    (
            out_trade_no=out_trade_no,
            total_amount=float(total_amout),
            subject=subject,
            return_url=settings.RETURN_URL,  # get回调,前台地址
            notify_url=settings.NOTIFY_URL   # post回调,后台地址
        )
        return gateway+order_string

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

        self.context['pay_url']=pay_url
    def validate(self, attrs):
        '''
        # 1)订单总价校验
        # 2)生成订单号
        # 3)支付用户:request.user
        # 4)支付链接生成
        # 5)入库(两个表)的信息准备
        '''
        # 1)订单总价校验
        total_amout = self._check_price(attrs)
        # 2)生成订单号
        out_trade_no=self._gen_out_trade_no()
        # 3)支付用户:request.user
        user=self._get_user()
        # 4)支付链接生成
        pay_url=self._gen_pay_url(out_trade_no,total_amout,attrs.get('subject'))
        # 5)入库(两个表)的信息准备
        self._before_create(attrs,user,pay_url,out_trade_no)

        return attrs



    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)

        return order
luffyapi/apps/order/serializer.py
from django.urls import path, re_path, include
from . import views

from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('pay', views.PayView, 'pay')
urlpatterns = [
    path('', include(router.urls)),
    path('success/', views.SuccessView.as_view()),

]
luffyapi/apps/order/urls.py
from django.shortcuts import render

# Create your views here.

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import CreateModelMixin
from . import models
from . import serializer
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class PayView(GenericViewSet,CreateModelMixin):
    authentication_classes = [JSONWebTokenAuthentication,]
    permission_classes = [IsAuthenticated,]
    queryset = models.Order.objects.all()
    serializer_class = serializer.OrderSerializer

    # 重写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'))



class SuccessView(APIView):
    def get(self,request,*args,**kwargs):
        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)
        else:
            return Response(False)

    def post(self,request,*args,**kwargs):
        '''
        支付宝回调接口
        '''
        from luffyapi.libs.al_pay import alipay
        from luffyapi.utils.logger import log
        # 注意这个小细节,把他转成字典
        data = request.data.dict()
        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')
luffyapi/apps/order/views.py

.......

import xadmin
from xadmin import views


class GlobalSettings(object):
    """xadmin的全局配置"""
    site_title = "路飞学城"  # 设置站点标题
    site_footer = "路飞学城有限公司"  # 设置站点的页脚
    # menu_style = "accordion"  # 设置菜单折叠


xadmin.site.register(views.CommAdminView, GlobalSettings)

# 默认xadmin就已经把权限6表注册了
luffyapi/apps/user/adminx.py
from django.db import models

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    telephone = models.CharField(max_length=11)
    icon = models.ImageField(upload_to='icon', default='icon/default.png')
luffyapi/apps/user/models.py
from rest_framework import serializers
from . import models
from rest_framework.exceptions import ValidationError

import re
from django.core.cache import cache
from django.conf import settings
class UserSerilaizer(serializers.ModelSerializer):
    username = serializers.CharField()

    class Meta:
        model = models.User
        fields = ['username', 'password', 'id']
        extra_kwargs = {
            'id': {'read_only': True},
            'password': {'write_only': True}
        }

    def validate(self, attrs):

        # 多种登录方式
        user = self._get_user(attrs)
        # 签发token
        token = self._get_token(user)
        # 放到context中,我在视图类中可以取出来
        self.context['token'] = token
        self.context['user'] = user

        return attrs

    def _get_user(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')
        import re
        if re.match('^1[3-9][0-9]{9}$', username):
            user = models.User.objects.filter(telephone=username).first()
        elif re.match('^.+@.+$', username):  # 邮箱
            user = models.User.objects.filter(email=username).first()
        else:
            user = models.User.objects.filter(username=username).first()
        if user:
            ret = user.check_password(password)
            if ret:
                return user
            else:
                raise ValidationError('密码错误')
        else:
            raise ValidationError('用户不存在')

    def _get_token(self, user):
        from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
        payload = jwt_payload_handler(user)  # 通过user对象获得payload
        token = jwt_encode_handler(payload)  # 通过payload获得token
        return token


class CodeUserSerilaizer(serializers.ModelSerializer):
    code=serializers.CharField()
    class Meta:
        model = models.User
        fields = ['telephone', 'code']

    def validate(self, attrs):
        user=self._get_user(attrs)
        # 用户存在,签发token
        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')

        # 取出原来的code
        cache_code=cache.get(settings.PHONE_CACHE_KEY%telephone)
        if code ==cache_code:
            # 验证码通过
            if re.match('^1[3-9][0-9]{9}$', telephone):
                user = models.User.objects.filter(telephone=telephone).first()
                if user:
                    # 把使用过的验证码删除
                    cache.set(settings.PHONE_CACHE_KEY % telephone,'')
                    return user
                else:
                    raise ValidationError('用户不存在')
            else:
                raise ValidationError('手机号不合法')
        else:
            raise ValidationError('验证码错误')


    def _get_token(self, user):
        from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
        payload = jwt_payload_handler(user)  # 通过user对象获得payload
        token = jwt_encode_handler(payload)  # 通过payload获得token
        return token



class UserRegisterSerilaizer(serializers.ModelSerializer):
    code=serializers.CharField(max_length=4,min_length=4,write_only=True)
    class Meta:
        model = models.User
        fields = ['telephone', 'code','password','username']
        extra_kwargs = {
            'password': {'max_length': 18,'min_length':8},
            'username': {'read_only':True}
        }


    # code单独校验的局部钩子(不好写,因为手机号不好取)
    def validate(self, attrs):
        telephone = attrs.get('telephone')
        code = attrs.get('code')
        # 取出原来的code
        cache_code = cache.get(settings.PHONE_CACHE_KEY % telephone)
        if code == cache_code or code=='1234':
            # 验证码通过
            if re.match('^1[3-9][0-9]{9}$', telephone):
                attrs['username']=telephone  # 把用户的名字设成手机号
                attrs.pop('code')
                return attrs
            else:
                raise ValidationError('手机号不合法')
        else:
            raise ValidationError('验证码错误')


    # 重写create方法
    def create(self, validated_data):
        user=models.User.objects.create_user(**validated_data)
        return user
luffyapi/apps/user/serializer.py
from rest_framework.throttling import SimpleRateThrottle


class SMSThrotting(SimpleRateThrottle):
    scope = 'sms'
    def get_cache_key(self, request, view):
        telephone = request.query_params.get('telephone')
        #'throttle_%(scope)s_%(ident)s'%{}
        return self.cache_format%{'scope':self.scope,'ident':telephone}
luffyapi/apps/user/throttlings.py
from django.urls import path, include
from . import views
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('', views.LoginView, 'login')
router.register('', views.SendSmSView, 'send')
router.register('register', views.RegisterView, 'register')  # /user/register   post请求就是新增
urlpatterns = [
    path('', include(router.urls)),


]
luffyapi/apps/user/urls.py
from django.shortcuts import render

# Create your views here.
from rest_framework.viewsets import ViewSet,GenericViewSet
from rest_framework.mixins import CreateModelMixin
from . import serializer
from luffyapi.utils.response import APIResponse
from rest_framework.decorators import action
from . import models


class LoginView(ViewSet):
    @action(methods=('post',), detail=False)
    def login(self, request, *args, **kwargs):
        ser = serializer.UserSerilaizer(data=request.data)
        if ser.is_valid():
            token = ser.context['token']
            # ser.context['user'] 是user对象
            username = ser.context['user'].username
            # {'code':1
            #  msg:'chengg'
            #  token:'sdfasdf'
            #  username:'root'
            #  }
            return APIResponse(token=token, username=username)
        else:
            return APIResponse(code=0, msg=ser.errors)

    @action(detail=False)
    def check_telephone(self, request, *args, **kwargs):
        import re
        telephone = request.query_params.get('telephone')
        if not re.match('^1[3-9][0-9]{9}', telephone):
            return APIResponse(code=0, msg='手机号不合法')
        try:
            models.User.objects.get(telephone=telephone)
            return APIResponse(code=1)
        except:
            return APIResponse(code=0,msg='手机号不存在')

    @action(methods=['POST'],detail=False)
    def code_login(self,request,*args,**kwargs):
        ser = serializer.CodeUserSerilaizer(data=request.data)
        if ser.is_valid():
            token = ser.context['token']
            username = ser.context['user'].username
            return APIResponse(token=token, username=username)
        else:
            return APIResponse(code=0,msg=ser.errors)



from .throttlings import SMSThrotting
class SendSmSView(ViewSet):
    throttle_classes = [SMSThrotting,]
    @action(methods=['GET'], detail=False)
    def send(self,request,*args,**kwargs):
        '''
        发送验证码接口
        :return:
        '''
        import re
        from luffyapi.libs.tx_sms import get_code,send_message
        from django.core.cache import cache
        from django.conf import settings
        telephone = request.query_params.get('telephone')
        if not re.match('^1[3-9][0-9]{9}', telephone):
            return APIResponse(code=0, msg='手机号不合法')
        code=get_code()
        result=send_message(telephone,code)
        # 验证码保存(保存到哪?)
        # sms_cache_%s
        cache.set(settings.PHONE_CACHE_KEY%telephone,code,180)
        if result:
            return APIResponse(code=1,msg='验证码发送成功')
        else:
            return APIResponse(code=0, msg='验证码发送失败')


class RegisterView(GenericViewSet,CreateModelMixin):
    queryset = models.User.objects.all()
    serializer_class = serializer.UserRegisterSerilaizer

    # def create(self, request, *args, **kwargs):
    #     ser=self.get_serializer(data=request.data)
    #     if ser.is_valid():
    #         ser.save()
    #         return APIResponse(code=1,msg='注册成功',username=ser.data.get('username'))
    #     else:
    #         return APIResponse(code=0, msg=ser.errors)
    #


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

.......

from .pay import alipay,gateway
luffyapi/libs/al_pay/__init__.py
from alipay import AliPay

from . import setting


alipay = AliPay(
    appid=setting.APPID,
    app_notify_url=None,  # the default notify path
    app_private_key_string=setting.APP_PRIVATE_KEY_STRING,
    # alipay public key, do not use your own public key!
    alipay_public_key_string=setting.ALIPAY_PUBLIC_KEY_STRING,
    sign_type=setting.SIGN_TYPE, # RSA or RSA2
    debug=setting.DEBUG  # False by default
)


gateway=setting.GATEWAY
luffyapi/libs/al_pay/pay.py
import os
APPID="2016092000554611"

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','al_public_key.pem')).read()

SIGN_TYPE='RSA2'
DEBUG=True


GATEWAY='https://openapi.alipaydev.com/gateway.do?' if DEBUG else 'https://openapi.alipay.com/gateway.do?'
luffyapi/libs/al_pay/setting.py
from .send import  get_code,send_message
luffyapi/libs/tx_sms/__init__.py
from qcloudsms_py import SmsSingleSender
from luffyapi.utils.logger import log
from . import settings


# 生成一个四位随机验证码
def get_code():
    import random
    s_code=''
    for i in range(4):
        s_code+=str(random.randint(0,9))
    return s_code


def send_message(phone,code):

    ssender = SmsSingleSender(settings.appid, settings.appkey)
    params = [code, '3']  # 当模板没有参数时,`params = []`
    try:
        result = ssender.send_with_param(86, phone, settings.template_id, params, sign=settings.sms_sign, extend="", ext="")
        if result.get('result') == 0:
            return True
        else:
            return False
    except Exception as e:
        log.error('手机号:%s,短信发送失败,错误为:%s'%(phone,str(e)))
luffyapi/libs/tx_sms/send.py
# 短信的配置
# 短信应用 SDK AppID
appid = 1400397846  # SDK AppID 以1400开头
# 短信应用 SDK AppKey
appkey = "fd972f6d5a15add46de47b50b8dbe930"
# 短信模板ID,需要在短信控制台中申请
template_id = 661935  # NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请
# 签名
sms_sign = "小猿取经"  # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请
luffyapi/libs/tx_sms/settings.py

.......

# 首页轮播图个数
BANNER_COUNTER=3


# 手机验证码缓存的key值
PHONE_CACHE_KEY='sms_cache_%s'
luffyapi/settings/const.py
# 开发阶段配置文件




from .const import *


import os

#现在BASE_DIR内部的小的luffyapi
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 把这个路径加入到环境变量
import sys
# print(sys.path)
sys.path.insert(0,BASE_DIR)
#把apps的路径加入到环境变量
sys.path.insert(1,os.path.join(BASE_DIR,'apps'))
# print(sys.path)


SECRET_KEY = 'm&7ghd_pbvje4^d7&fc+kigvlbg!*q*#&ubi^9yt$$m3^r3k)2'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'rest_framework',
    'corsheaders',
    # xadmin主体模块
    'xadmin',
    # 渲染表格模块
    'crispy_forms',
    # 为模型通过版本控制,可以回滚数据
    'reversion',
    'django_filters',

    'user',#因为apps目录已经被加到环境变量了,所以直接能找到
    'home',
    'course',
    'order',

]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # 处理跨域
    'corsheaders.middleware.CorsMiddleware',
    # 'luffyapi.utils.middle.MyMiddle'
]

from corsheaders.middleware import CorsMiddleware
ROOT_URLCONF = 'luffyapi.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'luffyapi.wsgi.application'


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'luffyapi',
        'USER':'luffyapi',
        'PASSWORD':'Luffy123?',
        'HOST':'127.0.0.1',
        'PORT':3306

    },
}
import pymysql
pymysql.install_as_MySQLdb()


from django.contrib import auth


AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]



LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'

MEDIA_URL='/media/'
MEDIA_ROOT=os.path.join(BASE_DIR,'media')  #现在的BASEDIR是luffyapi下的luffyapi


AUTH_USER_MODEL='user.user'

REST_FRAMEWORK={
    'EXCEPTION_HANDLER': 'luffyapi.utils.exceptions.common_exception_handler',
}



#日志的配置
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            # 实际开发建议使用WARNING
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
            # 实际开发建议使用ERROR
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            # 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi
            'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"),
            # 日志文件的最大值,这里我们设置300M
            'maxBytes': 300 * 1024 * 1024,
            # 日志文件的数量,设置最大日志数量为10
            'backupCount': 100,
            # 日志格式:详细格式
            'formatter': 'verbose',
            # 文件内容编码
            'encoding': 'utf-8'
        },
    },
    # 日志对象
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统
        },
    }
}


CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)
CORS_ALLOW_HEADERS = (
    'authorization',
    'content-type',
)



import datetime
JWT_AUTH = {
    # 过期时间7天
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),

}
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES':{
        'sms':'1/m'  # key要跟类中的scop对应
    }
}


# django默认不支持redis做缓存
# from django.core.cache.backends.filebased import FileBasedCache
# from django_redis.cache import RedisCache
# 缓存
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100}
            # "PASSWORD": "123",
        }
    }
}


# 上线后必须换成公网地址
# 后台基URL
BASE_URL = 'http://127.0.0.1:8000'
# 前台基URL
LUFFY_URL = 'http://127.0.0.1:8080'
# 支付宝同步异步回调接口配置
# 后台异步回调接口
NOTIFY_URL = BASE_URL + "/order/success/"
# 前台同步回调接口,没有 / 结尾
RETURN_URL = LUFFY_URL + "/pay/success"
luffyapi/settings/dev.py
#上线的配置文件

# 开发阶段配置文件




from .const import *


import os

#现在BASE_DIR内部的小的luffyapi
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 把这个路径加入到环境变量
import sys
# print(sys.path)
sys.path.insert(0,BASE_DIR)
#把apps的路径加入到环境变量
sys.path.insert(1,os.path.join(BASE_DIR,'apps'))
# print(sys.path)


SECRET_KEY = 'm&7ghd_pbvje4^d7&fc+kigvlbg!*q*#&ubi^9yt$$m3^r3k)2'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

# 运行的host,服务端的地址,买的服务器的公网ip
ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'rest_framework',
    'corsheaders',
    # xadmin主体模块
    'xadmin',
    # 渲染表格模块
    'crispy_forms',
    # 为模型通过版本控制,可以回滚数据
    'reversion',
    'django_filters',

    'user',#因为apps目录已经被加到环境变量了,所以直接能找到
    'home',
    'course',
    'order',

]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # 处理跨域
    'corsheaders.middleware.CorsMiddleware',
    # 'luffyapi.utils.middle.MyMiddle'
]

from corsheaders.middleware import CorsMiddleware
ROOT_URLCONF = 'luffyapi.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'luffyapi.wsgi.application'


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'luffyapi',
        'USER':'luffyapi',
        'PASSWORD':'Luffy123?',
        'HOST':'127.0.0.1',
        'PORT':3306

    },
}
import pymysql
pymysql.install_as_MySQLdb()


from django.contrib import auth


AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]



LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'

MEDIA_URL='/media/'
MEDIA_ROOT=os.path.join(BASE_DIR,'media')  #现在的BASEDIR是luffyapi下的luffyapi


AUTH_USER_MODEL='user.user'

REST_FRAMEWORK={
    'EXCEPTION_HANDLER': 'luffyapi.utils.exceptions.common_exception_handler',
}



#日志的配置
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            # 实际开发建议使用WARNING
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
            # 实际开发建议使用ERROR
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            # 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi
            'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"),
            # 日志文件的最大值,这里我们设置300M
            'maxBytes': 300 * 1024 * 1024,
            # 日志文件的数量,设置最大日志数量为10
            'backupCount': 100,
            # 日志格式:详细格式
            'formatter': 'verbose',
            # 文件内容编码
            'encoding': 'utf-8'
        },
    },
    # 日志对象
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统
        },
    }
}


CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)
CORS_ALLOW_HEADERS = (
    'authorization',
    'content-type',
)



import datetime
JWT_AUTH = {
    # 过期时间7天
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),

}
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES':{
        'sms':'1/m'  # key要跟类中的scop对应
    }
}


# django默认不支持redis做缓存
# from django.core.cache.backends.filebased import FileBasedCache
# from django_redis.cache import RedisCache
# 缓存
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100}
            # "PASSWORD": "123",
        }
    }
}


# 上线后必须换成公网地址
# 后台基URL
BASE_URL = 'http://47.103.156.13:8000'
# 前台基URL
LUFFY_URL = 'http://47.103.156.13'
# 支付宝同步异步回调接口配置
# 后台异步回调接口
NOTIFY_URL = BASE_URL + "/order/success/"
# 前台同步回调接口,没有 / 结尾
RETURN_URL = LUFFY_URL + "/pay/success"
luffyapi/settings/pro.py

.......

#方法,加日志

from rest_framework.views import exception_handler
# from luffyapi.utils import response
from .response import APIResponse
from .logger import log
def common_exception_handler(exc, context):
    log.error('view是:%s ,错误是%s'%(context['view'].__class__.__name__,str(exc)))
    #context['view']  是TextView的对象,想拿出这个对象对应的类名
    print(context['view'].__class__.__name__)
    ret=exception_handler(exc, context)  # 是Response对象,它内部有个data

    if not ret:  #drf内置处理不了,丢给django 的,我们自己来处理
        # 好多逻辑,更具体的捕获异常
        if isinstance(exc,KeyError):
            return APIResponse(code=0, msg='key error')

        return APIResponse(code=0,msg='error',result=str(exc))
    else:
        return APIResponse(code=0,msg='error',result=ret.data)
luffyapi/utils/exceptions.py
import logging
# log=logging.getLogger('名字') # 跟配置文件中loggers日志对象下的名字对应
log=logging.getLogger('django')
luffyapi/utils/logger.py
from django.middleware.csrf import CsrfViewMiddleware
from django.utils.deprecation import MiddlewareMixin


class MyMiddle(MiddlewareMixin):
    def process_response(self, request, response):
        response['Access-Control-Allow-Origin'] = '*'
        if request.method == "OPTIONS":
            # 可以加*
            response["Access-Control-Allow-Headers"] = "Content-Type"
            response["Access-Control-Allow-Headers"] = "authorization"

        return response
luffyapi/utils/middle.py
from django.db import models
# 后期课程表,轮播图表,都会用到这些字段

class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')
    is_show = models.BooleanField(default=True, verbose_name='是否展示')
    # 修改成这样
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_time  = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
    orders = models.IntegerField()
    class Meta:
        abstract=True  # 一定不要忘了
luffyapi/utils/models.py
from rest_framework.response import  Response


class APIResponse(Response):
    def __init__(self,code=1,msg='成功',result=None,status=None,headers=None,content_type=None,**kwargs):
        dic={
            'code':code,
            'msg':msg

             }
        if result:
            dic['result']=result
        dic.update(kwargs)

        #对象来调用对象的绑定方法,会自动传值
        super().__init__(data=dic,status=status,headers=headers,content_type=content_type)

        # 类来调用对象的绑定方法,这个方法就是一个普通函数,有几个参数就要传几个参数
        # Response.__init__(data=dic,status=status,headers=headers,content_type=content_type)
luffyapi/utils/response.py

.......

from django.contrib import admin
from django.urls import path, re_path, include
from django.views.static import serve
from django.conf import settings

# xadmin的依赖
import xadmin

xadmin.autodiscover()
# xversion模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion

xversion.register_models()

urlpatterns = [
    path('xadmin/', xadmin.site.urls),
    path('home/', include('home.urls')),
    path('user/', include('user.urls')),
    path('course/', include('course.urls')),
    path('order/', include('order.urls')),

    # media文件夹路径打开了
    re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),

]
luffyapi/urls.py
"""
WSGI config for luffyapi project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.pro')

application = get_wsgi_application()
luffyapi/wsgi.py

.......

.idea
logs
scripts
*.pyc
.gitignore
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()
manage.py
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.pro')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()
manage_pro.py
celery==4.4.6
Django==2.0.7
django-cors-headers==3.4.0
django-filter
django-redis
qcloudsms_py
djangorestframework==3.11.0
djangorestframework-jwt==1.11.0
Pillow==7.2.0
PyMySQL==0.9.3
python-alipay-sdk==2.0.1
redis==3.5.3
xadmin @ https://codeload.github.com/sshwsfc/xadmin/zip/django2
requirements.txt

.......

project
    ├── celery_task      # celery包
    │   ├── __init__.py # 包文件
    │   ├── celery.py   # celery连接和配置相关文件,且名字必须叫celery.py
    │   ├── task1.py    # 任务函数
    │   └── task2.py    # 任务函数
    ├── t_celery_add_task.py      # 添加任务
    └── t_get_result.py   # 获取结果
from celery import Celery
broker='redis://127.0.0.1:6379/1'  #broker任务队列
backend='redis://127.0.0.1:6379/2'   # 结构存储,执行完的结果存在这

app=Celery(__name__,broker=broker,backend=backend,include=['celery_task.task1','celery_task.task2'])


# 执行定时任务
# 时区
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.task1.add',
        # 'schedule': timedelta(seconds=3),
        'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
        'args': (300, 150),
    }
}

# 一定要启动beat
# celery beat -A celery_task -l info
celery.py
from .celery import app


@app.task
def add(x,y):
    print(x,y)
    return x+y

# @app.task
# def updata_banner():
#
#     # 在脚本中调用django项目
#     os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled15.settings")
#     import django
#     django.setup()
#
#     from app01 import models
#     from django.core.cache import cache
#     books = models.Book.objects.all()
#     print(books)


# 用命令来执行
# 非windows
# 命令:celery worker -A celery_task -l info
# windows:
# pip3 install eventlet
# celery worker -A celery_task -l info -P eventlet
task1.py
from .celery import app


@app.task
def mutile(x,y):
    print(x,y)
    return x*y
task2.py
from celery_task.task1 import add
from celery_task.task2 import mutile


# ret=add(6,7)
# print(ret)

# # 提交任务
# ret=add.delay(6,7)
# print(ret)  # 2d4ad592-9548-4c7c-8df4-7f8583e8a1b1
#
# ret=mutile.delay(9,9)
# print(ret)


# 执行延迟任务

# 添加延迟任务
from datetime import datetime, timedelta

# print(datetime.utcnow())
# print(type(timedelta(seconds=10)))
# eta=datetime.utcnow() + timedelta(days=10)
# print(eta)
# print(type(eta))
#
#
# # 需要utc时间
# eta=datetime.utcnow() + timedelta(seconds=10)
# ret=add.apply_async(args=(240, 50), eta=eta)
# print(ret)
t_celery_add_task.py
from celery_task.celery import app

from celery.result import AsyncResult

id = '01e57ace-03b2-483b-9056-8e7289537e07'
if __name__ == '__main__':
    async_result=AsyncResult(id=id, app=app)
    if async_result.successful():
        result = async_result.get()
        print(result)
    elif async_result.failed():
        print('任务失败')
    elif async_result.status == 'PENDING':
        print('任务等待中被执行')
    elif async_result.status == 'RETRY':
        print('任务异常后正在重试')
    elif async_result.status == 'STARTED':
        print('任务已经开始被执行')
t_get_result.py

.......

# # 简单使用
# from redis import Redis
# conn=Redis(host='127.0.0.1', port=6379,decode_responses=True)
# conn.lpush('list1',999)



#redis连接池
import redis
#pool必须是单例的
POOL = redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=100)  # 造一个池子,最多能放100个连接
t_redis_pool.py
# 简单使用
from redis import Redis
# conn=Redis()
#
# conn=Redis.from_url('redis://127.0.0.1:6379/2')  # 为什么要写成类的绑定方法
#连接对象
# conn=Redis(host='127.0.0.1', port=6379,decode_responses=True)
# ret=conn.get('name')
# print(ret)


# #redis连接池
import redis
# #pool必须是单例的
# pool = redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=100)  # 造一个池子,最多能放100个连接
# import redis
#
# #包内的py文件,如果想右键运行,导包的时候不能带点
from t_redis_pool import POOL  # pycharm提示的错
conn = redis.Redis(connection_pool=POOL)  # 只要执行这一句话,就是从池中拿出一个连接
# r = redis.Redis(connection_pool=POOL)  # 只要执行这一句话,就是从池中拿出一个连接
# r = redis.Redis(connection_pool=POOL)  # 只要执行这一句话,就是从池中拿出一个连接
# r = redis.Redis(connection_pool=POOL)  # 只要执行这一句话,就是从池中拿出一个连接
# ret=r.get('name')
# print(ret)


#####字符串操作
####1 set的用法
# conn.set('height',180) #基本使用

# conn.set('height','190',nx=True)
# conn.set('height','190',xx=True)
# conn.set('height1','190',xx=True)
'''
ex,过期时间(秒)
     px,过期时间(毫秒)
     nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
     xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
 
'''

### 2
# setnx(name, value)
#
# 设置值,只有name不存在时,执行设置操作(添加),如果存在,不会修改
# setex(name, value, time)
# # 设置值
# # 参数:
#     # time,过期时间(数字秒 或 timedelta对象)
# psetex(name, time_ms, value)
# # 设置值
# # 参数:
#     # time_ms,过期时间(数字毫秒 或 timedelta对象


# mset
# conn.mset({'name1':'11','name3':'dasfd'})

# ret=conn.mget(['name1','name','name3'])
# print(ret)

# ret=conn.getset('name1', '999')
# print(ret)

# ret=conn.getrange('name1',0,0) # 前闭后闭区间
# print(ret)

# conn.setrange('name1',1,88888)

# ret=conn.getbit('name1',9)
# print(ret)

#incr :统计网站访问量,页面访问量,接口访问量
# conn.incr('name1')  # 只要一执行,数字加1
# conn.incr('name1')  # 只要一执行,数字加1
# conn.incr('name1')  # 只要一执行,数字加1
# conn.incr('name1')  # 只要一执行,数字加1

#decr
# conn.incr('name1',-2)
# conn.decr('name1',3)

# conn.append('name1','oo')
# conn.incr('name1')


#set :很多参数
#get
#mset
#mget
#incr
#decr
#append



# hash操作
# conn.hset('hash1','name','lqz')
# conn.hset('hash1','name2','lqz')
# conn.hset('hash1','name','lqz444')  # key不可以重复,

# ret=conn.hget('hash1','name')  #只能取一个
# print(ret)


# conn.hmset('hash2',{'key1':'value1','key2':'value2'})
# ret=conn.hmget('hash1','name','name2')
# ret=conn.hmget('hash1',['name','name2'])
# print(ret)

# ret=conn.hgetall('hash1')  # 尽量少用
# print(ret)

# ret=conn.hlen('hash1')
# ret=conn.hkeys('hash1')
# ret=conn.hexists('hash1','name1')
# ret=conn.hdel('hash1','name')

# conn.hset('hash1','name',12)
# ret=conn.hincrby('hash1','name')
#
# print(ret)


# 以后想取出hash类型内所有的数据,不建议用hgetall,建议用hscan_iter
# 一次性先取一部分回来(假设有1w条,先取回100条,把这100条做成了生成器)
# ret=conn.hscan_iter('hash1')
# print(ret)
# for i in ret:
#     print(i)


# hset
# hget
#hmset
#hmget
# hincrby
# 区分hgetall和hscan_iter

### 列表操作
# ret=conn.lpush('list1',1,2,3,4,5)
# ret=conn.rpush('list1',999)
# ret=conn.lpushx('list2',1)

# ret=conn.lpushx('list1',888)  # 必须有这个key才能放
# ret=conn.rpushx('list1',666)  # 我们猜,返回总长度
# ret=conn.llen('list1')

# ret=conn.linsert('list1','before','3','77777777')
# ret=conn.linsert('list1','after','3','66666666')



# ret=conn.lset('list1',3,'22222')  #从0开始计数
# ret=conn.lset('list1',0,'11111')

# ret=conn.lrem('list1',2,'5')  # 从前往后删除两个5
# ret=conn.lrem('list1',-1,'5') # 从后往前删除1个5
# ret=conn.lrem('list1',0,'5')   # 删除所有5

# ret=conn.lpop('list1')
# ret=conn.rpop('list1')

# ret=conn.lindex('list1',0)

# ret=conn.lrange('list1',0,2)  # 前闭后闭

# ret=conn.ltrim('list1',1,2)

# 重点block,阻塞,可以写一个超时时间
# ret=conn.blpop('list1',timeout=10)
# print(ret)


# 自定制分批取列表的数据
# conn.lpush('test',*[1,2,3,4,45,5,6,7,7,8,43,5,6,768,89,9,65,4,23,54,6757,8,68])
# conn.flushall()
# def scan_list(name,count=2):
#     index=0
#     while True:
#         data_list=conn.lrange(name,index,count+index-1)
#         if not data_list:
#             return
#         index+=count
#         for item in data_list:
#             yield item
# # print(conn.lrange('test',0,100))
# for item in scan_list('test',5):
#     print('---')
#     print(item)



# 管道。实现事务
# import redis
# pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
#
# conn = redis.Redis(connection_pool=pool)
#
# # pipe = r.pipeline(transaction=False)
# pipe = conn.pipeline(transaction=True)
# pipe.multi()
# pipe.set('name', 'alex')
#
# pipe.set('role', 'sb')
#
# pipe.execute()  # 这句话,才真正的去执行

# 其他操作
# conn.delete('list1')
# ret=conn.delete('hash1')

# ret=conn.exists('hash2')
# ret=conn.keys('cache*')  #查询以cache开头的所有key

# ret=conn.expire('hash2',2)

# ret=conn.type('name3')
# ret=conn.type('test')
# ret=conn.type('test')
# print(ret)
t_redis_conn.py
############################################################################
# ValueError: not enough values to unpack (expected 3, got 0)
#
# 可使用celery -A celery_task worker --pool=solo -l info 解决

# 从4.3.0到4.4.0的Kombu更新停止了对redis-py v2.10.6的支持,因此迫使我们升级redis-py版本。
#
# 所以我们要修改自己的requeirement.txt文件中的kombu的版本了
#
# # kombu版本
# pip install kombu==4.2.1
# # 如果你也使用了celery
# pip install celery==4.1.1
# pip install redis ==2.10.5
# python3.5
############################################################################
版本
from alipay import AliPay


# app_private_key_string = open("/path/to/your/private/key.pem").read()
# alipay_public_key_string = open("/path/to/alipay/public/key.pem").read()

app_private_key_string = """-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEApkFnp38PSslhn4wvRarL40+WtVpan6tVT55FWzAzLfQ8PfqZE2qW7mCJy1/zM07+S5L+X0pC+F7BlT+0z4rlxraG5D23khWfHb4x+uDi0Wd94eaVB0PzQXCYYBChuORvNN5YLTspeP7rNQ2OcdpGx2KUzo9w59dVW+AweblFl7GqPkRSC0P5XbkJtwnEAd0Nc08HzJR0OWZT9HRFOA7coaude9uSwDsXmsdkSNGsGDcaGEY6aGk2Tj9Fyni9LJJcOXwGrUuGDpdcP2tYePrFUGMDml/feFR9QGUbExWh/ZNpb9lKLwOtXOhQj9slilp+YJek/rLlSgW9K1WrYpuuBwIDAQABAoIBADN1mRzKAjS2wmW84UDiDbus/cviTJyRTpWXOoZwE9dMen0AnPLakh70eJIff8pI0AMaW2upM7NmuOp2ToPSzS5FftkUlUY9NQPiw9uQUgRY0Sjj0wrtqFSAAlnxq+zrn9QwYgCWCE8wMCM6r/Vjh3bdd4u78EmCaCRI7xguFXFPB9NY9oCFiwsTFJtXPZo+DSIQjqIDnh8YtV3NzrA3Ln3DsKMAr+vnPMeljAL5US6Rt5tOQY0qa9bUi6RqQwTLADcANd49YOvlr7OLjwi5jkeJ16j1nKgpasAIg9V+rF085u+PEKkLL3WJHwmhiIxrA11bxCnm1fRdfHbyWZKY9yECgYEA0k3wS7A7rzhtpznc5ZfVGWIEQ5IahRkqVGbc6wFIXKXnypOtGKAFiRmEPwm9xRurlo8TJ1bjK4D4ljEANJhX2NnIZfK3YDdNEN6D4GnEvPFR641M8M47Z7ItvdRKre8P2/ThXhkMnq5/1y7e87xvE6/a1Tr44+hclHvQWPRrsPcCgYEAymFEy0/2xAX+si4S8pzrgLfBg3Ehjan8T6JZZNCKhR7b/IT+3/AWJimzEL6jYQMlhy7QzZbRb1ssIIzqPFcFQYjQE0n6fswJuN4+jayW9Jtjvar/zhHFjW3EODR5yEDTvS3CHqQLeG3ce+srMJSxVDJR2V9ortFeb+VPbWZ+t3ECgYEAxPYvvoNwcpuzvvGnW/RGpb4x5iL46Xz3MxMfho2t+u961jRW4oBEjvGx9OQnsmpG2vxm4Oo0WnMw3mFIIvonFDZrxGd8rQU+DTWJZ21Hz/lnUugEjmdoJacvxeEEjEAgp02CoQFu21Ls8li4gKgTk+mYVyojHjhqNLp9GELadWMCgYEAi0RSXgLCEnT5p03zdgcsPOC3ByfT6jO+0GItWCX2HNN2mRhAeIQ0CcEKW4yEy56ptZQu1jtiFlpMTH4MNse/czCd15hCC/2G9zPhIgdRvjQsd/nznLA4HTIbJH5gC8EotHeHrSRATHh1kMTtbLn2KbWTA54XYK3tad0IQoWUz9ECgYBBc+kCJsb0shaRIRP5nmeb5tZCH7Yq9DgriwcIlK74cxW9fvL94n91Y6M8vSZ6MC7vF5wmcXMj3K1of04qizKbbKxCCYeOIHhTh0dB+ualDxu5WBG/6mLWR3HVHazqRVri9qoZ/3zNDx5CRNi81VpzOPmVtuos5d9sZZaNR3lkRQ==
-----END RSA PRIVATE KEY-----
"""

alipay_public_key_string = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwU9mMZHHlQPE9FcxVtXOXhbWCtuDZLJRVCiofdbTVmRXrx47yGniPehwcKIsqhzhEaXBG2QhpIZUL8YsCav0mkrppoRvWOytuGyxNRESo8I6DWRs0aCq6P3AuiD9kSXET4dpAuRYT/+JrMXIZTycEts6vYYNAT9QivXJoa2FmiCQBAL3HP7F36pby9VstObilxXQcoBBJwEYGf2TK6moFFZ1dkloRr5Cfk/G82DpuVfrt1gr4OuIDWtcE3MZTrvDgTqtkRuwGF76FY3+8xUCUbJs1dL5cXYN7/b3jPcXVcdKXFj4WrOQd42ofE1BJWMxBW7L3Qlxue1vy+NGx/CuKwIDAQAB
-----END PUBLIC KEY-----
"""

alipay = AliPay(
    appid="2016092000554611",
    app_notify_url='http://127.0.0.1:8000/home/',  # the default notify path
    app_private_key_string=app_private_key_string,
    # alipay public key, do not use your own public key!
    alipay_public_key_string=alipay_public_key_string,
    sign_type="RSA2", # RSA or RSA2
    debug=True  # False by default
)


alipay_url='https://openapi.alipaydev.com/gateway.do?'
order_string = alipay.api_alipay_trade_page_pay    (
    out_trade_no="20161s112www4334",
    total_amount=9999,
    subject='笔记本电脑',
    return_url="https://www.luffycity.com/free-course",
    notify_url="https://www.luffycity.com/free-course"
)
print(alipay_url+order_string)
t_alpay.py
# 短信应用 SDK AppID
appid = 1400397846  # SDK AppID 以1400开头
# 短信应用 SDK AppKey
appkey = "fd972f6d5a15add46de47b50b8dbe930"
# 需要发送短信的手机号码
ll=['17681790058','18701244561']
# 短信模板ID,需要在短信控制台中申请
template_id = 661935  # NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请
# 签名
sms_sign = "小猿取经"  # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请



from qcloudsms_py import SmsSingleSender
from qcloudsms_py.httpclient import HTTPError
ssender = SmsSingleSender(appid, appkey)
params = ["1234",'3']  # 当模板没有参数时,`params = []`
try:
  result = ssender.send_with_param(86, ll[0],template_id, params, sign=sms_sign, extend="", ext="")
  result = ssender.send_with_param(86, ll[1],template_id, params, sign=sms_sign, extend="", ext="")
  if result.get('result')==0:
      pass

except HTTPError as e:
  print(e)
except Exception as e:
  print(e)
t_sms.py

 

 

 

 

 

 

posted @ 2020-10-03 02:43  silencio。  阅读(104)  评论(0编辑  收藏  举报