ASP.NET SignalR 與 LayIM2.0 配合輕鬆實現Web聊天室(九) 之 用 Redis 實現使用者線上離線狀態訊息處理(一)

丶Pz發表於2016-08-30

前言

  上一篇中簡單講解了用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是需要授權的,請各位諒解

相關文章