欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot+LayIM+t-io 实现好友申请通知流程

程序员文章站 2024-02-22 13:21:23
前言 在上一篇 spring boot + layim + t-io 文件上传、 监听用户状态的实现 中,已经介绍了两个小细节:用户的在离线状态和群人数的状态变化。今天的...

前言

在上一篇 spring boot + layim + t-io 文件上传、 监听用户状态的实现 中,已经介绍了两个小细节:用户的在离线状态和群人数的状态变化。今天的主要内容就是用户加好友的实现。

简介

加好友,大家用过qq都知道,无非是发起好友申请,对方收到消息通知,然后处理。不过,本篇只讲前半部分,消息通知的处理留到下一篇去讲。因为内容有点多,怕是一时半会消化不了。在介绍主体流程之前,先给大家介绍一下准备工作。

准备工作

首先,为了让数据更贴近实战,所以我用了比较“真实”的用户数据。结合fly模板,完善了用户中心头部的用户信息的数据绑定。数据绑定部分判断了是否已经是好友,来决定是否出现“加为好友”的按钮。示例如下,当用户自己看到自己的主页时,是这样的:

SpringBoot+LayIM+t-io 实现好友申请通知流程 

看到非好友的用户主页,是这样的:

SpringBoot+LayIM+t-io 实现好友申请通知流程 

绑定数据部分,简单给大家介绍一下,就是用thymleaf模板绑定。后台访问页面的时候,将 model 赋值即可。

/**
 * 属性赋值
 * */
 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中的数据取出来。

<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和申请备注,后端组织数据插入到数据库,代码如下:

/**
 * 提交好友申请
 * */
 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 。

/**
 * 获取starter
*/
private static layimwebsocketstarter getstarter(){
 return (layimwebsocketstarter)springutil.getbean("layimwebsocketstarter");
 }

第二步,获取 servergroupcontext

private static servergroupcontext getservergroupcontext(){
 return getstarter().getservergroupcontext();
 }

第三步,获取 channelcontext。

/**
 * 获取channelcontext
 * */
 private static channelcontext getchannelcontext(string toid) {
 servergroupcontext context = getservergroupcontext();
 //找到用户
 channelcontext channelcontext = context.users.find(context, toid);
 return channelcontext;
 }

第四步,发射,这里的代码就和聊天中的那部分代码差不多了。核心部分就是,获取channelcontext,然后给他发送消息。如果不在线就不用管。

/**
 * 服务端主动推送消息
 * */
 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 就诞生了。

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,监听事件。

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加上。

public static void main(string[] args) {
 springapplication springapplication = new springapplication(layimapplication.class);
 /**
  * 这里监听增加listener,listener才会触发
  * applylistener 是监听好友申请的事件
  * */
 springapplication.addlisteners(new applylistener());
 springapplication.run(args);
 }

功能拼接

马上就要成功了,我们在把事件串起来,在好友申请成功之后,发布事件。

/**
 * 好友申请
 * */
 @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;
 }

功能演示

讲了那么多,给大家看一下成品效果。(用户场景:安小鸟加皇上为好友,皇上接收消息并查看)

SpringBoot+LayIM+t-io 实现好友申请通知流程 

皇上收到消息,系统弹出左下角的小数字4。(调用 layim.msgbox(msgcount) 方法)

SpringBoot+LayIM+t-io 实现好友申请通知流程 

皇上点开消息盒子:

SpringBoot+LayIM+t-io 实现好友申请通知流程 

皇上收到了四位爱妃的申请,寝食难安,他会怎么处理呢?欲知后事如何,且听下回分解~~~

总结

本篇主要介绍了一个加好友的流程的实现。

  1. 好友申请按钮出不出现取决于用户是否为自己,是否已经是好友。(后端也要做验证)
  2. t-io的服务端主动推送,如何调用。关键词: servergroupcontext
  3. event的使用,除了applicationevent,还可以拓展其他类型,如消息队列,eventbus等。
  4. 各种细节处理,比如先判断对方是否在线,在去查询数据库。或者结合缓存等
  5. 由于是自己摸索,难免有代码繁杂混乱之处,

文中代码地址: https://github.com/fanpan26/springbootlayim

                           http://xiazai.jb51.net/201712/yuanma/springbootlayim-master.rar

以上所述是小编给大家介绍的springboot+layim+t-io 实现好友申请通知流程,希望对大家有所帮助