day111:MoFang:邀请好友流程&生成邀请好友二维码&第三方应用识别二维码&本地编译测试&记录邀请人信息
目录
6.客户端通过第三方识别微信二维码,服务端提供对应的接口允许访问
8.App配置私有协议, 允许第三方应用通过私有协议,唤醒APP
10.首页监听是否有来自第三方应用的唤醒:app_listener
12.对于后端注册接口(User.register):增加invite_uid的处理
1.邀请业务逻辑流程图
2.邀请好友-前端
1.邀请好友页面初始化:invite.html
invite.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="../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
2.邀请好友页面CSS代码
main.css
,代码:
.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代码
3.用户中心点击邀请好友进入到邀请好友界面
用户中心首页, 实现点击打开页面,user.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 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.邀请好友-后端接口(生成二维码)
1.安装生成二维码模块:flask-qrcode
flask-qrcode,文档: https://marcoagner.github.io/Flask-QRcode/
安装二维码生成模块
pip install flask-qrcode
初始化qrcode,application/__init__.py
,代码:
from flask_qrcode import QRcode # qrcode
QRCode = QRcode() def init_app(config_path):
"""全局初始化"""
# qrcode初始化配置
QRCode.init_app(app) return manager
2.生成二维码的后端接口
users/views.py
,视图提供生成二维码接口,代码:
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 # http://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
user/urls.py
,代码:
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),
]
application/settings/dev.py
,配置代码:
# 用户默认头像
DEFAULT_AVATAR = "95822582-39d8-43ce-9498-fdced7f6a144.jpeg"
# 服务端带外提供的url地址
SERVER_URL = "http://127.0.0.1:5000"
4.前端获取后端生成的二维码
客户端获取二维码,html/invite.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}`;
},
close_frame(){
this.game.outFrame("invite");
},
}
});
}
</script>
</body>
</html>
前端获取后端生成的二维码
5.前端长按页面,保存图片到相册
客户端用户长按页面, 保存图片到相册中,html/invite.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>
前端长按页面,保存图片到相册
6.客户端通过第三方识别微信二维码,服务端提供对应的接口允许访问
users/views.py
:
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)
7.download.html
模板目录下创建对应的html模板文件,templates/users/download.html
, 代码:
<!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){
// 去下载安卓
content.innerHTML = `<a href="/static/app/mofang.apk">下载魔方APP</a>`;
}
}
}, 5000); </script>
{% endif %}
</body>
</html>
8.App配置私有协议, 允许第三方应用通过私有协议,唤醒APP
config.xml
代码
<widget id="A6151729457001" version="0.0.1"> <name>MFdemo</name> <description> Example For APICloud. </description> <author email="developer@apicloud.com" href="http://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>
9.开始使用本地编译测试
1.如何本地编译
接下来的开发,我们不能再依赖官方提供的Apploader进行功能测试了,
所以我们使用由APICloud编辑器提供的本地编译, 编译自定义APPLoader来进行测试。
2.修改main.js的print函数
由此,带来了另一个问题,就是接下来,APP中打印的信息, 不能继续通过编辑器提供的console终端来查看了,所以我们修改main.js
的代码.
print(data,show=false){
// 打印数据
if(show){
alert(JSON.stringify(data));
}else{
console.log(JSON.stringify(data));
}
}
10.首页监听是否有来自第三方应用的唤醒:app_listener
index.html中监听是否来自第三方应用的唤醒.并接收参数.
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中监听是否来自第三方应用的唤醒
11.注册页面接收invite_uid参数
html/register.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参数
12.对于后端注册接口(User.register):增加invite_uid的处理
针对用户的注册功能, 增加invite_uid的处理,user/views.py
视图代码:
@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
user/marshmallow.py
,代码:
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
day111:MoFang:邀请好友流程&生成邀请好友二维码&第三方应用识别二维码&本地编译测试&记录邀请人信息的更多相关文章
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(四) 之 用户搜索(Elasticsearch),加好友流程(1)。
前面几篇基本已经实现了大部分即时通讯功能:聊天,群聊,发送文件,图片,消息.不过这些业务都是比较粗犷的.下面我们就把业务细化,之前用的是死数据,那我们就从加好友开始吧.加好友,首先你得知道你要加谁.L ...
- js生成邀请码(2)
//生成邀请码方法一 /*function createInviteCode() { var s = [],a=6,b=10; var chars = "123456789QWERTYUIP ...
- js生成邀请码(1)
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...
- XMPP系列(三)---获取好友列表、加入好友
1.心跳检測.掉线重连功能 client和server端都能够设置多久发送一次心跳包,假设对方没有返回正确的pong信息,则会断开连接,而加入掉线重连功能,则会自己主动进行连接. 假设自己写聊天功能还 ...
- 如何利用Python网络爬虫抓取微信好友数量以及微信好友的男女比例
前几天给大家分享了利用Python网络爬虫抓取微信朋友圈的动态(上)和利用Python网络爬虫爬取微信朋友圈动态——附代码(下),并且对抓取到的数据进行了Python词云和wordart可视化,感兴趣 ...
- Pyqt+QRcode 生成 识别 二维码
1.生成二维码 python生成二维码是件很简单的事,使用第三方库Python QRCode就可生成二维码,我用Pyqt给QRcode打个壳 一.python-qrcode介绍 python-qrco ...
- python实现树莓派生成并识别二维码
python实现树莓派生成并识别二维码 参考来源:http://blog.csdn.net/Burgess_Liu/article/details/40397803 设备及环境 树莓派2代 官方系统R ...
- HTML5实现扫描识别二维码/生成二维码
扫描识别二维码 思路: 1. 操作摄像头,获取图片.HTML5 WEBRTC的navigator.getUserMedia方法去实时获取摄像头资源. 2. 利用canvas使用相关算法分析图片识别图 ...
- C# ZXing.Net生成二维码、识别二维码、生成带Logo的二维码(二)
1.使用ZXint.Net生成带logo的二维码 /// <summary> /// 生成带Logo的二维码 /// </summary> /// <param name ...
随机推荐
- webug第十二关:我系统密码忘记了!
第十二关:我系统密码忘记了! 文件上传 直接上传php一句话, 菜刀链接
- MindManager 2021 版新增了哪些功能
MindManager Windows 21是一款强大的可视化工具和思维导图软件,在工作应用中有出色的表现.今天就带大家来看下这个新版本增加了哪些功能? 1.新增现代主题信息样式MindManager ...
- MathType单边大括号的编辑技巧你知道吗?
大家都知道,一般情况下,数学里面的括号都是成对出现的,但是也有些情况下可以只用到单边的括号,就比如分段函数,在编写的时候只需用到左半边的括号.MathType作为专业的公式编辑器,用它来编写公式再方便 ...
- 怎么给Folx添加需要储存的网站密码
Folx内置密码管理功能,可以帮助用户储存特定网站的密码,实现更加快速的登陆下载操作.在Folx的免费版本中,用户最多可以存储2个密码:而Folx专业版则不限制用户存储密码的数量. Folx通过两种方 ...
- python测试代码
前言: 编写函数或者类时,需要编写测试代码,来保证其的功能运行是否按预期的那样工作.在程序添加新的代码时,用来测试是否会破坏本身的功能. 我们使用python自带的unittest模块来测试代码. 编 ...
- websocket服务端开发
基于http请求以拉的方式去做服务器的推送,无论是实时性和有效字节都是差强人意的效果. 公司的im系统在与客户端的交互上实际上借助了websocket来实现服务器与客户端的事实消息推送,今天就来简单了 ...
- MVTMVC 区别
1,MVC的意思是 M:model V:views C:controller model是 主要是封装对数据库层的访问,对数据库中的数据进行增删改查操作 views 是 用于封装结果, 生程页面展示 ...
- python MD5加密和flask-generate_password_hash
实际开发过程中,有些数据是需要加密保存或者处理的,为了就是为了保证源数据的安全性.那么MD5加密作为一种简单有效的非对称加密方式在日常开发过程中也经常的被使用到.下面就来介绍下MD5算法: 1. * ...
- 在html页中添加视频的几种方式
1.avi格式代码片断如下: <object id="video" width="400" height="200" border=& ...
- 数据库原理-事务隔离与多版本并发控制(MVCC)
刚来美团实习,正好是星期天,不得不说,其内部的资料很丰富,看了部分文档后,对数据库事务这块更理解了.数据库事务的ACID,大家都知道,为了维护这些性质,主要是隔离性和一致性,一般使用加锁这种方式.同时 ...