Spring boot + LayIM + t-io 好友申请的处理以及上下线通知的实现

Java框架

浏览数:97

2020-6-1


前言

    在上一篇中已经完成了好友申请的流程。本篇就是对申请处理流程的简单讲解。核心知识点在前几篇中已经介绍过很多次了,所以本篇也算是和推送部分有关的最终篇了。后续就继续踏踏实实学习其他的东西吧。

申请处理

    申请的处理流程如下:

    同意申请:更新消息状态,新增系统消息,加为好友,推送消息

    拒绝申请:更新消息状态,新增系统消息,推送消息

代码实现

    首先,在UI上,LayIM已经做的很棒了。点击同意事件已经写好,所以要做的就是对接后台的逻辑。

    

上图弹层代码:

             layim.setFriendGroup({
                    type: 'friend'
                    , username: user.username
                    , avatar: user.avatar
                    , group: parent.layui.layim.cache().friend //获取好友分组数据
                    , submit: function (mygroup, index) {
                        req.post('/layim/apply/agree/' + id, {
                            group: mygroup
                        }, function (res) {
                            if (res.code != 0) {
                                return layer.msg(res.msg);
                            }else{
                                //将好友追加到主面板
                                parent.layui.layim.addList({
                                    type: 'friend'
                                    , avatar: user.avatar //好友头像
                                    , username: user.username //好友昵称
                                    , groupid: mygroup //所在的分组id
                                    , id: uid //好友ID
                                    , status:res.data.status//好友状态
                                    , sign: user.sign //好友签名
                                });
                                parent.layer.close(index);
                                othis.parent().html('已同意');
                            }
                        });
                    }
                });

可以注意到,这里用到了 setFriendGroup方法和addList方法。前者就是加好友的界面,后者就是将用户添加到好友列表中。

    加好友逻辑不在赘述,主要是加好友成功之后要做两件事情,第一件,通知对方“xx同意加你为好友”。第二,将当前用户信息动态的加入到对方的好友列表中。这里要注意的是,同意添加之后要返回对方好友的在线状态,以便根据状态对好友头像做置灰处理。

    判断是否在线的方法如下:(应该会有其他方法)

 /**
     * 判断一个用户是否在线
     * */
    public static boolean isOnline(long userId){
        ChannelContext channelContext = getServerGroupContext()
                .users.find(getServerGroupContext(),userId+"");
        return channelContext!=null && !channelContext.isClosed()&&!channelContext.isRemoved();
    }

    通过看源码,可以知道根据用户ID获取channelContext,是通过一个map来实现的

private ObjWithLock<DualHashBidiMap<String, ChannelContext>> map = 
new ObjWithLock<>(new DualHashBidiMap<String, ChannelContext>());

    其实本来我的写法是直接调用  map.getObj().containsKey(userId) 方法.后来查看源代码,发现还需要加锁。对锁的研究比较少,所以还是老老实实用了框架提供的find方法,然后对channelContext进行状态的判断。

/**
	 * Find.
	 *
	 * @param userid the userid
	 * @return the channel context
	 */
	public ChannelContext find(GroupContext groupContext, String userid) {
		if (groupContext.isShortConnection()) {
			return null;
		}

		if (StringUtils.isBlank(userid)) {
			return null;
		}
		String key = userid;
		Lock lock = map.getLock().readLock();
		DualHashBidiMap<String, ChannelContext> m = map.getObj();

		try {
			lock.lock();
			return m.get(key);
		} catch (Exception e) {
			throw e;
		} finally {
			lock.unlock();
		}
	}

    当调用同意添加好友接口之后,返回结果为:

    

    然后页面调用layim.addList方法,就可以将该用户动态加入到好友列表中

    

    同样我们在示范一遍,重新申请,然后申请人保留在线状态。

    

    大家看出区别了吗?不过目前这里有个小瑕疵,就是没有好友签名。不过无伤大雅,下次登录就可以了。

上下线通知

    其实从上文中的截图可以看到,头像有灰色有亮色同QQ一样来表示好友的在线状态。这个要归功于LayIM的好友属性中的 status 属性,它有onlineoffline两个值。我们可以通过一下方法实现头像的置灰功能。

layim.setFriendStatus(userId, 'offline');

layim.setFriendStatus(userId, 'online');

    下面回到我们的LayimMsgHandler当中,在处理handshake事件之后,之前已经介绍过绑定用户和绑定群组,最后呢要给所有在线的好友发送一个上线通知。具体通知方法如下:

  /**
     * 通知该用户的好友上线消息
     * */
    private void notify(ChannelContext channelContext,boolean online) throws IOException{
        long uid = Long.parseLong(channelContext.getUserid());
        //获取用户所有的好友ID
        List<String> allFriendIds = getUserService().getAllFriends(uid);
        if(allFriendIds.size()==0){
            return;
        }
        //构建消息体
        LayimToClientOnlineStatusMsgBody msgBody = new LayimToClientOnlineStatusMsgBody(uid,online);
        WsResponse statusPacket = BodyConvert.getInstance().convertToTextResponse(msgBody);

        //调用sendToAll的方法
        Aio.sendToAll(channelContext.getGroupContext(), statusPacket, filterChannelContext -> {
            //筛选掉已经移除和关闭的连接
            if(filterChannelContext.isRemoved()||filterChannelContext.isClosed()) {
                return false;
            }
            //筛选掉非当前用户好友的连接
            String channelContextUserid = filterChannelContext.getUserid();
            boolean exists = allFriendIds.stream().anyMatch(friendUserId -> 
                    friendUserId.equals(channelContextUserid));
            return exists;
        });
    }

    所以,在handshake成功之后,调用一下 notify(channelContext,true) 方法实现上线消息推送,当触发onClose方法的时候,调用notify(channelContext,false)的方法实现下线消息推送。

   @Override
    public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) 
            throws Exception {
        notify(channelContext,false);
        Aio.remove(channelContext,"onClose");
        return null;
    }

    最后呢,初始化好友列表的时候还要做一个事情,就是已经在线的应该是亮色,不在线的是灰色,还要注意在线的排序在前边。排序方法,我们让UserViewModel实现ComparablecompareTo方法。

 public void setSort(Integer sort) {
        this.status = sort==1?"online":"offline";
        this.sort = sort;
    }

    private int sort;

    @Override
    public int compareTo(Object o) {
        UserViewModel o1 = (UserViewModel) o;
        return o1.getSort().compareTo(this.getSort());
    }

    排序效果如下:

    

功能演示

    首先用“皇后”账号添加“皇上”为好友,皇上收到通知,准备处理。

    

    此时皇后的好友列表空空如也。

    

    皇上收到好友请求,打开并同意。

    

    此时,皇上的好友列表中增加了好友“皇后”

    

    而皇后的列表中也增加了皇上,并且收到了一条消息。

    

    

    至此整个好友申请处理流程结束。皇后看到皇上批准了好友请求,安心的睡去了。下线走人。

    

总结

    写了这么几篇,其实中心点就是一个,实现推送即可。毕竟业务是活的,可以随时变动,但是推送的中心思想不变。本篇没有介绍什么新东西。无非是业务上的完善。不过多看看源代码还是挺有好处的。多看多学多用,继续加油,希望我的博客能给看到的人一些小小的帮助。

    支持的小伙伴们给个star吧。虽然代码有点乱,后续肯定是要慢慢重构的啦。

    代码地址:https://github.com/fanpan26/SpringBootLayIM

    

作者:丶Pz