Node.js——仿微信网页版搭建聊天室(二)

将聊天记录存储到数据库中

上文只讲到在线聊天的操作,并未涉及到数据库操作

需求分析
  1. 使用FLEX布局设计微信交互界面,要去移动端和PC端通用;
  2. 具有添加朋友的功能,即用户登录网站后,可以将该网站的用户添加为朋友;
  3. 用户登录后,系统取出朋友列表显示;并且如果存在离线消息的话,取出显示;
  4. 用户点击朋友标签后,可以发送消息给朋友;若朋友在线,则直接发送,若朋友不在线,则将消息保存到数据库中;
  5. 用户与A好友通话过程中,如果收到B好友的消息,则可以显示未浏览的消息数量;
  6. 用户可以查询与某朋友的聊天历史记录;

Mongodb的schema设计

用户表包含用户名、密码、以及好友三个关键信息,其中好友为引用,指向其他用户表

var userSchema = new Schema({
    username: String,
    password: String,
    imgUrl:String,
    friends:[{type:Schema.Types.ObjectId, ref:'User' }],
});

最为关键的聊天记录表为如下格式

var chatPersonSchema = new Schema({
    personOne:{
        type:Schema.Types.ObjectId,
        ref:'User'
    },
    personTwo:{
        type:Schema.Types.ObjectId,
        ref:'User'
    },
    personOneNotRead:Number,
    personTwoNotRead:Number,
    children: [{
        from:{
            type:Schema.Types.ObjectId,
            ref:'User'
        },
        to:{
            type:Schema.Types.ObjectId,
            ref:'User'
        },
        message:String,
        time:{type:Date,default:Date.now()}
    }]
})
聊天记录表的设计分析
  • personOneperosonTwo
    • 意义:personOne和personTwo分别表示参与一对一聊天的两个人
    • 解析:参与聊天的双方的身份在不断变换,所以一个包含聊天对象A与聊天对象B的聊天记录不能以fromto作为标识符.
  • personOneNotReadpersonTwoNotRead
    • 意义:personOneNotRead表示由personOne和personTwo组成的聊天中personOne没读的消息数目,personTwoNotRead类同.
    • 解析:消息未读是相对的,A给B发消息,那么A的未读消息肯定为0,B就需要根据当时的情况来判断.
  • children
    • 意义:该数组存储消息的内容与发送方和接收方,from代表发送方,to代表接收方,message代表消息.
    • 解析:每当有新消息产生时将消息存入数组中

如何将消息存储入数据库

        var chatPerson =require('./schema/chatHistory');
        socket.on('message',function (obj) {
            var id1,id2;        
            //id1为posterid,id2为receivererid
            async.series({
            //这里不应该用series函数,而应该用parallel
                fuck1:function(done){
                    User.findOne({username:obj.poster},function(error,doc){
                        if(!error){
                            if(doc!=null){
                                id1 = doc._id;
                            }
                            done(null,id1);
                        }
                        else{
                            done(error,null);
                        }
                    });
                },
                fuck2:function(done){
                    User.findOne({username:obj.receiver},function(error,doc){
                        if(!error){
                            if(doc!=null){
                                id2 = doc._id;
                            }
                            done(null,id2);
                        }
                        else{
                            done(error,null);
                        }

                    });
                },
            //fuck1和fuck2都是通过poster和receiver的name来获取对应id
            },function(error,result){
                if(!error)
                {
                    chatPerson.findOne({$or: [
                        { personOne: result.fuck1, personTwo: result.fuck2},
                        { personOne: result.fuck2, personTwo: result.fuck1}]}).exec(function (err,doc) {
                        //因为聊天双方身份不确定,只能用$or来作为查询条件
                        if(doc==null){
                            //如果doc查不到,则创建一个对应poster和receiver的聊天对象,并设置未读消息记录为0
                            var shit = new chatPerson({
                                personOne:result.fuck1,
                                personTwo:result.fuck2,
                                personOneNotRead:0,
                                personTwoNotRead:0,
                            })
                            shit.save(function (err) {
                                if(err){
                                    console.log('保存失败');
                                }
                                chatPerson.update({$or: [
                                    { personOne: result.fuck1, personTwo: result.fuck2},
                                    { personOne: result.fuck2, personTwo: result.fuck1}]}, {$addToSet:{'children':
                                {
                                    from:id1,
                                    to:id2,
                                    message:obj.message,
                                    time:Date.now()
                                }},$inc: {"personTwoNotRead": 1}},function (err,doc) {

                                });
                                //插入消息
                                console.log('success');
                            })
                        }else{
                            //如果doc已经存在,那就不需要再创建一次
                            chatPerson.update({$or: [
                                { personOne: result.fuck1, personTwo: result.fuck2},
                                { personOne: result.fuck2, personTwo: result.fuck1}]}, {$addToSet:{'children':
                            {
                                from:id1,
                                to:id2,
                                message:obj.message,
                                time:Date.now()
                            }} },function (err,doc) {
                                if(doc.personOne==id2){
                                    chatPerson.update({$or: [
                                        { personOne: result.fuck1, personTwo: result.fuck2},
                                        { personOne: result.fuck2, personTwo: result.fuck1}]},{$inc:{'personOneNotRead':1}},
                                        function (err,doc) {

                                    })
                                }else{
                                    chatPerson.update({$or: [
                                            { personOne: result.fuck1, personTwo: result.fuck2},
                                            { personOne: result.fuck2, personTwo: result.fuck1}]},{$inc:{'personTwoNotRead':1}},
                                        function (err,doc) {
                                        })
                                }
                            });
                        }
                    });
                }
                else{
                    console.log('err');
                }
            });
            if(client.hasOwnProperty(obj.receiver)){
                client[obj.receiver].emit('receive',obj);
            }
            socket.emit('send',obj);
        })

清理未读消息操作与上类似,只不过是将perosonXXXNotRead设置为0而已

如何将查询到的聊天记录合并入查询的好友信息

聊天记录和用户的信息分属于不同的表,并且无法用ref方法引用到,所以在实现这个功能的时候遇到了很多困难

exports.findUsrInfo = function (req, cb) {
    User.findOne({username:req.session.user.username}).populate({path:'friends',select:"username imgUrl"})
        .exec(function (err,doc) {
        //通过session中的user的信息,查询到该客户端对应的用户信息
        var doc = (doc !== null) ? doc.toObject() : '';
            //console.log(doc);
        if(err){
            cb(true,err);
        }else {
            //console.log(doc);
            var index = 0;              //friends下标
            async.eachSeries(doc.friends, function (item,callback) {
            //eachSeries按顺序遍历执行doc.friends数组中的元素,each则是并发执行,如果函数中的操作带有回调函数,并且该操作依赖于上文提供的数据(例如friends的下标index),则会发生错误
                async.waterfall([
                //water按顺序依次执行其中的函数,并且上一个函数的结果为下一个函数的参数
                    function (done) {
                        chatPerson.findOne({ $or: [{ personOne: doc._id, personTwo: item._id},  { personOne: item._id, personTwo: doc._id}] },
                            function (err,history) {
                                done(null, history);
                                //done函数进入下一个函数
                            })
                    },
                    function (history, done) {
                    //这两个函数可以合并为一个函数,先findOne再popluate
                        if(history!=null){
                            var opts = [
                                {path:'personOne',select:'username'},
                                {path:'personTwo',select:'username'},
                                {path:'children.from',select:'username'},
                                {path:'children.to',select:'username'}
                            ];
                            history.populate(opts, function(err, fuckyoubitch) {
                                //console.log(fuckyoubitch);
                                if(fuckyoubitch!=null){
                                    doc.friends[index].chatHistory=fuckyoubitch;
                                    //通过下标定位,将聊天记录存入对应的好友信息中
                                }
                                ++index;
                                done(null, '');
                            });
                        }else{
                            console.log("dbhelper"+index);
                            ++index;
                            done(null, '');
                        }
                    },
                ], function (error, result) {
                    callback();
                })
            },function (err) {
                if(err){
                }else{
                    //console.log(doc);
                    cb(true,doc);
                }
            });
        }
    })
}

题外话:js文件如何获取从服务器端发送到express框架的数据

在视图文件下的布局文件中写下如下代码

<script>
    window.scriptData = JSON.stringify({{{scriptData}}});
    window.scriptData=eval("("+window.scriptData+")");//转换为json对象
</script>

于是可以在其他的js文件中获取服务器发送过来的数据了:)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容