一、 背景介绍
本次分享是以IT服务工单系统为背景,派单方与接单方进行相关的沟通交流,实现语音、文字、图片、表情以及视频和其他文件的即时通讯。
二、 项目环境
此次技术分享,基于uniapp编译的微信小程序+thinkphp5+mysql。此次使用的即时通讯采用的uniapp生态环境中的uni-im插件产品。uni-im是云端一体的、全平台的、免费的、开源即时通讯系统。那么uni-im的特点有以下几点:
基于uni-app,App、小程序、web全端兼容 基于uniCloud,前后端都使用js开发 基于uni-push2,专业稳定的全端推送系统 基于uni-id,完善的账户体系 支持服务端为非uniCloud(比如:应用服务端的开发语言是php、java、go、.net、python、c#等)或 不基于uni-id-pages 开发的项目接入。
三、 开始前的前提条件
1、开通uniCloud并创建服务空间 控制面板 。传统的IM产品服务端代码托管在服务商名下的服务器内,你只拥有代码和产生的数据的使用权,并非所有权;而uni-im的前后端代码都是开源的,是将代码托管在你名下的unicloud(serverless服务器)内。
2、在uniapp开发者后台将需要接入的应用开通uni-push2.0(注意:无论是APP、小程序、web端都需要开通,否则消息将无法实时更新)点此前往开通。
四、 开始部署到自己的uniapp项目中
1、打开uni-im插件下载地址:https://ext.dcloud.net.cn/plugin?name=uni-im
2、点击使用HBuilderX导入插件,选择你的项目,点击确定(同时会自动导入依赖的uni_modulesuni-id-pages)按提示操作自动配置pages.json3、准备分包,根据自身情况,小编这里将uniim单独做了分包,那么就以小编的为例。首先在page.json文件中准备两个分包,在subPackages注册分包根目录路径地址。如下图:
4、打开项目根目录的App.vue文件,初始化uni-id-pages和uniIm模块,首先引入,其次在onLaunch声明周期中初始化,示例如下:
<script> //1. 导入统一身份信息管理模块 import uniIdPagesInit from '@/uni_modules/uni-id-pages/init.js'; //2. 导入uniIm的Utils工具类 import uniImUtils from '@/uni_modules/uni-im/common/utils.js'; export default { onLaunch: async function() { console.log('App Launch'); //3. 初始化uni身份信息管理模块 uniIdPagesInit(); //4. 初始化uniIm uniImUtils.init(); }, onShow: function() { console.log('App Show'); }, onHide: function() { console.log('App Hide'); } }; </script>
5、在项目目录下的uniCloud-aliyun/cloudfunctions/common/uni-config-center/uni-id/config.json中进行数据配置,主要是配置token过期时间,token过期时间要同步自己的业务系统的用户token过期时间,其次是请求秘钥,这里的请求秘钥会在用户授权登录时调用接口时做签名。配置文件详见
6、对项目右键,选择“云服务空间初始化向导” 按提示部署项目(注意:选择绑定的服务空间,须在uni-push2.0的web控制台关联),这里推荐选择阿里云 ,如下图:
7、因为我这里是PHP的后端环境,那么就需要做外部系统联登。简单的讲就是自己的业务系统注册了后,也需要在unicloud通过api请求携带昵称、头像以及自身业务系统的用户ID等参数在unicloud进行注册,那么登录以及退出登录等同理,保证双方同步。那么在api请求之前,我们需要先进行云函数url化,简单的讲就是先将对应的云函数自定义为url地址供我们接下来的接口调用。首先,我们登录我·uniCLoud开发者后台,找到我们的云函数,点击详情后,设定我们的云函数的path地址。
8、这里以PHP为例,贴上我们请求接口需要做签名的函数方法,具体请参考官方文档
public function getSignature($params, $nonce, $timestamp) { $paramsStr = $this->getParamsString($params); $signature = hash_hmac('sha256', ((string)$timestamp . $paramsStr), ($this->requestAuthSecret . $nonce));//$this->requestAuthSecret为config.json中配置的请求秘钥 return strtoupper($signature); } private function getParamsString($params) { ksort($params); $paramsStr = []; foreach ($params as $key => $value) { if (gettype($value) == "array" || gettype($value) == "object") { continue; } array_push($paramsStr, $key . '=' . $value); } return join('&', $paramsStr); } //获取签名 public function getSignString($params) { $nonce = sprintf("%d", rand()); $timestamp = time() * 1000; $signature = $this->getSignature($params, $nonce, $timestamp); if($signature){ return ['code'=>1,'msg'=>'签名获取成功','data'=>['nonce'=>$nonce,'timestamp'=>$timestamp,'signature'=>$signature]]; }else{ return ['code'=>0,'msg'=>'获取失败']; } }
9、那么接下来我们首先在用户注册时,调用云函数进行unicloud的用户注册,当注册完成后,接口会返回用户的uni-id体系的用户Id,其次调用登录接口的云函数,按照接口要求入参,获得token以及token的过期时间等。
/** uniCloud注册 * @param $userData 用户数据 * @return array */ public function uniCloudExternalRegister($userData) { try { $data = [ 'clientInfo'=>[ 'appId'=>'__UNI__42054848', 'uniPlatform'=>'mp-weixin', ], 'params'=>[ "externalUid"=>(string)$userData['id'],//用户ID "nickname"=>$userData['nickname'],//用户昵称 "avatar"=>$userData['avatar'],//用户头像 "gender"=>0,//性别:0=未知 1=男 2=女 ] ]; $signatureResult = $this->getSignString($data['params']); if(!$signatureResult || !$signatureResult['code']){ throw new \Exception('签名失败'); } $header = [ 'Content-Type:application/json', 'uni-id-nonce:'.$signatureResult['data']['nonce'], 'uni-id-timestamp:' .$signatureResult['data']['timestamp'], 'uni-id-signature:'.$signatureResult['data']['signature'], ]; $result = Http::post($this->urls['externalRegister'],json_encode($data,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),[],$header); $result = json_decode($result,true); if(!isset($result['errCode']) || $result['errCode'] != 0){ throw new \Exception('联登注册失败,请稍后重试'); } $updateUserInfo = UserModel::where(['code'=>$userData['code']])->update(['uni_token'=>$result['newToken']['token'],'uid'=>$result['uid'],'uni_token_exceed_time'=>floor($result['newToken']['tokenExpired'] / 1000)]); if(!$updateUserInfo){ throw new \Exception('联登数据存储失败,请稍后重试'); } return ['code'=>1,'msg'=>'联登注册成功','data'=>$result]; }catch (\Exception $e){ return ['code'=>0,'msg'=>$e->getMessage()]; } } /** * unicloud登录 * @param $userData 用户数据 * @return array */ public function uniCloudLogin($userData) { try { $data = [ 'clientInfo'=>[ 'appId'=>'__UNI__42054848', 'uniPlatform'=>'mp-weixin', ], 'uniIdToken'=>$userData['uni_token'], 'params'=>[ "uid"=>(string)$userData['uid'], "externalUid"=>(string)$userData['id'], ] ]; $signatureResult = $this->getSignString($data['params']); if(!$signatureResult || !$signatureResult['code']){ throw new \Exception('签名失败'); } $header = [ 'Content-Type:application/json', 'uni-id-nonce:'.$signatureResult['data']['nonce'], 'uni-id-timestamp:' .$signatureResult['data']['timestamp'], 'uni-id-signature:'.$signatureResult['data']['signature'], ]; $result = Http::post($this->urls['externalLogin'],json_encode($data,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),[],$header); $result = json_decode($result,true); if(!isset($result['errCode']) || $result['errCode'] != 0){ throw new \Exception(isset($result['error']['message']) && $result['error']['message'] ? $result['error']['message'] : '联登登录失败,请稍后重试'); } if($userData['uni_token_exceed_time'] < time() ){ $updateUserInfo = UserModel::where(['code'=>$userData['code']])->update(['uni_token'=>$result['newToken']['token'],'uni_token_exceed_time'=>floor($result['newToken']['tokenExpired'] / 1000)]); if(!$updateUserInfo){ throw new \Exception('联登token更新失败,请稍后重试'); } } return ['code'=>1,'msg'=>'联登成功','data'=>$result]; }catch (\Exception $e){ return ['code'=>0,'msg'=>$e->getMessage()]; } }
10、登录成功后,那么需要将token以及过期时间返回给客户端,调用客户端集成的sdk进行登录。
//uniCloud登录 async uniCloudLogin(uniCloudResult){ let token = uniCloudResult.data.newToken.token; let tokenExpired = uniCloudResult.data.newToken.tokenExpired; let uniIdToken = {token,tokenExpired}; await uniImUtils.login(uniIdToken); },
11、那么确保账户对接成功后,打开“用户列表页”,路径:/uni_modules/uni-im/pages/userList/userList可以看到所有的注册用户,那么同时也可以直接打开'/uni_modules/uni-im/pages/chat/chat?user_id='+‘’unicloud的用户id’直接发起会话聊天,那么到这里,基于我们使用uni-im实现即时通讯的步骤就全部完成。
真的很实用