文章目录

  • 前言
    • 一、用户端
    • 1.基本展示
    • 2.难处理的点
    • 二、另一用户端
      • 1.前端websocket的整合
      • 2.手机息屏websocket断线问题
      • 2.websocket服务端配置
      • 3.后端整合websocket作为服务端,传输消息给前端
    • 总结

前言

最近工作需求来了个项目,前景为在支付宝平台上发布一个处方插件即小程序插件: 源于我们日常微信聊天的页面,只是旨在重心不同,微信着重于IM日常生活通讯,我的支付宝小程序插件插件重在提供IM问诊功能,以及问诊后,医生进行开方回传给患者用户。


一、用户端

1.基本展示

  • 其基本流程概要便是这个输入框回车发送了。
 <!-- 文字+图片消息模板--><view a:for="{{chatLists}}" a:key="{{index}}" a:for-index="index"><view class={{item.role=='patient'?"answer":"question"}} id='msg-{{index}}'><view class={{item.role=='patient'?"heard_img right" :"heard_img left"}}><image mode="scaleToFill" class="user-profile__avatar" src="{{item.userImgSrc}}" /></view><view class={{item.role=='patient'?"answer_text":"question_text"}} data-index="{{index}}" hidden="{{(item.msg_type==='image')}}"><view class="symbol"></view><view class="info"><text selectable="true">{{item.textMessage}}</text></view><view a:if={{item.isSending}} class="notice"><image class="icon" mode="scaleToFill" src="{{item.icon}}" /></view></view><view class={{item.role=='patient'?"answer_text": "question_text"}} hidden="{{!(item.msg_type==='image')}}"><image  mode="aspectFill" onTap="previewImage" data-index="{{index}}"  style="width:150px; height:170px" src="{{item.textMessage}}" /></view></view>
</view><input a:if="{{inputObj.inputStatus==='text'}}" class="chat-input-style" selection-start="-1" selection-end="-1" cursor="-1" maxlength="500" confirm-type="send" value="{{textMessage}}"adjust-position='{{false}}'focus="{{focus}}"onConfirm="chatInputSendTextMessage" onFocus="chatInputBindFocusEvent" onBlur="chatInputBindBlurEvent" onInput="chatInputGetValueEvent" confirm-hold="{{true}}"   placeholder='想和TA说点什么呢?'cursor-spacing='20'/>

其核心便是这个input组件的onConfirm属性,通过在js中设置方法回调进行将input的value获取,然后把整个页面的消息chatLists进行setData重新渲染即可。

2.难处理的点

如上基本展示是很容易去实现的,只要把握好页面结构html以及css样式即可。但是如下如果是要在支付宝小程序平台下去仿微信这样基于日常IM聊天的效果的体验感,是有点难度的,因为小程序是基于web开发的。**然后需要优化的点便是1.点击额外功能区把页面顶起来。2.在上滑获得聊天记录时,如果点击输入框,弹出键盘时,需要聚焦回到最底部消息 3.滑动页面时候在ios上会有卡顿的现象(估计是帧率的问题) 4.手机息屏1分钟左右,websocket断开的问题,导致无法进行正常通讯 ** 看如下动态图

  • 像这个点击 + 进行把额外操作区顶起来的关键代码如下
 <scroll-view scroll-y="{{true}}" onTouchStart="clickCloseExtra"  class="speak_box"  scroll-top="{{scrollTop}}"  onScroll="viewScroll"  scroll-into-view="{{toView}}"  style="margin-bottom:{{marginBottom}};height:{{chatHeight}}px">
.speak_box{display: block;-webkit-overflow-scrolling: touch;height: 100vh;padding:10px;
}
_page.chatInputExtraClickEvent = function (e) {_page.setData({'inputObj.extraObj.chatInputShowExtra': !_page.data.inputObj.extraObj.chatInputShowExtra,'marginBottom': !_page.data.inputObj.extraObj.chatInputShowExtra?'2.6rem':'.98rem',scrollTop: _page.data.scrollHeight,});extraButtonClickEvent && extraButtonClickEvent(!_page.data.inputObj.extraObj.chatInputShowExtra);};

对于第一点:点击+进行弹起,其关键在于将整个scroll-view的高度100%于整个屏幕(关键height: 100vh),后续采用margin-bottom,监听+号是否被点击事件(_page.data.inputObj.extraObj.chatInputShowExtra)
去解决setData该属性顶起的高度

对于第二点:在上滑获得聊天记录时,如果点击输入框。其主要是触发input的聚焦事件

_page.chatInputBindFocusEvent = function (e) {let messageList = _page.data.chatLists;_page.setData({'inputObj.inputType': 'text','inputObj.extraObj.chatInputShowExtra': false,'marginBottom': '.98rem','scrollTop': _page.data.scrollHeight-550,toView: 'msg-' +(messageList.length-1)});

其关键在于toView值,toview是前面scroll-view容器里的scroll-into-view属性的值,当将它进行setData时,由于前端消息数据变量的chatLists遍历渲染时有对应绑定下表,所以在toView时直接将其定位到最后一个消息的位置。完成下滑效果。
次关键点还有scrollTop,其为是滚动到页面的目标位置的API,这里将’scrollTop’: _page.data.scrollHeight-550这样设置,主要是为了scrollView的聚焦点对应上scrollTop,让后续的+点击可以顶起页面。

对于第三点: 在ios端滑动页面不流畅问题,就很好解决。

viewScroll: function(e){this.data.scrollTop = e.detail.scrollTop;this.data.scrollHeight = e.detail.scrollHeight;// 修复画面上下滑出现微抖动问题//超过阈值进行历史记录回显if(e.detail.scrollTop==0){if(this.time){clearTimeout(this.time);}this.time = setTimeout(()=>{this.setData({scrollTop: e.detail.scrollTop,});},250)},

在监听的scrollView滑动函数viewScroll里增加一个防抖(微妙级别的定时器)即可。

二、另一用户端

因为涉及websocket讲解,一并将上面,手机息屏1分钟左右,websocket断开的问题,导致无法进行正常通讯的解决方案提供

1.前端websocket的整合

let _this = this;// 连接let url = ws_chat+ '?biz=' + biz + '&uid=' + uid + '&name=' + name;my.connectSocket({url: url,data: {},header:{'content-type': 'application/json'},success: (res) => {console.log('WebSocket 连接成功');socket_state =1;},fail: (res) => {my.showToast({type: 'none',content: '无法连接服务器,请刷新...',duration: 1000,});},complete: () => {}});//接受云医服务器传来的数据my.onSocketMessage(function(result) {console.log(result);let resp = JSON.parse(Base64.baseDecode(result.data));if (resp.type == 'heartbeat') {return;}let content = resp.content;if (resp.type == 'text') {Assistant.sendQuestion(content,null,resp.type,false,null,_this,'doctor');}else if (resp.type == 'image') {Assistant.sendQuestion(content, null, resp.type, false, null,_this,'doctor');}else if (resp.type == 'endconsul') {Assistant.sendQuestion(content, null, "text", false, function() {_this.setData({'inputObj.chatInputShowExtra': false,})},_this,'doctor');         }else if(resp.type=='rpconsul'){Assistant.sendQuestion(content, null, 'text', false,function() {},_this,'doctor');}});

自行看代码便可理解。其是简单的一个websocket整合,可自行看支付宝相关文档介绍。

2.手机息屏websocket断线问题

其源于websocket是基于web的,当手机息屏行,可能是手机后台线程也进行闲置,无法继续为websocket提供服务,无法进行心跳继续发送,所以一分钟左右由于心跳无法继续发送就到导致websocket断开,但是重点便是这个websocket在支付宝小程序平台下只是处于断开状态,并没有关闭掉(有区别于微信小程序)。

所以当时查阅支付宝小程序websocket相关机制,1.如果是在websocket长时间心跳无保持的情况下,断开时触发my.onSocketClose的话,然后函数回调进行把websocket对象关闭掉,等待用户手机唤醒屏幕的时候 触发页面的onshow()方法再将websocket重连。 2.或者是直接在手机息屏的时候 触发onHide()方法时,直接将websocket给关闭。

2.websocket服务端配置

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(chatHandler(), "/gjws").addInterceptors(new WebSocketHandShakeInterceptor()).setAllowedOrigins("*");}@Beanpublic WebSocketHandler chatHandler() {return new ChatHandler();}@Beanpublic ServletServerContainerFactoryBean createWebSocketContainer() {ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();container.setMaxTextMessageBufferSize(8192);container.setMaxBinaryMessageBufferSize(8192);return container;}
}

代码中便是你websocket的url上下文,如我的配置url为im.server.url = ws://192.168.0.64:5506/gjws?biz=BIZID&uid=UID

关于nginx上的配置参考如下

location /ws {proxy_pass http://192.168.0.64:5506/gjws;#代理到上面的地址去,proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upgrade";proxy_set_header X-Real-IP $remote_addr;}

3.后端整合websocket作为服务端,传输消息给前端

@Overridepublic ResponseMsg<String> sendMsg(String token, RequestMsg<ReceiveMsgInVo> in) {// 医生端消息通过websocket发送到患者端ResponseMsg<String> response = new ResponseMsg<String>();String url = imServerUrl.replaceAll("BIZID", in.getData().getBiz()).replaceAll("UID", "0");String msg = JSON.toJSONString(in.getData());// base64转码String sm = new String(Base64Utils.encodeToString(msg.getBytes()));try {ImWebsocketClient wc = new ImWebsocketClient(new URI(url));wc.connect();while (wc.getReadyState().ordinal() == 0) {Thread.sleep(200);}if (wc.getReadyState().ordinal() == 1) {logger.info("医生端消息推送成功");wc.send(sm);}wc.close();response.setHead(ResponseHead.buildSuccessHead());response.setData("发送成功");} catch (Exception e) {// TODO Auto-generated catch blocklogger.error(e.getMessage(), e);response.setHead(ResponseHead.buildFailedHead());response.setData("发送失败");}return response;}

大部分系统是基于微服务的,我这边另一用户端回复消息时,是通过接口回调到我这边的服务,我这边将消息封装好ImWebsocketClient wc = new ImWebsocketClient(new URI(url));进行建立连接。建立连接成功后,触发TextWebSocketHandler的afterConnectionEstablished方法

@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// TODO Auto-generated method stubsuper.afterConnectionEstablished(session);String biz = (String) session.getAttributes().get("biz");String uid = (String) session.getAttributes().get("uid");logger.info("Login UID2:"+uid);//系统消息连接不处理if("0".equals(uid)) {return;}ChatMessageBean bean = new ChatMessageBean();bean.setBiz(biz);bean.setUid(uid);ChatHelper.addSession(session);RoomMate user = new RoomMate();user.setUid(uid);user.setSid(session.getId());ChatHelper.onLine(user);ChatHelper.joinChatRoom(bean,session.getId());logger.info("Login UID:"+uid);}

此时在前端的websocket建立连接的时候会触犯这个方法,然后进行重写将该对象与websocket session绑定。后续handler通过biz找到聊天室,获取到用户session,发送消息传至前端。其websocket消息发送关键在于session的确认从而找到对应的对象去发送。

@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// TODO Auto-generated method stubChatMessageBean bean = JSON.parseObject(Base64Utils.decodeFromString(message.getPayload()), ChatMessageBean.class);if (MessageTypeEnum.LOGIN.getCode().equals(bean.getType())) {logger.info(bean.toString());doLogin(session, bean);} else if (MessageTypeEnum.HEARTBEAT.getCode().equals(bean.getType())) {doHeartBeat(session, bean);} else if (MessageTypeEnum.TEXT.getCode().equals(bean.getType())) {logger.info(bean.toString());doText(session, bean);} else if (MessageTypeEnum.IMAGE.getCode().equals(bean.getType())) {logger.info(bean.toString());doText(session, bean);} else if (MessageTypeEnum.JOIN.getCode().equals(bean.getType())) {logger.info(bean.toString());doJoinChatRoom(session, bean);}  else if (MessageTypeEnum.NEWCONSUL.getCode().equals(bean.getType())) {logger.info(bean.toString());doNewConsul(session, bean);}  else if(MessageTypeEnum.ENDCONSUL.getCode().equals(bean.getType())) {logger.info(bean.toString());doText(session, bean);}  else if(MessageTypeEnum.RPCONSUL.getCode().equals(bean.getType())) {logger.info(bean.toString());doText(session, bean);}}private void doText(WebSocketSession session, ChatMessageBean message) throws IOException {List<RoomMate> mates = new ArrayList<>();//保存消息if(MessageTypeEnum.AUTO.getCode().equals(message.getSource())) {mates = ChatHelper.getAllRoomMates(message.getBiz(),message.getUid());}else {mates = ChatHelper.getOtherRoomMates(message.getBiz(),message.getUid());}logger.info("聊天室"+message.getBiz()+"在线用户:"+ JSON.toJSONString(mates));if(mates.size()>0) {for (RoomMate p : mates) {if(p!=null) {WebSocketSession ws = ChatHelper.getSession(p.getSid());if(ws!=null && ws.isOpen()) {ws.sendMessage(new TextMessage(Base64Utils.encodeToString(message.toString().getBytes("UTF-8"))));}}}}}

如上图,在前端进行websocket消息发送时,会触发handleTextMessage。后续只需对应将消息内容
ws.sendMessage(new TextMessage(Base64Utils.encodeToString(message.toString().getBytes(“UTF-8”))));通过聊天室另一方的userid,然后服务端websocket将消息推送至前端中即可。

总结

提示:这里对文章进行总结:

以上就是今天要讲的内容,本文仅仅简单介绍了部分IM的一个实现,小型通讯量是没问题的,如果后续说有大量的用户去时刻请求,后端可整合netty框架便可。如果有后续下咨询的朋友,请在下文评论区留言。

支付宝小程序平台的IM聊天插件相关推荐

  1. 小程序代码托管无忧,云效 代码管理接入支付宝小程序平台

    8月24日,阿里云云效代码管理(Codeup)正式接入支付宝小程序开放平台.小程序开发者再也不用担心代码托管问题,在支付宝开放平台上即可一键将代码托管到云端. 云效代码管理(Codeup)源自阿里巴巴 ...

  2. 如何将支付宝小程序迁移至微信/FinClip运行

    许多小程序开发者都会遇到这样一个困扰,自己已经在微信,支付宝等不同环境中开发了对应的小程序代码,但在不同平台中的小程序语法并不一致. 本篇文章将通过手把手入门的方式,快速了解「如何使用第三方工具互相转 ...

  3. map和foreach的区别和应用场景_支付宝小程序和微信小程序,两者有何区别?

    原标题:支付宝小程序和微信小程序,两者有何区别? 自2017年1月微信推出小程序之后,小程序的发展势头便愈来愈猛.作为腾讯老对头的阿里,也开始投入小程序的研发.在一年多的开放公测后,支付宝小程序终于于 ...

  4. 小程序向webview传参_独家 | 支付宝小程序向个人开发者开放公测

    基于兴趣和周围小群体开发的个人小程序,才是为支付宝提供更加多样化的生活服务场景的来源. 文 | Tech星球 (微信ID:tech618) 尹非凡.刘宁宁 2月26日,Tech星球(微信ID:tech ...

  5. 支付宝小程序面向个人开发者公测

    web前端教程 用大白话,来讲编程 昨日,支付宝小程序正式面向个人开发者开放公测,有开发能力的个人用户可访问支付宝小程序平台,扫码验证个人身份以后即可开始支付宝小程序账号申请并进行代码开发. 个人主体 ...

  6. 3年10亿怎么花?支付宝小程序公布生态孵化细则!

    小蚂蚁说: 自从支付宝3年投入10亿孵化小程序生态创新的消息传出以后,如何报名参加支付宝STS计划.如何获得孵化资源便成了不少创新创业者关注的焦点.日前,支付宝STS计划正式发布相关细则,开启报名通道 ...

  7. 核桃编程学python吗_学编程要趁早?对话核桃编程X支付宝 “小程序编程马拉松”三强得主...

    原标题:学编程要趁早?对话核桃编程X支付宝 "小程序编程马拉松"三强得主 来源:网络 浏览专业IT技术社区网站,创作沉浸式互动体验游戏,开发辟谣小程序......这并不是程序员大神 ...

  8. 现有小程序平台有哪些?如何让自己的App运行小程序?

    随着小程序越来越火热,越来越多的公司开始布局小程序,一些主流公司小程序平台应用已经非常成熟,但主流公司小程序只能在自己的生态内运行,并不输出其小程序技术能力至其他的产品.如果想要自己的App运行小程序 ...

  9. 支付宝小程序中使用F2图表

    支付宝小程序中使用F2图表 介绍 最近在支付宝小程序开发中接到显示图表的需求,因为支付宝小程序方未提供相关插件,并且目前支付宝小程序不支持document,所以根据推荐使用f2-canvas图表组件. ...

  10. 不止微信、支付宝!一文带你了解所有小程序平台

    小程序的平台越来越多了,开发者的精力也越来越分散.事实上,这些平台有怎样的特色?他们有怎样的代表作品?他们有几个入口?开发成本高吗?他们有给开发者怎样的扶持政策? 一文为你解析小程序六大平台. 01 ...

最新文章

  1. C++ 容器1 vector
  2. keil debug如何在watch直接修改变量值_printf系列教程03_SWO打印输出配置,基于Keil『Debug(printf)Viewer』...
  3. 《剑指offer》-- 二叉树的下一个结点、对称二叉树、按之字性顺序打印二叉树、把二叉树打印成多行
  4. w2k telnet port change
  5. 使用request简单爬虫
  6. 附一张css hack
  7. akka与neety_Akka STM –与STM Ref和Agent一起打乒乓球
  8. 女大学生两块钱成功进外企
  9. Python绘制散点对比图
  10. 编译原理递归下降语法分析器C++实现
  11. 大三上学期学期总结及百度实习感受
  12. 中兴新支点操作系统_中兴新支点操作系统
  13. windows无法连接到某个wifi_电脑提示Windows无法连接到这个网络/无线网络的解决方法...
  14. html 向上滑动,jQuery - 使用.slideUp()方法向上滑动HTML元素
  15. python算法工程师需要学什么_一个优秀的算法工程师必须具备哪些素质?
  16. 支付 | Java个人接入支付宝当面付(免签约,超详细步骤)
  17. vsCode 打开界面报错,尝试在目标目录创建文件时发生一个错误
  18. Ubuntu下快捷键操作
  19. 绘制简单封闭图形并且进行填充
  20. 已解决WslRegisterDistribution failed with error: 0x800701bc

热门文章

  1. 关于《高油压调速器机械液压系统的优化设计》的疑问
  2. php递归函数及简单实例讲解
  3. Polybase要求安装orcale jre 7更新 51或更高版本如何解决
  4. 数据的分析的方法及评价指标总结
  5. 每次循环都会创建新的数组,导致内存占用过多
  6. 全国大部分城市的经纬度位置
  7. wacom win10 未连接任何设备 驱动的问题 解决影拓3手绘板等老设备驱动无法在win10使用的问题
  8. SAP各模块常用数据库表大全--->常用表
  9. 大数据开源舆情分析系统-数据采集技术架构浅析
  10. 怎么评判网络舆情分析系统好不好的指标标准详解