UniApp 集成腾讯云 IM 消息搜索功能实战指南
一、功能实现原理
腾讯云 IM 通过 消息漫游 + 服务端搜索接口 实现消息检索,核心机制如下:
- 数据存储:消息默认存储7天(可扩展至30天)
- 索引构建:基于消息内容自动建立倒排索引
- 检索接口:提供关键词匹配、时间范围、发送者等多维度查询
- 结果排序:按消息时间戳倒序排列
二、核心实现步骤
1. 初始化搜索模块
// services/search.js
import { initIM } from './im'
const tim = initIM()
// 配置搜索参数
const SEARCH_CONFIG = {
MAX_KEYWORD_LENGTH: 30, // 最大关键词长度
DEFAULT_PAGE_SIZE: 20, // 默认分页大小
MAX_HISTORY_DAYS: 7 // 最大历史查询天数
}
// 创建搜索实例
export function createSearchEngine(options) {
return {
conversationID: options.conversationID,
keyword: '',
timeRange: {
startTime: Date.now() - 7 * 24 * 3600 * 1000,
endTime: Date.now()
},
page: 1,
pageSize: SEARCH_CONFIG.DEFAULT_PAGE_SIZE
}
}
2. 执行消息搜索
export async function executeSearch(engine) {
const tim = initIM()
try {
const res = await tim.getMessageList({
conversationID: engine.conversationID,
keyword: engine.keyword,
nextSeq: engine.page === 1 ? 0 : engine.nextSeq,
count: engine.pageSize
})
return {
messages: res.data.messageList || [],
hasNext: res.data.isCompleted === 0,
nextSeq: res.data.nextSeq
}
} catch (error) {
console.error('搜索失败:', error)
throw new Error('消息搜索失败,请检查网络')
}
}
3. 搜索结果渲染
<template>
<view class="search-results">
<scroll-view
scroll-y
@scrolltolower="loadMore"
>
<view
v-for="(msg, index) in results"
:key="msg.clientMsgID"
class="search-item"
>
<view class="highlight-text">
{{ msg.payload.text.replace(
new RegExp(engine.keyword, 'gi'),
(match) => `<mark>${match}</mark>`
) }}
</view>
<view class="meta-info">
{{ formatTime(msg.time) }} - {{ msg.from }}
</view>
</view>
<view v-if="loading" class="loading-tip">
加载中...
</view>
<view v-if="!hasNext" class="end-tip">
没有更多结果了
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
engine: createSearchEngine({
conversationID: 'current_conv_id'
}),
results: [],
loading: false,
hasNext: true
}
},
methods: {
async loadMore() {
if (this.loading || !this.hasNext) return
this.loading = true
const res = await executeSearch(this.engine)
this.results = [...this.results, ...res.messages]
this.hasNext = res.hasNext
this.loading = false
}
}
}
</script>
三、关键问题处理
1. 性能优化策略
// 防抖搜索(输入框场景)
let searchDebounce = null
export function debouncedSearch(engine, callback) {
clearTimeout(searchDebounce)
searchDebounce = setTimeout(async () => {
engine.page = 1
const res = await executeSearch(engine)
callback(res.messages, res.hasNext)
}, 300) // 300ms防抖
}
2. 复杂查询构建
// 高级查询构造器
export function buildAdvancedQuery(options) {
const tim = initIM()
return {
conversationID: options.convID,
keyword: options.keyword || '',
filter: {
fromAccount: options.from || [],
timeRange: options.timeRange || {
startTime: Date.now() - 7 * 24 * 3600 * 1000,
endTime: Date.now()
}
},
pageIndex: options.page || 1,
pageSize: options.pageSize || 20
}
}
// 使用示例
const query = buildAdvancedQuery({
convID: 'group123',
from: 'userA',
timeRange: {
startTime: new Date(2023, 0, 1).getTime(),
endTime: new Date(2023, 1, 1).getTime()
}
})
3. 敏感词过滤
// 集成腾讯云内容安全
export async function checkSensitiveWords(text) {
const res = await axios.post('https://cms.tencentcloudapi.com/', {
Action: 'TextModeration',
Content: text,
// 其他必要参数...
})
return res.data.Suggestion === 'Pass'
}
// 搜索前校验
export async function validateSearchKeyword(keyword) {
if (await checkSensitiveWords(keyword)) {
return keyword
}
throw new Error('包含敏感词汇')
}
四、高级功能扩展
1. 语音消息转文本搜索
// 集成语音识别服务
export async function transcribeAudioMessage(fileKey) {
const res = await axios.post('https://asr.tencentcloudapi.com/', {
Action: 'SentenceRecognition',
ProjectId: 0,
SubServiceType: 2,
EngSerViceType: '16k_zh',
SourceType: 1,
Url: fileKey
})
return res.data.Result
}
// 修改搜索逻辑
async function searchAudioMessages(engine) {
const { messages } = await executeSearch(engine)
const transcriptions = await Promise.all(
messages.filter(msg => msg.type === 'TIMAudioElem')
.map(async msg => ({
...msg,
transcription: await transcribeAudioMessage(msg.payload.fileKey)
}))
)
return transcriptions
}
2. 搜索结果聚合
// 按发送者聚合结果
function groupBySender(messages) {
return messages.reduce((acc, msg) => {
if (!acc[msg.from]) acc[msg.from] = []
acc[msg.from].push(msg)
return acc
}, {})
}
// 按日期聚合
function groupByDate(messages) {
return messages.reduce((acc, msg) => {
const dateKey = dayjs(msg.time).format('YYYY-MM-DD')
if (!acc[dateKey]) acc[dateKey] = []
acc[dateKey].push(msg)
return acc
}, {})
}
3. 搜索历史管理
// 本地存储搜索历史
export function saveSearchHistory(keyword) {
const history = uni.getStorageSync('search_history') || []
const newHistory = [...new Set([keyword, ...history.slice(0, 49)])]
uni.setStorageSync('search_history', newHistory)
}
// 清除历史记录
export function clearSearchHistory() {
uni.removeStorageSync('search_history')
}
五、常见问题排查
Q: 搜索结果不完整
A: 检查时间范围设置,确保endTime > startTime,且在有效期内(默认7天)Q: 特殊字符搜索异常
A: 对关键词进行URL编码处理:encodeURIComponent(keyword)
Q: 群聊消息搜索失败
A: 确认群类型是否为Private(私有群),Public群需群主操作Q: 消息内容高亮失效
A: 确保使用v-html指令渲染,并做好XSS防护:v-html="highlightedText"
六、性能优化建议
- 对超过1000条的会话禁用全局搜索
- 实现搜索结果虚拟滚动(仅渲染可视区域)
- 对高频关键词建立本地缓存(使用
uni.setStorage
) - 使用Web Worker处理复杂文本分析
- 限制最大并发搜索数为3