/** * AI角色主动推送消息工具模块 * * 功能说明: * 1. 跟进消息:发送给已聊过天但一段时间没互动的用户 * 2. 打招呼消息:发送给还没有和任何角色聊过天的新用户 */ const api = require('./api') const config = require('../config/index') // 上次检查时间(避免频繁请求) let lastCheckTime = 0 // 检查间隔(毫秒)- 默认5分钟 const CHECK_INTERVAL = 5 * 60 * 1000 /** * 获取待接收的主动推送消息 * @returns {Promise} 消息列表 */ async function getPendingMessages() { try { const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN) if (!token) { console.log('[proactiveMessage] 未登录,跳过获取推送消息') return [] } console.log('[proactiveMessage] 开始获取待推送消息...') const res = await api.proactiveMessage.getPending() console.log('[proactiveMessage] API响应:', JSON.stringify(res)) if (res.success && res.data) { console.log('[proactiveMessage] 获取到消息数量:', res.data.length) return res.data || [] } return [] } catch (error) { console.error('[proactiveMessage] 获取推送消息失败:', error) return [] } } /** * 检查并处理推送消息 * 在首页 onShow 或定时调用 * @param {object} options - 配置选项 * @param {boolean} options.force - 是否强制检查(忽略时间间隔) * @param {function} options.onNewMessages - 收到新消息时的回调 * @returns {Promise} 消息列表 */ async function checkAndShowMessages(options = {}) { const { force = false, onNewMessages } = options // 检查登录状态 const app = getApp() if (!app || !app.globalData || !app.globalData.isLoggedIn) { console.log('[proactiveMessage] 未登录,跳过检查') return [] } // 检查时间间隔(避免频繁请求) const now = Date.now() if (!force && lastCheckTime && (now - lastCheckTime < CHECK_INTERVAL)) { console.log('[proactiveMessage] 距离上次检查不足5分钟,跳过。上次:', lastCheckTime, '现在:', now) return [] } console.log('[proactiveMessage] 开始检查推送消息,force:', force) lastCheckTime = now const messages = await getPendingMessages() if (messages.length > 0) { console.log('[proactiveMessage] 收到推送消息:', messages.length, '条') console.log('[proactiveMessage] 消息详情:', JSON.stringify(messages)) // 更新会话列表的未读状态 messages.forEach(msg => { updateConversationUnread(msg) }) // 触发回调 if (typeof onNewMessages === 'function') { onNewMessages(messages) } } else { console.log('[proactiveMessage] 没有待推送消息') } return messages } /** * 更新会话列表未读状态 * @param {object} msg - 推送消息对象 */ function updateConversationUnread(msg) { // 获取当前会话列表缓存 const cacheKey = 'proactive_messages_cache' let cache = wx.getStorageSync(cacheKey) || {} // 记录消息,避免重复处理 const msgKey = `${msg.character_id}_${msg.sent_at}` if (cache[msgKey]) { return // 已处理过 } cache[msgKey] = { character_id: msg.character_id, character_name: msg.character_name, content: msg.content, message_type: msg.message_type, sent_at: msg.sent_at, processed_at: new Date().toISOString() } // 清理过期缓存(保留最近24小时的记录) const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000 Object.keys(cache).forEach(key => { const item = cache[key] if (new Date(item.processed_at).getTime() < oneDayAgo) { delete cache[key] } }) wx.setStorageSync(cacheKey, cache) // 触发页面更新 triggerPageRefresh() } /** * 触发页面刷新 * 通知当前页面刷新会话列表 */ function triggerPageRefresh() { const pages = getCurrentPages() if (pages.length === 0) return const currentPage = pages[pages.length - 1] // 如果当前页面有刷新会话列表的方法,调用它 if (currentPage && typeof currentPage.loadConversations === 'function') { currentPage.loadConversations() } // 如果当前页面有刷新未读数的方法,调用它 if (currentPage && typeof currentPage.loadUnreadCount === 'function') { currentPage.loadUnreadCount() } } /** * 获取角色的未处理推送消息 * @param {string} characterId - 角色ID * @returns {object|null} 消息对象 */ function getCharacterPendingMessage(characterId) { const cacheKey = 'proactive_messages_cache' const cache = wx.getStorageSync(cacheKey) || {} // 查找该角色的最新消息 let latestMsg = null Object.values(cache).forEach(msg => { if (msg.character_id === characterId) { if (!latestMsg || new Date(msg.sent_at) > new Date(latestMsg.sent_at)) { latestMsg = msg } } }) return latestMsg } /** * 清除角色的推送消息缓存 * 用户进入聊天后调用 * @param {string} characterId - 角色ID */ function clearCharacterMessages(characterId) { const cacheKey = 'proactive_messages_cache' let cache = wx.getStorageSync(cacheKey) || {} // 删除该角色的所有消息 Object.keys(cache).forEach(key => { if (cache[key].character_id === characterId) { delete cache[key] } }) wx.setStorageSync(cacheKey, cache) } /** * 标记角色的推送消息为已读 * 调用后端API标记已读,同时清除本地缓存 * @param {string} characterId - 角色ID */ async function markAsRead(characterId) { if (!characterId) { console.log('[proactiveMessage] 没有角色ID,跳过标记已读') return } // 先清除本地缓存 clearCharacterMessages(characterId) // 调用后端API标记已读 try { const res = await api.proactiveMessage.markAsRead({ character_id: characterId }) console.log('[proactiveMessage] 标记已读结果:', JSON.stringify(res)) } catch (err) { console.log('[proactiveMessage] 标记已读失败:', err) } } /** * 按消息ID列表标记已读 * @param {Array} messageIds - 消息ID列表 */ async function markAsReadByIds(messageIds) { if (!messageIds || messageIds.length === 0) { return } try { const res = await api.proactiveMessage.markAsRead({ message_ids: messageIds }) console.log('[proactiveMessage] 按ID标记已读结果:', JSON.stringify(res)) } catch (err) { console.log('[proactiveMessage] 按ID标记已读失败:', err) } } /** * 启动定时检查 * @param {number} interval - 检查间隔(毫秒),默认5分钟 * @returns {number} 定时器ID */ function startPeriodicCheck(interval = CHECK_INTERVAL) { return setInterval(() => { checkAndShowMessages() }, interval) } /** * 停止定时检查 * @param {number} timerId - 定时器ID */ function stopPeriodicCheck(timerId) { if (timerId) { clearInterval(timerId) } } module.exports = { getPendingMessages, checkAndShowMessages, getCharacterPendingMessage, clearCharacterMessages, markAsRead, markAsReadByIds, startPeriodicCheck, stopPeriodicCheck, CHECK_INTERVAL }