luffcc项目-06-使用容联云发送短信、Celery

一、使用容联云发送短信

https://www.yuntongxun.com/
目录结构

sms.py

import json
from .SmsSDK import SmsSDK
from django.conf import settings

accId = settings.SMS_INFO.get('ACCID')
accToken = settings.SMS_INFO.get('ACCTOKEN')
appId = settings.SMS_INFO.get('APPID')


def send_message(tid, mobile, datas):
    sdk = SmsSDK(accId, accToken, appId)
    """ 
    tid = '1'
    mobile = '18899241027'
    datas = ('1234', '3')
    """
    resp = sdk.sendMessage(tid, mobile, datas)
    resp = json.loads(resp)
    print(resp)
    return resp.get('statusCode') == '000000'


# send_message()

dev.py

SMS_INFO = {
    'ACCID': '8aaf0708754a3ef2017563ddb22d0773',
    'ACCTOKEN': '0b41612bc8a8429d84b5d37f29178743',
    'APPID': '8aaf0708754a3ef2017563ddb3110779',
    'TID': 1,
}

users/views.py

import random
import re

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
# Create your views here.
from rest_framework_jwt.views import ObtainJSONWebToken
from rest_framework import status
from users.serializers import CustomeSerializer, RegisterModelSerializer
from .utils import get_user_obj
from . import models
from lyapi.settings import contains

from lyapi.libs.ronglian_sms_sdk.sms import send_message
from django.conf import settings


class CustomLoginView(ObtainJSONWebToken):
    serializer_class = CustomeSerializer

#
class CheckPhoneNumber(APIView):

    def get(self,request):
        phone_number = request.GET.get('phone')
        if not re.match('^1[3-9][0-9]{9}$', phone_number):
            # 格式不对
            return Response({'error_msg':'手机号格式有误,请重新输入!'}, status=status.HTTP_400_BAD_REQUEST)

        # 验证唯一性
        ret = get_user_obj(phone_number)
        if ret:
            return Response({'error_msg': '手机号已被注册,请换手机号'}, status=status.HTTP_400_BAD_REQUEST)

        return Response({'msg': 'ok'})


class RegisterView(CreateAPIView):
    queryset = models.User.objects.all()
    serializer_class = RegisterModelSerializer

import logging
logger = logging.getLogger('django')
from django_redis import get_redis_connection


class GetSMSCodeView(APIView):

    def get(self,request,phone):
        # 验证是否已经发送过短信了
        conn = get_redis_connection('sms_code')
        ret = conn.get('mobile_interval_%s'%phone)

        if ret:
            return Response({'msg':'60秒内已经发送过了,别瞎搞'}, status=status.HTTP_400_BAD_REQUEST)

        # 生成验证码
        sms_code = "%06d" % random.randint(0,999999)

        # 保存验证码

        conn.setex('mobile_%s'%phone, contains.SMS_CODE_EXPIRE_TIME, sms_code)  # 设置有效期

        conn.setex('mobile_interval_%s'%phone, contains.SMS_CODE_INTERVAL_TIME, sms_code)  # 设置发送短信的时间间隔

        # 发送验证码

        ret = send_message(settings.SMS_INFO.get('TID'), phone, (sms_code, contains.SMS_CODE_EXPIRE_TIME // 60))
        if not ret:
            logger.error('{}手机号短信发送失败'.format(phone))
            return Response({'msg': '短信发送失败,请联系管理员'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


        return Response({'msg':'ok'})

在这里插入图片描述
Register.vue

<template>
	<div class="box">
		<img src="../../static/img/Loginbg.3377d0c.jpg" alt="">
		<div class="register">
      <div class="login-title">
				<img src="../../static/img/Logotitle.1ba5466.png" alt="">
				<p>帮助有志向的年轻人通过努力学习获得体面的工作和生活!</p>
			</div>
			<div class="register_box">
        <div class="register-title">注册路飞学城</div>
				<div class="inp">
					<input v-model = "mobile" type="text" placeholder="手机号码" class="user" @blur="checkPhone">
					<input v-model = "password" type="password" placeholder="密码" class="user">
					<input v-model = "r_password" type="password" placeholder="确认密码" class="user">

					<div>
            <input v-model = "sms" type="text" placeholder="输入验证码" class="user" style="width: 62%">
            <button style="width: 34%;height: 41px;" @click="getSmsCode">{{btn_msg}}</button>
          </div>
					<button class="register_btn" @click="registerHandler">注册</button>
					<p class="go_login" >已有账号 <router-link to="/user/login">直接登录</router-link></p>
				</div>
			</div>
		</div>
	</div>
</template>

<script>
export default {
  name: 'Register',
  data(){
    return {
      sms:"",
      mobile:"",
      password:"",
      r_password:"",
      validateResult:false,
      interval_time: 60,
      btn_msg: '点击获取验证码',
      flag: false,

    }
  },
  created(){
  },
  methods:{
    checkPhone(){
      let phoneNumber = this.mobile;
      // 前端校验格式
      let reg = /^1[3-9][0-9]{9}$/;
      if (!reg.test(phoneNumber)){
        this.$message.error("手机号格式不对");
        return false;
      }
      // 发送请求
      this.$axios.get(`${this.settings.Host}/users/check_phone/?phone=${phoneNumber}`)
        .then((res)=>{
          console.log(res);
        }).catch((error)=>{
          this.$message.error(error.response.data.error_msg);
      })
    },

    registerHandler(){
      this.$axios.post(`${this.$settings.Host}/users/register/`, {
        sms: this.sms,
        phone: this.mobile,
        password: this.password,
        r_password: this.r_password,
      }).then((res)=>{
        sessionStorage.token = res.data.token;
        sessionStorage.username = res.data.username;
        sessionStorage.id = res.data.id;
        this.$router.push('/');
      }).catch((error)=>{
        console.log(error.response);
      })
    },

    // 点击获取验证码
    getSmsCode(){
      this.$axios.get(`${this.$settings.Host}/users/sms_code/${this.mobile}/`)
      .then((res)=>{
        if (!this.flag){
          this.flag = setInterval(()=>{
          if (this.interval_time > 0){
            this.interval_time--;
            this.btn_msg = `${this.interval_time}秒后重新获取`;
          }else {
            this.interval_time = 60;
            this.btn_msg = '点击获取验证码'
            clearInterval(this.flag);
            this.flag = false;
          }
        }, 1000)

        }

      })
      .catch((error)=>{
        this.$message.error(error.response.data.msg);

      })

    }

  },

};
</script>

...

二、Celery

Celery是一个功能完备即插即用的异步任务队列系统。它适用于异步处理问题,当发送邮件、或者文件上传, 图像处理等等一些比较耗时的操作,可将其异步执行,这样用户不需要等待很久,提高用户体验。
文档:http://docs.jinkan.org/docs/celery/getting-started/index.html

1.Celery的特点是:
  • 简单,易于使用和维护,有丰富的文档。
  • 高效,单个celery进程每分钟可以处理数百万个任务。
  • 灵活,celery中几乎每个部分都可以自定义扩展。
2.Celery的架构

Celery的架构由三部分组成,消息队列(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。

一个celery系统可以包含很多的worker和broker

Celery本身不提供消息队列功能,但是可以很方便地和第三方提供的消息中间件进行集成,包括RabbitMQ,Redis,MongoDB等
3.安装
pip install -U celery  #-U是update的意思,有就进行更新,没有就安装
#后面单独将celery运行起来就可以了

也可从官方直接下载安装包:https://pypi.python.org/pypi/celery/

tar xvfz celery-0.0.0.tar.gz
cd celery-0.0.0
python setup.py 
python setup.py install
4.使用

使用celery第一件要做的最为重要的事情是需要先创建一个Celery实例,我们一般叫做celery应用,或者更简单直接叫做一个app。app应用是我们使用celery所有功能的入口,比如创建任务,管理任务等,在使用celery的时候,app必须能够被其他的模块导入。
一般celery任务目录直接放在项目的根目录下即可,路径:

lyapi/
├── mycelery/
    ├── config.py     # 配置文件
    ├── __init__.py   
    ├── main.py       # 主程序
    └── sms/          # 一个目录可以放置多个任务,该目录下存放当前任务执行时需要的模块或依赖,也可以每个任务单独一个目录
        └── tasks.py  # 任务的文件,名称必须是这个!!!

main.py,代码:

from celery import Celery

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

app = Celery()


app.config_from_object('mycelery.config')

app.autodiscover_tasks(['mycelery.sms'])

配置文件config.py代码:

# 任务队列的链接地址(变量名必须叫这个)
broker_url = 'redis://127.0.0.1:6379/14'
# 结果队列的链接地址(变量名必须叫这个)
result_backend = 'redis://127.0.0.1:6379/15'

创建一个任务文件sms/tasks.py,并创建任务,代码:


from mycelery.main import app

from lyapi.libs.ronglian_sms_sdk.sms import send_message
from django.conf import settings
from lyapi.settings import contains

import logging
logger = logging.getLogger('django')


@app.task(name='smsCode')
def sms_codes(phone, sms_code):

    # 发送验证码
    ret = send_message(settings.SMS_INFO.get('TID'), phone, (sms_code, contains.SMS_CODE_EXPIRE_TIME // 60))
    if not ret:
        logger.error('{}手机号短信发送失败'.format(phone))

    return '短信发送成功'


@app.task()
def sms_code2():
    print('xxx2')
    return '发送短信成功2'

接下来,我们运行celery,在终端,项目根目录下(也就是mycelery的外层目录里面)执行指令

celery -A mycelery.main worker --loglevel=info (或者直接写info也行) #-A是指定celery启动入口

运行起来之后,如果又添加了新的任务,需要重新启动celery。
SmsSDK.py

#####################################################################################################
#
#  Copyright (c) 2014 The CCP project authors. All Rights Reserved.
#
#  Use of this source code is governed by a Beijing Speedtong Information Technology Co.,Ltd license
#  that can be found in the LICENSE file in the root of the web site.
#
#   https://www.yuntongxun.com
#
#  An additional intellectual property rights grant can be found
#  in the file PATENTS.  All contributing project authors may
#  be found in the AUTHORS file in the root of the source tree.

from . import algorithm
import requests
import time
import json
import traceback

class SmsSDK:
    """短信SDK"""

    # 容联云通讯服务地址
    url = 'https://app.cloopen.com:8883'
    # 发送短信URI
    sendMessageURI = '/2013-12-26/Accounts/{}/SMS/TemplateSMS'

    def __init__(self, accId, accToken, appId):
        self.__accId = accId
        self.__accToken = accToken
        self.__appId = appId

    def sendMessage(self, tid: str, mobile: str, datas: tuple) -> str:
        """发送短信
        Args:
            tid: 短信模板ID,容联云通讯网站自行创建
            mobile: 下发手机号码,多个号码以英文逗号分隔
            datas: 模板变量
        Returns:
            返回发送结果和发送成功消息ID
            发送成功示例:
            {"statusCode":"000000","templateSMS":{"dateCreated":"20130201155306",
             "smsMessageSid":"ff8080813c373cab013c94b0f0512345"}}
            发送失败示例:
            {"statusCode": "172001", "statusMsg": "网络错误"}
        """
        timestamp = time.strftime('%Y%m%d%H%M%S', time.localtime())
        url = self.__buildSendMessageUrl(timestamp)
        headers = self.__buildHeaders(timestamp)
        body = self.__buildSendMessageBody(tid, mobile, datas)
        self.__logRequestInfo(url, headers, body)
        try:
            r = requests.post(url, headers=headers, data=body, timeout=(2, 5))
            if (r.status_code == requests.codes.ok):
                print('Response body: ', r.text)
                return r.text
            else:
                return json.dumps({'statusCode': str(r.status_code)})
        except:
            traceback.print_exc()
            return '{"statusCode": "172001", "statusMsg": "网络错误"}'

    def __buildSendMessageUrl(self, timestamp):
        """构建发送短信URL"""
        return f'{self.url}{self.sendMessageURI.format(self.__accId)}?sig={self.__buildSign(timestamp)}'

    def __buildSign(self, timestamp):
        """构建签名sig
        Args:
            timestamp: 时间字符串 格式:yyyyMMddHHmmss
        Returns:
            签名大写字符串
        """
        plaintext = f'{self.__accId}{self.__accToken}{timestamp}'
        print("Sign plaintext: ", plaintext)
        return algorithm.md5(plaintext).upper()

    def __buildHeaders(self, timestamp):
        """构建请求报头"""
        headers = {}
        headers['Content-Type'] = 'application/json;charset=utf-8'
        headers['Accept'] = 'application/json'
        headers['Accept-Charset'] = 'UTF-8'
        headers['Authorization'] = self.__buildAuthorization(timestamp)
        return headers

    def __buildAuthorization(self, timestamp):
        """构建报头Authorization
        Args:
            timestamp: 时间字符串 格式:yyyyMMddHHmmss
        Returns:
            Authorization字符串
        """
        plaintext = f'{self.__accId}:{timestamp}'
        print("Authorization plaintext: %s" % plaintext)
        return algorithm.base64Encoder(plaintext)

    def __buildSendMessageBody(self, tid, mobile, datas):
        """构建发送短信报文"""
        body = {}
        body['to'] = mobile
        body['appId'] = self.__appId
        body['templateId'] = tid
        body['datas'] = datas
        return json.dumps(body)

    def __logRequestInfo(self, url, headers, body):
        """打印请求信息日志"""
        print('Request url: ', url)
        print('Request headers: ', headers)
        print('Request body: ', body)

users/serializers.py

import re

from rest_framework_jwt.serializers import JSONWebTokenSerializer
from rest_framework import serializers
from rest_framework_jwt.compat import get_username_field, PasswordField
from django.utils.translation import ugettext as _
from django.contrib.auth import authenticate, get_user_model
from rest_framework_jwt.settings import api_settings
from . import models
from django.contrib.auth.hashers import make_password
from django_redis import get_redis_connection

from .utils import get_user_obj

User = get_user_model()
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER


class CustomeSerializer(JSONWebTokenSerializer):

    def __init__(self, *args, **kwargs):
        """
        Dynamically add the USERNAME_FIELD to self.fields.
        """
        super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)

        self.fields[self.username_field] = serializers.CharField()
        self.fields['password'] = PasswordField(write_only=True)
        self.fields['ticket'] = serializers.CharField(write_only=True)
        self.fields['randstr'] = serializers.CharField(write_only=True)

    #
    def validate(self, attrs):
        credentials = {
            self.username_field: attrs.get(self.username_field),
            'password': attrs.get('password'),
            'ticket': attrs.get('ticket'),
            'randstr': attrs.get('randstr'),
        }
        # {'username':'root',password:'123'}

        if all(credentials.values()):

            user = authenticate(self.context['request'], **credentials)  # self.context['request']当前请求的request对象

            if user:
                if not user.is_active:
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)

                payload = jwt_payload_handler(user)

                return {
                    'token': jwt_encode_handler(payload),
                    'user': user
                }
            else:
                msg = _('Unable to log in with provided credentials.')
                raise serializers.ValidationError(msg)
        else:
            msg = _('Must include "{username_field}" and "password".')
            msg = msg.format(username_field=self.username_field)
            raise serializers.ValidationError(msg)


class RegisterModelSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(read_only=True)
    sms = serializers.CharField(max_length=6, min_length=4, write_only=True)  # '3333'
    r_password = serializers.CharField(write_only=True)
    token = serializers.CharField(read_only=True)  #

    class Meta:
        model = models.User
        fields = ['id', 'phone', 'password', 'r_password', 'sms', 'token']
        extra_kwargs = {
            'password': {'write_only': True},
        }


    # 校验密码和确认密码
    def validate(self, attrs):
        # 校验手机号
        phone_number = attrs.get('phone')
        sms = attrs.get('sms')
        if not re.match('^1[3-9][0-9]{9}$', phone_number):
            raise serializers.ValidationError('手机号格式不对')

        ret = get_user_obj(phone_number)
        if ret:
            raise serializers.ValidationError('has one!!!')
        p1 = attrs.get('password')
        p2 = attrs.get('r_password')
        if p1 != p2:
            raise serializers.ValidationError('两次密码不一致,请核对')

    #  校验验证码
        conn = get_redis_connection('sms_code')
        ret = conn.get('mobile_%s'%phone_number)
        if not ret:
            raise serializers.ValidationError('验证码已失效')
        if ret.decode() != sms:
            raise serializers.ValidationError('验证码错误')

        return attrs

    def create(self, validated_data):

        validated_data.pop('r_password')
        validated_data.pop('sms')
        # 密码加密
        hash_password = make_password(validated_data['password'])
        validated_data['password'] = hash_password

        validated_data['username'] = validated_data.get('phone')

        user = models.User.objects.create(
            **validated_data
        )

        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

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

        user.token = token

        return user

users/views.py

import random
import re

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
# Create your views here.
from rest_framework_jwt.views import ObtainJSONWebToken
from rest_framework import status
from users.serializers import CustomeSerializer, RegisterModelSerializer
from .utils import get_user_obj
from . import models
from lyapi.settings import contains

from lyapi.libs.ronglian_sms_sdk.sms import send_message
from django.conf import settings


class CustomLoginView(ObtainJSONWebToken):
    serializer_class = CustomeSerializer

#
class CheckPhoneNumber(APIView):

    def get(self,request):
        phone_number = request.GET.get('phone')
        if not re.match('^1[3-9][0-9]{9}$', phone_number):
            # 格式不对
            return Response({'error_msg':'手机号格式有误,请重新输入!'}, status=status.HTTP_400_BAD_REQUEST)

        # 验证唯一性
        ret = get_user_obj(phone_number)
        if ret:
            return Response({'error_msg': '手机号已被注册,请换手机号'}, status=status.HTTP_400_BAD_REQUEST)

        return Response({'msg': 'ok'})


class RegisterView(CreateAPIView):
    queryset = models.User.objects.all()
    serializer_class = RegisterModelSerializer

import logging
logger = logging.getLogger('django')
from django_redis import get_redis_connection


class GetSMSCodeView(APIView):

    def get(self,request,phone):
        # 验证是否已经发送过短信了
        conn = get_redis_connection('sms_code')
        ret = conn.get('mobile_interval_%s'%phone)

        if ret:
            return Response({'msg':'60秒内已经发送过了,别瞎搞'}, status=status.HTTP_400_BAD_REQUEST)

        # 生成验证码
        sms_code = "%06d" % random.randint(0,999999)

        # 保存验证码

        conn.setex('mobile_%s'%phone, contains.SMS_CODE_EXPIRE_TIME, sms_code)  # 设置有效期

        conn.setex('mobile_interval_%s'%phone, contains.SMS_CODE_INTERVAL_TIME, sms_code)  # 设置发送短信的时间间隔

        # 发送验证码

        # ret = send_message(settings.SMS_INFO.get('TID'), phone, (sms_code, contains.SMS_CODE_EXPIRE_TIME // 60))
        # if not ret:
        #     logger.error('{}手机号短信发送失败'.format(phone))
        #     return Response({'msg': '短信发送失败,请联系管理员'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        from mycelery.sms.tasks import sms_codes
        sms_codes.delay(phone, sms_code)

        return Response({'msg': 'ok'})

users/urls.py

from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token
from . import views
from django.urls import path,re_path

urlpatterns = [

    path(r'login/', views.CustomLoginView.as_view()),  #颁发token值的
    path(r'verify/', verify_jwt_token),
    path(r'check_phone/', views.CheckPhoneNumber.as_view()),
    path(r'register/', views.RegisterView.as_view()),
    re_path(r'sms_code/(?P<phone>1[3-9][0-9]{9})/', views.GetSMSCodeView.as_view()),

]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Django-Celery是一个用于在Django应用中集成Celery任务队列的插件。它允许你将耗时的任务异步执行,从而提高应用的性能和响应速度。下面是一些使用Django-Celery的步骤: 1. 安装Celery和Django-Celery:首先,确保你已经安装了Celery和Django-Celery。你可以通过运行以下命令来安装它们: ``` pip install celery django-celery ``` 2. 配置Celery:在Django项目的`settings.py`文件中,添加以下配置: ```python # settings.py # 配置Celery Broker(消息代理) BROKER_URL = 'amqp://guest:guest@localhost:5672//' # 配置Celery Backend(结果存储) CELERY_RESULT_BACKEND = 'db+sqlite:///results.sqlite' # 配置Celery Beat(定时任务) CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler' ``` 请根据你的实际情况修改上述配置。 3. 创建Celery App:在你的Django项目中,创建一个名为`celery.py`的文件,并添加以下代码: ```python # celery.py from celery import Celery app = Celery('your_project_name') # 配置Celery app.config_from_object('django.conf:settings', namespace='CELERY') # 自动从Django app中加载任务 app.autodiscover_tasks() ``` 确保将`your_project_name`替换为你的项目名称。 4. 创建任务:在Django app中创建一个任务。例如,你可以在你的app目录下创建一个名为`tasks.py`的文件,并添加以下代码: ```python # tasks.py from celery import shared_task @shared_task def add(x, y): return x + y ``` 这是一个简单的任务示例,将两个数字相加并返回结果。 5. 启动Celery Worker:运行以下命令来启动Celery worker: ``` celery -A your_project_name worker --loglevel=info ``` 确保将`your_project_name`替换为你的项目名称。 6. 调用任务:在你的Django应用程序中,你可以通过导入任务函数并调用它来触发任务的执行。例如: ```python from your_app.tasks import add result = add.delay(1, 2) ``` 这里使用了`delay()`方法来异步调用任务。你也可以使用`apply_async()`方法来更精确地控制任务的执行。 这些是使用Django-Celery的基本步骤。你还可以配置更多高级选项,如任务重试、任务结果存储等。请参考Django-Celery的官方文档以获取更多详细信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值