day111:MoFang:邀請好友流程&生成邀請好友二維碼&第三方應用識別二維碼&本地編譯測試&記錄邀請人資訊
- 2020 年 12 月 17 日
- 筆記
- PythonS31-筆記, Python全棧31期-筆記
目錄
6.客戶端通過第三方識別微信二維碼,服務端提供對應的介面允許訪問
8.App配置私有協議, 允許第三方應用通過私有協議,喚醒APP
10.首頁監聽是否有來自第三方應用的喚醒:app_listener
12.對於後端註冊介面(User.register):增加invite_uid的處理
1.邀請業務邏輯流程圖

2.邀請好友-前端

<!DOCTYPE html> <html> <head> <title>邀請好友</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app frame avatar" id="app"> <div class="box"> <p class="title">邀請好友</p> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <img class="invite_code" src="../static/images/code.jpg" alt=""> </div> <p class="invite_tips">長按保存圖片到相冊</p> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { prev:{name:"",url:"",params:{}}, current:{name:"invite",url:"invite.html",params:{}}, } }, methods:{ close_frame(){ this.game.outFrame("invite"); }, } }); } </script> </body> </html>
邀請好友頁面初始化:invite.html

.invite_code{ width: 14rem; height: 14rem; position: absolute; left: 7rem; top: 11rem; } .invite_tips{ position: absolute; left: 7rem; top: 26.4rem; text-align: center; color: #fff; font-size: 1.5rem; }
邀請好友頁面CSS程式碼

<!DOCTYPE html> <html> <head> <title>用戶中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app user" id="app"> <div class="bg"> <img src="../static/images/bg0.jpg"> </div> <img class="back" @click="goto_index" src="../static/images/user_back.png" alt=""> <img class="setting" @click="goto_setting" src="../static/images/setting.png" alt=""> <div class="header"> <div class="info"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" :src="avatar" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <p class="user_name">{{nickname}}</p> </div> <div class="wallet"> <div class="balance"> <p class="title"><img src="../static/images/money.png" alt="">錢包</p> <p class="num">99,999.00</p> </div> <div class="balance"> <p class="title"><img src="../static/images/integral.png" alt="">果子</p> <p class="num">99,999.00</p> </div> </div> <div class="invite" @click="open_invite_page"> <img class="invite_btn" src="../static/images/invite.png" alt=""> </div> </div> <div class="menu"> <div class="item" @click="open_friend_list"> <span class="title">好友列表</span> <span class="value">查看</span> </div> <div class="item"> <span class="title">我的主頁</span> <span class="value">查看</span> </div> <div class="item"> <span class="title">任務列表</span> <span class="value">75%</span> </div> <div class="item"> <span class="title">收益明細</span> <span class="value">查看</span> </div> <div class="item"> <span class="title">實名認證</span> <span class="value">未認證</span> </div> </ul> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { nickname:"", avatar:"", prev:{name:"",url:"",params:{}}, current:{name:"user",url:"user.html",params:{}}, } }, created(){ this.get_user_info(); this.change_avatar(); }, methods:{ open_invite_page(){ // ***打開邀請好友頁面*** this.game.goFrame("invite","invite.html", this.current,null,{ type:"push", //動畫類型(詳見動畫類型常量) subType:"from_top", //動畫子類型(詳見動畫子類型常量) duration:300 //動畫過渡時間,默認300毫秒 }); }, open_friend_list(){ // 打開好友列表主框架頁面 // this.game.goWin("friends","friends.html",this.current); // 打開好友列表數據頁面 this.game.goFrame("friends","friends.html",this.current); this.game.goFrame("friend_list","friend_list.html",this.current,{ x: 0, y: 190, w: 'auto', h: 'auto', },null,true); }, change_avatar(){ api.addEventListener({ name: 'change_avatar' }, (ret, err)=>{ if( ret ){ var token = this.game.get("access_token") || this.game.fget("access_token"); this.avatar = `${this.settings.avatar_url}?sign=${ret.value.avatar}&token=${token}`; } }); }, get_user_info(){ var token = this.game.get("access_token") || this.game.fget("access_token"); // 獲取當前登陸用戶基本資訊 this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.info", "params": {} },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ var res = response.data.result; this.game.print(res); if(parseInt(res.errno) === 1000){ this.nickname = res.nickname; this.avatar = `${this.settings.avatar_url}?sign=${res.avatar}&token=${token}`; } }) }, goto_index(){ // 返回首頁 this.game.outWin("user"); }, goto_setting(){ // 進入設置 this.game.goFrame("setting","setting.html", this.current); } } }); } </script> </body> </html>
用戶中心點擊邀請好友進入到邀請好友介面
3.邀請好友-後端介面(生成二維碼)
安裝二維碼生成模組
pip install flask-qrcode
from flask_qrcode import QRcode # qrcode QRCode = QRcode() def init_app(config_path): """全局初始化""" # qrcode初始化配置 QRCode.init_app(app) return manager
from application import QRCode from flask import make_response,request @jwt_required # 驗證jwt def invite_code(): """邀請好友的二維碼""" current_user_id = get_jwt_identity() user = User.query.get(current_user_id) if user is None: return { "errno": status.CODE_NO_USER, "errmsg": message.user_not_exists, } static_path = os.path.join(current_app.BASE_DIR, current_app.config["STATIC_DIR"]) # 如果用戶頭像不存在,則使用默認頭像 if not user.avatar: user.avatar = current_app.config["DEFAULT_AVATAR"] avatar = static_path +"/"+ user.avatar data = current_app.config.get("SERVER_URL",request.host_url[:-1])+"/users/invite/download?uid=%s" % current_user_id # //127.0.0.1:5000/users/invite/download?uid=15 image = QRCode.qrcode(data,box_size=16,icon_img=avatar) # 根據用戶頭像生成二維碼 b64_image = image[image.find(",")+1:] qrcode_iamge = base64.b64decode(b64_image) response = make_response(qrcode_iamge) response.headers["Content-Type"] = "image/png" return response
from . import views from application.utils import path urlpatterns = [ path("/avatar", views.avatar), path("/invite/code", views.invite_code), path("/invite/download", views.invite_download), ]
# 用戶默認頭像 DEFAULT_AVATAR = "95822582-39d8-43ce-9498-fdced7f6a144.jpeg" # 服務端帶外提供的url地址 SERVER_URL = "//127.0.0.1:5000"

<!DOCTYPE html> <html> <head> <title>邀請好友</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app frame avatar" id="app"> <div class="box"> <p class="title">邀請好友</p> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <img class="invite_code" :src="code_url" alt=""> </div> <p class="invite_tips">長按保存圖片到相冊</p> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { code_url:"", //二維碼url地址 prev:{name:"",url:"",params:{}}, current:{name:"invite",url:"invite.html",params:{}}, } }, created(){ this.get_qrcode(); }, methods:{ get_qrcode(){ // ***獲取二維碼*** var token = this.game.get("access_token") || this.game.fget("access_token"); this.code_url = `${this.settings.code_url}/users/invite/code?token=${token}`; }, close_frame(){ this.game.outFrame("invite"); }, } }); } </script> </body> </html>
前端獲取後端生成的二維碼

<!DOCTYPE html> <html> <head> <title>邀請好友</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app frame avatar" id="app"> <div class="box"> <p class="title">邀請好友</p> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <img class="invite_code" :src="code_url" alt=""> </div> <p class="invite_tips">長按保存圖片到相冊</p> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { code_url:"", //二維碼url地址 prev:{name:"",url:"",params:{}}, current:{name:"invite",url:"invite.html",params:{}}, } }, created(){ this.get_qrcode(); }, methods:{ get_qrcode(){ // 獲取二維碼 var token = this.game.get("access_token") || this.game.fget("access_token"); this.code_url = `${this.settings.code_url}/users/invite/code?token=${token}`; // ***監聽頁面是否被長按*** api.addEventListener({ name:'longpress' }, (ret, err)=>{ api.saveMediaToAlbum({ path: this.code_url }, (ret, err)=> { if (ret && ret.status) { alert('保存成功'); } else { alert('保存失敗'); } }); }); }, close_frame(){ this.game.outFrame("invite"); }, } }); } </script> </body> </html>
前端長按頁面,保存圖片到相冊
from flask import render_template def invite_download(): uid = request.args.get("uid") if "micromessenger" in request.headers.get("User-Agent").lower(): # 判斷是否是微信識別二維碼 position = "weixin" else: position = "other" return render_template("users/download.html",position=position,uid=uid)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta > <title>Title</title> <style> body{ background-color: #000; } img{ width: 100%; } a{ color: #fff; } </style> </head> <body> {% if position == "weixin" %} <img src="/static/openbrowser.png" alt=""> {% else %} <div id="content"> </div> <script> // 嘗試通過打開客戶端已經安裝的魔方APP var iframe = document.createElement("iframe"); iframe.src = "mofang://?uid={{ uid }}"; // app的私有協議 iframe.hidden=true; document.body.appendChild(iframe); // 如果等待了4秒以後, setTimeout(function() { if (!document.hidden) { // 在4秒內如果頁面出去了。說明這個時候document.hidden是true,這段程式碼就不執行了。 // 就算是再切回來也是不執行的。 // 如果你進了這個函數,沒離開。。那就會在4秒後跳進這裡 alert('你還沒安裝魔方APP,去下載去'); u = navigator.userAgent; let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android終端 let isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios終端 var content = document.querySelector("#content"); if (isiOS) { // 去下載ios content.innerHTML = `<a href="/static/app/mofang.apk">下載魔方APP</a>`; } if (isAndroid){ // 去下載Android content.innerHTML = `<a href="/static/app/mofang.apk">下載魔方APP</a>`; } } }, 5000); </script> {% endif %} </body> </html>
<widget id="A6151729457001" version="0.0.1"> <name>MFdemo</name> <description> Example For APICloud. </description> <author email="[email protected]" href="//www.apicloud.com"> Developer </author> <content src="html/index.html" /> <access origin="*" /> <preference name="pageBounce" value="false"/> <preference name="appBackground" value="rgba(0,0,0,0.0)"/> <preference name="windowBackground" value="rgba(0,0,0,0.0)"/> <preference name="frameBackgroundColor" value="rgba(0,0,0,0.0)"/> <preference name="hScrollBarEnabled" value="false"/> <preference name="vScrollBarEnabled" value="false"/> <preference name="autoLaunch" value="true"/> <preference name="fullScreen" value="false"/> <preference name="autoUpdate" value="true" /> <preference name="smartUpdate" value="false" /> <preference name="debug" value="true"/> <preference name="statusBarAppearance" value="true"/> <permission name="readPhoneState" /> <permission name="camera" /> <permission name="record" /> <permission name="location" /> <permission name="fileSystem" /> <permission name="internet" /> <permission name="bootCompleted" /> <permission name="hardware" /> <preference name="urlScheme" value="mofang" /> <!-- **允許第三方應用通過私有協議,喚醒APP** --> </widget>
接下來的開發,我們不能再依賴官方提供的Apploader進行功能測試了,
所以我們使用由APICloud編輯器提供的本地編譯, 編譯自定義APPLoader來進行測試。


print(data,show=false){ // 列印數據 if(show){ alert(JSON.stringify(data)); }else{ console.log(JSON.stringify(data)); } }
html/index.html,程式碼:

<!DOCTYPE html> <html lang="en"> <head> <title>首頁</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta name="format-detection" content="telephone=no,email=no,date=no,address=no"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app" id="app"> <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png"> <div class="bg"> <img src="../static/images/bg0.jpg"> </div> <ul> <li><img class="module1" src="../static/images/image1.png"></li> <li><img class="module2" @click="gohome" src="../static/images/image2.png"></li> <li><img class="module3" src="../static/images/image3.png"></li> <li><img class="module4" src="../static/images/image4.png"></li> </ul> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { music_play:true, // 默認播放背景音樂 prev:{name:"",url:"",params:{}}, // 上一頁狀態 current:{name:"index",url:"index.html","params":{}}, // 下一頁狀態 } }, watch:{ music_play(){ if(this.music_play){ this.game.play_music("../static/mp3/bg1.mp3"); }else{ this.game.stop_music(); } } }, created(){ this.app_listener(); this.check_user_login(); }, methods:{ // **監聽是否有來自第三方應用的喚醒** app_listener(){ // 使用appintenr監聽並使用appParam接收URLScheme的參數 // 收集操作保存起來,並跳轉到註冊頁面. // 註冊frame中, 用戶註冊成功以後,記錄邀請資訊. api.addEventListener({ name:'appintent' // 當前事件監聽必須是唯一的,整個APP中只能編寫一次,否則衝突導致監聽無效 },(ret,err)=>{ var appParam = ret.appParam; this.game.print(typeof appParam); // {"uid":"15"} // 保存URLScheme參數到本地 this.game.fsave(appParam); // 跳轉到註冊頁面 this.game.goWin("user","register.html", this.current); }); }, check_user_login(){ let token = this.game.get("access_token") || this.game.fget("access_token"); this.game.checkout(this, token, (new_access_token)=>{ if(new_access_token.errno == 1005){ this.game.save({"access_token":""}); this.game.fremove("access_token"); } }); }, gohome(){ if(this.game.get("access_token") || this.game.fget("access_token")){ this.game.goWin("user","user.html", this.current); }else{ this.game.goWin("user","login.html", this.current); } } } }) } </script> </body> </html>
index.html中監聽是否來自第三方應用的喚醒

<!DOCTYPE html> <html> <head> <title>註冊</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app" id="app"> <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png"> <div class="bg"> <img src="../static/images/bg0.jpg"> </div> <div class="form"> <div class="form-title"> <img src="../static/images/register.png"> <img class="back" @click="back" src="../static/images/back.png"> </div> <div class="form-data"> <div class="form-data-bg"> <img src="../static/images/bg1.png"> </div> <div class="form-item"> <label class="text">手機</label> <input type="text" v-model="mobile" @change="check_mobile" placeholder="請輸入手機號"> </div> <div class="form-item"> <label class="text">驗證碼</label> <input type="text" class="code" v-model="sms_code" placeholder="請輸入驗證碼"> <img class="refresh" @click="send" src="../static/images/refresh.png"> </div> <div class="form-item"> <label class="text">密碼</label> <input type="password" v-model="password" placeholder="請輸入密碼"> </div> <div class="form-item"> <label class="text">確認密碼</label> <input type="password" v-model="password2" placeholder="請再次輸入密碼"> </div> <div class="form-item"> <input type="checkbox" class="agree" v-model="agree" checked> <label><span class="agree_text">同意磨方《用戶協議》和《隱私協議》</span></label> </div> <div class="form-item"> <img class="commit" @click="registerHandle" src="../static/images/commit.png"/> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { is_send: false, send_interval: 60, // 簡訊發送冷卻時間 mobile:"", password: "", password2: "", sms_code:"", agree:false, music_play:true, prev:{name:"",url:"",params:{}}, current:{name:"register",url:"register.html","params":{}}, } }, watch:{ music_play(){ if(this.music_play){ this.game.play_music("../static/mp3/bg1.mp3"); }else{ this.game.stop_music(); } } }, methods:{ send(){ // 點擊發送簡訊 if (!/1[3-9]\d{9}/.test(this.mobile)){ api.alert({ title: "警告", msg: "手機號碼格式不正確!", }); return; // 阻止程式碼繼續往下執行 } if(this.is_send){ api.alert({ title: "警告", msg: `簡訊發送冷卻中,請${this.send_interval}秒之後重新點擊發送!`, }); return; // 阻止程式碼繼續往下執行 } this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "Home.sms", "params": { "mobile": this.mobile, } }).then(response=>{ if(response.data.result.errno != 1000){ api.alert({ title: "錯誤提示", msg: response.data.result.errmsg, }); }else{ this.is_send=true; // 進入冷卻狀態 this.send_interval = 60; var timer = setInterval(()=>{ this.send_interval--; if(this.send_interval<1){ clearInterval(timer); this.is_send=false; // 退出冷卻狀態 } }, 1000); } }).catch(error=>{ this.game.print(error.response); }); }, registerHandle(){ // 註冊處理 this.game.play_music('../static/mp3/btn1.mp3'); // 驗證數據[雙向驗證] if (!/1[3-9]\d{9}/.test(this.mobile)){ api.alert({ title: "警告", msg: "手機號碼格式不正確!", }); return; // 阻止程式碼繼續往下執行 } if(this.password.length<3 || this.password.length > 16){ api.alert({ title: "警告", msg: "密碼長度必須在3-16個字元之間!", }); return; } if(this.password != this.password2){ api.alert({ title: "警告", msg: "密碼和確認密碼不匹配!", }); return; // 阻止程式碼繼續往下執行 } if(this.sms_code.length<1){ api.alert({ title: "警告", msg: "驗證碼不能為空!", }); return; // 阻止程式碼繼續往下執行 } if(this.agree === false){ api.alert({ title: "警告", msg: "對不起, 必須同意磨方的用戶協議和隱私協議才能繼續註冊!", }); return; // 阻止程式碼繼續往下執行 } var invite_uid = 0; var uid = this.game.fget("uid"); // {"uid":"15"} if(uid>0){ invite_uid = uid; } this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.register", "params": { "mobile": this.mobile, "sms_code":this.sms_code, "password":this.password, "password2":this.password2, "invite_uid": invite_uid //**註冊頁面接收invite_uid參數** } }).then(response=>{ this.game.print(response.data.result); if(response.data.result.errno != 1000){ api.alert({ title: "錯誤提示", msg: response.data.result.errmsg, }); }else{ // 註冊成功! api.confirm({ title: '磨方提示', msg: '註冊成功', buttons: ['返回首頁', '個人中心'] }, (ret, err)=>{ if(ret.buttonIndex == 1){ // 跳轉到首頁 this.game.outWin("user"); }else{ // 刪除邀請人 this.game.femove("uid"); // 跳轉到個人中心 this.game.goFrame("user","user.html", this.current); } }); } }).catch(error=>{ this.game.print(error.response); }); }, check_mobile(){ // 驗證手機號碼 this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.mobile", "params": {"mobile": this.mobile} }).then(response=>{ this.game.print(response.data.result); if(response.data.result.errno != 1000){ api.alert({ title: "錯誤提示", msg: response.data.result.errmsg, }); } }).catch(error=>{ this.game.print(error.response.data.error); }); }, back(){ // this.game.outWin(); // this.game.outFrame(); this.game.goGroup("user",0); } } }) } </script> </body> </html>
註冊頁面接收invite_uid參數
@jsonrpc.method("User.register") def register(mobile,password,password2, sms_code,invite_uid): """用戶資訊註冊""" try: ms = MobileSchema() ms.load({"mobile": mobile}) us = UserSchema() user = us.load({ "mobile":mobile, "password":password, "password2":password2, "sms_code": sms_code, "invite_uid": invite_uid, # **** }) data = {"errno": status.CODE_OK,"errmsg":us.dump(user)} except ValidationError as e: data = {"errno": status.CODE_VALIDATE_ERROR,"errmsg":e.messages} return data
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema,auto_field from marshmallow import post_load,pre_load,validates_schema from application import redis class UserSchema(SQLAlchemyAutoSchema): mobile = auto_field(required=True, load_only=True) password = fields.String(required=True, load_only=True) password2 = fields.String(required=True, load_only=True) sms_code = fields.String(required=True, load_only=True) invite_uid = fields.Integer(required=True, load_only=True) # ****** class Meta: model = User include_fk = True # 啟用外鍵關係 include_relationships = True # 模型關係外部屬性 fields = ["id", "name","mobile","password","password2","sms_code","invite_uid"] # 如果要全換全部欄位,就不要聲明fields或exclude欄位即可 sql_session = db.session @post_load() def save_object(self, data, **kwargs): invite_uid = int( data["invite_uid"] ) data.pop("password2") data.pop("sms_code") data.pop("invite_uid") # ***** data["name"] = data["mobile"] instance = User(**data) db.session.add( instance ) db.session.commit() # ***記錄邀請資訊到Mongdb中*** if invite_uid > 0: """只有invite_uid大於0,才是經過邀請註冊進來的新用戶""" # 驗證是否屬於有效的邀請 invite_user = User.query.get(invite_uid) if invite_user is not None: """只有邀請人存在的情況下才算有效邀請""" query = {"_id":invite_uid} ret = mongo.db.user_invite_list.find_one(query) if ret: mongo.db.user_invite_list.update(query,{"$push":{"invite_list":instance.id}}) else: data = {"_id": invite_uid, "invited_list": [instance.id]} mongo.db.user_invite_list.insert(data) # 添加好友關係 return instance @validates_schema def validate(self,data, **kwargs): # 校驗密碼和確認密碼 if data["password"] != data["password2"]: raise ValidationError(message=Message.password_not_match,field_name="password") #todo 校驗簡訊驗證碼 #1. 從redis中提取驗證碼 redis_sms_code = redis.get("sms_%s" % data["mobile"]) if redis_sms_code is None: raise ValidationError(message=Message.sms_code_expired,field_name="sms_code") redis_sms_code = redis_sms_code.decode() #2. 從客戶端提交的數據data中提取驗證碼 sms_code = data["sms_code"] #3. 字元串比較,如果失敗,則拋出異常,否則,直接刪除驗證碼 if sms_code != redis_sms_code: raise ValidationError(message=Message.sms_code_error, field_name="sms_code") redis.delete("sms_%s" % data["mobile"]) return data


