美文网首页前端社团
Node.js——仿微信网页版搭建聊天室(二)

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

作者: 折柳画马 | 来源:发表于2016-10-16 20:07 被阅读656次

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

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

    需求分析
    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文件中获取服务器发送过来的数据了:)

    相关文章

      网友评论

        本文标题:Node.js——仿微信网页版搭建聊天室(二)

        本文链接:https://www.haomeiwen.com/subject/jlnoyttx.html