day110:MoFang:重新构造用户关系状态&添加好友&处理好友申请&获取申请好友历史记录&好友列表显示
目录:
day109+day110所学内容流程图
1.用户关系状态:重新构造
在day109博客的前提下, 我们对现在的用户关系处理功能在服务端和客户端上面进行重新调整下.
1.重新构造用户关系模型
user/models.py
,代码:
class UserRelation(BaseModel):
"""用户关系"""
__tablename__ = "mf_user_relation"
relation_status_chioce = (
(1,"好友"),
(2,"关注"),
(3,"拉黑"),
)
relation_type_chioce = (
(1, "手机"),
(2, "账号"),
(3, "邮箱"),
(4, "昵称"),
(5, "群聊"),
(6, "二维码邀请注册"),
)
send_user = db.Column(db.Integer, comment="用户1") # 主动构建关系的用户
receive_user = db.Column(db.Integer, comment="用户2") # 接受关系请求的用户
relation_type = db.Column(db.Integer, default=0, comment="构建关系类型")
relation_status = db.Column(db.Integer, default=0, comment="关系状态") def __repr__(self):
return "用户%s通过%s对%s进行了%s操作" % (self.send_user,self.relation_type, self.receive_user,self.relation_status)
2.修改之前写过的schema序列化器,去除status中的第三个参数
users/marshmallow.py
,代码:
from sqlalchemy import or_,and_
from .models import UserRelation
class UserSearchInfoSchema(SQLAlchemyAutoSchema):
"""用户搜索信息返回"""
id = auto_field()
nickname = auto_field()
avatar = auto_field()
relation_status = fields.String(dump_only=True) @post_dump()
def relation_status_post(self, data, **kwargs):
relaionship = UserRelation.query.filter(
or_(
and_(UserRelation.send_user==self.context["user_id"], UserRelation.receive_user==data["id"]),
and_(UserRelation.receive_user==self.context["user_id"], UserRelation.send_user==data["id"]),
)
).first()
if relaionship is not None:
data["relation_status"] = UserRelation.relation_status_chioce[relaionship.relation_status-1]
else:
data["relation_status"] = (0,"添加")
return data class Meta:
model = User
include_fk = True
include_relationships = True
fields = ["id","nickname","avatar","relation_status"]
sql_session = db.session
3.前端根据后端返回的不同的关系状态,提供对应的操作菜单
客户端根据不同的关系状态,提供对应的操作菜单,add_friend.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 update_nickname add_friend" 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="account" placeholder="输入昵称/手机/邮箱/魔方账号....">
</div>
<div class="friends_list">
<!-- for循环 -->
<div class="item" v-for="user in search_user_list">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" :src="avatar_url(user.avatar)" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">{{user.nickname}}</p>
<p class="time">刚刚搜索</p>
</div>
<div class="status" @click="change_relation(user.id,user.relation_status)">{{user.relation_status[1]}}</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">3小时前</p>
</div>
<div class="status">等待通过</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">1天前</p>
</div>
<div class="status">已通过</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">7天前</p>
</div>
<div class="status">已超时</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">7天前</p>
</div>
<div class="status">已拒绝</div>
</div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
user_id: "", // 当前登陆用户Id
search_user_list:[],
search_timer:"",
account:"",
prev:{name:"",url:"",params:{}},
current:{name:"add_friend",url:"add_friend.html",params:{}},
}
},
watch:{
account(){
clearTimeout(this.search_timer);
if(this.account.length>0){
this.search_timer = setTimeout(()=>{
this.search_user();
},2000);
}else{
this.search_user_list = [];
}
}
},
created(){
this.user_id = this.game.get("user_id") || this.game.fget("user_id");
},
methods:{
avatar_url(avatar){
var token = this.game.get("access_token") || this.game.fget("access_token");
return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`;
},
search_user(){
// 搜素用户
var token = this.game.get("access_token") || this.game.fget("access_token");
if(!token){
this.game.goFrame("login","login.html", this.current);
return ;
}
this.game.checkout(this, token, (new_access_token)=>{
if(!new_access_token){
this.game.print(new_access_token);
return ;
}
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.user.relation",
"params": {
"account": this.account,
}
},{
headers:{
Authorization: "jwt " + new_access_token,
}
}).then(response=>{
this.game.print(response.data.result);
if(parseInt(response.data.result.errno)==1000){
this.search_user_list = response.data.result.user_list;
}else if(parseInt(response.data.result.errno) == 1008){
this.search_user_list = [];
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
}); });
},
close_frame(){
this.game.outFrame("add_friend");
},
add_friend_commit(){
// 提交搜索信息 },
change_relation(user_id,status){
// ***关系状态修改***
this.game.print(user_id);
todo = [];
if(status[0] == 0){
// 未添加
todo.push("添加对方为好友");
}else if(status[0]==1){
// 已添加
return ;
}else if(status[0]==2){
// 关注关系
todo.push("添加对方为好友");
} api.actionSheet({
title: '操作',
buttons: todo
}, (ret, err)=>{
if(status[0] == 0 && ret.buttonIndex == 1 ){
// 申请添加好友
this.game.print(status[0]);
this.game.print(ret.buttonIndex);
}
}); }
}
});
}
</script>
</body>
</html>
根据不同的关系状态提供对应的操作菜单
2.添加好友
1.添加好友后端接口:User.friend.add
user/views.py
,代码:
@jsonrpc.method("User.friend.add")
@jwt_required # 验证jwt
def add_friend_apply(user_id):
"""申请添加好友"""
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,
} receive_user = User.query.get(user_id)
if receive_user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.receive_user_not_exists,
} # todo 查看是否被对方拉黑了 # 添加一个申请记录到MongoDB中
document = {
"send_user_id": user.id,
"send_user_nickname": user.nickname,
"send_user_avatar": user.avatar,
"receive_user_id": receive_user.id,
"receive_user_nickname": receive_user.nickname,
"receive_user_avatar": receive_user.avatar,
"time": datetime.now().timestamp(), # 操作时间
"status": 0,
}
mongo.db.user_relation_history.insert_one(document)
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
}
2.当两个用户关系为未添加时,可以添加好友
客户端发送请求添加好友,代码:
html/add_friend.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 update_nickname add_friend" 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="account" placeholder="输入昵称/手机/邮箱/魔方账号....">
</div>
<div class="friends_list">
<div class="item" v-for="user in search_user_list">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" :src="avatar_url(user.avatar)" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">{{user.nickname}}</p>
<p class="time">刚刚搜索</p>
</div>
<div class="status" @click="change_relation(user.id,user.relation_status)">{{user.relation_status[1]}}</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">3小时前</p>
</div>
<div class="status">等待通过</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">1天前</p>
</div>
<div class="status">已通过</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">7天前</p>
</div>
<div class="status">已超时</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">7天前</p>
</div>
<div class="status">已拒绝</div>
</div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
user_id: "", // 当前登陆用户Id
search_user_list:[],
search_timer:"",
account:"",
prev:{name:"",url:"",params:{}},
current:{name:"add_friend",url:"add_friend.html",params:{}},
}
},
watch:{
account(){
clearTimeout(this.search_timer);
if(this.account.length>0){
this.search_timer = setTimeout(()=>{
this.search_user();
},2000);
}else{
this.search_user_list = [];
}
}
},
created(){
this.user_id = this.game.get("user_id") || this.game.fget("user_id");
},
methods:{
avatar_url(avatar){
var token = this.game.get("access_token") || this.game.fget("access_token");
return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`;
},
search_user(){
// 搜素用户
var token = this.game.get("access_token") || this.game.fget("access_token");
if(!token){
this.game.goFrame("login","login.html", this.current);
return ;
}
this.game.checkout(this, token, (new_access_token)=>{
if(!new_access_token){
this.game.print(new_access_token);
return ;
}
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.user.relation",
"params": {
"account": this.account,
}
},{
headers:{
Authorization: "jwt " + new_access_token,
}
}).then(response=>{
this.game.print(response.data.result);
if(parseInt(response.data.result.errno)==1000){
this.search_user_list = response.data.result.user_list;
}else if(parseInt(response.data.result.errno) == 1008){
this.search_user_list = [];
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
}); });
},
close_frame(){
this.game.outFrame("add_friend");
},
add_friend_commit(){
// 提交搜索信息 },
change_relation(user_id,status){
// 关系状态修改
this.game.print(user_id);
todo = [];
if(status[0] == 0){
// 未添加
todo.push("添加对方为好友");
}else if(status[0]==1){
// 已添加
return ;
}else if(status[0]==2){
// 关注关系
todo.push("添加对方为好友");
} api.actionSheet({
title: '操作',
buttons: todo
}, (ret, err)=>{
// ***当两个用户关系为未添加时,可以添加对方为好友***
if(status[0] == 0 && ret.buttonIndex == 1 ){
// ***申请添加好友***
var token = this.game.get("access_token") || this.game.fget("access_token");
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.friend.add",
"params": {
"user_id": user_id,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 添加好友成功
this.game.print(">>>> 3")
this.game.print("添加好友成功!");
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
}
}); }
}
});
}
</script>
</body>
</html>
当两个用户关系为为添加时,可以添加好友
3.处理好友申请
1.处理好友申请-前端
当两个人不是好友,并且状态为等待通过时,会触发处理好友申请接口
add_friend.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 update_nickname add_friend" 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="account" placeholder="输入昵称/手机/邮箱/魔方账号....">
</div>
<div class="friends_list">
<div class="item" v-for="user in search_user_list">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" :src="avatar_url(user.avatar)" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">{{user.nickname}}</p>
<p class="time">刚刚搜索</p>
</div>
<div class="status" @click="change_relation(user.id,user.relation_status)">{{user.relation_status[1]}}</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">3小时前</p>
</div>
<div class="status">等待通过</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">1天前</p>
</div>
<div class="status">已通过</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">7天前</p>
</div>
<div class="status">已超时</div>
</div>
<div class="item">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">长昵称都很好</p>
<p class="time">7天前</p>
</div>
<div class="status">已拒绝</div>
</div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
user_id: "", // 当前登陆用户Id
search_user_list:[],
search_timer:"",
account:"",
prev:{name:"",url:"",params:{}},
current:{name:"add_friend",url:"add_friend.html",params:{}},
}
},
watch:{
account(){
clearTimeout(this.search_timer);
if(this.account.length>0){
this.search_timer = setTimeout(()=>{
this.search_user();
},2000);
}else{
this.search_user_list = [];
}
}
},
created(){
this.user_id = this.game.get("user_id") || this.game.fget("user_id");
},
methods:{
avatar_url(avatar){
var token = this.game.get("access_token") || this.game.fget("access_token");
return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`;
},
search_user(){
// 搜素用户
var token = this.game.get("access_token") || this.game.fget("access_token");
if(!token){
this.game.goFrame("login","login.html", this.current);
return ;
}
this.game.checkout(this, token, (new_access_token)=>{
if(!new_access_token){
this.game.print(new_access_token);
return ;
}
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.user.relation",
"params": {
"account": this.account,
}
},{
headers:{
Authorization: "jwt " + new_access_token,
}
}).then(response=>{
this.game.print(response.data.result);
if(parseInt(response.data.result.errno)==1000){
this.search_user_list = response.data.result.user_list;
}else if(parseInt(response.data.result.errno) == 1008){
this.search_user_list = [];
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
}); });
},
close_frame(){
this.game.outFrame("add_friend");
},
add_friend_commit(){
// 提交搜索信息 },
change_relation(user_id,status){
// 关系状态修改
this.game.print(status);
todo = [];
if(status[0] == 0){
// 未添加
if(status[1]=="等待通过"){
todo.push("通过");
todo.push("拒绝");
}else if(status[1]=="添加"){
todo.push("添加对方为好友");
} }else if(status[0]==1){
// 已添加
return ;
}else if(status[0]==2){
// 关注关系
todo.push("添加对方为好友");
} api.actionSheet({
title: '操作',
buttons: todo
}, (ret, err)=>{
var token = this.game.get("access_token") || this.game.fget("access_token");
// 第一种情况
if( status[0] == 0 && status[1]=="添加" && ret.buttonIndex == 1 ){
// 申请添加好友
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.friend.add",
"params": {
"user_id": user_id,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// ***添加好友成功***
api.alert({
title: '提示',
msg: '"添加好友成功!"',
}, function(ret, err){
}); }else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
}); // ***第二种情况:处理好友申请***
}else if(status[0] == 0 && status[1]=="等待通过"){
// 处理好友申请
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.friend.apply",
"params": {
"user_id": user_id,
"agree": ret.buttonIndex==1?true:false,
"search_text": this.account, // 搜索框中的搜索内容
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 添加好友成功
api.alert({
title: '提示',
msg: '"处理好友申请成功!"',
}, function(ret, err){
}); }else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
}
}); }
}
});
}
</script>
</body>
</html>
处理好友申请-前端
2.提供处理好友申请的接口
客户端进行对应菜单操作的时候,提供好友申请的处理接口,user/views.py
代码:
from sqlalchemy import and_
@jsonrpc.method("User.friend.apply")
@jwt_required # 验证jwt
def add_friend_apply(user_id,agree,search_text):
"""处理好友申请"""
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,
} receive_user = User.query.get(user_id)
if receive_user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.receive_user_not_exists,
} relaionship = UserRelation.query.filter(
or_(
and_(UserRelation.send_user == user.id, UserRelation.receive_user == receive_user.id),
and_(UserRelation.receive_user == user.id, UserRelation.send_user == receive_user.id),
)
).first() if agree:
if receive_user.mobile == search_text:
chioce = 0
elif receive_user.name == search_text:
chioce = 1
elif receive_user.email== search_text:
chioce = 2
elif receive_user.nickname == search_text:
chioce = 3
else:
chioce = 4 # ?????
if relaionship is not None:
relaionship.relation_status = 1
relaionship.relation_type = chioce
db.session.commit()
else:
relaionship = UserRelation(
send_user=user.id,
receive_user=receive_user.id,
relation_status=1,
relation_type=chioce,
)
db.session.add(relaionship)
db.session.commit() # 调整mongoDB中用户关系的记录状态
query = {
"$or": [{
"$and": [
{
"send_user_id": user.id,
"receive_user_id": receive_user.id,
"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}
}
],
}, {
"$and": [
{
"send_user_id": receive_user.id,
"receive_user_id": user.id,
"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}
}
],
}]
}
if agree:
argee_status = 1
else:
argee_status = 2 ret = mongo.db.user_relation_history.update(query, {"$set":{"status":argee_status}})
if ret and ret.get("nModified") < 1:
return {
"errno": status.CODE_UPDATE_USER_RELATION_ERROR,
"errmsg": message.update_user_relation_fail,
}
else:
return {
"errno": status.CODE_OK,
"errmsg": message.update_success,
}
4.获取申请好友历史记录
1.服务端提供接口:User.relation.history
在添加好友页面刚打开时,直接获取与当前用户存在申请好友历史的所有记录.
服务端提供api接口, user/views.py
,代码:
@jsonrpc.method("Use.relation.history")
@jwt_required # 验证jwt
def history_relation():
"""查找好友关系历史记录"""
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,
} query = {
"$or":[
{"send_user_id":user.id,"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}},
{"receive_user_id": user.id,"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}},
]
}
document_list = mongo.db.user_relation_history.find(query,{"_id":0})
data_list = []
for document in document_list:
if document.get("send_user_id") == user.id and document.get("status") == 0:
document["status"] = (0,"已添加")
elif document.get("receive_user_id") == user.id and document.get("status") == 0:
document["status"] = (0a, "等待通过")
elif document.get("status") == 1:
document["status"] = (1, "已通过")
else:
document["status"] = (2, "已拒绝") data_list.append(document) return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"data_list": data_list,
}
2.前端显示申请好友的所有历史记录
客户端代码:
add_friend.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 update_nickname add_friend" 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="account" placeholder="输入昵称/手机/邮箱/魔方账号....">
</div>
<div class="friends_list">
<div class="item" v-for="user in search_user_list">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" :src="avatar_url(user.avatar)" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">{{user.nickname}}</p>
<p class="time">刚刚搜索</p>
</div>
<div class="status" @click="change_relation(user.id,user.relation_status)">{{user.relation_status[1]}}</div>
</div>
<div class="item" v-for="relation in history_list">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" v-if="relation.send_user_id==user_id" :src="avatar_url(relation.receive_user_avatar)" alt="">
<img class="user_avatar" v-else :src="avatar_url(relation.send_user_avatar)" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username" v-if="relation.send_user_id==user_id">{{relation.receive_user_nickname}}</p>
<p class="username" v-else>{{relation.send_user_nickname}}</p>
<p class="time">{{game.time_format(relation.time)}}</p>
</div>
<div class="status">{{relation.status[1]}}</div>
</div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
user_id: "", // 当前登陆用户Id
search_user_list:[],
search_timer:"",
account:"",
history_list:[],
prev:{name:"",url:"",params:{}},
current:{name:"add_friend",url:"add_friend.html",params:{}},
}
},
watch:{
account(){
clearTimeout(this.search_timer);
if(this.account.length>0){
this.search_timer = setTimeout(()=>{
this.search_user();
},2000);
}else{
this.search_user_list = [];
}
}
},
created(){
this.user_id = this.game.get("id") || this.game.fget("id");
this.get_relation_history();
},
methods:{
get_relation_history(){
// ***获取历史信息记录***
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Use.relation.history",
"params": {}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
this.game.print(response.data.result);
if(parseInt(response.data.result.errno)==1000){
this.history_list = response.data.result.data_list;
}else if(parseInt(response.data.result.errno) == 1008){
this.history_list = [];
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
avatar_url(avatar){
var token = this.game.get("access_token") || this.game.fget("access_token");
return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`;
},
search_user(){
// 搜素用户
var token = this.game.get("access_token") || this.game.fget("access_token");
if(!token){
this.game.goFrame("login","login.html", this.current);
return ;
}
this.game.checkout(this, token, (new_access_token)=>{
if(!new_access_token){
this.game.print(new_access_token);
return ;
}
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.user.relation",
"params": {
"account": this.account,
}
},{
headers:{
Authorization: "jwt " + new_access_token,
}
}).then(response=>{
this.game.print(response.data.result);
if(parseInt(response.data.result.errno)==1000){
this.search_user_list = response.data.result.user_list;
}else if(parseInt(response.data.result.errno) == 1008){
this.search_user_list = [];
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
}); });
},
close_frame(){
this.game.outFrame("add_friend");
},
add_friend_commit(){
// 提交搜索信息 },
change_relation(user_id,status){
// 关系状态修改
this.game.print(status);
todo = [];
if(status[0] == 0){
// 未添加
if(status[1]=="等待通过"){
todo.push("通过");
todo.push("拒绝");
}else if(status[1]=="添加"){
todo.push("添加对方为好友");
} }else if(status[0]==1){
// 已添加
return ;
}else if(status[0]==2){
// 关注关系
todo.push("添加对方为好友");
} api.actionSheet({
title: '操作',
buttons: todo
}, (ret, err)=>{
var token = this.game.get("access_token") || this.game.fget("access_token");
if( status[0] == 0 && status[1]=="添加" && ret.buttonIndex == 1 ){
// 申请添加好友
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.friend.add",
"params": {
"user_id": user_id,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 添加好友成功
api.alert({
title: '提示',
msg: '"添加好友成功!"',
}, function(ret, err){
}); }else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
}else if(status[0] == 0 && status[1]=="等待通过"){
// 处理好友申请
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.friend.apply",
"params": {
"user_id": user_id,
"agree": ret.buttonIndex==1?true:false,
"search_text": this.account, // 搜索框中的搜索内容
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 添加好友成功
api.alert({
title: '提示',
msg: '"处理好友申请成功!"',
}, function(ret, err){
}); }else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
}
}); }
}
});
}
</script>
</body>
</html>
前端显示申请好友的所有历史记录
3.main.js提供时间格式化方法
main.js
提供时间格式化,代码:
time_format(time){
// 时间距离格式化显示
var now_time = parseInt((new Date() - 0) / 1000); // 当前客户端的秒时间戳
var has_time = now_time - time;
if(has_time<5 * 60){
return "刚刚";
}else if(has_time<30*60){
return parseInt(has_time/60)+"分钟前";
}else if(has_time<60*60){
return "半个小时前";
}else if(has_time<12*60*60){
return parseInt(has_time/60/60)+"小时前";
}else if(has_time<24*60*60){
return "半天前";
}else if(has_time<7*24*60*60){
return parseInt(has_time/24/60/60)+"天前";
}else if(has_time<14*24*60*60){
return "一周前";
}else if(has_time<30*24*60*60){
return "半个月前";
}
}
5.好友列表
1.好友列表页面展示好友列表数据
html/friends_list.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 setting" id="app">
<div class="friends_list">
<div class="item" v-for="user in friends">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" :src="avatar_url(user.avatar)" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<div class="info">
<p class="username">{{user.nickname}}</p>
<p class="fruit">果子:{{user.fruit}}</p>
</div>
<div v-if="user.fruit_status==1" class="behavior pick">摘</div>
<div v-if="user.fruit_status==2" class="behavior protect">护</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
friends:[],
page: 1,
is_send_ajax:false,
prev:{name:"",url:"",params:{}},
current:{name:"friend_list",url:"friend_list.html",params:{}},
}
},
created(){
this.get_friends();
this.get_friends_listener();
this.page_out_listener();
},
methods:{
avatar_url(avatar){
var token = this.game.get("access_token") || this.game.fget("access_token");
return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`;
},
get_friends_listener(){
// 监听下拉刷新获取好友列表信息
api.setRefreshHeaderInfo({
loadingImg: 'widget://image/refresh.png',
bgColor: null,
textColor: '#fff',
textDown: '下拉刷新...',
textUp: '松开刷新...'
}, (ret, err)=>{
//在这里从服务器加载数据,加载完成后调用api.refreshHeaderLoadDone()方法恢复组件到默认状态
this.get_friends();
setTimeout(()=>{
api.refreshHeaderLoadDone();
},4000);
});
},
page_out_listener(){
// 监听什么时候需要退出当前页面
api.addEventListener({
name: 'out_page_to_user'
}, (ret, err)=>{
this.goto_home();
});
},
get_friends(){
if(this.is_send_ajax){
return ;
}
// ***通过请求获取当前用户的好友列表***
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.is_send_ajax = true;
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "User.friend.list",
"params": {
"page": this.page
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
if(this.page+1 == response.data.result.pages){
this.is_send_ajax = true;
}else{
this.is_send_ajax = false;
this.page+=1;
}
if(this.page>1){
api.refreshHeaderLoadDone();
}
this.friends = response.data.result.friend_list.concat(this.friends);
}else if(parseInt(response.data.result.errno) == 1008){
this.friends = [];
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
goto_home(){
// 退出当前页面
this.game.outFrame("friend_list","friend_list.html", this.current);
},
}
});
}
</script>
</body>
</html>
好友列表页面展示好友列表数据
2.服务端提供展示好友列表数据接口:User.friend.list
服务端提供API接口,user/views.py
代码:
@jsonrpc.method("User.friend.list")
@jwt_required # 验证jwt
def list_friend(page=1,limit=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,
} pagination = UserRelation.query.filter(
or_(
and_(UserRelation.send_user == user.id),
and_(UserRelation.receive_user == user.id),
)
).paginate(page,per_page=limit)
user_id_list = []
for relation in pagination.items:
if relation.send_user == user.id:
user_id_list.append(relation.receive_user)
else:
user_id_list.append(relation.send_user) # 获取用户详细信息
user_list = User.query.filter(User.id.in_(user_id_list)).all()
friend_list = [{"avatar":user.avatar,"nickname":user.nickname,"id":user.id,"fruit":0,"fruit_status":0} for user in user_list]
pages = pagination.pages
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"friend_list": friend_list,
"pages": pages
}
day110:MoFang:重新构造用户关系状态&添加好友&处理好友申请&获取申请好友历史记录&好友列表显示的更多相关文章
- Redis位图法记录在线用户的状态
Redis位图法记录在线用户的状态 位图 Redis官方文档对于位图的介绍如下: 位图不是一个真实的数据类型,而是定义在字符串类型上的面向位的操作的集合.由于字符串类型是二进制安全的二进制大对象,并且 ...
- 四、续绑定SignaIR的用户管理-(添加好友和消息盒子)
一.聊天消息表(普通消息,申请消息,群聊消息) CREATE TABLE MSG_INFO ( MSG_Id INT PRIMARY KEY AUTO_INCREMENT, -- 消息标识 MSG_T ...
- XMPP即时通讯协议使用(十)——好友关系状态
sub ask recv 订阅 询问 接受 含义 substatus -1- 应该删除这个好友 Indicates that the roster item should be ...
- day108:MoFang:首页检测用户是否登录&在项目中使用MongoDB&用户页面更新用户信息&交易密码界面实现
目录 1.首页页面也要检测用户是否登录 2.在flask中使用MongoDB 3.用户页面更新用户信息 4.交易密码界面/密码修改界面/昵称修改界面初始化 5.交易密码实现 1.首页页面也要检测用户是 ...
- iOS开发之记录用户登录状态
iOS开发之记录用户登录状态 我们知道:CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreData还是蛮好用的.今天要说的是如何记录我们用户的登陆状态.例如微信 ...
- IOS开发之记录用户登陆状态
上一篇博客中提到了用CoreData来进行数据的持久化,CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreData还是蛮好用的.今天要说的是如何记录我们用户的登 ...
- 添加用户useradd,给用户设置修改密码passwd,修改用户信息usermod,修改用户密码状态chage,删除用户userdel,查询用户及组id,切换用户su,查看当前环境变量env
useradd 用户名 passwd 用户名,给指定用户设密码 passwd给当前用户设密码 添加一个用户系统会自动在以下文件或目录创建对应用户信息: [root@localhost ~]# grep ...
- IOS开发之记录用户登陆状态,ios开发用户登陆
IOS开发之记录用户登陆状态,ios开发用户登陆 上一篇博客中提到了用CoreData来进行数据的持久化,CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreDa ...
- Unity判断用户联网状态,WiFi/移动网络/无网络
Unity判断用户联网状态 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享. ...
- Asp.Net使用加密cookie代替session验证用户登录状态 源码分享
首先 session 和 cache 拥有各自的优势而存在. 他们的优劣就不在这里讨论了. 本实例仅存储用户id于用户名,对于多级权限的架构,可以自行修改增加权限字段 本实例采用vs2010编写 ...
随机推荐
- Python Boolean类型 判断
and 判断非Boolean类型数据会自动转换类型 "A" and "B" → "B" 因为表达式 A 和 B都为True所以返回 &quo ...
- 解决WIN7下pl/sql连接弹出空白提示框问题
问题描述: win7 32位系统,已安装oracle10.0开发客户端,已配置数据库, 登陆pl/sql时出现空白提示框问题,尝试重装oracle无果,于是上网查找解决方法,逐步尝试,终于把客户端弄好 ...
- Pytorch 感知机
单层感知机 \[\begin{aligned} & y = XW + b \\ & y = \sum x_i*w_i+b\\ \end{aligned} \] Derivative \ ...
- linux命令补充
1.nohup nohup /usr/local/node/bin/node /www/im/chat.js >> /usr/local/node/output.log 2>& ...
- Linux查看CPU 内存命令
查看CPU 内存命令:https://www.cnblogs.com/ggjucheng/archive/2013/01/14/2859613.html 查看某一进程内存占用:ps -ef 获取PID ...
- 西电oj135题 拼数字并排序
类别综合 时间限制 1S 内存限制 1000Kb 问题描述 对于输入的字符串(只包含字母和数字),将其中的连续数字拼接成整数,然后将这些整数按从大到小顺序输出.例如字符串"abc123d5e ...
- spring boot 常见问题
什么是 Spring Boot? 简单来说,spring boot 底层就是:spring + spring mvc + tomcat + 其他框架 starter: spring boot 依靠 s ...
- 通用图像分割任务- 使用 Mask2Former 和 OneFormer
本文介绍两个领先的图像分割神经网络模型: Mask2Former 和 OneFormer.相关模型已经在 Transformers 提供. Transformers 是一个开源库,提供了很多便捷的先进 ...
- c# 递归应用 完成js文件自动引用
背景: 两张表,分别是 :sys_tbl,和 sys_field,其中:sys_tbl 是系统所有表的信息,包含两个字段 :code(表名),name(表描述信息):sys_fld 是记录第张表中的字 ...
- DVWA-XSS (Reflected) 反射性 XSS
反射性XSS,是非持久性,也是最常见的XSS,通过解析传入前段页面,常见为交互式输入框 LOW 审计源码 <?php // 发送请求头 header ("X-XSS-Protectio ...