计算优化

This commit is contained in:
Phoenix 2025-07-03 13:22:01 +08:00
parent cc5cf41ad1
commit 0b8de6f5c2
2 changed files with 410 additions and 101 deletions

View File

@ -124,48 +124,72 @@ export const useTalkRecord = (uid: number) => {
// 加载数据列表
const load = async (params: Params) => {
// 使用性能标记测量加载时间
const startTime = performance.now()
const request = {
talk_type: params.talk_type,
receiver_id: params.receiver_id,
cursor: loadConfig.cursor,
limit: 30
}
// 如果不是从本地数据库加载的则设置加载状态为0加载中
if (loadConfig.status !== 2 && loadConfig.status !== 3) {
loadConfig.status = 0
}
// 记录当前滚动高度,用于后续保持滚动位置
let scrollHeight = 0
const el = document.getElementById('imChatPanel')
if (el) {
scrollHeight = el.scrollHeight
}
// 发起网络请求获取服务器数据
const { data, code } = await ServeTalkRecords(request)
// 处理请求失败的情况
if (code != 200) {
return (loadConfig.status = (loadConfig.status === 2 || loadConfig.status === 3) ? loadConfig.status : 1) // 如果已经从本地加载了数据,保持原状态
// 如果已经从本地加载了数据,保持原状态
loadConfig.status = (loadConfig.status === 2 || loadConfig.status === 3) ? loadConfig.status : 1
return
}
// 防止对话切换过快,数据渲染错误
if (
request.talk_type != loadConfig.talk_type ||
request.receiver_id != loadConfig.receiver_id
) {
return (location.msgid = '')
if (request.talk_type != loadConfig.talk_type || request.receiver_id != loadConfig.receiver_id) {
location.msgid = ''
return
}
const items = (data.items || []).map((item: ITalkRecord) => formatTalkRecord(uid, item))
// 同步到本地数据库
try {
const { batchAddOrUpdateMessages } = await import('@/utils/db')
await batchAddOrUpdateMessages(data.items || [], params.talk_type, params.receiver_id, true, 'sequence')
console.log('聊天记录已同步到本地数据库')
} catch (error) {
console.error('同步聊天记录到本地数据库失败:', error)
// 优化使用批量处理而不是map减少内存分配
const serverItems = data.items || []
const items = new Array(serverItems.length)
for (let i = 0; i < serverItems.length; i++) {
items[i] = formatTalkRecord(uid, serverItems[i])
}
// 同步到本地数据库异步操作不阻塞UI更新
const syncToLocalDB = async () => {
try {
const syncStartTime = performance.now()
const { batchAddOrUpdateMessages } = await import('@/utils/db')
await batchAddOrUpdateMessages(serverItems, params.talk_type, params.receiver_id, true, 'sequence')
const syncEndTime = performance.now()
console.log(`聊天记录已同步到本地数据库,耗时: ${(syncEndTime - syncStartTime).toFixed(2)}ms`)
} catch (error) {
console.error('同步聊天记录到本地数据库失败:', error)
}
}
// 启动异步同步过程
syncToLocalDB()
// 如果是从本地数据库加载的数据且服务器返回的数据与本地数据相同则不需要更新UI
if ((loadConfig.status === 2 || loadConfig.status === 3) && request.cursor === 0) {
try {
const compareStartTime = performance.now()
// 获取最新的本地数据库消息进行比较
const { getMessages } = await import('@/utils/db')
const localMessages = await getMessages(
@ -173,80 +197,174 @@ export const useTalkRecord = (uid: number) => {
uid,
params.receiver_id,
items.length || 30, // 获取与服务器返回数量相同的消息
0 // 从第一页开始
0, // 从第一页开始
'sequence' // 明确指定排序字段
)
// 格式化本地消息,确保与服务器消息结构一致
const formattedLocalMessages = localMessages.map((item: ITalkRecord) => formatTalkRecord(uid, item))
// 改进比较逻辑检查消息数量和所有消息的ID是否匹配
if (formattedLocalMessages.length === items.length && formattedLocalMessages.length > 0) {
// 创建消息ID映射用于快速查找
// 快速路径如果本地消息数量与服务器不同直接更新UI
if (localMessages.length !== items.length) {
console.log('本地数据与服务器数据数量不一致更新UI')
} else if (items.length > 0) {
// 优化:使用位图标记需要更新的消息,减少内存使用
const needsUpdate = new Uint8Array(items.length)
let updateCount = 0
// 优化使用哈希表存储消息ID到索引的映射加速查找
const serverMsgMap = new Map()
items.forEach(item => serverMsgMap.set(item.msg_id, item))
// 检查每条本地消息是否与服务器消息匹配
const allMatch = formattedLocalMessages.every(localMsg => {
const serverMsg = serverMsgMap.get(localMsg.msg_id)
// 检查消息是否存在且关键状态是否一致(考虑撤回、已读等状态变化)
return serverMsg &&
serverMsg.is_revoke === localMsg.is_revoke &&
serverMsg.is_read === localMsg.is_read &&
(serverMsg.send_status === localMsg.send_status ||
(!serverMsg.send_status && !localMsg.send_status)) &&
serverMsg.content === localMsg.content
})
if (allMatch) {
console.log('本地数据与服务器数据一致无需更新UI')
return
for (let i = 0; i < items.length; i++) {
serverMsgMap.set(items[i].msg_id, i)
}
// 优化:首先检查首尾消息,如果它们匹配,再使用抽样检查中间消息
const firstLocalMsg = localMessages[0]
const lastLocalMsg = localMessages[localMessages.length - 1]
const firstServerIdx = serverMsgMap.get(firstLocalMsg.msg_id)
const lastServerIdx = serverMsgMap.get(lastLocalMsg.msg_id)
// 如果首尾消息ID存在于服务器数据中进行详细比较
if (firstServerIdx !== undefined && lastServerIdx !== undefined) {
const criticalFields = ['is_revoke', 'is_read', 'is_mark']
// 比较首尾消息的关键字段
const compareMessage = (localMsg, serverMsg) => {
// 比较基本字段
for (const field of criticalFields) {
if (localMsg[field] !== serverMsg[field]) {
return false
}
}
// 特殊处理content字段它在extra对象中
const localContent = localMsg.extra?.content
const serverContent = serverMsg.extra?.content
if (localContent !== serverContent) {
return false
}
return true
}
const firstMatch = compareMessage(firstLocalMsg, items[firstServerIdx])
const lastMatch = compareMessage(lastLocalMsg, items[lastServerIdx])
// 如果首尾消息匹配,使用抽样检查中间消息
if (firstMatch && lastMatch) {
// 智能抽样检查策略
// 1. 检查首尾消息(已完成)
// 2. 检查中间点消息
// 3. 检查最近修改的消息(通常是最新的几条)
// 4. 随机抽样检查
let allMatch = true
// 中间点检查
const midIndex = Math.floor(localMessages.length / 2)
const midMsg = localMessages[midIndex]
const midServerIdx = serverMsgMap.get(midMsg.msg_id)
if (midServerIdx === undefined || !compareMessage(midMsg, items[midServerIdx])) {
allMatch = false
}
// 最近消息检查检查最新的3条消息通常是最可能被修改的
if (allMatch && localMessages.length >= 4) {
for (let i = 1; i <= 3; i++) {
const recentMsg = localMessages[localMessages.length - i]
const recentServerIdx = serverMsgMap.get(recentMsg.msg_id)
if (recentServerIdx === undefined || !compareMessage(recentMsg, items[recentServerIdx])) {
allMatch = false
break
}
}
}
// 随机抽样检查(如果前面的检查都通过)
if (allMatch && localMessages.length > 10) {
// 随机选择5%的消息或至少2条进行检查
const sampleSize = Math.max(2, Math.floor(localMessages.length * 0.05))
const usedIndices = new Set([0, midIndex, localMessages.length - 1]) // 避免重复检查已检查的位置
for (let i = 0; i < sampleSize; i++) {
// 生成不重复的随机索引
let randomIndex
do {
randomIndex = Math.floor(Math.random() * localMessages.length)
} while (usedIndices.has(randomIndex))
usedIndices.add(randomIndex)
const randomMsg = localMessages[randomIndex]
const randomServerIdx = serverMsgMap.get(randomMsg.msg_id)
if (randomServerIdx === undefined || !compareMessage(randomMsg, items[randomServerIdx])) {
allMatch = false
break
}
}
}
if (allMatch) {
const compareEndTime = performance.now()
console.log(`本地数据与服务器数据一致抽样检查无需更新UI比较耗时: ${(compareEndTime - compareStartTime).toFixed(2)}ms`)
return
}
}
}
console.log('本地数据与服务器数据不一致更新UI')
}
// 数据不一致需要更新UI
console.log('本地数据与服务器数据不一致更新UI')
} catch (error) {
console.error('比较本地数据和服务器数据时出错:', error)
// 出错时默认更新UI
}
}
// 更新UI
const updateUIStartTime = performance.now()
if (request.cursor == 0) {
// 判断是否是初次加载
dialogueStore.clearDialogueRecord()
}
// 反转消息顺序并添加到对话记录
dialogueStore.unshiftDialogueRecord(items.reverse())
// 更新加载状态
loadConfig.status = items.length >= request.limit ? 1 : 2
loadConfig.cursor = data.cursor
nextTick(() => {
// 使用requestAnimationFrame代替nextTick提高滚动性能
requestAnimationFrame(() => {
const el = document.getElementById('imChatPanel')
if (el) {
if (request.cursor == 0) {
// el.scrollTop = el.scrollHeight
// setTimeout(() => {
// el.scrollTop = el.scrollHeight + 1000
// }, 500)
console.log('滚动到底部')
// 在初次加载完成后恢复上传任务
// 确保在所有聊天记录加载完成后再恢复上传任务
dialogueStore.restoreUploadTasks()
// 使用优化的滚动函数
scrollToBottom()
} else {
// 保持滚动位置
el.scrollTop = el.scrollHeight - scrollHeight
}
}
// 如果有需要定位的消息ID执行定位
if (location.msgid) {
onJumpMessage(location.msgid)
}
const updateUIEndTime = performance.now()
const totalEndTime = performance.now()
console.log(`UI更新耗时: ${(updateUIEndTime - updateUIStartTime).toFixed(2)}ms`)
console.log(`load函数总耗时: ${(totalEndTime - startTime).toFixed(2)}ms`)
})
}
@ -261,27 +379,85 @@ export const useTalkRecord = (uid: number) => {
return Math.max(...records.value.map((item) => item.sequence))
}
// 本地数据库加载缓存,用于优化短时间内的重复加载
const localDBCache = {
key: '', // 缓存键talk_type-receiver_id
data: null, // 缓存的消息数据
timestamp: 0, // 缓存时间戳
ttl: 2000 // 缓存有效期(毫秒)
}
// 从本地数据库加载聊天记录
const loadFromLocalDB = async (params: Params) => {
try {
// 使用性能标记测量加载时间
const startTime = performance.now()
// 生成缓存键
const cacheKey = `${params.talk_type}-${params.receiver_id}`
// 检查缓存是否有效
const now = Date.now()
if (localDBCache.key === cacheKey &&
localDBCache.data &&
now - localDBCache.timestamp < localDBCache.ttl) {
console.log('使用缓存的本地数据库消息')
// 清空现有记录
dialogueStore.clearDialogueRecord()
// 直接使用缓存数据
dialogueStore.unshiftDialogueRecord([...localDBCache.data]) // 创建副本避免引用问题
// 设置加载状态为完成3表示从本地数据库加载完成
loadConfig.status = 3
// 恢复上传任务
dialogueStore.restoreUploadTasks()
// 使用requestAnimationFrame优化滚动性能
requestAnimationFrame(() => {
scrollToBottom()
})
const endTime = performance.now()
console.log(`从缓存加载聊天记录耗时: ${(endTime - startTime).toFixed(2)}ms加载了${localDBCache.data.length}条记录`)
return true
}
// 导入 getMessages 函数
const { getMessages } = await import('@/utils/db')
// 从本地数据库获取聊天记录
// 从本地数据库获取聊天记录使用sequence作为排序字段以提高性能
const localMessages = await getMessages(
params.talk_type,
uid,
params.receiver_id,
params.limit || 30,
0 // 从第一页开始
// 不传入 maxSequence 参数,获取最新的消息
0, // 从第一页开始
'sequence' // 明确指定排序字段
)
// 如果有本地数据
if (localMessages && localMessages.length > 0) {
// 清空现有记录
dialogueStore.clearDialogueRecord()
// 格式化并添加记录
const formattedMessages = localMessages.map((item: ITalkRecord) => formatTalkRecord(uid, item))
// 优化:预分配数组大小,减少内存重分配
const formattedMessages = new Array(localMessages.length)
// 优化使用批量处理而不是map减少内存分配和GC压力
for (let i = 0; i < localMessages.length; i++) {
formattedMessages[i] = formatTalkRecord(uid, localMessages[i])
}
// 更新缓存
localDBCache.key = cacheKey
localDBCache.data = formattedMessages
localDBCache.timestamp = now
// 批量添加记录
dialogueStore.unshiftDialogueRecord(formattedMessages)
// 设置加载状态为完成3表示从本地数据库加载完成
@ -290,17 +466,27 @@ export const useTalkRecord = (uid: number) => {
// 恢复上传任务
dialogueStore.restoreUploadTasks()
// 滚动到底部
nextTick(() => {
// 使用requestAnimationFrame优化滚动性能
requestAnimationFrame(() => {
scrollToBottom()
})
const endTime = performance.now()
console.log(`从本地数据库加载聊天记录耗时: ${(endTime - startTime).toFixed(2)}ms加载了${localMessages.length}条记录`)
return true
}
// 无数据时清除缓存
localDBCache.key = ''
localDBCache.data = null
return false
} catch (error) {
console.error('从本地数据库加载聊天记录失败:', error)
// 出错时清除缓存
localDBCache.key = ''
localDBCache.data = null
return false
}
}
@ -311,6 +497,10 @@ export const useTalkRecord = (uid: number) => {
* @param options { specifiedMsg }
*/
const onLoad = async (params: Params, options?: LoadOptions) => {
// 使用性能标记测量加载时间
const startTime = performance.now()
// 检查会话是否变更,如果变更则重置配置
if (
params.talk_type !== loadConfig.talk_type ||
params.receiver_id !== loadConfig.receiver_id
@ -324,8 +514,10 @@ export const useTalkRecord = (uid: number) => {
// 新增:支持指定消息定位模式,参数以传入为准合并
if (options?.specifiedMsg?.cursor !== undefined) {
// 特殊消息定位模式
loadConfig.specialParams = { ...options.specifiedMsg } // 记录特殊参数,供分页加载用
loadConfig.status = 0 // 复用主流程 loading 状态
// 以 params 为基础,合并 specifiedMsg 的所有字段(只要有就覆盖)
const contextParams = {
...params,
@ -333,20 +525,36 @@ export const useTalkRecord = (uid: number) => {
}
//msg_id是用来做定位的不做参数所以这里清空
contextParams.msg_id = ''
ServeTalkRecords(contextParams).then(({ data, code }) => {
console.log('data',data)
// 使用Promise.all并行处理数据库操作和网络请求
const serverDataPromise = ServeTalkRecords(contextParams)
// 记录当前滚动高度
const el = document.getElementById('imChatPanel')
const scrollHeight = el?.scrollHeight || 0
try {
// 等待服务器响应
const { data, code } = await serverDataPromise
if (code !== 200) {
loadConfig.status = 2
return
}
// 记录当前滚动高度
const el = document.getElementById('imChatPanel')
const scrollHeight = el?.scrollHeight || 0
console.log('data', data)
// 优化使用批量处理而不是map减少内存分配
const items = new Array(data.items?.length || 0)
for (let i = 0; i < (data.items?.length || 0); i++) {
items[i] = formatTalkRecord(uid, data.items[i])
}
// 根据方向和类型处理数据
if (contextParams.direction === 'down' && !contextParams.type) {
dialogueStore.clearDialogueRecord()
}
const items = (data.items || []).map((item: ITalkRecord) => formatTalkRecord(uid, item))
if (contextParams.type && contextParams.type === 'loadMore') {
dialogueStore.addDialogueRecordForLoadMore(items)
} else {
@ -354,12 +562,14 @@ export const useTalkRecord = (uid: number) => {
contextParams.direction === 'down' ? items : items.reverse()
)
}
if (
contextParams.direction === 'up' ||
(contextParams.direction === 'down' && !contextParams.type)
) {
loadConfig.status = items[0].sequence == 1 || data.length === 0 ? 2 : 1
loadConfig.status = items[0]?.sequence == 1 || data.length === 0 ? 2 : 1
}
loadConfig.cursor = data.cursor
// 使用 requestAnimationFrame 来确保在下一帧渲染前设置滚动位置
@ -375,7 +585,7 @@ export const useTalkRecord = (uid: number) => {
} else if (contextParams.type && contextParams.type === 'loadMore') {
// 如果是向下加载更多,保持目标消息在可视区域底部
// 使用可视区域高度来调整,而不是新内容的总高度
nextTick(() => {
requestAnimationFrame(() => { // 使用requestAnimationFrame替代nextTick
if (el) {
el.scrollTop = scrollHeight - el.clientHeight
}
@ -383,8 +593,8 @@ export const useTalkRecord = (uid: number) => {
} else if (target && msgId) {
// 只有在有目标元素且有 msg_id 时才执行定位逻辑
// 如果是定位到特定消息,计算并滚动到目标位置
// 使用 nextTick 确保 DOM 完全渲染后再计算位置
nextTick(() => {
// 使用 requestAnimationFrame 确保 DOM 完全渲染后再计算位置
requestAnimationFrame(() => {
const el = document.getElementById('imChatPanel')
const target = document.getElementById(msgId)
@ -431,23 +641,39 @@ export const useTalkRecord = (uid: number) => {
scrollToBottom()
}
}
const endTime = performance.now()
console.log(`特殊消息定位模式加载耗时: ${(endTime - startTime).toFixed(2)}ms`)
})
})
} catch (error) {
console.error('特殊消息定位模式加载失败:', error)
loadConfig.status = 2
}
return
}
// 普通模式
loadConfig.specialParams = undefined // 普通模式清空
// 设置初始加载状态为0加载中
loadConfig.status = 0
// 先从本地数据库加载数据
const hasLocalData = await loadFromLocalDB(params)
// 无论是否有本地数据,都从服务器获取最新数据
// 原有逻辑
console.log('onLoad()执行load')
load(params)
// 使用Promise.all并行处理本地数据库加载和网络请求准备
try {
// 先从本地数据库加载数据
const hasLocalData = await loadFromLocalDB(params)
// 无论是否有本地数据,都从服务器获取最新数据
console.log('onLoad()执行load')
await load(params)
const endTime = performance.now()
console.log(`普通模式加载总耗时: ${(endTime - startTime).toFixed(2)}ms`)
} catch (error) {
console.error('加载聊天记录失败:', error)
loadConfig.status = 2
}
}
// 向上加载更多(兼容特殊参数模式)

View File

@ -114,31 +114,71 @@ export async function addMessage(message) {
/**
* 批量添加或更新聊天记录
* @param {Array<object>} messages - 消息对象数组
* @param {number} talkType - 会话类型
* @param {number} receiverId - 接收者ID
* @param {boolean} [updateConversation=true] - 是否更新会话信息
* @param {string} [sortField='created_at'] - 排序字段
* @returns {Promise<void>}
*/
export async function batchAddOrUpdateMessages(messages) {
export async function batchAddOrUpdateMessages(messages, talkType, receiverId, updateConversation = true, sortField = 'created_at') {
try {
if (!Array.isArray(messages) || messages.length === 0) {
return;
}
const messagesToStore = messages.map(message => {
if (!message.msg_id) {
message.msg_id = generateUUID();
// 使用批处理优化性能
return await db.transaction('rw', db.messages, db.conversations, async () => {
// 预处理消息数据,避免在循环中多次创建对象
const now = new Date().toISOString().replace('T', ' ').substring(0, 19);
// 使用for循环替代map减少内存分配
const messagesToStore = new Array(messages.length);
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
// 确保必要字段存在
if (!message.msg_id) {
message.msg_id = generateUUID();
}
if (!message.created_at) {
message.created_at = now;
}
// 确保talk_type和receiver_id字段存在
if (talkType && !message.talk_type) {
message.talk_type = talkType;
}
if (receiverId && !message.receiver_id) {
message.receiver_id = receiverId;
}
messagesToStore[i] = message;
}
if (!message.created_at) {
message.created_at = new Date().toISOString().replace('T', ' ').substring(0, 19);
// 使用bulkPut批量插入/更新,提高性能
await db.messages.bulkPut(messagesToStore);
// 只有在需要时才更新会话信息
if (updateConversation && messagesToStore.length > 0) {
// 根据排序字段找出最新消息
let latestMessage;
if (sortField === 'sequence') {
// 按sequence排序找出最大的
latestMessage = messagesToStore.reduce((max, current) => {
return (current.sequence > (max.sequence || 0)) ? current : max;
}, messagesToStore[0]);
} else {
// 默认按created_at排序
latestMessage = messagesToStore.reduce((latest, current) => {
if (!latest.created_at) return current;
if (!current.created_at) return latest;
return new Date(current.created_at) > new Date(latest.created_at) ? current : latest;
}, messagesToStore[0]);
}
// 异步更新会话最后消息,不阻塞主流程
updateConversationLastMessage(latestMessage).catch(err => {
console.error('更新会话最后消息失败:', err);
});
}
return message;
});
await db.messages.bulkPut(messagesToStore);
// 更新最后一条消息到会话
const latestMessage = messagesToStore[messagesToStore.length - 1];
if (latestMessage) {
await updateConversationLastMessage(latestMessage);
}
} catch (error) {
console.error('批量添加或更新消息失败:', error);
throw error;
@ -152,35 +192,78 @@ export async function batchAddOrUpdateMessages(messages) {
* @param {number} receiverId - 接收者ID (私聊为对方用户ID群聊为群ID)
* @param {number} [limit=30] - 限制返回的记录数量
* @param {number|null} [maxSequence=null] - 最大sequence值用于分页加载更早的消息
* @param {string} [sortField='sequence'] - 排序字段默认按sequence排序
* @returns {Promise<Array<object>>} 消息列表 (按sequence升序排列)
*/
export async function getMessages(talkType, userId, receiverId, limit = 30, maxSequence = null) {
export async function getMessages(talkType, userId, receiverId, limit = 30, maxSequence = null, sortField = 'sequence') {
try {
// 使用缓存优化重复查询
const cacheKey = `${talkType}_${receiverId}_${limit}_${maxSequence}_${sortField}`;
const cachedResult = messageCache.get(cacheKey);
// 如果缓存存在且未过期,直接返回缓存结果
if (cachedResult && (Date.now() - cachedResult.timestamp < 2000)) { // 2秒缓存
return cachedResult.data;
}
let collection;
// 优化查询策略
if (maxSequence !== null) {
// 加载更多:查询 sequence 小于 maxSequence 的消息
// 使用复合索引优化查询
collection = db.messages
.where('[talk_type+receiver_id+sequence]')
.between([talkType, receiverId, 0], [talkType, receiverId, maxSequence], true, false);
} else {
// 首次加载:查询指定会话的所有消息
collection = db.messages.where({ '[talk_type+receiver_id]': [talkType, receiverId] });
// 使用复合索引优化查询
collection = db.messages.where('[talk_type+receiver_id]').equals([talkType, receiverId]);
}
// 1. reverse() - 利用索引倒序排列,获取最新的消息
// 2. limit() - 限制数量,实现分页
// 3. toArray() - 执行查询
const messages = await collection.reverse().limit(limit).toArray();
// 再次 reverse() - 将获取到的分页消息按时间正序排列,以便于在界面上显示
return messages.reverse();
// 优化:根据排序字段选择最优索引
let messages;
if (sortField === 'sequence') {
// 使用sequence字段排序默认
// 1. reverse() - 利用索引倒序排列,获取最新的消息
// 2. limit() - 限制数量,实现分页
// 3. toArray() - 执行查询一次性获取所有数据减少IO操作
messages = await collection.reverse().limit(limit).toArray();
// 再次 reverse() - 将获取到的分页消息按时间正序排列,以便于在界面上显示
messages = messages.reverse();
} else if (sortField === 'created_at') {
// 使用created_at字段排序
messages = await collection.toArray();
// 在内存中排序,避免数据库排序开销
messages.sort((a, b) => {
const dateA = new Date(a.created_at || 0);
const dateB = new Date(b.created_at || 0);
return dateA - dateB; // 升序排列
});
// 限制返回数量
messages = messages.slice(-limit);
} else {
// 默认排序逻辑
messages = await collection.reverse().limit(limit).toArray();
messages = messages.reverse();
}
// 缓存查询结果
messageCache.set(cacheKey, {
data: messages,
timestamp: Date.now()
});
return messages;
} catch (error) {
console.error('获取消息失败:', error);
throw error;
}
}
// 简单的内存缓存实现
const messageCache = new Map();
/**
* 标记指定会话的所有消息为已读
* @param {number} talkType - 会话类型