前言
在上一篇 spring boot + layim + t-io 文件上傳、 監(jiān)聽用戶狀態(tài)的實現(xiàn) 中,已經介紹了兩個小細節(jié):用戶的在離線狀態(tài)和群人數(shù)的狀態(tài)變化。今天的主要內容就是用戶加好友的實現(xiàn)。
簡介
加好友,大家用過qq都知道,無非是發(fā)起好友申請,對方收到消息通知,然后處理。不過,本篇只講前半部分,消息通知的處理留到下一篇去講。因為內容有點多,怕是一時半會消化不了。在介紹主體流程之前,先給大家介紹一下準備工作。
準備工作
首先,為了讓數(shù)據更貼近實戰(zhàn),所以我用了比較“真實”的用戶數(shù)據。結合fly模板,完善了用戶中心頭部的用戶信息的數(shù)據綁定。數(shù)據綁定部分判斷了是否已經是好友,來決定是否出現(xiàn)“加為好友”的按鈕。示例如下,當用戶自己看到自己的主頁時,是這樣的:
看到非好友的用戶主頁,是這樣的:
綁定數(shù)據部分,簡單給大家介紹一下,就是用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中的數(shù)據取出來。
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地址去搜尋。
發(fā)起好友申請
我們先根據layim的業(yè)務分析。首先,要知道我們要加誰(toid)為好友。然后在加上一個備注(remark)。這些東西交給后臺就ok了。為了避免連表查詢,對于系統(tǒng)消息的存儲我做了用戶名和用戶頭像的冗余。表主要包含字段:用戶id,用戶頭像,用戶名,被申請用戶id,申請時間,申請類型,備注,已讀等其他屬性。
所以,發(fā)起好友申請就很簡單了。就是一個添加功能,前端傳的就是被申請人用戶id和申請備注,后端組織數(shù)據插入到數(shù)據庫,代碼如下:
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,申請完了,下面我們要做啥?沒錯,通知對方,喂,我向你發(fā)送了申請,快快處理。在這里呢我遇到了一個問題。由于springboot程序占用端口 8080,而t-io占用端口8888,也就是說,如果我想在8080端口的業(yè)務中主動調用8888的服務推送,我不知道如何獲取相應的channelcontext。不過經過詢問作者之后,一句話解決了我的問題。
拿到 servergroupcontext ,問題迎刃而解。
在之前的程序啟動的時候注冊了 layimwebsocketstarter 這個bean。所以,在8080業(yè)務端如果能拿到它的話就沒問題了。
得到 layimwebsocketstarter ,就能得到 servergroupcontext, 然后就能在服務端做主動推送了。
當然可能沒有開發(fā)過這個東西,對于上文中的問題不是很理解,沒關系,其實我就想說明,如果從服務端主動向客戶端推送消息的話,使用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; } |
第四步,發(fā)射,這里的代碼就和聊天中的那部分代碼差不多了。核心部分就是,獲取channelcontext,然后給他發(fā)送消息。如果不在線就不用管。
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( "執(zhí)行到了發(fā)送方法:pushapplymessage" ); layimtoclientnoticemsgbody body = new layimtoclientnoticemsgbody(); channelcontext channelcontext = getchannelcontext(toid); //先判斷是否在線,再去查詢數(shù)據庫,減少查詢次數(shù) 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){ } } |
現(xiàn)在推送已經搞定了,那么什么時候推送呢?由于這個系統(tǒng)消息的推送可以不用那么即時,于是我看了下,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; } } |
在創(chuàng)建一個listener,監(jiān)聽事件。
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(); } } |
不過我有個疑問,發(fā)現(xiàn)listener中執(zhí)行的時候是同步的。后來加了@async 和@enableasync 也沒用,于是我就用了new thread().start()實現(xiàn)異步,確保不影響主要申請流程。(這是個疑問,自己沒搞明白的地方)
最后,別忘了在application啟動的時候把listener加上。
1
2
3
4
5
6
7
8
9
|
public static void main(string[] args) { springapplication springapplication = new springapplication(layimapplication. class ); /** * 這里監(jiān)聽增加listener,listener才會觸發(fā) * applylistener 是監(jiān)聽好友申請的事件 * */ springapplication.addlisteners( new applylistener()); springapplication.run(args); } |
功能拼接
馬上就要成功了,我們在把事件串起來,在好友申請成功之后,發(fā)布事件。
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); //申請成功,發(fā)布申請事件,通知 toid處理消息,如果不在線,不會進行處理 if (result.issuccess()){ applicationcontext.publishevent( new applyevent( "apply" ,toid)); } return result; } |
功能演示
講了那么多,給大家看一下成品效果。(用戶場景:安小鳥加皇上為好友,皇上接收消息并查看)
皇上收到消息,系統(tǒng)彈出左下角的小數(shù)字4。(調用 layim.msgbox(msgcount) 方法)
皇上點開消息盒子:
皇上收到了四位愛妃的申請,寢食難安,他會怎么處理呢?欲知后事如何,且聽下回分解~~~
總結
本篇主要介紹了一個加好友的流程的實現(xiàn)。
- 好友申請按鈕出不出現(xiàn)取決于用戶是否為自己,是否已經是好友。(后端也要做驗證)
- t-io的服務端主動推送,如何調用。關鍵詞: servergroupcontext
- event的使用,除了applicationevent,還可以拓展其他類型,如消息隊列,eventbus等。
- 各種細節(jié)處理,比如先判斷對方是否在線,在去查詢數(shù)據庫。或者結合緩存等
- 由于是自己摸索,難免有代碼繁雜混亂之處,
文中代碼地址: https://github.com/fanpan26/springbootlayim
以上所述是小編給大家介紹的springboot+layim+t-io 實現(xiàn)好友申請通知流程,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:https://my.oschina.net/panzi1/blog/1586421?utm_source=tuicool&utm_medium=referral