記得從高中上課時常常偷偷的和同窗們使用qq進行聊天,那時候常常須要進行下載qq,可是當時又沒有那麼多的流量進行下載,這就是一個很尷尬的事情了,當時就多想要有一個能夠進行線上聊天的網站呀,不用每次痛苦的進行藍牙傳送軟件了,如今,我從事了IT這個行業,便想要去實現當初的那個夢想吧。畢竟,不去努力的實現夢想,你會從一個夢想家變成一個幻想家。html
因爲從事的是前端工做,界面什麼的對我來講是so easy,後臺部分固然是選擇了node.js,通過分析呢,數據庫部分選擇了mongoose,很早以前作個一個簡易的聊天室,採用的是ajax輪詢後臺的方式實現的,這種方式對於服務器的壓力很大,並且在用戶量上去的的時候也會出現卡頓的現象,客戶端和服務器通訊使用的是webSocket協議。使用WebScoket協議,咱們能夠實現客戶端和服務器的全雙工通訊,實現實時的服務器向客戶端的消息推送,信息返回。因爲瀏覽器對於WebScoket的支持性不是特別廣泛,因此咱們使用的是封裝了WebScoket的socket.io,socket.io能夠適配於全部的瀏覽器,所以,咱們使用socket.io來實現客戶端和服務端的通訊。前端
選中MongoDB是由於它的簡便性以及易操做性,聊天系統並非說必需要嚴格遵照例如MySQL的CUID等準則,它容許咱們能夠延遲個幾百毫秒收到聊天信息,其優勢主要有:node
(1)mongodb數據庫體積較小,系統運行時較爲靈活。 (2)能夠提供對於任意類型的數據的查詢。 (3)使用相應的技巧能夠下降咱們代碼量以及提高查詢速度。 (4)mongodb能夠對咱們以前的表格進行預加載。
咱們簡要的選擇了咱們要使用的技術,那麼咱們還須要對要作的產品進行分析設計,分析出來咱們要作的產品都有哪些功能,咱們要怎麼樣去實現這些功能,根據以前對於聊天系統的認知,咱們設置系統的功能暫時分爲後臺管理系統和前臺聊天室。其中後臺管理系統有當前用戶管理功能、聊天信息管理功能、而前臺聊天室擁有添加好友、刪除好友、好友私聊、羣組羣聊、修改密碼、修改我的信息等功能。大概的功能模塊圖以下所示:jquery
通過上部分的分析,咱們隊系統有了初步的認知。該系統分爲兩個主體,用戶登陸後的主頁即爲聊天室主頁如圖1所示。系統管理員登陸後的主頁即爲adminChat.ejs如圖2所示。
git
圖1 聊天室主頁
圖2 後臺管理主頁
因爲是聊天系統,因此用戶必須先要通過登陸才能進入聊天室或者後臺管理系統。所以系統的首頁即是登陸頁,在登陸頁也能夠進行註冊。
下面以「登陸」功能進行主要講解。在登陸界面,主要使用ajax去異步判斷是否能登陸成功,若是數據庫中沒有該用戶,會提示用戶前去註冊,採用MVC設計模式。其運行界面如圖3所示。
github
圖3 登陸界面
登陸界面前臺代碼以下:web
//Form表單的提交控件代碼 <form class="form-horizontal" method="post" action="/login"> <div class="form-group row" style="margin-top: 40px; color: white;" > <label for="username" class="control-label col-sm-4" >用戶名</label> <div class="col-sm-6"> <input type="text" class="form-control" id="username" name="username" placeholder="用戶名"> </div> </div> <div class="form-group row" style="color: white;"> <label for="password" class="control-label col-sm-4">密碼</label> <div class="col-sm-6"> <input type="password" class="form-control" id="password" name="password" placeholder="密碼"> </div> </div> <div class="form-group row" style="text-align: center;"> <button type="submit" class="btn btn-primary">登錄</button> <a href="/reg" class="btn btn-primary">註冊</a> </div> </form>
其後臺主要代碼以下:ajax
//登陸操做 router.post('/login', function(req, res, next) { //判斷用戶名是否存在 User.findOne({username:req.body.username},function (error,user) { if (!user) { //用戶名不存在 error = "用戶名不存在,請先去註冊"; }else if (req.body.password !== user.password) { error = "密碼輸入錯誤"; } if (error) { req.session.error = error; //跳轉路由 return res.redirect('/login'); } req.session.success = '登陸成功'; //跳轉到首頁 req.session.user = user; if(user.username === 'weChatAdmin'){ //若是是管理員登陸,就登陸到管理員界面 res.render('adminHome',{user:user}); }else{ res.render('chatHome',{user:user}); } }) });
能夠從登陸頁直接跳轉到註冊頁,註冊時會先判斷當前的用戶名是否已經被註冊,若是沒有被註冊,就將新增的用戶信息插入到用戶列表中。註冊時爲了提升用戶體驗性,便沒有將用戶所需字段所有讓新用戶進行填寫,用戶能夠到聊天室中進行我的信息的完善。註冊界面如圖4所示。
sql
圖4 註冊界面
註冊的後臺主要代碼以下:mongodb
//註冊操做 router.post('/reg', function(req, res, next) { //1.查詢數據庫,判斷當前的用戶名是否存在 User.findOne({username:req.body.username}, function (error,user) { //用戶已經存在 if (user) { //console.log('用戶已經存在'); error = "該用戶名已存在,請從新輸入"; }else if (req.body.password !== req.body.repassword) { error = "兩次密碼輸入不一致"; //console.log("兩次密碼輸入不一致"); } if (error) { //將錯誤存入session req.session.error = error; //回到註冊頁面 return res.redirect('/reg'); }
當用戶爲普通用戶時,登陸進聊天室後,經過點擊左上角的用戶頭像進行我的信息的查看、修改。
「查看」功能的實現原理是:當點擊頭像時,觸發綁定在頭像上的點擊事件,在事件中,先異步根據用戶ID去數據庫中獲取用戶信息,而後觸發模態框彈出事件,並對模態框進行渲染,使用模態框控件來展現用戶信息。其效果界面如圖5所示。
圖5 展現我的信息界面
後臺查詢的mongoose代碼以下:
User.find({username:req.body.username},function(error,user){ if(error){ res.send({ status:8000,msg :"查詢用戶列表失敗"}); }else{ res.send({status:200,user:user}); } });
有了查看功能,便引出了修改功能,在查看界面能夠直接點擊編輯按鈕,從而觸發編輯模態框的展現事件,先異步去獲取該用戶的信息,對模態框進行渲染,而後對輸入框進行編輯,編輯完成後,點擊肯定按鈕,首先會數據框的數據進行格式判斷,而後將修改後的數據根據用戶ID保存到數據庫用戶表中。其效果界面如圖6所示。
圖6 編輯我的信息界面 使用ajax進行異步修改,js部分代碼以下所示:
$.ajax({ type:'post', dataType:'json', data:queryData, url:'/compileUserInfo', success:function(data){ if(data.status == 200){ alert(data.msg); $('#compileUserInfoModel').modal('hide');//成功後將模態框關閉 //在這裏進行input值得初始化 }else{ alert(data.msg); } }, error:function(e){ alert(e); }, })
後臺主要代碼以下所示:
router.post('/compileUserInfo',function(req,res,next){ var username = req.body.username; var oldValue = {username:req.body.username}; varnewValue= {$set:{username:req.body.username ,mail:req.body.mail,phone:req.body.phone,country:req.body.country,city: req.body.city}}; User.update(oldValue,newValue,function(err,result){ if(err){ res.send({status:8000,msg:"編輯用戶信息失敗"}); }else{ res.send({status:200, msg:"編輯用戶信息成功"}); } }) });
在聊天室左邊的我的交互框中,點擊系統設置,便找到了當前用戶的密碼修改。點擊出現修改密碼的模態框。其實現方法是先判斷用戶輸入的原密碼是否正確,而後判斷新密碼和重複輸入的密碼是否一致。經過判斷條件後根據用戶ID將用戶密碼修改成新密碼保存到數據庫中。其運行界面如圖7所示。
圖7 修改用戶密碼界面 後臺sql代碼以下:
User.update({username:username},{$set:{password:newPassword}},function(err,result){ if(err){ res.send({status:8000,msg:"修改用戶密碼失敗"}); }else{ res.send({status:200,msg:"修改用戶密碼成功"}); } });
聊天室的最右邊,是當前系統中全部的用戶列表,若是想要和某個用戶進行私聊,就必須先添加該用戶爲好友。點擊該系統用戶,會彈出模態框顯示是查看該用戶信息仍是添加該用戶爲好友。如圖8所示。如若選擇查看該用戶信息,則會彈出展現該用戶信息的模態框。當選擇添加該用戶爲好友時,則向該用戶發送好友請求,若是該用戶贊成,則二人即是好友關係,能夠進行私聊了。
圖8 添加好友界面 向特定的用戶發送好友請求,是經過socket.io來實現的,具體實現的代碼以下: 用戶列表中的聊天室用戶綁定點擊事件,在事件中會先去判斷該用戶是否已是本用戶的好友,代碼以下:
document.getElementById('userListWrapper').addEventListener('click', function(e) { var target = e.target; if (target.nodeName.toLowerCase() == 'li') { //判斷當前要添加的用戶是否已是本身的好友 $.post('/judgeFriend',{master:USERNAME,friend:target.innerHTML}, function(data,status){ if(data.status == 300){ alert(data.msg); }else{ that.socket.emit('sendFriendReq',target.innerHTML,USERNAME); } } ); //向特定的好友發送好友請求 }; }, false);
通過判斷該用戶並非你的好友,那麼這時就能夠向該用戶發送好友請求了,須要準確的定位到該用戶,從而保證不會將好友請求消息發給其餘用戶,執行下列代碼:
this.socket.emit('sendFriendReq',target.innerHTML,USERNAME);
該代碼會定位到server.js中的sendFriendReq事件,在server.js中,採用廣播機制,將這條好友請求消息發送給當前鏈接WS協議的全部用戶,每一個用戶根據請求消息中的請求對象去判斷是否是本身的請求消息,若是是,就作出相應的處理,若是不是,就將此消息進行忽略。其實現的相應邏輯代碼以下所示:
socket.on('sendFriendReq',function(toOne,fromOne){ //服務器server.js端執行的代碼。相應的去觸發每一個用戶的addFriReq事件。 socket.broadcast.emit('addFriReq',toOne,fromOne); }); //客戶端對於服務器端的addFriReq事件進行處理。 this.socket.on('addFriReq',function(toOne,fromOne){ var toOne = toOne, fromOne = fromOne; if(toOne==USERNAME){ //alert(fromOne+'請求添加你爲好友'); var confirmReq = confirm(fromOne+'請求添加你爲好友'); if(confirmReq){ //發送消息告訴fromOne贊成了好友請求,同時將該好友添加至好友列表中 that.socket.emit('agreeReq',toOne,fromOne); }else{ //發送消息告訴fromOne拒絕了好友請求 that.socket.emit('refuseReq',toOne,fromOne); } } });
圖9 刪除好友界面
聊天交友,不免會有人看誰不順眼,恰巧這我的又是該用戶的好友,這時就想要將此好友給刪了,這時在好友列表中對着該好友右鍵,即可選擇將該好友刪除。其界面如上圖9所示。
當有悄悄話想要和好友進行訴說的時候,這時候就要和該好友進行私聊了,在好友列表鼠標左鍵點擊該好友,便會看到聊天室中間彈出了私聊的窗口。它的實現原理是:點擊該好友時,觸發私聊窗口的彈出事件,而且將模型中保存聊天信息時的sayto字段設置爲當前私聊對象的username。其界面如圖10所示。
圖10 私聊好友界面
該系統的實現原理是,根據消息列表的sayto字段來進行區分聊天對象,根據sayto字段去保存或查詢出消息列表中的聊天記錄。使用ajax去進行數據的請求和保存,而且雙方會根據socket.io來指向特定的用戶去響應聊天渲染事件。Js代碼以下:
$.ajax({ type :'post', dataType:'json', url :'/getChatMsg', data : {sayto:sayto,fromto:user}, success : function(data){ if(data.status===200){ console.log(data.msg); container.innerHTML=''; for(var i=0;i<data.msg.length;i++){ var msgToDisplay = document.createElement('p'); msgToDisplay.style.color = color || '#000'; msgToDisplay.innerHTML = data.msg[i].username + '<span class="timespan"></span>' + data.msg[i].content; container.appendChild(msgToDisplay); container.scrollTop = container.scrollHeight; } } },
當系統用戶想要進行方便且快速的傳播消息時,這時用戶可使用羣聊功能,在左側羣組列表裏點擊相應的聊天羣,即可以進入到相應的羣組聊天室,在這裏全部的用戶能夠暢所欲言。效果如圖11所示。
圖11 好友羣聊界面
後臺管理員登陸的設計模式和用戶登陸模式相同,當前系統設定的有一個超級管理員,超級管理員用戶名爲「zidingyi」,密碼爲「123456」。
在超級管理員登陸時,後臺判斷到是「系統管理員」,而後就跳轉到咱們的後臺管理界面。後臺管理有三大模塊:用戶管理模塊、聊天信息管理模塊、管理員信息修改模塊、管理員地圖位置查看模塊。
「用戶管理」功能能夠進行根據用戶名的搜索查找。同時,能夠對用戶進行刪除、禁用、編輯等操做。設計原理是使用jqueryForm配合着node.js+mongoose的後臺進行數據庫中用用戶列表的檢索。其中運行圖如圖12。
圖12 後臺用戶管理界面
查看全部用戶信息的後臺sql語句爲:
User.find({},function(error,user){ res.render('adminFriendList',{userList:user}); });
點擊禁用,會在後臺將該用戶的disabled屬性設置爲true,同時,該條記錄的背景顏色會被改爲紅色表示禁止使用的帳號。若是再使用該帳號進行登陸,前臺會報錯爲該帳號已經被禁止使用。其後臺效果如圖5-13後臺用戶管理界面所示。
點擊修改,則傳遞選中行的用戶ID,主要實現和在聊天室修改我的信息相同。
點擊刪除,則傳遞選中行的用戶ID,將該用戶從用戶列表中進行刪除。
圖13 後臺禁止用戶界面
「聊天信息管理」功能能夠進行根據發送者用戶名搜索查找、也能夠根據接受者用戶名搜索查找聊天信息。其中運行圖如圖14。
圖14 後臺聊天信息管理界面
查看全部聊天信息並進行後臺分頁的mongoose語句爲:
router.post('/msgList',function(req,res,next){ var sendMsg = req.body.sendMsg; var acceptMsg = req.body.acceptMsg; var pageIndex = req.body.pageIndex; var pageSize = req.body.pageSize; var searchData = {}; if(sendMsg!=""&& sendMsg!=null){ searchData.username = sendMsg; } if(acceptMsg!="" && acceptMsg !=null){ searchData.sayto = acceptMsg; } var a = 0; var msgCount = ""; Message.find({},function(error,msgList){ msgCount = msgList.length; Message.find(searchData) .skip(parseInt(pageIndex)*parseInt(pageSize)) .limit(parseInt(pageSize)) .exec(function(error,msgList){ res.send({ msgList:msgList, totalPages:Math.ceil(msgCount/pageSize), totalElements:msgCount }); }); }); });
另外,在圖14中點擊修改按鈕,則傳遞選中行的聊天信息ID,彈出修改模態框,能夠點擊進行修改,其效果如圖15所示。
同理,在圖14中點擊刪除按鈕,則傳遞選中行的聊天信息ID,將該條消息從消息列表中進行刪除。
圖5-15 修改聊天信息界面
上述爲該系統的總體實現的部分,固然系統的完善度以及可擴展性都不算特別高,還有待進一步的開發與拓展。對於系統的擴展,有想法的童鞋能夠和我聯繫,來呀,一塊兒搞呀!!!
- 首先要安裝的是node.js環境,安裝好以後會自帶有npm
- 而後使用npm install安裝所需的各個第三方組件,這些組件在package.json中都已有,只需install就好了
- 而後須要在本地安裝MongoDB,這個安裝介紹起來講麻煩也麻煩,說簡單也簡單,並且網上已經有好多安裝教程,若是你們對於MongoDB還不是特別瞭解的話,能夠去搜索學習一下,這裏給你們推薦菜鳥教程的講解
http://www.runoob.com/mongodb/mongodb-window-install.html- 安裝好上述全部環境以後,先在本地運行起來MongoDB,而後執行node server,就能夠打開本地locallhost:3000/login進行登陸註冊了。
- 所寫的代碼也很少,還有待進一步的重構與整理,這裏上傳到了github,下邊貼出我github的地址,你們就手動的點個星吧,萬分感謝!!!
https://github.com/Blackgan3/WeChat- 固然,網頁版的聊天室,不部署到線上怎麼能行呢,本身買了個服務器來玩,如今項目已經部署到了線上,正在考慮能夠加一個機器人。哈哈
http://www.blackgan.cn
從開始決定要作一個聊天系統,本身也以爲是否是不可能,當時也很沒自信去說本身能去作一個獨立的前臺和後臺系統,可是可是想的就是懼怕什麼呢,作不出來能死嗎?若是隻是想,而不去幹,那你永遠也不會獲得什麼。因而邊去一遍調研,一遍嘗試些寫,固然白天仍是要工做的,通常都是晚上下班後寫一點,遇到不會的天天再學一點。如今系統已經粗糙的成了個大概的雛形。
當朋友使用這個系統在網上聊天,發送過去消息成功的時候,心裏是很激動的,或許這不是最好的,這個系統也不會給我帶來什麼經濟效益,但從那一刻起,我至少明白了,要敢想敢作!加油。
以爲做者寫的對你有用處的話,就賞個零花錢讓做者買個冰棍吧,感謝支持與鼓勵