目录:

1.用户关系状态:重新构造

2.添加好友

3.处理好友申请

4.获取申请好友历史记录

5.好友列表

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:重新构造用户关系状态&添加好友&处理好友申请&获取申请好友历史记录&好友列表显示的更多相关文章

  1. Redis位图法记录在线用户的状态

    Redis位图法记录在线用户的状态 位图 Redis官方文档对于位图的介绍如下: 位图不是一个真实的数据类型,而是定义在字符串类型上的面向位的操作的集合.由于字符串类型是二进制安全的二进制大对象,并且 ...

  2. 四、续绑定SignaIR的用户管理-(添加好友和消息盒子)

    一.聊天消息表(普通消息,申请消息,群聊消息) CREATE TABLE MSG_INFO ( MSG_Id INT PRIMARY KEY AUTO_INCREMENT, -- 消息标识 MSG_T ...

  3. XMPP即时通讯协议使用(十)——好友关系状态

    sub  ask  recv 订阅 询问 接受 含义 substatus -1-  应该删除这个好友          Indicates that the roster item should be ...

  4. day108:MoFang:首页检测用户是否登录&在项目中使用MongoDB&用户页面更新用户信息&交易密码界面实现

    目录 1.首页页面也要检测用户是否登录 2.在flask中使用MongoDB 3.用户页面更新用户信息 4.交易密码界面/密码修改界面/昵称修改界面初始化 5.交易密码实现 1.首页页面也要检测用户是 ...

  5. iOS开发之记录用户登录状态

    iOS开发之记录用户登录状态 我们知道:CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreData还是蛮好用的.今天要说的是如何记录我们用户的登陆状态.例如微信 ...

  6. IOS开发之记录用户登陆状态

    上一篇博客中提到了用CoreData来进行数据的持久化,CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreData还是蛮好用的.今天要说的是如何记录我们用户的登 ...

  7. 添加用户useradd,给用户设置修改密码passwd,修改用户信息usermod,修改用户密码状态chage,删除用户userdel,查询用户及组id,切换用户su,查看当前环境变量env

    useradd 用户名 passwd 用户名,给指定用户设密码 passwd给当前用户设密码 添加一个用户系统会自动在以下文件或目录创建对应用户信息: [root@localhost ~]# grep ...

  8. IOS开发之记录用户登陆状态,ios开发用户登陆

    IOS开发之记录用户登陆状态,ios开发用户登陆 上一篇博客中提到了用CoreData来进行数据的持久化,CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreDa ...

  9. Unity判断用户联网状态,WiFi/移动网络/无网络

    Unity判断用户联网状态 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享. ...

  10. Asp.Net使用加密cookie代替session验证用户登录状态 源码分享

    首先 session 和 cache 拥有各自的优势而存在.  他们的优劣就不在这里讨论了. 本实例仅存储用户id于用户名,对于多级权限的架构,可以自行修改增加权限字段   本实例采用vs2010编写 ...

随机推荐

  1. 鸿蒙hi3861V100开发板问题记录

    1.引脚复用 2.引脚复用方法: 1.看业务代码使用的是uart几,如使用的是uart2(实测可用uart1 tx为GPIO6, rx为GPIO5:uart2 tx为GPIO11,rx为GPIO12) ...

  2. vue项目部署后页面加载首次很慢的优化方案

    参考: vue项目首次加载特别慢需要怎么配置? 1.看看你的依赖包是不是全局引入的,改为组件内按需引入,可大大降低加载时长.或者将组件引入方式改为cdn引入.需要注意的是,两种引入方式不能共存. 2. ...

  3. Python Boolean类型 判断

    and 判断非Boolean类型数据会自动转换类型 "A" and "B" → "B" 因为表达式 A 和 B都为True所以返回 &quo ...

  4. MySql数据库读取字段错误问题

    一个小小的BUG,断断续续搞了一个月才搞定,使用MySql的时候使用mysql_fetch_fields()获取的字段始终始终是错误的,因为是修改别人的代码,一直找不到问题,Debug了无数次还是搞不 ...

  5. Delphi中IdHttp调用接口,返回值乱码

    --------开发环境是Delphi XE10-------- 这里要说的是BUG问题, var respStream : TStringStream; respStream.DataString有 ...

  6. Do whlie 循环

    Do whlie 循环 ◆对于while语句而言,如果不满足条件,则不能进入循环.但有时候我们需要即使不满足条件,也至少执行-次. ◆do...while循环和while循环相似,不同的是,do... ...

  7. StatefulSet 模板,更新,扩缩容,删除

    概念: StatefulSet是用来管理有状态应用的工作负载API对象,kubectl 中可以简写sts ,sts每一个pod生成一个唯一的标识符,sts_name-number,number从0开始 ...

  8. 博弈论练习6 Deleting Divisors(sg找规律,思维)

    题目链接在这里:G-Deleting Divisors_牛客竞赛博弈专题班组合游戏基本概念.对抗搜索.Bash游戏.Nim游戏习题 (nowcoder.com) 这道题一道比较明显的思路是使用sg函数 ...

  9. python实现简单猜数字游戏

    #!/usr/bin/env python import os import random import sys import time def yanse(s): print('\033[25;31 ...

  10. c输入的缓冲区

    作业题:输入两个整数(12和37),从键盘输入'A'和'a'时,输出两个数中的较大数:从键盘输入'B'和'b'时,输出两个数中的较小数. int a; char c; scanf("%d&q ...