day110:MoFang:重新构造用户关系状态&添加好友&处理好友申请&获取申请好友历史记录&好友列表显示

2023-05-24,,

目录:

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

《day110:MoFang:重新构造用户关系状态&添加好友&处理好友申请&获取申请好友历史记录&好友列表显示.doc》

下载本文的Word格式文档,以方便收藏与打印。