day108:MoFang:首页检测用户是否登录&在项目中使用MongoDB&用户页面更新用户信息&交易密码界面实现
- 2020 年 12 月 11 日
- 筆記
- PythonS31-笔记, Python全栈31期-笔记
目录
1.首页页面也要检测用户是否登录
created(){ this.check_user_login(); }, methods:{ // 首页检测用户是否登录 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"); } }); },
2.修改main.js中checkout函数
当检查用户登录状态时,如果用户点击取消登录,则返回1005状态码.
class Game{ // 验证用户token是否存在或者是否失效的功能 checkout(vm, token, callback){ vm.axios.post("",{ "jsonrpc": "2.0", "id": vm.uuid(), "method": "User.check", "params": {} },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno) === 1005){ api.actionSheet({ title: '本次登陆超时,是否更新登陆状态?', cancelTitle: '取消登陆', destructiveTitle: '更新登陆状态', }, (ret, err)=>{ if( ret.buttonIndex == 1 ){ this.get_acccess_by_refresh(vm, callback); }else{ // ******** callback({"errno":1005,"errmsg":"登陆超时!"}); } }); }else if (parseInt(response.data.result.errno) === 1000){ callback(token); } return false; }).catch(error=>{ this.print(error); }); } get_acccess_by_refresh(vm, callback){ var token = this.get("refresh_token") || this.fget("refresh_token"); if( !token ){ return false; } vm.axios.post("",{ "jsonrpc": "2.0", "id": vm.uuid(), "method": "User.refresh", "params": {} },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(response.data.result.errno===1000){ this.save({"access_token": response.data.result.access_token}); callback(response.data.result.access_token); } }) }
2.在flask中使用MongoDB
1.安装
pip install Flask-PyMongo
2.初始化和配置
from flask_pymongo import PyMongo # mongoDB mongo = PyMongo() def init_app(config_path): """全局初始化""" # 数据库初始化 db.init_app(app) app.db = db redis.init_app(app) mongo.init_app(app) # 初始化mongo
from . import InitConfig class Config(InitConfig): """项目开发环境下的配置""" # mongoDB配置信息 MONGO_URI = "mongodb://127.0.0.1:27017/mofang"
import base64, uuid,os from application import mongo from datetime import datetime @jsonrpc.method("User.avatar.update") @jwt_required # 验证jwt def update_avatar(avatar): """获取用户信息""" # 1. 接受客户端上传的头像信息 ext = avatar[avatar.find("/")+1:avatar.find(";")] # 资源格式 b64_avatar = avatar[avatar.find(",")+1:] b64_image = base64.b64decode(b64_avatar) filename = uuid.uuid4() static_path = os.path.join( current_app.BASE_DIR,current_app.config["STATIC_DIR"] ) with open("%s/%s.%s" % (static_path, filename,ext),"wb") as f: f.write(b64_image) # 2.判断用户是否存在 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, } # 3.将用户头像存到mysql数据库中 user.avatar = "%s.%s" % (filename,ext) db.session.commit() # ***4.添加修改记录到MongoDB***! document = { "user_id": user.id, "user_name": user.name, "user_nikcname": user.nickname, "updated_time": datetime.now().timestamp(), # 修改时间 "avatar": avatar, # 图片内容 "type": "avatar", # 本次操作的类型 } mongo.db.user_info_history.insert_one(document) return { "errno": status.CODE_OK, "errmsg": message.avatar_save_success, "avatar": "%s.%s" % (filename,ext) }
3.用户页面更新用户信息
2.并且在user.html中添加之前已经写过的change_avatar函数和get_user_info函数
<!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"> <img class="invite_btn" src="../static/images/invite.png" alt=""> </div> </div> <div class="menu"> <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> <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:{ 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>
用户页面更新用户信息
解决头像圆角问题
.user .info .user_avatar{ position: absolute; z-index: 1; width: 4.56rem; height: 4.56rem; margin: auto; top: 0; bottom: 0; left: 0; right: 0; border-radius: 1rem; }
4.交易密码界面/密码修改界面/昵称修改界面初始化
1.配置页面点击交易/密码修改/昵称,进入到相应界面
<!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 setting" id="app"> <div class="bg"> <img src="../static/images/form_bg.png"> </div> <img class="back" @click="goto_home" src="../static/images/user_back.png" alt=""> <div class="form"> <div class="item avatar" @click="update_avatar_frame"> <span class="title">头像</span> <span class="goto">></span> <span class="value"> <img :src="avatar" alt=""> </span> </div> <div class="item" @click="update_nickname_frame"> <span class="title">昵称</span> <span class="goto">></span> <span class="value">{{nickname}}</span> </div> <div class="item"> <span class="title">手机号</span> <span class="goto">></span> <span class="value">{{mobile}}</span> </div> <div class="item" @click="update_password_frame"> <span class="title">登陆密码</span> <span class="value"></span> <span class="goto">></span> </div> <div class="item" @click="update_transaction_password_frame"> <span class="title">交易密码</span> <span class="value"></span> <span class="goto">></span> </div> <div class="item"> <span class="title">地址管理</span> <span class="value"></span> <span class="goto">></span> </div> <div class="item"> <span class="title">设备管理</span> <span class="value"></span> <span class="goto">></span> </div> <div class="item logout"> <img @click="change_account" src="../static/images/change_account.png" alt=""> <p @click="logout">退出账号</p> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { nickname: "", mobile: "", avatar: "../static/images/avatar.png", prev:{name:"",url:"",params:{}}, current:{name:"setting",url:"setting.html",params:{}}, } }, created(){ this.get_user_info(); this.change_avatar(); }, methods:{ 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}`; this.mobile = res.mobile; } }) }, 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}`; } }); }, goto_home(){ // this.game.outFrame("setting"); this.game.goFrame("user","user.html",this.current); }, change_account(){ // 切换账号 this.game.goFrame("login","login.html", this.current); }, logout(){ // 退出账号 api.actionSheet({ title: '您确认要退出当前登录吗?', cancelTitle: '取消', destructiveTitle: '退出登录' }, (ret, err)=>{ if( ret ){ this.game.print(ret); // 取消为2 if(ret.buttonIndex==1){ this.game.save({"access_token":"","refresh_token":""}); this.game.fremove(["access_token","refresh_token"]); this.game.outWin("user"); } } }); }, update_avatar_frame(){ // 显示修改头像的页面frame this.game.goFrame("avatar","avatar.html", this.current,null,{ type:"push", //动画类型(详见动画类型常量) subType:"from_top", //动画子类型(详见动画子类型常量) duration:300 //动画过渡时间,默认300毫秒 }); }, update_transaction_password_frame(){ this.game.goFrame("transaction_password","transaction_password.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); }, update_password_frame(){ this.game.goFrame("password","password.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); }, update_nickname_frame(){ this.game.goFrame("password","password.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); } } }); } </script> </body> </html>
配置页面点击不同项进入不同页面
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 update_password" 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"> <input class="password" type="text" v-model="password" placeholder="交易密码...."> <input class="password password2" type="text" v-model="password2" placeholder="确认交易密码...."> </div> <img @click="update_password_commit" class="btn" src="../static/images/yes.png" alt=""> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { password:"", password2:"", prev:{name:"",url:"",params:{}}, current:{name:"transaction_password",url:"transaction_password.html",params:{}}, } }, methods:{ close_frame(){ this.game.outFrame("transaction_password"); }, update_password_commit(){ // 提交用户密码 } } }); } </script> </body> </html>
交易页面transaction_password.html初始化
交易界面CSS样式
.update_nickname .nickname, .update_password .password { margin: 8rem 4.4rem 4rem; width: 19.50rem; height: 4rem; line-height: 4rem; background-color: #cc9966; outline: none; border: 1px solid #330000; text-align: center; font-size: 1.5rem; color: #ffffcc; } .update_password .password{ margin-top: 5.6rem; margin-bottom: 0.4rem; } .update_password .password2{ margin-top: 0.4rem; }
交易界面CSS样式
3.密码修改界面初始化
<!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 update_password" 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"> <input class="password" type="text" v-model="password" placeholder="原密码...."> <input class="password password2" type="text" v-model="password2" placeholder="新密码...."> </div> <img @click="update_password_commit" class="btn" src="../static/images/yes.png" alt=""> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { password:"", password2:"", prev:{name:"",url:"",params:{}}, current:{name:"password",url:"password.html",params:{}}, } }, methods:{ close_frame(){ this.game.outFrame("password"); }, update_password_commit(){ // 提交用户密码 } } }); } </script> </body> </html>
密码修改界面password.html
4.昵称修改界面初始化
<!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 update_nickname" 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"> <input class="nickname" type="text" v-model="nickname" placeholder="输入昵称...."> </div> <img @click="update_nickname_commit" class="btn" src="../static/images/yes.png" alt=""> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { nickname:"", prev:{name:"",url:"",params:{}}, current:{name:"nickname",url:"nickname.html",params:{}}, } }, methods:{ close_frame(){ this.game.outFrame("nickname"); }, update_nickname_commit(){ // 提交用户昵称 } } }); } </script> </body> </html>
昵称修改界面nickname.html初始化
5.交易密码实现
1.模型中新增交易密码字段
from werkzeug.security import generate_password_hash, check_password_hash from application.utils.models import BaseModel,db class User(BaseModel): """用户基本信息""" # 新增一个交易密码的字段 _transaction_password = db.Column(db.String(255), comment="交易密码") @property def transaction_password(self): return self._transaction_password @transaction_password.setter def transaction_password(self, rawpwd): """密码加密""" self._transaction_password = generate_password_hash(rawpwd) def check_transaction_password(self, rawpwd): """验证密码""" return check_password_hash(self.transaction_password, rawpwd)
数据迁移,命令:
python manage.py db migrate -m "add user tansaction password" python manage.py db upgrade
@jsonrpc.method("User.transaction.password") @jwt_required # 验证jwt def transaction_password(password1, password2,old_password=None): """ 交易密码的初始化和修改 1. 刚注册的用户,没有交易密码,所以此处填写的是新密码 2. 已经有了交易密码的用户,修改旧的交易密码 """ # 1.判断两次输入密码是否一致 if password1 != password2: return { "errno": status.CODE_TRANSACTION_PASSWORD_ERROR, "errmsg": message.transaction_password_not_match } # 2.判断用户是否存在 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, } # 3.如果之前有存在交易密码,则需要验证旧密码 if user.transaction_password: # 去数据库查看用户是否有旧的交易密码 """修改""" # 验证旧密码 ret = user.check_transaction_password(old_password) if ret == False: # 如果验证旧密码失败 return { "errno": status.CODE_PASSWORD_ERROR, "errmsg": message.transaction_password_error } """设置交易密码""" user.transaction_password = password1 db.session.commit() return { "errno": status.CODE_OK, "errmsg": message.ok }
<!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 update_password" 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" v-if="is_set_password"> <input class="password" type="password" v-model="old_password" placeholder="旧交易密码...."> <input class="password password2" type="password" v-model="password1" placeholder="新交易密码...."> <input class="password password2" type="password" v-model="password2" placeholder="确认新交易密码...."> </div> <div class="content" v-else> <input class="password" type="password" v-model="password1" placeholder="交易密码...."> <input class="password password2" type="password" v-model="password2" placeholder="确认交易密码...."> </div> <img @click="update_password_commit" class="btn" src="../static/images/yes.png" alt=""> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { is_set_password: true, old_password: "", // 旧交易密码 password1:"", // 新交易密码 password2:"", // 确认新交易密码 prev:{name:"",url:"",params:{}}, current:{name:"transaction_password",url:"transaction_password.html",params:{}}, } }, created(){ if( this.game.fget("is_set_transaction_password") === "false"){ this.is_set_password = false; } }, methods:{ close_frame(){ this.game.outFrame("transaction_password"); }, update_password_commit(){ // 1.提交用户新密码 params = { password1: this.password1, password2: this.password2, } // 2.如果不是第一次设置密码,需要将旧密码也提交到后端 if(this.is_set_password){ params.old_password = this.old_password; } // 3.向交易密码后端接口发送请求 var token = this.game.get("access_token") || this.game.fget("access_token"); this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.transaction.password", "params": params },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ var res = response.data.result; if(parseInt(res.errno) === 1000){ this.game.fsave({"is_set_transaction_password":true}); this.game.outFrame("transaction_password"); }else{ api.alert({ title: '错误!', msg: res.errmsg, }, function(ret, err){ }); this.game.print(res.errno); } }).catch(error=>{ this.game.print(error); }); } } }); } </script> </body> </html>
@jsonrpc.method("User.transaction.password") @jwt_required # 验证jwt def transaction_password(password1, password2,old_password=None): """ 交易密码的初始化和修改 1. 刚注册的用户,没有交易密码,所以此处填写的是新密码 2. 已经有了交易密码的用户,修改旧的交易密码 """ if password1 != password2: return { "errno": status.CODE_TRANSACTION_PASSWORD_ERROR, "errmsg": message.transaction_password_not_match } 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, } # 1.如果之前有存在交易密码,则需要验证旧密码 if user.transaction_password: """修改""" # 验证旧密码 ret = user.check_transaction_password(old_password) if ret == False: return { "errno": status.CODE_PASSWORD_ERROR, "errmsg": message.transaction_password_error } """设置交易密码""" user.transaction_password = password1 db.session.commit() # ***2.添加交易密码的修改记录,为了保证安全,仅仅记录旧密码!*** if old_password: document = { "user_id": user.id, "user_name": user.name, "user_nikcname": user.nickname, "updated_time": datetime.now().timestamp(), # 修改时间 "transaction_password": old_password, # 变更内容 "type": "transaction_password", # 本次操作的类型 } mongo.db.user_info_history.insert_one(document) return { "errno": status.CODE_OK, "errmsg": message.ok }