前言
上一篇中簡單講解了用Redis緩存在線用戶邏輯。篇幅也比較小,本篇將詳細實現用戶的上線下線通知、圖片效果轉換功能。而且,代碼和開發思路都會詳細介紹。
效果展示
目前有三個用戶,user1,user2,user3.下圖會簡單展示用戶上線,下線的消息推送效果。
圖一:用戶1登錄,此時好友均不在線。(頭像為灰色,谷歌瀏覽器)
圖二:用戶1登錄(打開360瀏覽器模擬用戶1登錄),此時谷歌瀏覽器用戶收到該用戶上線通知,圖標亮起
圖三:用戶2登錄(打開搜狗瀏覽器模擬用戶2登錄),此時谷歌瀏覽器用戶收到該用戶上線通知,圖標亮起
圖四:三個用戶均在線(三個瀏覽器截圖)
圖五:谷歌瀏覽器用戶下線(其他兩個用戶收到下線消息,頭像變黑白)
實戰講解
不完美的地方:
1.用戶在線或者下線會有不准確性,需要重新刷新頁面才可以
2.群內用戶圖標暫時還未處理
3.打開聊天窗口的圖標也未作處理(如果用戶不在線,圖標還是亮的)
現在進入正題,所有后台的操作均是為了給前端提供數據。那么前端只要做一件事情即可,接收消息,根據用戶上下線改變用戶頭像的狀態。我的方法是用了一個第三方的js。官網:https://en.wikipedia.org/wiki/Grayscale,我把js拷貝到本地,並且封裝成layui可引用的js。
正如之前所講的,我們定義一個gray插件,並且暴漏grayscale對象。小伙伴們不要忘了在首頁上配置上該js。
//自定義模塊 layui.extend({ signalr: '/scripts/signalr/signalr', autohub: '/scripts/signalr/autohub',//自動生成的 hub: '/Scripts/signalr/hub', gray:'/scripts/gray'//控制圖片黑白的js });
然后,我們要做的就很簡單了。想把哪個圖片置灰,就調用類似如下方法,那么圖片就會替換為一個base64的灰色圖片了,效果在上邊的截圖中,相信大家已經看到了。
var imgs = $("img[data-status='hide']"); if (imgs.length) {
grayscale(imgs); }
大家都知道,剛進入layim界面的時候,我們會有好友列表信息。那么上一篇講到的用Redis存儲在線用戶列表就能派上用場了。我們找到base方法(獲取用戶基礎信息,好友信息,群組信息等)
//用戶組信息 var rowFriendDetails = ds.Tables[2].Rows.Cast<DataRow>().Select(x => new GroupUserEntity { id = x["uid"].ToInt(), avatar = x["avatar"].ToString(), groupid = x["gid"].ToInt(), remarkname = x["remarkname"].ToString(), username = x["nickname"].ToString(), sign = x["sign"].ToString(), //status之前的字段是為空的,現在我們把他的在線狀態加上,IsOnline方法接收一個userid參數,從Redis緩存中讀取該用戶是否在線並返回 status = LayIMCache.Instance.IsOnline(x["uid"].ToInt()) ? "online" : "hide" }).OrderByDescending(x => x.status);//這里要根據用戶是否在線這個字段排序,保證在線用戶都在好友列表最上邊
注釋的部分就是改動的地方,這樣我們在看一下初始化layim之后返回的json格式信息。
看到畫紅框的地方了吧,我的兩個好友都不在線(hide)。但是呢,圖標還是亮的,怎么辦,那就需要等數據加載完之后我們用上邊的處理圖標的js處理一下。沒錯,又要改layim代碼了。(PS:官方是不建議改的哈,否則升級不好整合)好多同學想要改代碼無從下手,我簡單說一下我的改代碼思路。其實無論js要干嘛,最終它還是為html服務,所以,我們找到用戶頭像的標簽:
上圖呢是處理過的圖片,不過沒關系,我們只要關心,這個img標簽在哪里就可以了。(注意:data-status是我加的,也就是說,改源代碼就是加了這么個東西)然后我們找到layim代碼,從模板里面找就可以了。
紅框的地方就是我改動的地方。改完它之后,我們在看主界面,這樣我們就知道img標簽中,哪個是需要處理成黑白圖片的了。然后在ready方法中調用:
initStatus: function () { //循環檢測需要置為黑白的頭像 //這里用interval的原因是,圖片可能還么加載完導致黑白效果不出的問題 this.userAvatarInterval = setInterval(function () { //獲取到帶 hide標簽的img對象 var imgs = $("img[data-status='hide']"); if (imgs.length) { //設置黑白效果 grayscale(imgs); //停止循環 clearInterval(other.userAvatarInterval); } }, 200); //超過五秒后停止(保險起見) setTimeout(function () { clearInterval(other.userAvatarInterval); }, 5000); },
這樣我們就完成了初始化之后,知道哪個好友在線,哪個好友不在線了。現在有什么問題呢,很顯然,加入我現在的好友都不在線,那么圖標都是黑色的。如果某個好友上線了,我不刷新頁面,但是它的圖標還是黑色的對吧。所以,我們就要用到singalR了,回歸消息推送,下面我們要做的就是當用戶上線或者下線之后給自己的好友發送消息推送。因為,已經用上Redis了,所以,干脆每個人的好友列表我也同樣用Redis來保存。不過不同的是,這個好友列表緩存需要時時更新,比如加好友之后,刪好友之后等。這里呢,我沒做那么麻煩,我設置了一天的過期時間,這樣,每一天過后才更新。看一下獲取好友列表代碼:
#region 獲取某個用戶的好友列表 /// <summary> /// 獲取某個用戶的好友列表 /// </summary> /// <param name="userid">用戶ID</param> /// <returns>返回格式如下 ""或者 "10001,10002,10003"</returns> public string GetUserFriends(int userid) { //先讀取緩存 var friends = LayIMCache.Instance.GetUserFriendList(userid); //如果緩存中沒有 if (friends == "") { //從數據庫讀取,在保存到緩存中 friends = _dal.GetUserFriends(userid); LayIMCache.Instance.SetUserFriendList(userid, friends); } return friends; } #endregion
由於灰色頭像是base64的,如果用戶上線,我們需要把該用戶原來的頭像傳給前端,然后替換該頭像。所以,這個緩存我暫時就把頭像信息也保存進去了(業務耦合了)。看一下Redis中的值:
如圖所示,用戶10003的好友為:10004,10005. 值組成格式為:userAvatar + $LAYIM$ + friendids 。同樣,我們在HubServer中增加發送用戶上下線消息的方法:
/// <summary> /// 發送用戶上下線的消息 /// </summary> public static void SendUserOnOffLineMessage(string userId,bool online=true) { int userid = userId.ToInt(); //1.獲取用戶的所有好友 var users = LayimUserBLL.Instance.GetUserFriends(userid); //沒有好友,不發消息 var friends = users.Split(new string[] { "$LAYIM$" }, StringSplitOptions.RemoveEmptyEntries); if (friends.Length == 2) { var avatar = friends[0]; var notifyUsers = friends[1]; //2.發送用戶上下線通知 UserOnOffLineMessage message = new UserOnOffLineMessage { avatar = avatar, online = online, userid = userid }; SendMessage(message, notifyUsers, ChatToClientType.UserOnOffLineToClient, true); } }
這個方法,就是獲取緩存里面的用戶好友信息和頭像信息,然后組成一個新的Message,並發送消息。那么這個方法在哪里調用呢。我們還是回到LayIMHub代碼中。
/// <summary> /// 建立連接 /// </summary> /// <returns></returns> public override Task OnConnected() { //將當前用戶添加到redis在線用戶緩存中 LayIMCache.Instance.OperateOnlineUser(CurrentOnlineUser); //發送用戶上線消息 HubServer.HubServerHelper.SendUserOnOffLineMessage(CurrentUserId); return Clients.Caller.receiveMessage("連接成功"); } /// <summary> /// 失去連接 /// </summary> /// <param name="stopCalled"></param> /// <returns></returns> public override Task OnDisconnected(bool stopCalled) { //將當前用戶從在線用戶列表中剔除 LayIMCache.Instance.OperateOnlineUser(CurrentOnlineUser, isDelete: true); //發送用戶下線消息 HubServer.HubServerHelper.SendUserOnOffLineMessage(CurrentUserId, online: false); return Clients.Caller.receiveMessage("失去連接"); } /// <summary> /// 重新連接 /// </summary> /// <returns></returns> public override Task OnReconnected() { //將當前用戶添加到redis在線用戶緩存中 LayIMCache.Instance.OperateOnlineUser(CurrentOnlineUser); //發送用戶上線消息 HubServer.HubServerHelper.SendUserOnOffLineMessage(CurrentUserId); return Clients.Caller.receiveMessage("重新連接"); }
哈哈,是不是發現利用OnConnected,OnReconnected,DisConnected方法能做好多事情啊。然后我們在Index頁面增加一個分支就可以了。
我們先運行一下,看看效果:
我們把消息定義成統一格式是有好處的,這樣我們可以根據自己的業務進行處理,接受消息就一個接口:receiveMessage。 可以看到msg里面有用戶頭像,和在線狀態,還有用戶id。得到了這些信息之后,我們去處理一下就OK了。
//重新設置用戶頭像,黑白或者亮 resetUserAvatar: function (obj) { var avatar = obj.avatar, online = obj.online, userid = obj.userid;
//這句代碼是定位到該用戶下的頭像 var imgObj = $('#layim-friend' + userid).find('img'); if (imgObj.length) { if (obj.online) { //如果上線了,將頭像換成原來的頭像,即非黑白頭像 imgObj.attr('src', avatar); } else { //將頭像置黑 grayscale(imgObj); } } }
到此為止,功能開發結束。
總結
本篇內容相對來說比上一篇多一點,涉及內容有,Redis緩存,更新等。圖片黑白處理,SignalR消息處理。以及源代碼閱讀。
總之呢,最重要的部分就是SignalR這個推送如果穩定了,只要消息能夠送達客戶端,那么任由客戶端去處理了。大家還有注意學會自定義消息內容。保證自己的業務能夠順暢。
下篇預告:無(我也不知道寫啥,到時候在看吧)
想要學習的小伙伴,可以關注我的博客哦,我的QQ:645857874,Email:fanpan26@126.com
GitHub:https://github.com/fanpan26/LayIM_NetClient/ (無source源代碼哦,因為layim是需要授權的,請各位諒解)