3 用户注册
使用单例模式重构云通信的发送短信的demo,并把其命名为sms.py,代码如下:
# coding=utf-8 import logging from CCPRestSDK import REST import ConfigParser # 帐号 accountSid= '8aaf07085f9eb021015fb58c56d906e8' # 主帐号Token accountToken= 'e21ca96da61c49ee97e0fdf1ba47de5a' # 应用Id appId='8aaf07085f9eb021015fb58c574106ef' # 请求地址,格式如下,不需要写http:// serverIP='app.cloopen.com' # 请求端口 serverPort='8883' # REST版本号 softVersion='2013-12-26' # 发送模板短信 # @param to 手机号码 # @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 '' # @param $tempId 模板Id # 希望用单例实现 # 使用一个属性来记录初始化的代码内容. 之后如果发现已经有属性的值了, 就直接返回即可 class CCP(object): def __new__(cls): # 判断系统是否有instance的值 if not hasattr(cls, 'instance'): # 创建instance # cls.instance = super(CCP, cls).__new__(cls) # # 实现一些需要初始化的代码 # # 初始化REST SDK # cls.instance.rest = REST(serverIP, serverPort, softVersion) # cls.instance.rest.setAccount(accountSid, accountToken) # cls.instance.rest.setAppId(appId) obj = super(CCP, cls).__new__(cls) # 实现一些需要初始化的代码 # 初始化REST SDK obj.rest = REST(serverIP, serverPort, softVersion) obj.rest.setAccount(accountSid, accountToken) obj.rest.setAppId(appId) cls.instance = obj # 如果是第二次进来, 直接返回即可 return cls.instance def send_template_sms(self, to,datas,temp_id): # 1. 调用发短信接口 try: result = self.rest.sendTemplateSMS(to, datas, temp_id) except Exception as e: logging.error(e) raise e # 通过打印查看, 我们发现如果返回statusCode为6个0,就表示发送成功 #{'templateSMS': {'smsMessageSid': 'cfb833c890b3480cb84457077a0e545e', 'dateCreated': '20171201100239'}, # 'statusCode': '000000'} # smsMessageSid:cfb833c890b3480cb84457077a0e545e # dateCreated:20171201100239 # statusCode:000000 print result # 2. 获取statusCode, 判断是否发送成功 status_Code = result.get('statusCode') if status_Code == '000000': return 0 # 3. 需要告诉服务器是否成功. 我们这里暂时定义, 返回0就是成功, 返回-1就是失败 else: return -1 # ccp = CCP() # 希望帮我们把鉴权的代码封装起来, 只调用一次 # # 使用时, 直接调用方法传参即可 # ccp.send_template_sms('17610812003', ["1234", 5], 1) #sendTemplateSMS(手机号码,内容数据,模板Id) if __name__ == '__main__': ccp = CCP() result = ccp.send_template_sms('17610812003', ["1234", 5], 1)
在ihome目录下创建response_code.py保存一些响应的值
# coding:utf-8 class RET: OK = "0" DBERR = "4001" NODATA = "4002" DATAEXIST = "4003" DATAERR = "4004" SESSIONERR = "4101" LOGINERR = "4102" PARAMERR = "4103" USERERR = "4104" ROLEERR = "4105" PWDERR = "4106" REQERR = "4201" IPERR = "4202" THIRDERR = "4301" IOERR = "4302" SERVERERR = "4500" UNKOWNERR = "4501" error_map = { RET.OK : u"成功", RET.DBERR : u"数据库查询错误", RET.NODATA : u"无数据", RET.DATAEXIST : u"数据已存在", RET.DATAERR : u"数据错误", RET.SESSIONERR : u"用户未登录", RET.LOGINERR : u"用户登录失败", RET.PARAMERR : u"参数错误", RET.USERERR : u"用户不存在或未激活", RET.ROLEERR : u"用户身份错误", RET.PWDERR : u"密码错误", RET.REQERR : u"非法请求或请求次数受限", RET.IPERR : u"IP受限", RET.THIRDERR : u"第三方系统错误", RET.IOERR : u"文件读写错误", RET.SERVERERR : u"内部错误", RET.UNKOWNERR : u"未知错误", }
在api_1_0/下的verify_code.py增加发送验证码的接口,代码如下:
@api.route("/sms_codes/<re(r'1[34578]\d{9}'):mobile>") def send_sms_code(mobile): # 一. 获取参数 # mobile # imaga_code 图片的验证码 # image_code_id uuid image_code = request.args.get('image_code') image_code_id = request.args.get('image_code_id') # 二. 验证参数的完整性及有效性 if not all([image_code, image_code_id]): resp_dict = { 'errno': RET.PARAMERR, 'errmsg': '参数不完整' } return jsonify(resp_dict) # 三. 处理业务逻辑 # 1. try: 从redis中获取真实的图片验证码 try: real_image_code = redis_store.get('image_code_' + image_code_id) except Exception as e: # 保存错误日志 logging.error(e) # 返回错误信息 resp_dict = { 'errno': RET.DBERR, 'errmsg': '获取图片验证码失败' } return jsonify(resp_dict) # 2. 判断图像验证码是否过期 # 一般从数据库中获取了一个空值NULL 就是None if real_image_code is None: # 返回错误信息 resp_dict = { 'errno': RET.NODATA, 'errmsg': '图片验证码过期/失效' } return jsonify(resp_dict) # 3. try:无论验证成功与否, 都执行删除redis中的图形验证码 try: redis_store.delete('image_code_' + image_code_id) except Exception as e: logging.error(e) # 一般来说, 只要是删除数据库出错, 都不应该返回错误信息. 因为这个操作, 不是用户做错了 # 此时, 只需要记录日志即可 # 4. 判断用户填写的验证码与真实验证码是否一致, 需要转换小(大)写后在比较 if real_image_code.lower() != image_code.lower(): resp_dict = { 'errno': RET.DATAERR, 'errmsg': '图片验证码填写有误' } return jsonify(resp_dict) # 5. try:判断用户手机号是否注册过--> 在短信发送之前, 节省资源 try: user = User.query.filter_by(mobile = mobile).first() except Exception as e: logging.error(e) # 理论上应该返回错误信息, 但是注册的时候还需要去验证, 去获取数据库. # 因此, 考虑到用户体验, 我们这一次就放过去, 让用户先接受验证码, 知道注册的时候再去判断 else: # 如果查询成功, 再次判断user是否存在 # 如果数据库没有数据, 返回一个NULL --> None if user is not None: resp_dict = { 'errno': RET.DATAEXIST, 'errmsg': '手机号已注册, 直接登录即可' } return jsonify(resp_dict) # 6. 创建/生成6位验证码 sms_code = '%06d' % random.randint(0, 999999) # 7. try:将短信验证码保存redis中 try: # 保存到redis中 setex: 可以设置数据并设置有效期 # 需要三个参数: key , expiretime, value redis_store.setex('sms_code_' + mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code) except Exception as e: logging.error(e) resp_dict = { 'errno': RET.DBERR, 'errmsg': '保存验证码异常' } return jsonify(resp_dict) # 8. try:发送验证码 try: ccp = CCP() # 第一个是手机号, 第二个发短信模板需要的参数[验证码, 过期时间], 第三个短信的模板编号 # result 如果发送短信成功, 就会返回0, 如果失败,就会返回-1 result = ccp.send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES / 60], 1) except Exception as e: logging.error(e) resp_dict = { 'errno': RET.THIRDERR, 'errmsg': '发送短信异常' } return jsonify(resp_dict) # 四. 返回数据 if result == 0: # 0, 表示发送短信成功 resp_dict = { 'errno': RET.OK, 'errmsg': '发送短信成功' } return jsonify(resp_dict) else: # -1, 表示发送短信失败 resp_dict = { 'errno': RET.THIRDERR, 'errmsg': '发送短信失败' } return jsonify(resp_dict)
前端调用接口的代码如下:
var req_data = {
image_code_id: imageCodeId,
image_code: imageCode
};
$.get("/api/v1_0/sms_codes/" + mobile, req_data, function (resp) {
// 根据返回的返回数据,进行相应的处理
if (resp.errno == 4004 || resp.errno == 4002) {
// 图片验证码的错误
$("#image-code-err span").html(resp.errmsg);
$("#image-code-err").show();
//恢复按钮点击
$(".phonecode-a").attr("onclick", "sendSMSCode();");
} else if ( resp.errno == 0 ) {
// 发送短信成功
var $time = $(".phonecode-a");
var duration = 60;
// 设置定时器
var intervalid = setInterval(function(){
$time.html(duration + "秒");
if(duration === 1){
// 清除定时器
clearInterval(intervalid);
$time.html('获取验证码');
$(".phonecode-a").attr("onclick", "sendSMSCode();");
}
duration = duration - 1;
}, 1000, 60);
} else {
//理论上应该对各个错误进行针对性处理. 我们这里只是简单的判断了两种错误, 其他错误就直接填出alert提示
alert(resp.errmsg);
}
})

浙公网安备 33010602011771号