微信小程序 -- 聊天室小程序(雲開發)
從微信小程序開發社區更新watch
接口之后,一直在構思這個項目。項目已經完成很久,但是一直都沒有空寫一篇博客記錄展示一下。
開源地址
wx-cloud-im: 基於微信雲開發 cloudbase 構建聊天小程序 提供即時通訊
技術棧
![]() |
![]() |
---|---|
雲開發 | NodeJS |
功能實現
- 即時消息監聽推送
使用
watch
接口(見附錄),對數據庫信息變動進行監聽,實現 訂閱-發布 形式的消息推送,同時在小程序端也完成了消息推送聊天界面變化的動畫實現
- 文本內容安全核驗
使用微信小程序
openapi
對文本內容安全進行校驗
- 圖片內容安全核驗及重復性檢查
將圖片轉為
Buffer
形式上傳,並進行內容安全校驗,同時計算Buffer
的MD5
值,實現重復性檢查
- 歷史消息查詢
通過對
scroll-view
的ID
錨點的計算,達到平滑切換信息的效果
- 小黑屋功能:禁止用戶發言
無法通過內容安全校驗的信息會被記錄下來,管理員可以調用
cloud-user-black
雲函數對對應用戶進行封禁,同時計時器自動每天觸發一次,用戶到達封禁日期期限自動解除發言限制
- 消息位置錨定
scroll-view
新消息和歷史消息平滑的動畫效果
效果預覽
數據表設計
chat-users 聊天室用戶信息表
字段 | 說明 | 類型 |
---|---|---|
_id | 數據庫記錄唯一ID | string |
openid | 用戶唯一身份識別ID | string |
userInfo | 用戶頭像 昵稱 地址等信息 | object |
chat-users-ban 聊天室小黑屋信息表
字段 | 說明 | 類型 |
---|---|---|
_id | 數據庫記錄唯一ID | string |
ban_date | 禁言時長 單位天 | number |
_createTime | 記錄創建時間 | string |
_updateTime | 記錄更新時間 | string |
chat-msgs 消息記錄表
字段 | 說明 | 類型 |
---|---|---|
_id | 數據庫記錄唯一ID | string |
roomId | 會話房間號 | number |
openid | 消息發送者openid | string |
msgType | 消息類型 目前有 text image | string |
content | 消息內容 text :對應消息內容 image:對應圖片地址 | string |
userInfo | 用戶頭像 昵稱 地址等信息 | object |
_createTime | 消息創建時間 | string |
chat-msgs-ban 非法消息記錄表(內容/圖片安全校驗不通過)
字段 | 說明 | 類型 |
---|---|---|
_id | 數據庫記錄唯一ID | string |
roomId | 會話房間號 | number |
openid | 消息發送者openid | string |
msgType | 消息類型 目前有 text image | string |
content | 消息內容 text :對應消息內容 image:對應圖片地址 | string |
userInfo | 用戶頭像 昵稱 地址等信息 | object |
_createTime | 消息創建時間 | string |
拓展開發
項目提供的聊天室Demo
為單聊天室模式,默認roomId = 1
。為如果想要做成多用戶聊天不同的形式,如QQ
,只需要做如下幾個步驟
-
自定義數據集合,為不同用戶之間聊天分配不同的
roomId
-
引用組件時傳入不同
roomId
即可<chat-box roomId="{{roomId}}"></chat-box>
-
調用消息發送雲函數時,傳入
roomId
TIPS
建議復用index/index.js
頁面,只需跳轉該頁面時,攜帶roomId
參數,並賦值給data
中的roomId
即可
onLoad: function (options){
this.setData({
roomId:options.roomId
})
}
附錄
watch
監聽集合中符合查詢條件的數據的更新事件。使用 watch
時,支持 where
, orderBy
, limit
,不支持 field
。
參數
屬性 | 類型 | 默認值 | 必填 | 說明 |
---|---|---|---|---|
onChange | function | 是 | 成功回調,回調傳入的參數 snapshot 是變更快照,snapshot 定義見下方 | |
onError | function | 是 | 失敗回調 |
返回值
Watcher 對象
屬性 | 類型 | 說明 |
---|---|---|
close | function | 關閉監聽,無需參數,返回 Promise,會在關閉完成時 resolve |
參數說明
snapshot 說明
字段 | 類型 | 說明 |
---|---|---|
docChanges | ChangeEvent[] | 更新事件數組 |
docs | object[] | 數據快照,表示此更新事件發生后查詢語句對應的查詢結果 |
type | string | 快照類型,僅在第一次初始化數據時有值為 init |
id | number | 變更事件 id |
ChangeEvent 說明
字段 | 類型 | 說明 |
---|---|---|
id | number | 更新事件 id |
queueType | string | 列表更新類型,表示更新事件對監聽列表的影響,枚舉值,定義見 QueueType |
dataType | string | 數據更新類型,表示記錄的具體更新類型,枚舉值,定義見 DataType |
docId | string | 更新的記錄 id |
doc | object | 更新的完整記錄 |
updatedFields | object | 所有更新的字段及字段更新后的值,key 為更新的字段路徑,value 為字段更新后的值,僅在 update 操作時有此信息 |
removedFields | string[] | 所有被刪除的字段,僅在 update 操作時有此信息 |
QueueType 枚舉值
枚舉值 | 說明 |
---|---|
init | 初始化列表 |
update | 列表中的記錄內容有更新,但列表包含的記錄不變 |
enqueue | 記錄進入列表 |
dequeue | 記錄離開列表 |
DataType 枚舉值
枚舉值 | 說明 |
---|---|
init | 初始化數據 |
update | 記錄內容更新,對應 update 操作 |
replace | 記錄內容被替換,對應 set 操作 |
add | 記錄新增,對應 add 操作 |
remove | 記錄被刪除,對應 remove 操作 |
返回值說明
返回值 Watcher
上只有一個 close
方法,可以用於關閉監聽。
orderBy 與 limit
從 2.9.2
起,在監聽時支持使用 orderBy
和 limit
,如果不傳或版本號低於 2.9.2
,則默認按 id
降序排列(等同於 orderBy('id', 'desc')
),limit
默認不存在即取所有數據。
示例代碼:根據查詢條件監聽*
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)
}
})
示例代碼:監聽一個記錄的變化
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)
}
})
示例代碼:關閉監聽
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()