在写uni-app即时聊天项目的时候,就想着用uni-app实现微信朋友圈的功能,在完成这些功能的时候,也遇到了很多的问题,比如数据表的设计(个人动态表,朋友圈动态表,通知表等),因为也是第一次去制作这种功能,所以可能在思路上还是有所不足的,以下代码仅供参考,如果有更好的方式,请大佬们提出来(可在评论区留言)

更多文章请访问 深的个人博客

功能设计简图

关于个人动态的操作

个人动态表的部分操作会导致朋友圈动态表的改变,例如发布动态和删除动态

关于朋友圈动态的操作

表设计

个人动态表

const mongoose = require("mongoose")// 动态表
const dynamicSchema = new mongoose.Schema({userID: { type: mongoose.Schema.Types.ObjectId, ref: "user" },    //用户idlogList: [{ text: String, imgList: Array, originalList: Array, comments: Array, like: Array, date: Date, address: String }],// imgList 存储压缩图的数组(为什么添加这个属性:因为在加载朋友圈图片时如果直接加载原图片速度会很慢,所以在上传图片的时候需要进行图片压缩,在加载的时候先加载压缩图,点击预览时加载原图片,图片压缩的办法在后面会提到)// originalList 存储原始图片的数组// text 文字内容// comments 评论// like 点赞// date 日期// address 位置
})module.exports = mongoose.model("dynamic", dynamicSchema);

朋友圈动态表

const mongoose = require("mongoose")// 朋友圈动态表(包括好友和自己的动态)
const socialSchema = new mongoose.Schema({userID: { type: mongoose.Schema.Types.ObjectId, ref: "user" }, //用户iddynamicList: [{ friend: String, date: Date }// friend  存储用户id// date  动态发布的时间],
})module.exports = mongoose.model("social", socialSchema);

评论通知表

const mongoose = require("mongoose")// 评论通知表
const comNotifySchema = new mongoose.Schema({userID: String,   //用户idnotify_list: [],// notify_list传入的对象,示例如下// {  //     fromUser: id,              // 点赞或者评论用户的id//     fromName: String,          // 点赞或者评论用户的名字//     toUser: id,                // 评论谁或者是回复谁(id) ,点赞时为null//     toName: String,            // 评论谁或者是回复谁(名字),点赞时为null//     date, logDate,//     type: "like" || "comment", // 类型:评论,点赞//     content: null || String,   // 内容:评论则存在对应的内容,点赞则为null//     unRead: false || true      // 用户是否已读// }
})
module.exports = mongoose.model("comNotify", comNotifySchema);

动态发表

效果图示

思路简析

在发表动态的时候,先要判断动态里是否有图片,如果有图片则先上传图片(没有图片直接发表动态),后端接收图片的时候会进行图片压缩,并返回两个图片地址(压缩图地址,原图地址),因为uni-app不支持多张图片同时上传只能一张一张上传,所以前端需要判断所有图片是否都上传成功,都上传成功后则执行动态发布的请求,后端发表动态时会执行三个步骤:添加到个人动态表,添加到朋友圈表(自己和好友),添加到动态通知表(好友),执行完这三个步骤,则动态发布成功

后端

图片压缩

图片压缩需要安装 images 模块

const images = require("images")// 上传朋友圈图片
"POST /upload/dynamic": async ctx => {// let data = ctx.req.body let url = basePath + ctx.req.body.savePath + "/" + ctx.req.file.filenamelet imgPath = "public/" + ctx.req.body.savePath + "/" + ctx.req.file.filenamelet zipUrl = compression(imgPath, ctx.req.body.savePath, ctx.req.file.filename, 200)ctx.body = {imgUrl: zipUrl, // 压缩图地址 url // 原图片地址}
},// 图片压缩的方法
function compression(filePath, savePath, fileName, num) {//根据文件路径读取文件,返回文件列表let zipPath = "public/" + savePath + "/" + "zip" + fileName  //压缩后保存的路径let number = num ? num : 150     //缩放的图片像素宽度images(filePath)                 //Load image from file //加载图像文件.size(number)                          //Geometric scaling the image to 400 pixels width//等比缩放图像到150像素宽.save(zipPath, {               //Save the image to a file, with the quality of 50quality: 100                    //保存图片到文件,图片质量为100});return basePath + savePath + "/" + "zip" + fileName  //压缩后保存的路径
}
动态发布
// 发布动态
exports.published = async data => {let { token, text, imgList, comments, originalList, like, date, address } = datalet tokenRes = verifyToken(token)let user = await User.findById(tokenRes.id)// 添加到我的动态表中let tokenDynamic = await Dynamic.findOne({ userID: tokenRes.id })if (tokenDynamic) { // 个人动态表存在let logList = tokenDynamic.logListlogList.unshift({text, imgList, originalList, comments, like, date, address})res = await Dynamic.updateOne({ userID: tokenRes.id }, { $set: { logList } })} else {  // 个人动态表不存在res = await Dynamic.create({userID: tokenRes.id,logList: [{text, imgList, originalList, comments, like, date, address}]})}if (res.userID || res.nModified == 1) {  // 将动态成功存储在个人动态表中后let friends = await Friend.findOne({ userID: tokenRes.id }) // 获取好友表let friend_list = friends.friend_listfriend_list.push({  //将token用户也添加到好友表中user: tokenRes.id,nickName: ""})// 遍历好友表将动态添加到好友的朋友圈动态表中,因为循环体中包含了 async 和 await 所以必须使用Promise.all包裹// 如果直接遍历会导致循环体中的代码失效let result = await Promise.all(friend_list.map(async item => {let d_list = await Social.findOne({ userID: item.user })if (d_list) {let dynamicList = d_list.dynamicListdynamicList.unshift({friend: tokenRes.id, date,})let d_res = await Social.updateOne({ userID: item.user }, { $set: { dynamicList } })} else {let d_res = await Social.create({userID: item.user,dynamicList: [{ friend: tokenRes.id, date }]})}// 添加到好友动态通知表中if (item.user != tokenRes.id) {let dy = await DyNotify.findOne({ userID: item.user })if (dy) { // 动态通知表存在let notify_list = dy.notify_listnotify_list[0] = {fromUser: user._id,fromImg: user.avatars}let d_res = await DyNotify.updateOne({ userID: item.user }, { $set: { notify_list } })} else { // 动态通知表不存在let d_res = await DyNotify.create({userID: item.user,notify_list: [{ormUser: user._id,fromImg: user.avatars}]})}let socketUser = await userSocket.findOne({ userId: item.user })// 已经将socket.io对象挂载到global对象上,所以可以直接调用io.to(socketUser.socketId).emit("getDyNotify")}}))return { status: 1, msg: "发布成功" }} else {return { status: 0, msg: "发布失败" }}}

前端

<template><view class="content"><comtarbar class="comtarbar"><template #center><view class="title">编辑</view></template><template #right><view class="btn" @tap="uploadImg">发表</view></template></comtarbar><view class="edit_box"><view class="textarea-box"><textarea v-model="content" placeholder="请输入文字" /></view><view class="e_title"><view class="title">添加图片</view><view v-if="imglist.length > 2"><view class="btn" @tap="deleteAll">移除全部</view></view></view><view class="imagebox"><view class="image_item" v-for="(item, index) in imglist" :key="index"><image @tap="previewImg(index)" class="c_image" :src="item" mode="aspectFill"></image><image @tap="deleteImg(index)" class="yc" src="../../../static/images/edit/yc.png" mode="widthFix"></image></view><view class="image_item" v-if="imglist.length < 9" @tap="choose"><image class="c_image" src="../../../static/images/group/addimage.png" mode="widthFix"></image></view></view><view class="e_title"><view class="title">添加位置</view></view><view class="localtion"><view class="localtion_btn" @tap="getLocaltion">定位</view><view v-if="address != ''" class="address">{{ address }}</view></view></view></view>
</template><script>
import request from '../../../api/request.js';
import { mapGetters } from 'vuex';
export default {data() {return {content: '',imglist: [],address: '',path: ''};},onLoad(options) {this.path = options.path;},computed: {...mapGetters(['getUser'])},methods: {// 选择图片choose() {uni.chooseImage({count: 9, //默认9sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有sourceType: ['album', 'camera '], //从相册选择success: res => {res.tempFilePaths.map(item => {if (this.imglist.length < 9) {this.imglist.push(item);}return item;});}});},// 移除图片deleteImg(index) {this.imglist.splice(index, 1);},// 移除全部图片deleteAll() {this.imglist = [];},// 预览图片previewImg(index) {uni.previewImage({urls: this.imglist,current: index,longPressActions: {itemList: ['发送给朋友', '保存图片', '收藏'],success: function(data) {console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');},fail: function(err) {console.log(err.errMsg);}}});},// 获取位置getLocaltion() {uni.chooseLocation({success: res => {this.address = res.address;}});},// 上传图片uploadImg() {// 如果有图片if (this.imglist.length > 0) {let list = [];let originalList = [];this.imglist.map(item => {//遍历上传图片uni.uploadFile({url: 'http://localhost:3000/upload/dynamic',filePath: item,name: 'file',formData: {token: uni.getStorageSync('token'),savePath: 'dynamic'},success: uploadFileRes => {let { imgUrl, url } = JSON.parse(uploadFileRes.data);list.push(imgUrl);originalList.push(url);if (list.length == this.imglist.length) {//图片上传完毕后发布动态this.published(list, originalList);}}});return item;});} else {//没有图片this.published();}},// 发布动态async published(list, originalList) {let res = await request({url: '/dynamic/published',data: {token: uni.getStorageSync('token'),text: this.content,imgList: list ? list : [],originalList: originalList ? originalList : [],comments: [],like: [],date: new Date(),address: this.address},method: 'POST'});res = res[1].data;if (res.status) {uni.showToast({title: res.msg,duration: 1000});setTimeout(() => {uni.navigateBack({delta: 1});if (this.path == 'dynamic') {uni.$emit('refresh'); //刷新数据uni.navigateTo({url: '../dynamic/dynamic'});} else {uni.$emit('refreshSocial'); //刷新数据uni.$emit('refresh'); //刷新数据uni.navigateTo({url: '../individual/individual?id=' + this.getUser._id});}}, 1300);} else {uni.showToast({title: res.msg,icon: 'none',duration: 1000});}}}
};
</script>

删除动态

思路:删除动态前端需要传递token和date,只需要这两个参数就可找到对应的动态并执行删除操作,需要执行删除的表有两个:个人动态表和朋友圈动态表,其中朋友圈动态表包括用户自身和好友的

后端

// 删除动态
exports.deleteDynamic = async data => {let { token, date } = datalet tokenRes = verifyToken(token)let tokenDynamic = await Dynamic.findOne({ userID: tokenRes.id })// 从动态表中删除该动态let logList = tokenDynamic.logListlet index = logList.findIndex(item => {return new Date(item.date).getTime() == new Date(date).getTime()})// 删除图片let imgList = logList[index].imgListlet originalList = logList[index].originalListlet newImgList = [...imgList, ...originalList]let basePath = "http://localhost:3000"// let basePath = "http://yemengs.cn"if (newImgList.length > 0) {newImgList.forEach(element => {let path = element.replace(basePath, "public")if (fs.existsSync(path)) {fs.unlinkSync(path);}console.log(path)});}logList.splice(index, 1)// 更新个人动态表let res = await Dynamic.updateOne({ userID: tokenRes.id }, { $set: { logList } }) let friends = await Friend.findOne({ userID: tokenRes.id })if (friends) {let friend_list = friends.friend_listif (friend_list.length > 0) {// 将动态从自己和好友的朋友圈动态表中移除let result = await Promise.all(friend_list.map(async item => {let social = await Social.findOne({ userID: item.user })let dynamicList = social.dynamicListlet index = dynamicList.findIndex(item2 => {return item2.friend.toString() == tokenRes.id.toString() && new Date(item2.date).getTime() == new Date(date).getTime()})if (index > -1) {dynamicList.splice(index, 1)let updateSocial = await Social.updateOne({ userID: item.user }, { $set: { dynamicList } })}}))}}if (res.nModified > 0) {return { status: 1, msg: "删除成功" }} else {return { status: 0, msg: "删除失败" }}
}

前端

// 删除动态
deleteLog(date) {uni.showModal({title: '提示',content: '确定删除该动态吗',success: async res => {if (res.confirm) {let res = await request({url: '/dynamic/delete',data: {date,token: uni.getStorageSync('token')},method: 'POST'});res = res[1].data;if (res.status == 1) {// 本地删除数据 ,为了不刷新数据造成scroll-view跳转的情况let index = this.social.findIndex(item => {return new Date(item.date).getTime() == new Date(date).getTime();});if (index > -1) {this.social.splice(index, 1);}uni.$emit('refresh'); //刷新数据}} else if (res.cancel) {}}});
},

评论与点赞

效果图

评论

后端

// 评论
exports.comment = async data => {let { token, id, date, toUser, toName, content } = datalet tokenRes = verifyToken(token)let user = await User.findById(tokenRes.id)let idDynamic = await Dynamic.findOne({ userID: id })let logList = idDynamic.logListlet index = logList.findIndex(item => {return new Date(item.date).getTime() == new Date(date).getTime()})logList[index].comments.unshift({fromUser: user._id,fromName: user.name,toUser,toName,content,})// 更新动态评论let res = await Dynamic.updateOne({ userID: id }, { $set: { logList } })if (res.nModified == 1) {// 添加到通知表let arr = []if (toUser == id && user._id != id) {  //当toUser和id相同时并且fromUser和id不同时,只需通知一个人(动态的主人)// console.log("第一种情况")arr = [{ to: id, id: tokenRes.id, name: user.name }]} else if (user._id == id && toUser != id) { //当fromUser和id相同而toUser不同时,只需通知一个人(toUser)// console.log("第二种情况", toName)arr = [{ to: toUser, id: tokenRes.id, name: user.name }]} else if (toUser == id && user._id == id) {// console.log("第三种情况")} else { //不同时,则需要通知两个人// console.log("第四种情况")arr = [{ to: id, id: tokenRes.id, name: user.name }, { to: toUser, id: tokenRes.id, name: user.name }]}let pro_result = await Promise.all(arr.map((async item => {let comNotify = await ComNotify.findOne({ userID: item.to })let result = nullif (comNotify) {let obj = {fromUser: item.id,fromName: item.name,toUser,toName,date: new Date(),logDate: date,type: "comment",content,text: logList[index].text,unRead: false,id}let notify_list = comNotify.notify_listnotify_list.unshift(obj)result = await ComNotify.updateOne({ userID: id }, { $set: { notify_list } })} else {let notify_list = [{fromUser: item.id,fromName: item.name,toUser,toName,date: new Date(),logDate: date,type: "comment",content,text: logList[index].text,unRead: false,id}]result = await ComNotify.create({ userID: item.to, notify_list })}if (result.userID || result.nModified > 0) {let socketUser = await userSocket.findOne({ userId: item.to })// 已经将socket.io对象挂载到global对象上,所以可以直接调用io.to(socketUser.socketId).emit("getComNotify")}})))return { status: 1, msg: "评论成功" }} else {return { status: 0, msg: "评论失败" }}}

前端

// 评论
async comment(content) {this.showComment = false; // 隐藏评论组件// 本地添加数据(减少请求)this.social.map(item => {if (item.id == this.id && new Date(item.log.date).getTime() == new Date(this.date).getTime()) {item.log.comments.unshift({fromUser: this.getUser._id,fromName: this.getUser.name,toUser: this.toUser,toName: this.toName,content});}return item;});// 向服务器发送数据let res = await request({url: '/dynamic/comment',data: {token: uni.getStorageSync('token'),id: this.id,date: this.date,toUser: this.toUser,toName: this.toName,content},method: 'POST'});if(res[1].data.status){uni.showToast({title: "评论成功",duration:1000});}else{uni.showToast({title: "发表评论失败",icon:"none",duration:1000});}
},

点赞

后端

// 点赞
exports.giveALike = async data => {// id:动态所属用户的idlet { token, id, date } = data  let tokenRes = verifyToken(token)let user = await User.findById(tokenRes.id)let idDynamic = await Dynamic.findOne({ userID: id })let logList = idDynamic.logListlet index = logList.findIndex(item => {return new Date(item.date).getTime() == new Date(date).getTime()})// 在点赞的数组中查找该用户是否已经点过赞let likeIndex = logList[index].like.findIndex(item2 => {return item2.id.toString() == user._id.toString()})let res = nullif (likeIndex > -1) { // 已经点过赞则执行取消点赞操作logList[index].like.splice(likeIndex, 1)res = await Dynamic.updateOne({ userID: id }, { $set: { logList } })} else {  // 没有则执行点赞操作logList[index].like.unshift({ id: user._id, nickName: null })res = await Dynamic.updateOne({ userID: id }, { $set: { logList } })if (res.nModified > 0) {if (tokenRes.id != id) {// 添加到通知表let comNotify = await ComNotify.findOne({ userID: id })let result = nullif (comNotify) {let obj = {fromUser: tokenRes.id,fromName: user.name,toUser: null,toName: null,date: new Date(),logDate: date,type: "like",content: null,text: logList[index].text,unRead: false,id}let notify_list = comNotify.notify_listnotify_list.unshift(obj)result = await ComNotify.updateOne({ userID: id }, { $set: { notify_list } })} else {let notify_list = [{fromUser: tokenRes.id,fromName: user.name,toUser: null,toName: null,date: new Date(),logDate: date,type: "like",content: null,text: logList[index].text,unRead: false,id}]result = await ComNotify.create({ userID: id, notify_list })}if (result.userID || result.nModified > 0) { // 点赞成功,通知用户 let socketUser = await userSocket.findOne({ userId: id })global.io.to(socketUser.socketId).emit("getComNotify")}}}}return {}
}

前端

// 点赞触发的事件
async giveALike(id, date) {// index 是 点赞和评论组件的索引值this.index = -1; // 本地添加数据this.social.map(item => {if (item.id == id && new Date(item.log.date).getTime() == new Date(date).getTime()) {// 判断点赞数组中是否存在该用户let index = item.log.like.findIndex(item => {//通过或来判断,因为存到数据库是存的id,本地存的是namereturn item.id == this.getUser._id;});if (index == -1) {//如果不存在则添加item.log.like.unshift({ id: this.getUser._id, nickName: this.getUser.name });} else {//存在则删除item.log.like.splice(index, 1);}}return item;});// 向服务器发送数据let res = await request({url: '/dynamic/like',data: {token: uni.getStorageSync('token'),id, // 动态所属用户的iddate},method: 'POST'});
},

获取个人动态

获取个人动态主要分为两种情况:第一种就是用户自己获取自己的个人动态,第二种是用户查看朋友的个人动态。第一种情况是可以直接获取的,第二种情况就要进行一些条件判断:1.动态点赞的用户是否跟其是朋友关系,不是则进行限制(非好友关系不可见)。2. 评论的用户跟其是否是朋友关系,不是则进行限制操作(非好友关系不可见)

后端

// 获取个人动态
exports.acquire = async data => {let { token, id, page, limit } = datalet tokenRes = verifyToken(token)let tokenUser = await User.findById(tokenRes.id)let obj = {}let result = await Friend.findOne({ userID: tokenRes.id }).populate("friend_list.user", "avatars")let friend_list = []if (result) {friend_list = result.friend_list}if (tokenRes.id == id) {//是用户自己obj.avatars = tokenUser.avatarsobj.id = tokenUser._idobj.nickName = tokenUser.name} else {let index = friend_list.findIndex(item => { //在好友表中查找并获取好友信息(昵称和用户头像)return item.user._id == id})if (index > -1) { //是好友let friend = friend_list[index]obj.avatars = friend.user.avatarsobj.id = friend.user._idobj.nickName = friend.nickName} else { //不是好友let idUser = await User.findById(id)obj.avatars = idUser.avatarsobj.id = idobj.nickName = idUser.name}}let res = await Dynamic.findOne({ userID: id })if (res) {let logList = res.logListlet count = logList.lengthlet maxPage = Math.ceil(count / limit)if (page > maxPage) {return { logList: [], avatars: obj.avatars, name: obj.nickName }}let skip = (page - 1) * limitlet spliceLogList = logList.splice(skip, limit)let oldLogList = []let mapRes = await Promise.all(spliceLogList.map(async item => {let like = [] //存储点赞里是好友关系的用户// 遍历点赞,在朋友中寻找,不是好友关系则屏蔽item.like.map(item4 => {if (item4.id == tokenRes.id) { //是token用户自身item4.nickName = tokenUser.namelike.push(item4)} else { //不是token用户自身则从好友列表中查找friend_list.map(item5 => {if (item5.user._id.toString() == item4.id.toString()) {item4.nickName = item5.nickNamelike.push(item4)}return item5})}return item4})item.like = likelet comCount = item.comments.length  //评论的个数let comments = [] //存储评论里是好友关系的用户// 遍历评论,在朋友中寻找,不是好友关系则屏蔽for (let i = 0; i < item.comments.length; i++) {if (i > 4) {break}let item6 = item.comments[i]// 下面两个变量是为了确定回复者和被回复者与token用户是否是好友关系let isFriends1 = falselet isFriends2 = falseif (item6.fromUser == tokenRes.id) { //是token用户自身item6.fromName = tokenUser.nameisFriends1 = true}if (item6.toUser == tokenRes.id) { //是token用户自身item6.toUser == tokenUser.nameisFriends2 = true}if (isFriends1 != true || isFriends2 != true) {friend_list.map(item7 => { //判断回复者的好友关系if (item7.user._id.toString() == item6.fromUser.toString()) {item6.fromName = item7.nickNameisFriends1 = true}if (item7.user._id.toString() == item6.toUser.toString()) {item6.toName = item7.nickNameisFriends2 = true}return item7})}if (isFriends1 && isFriends2) {//当两个人与token用户都是好友关系时才显示comments.push(item6)}}// item.comments.map(item6 => {//     return item6// })item.comments = commentslet newObj = {comCount,avatars: obj.avatars,id: obj.id,nickName: obj.nickName,text: item.text,imgList: item.imgList,originalList: item.originalList,comments: item.comments,like: item.like,date: item.date,address: item.address,_id: item._id,}// let newObj = Object.assign(obj, item)// console.log(newObj)oldLogList.push(newObj)// console.log(newDynamicList)return item}))// 排序(因为以上map遍历以及内嵌了await 导致执行顺序会乱)function listSort(a, b) {return new Date(b.date).getTime() - new Date(a.date).getTime()}let newLogList = oldLogList.sort(listSort)return { logList: newLogList, avatars: obj.avatars, name: obj.nickName }} else {return { logList: [], avatars: obj.avatars, name: obj.nickName }}}

前端

// 获取个人动态
async acquire() {let res = await request({url: '/dynamic/acquire',data: {token: uni.getStorageSync('token'),id: this.id,limit: this.limit,page: this.page}});let logList = res[1].data.logList;this.avatars = res[1].data.avatars;this.name = res[1].data.name;if (logList.length > 0) {this.social.push(...logList);}
},

获取动态详情

获取动态详情的情况跟获取个人动态的情况是相似的都需要判断是谁在获取动态,不是动态所属的人查看则需要进行对应的条件判断

后端

// 获取动态详情
exports.singleDynamic = async data => {let { token, id, date } = datalet tokenRes = verifyToken(token)let tokenUser = await User.findById(tokenRes.id)let obj = {}let result = await Friend.findOne({ userID: tokenRes.id }).populate("friend_list.user", "avatars")let friend_list = []if (result) {friend_list = result.friend_list}if (tokenRes.id == id) {//是用户自己obj.avatars = tokenUser.avatarsobj.id = tokenUser._idobj.nickName = tokenUser.name} else {let index = friend_list.findIndex(item => { //在好友表中查找并获取好友信息(昵称和用户头像)return item.user._id == id})if (index > -1) { //是好友let friend = friend_list[index]obj.avatars = friend.user.avatarsobj.id = friend.user._idobj.nickName = friend.nickName} else {let idUser = await User.findById(id)obj.avatars = idUser.avatarsobj.id = idobj.nickName = idUser.name}}let res = await Dynamic.findOne({ userID: id })let logIndex = res.logList.findIndex(item => {return new Date(item.date).getTime() == new Date(date).getTime()})let log = res.logList[logIndex]let like = [] //存储点赞里是好友关系的用户// 遍历点赞,在朋友中寻找,不是好友关系则屏蔽log.like.map(item4 => {if (item4.id == tokenRes.id) { //是token用户自身item4.nickName = tokenUser.namelike.push(item4)} else { //不是token用户自身则从好友列表中查找friend_list.map(item5 => {if (item5.user._id.toString() == item4.id.toString()) {item4.nickName = item5.nickNamelike.push(item4)}return item5})}return item4})log.like = likelet comments = [] //存储评论里是好友关系的用户// 遍历评论,在朋友中寻找,不是好友关系则屏蔽log.comments.map(item6 => {// 下面两个变量是为了确定回复者和被回复者与token用户是否是好友关系let isFriends1 = falselet isFriends2 = falseif (item6.fromUser == tokenRes.id) { //是token用户自身item6.fromName = tokenUser.nameisFriends1 = true}if (item6.toUser == tokenRes.id) { //是token用户自身item6.toUser == tokenUser.nameisFriends2 = true}if (isFriends1 != true || isFriends2 != true) {friend_list.map(item7 => { //判断回复者的好友关系if (item7.user._id.toString() == item6.fromUser.toString()) {item6.fromName = item7.nickNameisFriends1 = true}if (item7.user._id.toString() == item6.toUser.toString()) {item6.toName = item7.nickNameisFriends2 = true}return item7})}if (isFriends1 && isFriends2) {//当两个人与token用户都是好友关系时才显示comments.push(item6)}return item6})log.comments = commentslet newObj = {avatars: obj.avatars,id: obj.id,nickName: obj.nickName,text: log.text,imgList: log.imgList,originalList: log.originalList,comments: log.comments,like: log.like,date: log.date,address: log.address,_id: log._id,}return newObj
}

前端

// 获取动态详情
async getSingleDynamic(id, date) {let res = await request({url: '/dynamic/single',data: {token: uni.getStorageSync('token'),id,date}});this.log = res[1].data;
}

获取朋友圈

效果图

后端

朋友圈动态表只存储用户的id和date,所以在获取的时候必须要根据id和date到对应用户的个人动态表里面查找

const Social = require("../model/socialModel")  // 朋友圈动态表
const Friend = require("../model/friendModel")  // 好友表
const Dynamic = require("../model/dynamicModel")  // 个人动态表
const User = require("../model/userModel")  // 用户表const { verifyToken } = require("../tool/token")// 获取朋友圈
exports.acquire = async data => {let { token, page, limit } = datalet tokenRes = verifyToken(token)let social = await Social.findOne({ userID: tokenRes.id })let tokenUser = await User.findById(tokenRes.id)if (social) {let dynamicList = social.dynamicListif (dynamicList.length == 0) {  //return {dynamicList}} else {let count = dynamicList.lengthlet maxPage = Math.ceil(count / limit)if (page > maxPage) {return []}let skip = (page - 1) * limitlet spliceDynamicList = dynamicList.splice(skip, limit)let result = await Friend.findOne({ userID: tokenRes.id }).populate("friend_list.user", "avatars")if (result) {let friend_list = result.friend_listlet oldDynamicList = []let mapRes = await Promise.all(spliceDynamicList.map(async item => {let obj = {}if (item.friend == tokenRes.id) {//是用户自己obj.avatars = tokenUser.avatarsobj.id = tokenUser._idobj.nickName = tokenUser.name} else { //是好友let index = friend_list.findIndex(item2 => { //在好友表中查找并获取好友信息(昵称和用户头像)return item2.user._id == item.friend})if (index > -1) {let friend = friend_list[index]obj.avatars = friend.user.avatarsobj.id = friend.user._idobj.nickName = friend.nickName}}let f_dynamic = await Dynamic.findOne({ userID: item.friend })let logIndex = f_dynamic.logList.findIndex(item3 => { //查找动态// 根据时间查找return new Date(item3.date).getTime() == new Date(item.date).getTime()})if (logIndex > -1) {let log = f_dynamic.logList[logIndex]let like = [] //存储点赞里是好友关系的用户// 遍历点赞,在朋友中寻找,不是好友关系则屏蔽log.like.map(item4 => {if (item4.id == tokenRes.id) { //是token用户自身item4.nickName = tokenUser.namelike.push(item4)} else { //不是token用户自身则从好友列表中查找friend_list.map(item5 => {if (item5.user._id.toString() == item4.id.toString()) {item4.nickName = item5.nickNamelike.push(item4)}return item5})}return item4})log.like = likelet comments = [] //存储评论里是好友关系的用户// 遍历评论,在朋友中寻找,不是好友关系则屏蔽for (let i = 0; i < log.comments.length; i++) {let item6 = log.comments[i]// 下面两个变量是为了确定回复者和被回复者与token用户是否是好友关系let isFriends1 = falselet isFriends2 = falseif (item6.fromUser == tokenRes.id) { //是token用户自身item6.fromName = tokenUser.nameisFriends1 = true}if (item6.toUser == tokenRes.id) { //是token用户自身item6.toUser == tokenUser.nameisFriends2 = true}if (isFriends1 != true || isFriends2 != true) {friend_list.map(item7 => { //判断回复者的好友关系if (item7.user._id.toString() == item6.fromUser.toString()) {item6.fromName = item7.nickNameisFriends1 = true}if (item7.user._id.toString() == item6.toUser.toString()) {item6.toName = item7.nickNameisFriends2 = true}return item7})}if (isFriends1 && isFriends2) {//当两个人与token用户都是好友关系时才显示comments.push(item6)}}obj.comCount = comments.length //评论的条数log.comments = comments.splice(0, 5) //截取返回obj.log = logoldDynamicList.push(obj)}return item}))// 排序(因为以上map遍历以及内嵌了await 导致执行顺序会乱)function listSort(a, b) {return new Date(b.log.date).getTime() - new Date(a.log.date).getTime()}let newDynamicList = oldDynamicList.sort(listSort)return newDynamicList} else {return {dynamicList}}}} else { //social不存在直接返回return {dynamicList: []}}
}

个人空间

###效果图

留言

表设计

留言表
const mongoose = require("mongoose")// 留言表
//一条留言只能空间所属用户和留言用户可以回复信息  空间所属用户的留言只有空间所属用户可以回复
const messageSchema = new mongoose.Schema({userID: String, //用户idmessages: [{ "user": { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, content: String, "nickName": String, children: Array, date: Date }],
})
module.exports = mongoose.model("message", messageSchema);
留言通知表
const mongoose = require("mongoose")// 留言通知表
const messNotifySchema = new mongoose.Schema({userID: String,  //用户idnotify_list: [],// notify_list 传入的对象格式如下// { "user": id, content: String, "nickName": String, type: String, date: Date ,unRead:true||false}//type:"message"||"reply" :留言 | 回复
})
module.exports = mongoose.model("messNotify", messNotifySchema);

关于留言的相关操作

发布留言

后端
// 发布留言
exports.published = async data => {// id是所属空间的用户(对谁留言)let { token, id, content } = datalet tokenRes = verifyToken(token)let idMessage = await Message.findOne({ userID: id })let result = nullif (idMessage) {//留言表存在let messages = idMessage.messagesmessages.unshift({user: tokenRes.id,content,nickName: null,children: [],date: new Date()})result = await Message.updateOne({ userID: id }, { $set: { messages } })} else {//留言表不存在result = await Message.create({userID: id,messages: [{user: tokenRes.id,content,nickName: null,children: [],date: new Date()}]})}if (result.userID || result.nModified) { // 留言发表成功if (tokenRes.id !== id) {// 成功发表留言后将该留言存储在留言通知表中let res = await MessNotify.findOne({ userID: id })let resNotify = nullif (res) { //留言通知表存在let notify_list = res.notify_listnotify_list.unshift({user: tokenRes.id,content,nickName: null,type: "message",date: new Date(),unRead: false})resNotify = await MessNotify.updateOne({ userID: id }, { $set: { notify_list } })} else {//留言通知表不存在resNotify = await MessNotify.create({userID: id,notify_list: [{user: tokenRes.id,content,nickName: null,type: "message",date: new Date(),unRead: false}]})}if (resNotify.userID || resNotify.nModified > 0) {socketUser = await userSocket.findOne({ userId: id })io.to(socketUser.socketId).emit("message")}}return { status: 1, msg: "发表成功" }} else {return { stats: 0, msg: "发表失败" }}
}
前端
async message(){let res = await request({//新留言url: '/message/published',data: {token: uni.getStorageSync('token'),id: this.id,content},method: 'POST'});if (res[1].data.status) {// 本地添加数据,避免刷新页面(减少请求)this.messages.unshift({user: {_id:this.getUser._id,avatars:this.getUser.avatars},content,nickName: this.getUser.name,children: [],date: new Date()});}
}

回复留言

后端
// 回复留言
exports.reply = async data => {let { token, id, messageID, message, date, content } = datalet tokenRes = verifyToken(token)let idMessage = await Message.findOne({ userID: id })let messages = idMessage.messageslet index = messages.findIndex(item => {return messageID.toString() == item.user.toString() && new Date(item.date).getTime() == new Date(date).getTime()})messages[index].children.unshift({user: tokenRes.id,date: new Date(),content})let result = await Message.updateOne({ userID: id }, { $set: { messages } })if (result.nModified > 0) {let toUser = null              //一条留言只能空间所属用户和留言用户可以回复信息if (tokenRes.id == messageID) { //当token用户和留言用户(messageID)是同一个人时,则toUser就是id(空间用户)toUser = id} else {//当token用户和留言用户(messageID)不是同一个人时,则toUser就是messageID(留言用户)toUser = messageID}// 成功发表留言后将该留言存储在留言通知表中let res = await MessNotify.findOne({ userID: toUser })let resNotify = nullif (res) {let notify_list = res.notify_listnotify_list.unshift({user: tokenRes.id,content,nickName: null,type: "reply",date: new Date(),message,unRead: false})resNotify = await MessNotify.updateOne({ userID: toUser }, { $set: { notify_list } })} else {resNotify = await MessNotify.create({userID: toUser,notify_list: [{user: tokenRes.id,content,nickName: null,type: "reply",date: new Date(),message,unRead: false}]})}if (resNotify.userID || resNotify.nModified > 0) {socketUser = await userSocket.findOne({ userId: id })io.to(socketUser.socketId).emit("message")}return { status: 1, msg: "回复成功" }}
}
前端
async reply(){//回复let res = await request({url: '/message/reply',data: {token: uni.getStorageSync('token'),id: this.id,messageID: this.params.messageID,date: this.params.date,content,message: this.params.message},method: 'POST'});if (res[1].data.status) {// 本地添加数据,避免刷新页面let index = this.messages.findIndex(item => {return this.params.messageID.toString() == item.user._id.toString() && new Date(item.date).getTime() == new Date(this.params.date).getTime();});this.messages[index].children.unshift({user: {_id:this.getUser._id,avatars:this.getUser.avatars},name: this.getUser.name,date: new Date(),content});}}

获取留言

原则:非好友关系,留言不可见

后端
// 获取留言
exports.acquire = async data => {let { token, id, page, limit } = datalet tokenRes = verifyToken(token)let user = await User.findById(tokenRes.id)let idMessage = await Message.findOne({ userID: id }).populate("messages.user", ["name", "avatars"])if (idMessage) {let messages = idMessage.messagesif (messages.length == 0) {return {messages}} else {let mount = messages.lengthlet maxPage = Math.ceil(mount / limit)if (page > maxPage) {return {messages: [],}} else {let skip = (page - 1) * limitlet oldMessage = messages.splice(skip, limit)let tokenFriend = await Friend.findOne({ userID: tokenRes.id })let friend_list = tokenFriend.friend_listlet newMessage = []oldMessage.map(async item => {let obj = {user: item.user,content: item.content,_id: item._id,date: item.date}obj.children = []if (item.user._id.toString() == tokenRes.id) { // 留言是token用户发表的obj.nickName = user.nameitem.children.map(item3 => {//遍历回复if (item3.user.toString() == tokenRes.id.toString()) {obj.children.push({user: item3.user,name: user.name,content: item3.content})} else {let index = friend_list.findIndex(item5 => {return item3.user.toString() == item5.user.toString()})obj.children.push({user: item3.user,name: friend_list[index].nickName,content: item3.content})}})newMessage.push(obj)} else {  // 留言不是token用户发表的let index = friend_list.findIndex(item2 => {return item2.user.toString() == item.user._id.toString()})if (index > -1) { // 能在好友表中找到,则添加(非好友关系,留言不可见)obj.nickName = friend_list[index].nickNameitem.children.map(item3 => { //遍历回复if (item3.user.toString() == tokenRes.id.toString()) {obj.children.push({user: item3.user,name: user.name,content: item3.content})} else {let index = friend_list.findIndex(item5 => {return item3.user.toString() == item5.user.toString()})obj.children.push({user: item3.user,name: friend_list[index].nickName,content: item3.content})}})newMessage.push(obj)}}})return {messages: newMessage,}}}} else {return {messages: []}}
}
前端
// 获取留言
async acquireMessage() {let res = await request({url: '/message/acquire',data: {token: uni.getStorageSync('token'),id: this.id,page: this.mess_page,limit: this.mess_limit}});this.messages = res[1].data.messages;
},

访客

访客表设计

const mongoose = require("mongoose")// 访问表
const visitorsSchema = new mongoose.Schema({userID: String,    //用户idcount: Number,visitors: [{ "user": { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, "nickName": String, date: String, unRead: Boolean }],// unRead : 用户是否已读该条访客记录
})
module.exports = mongoose.model("visitor", visitorsSchema);

后端

const Visitor = require("../model/visitorsModel") // 访客表
const Friend = require("../model/friendModel")
const User = require("../model/userModel")
const userSocket = require("../model/userSocketModel")const { verifyToken } = require("../tool/token")
// 记录访客
exports.record = async data => {let { token, id } = datalet tokenRes = verifyToken(token)let idVisitors = await Visitor.findOne({ userID: id })let tokenUser = await User.findById(tokenRes.id)let result = nullif (idVisitors) {let visitors = idVisitors.visitorslet count = idVisitors.countlet index = visitors.findIndex(item => {return item.user.toString() == tokenRes.id.toString()})if (index > -1) {// 时间差let timeDifference = (new Date().getTime() - new Date(visitors[index].date).getTime()) / 1000 / 60// 同一个用户访问时间差大于10分钟时才记录let judge = timeDifference > 10if (!judge) {return {}}}count++visitors.unshift({user: tokenRes.id,nickName: tokenUser.name,date: new Date(),unRead: false})result = await Visitor.updateOne({ userID: id }, { $set: { count, visitors } })} else {result = await Visitor.create({userID: id,visitors: [{user: tokenRes.id,nickName: tokenUser.name,date: new Date(),unRead: false}],count: 1})}if (result.nModified || result.userID) {socketUser = await userSocket.findOne({ userId: id })io.to(socketUser.socketId).emit("refreshVisitor")return { status: 1, msg: "记录成功" }} else {return { status: 0, msg: "记录失败" }}
}// 获取访客
exports.acquire = async data => {let { token, page, limit } = datalet tokenRes = verifyToken(token)let idVisitors = await Visitor.findOne({ userID: tokenRes.id }).populate("visitors.user", ["avatars", "name"])if (!idVisitors) { //不存在时return {newVisitor: 0,visitors: [],count: 0}} else {let visitors = idVisitors.visitorslet newVisitor = 0for (let i = 0; i < visitors.length; i++) {if (visitors[i].unRead) {break} else {newVisitor++}}let mount = visitors.lengthlet maxPage = Math.ceil(mount / limit)if (page > maxPage) {return {visitors: [],count: idVisitors.count}} else {let skip = (page - 1) * limitlet oldVisitors = visitors.splice(skip, limit)let tokenFriend = await Friend.findOne({ userID: tokenRes.id })let newVisitors = []oldVisitors.map(async item => {let index = tokenFriend.friend_list.findIndex(item2 => {return item2.user.toString() == item.user._id.toString()})if (index > -1) {newVisitors.push({user: item.user,nickName: tokenFriend.friend_list[index].nickName,date: item.date,isFriend: true,unRead: item.unRead})} else {newVisitors.push({user: item.user,nickName: item.user.name,date: item.date,isFriend: false,unRead: item.unRead})}})return {newVisitor,visitors: newVisitors,count: idVisitors.count}}}
}// 删除访客
exports.remove = async data => {let { token, id, date } = datalet tokenRes = verifyToken(token)let tokenVisitors = await Visitor.findOne({ userID: tokenRes.id })let visitors = tokenVisitors.visitorslet index = visitors.findIndex(item => {return item.user == id && new Date(item.date).getTime() == new Date(date).getTime()})visitors.splice(index, 1)let res = await Visitor.updateOne({ userID: tokenRes.id }, { $set: { visitors } })if (res.nModified > 0) {return { status: 1, msg: "删除成功" }} else {return { status: 0, msg: "删除失败" }}
}// 更新新访客
exports.update = async data => {let { token } = datalet tokenRes = verifyToken(token)let tokenVisitors = await Visitor.findOne({ userID: tokenRes.id })let visitors = tokenVisitors.visitorsfor (let i = 0; i < visitors.length; i++) {if (visitors[i].unRead) {break} else {visitors[i].unRead = true}}let result = await Visitor.updateOne({ userID: tokenRes.id }, { $set: { visitors } })if (result.nModified > 0) {return { status: 1, msg: "更新成功" }} else {return { status: 0, msg: "更新失败" }}
}

uni-app 即时聊天:朋友圈相关推荐

  1. 如何在App中实现朋友圈功能之四在朋友圈中添加发送图片功能——箭扣科技Arrownock

    如何在App中实现朋友圈功能 之四 在朋友圈中添加发送图片功能 实现概念: 当用户在界面点击发送按钮的时候,如果已经有选择好的图片,我们的做法是先上传图片到服务器,再将图片Id作为Post的属性上传. ...

  2. 如何在App中实现朋友圈功能之二快速实现用户信息的自定义——箭扣科技Arrownock...

    如何在App中实现朋友圈功能 之二 快速实现用户信息的自定义 自我关联社交元素: anSocial中很多的社交元素API,如帖子(Post).相册(Album).文件(File)等,这些API的可选参 ...

  3. 如何在App中实现朋友圈功能之三快速实现双向好友功能——箭扣科技Arrownock

    如何在App中实现朋友圈功能 之三 快速实现双向好友功能 在社交APP的使用中,用户相互添加好友是一个必要的场景,本期技术分享在这里给大家介绍如何利用AnSocial的Friend来实现微博粉丝和微信 ...

  4. 尤里先生查看陌生人朋友圈教程_微信APP看陌生人朋友圈的操作教程

    说到微信APP,估计大家都是熟悉的,那么微信APP看陌生人朋友圈的基础操作各位了解吗?下文就是微信APP看陌生人朋友圈的教程.不懂的各位一起来学习吧! 首先,我们登录微信,打开通讯录. 查找你很久没有 ...

  5. 如何在App中实现朋友圈功能之六快速实现下拉加载朋友圈功能——箭扣科技Arrownock

    如何在App中实现朋友圈功能 之六 快速实现下拉加载朋友圈功能 实现逻辑: 根据上次下拉刷新的时间从数据库加载出limit(比如10)条post,判断总数据条数: a. 如果总数据大于limit条,清 ...

  6. 颠覆QQ, 干掉微信? 腾讯内测全新社交App,会是下一款国民社交APP吗? 微信朋友圈可斗图了!...

    综合自  中国基金报.上游新闻,马哥运维, 编辑:可可 拥有微信.QQ 两大社交平台的腾讯,近日,悄悄地内测一款拓展新朋友产品:朋友App.微信也在重大改版中. 这款产品尚处于测试当中,并未正式上架, ...

  7. BOOS 机器人智能回复 智能聊天 朋友圈 加粉 采集ID

    新型嗅探加好友,抗封,成功率高. BOOS 黑老板 是一款xposed 插件,用来方便使用微信的各种功能,实现各种自动化,提高生产力.  + 自动切换帐号 + 模拟定位 + 添加,导出附近的人  + ...

  8. ReactNative聊天APP实战|仿微信聊天/朋友圈/红包界面

    项目简介 最近一直在研究RN技术,想着找个项目练练手,之前就有使用vue.react技术开发过聊天项目,这次就使用reactNative技术开发个仿微信RN版.是基于 react-native+rea ...

  9. uniapp 打包H5,打包小程序,打包app分享到微信聊天、朋友圈

    1.uniapp打包H5操作手法:Hbuilder->发行->网站-PC-WEB端或手机端H5访问,需要填写个访问域名,即发布后访问的域名路径 2.uniapp打包H5配置注意事项:uni ...

  10. uni-app 即时聊天

    项目介绍 前段时间在B站看到了有一个UP主在讲uni-app即时聊天的项目(逸刻时光),在看了这个视频之后,感觉还是挺有兴趣的,所以在看他的讲解视频之后,就自己动手写了这个即时聊天项目,在样式方面跟( ...

最新文章

  1. 递归查找具有特定扩展名的文件
  2. pytorch 图像归一化
  3. 深入探讨 Java 类加载器
  4. linux获取网卡的ip,Windows和Linux系统下获取多网卡的ip地址
  5. 优化器 - tf.keras.optimizers.SGD()【TensorFlow2入门手册】
  6. golang beego快速入门示例(单文件hello.go)
  7. 揭晓新版《Windows Sysinternals实战指南》读书积赞活动
  8. emacs扩展功能_3个用于组织的Emacs扩展
  9. makefile教程链接
  10. SQL2005服务器上安装SQL2008失败
  11. Xmanager注册码激活教程
  12. vs2015——拖动选项卡导致软件崩溃重启
  13. Eclipse 远程开发插件 RSE 及远程登录
  14. 分析classpath、path、JAVA_HOME的作用及JAVA环境变量配置(转发博客园)
  15. linux查看气质系统文件命令,气质_ITPUB博客
  16. 赶集网遭遇“倒闭”传闻
  17. 无烦恼厨房android版,无烦恼厨房安卓版
  18. 线性回归理解(附纯python实现)
  19. 第46篇-网易易盾滑块请求参数分析【2022-11-16】
  20. iframe简单用法

热门文章

  1. 当下的事,就是最重要的事,安下心,用良知去处理
  2. ADRC——ESO扩张状态观测器simulink实现(含代码)
  3. 数据分析(2):多维度拆解法
  4. kube-proxy模式之iptables
  5. debian java pick up_ubuntu15.04安装java的时出现Picked up JAVA_TOOL_OPTIONS: -jav
  6. 2018年前端年度工作总结
  7. 栈练习之Example005-检查一个程序中的花括号、方括号和圆括号是否配对
  8. 整理全菜30篇学习vue和脚手架的笔记
  9. 阿里巴巴回购雅虎股权 雅虎收购变成三巨头博弈
  10. jQuery小游戏——小鸟飞行闪躲