前言
在上一篇 spring boot + layim + t-io 文件上传、 监听用户状态的实现 中,已经介绍了两个小细节:用户的在离线状态和群人数的状态变化。今天的主要内容就是用户加好友的实现。
简介
加好友,大家用过qq都知道,无非是发起好友申请,对方收到消息通知,然后处理。不过,本篇只讲前半部分,消息通知的处理留到下一篇去讲。因为内容有点多,怕是一时半会消化不了。在介绍主体流程之前,先给大家介绍一下准备工作。
准备工作
首先,为了让数据更贴近实战,所以我用了比较“真实”的用户数据。结合fly模板,完善了用户中心头部的用户信息的数据绑定。数据绑定部分判断了是否已经是好友,来决定是否出现“加为好友”的按钮。示例如下,当用户自己看到自己的主页时,是这样的:
看到非好友的用户主页,是这样的:
绑定数据部分,简单给大家介绍一下,就是用thymleaf模板绑定。后台访问页面的时候,将 model 赋值即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/** * 属性赋值 * */ private void setmodel(user user,model model){ long currentuserid = getuserid(); long visituserid = user.getid(); //是否是自己 boolean isself = currentuserid == visituserid; //两个用户是否已经是好友 boolean isfriend = groupservice.isfriend(currentuserid,visituserid); map<string,object> usermap = new hashmap<>( 8 ); usermap.put( "avatar" ,user.getavatar()); usermap.put( "name" ,user.getusername()); usermap.put( "addtime" , timeutil.formatdate(user.getcreateat())+ " 加入" ); if (user.getsign()== null ||user.getsign().length()== 0 ) { usermap.put( "sign" , "" ); } else { usermap.put( "sign" , "(" + user.getsign() + ")" ); } usermap.put( "uid" ,user.getid()); usermap.put( "self" ,isself || isfriend); model.addattribute( "user" ,usermap); } |
然后页面上,将model中的数据取出来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<div class = "fly-home" style= "background-image: url();" > <input type= "hidden" th:value= "${user.uid}" id= "visituid" /> <img src= "" th:src= "${user.avatar}" th:alt= "${user.name}" /> <h1> <p th:text= "${user.name}" ></p> <i class = "iconfont icon-nan" ></i> </h1> <p class = "fly-home-info" > <!--<i class = "iconfont icon-zuichun" title= "飞吻" ></i><span style= "color: #ff7200;" > 67206 飞吻</span>--> <i class = "iconfont icon-shijian" ></i><span th:text= "${user.addtime}" ></span> <!--<i class = "iconfont icon-chengshi" ></i><span>来自杭州</span>--> <i class = "iconfont icon-qq" th: if = "${user.self==false}" ></i><a lay-event= "addfriend" href= "#" rel= "external nofollow" title= "添加ta为好友" th: if = "${user.self==false}" >加为好友</a> </p> <p class = "fly-home-sign" th:text= "${user.sign}" ></p> </div> |
ok,以上就是简单的准备工作。想了解详情代码的可以去文末的github地址去搜寻。
发起好友申请
我们先根据layim的业务分析。首先,要知道我们要加谁(toid)为好友。然后在加上一个备注(remark)。这些东西交给后台就ok了。为了避免连表查询,对于系统消息的存储我做了用户名和用户头像的冗余。表主要包含字段:用户id,用户头像,用户名,被申请用户id,申请时间,申请类型,备注,已读等其他属性。
所以,发起好友申请就很简单了。就是一个添加功能,前端传的就是被申请人用户id和申请备注,后端组织数据插入到数据库,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/** * 提交好友申请 * */ public jsonresult savefriendapply( long toid,string remark){ remark = htmlutils.htmlescape(remark); contextuser user = shiroutil.getcurrentuser(); long userid = long .parselong(user.getuserid()); int record = applyrepository.countbytoidanduidandtypeandresult(toid,userid,applytype.friend, 0 ); if (record > 0 ){ return jsonresult.fail( "已经申请过" ); } apply apply = new apply(); apply.settype(applytype.friend); apply.settoid(toid); apply.setremark(remark); apply.setuid(userid); apply.setavatar(user.getavatar()); apply.setname(user.getusername()); apply.setread( false ); apply.setresult( 0 ); return saveapply(apply); } |
ok,申请完了,下面我们要做啥?没错,通知对方,喂,我向你发送了申请,快快处理。在这里呢我遇到了一个问题。由于springboot程序占用端口 8080,而t-io占用端口8888,也就是说,如果我想在8080端口的业务中主动调用8888的服务推送,我不知道如何获取相应的channelcontext。不过经过询问作者之后,一句话解决了我的问题。
拿到 servergroupcontext ,问题迎刃而解。
在之前的程序启动的时候注册了 layimwebsocketstarter 这个bean。所以,在8080业务端如果能拿到它的话就没问题了。
得到 layimwebsocketstarter ,就能得到 servergroupcontext, 然后就能在服务端做主动推送了。
当然可能没有开发过这个东西,对于上文中的问题不是很理解,没关系,其实我就想说明,如果从服务端主动向客户端推送消息的话,使用servergroupcontext即可。
服务端主动推送
以下代码在 com.fyp.layim.im.common.util.pushutil 中
ok,接上文,我们按照步骤来。
第一步,获取 layimwebsocketstarter 。
1
2
3
4
5
6
|
/** * 获取starter */ private static layimwebsocketstarter getstarter(){ return (layimwebsocketstarter)springutil.getbean( "layimwebsocketstarter" ); } |
第二步,获取 servergroupcontext
1
2
3
|
private static servergroupcontext getservergroupcontext(){ return getstarter().getservergroupcontext(); } |
第三步,获取 channelcontext。
1
2
3
4
5
6
7
8
9
|
/** * 获取channelcontext * */ private static channelcontext getchannelcontext(string toid) { servergroupcontext context = getservergroupcontext(); //找到用户 channelcontext channelcontext = context.users.find(context, toid); return channelcontext; } |
第四步,发射,这里的代码就和聊天中的那部分代码差不多了。核心部分就是,获取channelcontext,然后给他发送消息。如果不在线就不用管。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/** * 服务端主动推送消息 * */ public static void pushapplymessage(string toid) { logger.info( "执行到了发送方法:pushapplymessage" ); layimtoclientnoticemsgbody body = new layimtoclientnoticemsgbody(); channelcontext channelcontext = getchannelcontext(toid); //先判断是否在线,再去查询数据库,减少查询次数 if (channelcontext != null && !channelcontext.isclosed()) { int count = getapplyservice().getunreadmsgcount( long .parselong(toid)); body.setcount(count); push(channelcontext, body); } } /** * 服务端主动推送消息 * */ private static void push(channelcontext channelcontext,object msg) { try { wsresponse response = bodyconvert.getinstance().converttotextresponse(msg); aio.send(channelcontext, response); } catch (ioexception ex){ } } |
现在推送已经搞定了,那么什么时候推送呢?由于这个系统消息的推送可以不用那么即时,于是我看了下,springboot里面有类似的事件机制,于是乎 applyevent 就诞生了。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class applyevent extends applicationevent { public applyevent(object source) { super (source); } private long toid; public long gettoid(){ return toid; } public applyevent(object source, long toid) { super (source); this .toid = toid; } } |
在创建一个listener,监听事件。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class applylistener implements applicationlistener<applyevent> { private logger logger = loggerfactory.getlogger(applylistener. class ); @override public void onapplicationevent(applyevent applyevent) { new thread(){ public void run(){ long toid = applyevent.gettoid(); //这里就要调用上文中的推送了 pushutil.pushapplymessage(toid.tostring()); } }.start(); } } |
不过我有个疑问,发现listener中执行的时候是同步的。后来加了@async 和@enableasync 也没用,于是我就用了new thread().start()实现异步,确保不影响主要申请流程。(这是个疑问,自己没搞明白的地方)
最后,别忘了在application启动的时候把listener加上。
1
2
3
4
5
6
7
8
9
|
public static void main(string[] args) { springapplication springapplication = new springapplication(layimapplication. class ); /** * 这里监听增加listener,listener才会触发 * applylistener 是监听好友申请的事件 * */ springapplication.addlisteners( new applylistener()); springapplication.run(args); } |
功能拼接
马上就要成功了,我们在把事件串起来,在好友申请成功之后,发布事件。
1
2
3
4
5
6
7
8
9
10
11
12
|
/** * 好友申请 * */ @postmapping (value = "/apply-friend" ) public jsonresult apply( @requestparam ( "toid" ) long toid, @requestparam ( "remark" ) string remark){ jsonresult result = applyservice.savefriendapply(toid, remark); //申请成功,发布申请事件,通知 toid处理消息,如果不在线,不会进行处理 if (result.issuccess()){ applicationcontext.publishevent( new applyevent( "apply" ,toid)); } return result; } |
功能演示
讲了那么多,给大家看一下成品效果。(用户场景:安小鸟加皇上为好友,皇上接收消息并查看)
皇上收到消息,系统弹出左下角的小数字4。(调用 layim.msgbox(msgcount) 方法)
皇上点开消息盒子:
皇上收到了四位爱妃的申请,寝食难安,他会怎么处理呢?欲知后事如何,且听下回分解~~~
总结
本篇主要介绍了一个加好友的流程的实现。
- 好友申请按钮出不出现取决于用户是否为自己,是否已经是好友。(后端也要做验证)
- t-io的服务端主动推送,如何调用。关键词: servergroupcontext
- event的使用,除了applicationevent,还可以拓展其他类型,如消息队列,eventbus等。
- 各种细节处理,比如先判断对方是否在线,在去查询数据库。或者结合缓存等
- 由于是自己摸索,难免有代码繁杂混乱之处,
文中代码地址: https://github.com/fanpan26/springbootlayim
以上所述是小编给大家介绍的springboot+layim+t-io 实现好友申请通知流程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
原文链接:https://my.oschina.net/panzi1/blog/1586421?utm_source=tuicool&utm_medium=referral