沾湿的烟花好比褪色的晚霞 發表於 2021-9-3 21:50:00

微信小程序--聊天室小程序(云开发)

<h3 id="微信小程序----聊天室小程序云开发">微信小程序 -- 聊天室小程序(云开发)</h3>
<p>从微信小程序开发社区更新<code>watch</code>接口之后,一直在构思这个项目。项目已经完成很久,但是一直都没有空写一篇博客记录展示一下。</p>
<h4 id="开源地址">开源地址</h4>
<p>wx-cloud-im: 基于微信云开发 cloudbase 构建聊天小程序 提供即时通讯</p>
<h4 id="技术栈">技术栈</h4>
<table>
<thead>
<tr>
<th><img src="https://img2020.cnblogs.com/blog/1141382/202109/1141382-20210903214848756-1316478810.png" alt="" loading="lazy"></th>
<th><img src="https://img2020.cnblogs.com/blog/1141382/202109/1141382-20210903214859536-276437819.png" alt="" loading="lazy"></th>
</tr>
</thead>
<tbody>
<tr>
<td>云开发</td>
<td>NodeJS</td>
</tr>
</tbody>
</table>
<h4 id="功能实现">功能实现</h4>
<ul>
<li>即时消息监听推送</li>
</ul>
<blockquote>
<p>使用<code>watch</code>接口(见附录),对数据库信息变动进行监听,实现 订阅-发布 形式的消息推送,同时在小程序端也完成了消息推送聊天界面变化的动画实现</p>
</blockquote>
<ul>
<li>文本内容安全核验</li>
</ul>
<blockquote>
<p>使用微信小程序<code>openapi</code>对文本内容安全进行校验</p>
</blockquote>
<ul>
<li>图片内容安全核验及重复性检查</li>
</ul>
<blockquote>
<p>将图片转为<code>Buffer</code>形式上传,并进行内容安全校验,同时计算<code>Buffer</code>的<code>MD5</code>值,实现重复性检查</p>
</blockquote>
<ul>
<li>历史消息查询</li>
</ul>
<blockquote>
<p>通过对<code>scroll-view</code>的<code>ID</code>锚点的计算,达到平滑切换信息的效果</p>
</blockquote>
<ul>
<li>小黑屋功能:禁止用户发言</li>
</ul>
<blockquote>
<p>无法通过内容安全校验的信息会被记录下来,管理员可以调用<code>cloud-user-black</code>云函数对对应用户进行封禁,同时计时器自动每天触发一次,用户到达封禁日期期限自动解除发言限制</p>
</blockquote>
<ul>
<li>消息位置锚定 <code>scroll-view</code></li>
</ul>
<blockquote>
<p>新消息和历史消息平滑的动画效果</p>
</blockquote>
<h4 id="效果预览">效果预览</h4>
<p><img src="https://img2020.cnblogs.com/blog/1141382/202109/1141382-20210903214909052-1904851922.png" alt="" loading="lazy"></p>
<h4 id="数据表设计">数据表设计</h4>
<blockquote>
<p>chat-users 聊天室用户信息表</p>
</blockquote>
<table>
<thead>
<tr>
<th>字段</th>
<th>说明</th>
<th>类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>_id</td>
<td>数据库记录唯一ID</td>
<td>string</td>
</tr>
<tr>
<td>openid</td>
<td>用户唯一身份识别ID</td>
<td>string</td>
</tr>
<tr>
<td>userInfo</td>
<td>用户头像 昵称 地址等信息</td>
<td>object</td>
</tr>
</tbody>
</table>
<blockquote>
<p>chat-users-ban 聊天室小黑屋信息表</p>
</blockquote>
<table>
<thead>
<tr>
<th>字段</th>
<th>说明</th>
<th>类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>_id</td>
<td>数据库记录唯一ID</td>
<td>string</td>
</tr>
<tr>
<td>ban_date</td>
<td>禁言时长 单位天</td>
<td>number</td>
</tr>
<tr>
<td>_createTime</td>
<td>记录创建时间</td>
<td>string</td>
</tr>
<tr>
<td>_updateTime</td>
<td>记录更新时间</td>
<td>string</td>
</tr>
</tbody>
</table>
<blockquote>
<p>chat-msgs 消息记录表</p>
</blockquote>
<table>
<thead>
<tr>
<th>字段</th>
<th>说明</th>
<th>类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>_id</td>
<td>数据库记录唯一ID</td>
<td>string</td>
</tr>
<tr>
<td>roomId</td>
<td>会话房间号</td>
<td>number</td>
</tr>
<tr>
<td>openid</td>
<td>消息发送者openid</td>
<td>string</td>
</tr>
<tr>
<td>msgType</td>
<td>消息类型 目前有 text image</td>
<td>string</td>
</tr>
<tr>
<td>content</td>
<td>消息内容 text :对应消息内容 image:对应图片地址</td>
<td>string</td>
</tr>
<tr>
<td>userInfo</td>
<td>用户头像 昵称 地址等信息</td>
<td>object</td>
</tr>
<tr>
<td>_createTime</td>
<td>消息创建时间</td>
<td>string</td>
</tr>
</tbody>
</table>
<blockquote>
<p>chat-msgs-ban 非法消息记录表(内容/图片安全校验不通过)</p>
</blockquote>
<table>
<thead>
<tr>
<th>字段</th>
<th>说明</th>
<th>类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>_id</td>
<td>数据库记录唯一ID</td>
<td>string</td>
</tr>
<tr>
<td>roomId</td>
<td>会话房间号</td>
<td>number</td>
</tr>
<tr>
<td>openid</td>
<td>消息发送者openid</td>
<td>string</td>
</tr>
<tr>
<td>msgType</td>
<td>消息类型 目前有 text image</td>
<td>string</td>
</tr>
<tr>
<td>content</td>
<td>消息内容 text :对应消息内容 image:对应图片地址</td>
<td>string</td>
</tr>
<tr>
<td>userInfo</td>
<td>用户头像 昵称 地址等信息</td>
<td>object</td>
</tr>
<tr>
<td>_createTime</td>
<td>消息创建时间</td>
<td>string</td>
</tr>
</tbody>
</table>
<h4 id="拓展开发">拓展开发</h4>
<p>项目提供的聊天室<code>Demo</code>为单聊天室模式,默认<code>roomId = 1</code>。为如果想要做成多用户聊天不同的形式,如<code>QQ</code>,只需要做如下几个步骤</p>
<ol>
<li>
<p>自定义数据集合,为不同用户之间聊天分配不同的 <code>roomId</code></p>
</li>
<li>
<p>引用组件时传入不同<code>roomId</code>即可</p>
<pre><code class="language-html">&lt;chat-box roomId="{{roomId}}"&gt;&lt;/chat-box&gt;
</code></pre>
</li>
<li>
<p>调用消息发送云函数时,传入 <code>roomId</code></p>
</li>
</ol>
<blockquote>
<p>TIPS</p>
</blockquote>
<p>建议复用<code>index/index.js</code>页面,只需跳转该页面时,携带<code>roomId</code>参数,并赋值给<code>data</code>中的<code>roomId</code>即可</p>
<pre><code class="language-js"> onLoad: function (options){
        this.setData({
                roomId:options.roomId
        })
}
</code></pre>
<h3 id="_"></h3>
<h3 id="附录">附录</h3>
<h4 id="watch">watch</h4>
<blockquote>
<p>支持端:小程序 2.8.1, Web</p>
</blockquote>
<p>监听集合中符合查询条件的数据的更新事件。使用 <code>watch</code> 时,支持 <code>where</code>, <code>orderBy</code>, <code>limit</code>,不支持 <code>field</code>。</p>
<h4 id="参数">参数</h4>
<table>
<thead>
<tr>
<th style="text-align: left">属性</th>
<th style="text-align: left">类型</th>
<th style="text-align: left">默认值</th>
<th style="text-align: left">必填</th>
<th style="text-align: left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">onChange</td>
<td style="text-align: left">function</td>
<td style="text-align: left"></td>
<td style="text-align: left">是</td>
<td style="text-align: left">成功回调,回调传入的参数 snapshot 是变更快照,snapshot 定义见下方</td>
</tr>
<tr>
<td style="text-align: left">onError</td>
<td style="text-align: left">function</td>
<td style="text-align: left"></td>
<td style="text-align: left">是</td>
<td style="text-align: left">失败回调</td>
</tr>
</tbody>
</table>
<h4 id="返回值">返回值</h4>
<p>Watcher 对象</p>
<table>
<thead>
<tr>
<th style="text-align: left">属性</th>
<th style="text-align: left">类型</th>
<th style="text-align: left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">close</td>
<td style="text-align: left">function</td>
<td style="text-align: left">关闭监听,无需参数,返回 Promise,会在关闭完成时 <code>resolve</code></td>
</tr>
</tbody>
</table>
<h4 id="参数说明">参数说明</h4>
<h4 id="snapshot-说明">snapshot 说明</h4>
<table>
<thead>
<tr>
<th style="text-align: left">字段</th>
<th style="text-align: left">类型</th>
<th style="text-align: left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">docChanges</td>
<td style="text-align: left">ChangeEvent[]</td>
<td style="text-align: left">更新事件数组</td>
</tr>
<tr>
<td style="text-align: left">docs</td>
<td style="text-align: left">object[]</td>
<td style="text-align: left">数据快照,表示此更新事件发生后查询语句对应的查询结果</td>
</tr>
<tr>
<td style="text-align: left">type</td>
<td style="text-align: left">string</td>
<td style="text-align: left">快照类型,仅在第一次初始化数据时有值为 init</td>
</tr>
<tr>
<td style="text-align: left">id</td>
<td style="text-align: left">number</td>
<td style="text-align: left">变更事件 id</td>
</tr>
</tbody>
</table>
<h4 id="changeevent-说明">ChangeEvent 说明</h4>
<table>
<thead>
<tr>
<th style="text-align: left">字段</th>
<th style="text-align: left">类型</th>
<th style="text-align: left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">id</td>
<td style="text-align: left">number</td>
<td style="text-align: left">更新事件 id</td>
</tr>
<tr>
<td style="text-align: left">queueType</td>
<td style="text-align: left">string</td>
<td style="text-align: left">列表更新类型,表示更新事件对监听列表的影响,枚举值,定义见 QueueType</td>
</tr>
<tr>
<td style="text-align: left">dataType</td>
<td style="text-align: left">string</td>
<td style="text-align: left">数据更新类型,表示记录的具体更新类型,枚举值,定义见 DataType</td>
</tr>
<tr>
<td style="text-align: left">docId</td>
<td style="text-align: left">string</td>
<td style="text-align: left">更新的记录 id</td>
</tr>
<tr>
<td style="text-align: left">doc</td>
<td style="text-align: left">object</td>
<td style="text-align: left">更新的完整记录</td>
</tr>
<tr>
<td style="text-align: left">updatedFields</td>
<td style="text-align: left">object</td>
<td style="text-align: left">所有更新的字段及字段更新后的值,key 为更新的字段路径,value 为字段更新后的值,仅在 <code>update</code> 操作时有此信息</td>
</tr>
<tr>
<td style="text-align: left">removedFields</td>
<td style="text-align: left">string[]</td>
<td style="text-align: left">所有被删除的字段,仅在 <code>update</code> 操作时有此信息</td>
</tr>
</tbody>
</table>
<h4 id="queuetype-枚举值">QueueType 枚举值</h4>
<table>
<thead>
<tr>
<th style="text-align: left">枚举值</th>
<th style="text-align: left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">init</td>
<td style="text-align: left">初始化列表</td>
</tr>
<tr>
<td style="text-align: left">update</td>
<td style="text-align: left">列表中的记录内容有更新,但列表包含的记录不变</td>
</tr>
<tr>
<td style="text-align: left">enqueue</td>
<td style="text-align: left">记录进入列表</td>
</tr>
<tr>
<td style="text-align: left">dequeue</td>
<td style="text-align: left">记录离开列表</td>
</tr>
</tbody>
</table>
<h4 id="datatype-枚举值">DataType 枚举值</h4>
<table>
<thead>
<tr>
<th style="text-align: left">枚举值</th>
<th style="text-align: left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">init</td>
<td style="text-align: left">初始化数据</td>
</tr>
<tr>
<td style="text-align: left">update</td>
<td style="text-align: left">记录内容更新,对应 <code>update</code> 操作</td>
</tr>
<tr>
<td style="text-align: left">replace</td>
<td style="text-align: left">记录内容被替换,对应 <code>set</code> 操作</td>
</tr>
<tr>
<td style="text-align: left">add</td>
<td style="text-align: left">记录新增,对应 <code>add</code> 操作</td>
</tr>
<tr>
<td style="text-align: left">remove</td>
<td style="text-align: left">记录被删除,对应 <code>remove</code> 操作</td>
</tr>
</tbody>
</table>
<h4 id="返回值说明">返回值说明</h4>
<p>返回值 <code>Watcher</code> 上只有一个 <code>close</code> 方法,可以用于关闭监听。</p>
<h4 id="orderby-与-limit">orderBy 与 limit</h4>
<p>从 <code>2.9.2</code> 起,在监听时支持使用 <code>orderBy</code> 和 <code>limit</code>,如果不传或版本号低于 <code>2.9.2</code>,则默认按 <code>id</code> 降序排列(等同于 <code>orderBy('id', 'desc')</code>),<code>limit</code> 默认不存在即取所有数据。</p>
<p><em>示例代码:根据查询条件监听</em>*</p>
<pre><code class="language-javascript">const db = wx.cloud.database()
const watcher = db.collection('todos')
// 按 progress 降序
.orderBy('progress', 'desc')
// 取按 orderBy 排序之后的前 10 个
.limit(10)
// 筛选语句
.where({
    // 填入当前用户 openid,或如果使用了安全规则,则 {openid} 即代表当前用户 openid
    _openid: '{openid}'
})
// 发起监听
.watch({
    onChange: function(snapshot) {
      console.log('snapshot', snapshot)
    },
    onError: function(err) {
      console.error('the watch closed because of error', err)
    }
})
</code></pre>
<h4 id="示例代码监听一个记录的变化">示例代码:监听一个记录的变化</h4>
<pre><code class="language-javascript">const db = wx.cloud.database()
const watcher = db.collection('todos').doc('x').watch({
onChange: function(snapshot) {
    console.log('snapshot', snapshot)
},
onError: function(err) {
    console.error('the watch closed because of error', err)
}
})
</code></pre>
<h4 id="示例代码关闭监听">示例代码:关闭监听</h4>
<pre><code class="language-javascript">const db = wx.cloud.database()
const watcher = db.collection('todos').where({
_openid: 'xxx' // 填入当前用户 openid
}).watch({
onChange: function(snapshot) {
    console.log('snapshot', snapshot)
},
onError: function(err) {
    console.error('the watch closed because of error', err)
}
})
// ...
// 关闭
await watcher.close()
</code></pre><br><br>
来源:https://www.cnblogs.com/masterchd/p/15225067.html
頁: [1]
查看完整版本: 微信小程序--聊天室小程序(云开发)