最近新项目中需要集成IM 即时通讯功能,我们使用的是uniapp 开发的app,拿到这个需求的第一反应是到dcloud插件市场去看下载排名靠前的im 插件,发现下载量和收藏量最高的是GoEasy 即时通讯。GoEasy这个厂商好熟悉呀,想起了以前项目中做系统通知用的是他们的websocket实时通讯的pubsub接口,很好用,所以果断下载demo源码跑起来看看,同时也记录一下集成过程和中间遇到的问题已经相应的解决方案。
1. 访问http://www.goeasy.io 注册GoEasy账号,创建免费的增强型应用(不花钱就可以试用多好呀),获得appkey。
Keys分为两类: Basic Keys 和 Professional Keys, 至于区别你就记住一点,要想要更安全,就直接使用Professional Keys,扩展阅读:https://www.goeasy.io/cn/docs/goeasy-2.x/common/otp/otp.html
2. 若需要打包为微信小程序,需要登录微信公众平台->微信小程序开发设置->服务器域名,添加socket合法域名:wss://wx-hangzhou.goeasy.io
3. 如果想要体验发送图片、语音、视频功能,还需要配置阿里云oss
GoEasy 的多媒体文件发送的相关接口是默认集成了阿里云oss的。如果不想要集成第三方存储服务,那么图片视频这些多媒体文件的上传工作就需要您自己完成,然后通过GoEasy自定义消息接口来完成发送。
GoEasy没有托管我们的用户系统,什么意思呢?就是说像好友关系、群成员关系这些需要我们自己来维护, 那么GoEasy 是如何实现单聊和群聊的呢?
连接GoEasy时指定的当前登录的用户id,这样goeasy就知道这个app或者小程序当前是谁,发了消息后就知道发送者是谁,如果需要接收消息时goeasy也就知道消息应该推送给哪个设备。可以再稍微详细一点儿吗?当然可以!
先说私聊,连接goeasy时已经指明了当前登录用户的ID,发送私聊消息时,在参数中传入to.id; 监听了私聊消息后,所有发送给当前登录用户的私聊消息都会在这个监听器中被捕捉,然后你可以根据发送者来筛选你需要的消息。所以关键点在id参数,等会儿我会通过代码来展示具体实现。
再说群聊,群在我们自己的系统中已经建好,有群就有群id,发送消息时,只需要指定groupid就可以了;接收消息也很简单,群成员都订阅指定群id,并监听群消息就可以接收了,由于goeasy未托管群与群成员的关系,每次连接成功后,都需要重新订阅群消息,否者会收不到群消息哦。后面也会通过代码来展示具体实现。
个人觉得了解这个工作原理,在设计程序时有很大帮助。
在main.js 中引入GoEasy 的sdk,并且将GoEasy和goEasy初始化好的对象挂载到全局,方便在app的其他页面调用goeasy的接口。
GoEasy连接的建立我是在登录成功后访问的页面中做的,这样就可以在用户登录成功后,第一时间与goeasy建立连接,然后就可以监听很多消息了,比如群消息、会话列表等。
连接前加上状态判断
连接的代码
1. 调用了GoEasy connect接口后,怎么日志中一直在打印connecting呢?
答:调用了connect接口之后,GoEasy sdk会自动尝试重连直到连接成功,如果出现一直连接中,你需要检查你的网络是否稳定,是否有防火墙限制等,最简单的方式则是切换到手机移动数据热点来看看是否可以连接成功。
2. GoEasy connect 接口返回code:401,error:Sorry, your application is a Basic Edition and does not support IM APIs, please upgrade to the Enhanced Edition if you need to invoke IM APIs,怎么回事?
答:这个错误是因为你创建的应用的类型不支持im 模块,只有增强型才可以使用im 模块,所以只需要重新创建一个免费的增强型就可以使用了。增强型是既支持pubsub模块又支持im模块,但基础型只支持pubsub模块。两个类型都有免费应用可以创建,所以不影响我们试用。
3. GoEasy connect 接口返回code:408,error:It is already connected, don't try again until disconnect() is called, 怎么回事?
答:这个就是客户端已经建立goeasy连接了,又在尝试调用connect接口就会报这个错。处理方式很简单,在连接时加上状态判断,只有当连接断开时才调用connect接口。我上面的代码演示中专门注视了的哦。 :)
4. Undefined的问题,比如goEasy is undefined, im is undefined 等
答:这个undefined 的问题,不多说了,多数情况是没有挂载到全局导致其他页面无法获取到您要的goeasy参数。遇到这种问题,需要逐步看看参数的定义情况,具体的就不多说了,前段程序员的基本排查能力要具备吧,哈哈哈哈。
5. GoEasy connect接口中的data属性是干什么的?
答:data可以带上当前用户的一些用户信息,比如常见的有昵称和头像,这个data属性会在多个接口中作为用户的附加信息返回给我们,比如会话列表接口、历史消息接口,这样可以省去我们再次从数据裤中读取这些信息。
通讯录,通常包含好友列表和已加群列表。由于GoEasy未托管用户系统,这些信息我将从我们的服务端获取数据。
1. createMessage,GoEasy 提供了多种类型的消息创建接口,我们可以直接调用
im.createTextMessage :创建文本消息,可以发送字符串,对象和json格式字符串
im.createCustomMessage: 创建自定义消息,可以发送一个任意自定义格式的对象,或字符串,如果需要发送文件,只能是文件的路径
im.createImageMessage:创建图片信息,需要集成阿里云oss存储服务才能使用该接口
im.createAudioMessage:创建语音信息,需要集成阿里云oss存储服务才能使用该接口
im.createVideoMessage:创建视频信息,需要集成阿里云oss存储服务才能使用该接口
im.createFileMessage:创建文件信息,需要集成阿里云oss存储服务才能使用该接口
2. 在createMessage时指定接收方的id 和接收方的用户信息data
发送私聊消息
1. 进入私聊页面后,在onload中就开始监听私聊消息,根据goeasy工作原理,用户的私聊消息监听会监听到所有好友的发送给当前用户的私聊消息,所以在接收消息时,需要过滤掉当前聊天好友的消息。注意:不会监听到自己发给好友的信息哦
2. 如果你的私聊功能中加入了已读未读提醒,为了及时展示已读,需要在一收到消息时就标记为已读。标记已读的文档可以参考https://www.goeasy.io/cn/docs/goeasy-2.x/im/conversation/mark-read.html
3. 在onunload事件中清空监听器中内容, 在2.4.8及以前的版本中,直接清空内容即可,2.4.9版本开始,需要通过off的方式来取消监听。(GoEasy所有的监听器都是这两个取消监听的方式哦,不同sdk的版本,取消方式有不同)
2.4.8及之前的版本:
2.4.9及以上版本:
渲染私聊消息时分为以下几种情况:
1. 进入聊天页面后,想要获取之前的历史消息,进行渲染
这种情况直接调用goeasy的history接口就可以获取,在接口中指定想要获取哪位好友的聊天信息就可以了,接口中有返回每条消息的发送者的id,可以方便我们进行消息渲染时判断显示在聊天页面的左列还是右列。
注意事项:
调用history接口时,首次调用时lastTimestamp必须传null (如果不传null,会导致消息返回的不正确,这个我猜想goeasy应该用到了缓存机制,具体的没有去了解),默认返回最后10条消息;第二次查询时,lastTimestamp为上次查询结果里最后一条消息的时间戳。
2. 进入聊天页面后,接收到新的好友消息后进行渲染
在私聊消息监听器中收到当前好友的消息时,直接将消息push到messages中就可以了。(使用vue框架的前提下)
3. 进入聊天页面后,自己发送的消息渲染到聊天页面
自己发送的消息不会在私聊监听器中返回,所以在消息发送后,将创建好的message对象直接push到messages中就可以了。(使用vue框架的前提下)
调用recallMessage接口撤回消息,传入message的数组就可以了。GoEasy撤回接口文档描述
注意事项:
撤回时需要传的message对象需要是goeasy这边创建的message对象,如果是自己封装的message对象,即使字段都一样也不能撤回成功。
调用deleteMessage接口删除消息,传入message的数组就可以了。 GoEasy删除消息接口的文档描述
注意事项:
删除时需要传的message对象需要是goeasy这边创建的message对象,如果是自己封装的message对象,即使字段都一样也不能删除成功。
私聊接口的常见问题排查
1. 发送了消息,但goeasy的私聊监听器中没有收到监听消息,是什么原因呢?
答:询问goeasy在线客服得知,监听器中的代码如果报错了,那么有新消息时是不会触发监听器的;或者满足消息渲染的条件导致看起来没有收到消息。你可以注释掉监听器中的所有代码,只留一句打印收到的消息,看看是否可以打印出新收到的消息。(我当时就是因为监听器中if (friendId === this.friend.uuid)的条件不满足导致消息没有渲染到页面,根本原因是 “12”===12返回了false,所以这些细节都需要注意)
2. 页面刷新后,无法收到新发送的消息,不刷新就可以正常收到,怎么回事呢?
答:uniapp开发的程序中,goeasy连接是挂载到全局的,只需要连接一次。但如果是将程序运行到了浏览器,然后在浏览器点了刷新按钮,那么这个客户端对goeasy来说是全新的客户端,那么之前的连接也就自然无效,所有自然也就无法收到新发送的消息。如果想要在浏览器上刷新后还可以接收消息,那么你需要重新建立goeasy连接,通常这种情况也可以不处理,因为真正在真机上运行的app是没有像浏览器这样的刷新功能的。
跟私聊消息的发送接口一致,唯一有变化的是以下三个属性。 群禁言就可以通过控制用户调用发送群聊消息的接口来实现。
id: 需要是groupid
type: 必须是this.GoEasy.IM_SCENE.GROUP
data: 是群信息,比如群头像和群昵称
接收群消息的两个前提条件,缺一不可:
1. 订阅了群消息 (订阅接口只能在客户端主动发起。)
2. 监听了群消息
跟私聊消息的实现方式类似。分不同情况进行不同渲染。
如果群成员退群后不再接收群消息时,就可以调用im.unsubscribeGroup来取消订阅群消息。这个接口只能在客户端主动发起。比如踢人,就可以让群成员主动取消订阅群消息达到踢人后的效果。
注意:
取消订阅群消息后,最好将相应的会话也一起删掉,否则用户通过会话列表进入群聊还可以加载到群历史消息,除非我们自己在加载历史消息加上判断,只有群成员才能加载历史消息。
当用户登录后,通过im.latestConversations获取当前登录用户的最后100条会话。会话列表接口中返回了总的未读数量、单个会话中消息发送者的信息、单个会话中消息未读数量、单个会话中的最后一条消息、是否为置顶会话等,有了这些信息就可以渲染出会话列表视图了。
im.latestConversations 接口文档地址
用户登录成功后的首次获取会话列表是通过im.latestConversations 获取的,那么如果用户不退出登录的前提下,要想要实时更新会话列表的显示,就需要监听实时会话列表更新了哦,监听中每次返回的都是最新的会话列表,返回的数据格式跟im.latestConversations 获取到的是一致的,所以可以拿到更新后的会话列表直接进行渲染不需要做差异处理。很方便的!
监听会话列表变化
在onload事件中进行监听,如果需要取消监听,就可以在onunload中处理。
删除会话没有什么特别的,直接调用接口就可以了,im.removePrivateConversation 和 im.removeGroupConversation。操作完成后会自动触发会话列表更新。
删除会话接口描述文档
跟删除会话一样,会话置顶也很简单,调用相应接口后,会话中的top属性会改为true,也会自动触发会话列表更新
会话置顶接口描述文档
用户退出登录后,一定要调用disconnect接口,这个接口是GoEasy判断当前用户是否登录的重要点,如果没有调用这个接口,会导致用户在退出系统后,还可以收到离线通知栏消息,这样用户就傻逼了,哈哈哈哈
Disconnect接口的文档
使用otp + professional keys 来保证appkey的安全https://www.goeasy.io/cn/docs/goeasy-2.x/common/otp/otp.html
GoEasy访问控制允许开发者对消息的发送和接收权限进行控制https://www.goeasy.io/cn/docs/goeasy-2.x/common/security/authorization/im.html
到此,私聊和群聊的实现都差不多了,后面还要集成通知栏推送, 如果大家感兴趣,请留言哦,我会在集成成功之后集成一篇文档来节约大家伙儿的时间。