斑马斑马-16-微信小程序-登录页面

一、简介

小程序注册登录功能

python后台API开发

阿里云验证码发送

二、小程序注册页面

<!--pages/register/register.wxml-->
<view wx:if="{{!success}}">
    <view class='row'>
        <view class='info'>
            <input class='info-input1' bindinput="handleInputPhone" placeholder="请输入你的手机号" />
        </view>
        <button class='button_yam' bindtap='doGetCode' disabled='{{disabled}}' style="background-color:{{color}}">{{text}}</button>
    </view>
    <view class='row'>
        <view class='info'>
            <input class='info-input' bindinput="handleVerificationCode" placeholder="请输入你的验证码" />
        </view>

    </view>
    <view class='row'>
        <view class='info'>
            <input type='password' class='info-input' bindinput="handleNewChanges" placeholder="请输入你的密码" />
        </view>

    </view>
    <view class='row'>
        <view class='info'>
            <input type='password' class='info-input' bindinput="handleNewChangesAgain" placeholder="请重新输入你的密码" />
        </view>

    </view>
    <button class='submit' bindtap='submit'>提交</button>
</view>
<view class='success' wx:if="{{success}}">
    <view class='cheer'>
        <icon type="success" size="24" /> 恭喜您注册成功!</view>
    <button type="default" class='return' bindtap='return_home'>返回首页</button>
</view>
register.wxml
/* pages/register/register.wxss */
page{
  background: #F0F0F0 ;
}
.row{
 margin-top: 20rpx;
 overflow: hidden;
 line-height: 100rpx;
 border-bottom: 1rpx solid #ccc;
 margin-left: 20rpx;
 margin-right: 20rpx;
 color: #777;
 background: #fff;

}
.info-input{
 height: 100rpx;
 margin-left: 50rpx;
 color: #777;
   float: left;
}
.info-input1{
 height: 100rpx;
 margin-left: 50rpx;
 color: #777;
   float: left;
   width: 420rpx;
}
.button_yam{
 width: 200rpx !important;
 height: 70rpx;
 line-height: 70rpx;
 font-size: 28rpx;
 background: #33FF99;
 float: left;
 margin-left: 10rpx;
 margin-top: 15rpx;
 color: #FFFFFF;
 padding: 0;
}
.submit{
 margin-top: 50rpx;
 margin-left: 20rpx;
 margin-right: 20rpx;
 background: #00CCFF;
  color: #FFFFFF;
}
.success{
 background: #ffffff;

}
.cheer{
 text-align: center;
 line-height: 400rpx;
 font-size: 60rpx;
 position: relative;
}
.return{
 margin: 20rpx;

}
 
register.wxss
Page({
 
  /**
   * 页面的初始数据
   */
  data: {
    text: '获取验证码', //按钮文字
    currentTime: 61, //倒计时
    disabled: false, //按钮是否禁用
    phone: '', //获取到的手机栏中的值
    VerificationCode: '',
    Code: '',
    NewChanges: '',
    NewChangesAgain: '',
    success: false,
    state: ''
  },
  /**
    * 获取验证码
    */
  return_home: function (e) {
    wx.navigateTo({
      url: '/pages/login/login',
    })
 
  },
  handleInputPhone: function (e) {
    this.setData({
      phone: e.detail.value
    })
  },
  handleVerificationCode: function (e) {
    console.log(e);
    this.setData({
      Code: e.detail.value
    })
  },
  handleNewChanges: function (e) {
    console.log(e);
    this.setData({
      NewChanges: e.detail.value
    })
  },
  handleNewChangesAgain: function (e) {
    console.log(e);
    this.setData({
      NewChangesAgain: e.detail.value
    })
 
  },
  doGetCode: function () {
    var that = this;
    that.setData({
      disabled: true, //只要点击了按钮就让按钮禁用 (避免正常情况下多次触发定时器事件)
      color: '#ccc',
    })
 
    var phone = that.data.phone;
    var currentTime = that.data.currentTime //把手机号跟倒计时值变例成js值
    var warn = null; //warn为当手机号为空或格式不正确时提示用户的文字,默认为空
    var phone = that.data.phone;
    var currentTime = that.data.currentTime //把手机号跟倒计时值变例成js值
    var warn = null; //warn为当手机号为空或格式不正确时提示用户的文字,默认为空
    wx.request({
      url: '', //后端判断是否已被注册, 已被注册返回1 ,未被注册返回0
      method: "GET",
      header: {
        'content-type': 'application/x-www-form-urlencoded'
      },
      success: function (res) {
        that.setData({
          state: res.data
        })
        if (phone == '') {
          warn = "号码不能为空";
        } else if (phone.trim().length != 11 || !/^1[3|4|5|6|7|8|9]\d{9}$/.test(phone)) {
          warn = "手机号格式不正确";
        } //手机号已被注册提示信息
         else if (that.data.state == 1) {  //判断是否被注册
           warn = "手机号已被注册";
 
         }
         else {
          wx.request({
            url: '', //填写发送验证码接口
            method: "POST",
            data: {
              coachid: that.data.phone
            },
            header: {
              'content-type': 'application/x-www-form-urlencoded'
            },
            success: function (res) {
              console.log(res.data)
              that.setData({
               VerificationCode: res.data.verifycode
              })
              
 
              //当手机号正确的时候提示用户短信验证码已经发送
              wx.showToast({
                title: '短信验证码已发送',
                icon: 'none',
                duration: 2000
              });
              //设置一分钟的倒计时
              var interval = setInterval(function () {
                currentTime--; //每执行一次让倒计时秒数减一
                that.setData({
                  text: currentTime + 's', //按钮文字变成倒计时对应秒数
 
                })
                //如果当秒数小于等于0时 停止计时器 且按钮文字变成重新发送 且按钮变成可用状态 倒计时的秒数也要恢复成默认秒数 即让获取验证码的按钮恢复到初始化状态只改变按钮文字
                if (currentTime <= 0) {
                  clearInterval(interval)
                  that.setData({
                    text: '重新发送',
                    currentTime: 61,
                    disabled: false,
                    color: '#33FF99'
                  })
                }
              }, 100);
            }
          })
        };
        //判断 当提示错误信息文字不为空 即手机号输入有问题时提示用户错误信息 并且提示完之后一定要让按钮为可用状态 因为点击按钮时设置了只要点击了按钮就让按钮禁用的情况
        if (warn != null) {
          wx.showModal({
            title: '提示',
            content: warn
          })
          that.setData({
            disabled: false,
            color: '#33FF99'
          })
          return;
        }
      }
 
    })
 
  },
  submit: function (e) {
    var that = this
    if (this.data.Code == '') {
      wx.showToast({
        title: '请输入验证码',
        image: '/images/error.png',
        duration: 2000
      })
      return
    } else if (this.data.Code != this.data.VerificationCode) {
      wx.showToast({
        title: '验证码错误',
        image: '/images/error.png',
        duration: 2000
      })
      return
    }
    else if (this.data.NewChanges == '') {
      wx.showToast({
        title: '请输入密码',
        image: '/images/error.png',
        duration: 2000
      })
      return
    } else if (this.data.NewChangesAgain != this.data.NewChanges) {
      wx.showToast({
        title: '两次密码不一致',
        image: '/images/error.png',
        duration: 2000
      })
      return
    } else {
      var that = this
      var phone = that.data.phone;
      wx.request({
        url: getApp().globalData.baseUrl + '/Coachs/insert' ,
        method: "POST",
        data: {
          coachid: phone,
          coachpassword: that.data.NewChanges
        },
        header: {
          "content-type": "application/x-www-form-urlencoded"
        },
        success: function (res) {
          wx.showToast({
            title: '提交成功~',
            icon: 'loading',
            duration: 2000
          })
          console.log(res)
          that.setData({
            success: true
          })
        }
      })
    }
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
 
  }
})
register.js

三、Python后台API

1 先搭一个环境

2 配置环境

  2.1 安装django 1.11.7

  pip install django==1.11.7

  2.2 安装drf

  pip install dajngorestframework  

  2.3 setting文件中注册rest_framework

  INSTALLED_APPS 中添加  'rest_framework'

  2.4 修改WebchatAPI下面的url   

from django.contrib import admin
# from django.urls import path
from django.conf.urls import url,include
urlpatterns = [
    url(r'^api/',include('api.urls',namespace='api') ),
]
WebchatAPI下url

  2.5 修改API下面的url    

from django.conf.urls import url, include
from django.contrib import admin
from API import views

urlpatterns = [
    url(r'^isExist/', views.IsExist.as_view()),
    # url(r'^login/', views.LoginView.as_view()),
]
url.py

  2.6 修改view中逻辑

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response

# Create your views here.


class IsExist(APIView):
    def post(self,request,*args,**kwargs):
        print(request.data)
        return Response({"state":1})
View.py

  2.7 微信端 获取验证码逻辑 

doGetCode: function () {
    var that = this;
    that.setData({
      disabled: true, //只要点击了按钮就让按钮禁用 (避免正常情况下多次触发定时器事件)
      color: '#ccc',
    })
 
    var phone = that.data.phone;
    var currentTime = that.data.currentTime //把手机号跟倒计时值变例成js值
    var warn = null; //warn为当手机号为空或格式不正确时提示用户的文字,默认为空
    var phone = that.data.phone;
    var currentTime = that.data.currentTime //把手机号跟倒计时值变例成js值
    var warn = null; //warn为当手机号为空或格式不正确时提示用户的文字,默认为空
    wx.request({
      url: 'http://127.0.0.1:8000/API/isExist/', //后端判断是否已被注册, 已被注册返回1 ,未被注册返回0
      method: "POST",
      data:{phone:this.data.phone},
      header: {
        'content-type': 'application/x-www-form-urlencoded'
      },
      success: function (res) {
        that.setData({
          state: res.data.state
        })
        if (phone == '') {
          warn = "号码不能为空";
        } else if (phone.trim().length != 11 || !/^1[3|4|5|6|7|8|9]\d{9}$/.test(phone)) {
          warn = "手机号格式不正确";
        } //手机号已被注册提示信息
         else if (that.data.state == 1) {  //判断是否被注册
           warn = "手机号已被注册";
         }
         else {
          wx.request({
            url: '', //填写发送验证码接口
            method: "POST",
            data: {
              coachid: that.data.phone
            },
            header: {
              'content-type': 'application/x-www-form-urlencoded'
            },
            success: function (res) {
              console.log(res.data)
              that.setData({
               VerificationCode: res.data.verifycode
              }) 
              //当手机号正确的时候提示用户短信验证码已经发送
              wx.showToast({
                title: '短信验证码已发送',
                icon: 'none',
                duration: 2000
              });
              //设置一分钟的倒计时
              var interval = setInterval(function () {
                currentTime--; //每执行一次让倒计时秒数减一
                that.setData({
                  text: currentTime + 's', //按钮文字变成倒计时对应秒数
 
                })
                //如果当秒数小于等于0时 停止计时器 且按钮文字变成重新发送 且按钮变成可用状态 倒计时的秒数也要恢复成默认秒数 即让获取验证码的按钮恢复到初始化状态只改变按钮文字
                if (currentTime <= 0) {
                  clearInterval(interval)
                  that.setData({
                    text: '重新发送',
                    currentTime: 61,
                    disabled: false,
                    color: '#33FF99'
                  })
                }
              }, 100);
            }
          })
        };
        //判断 当提示错误信息文字不为空 即手机号输入有问题时提示用户错误信息 并且提示完之后一定要让按钮为可用状态 因为点击按钮时设置了只要点击了按钮就让按钮禁用的情况
        if (warn != null) {
          wx.showModal({
            title: '提示',
            content: warn
          })
          that.setData({
            disabled: false,
            color: '#33FF99'
          })
          return;
        }
      }
 
    })
 
  },
register.js 中 doGetCode

3 期间遇到的问题

  问题1:如果运行过程中  django1.11 启动错误:Generator expression must be parenthesized  

     原因:由于django 1.11版本和python3.7版本不兼容, 2.0版本以后的Django修复了这个问题

  解决方案:

   方法1.找到对应路径下的widgets.py,将逗号删除即可

    

  问题2:微信小程序运行过程中报错

  原因:微信的安全验证 

  1:微信的网络请求api必须是https

  2:在微信小程序后台设置

  

  解决方案:

  勾选 不进行域名校验

  

4 测试

四、对接阿里云

 1:首先你得有一个云服务器,如果没有可以找我

 2:我们再云服务器上开通短信服务,设置验证码模板

3:安装Redis数据库,用于存储验证码,并判断是否过期。我们这里用宝塔安装

4:发送短信代码

  4.1 首先添加一个文件夹就叫Ali,封装一个发送短信的统一接口

  需要安装python的SDK:pip install aliyun-python-sdk-core-v3

  4.2 为了方便管理我们把一些重要的常量,放置在一个yaml文件中

  pip install pyyaml

# 项目的域名IP
#PROJECT_ADDR: http://39.99.213.203:8000

# 媒体文件默认地址
#MEDIA_ADDR: http://39.99.213.203:8000/media/

# mysql连接信息
#Mysql:
#  NAME: ZebraHome
#  USER: szBellMen
#  PASSWORD: Zebra123456
#  HOST: 39.99.213.203
#  PORT: 3306

# redis连接信息
Redis:
  NAME: 0
  HOST: 39.99.213.203
  PORT: 6379
Redis_Celery_BROKER:
  NAME: 1
  HOST: 39.99.213.203
  PORT: 6379
Redis_Celery_RESULT:
  NAME: 2
  HOST: 39.99.213.203
  PORT: 6379

# 连接物联网平台的账户
Aliyun:
  accessKeyId: LTAI4G4naVRo7ry8LYmu2Enm
  accessKeySecret: bQkXA5ENZUyGhuQc3mO4BaM7ys3CR6
  regionId: cn-hangzhou

#消息推送相关
#AppSetting:
#  adminAccount: d4281673-3bc0-45e4-88dc-157c03307c97
#  iosAppKey: 29058022
#  androidAppKey: 29161307
#  regionId: cn-hangzhou

#短信服务相关
SMSSetting:
  adminPhone: 18896527725
  signName: 斑马之家
  templateCode: SMS_186614747
  regionId: cn-hangzhou
  messageType: SmsUp
  queueName: Alicom-Queue-1745978963455427-SmsUp
  endpoint: http://1943695596114318.mns.cn-hangzhou.aliyuncs.com
##语音服务相关
#SCSetting:
#  #主叫号码,必须是已购买的号码。
#  CALLEDSHOWNUMBER: 051068514916
##系统中默认天数
#Days:
#  #注意一旦设置后,告警求助、设备共享、设备消息、用户信息都是默认7天
#  NOTIFY_INFO_DAYS: 7
configuration.yaml

  4.3 同时需要在setting文件设置

# 导入配置文件,获取当前的目录
import os, yaml
yamlPath = os.path.join(BASE_DIR, "conf", "configuration.yaml")
# print(u"yamlPath=%s" % yamlPath)
with open(yamlPath, 'rb') as f:
    yamlResult = yaml.load(f, Loader=yaml.FullLoader)
settings.py

   4.4 创建APIModel主要是基于面向对象思想,用于设置消息模型Model

'''
# 说明:数据模型,用于方法之间数据传递
# 优点:调用时,方便快捷(只需要给相关属性赋值)
#       如果发生变动,可快速统一修改,ps:统一规范消息的生成规则
#       给变量赋值时候做校验,提前规避风险。ps:对字段做非空校验,对字段类型做校验
'''
from WechatAPI.settings import yamlResult
from datetime import datetime
from API.APIEnum import  SMSTemplateCode
import json
import random
import logging
import os
# @Time : 2020-3-27 14:13:27
# @Author : Yango
# @Title : 短信服务发送模型
# @Content : 用于短信,各个业务场景调用阿里云接口时所使用的对象模型
# @Software: PyCharm
class SMSModel:
    ADMINPHONE = yamlResult["SMSSetting"]['adminPhone']
    SIGNNAME = yamlResult["SMSSetting"]['signName']
    TEMPLATECODE = yamlResult["SMSSetting"]['templateCode']
    ACCESSKEYID = yamlResult["Aliyun"]['accessKeyId']

    # 接收短信的手机号码。
    # 必填,string 类型
    # 支持对单个、多个手机号码发送短信,手机号码之间以英文逗号(,)分隔。
    @property
    def phone_numbers(self):
        if hasattr(self, '_SMSModel__phone_numbers'):
            return self.__phone_numbers
        else:
            return self.get_phone_numbers_by_user_list

    @phone_numbers.setter
    def phone_numbers(self, value):
        if value:
            self.__phone_numbers = value
        else:
            print("error:【SMSModel】中(phone_numbers)不能为空!")

    # 阿里云短信签名名称。
    # 必填,string 类型
    # 可在控制台签名管理页面签名名称一列查看,必须是已添加、并通过审核的短信签名。
    @property
    def sign_name(self):
        return self.__sign_name if hasattr(self, '_SMSModel__sign_name') else self.SIGNNAME

    @sign_name.setter
    def sign_name(self, value):
        if value:
            self.__sign_name = value
        else:
            print("error:【SMSModel】中(sign_name)不能为空!")

    # 短信模板ID。
    # 必填,string 类型
    # 可在控制台签名管理页面模板CODE一列查看,必须是已添加、并通过审核的模板。
    @property
    def template_code(self):
        return self.__template_code if hasattr(self, '_SMSModel__template_code') else self.TEMPLATECODE

    @template_code.setter
    def template_code(self, value):
        if value:
            self.__template_code = value
        else:
            print("error:【SMSModel】中(template_code)不能为空!")

    # 主账号AccessKey的ID。
    # 非必填,string 类型
    @property
    def access_key_id(self):
        return self.__access_key_id if hasattr(self, '_SMSModel__access_key_id') else self.ACCESSKEYID

    @access_key_id.setter
    def access_key_id(self, value):
        self.__access_key_id = value

    # 系统规定参数。
    # 非必填,string 类型,取值:SendSms。
    @property
    def action(self):
        return self.__action if hasattr(self, '_SMSModel__action') else "SendSms"

    @action.setter
    def action(self, value):
        self.__action = value

    # 外部流水扩展字段。
    # 非必填,string 类型
    @property
    def out_id(self):
        return self.__out_id if hasattr(self, '_SMSModel__out_id') else ""

    @out_id.setter
    def out_id(self, value):
        self.__out_id = value

    # 上行短信扩展码,无特殊需要此字段的用户请忽略此字段。
    # 非必填,string 类型
    @property
    def sms_up_extend_dode(self):
        return self.__sms_up_extend_dode if hasattr(self, '_SMSModel__sms_up_extend_dode') else ""

    @sms_up_extend_dode.setter
    def sms_up_extend_dode(self, value):
        self.__sms_up_extend_dode = value

    # 短信模板变量对应的实际值,JSON格式。
    # 非必填,json 类型
    # {"code": "1111"}
    @property
    def template_param(self):
        if hasattr(self, '_SMSModel__template_param'):
            return self.__template_param
        elif hasattr(self, '_SMSModel__template_code'):
            # 信息变更验证码               # 修改密码验证码            # 用户注册验证码
            # 登录异常验证码               # 登录确认验证码            # 身份验证验证码
            if self.__template_code == SMSTemplateCode.CHANGEINFO.name \
                    or self.__template_code == SMSTemplateCode.CHANGEINFOCONFIRM.name \
                    or self.__template_code == SMSTemplateCode.CHANGEPASSWORD.name \
                    or self.__template_code == SMSTemplateCode.USERREGISTER.name \
                    or self.__template_code == SMSTemplateCode.LOGINERROR.name \
                    or self.__template_code == SMSTemplateCode.LOGINCONFIRM.name \
                    or self.__template_code == SMSTemplateCode.Authentication.name:
                return self.get_verification_code
            elif self.__template_code == SMSTemplateCode.HELP.name \
                    or self.__template_code == SMSTemplateCode.ALARM.name:
                return self.get_help_alarm_content
        else:
            return '{"code": "7725"}'

    @template_param.setter
    def template_param(self, value):
        self.__template_param = value

    #######业务特有########
    @property
    def user_list(self):
        return self.__user_list if hasattr(self, '_SMSModel__user_list') else []

    @user_list.setter
    def user_list(self, value):
        if isinstance(value, list):
            self.__user_list = value
        else:
            print("error:【SMSModel】中(user_list)输入类型与预设类型不一致")

    @property
    def sms_code(self):
        return self.__sms_code if hasattr(self, '_SMSModel__sms_code') else ""

    @sms_code.setter
    def sms_code(self, value):
        self.__sms_code = value


    @property
    def code_length(self):
        return self.__code_length if hasattr(self, '_SMSModel__code_length') else 4

    @code_length.setter
    def code_length(self, value):
        if isinstance(value, int):
            self.__code_length = value
        else:
            print("error:【SMSModel】中(code_length)输入类型与预设类型不一致")

    @property
    def code_type(self):
        return self.__code_type if hasattr(self, '_SMSModel__code_type') else "only_num"

    @code_type.setter
    def code_type(self, value):
        self.__code_type = value

    ########业务特有########

    # 将列表形式的user_list值,通过','拼接成字符串;如果user_list没有传递值,则默认返回ADMINPHONE(管理员联系方式)
    # returnstring    ps:字符串
    # retrun :用户1电话,用户2电话.'     ps:多个值使用逗号分隔.
    # 注意:电话号码一次最多1000个的限制,调用此方法时候,需要把【user_list】赋值
    @property
    def get_phone_numbers_by_user_list(self):
        return ','.join(self.__user_list) if hasattr(self, '_SMSModel__user_list') else self.ADMINPHONE

    # 获取验证码
    # returnstring    ps:字符串
    # retrun :验证码
    # 注意:调用此方法时候,需要把【template_code】赋值
    @property
    def get_verification_code(self):
        template_code = self.__template_code if hasattr(self, '_SMSModel__template_code') else self.TEMPLATECODE
        if SMSTemplateCode[template_code]:
            tecd = SMSTemplateCode[template_code]
            if tecd and tecd.name == "CHANGEINFO" \
                    or tecd.name == "CHANGEINFOCONFIRM" \
                    or tecd.name == "CHANGEPASSWORD" \
                    or tecd.name == "USERREGISTER" \
                    or tecd.name == "LOGINERROR" \
                    or tecd.name == "LOGINCONFIRM" \
                    or tecd.name == "Authentication":
                str_code = self.make_code
                temp_param = {}
                temp_param.setdefault("code", str_code)
                template_parameters = json.dumps(temp_param)
                self.__template_param = template_parameters
                self.template_code = tecd.value
            return str_code
        else:
            return "7725"



    # 生成随机码,内部调用,
    # returnstring    ps:字符串
    # retrun :验证码
    # 注意:调用此方法时候,需要把【template_code】赋值
    @property
    def make_code(self):
        code_list = []
        if self.code_type == "only_num":
            for i in range(self.code_length):
                code_list.append(str(random.randint(0, 9)))
        else:
            for i in range(self.code_length):
                random_num = random.randint(0, 9)  # 随机生成0-9的数字
                # 利用random.randint()函数生成一个随机整数a,使得65<=a<=90
                # 对应从“A”到“Z”的ASCII码
                a = random.randint(65, 90)
                b = random.randint(97, 122)
                random_uppercase_letter = chr(a)
                random_lowercase_letter = chr(b)
                code_list.append(str(random_num))
                code_list.append(random_uppercase_letter)
                code_list.append(random_lowercase_letter)

        verification_code = ''.join(code_list)
        return verification_code
APIModels

  4.5 同时相应的配套设备枚举类型也得跟上

from enum import Enum
from enum import unique

@unique
class SMSTemplateCode(Enum):
    ######模板类型验证码######
    # 信息变更验证码
    CHANGEINFO = "1"
    # 信息变更确认验证码
    CHANGEINFOCONFIRM = "2"
    # 修改密码验证码
    CHANGEPASSWORD = "3"
    # 用户注册验证码
    USERREGISTER = "SMS_186614747"
    # 登录异常验证码
    LOGINERROR = "4"
    # 登录确认验证码
    LOGINCONFIRM = "5"
    # 身份验证验证码
    Authentication = "6"
    ######模板类型验证码######
    # 设备求助模板
    HELP = "7"
    # 设备告警模板
    ALARM = "8"
APIEnum

  4.6 安装django-redis 用于存储验证码

  pip install django-redis

  4.7 设置settings.py  

# ============配置reids==================
# 配置Redis为Django缓存
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        # 地址
        "LOCATION": "redis://{}:{}/{}".format(yamlResult['Redis']['HOST'], yamlResult['Redis']['PORT'],
                                              yamlResult['Redis']['NAME'], ),

        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100}

        }
    }
}
# 将session缓存在Redis中
# SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# SESSION_CACHE_ALIAS = "default"
# # ============配置reids==================
setting.py

  至此,短信发送功能已经可以实现

五、完善注册功能

注册时候前后台手机格式验证

注册时后台验证是否已经注册

验证验证码是否正确,是否过期 

5.1 创建mysql数据库(WechatAPI),创建用户模型(UserInfo)

     5.2.1  安装 pip install pymysql  

  5.2.2  在项目里的__init__.py文件中中加入以下代码:   

  import pymysql  

  pymysql.install_as_MySQLdb()

  5.2.3  修改配置文件  

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

# # ============配置mysql数据库--开始==================
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': yamlResult['Mysql']['NAME'],
        'USER': yamlResult['Mysql']['USER'],
        'PASSWORD': yamlResult['Mysql']['PASSWORD'],
        'HOST': yamlResult['Mysql']['HOST'],
        'PORT': yamlResult['Mysql']['PORT'],
        'OPTIONS': {'init_command': 'SET default_storage_engine=INNODB;',
                    "init_command": "SET foreign_key_checks = 0;",
                    'charset': 'utf8mb4'}
    }
}
# # ============配置mysql数据库--结束==================
INSTALLED_APPS 中添加API,设置mydql数据库连接

  5.2.4  UserInfo模型

from django.db import models
from django.utils import timezone
import uuid
# Create your models here.
# 用户表
class UserInfo(models.Model):
    id = models.CharField(verbose_name=u'用户ID-用户唯一标识', max_length=50, primary_key=True, auto_created=True,
                          default=uuid.uuid4, editable=False)  # 主键
    user_type = models.CharField(verbose_name=u'用户类型', max_length=20, default=u'1')  # 新增类型,普通/会员
    username = models.CharField(verbose_name=u'用户名', max_length=100)
    id_card = models.CharField(verbose_name=u'身份证', max_length=20)
    phone = models.CharField(verbose_name=u'手机号', max_length=20)
    password = models.CharField(verbose_name=u'密码', max_length=100)
    email = models.EmailField(verbose_name=u'邮箱')
    QQId = models.CharField(verbose_name=u'QQ号id', max_length=50, null=True)
    wechatId = models.CharField(verbose_name=u'微信 账号id', max_length=50, null=True)
    weiboId = models.CharField(verbose_name=u'微博 账号id', max_length=50, null=True)
    create_date = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    # avatar = models.ImageField(upload_to='avatar', default='', verbose_name='头像')

    # 该条记录状态
    status = models.CharField(verbose_name=u'该条记录状态', max_length=10, default=u'1')  # 正常,删除

    def __str__(self):
        return self.id

    class Meta:
        db_table = 'UserInfo'
models

  5.2.5  生成数据表 

  python manage.py makemigrations
  python manage.py migrate

至此,要保证数据库中生成相应的实体模型

5.2 业务层逻辑验证 与 手机前端验证

  5.2.1 序列化器,完成手机格式的后台校验

  5.2.2 校验用户是否已注册

  5.2.3 发送验证码,并把数据写入到redis数据库

  5.2.4 提交的时候进行验证码检查

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from API.APIModels import SMSModel
from Ali.SMSMessage import sendInfo
from django_redis import get_redis_connection
from API.APIEnum import SMSTemplateCode
from API.models import UserInfo
import re


# Create your views here.

#########定义一些校验规则--开始#########
def phone_validator(values):
    if not re.match(r"^1[3|4|5|6|7|8|9]\d{9}$", values):
        raise ValidationError('手机格式错误')


#########定义一些校验规则--结束#########
# 定义一个序列化器
class RegisterMessageSerializer(serializers.Serializer):
    phone = serializers.CharField(label='手机号', validators=[phone_validator, ])


class IsExist(APIView):
    def post(self, request, *args, **kwargs):
        ser = RegisterMessageSerializer(data=request.data)
        if not ser.is_valid():
            return Response({"status": "fail", "msg": "手机号码格式有误"})
        else:
            phone = request.data.get("phone")
            # 判断用户是否注册
            if UserInfo.objects.filter(phone=phone).first():
                return Response({"status": "fail", "msg": "用户已注册!"})
            else:
                return Response({"status": "success", "msg": "该用户尚未注册!"})


class SendCode(APIView):
    def post(self, request, *args, **kwargs):
        phone = request.data.get("phone")
        template_code = request.data.get("template_code")
        print(phone, template_code)
        if not all([phone, template_code]):
            return Response({"status": "fail", "verifycode": "", "msg": "必要入参不能为空!"})
        sms = SMSModel()
        sms.phone_numbers = phone
        sms.template_code = template_code
        verifycode = sms.get_verification_code  # 获取验证码
        res = sendInfo(sms) # 发送验证码
        if res:
            # 将验证码和手机号存入到redis,保活60s
            r = get_redis_connection()
            r.set(phone, verifycode, 600)  # 该值的有效期为
            return Response({"status": "success", "verifycode": verifycode, "msg": "验证码发送成功!"})
        else:
            return Response({"status": "fail", "verifycode": "", "msg": "验证码发送失败!请稍后重试"})

class UserRegister(APIView):
    def post(self, request, *args, **kwargs):
        phone = request.data.get("phone")
        password = request.data.get("password")
        verificationCode = request.data.get("verificationCode")
        if not all([phone, password,verificationCode]):
            return Response({"status": "fail", "verifycode": "", "msg": "必要入参不能为空!"})
        r = get_redis_connection()
        true_verifycode = r.get(phone)
        if not true_verifycode:
            return Response({"status": "fail", "verifycode": "", "msg": "验证码已过期,请重新发送!"})
        true_verifycode = true_verifycode.decode()  # 将字节转为字符串型
        if verificationCode == true_verifycode:
            UserInfo.objects.create(phone=phone,password=password)
            return Response({"status": "success", "verifycode": true_verifycode, "msg": "验证码通过!"})
        else:
            return Response({"status": "fail", "verifycode": true_verifycode, "msg": "验证码不正确,请重新发送!"})
views.py

  5.2.5 手机端js

Page({

  /**
   * 页面的初始数据
   */
  data: {
    text: '获取验证码', //按钮文字
    currentTime: 61, //倒计时
    disabled: false, //按钮是否禁用
    phone: '', //获取到的手机栏中的值
    VerificationCode: '',
    Code: '',
    NewChanges: '',
    NewChangesAgain: '',
    success: false,
    state: '',
    result: {}
  },
  /**
    * 获取验证码
    */
  return_home: function (e) {
    wx.navigateTo({
      url: '/pages/login/login',
    })

  },
  handleInputPhone: function (e) {
    this.setData({
      phone: e.detail.value
    })
  },
  handleVerificationCode: function (e) {
    console.log(e);
    this.setData({
      Code: e.detail.value
    })
  },
  handleNewChanges: function (e) {
    console.log(e);
    this.setData({
      NewChanges: e.detail.value
    })
  },
  handleNewChangesAgain: function (e) {
    console.log(e);
    this.setData({
      NewChangesAgain: e.detail.value
    })

  },
  doGetCode: function () {
    var that = this;
    that.setData({
      disabled: true, //只要点击了按钮就让按钮禁用 (避免正常情况下多次触发定时器事件)
      color: '#ccc',
    })
    var phone = that.data.phone;
    var currentTime = that.data.currentTime //把手机号跟倒计时值变例成js值
    var warn = null; //warn为当手机号为空或格式不正确时提示用户的文字,默认为空
    var phone = that.data.phone;
    var currentTime = that.data.currentTime //把手机号跟倒计时值变例成js值
    var warn = null; //warn为当手机号为空或格式不正确时提示用户的文字,默认为空
    if (phone == '') {
      warn = "号码不能为空";
    } else if (phone.trim().length != 11 || !/^1[3|4|5|6|7|8|9]\d{9}$/.test(phone)) {
      warn = "手机号格式不正确";
    }
    if (warn != null) {
      wx.showModal({
        title: '提示',
        content: warn
      })
      that.setData({
        disabled: false,
        color: '#33FF99'
      })
      return;
    }
    wx.request({
      url:  getApp().globalData.baseUrl +'/API/isExist/', //后端判断是否已被注册, 已被注册返回1 ,未被注册返回0
      method: "POST",
      data: { phone: this.data.phone },
      header: {
        'content-type': 'application/x-www-form-urlencoded'
      },
      success: function (res) {
        that.setData({
          result: res.data
        })
        if (that.data.result.status == "fail") { //手机号已被注册提示信息 //判断是否被注册
          warn = that.data.result.msg;
        }
        else {
          wx.request({
            url:  getApp().globalData.baseUrl +'/API/sendCode/', //填写发送验证码接口
            method: "POST",
            data: {
              phone: that.data.phone,
              template_code: "USERREGISTER"
            },
            header: {
              'content-type': 'application/x-www-form-urlencoded'
            },
            success: function (res) {
              console.log(res.data)
              that.setData({
                VerificationCode: res.data.verifycode,
              })
              if (that.data.result.status == "fail") {
                //当手机号正确的时候提示用户短信验证码已经发送
                wx.showToast({
                  title: that.data.result.msg,
                  icon: 'none',
                  duration: 2000
                });
              }

              //设置一分钟的倒计时
              var interval = setInterval(function () {
                currentTime--; //每执行一次让倒计时秒数减一
                that.setData({
                  text: currentTime + 's', //按钮文字变成倒计时对应秒数
                })
                //如果当秒数小于等于0时 停止计时器 且按钮文字变成重新发送 且按钮变成可用状态 倒计时的秒数也要恢复成默认秒数 即让获取验证码的按钮恢复到初始化状态只改变按钮文字
                if (currentTime <= 0) {
                  clearInterval(interval)
                  that.setData({
                    text: '重新发送',
                    currentTime: 61,
                    disabled: false,
                    color: '#33FF99'
                  })
                }
              }, 1000);
            }
          })
        };
        //判断 当提示错误信息文字不为空 即手机号输入有问题时提示用户错误信息 并且提示完之后一定要让按钮为可用状态 因为点击按钮时设置了只要点击了按钮就让按钮禁用的情况
        if (warn != null) {
          wx.showModal({
            title: '提示',
            content: warn
          })
          that.setData({
            disabled: false,
            color: '#33FF99'
          })
          return;
        }
      }

    })

  },
  submit: function (e) {
    var that = this
    if (this.data.Code == '') {
      wx.showToast({
        title: '请输入验证码',
        image: '/static/images/error.png',
        duration: 2000
      })
      return
    } else if (this.data.Code != this.data.VerificationCode) {
      wx.showToast({
        title: '验证码错误',
        image: '/static/images/error.png',
        duration: 2000
      })
      return
    }
    else if (this.data.NewChanges == '') {
      wx.showToast({
        title: '请输入密码',
        image: '/static/images/error.png',
        duration: 2000
      })
      return
    } else if (this.data.NewChangesAgain != this.data.NewChanges) {
      wx.showToast({
        title: '两次密码不一致',
        image: '/static/images/error.png',
        duration: 2000
      })
      return
    } else {
      var that = this
      var phone = that.data.phone;
      wx.request({
        url: getApp().globalData.baseUrl + '/API/userRegister/',
        method: "POST",
        data: {
          phone: phone,
          password: that.data.NewChanges,
          verificationCode: this.data.VerificationCode
        },
        header: {
          "content-type": "application/x-www-form-urlencoded"
        },
        success: function (res) {
          wx.showToast({
            title: '提交成功~',
            icon: 'loading',
            duration: 2000
          })
          that.setData({
            success: true
          })
        }
      })
    }
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})
register.js

  5.2.6 手机端css

/* pages/register/register.wxss */
page{
  background: #F0F0F0 ;
}
.row{
 margin-top: 20rpx;
 overflow: hidden;
 line-height: 100rpx;
 border-bottom: 1rpx solid #ccc;
 margin-left: 20rpx;
 margin-right: 20rpx;
 color: #777;
 background: #fff;

}
.info-input{
 height: 100rpx;
 margin-left: 50rpx;
 color: #777;
   float: left;
}
.info-input1{
 height: 100rpx;
 margin-left: 50rpx;
 color: #777;
   float: left;
   width: 420rpx;
}
.button_yam{
 width: 200rpx !important;
 height: 70rpx;
 line-height: 70rpx;
 font-size: 28rpx;
 background: #33FF99;
 float: left;
 margin-left: 10rpx;
 margin-top: 15rpx;
 color: #FFFFFF;
 padding: 0;
}
.submit{
 margin-top: 50rpx;
 margin-left: 20rpx;
 margin-right: 20rpx;
 background: #00CCFF;
  color: #FFFFFF;
}
.success{
 background: #ffffff;

}
.cheer{
 text-align: center;
 line-height: 400rpx;
 font-size: 60rpx;
 position: relative;
}
.return{
 margin: 20rpx;

}
 
register.wxss

  5.2.7 手机端hml

<!--pages/register/register.wxml-->
<view wx:if="{{!success}}">
    <view class='row'>
        <view class='info'>
            <input class='info-input1' bindinput="handleInputPhone" placeholder="请输入你的手机号" />
        </view>
        <button class='button_yam' bindtap='doGetCode' disabled='{{disabled}}' style="background-color:{{color}}">{{text}}</button>
    </view>
    <view class='row'>
        <view class='info'>
            <input class='info-input' bindinput="handleVerificationCode" placeholder="请输入你的验证码" />
        </view>

    </view>
    <view class='row'>
        <view class='info'>
            <input type='password' class='info-input' bindinput="handleNewChanges" placeholder="请输入你的密码" />
        </view>

    </view>
    <view class='row'>
        <view class='info'>
            <input type='password' class='info-input' bindinput="handleNewChangesAgain" placeholder="请重新输入你的密码" />
        </view>

    </view>
    <button class='submit' bindtap='submit'>提交</button>
</view>
<view class='success' wx:if="{{success}}">
    <view class='cheer'>
        <icon type="success" size="24" /> 恭喜您注册成功!</view>
    <button type="default" class='return' bindtap='return_home'>返回首页</button>
</view>
register.wxml

  5.2.8 app设置全局变量

  globalData: {
    userInfo: null,
    baseUrl:"http://127.0.0.1:8000"
  }
app.js

5.3 测试

  5.3.1 测试不符合规范的手机号

 

  5.3.2 测试验证码发送和redis数据库

  5.3.3 提交的时候做验证校验

  5.3.4 测试已注册用户重复注册

猜你喜欢

转载自www.cnblogs.com/YK2012/p/12928230.html
今日推荐