ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(九) 之 用 Redis 实现用户在线离线状态消息处理(一)
前言
上一篇中简单讲解了用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是需要授权的,请各位谅解)