// utils/chat-core.js — 全局 IM 连接、监听、重连、角标 import GoEasy from '../static/lib/goeasy-2.13.24.esm.min'; import { jianquanxian } from './imAuth/jianquanxian'; import { parseOrderCardText } from './group-chat.js'; import { persistGroupMeta, getFreshImUser, getImUserIdForRole, getLocalImUserId, getLocalImIdentity } from './im-user.js'; let _globalPrivateHandler = null; let _globalGroupHandler = null; let _globalConvHandler = null; let _imLockDepth = 0; let _loadConvTimer = null; let _loadConvInFlight = false; async function withImLock(fn) { if (_imLockDepth > 0) { return await fn(); } _imLockDepth += 1; try { return await fn(); } finally { _imLockDepth -= 1; } } function initGlobalMessageSystem(app) { app.updateUnreadCount = (count) => updateUnreadCount(app, count); app.disconnectGoEasy = () => disconnectGoEasy(app); app.ensureConnection = () => ensureConnection(app); app.connectWithIdentity = (identityType, userId, isAutoRestore) => connectWithIdentity(app, identityType, userId, isAutoRestore); app.connectForCurrentRole = () => connectForCurrentRole(app); app.switchRoleAndReconnect = (newRole) => switchRoleAndReconnect(app, newRole); app.toggleDoNotDisturb = (enabled) => toggleDoNotDisturb(app, enabled); app.toggleNotificationMute = (enabled) => toggleNotificationMute(app, enabled); app.closeNotification = () => closeNotification(app); app.loadConversations = () => loadConversations(app); app.updateCurrentPageState = () => updateCurrentPageState(app); app.getSavedConnection = () => getSavedConnection(app); app.clearSavedConnection = () => clearSavedConnection(app); app.saveConnectionState = () => saveConnectionState(app); app.handleNewMessage = (message) => handleNewMessage(app, message); app.ensureImForRole = (role) => ensureImForRole(app, role); app.syncConnectionStatus = () => syncConnectionStatus(app); app.restoreTabBarBadge = () => restoreCachedBadge(app); app.startImWhenReady = () => startImWhenReady(app); loadUserSettings(app); initNetworkListener(app); initAppStateListener(app); bindGoEasyLifecycle(app); setupEventSystem(app); checkAndRestoreConnection(app); } function bindGoEasyLifecycle(app) { if (!wx.goEasy || app._goEasyLifecycleBound) return; app._goEasyLifecycleBound = true; try { wx.goEasy.on('connected', () => { app.globalData.goEasyConnection.status = 'connected'; app.globalData.goEasyConnection.autoReconnect = true; setupMessageListeners(app); loadConversations(app); startHeartbeat(app); syncConnectionStatus(app); }); wx.goEasy.on('disconnected', () => { app.globalData.goEasyConnection.status = 'disconnected'; if (!app._imSuppressDisconnectEvent) { app.emitEvent('connectionChanged', { status: 'disconnected' }); } if (app.globalData.goEasyConnection.autoReconnect) { setTimeout(() => ensureConnection(app), app.globalData.goEasyConnection.config.reconnectDelay); } }); } catch (e) { console.warn('绑定 GoEasy 生命周期失败:', e); } } function getCurrentGoEasyUserId() { return (wx.goEasy && wx.goEasy.im && wx.goEasy.im.userId) || null; } function getExpectedImUserId(app) { const fromLocal = getLocalImUserId(app); if (fromLocal) return fromLocal; const uid = wx.getStorageSync('uid'); if (!uid) return ''; const role = app.globalData.currentRole || getLocalImIdentity(app) || 'normal'; return getImUserIdForRole(role, uid); } function loadUserSettings(app) { try { const messageSettings = wx.getStorageSync(app.globalData.messageManager.cacheKeys.messageSettings); if (messageSettings) { app.globalData.messageManager = { ...app.globalData.messageManager, ...messageSettings }; } const notificationMuted = wx.getStorageSync(app.globalData.messageManager.cacheKeys.notificationMuted); if (notificationMuted !== '') { app.globalData.messageManager.notificationMuted = notificationMuted; } const savedUnread = wx.getStorageSync(app.globalData.messageManager.cacheKeys.unreadTotal); if (savedUnread !== '' && savedUnread !== undefined) { app.globalData.messageManager.unreadTotal = savedUnread; updateTabBarBadge(app, savedUnread); } const savedMessages = wx.getStorageSync(app.globalData.messageManager.cacheKeys.latestMessages); if (savedMessages) { app.globalData.messageManager.latestMessages = savedMessages; } } catch (error) { console.warn('加载用户设置失败:', error); } } function initNetworkListener(app) { wx.onNetworkStatusChange((res) => { if (res.isConnected) { app.globalData.goEasyConnection.autoReconnect = true; app.globalData.goEasyConnection.reconnectAttempts = 0; app.emitEvent('connectionChanged', { status: 'reconnecting', reason: 'network' }); ensureConnection(app); } else { app.globalData.goEasyConnection.status = 'disconnected'; app.emitEvent('connectionChanged', { status: 'disconnected', reason: 'network' }); } }); } function initAppStateListener(app) { wx.onAppShow(() => { updateCurrentPageState(app); app.globalData.goEasyConnection.autoReconnect = true; restoreCachedBadge(app); ensureConnection(app); setTimeout(() => loadConversations(app), 400); }); wx.onAppHide(() => { pauseHeartbeat(app); saveConnectionState(app); }); } function checkAndRestoreConnection(app) { try { const savedConnection = wx.getStorageSync(app.globalData.goEasyConnection.cacheKeys.savedConnection); if (savedConnection && savedConnection.userId && savedConnection.identityType) { const now = Date.now(); const lastTime = savedConnection.lastConnectTime || 0; const hoursDiff = (now - lastTime) / (1000 * 60 * 60); const validityHours = app.globalData.goEasyConnection.config.cacheValidityHours || 12; if (hoursDiff > validityHours) { clearSavedConnection(app); } } ensureConnection(app); } catch (error) { console.warn('检查保存的连接信息失败:', error); } } function setupEventSystem(app) { if (!app.globalData.eventListeners) { app.globalData.eventListeners = {}; } } function isSdkConnected() { if (!wx.goEasy?.getConnectionStatus) return false; const s = wx.goEasy.getConnectionStatus(); return s === 'connected' || s === 'reconnected'; } function isImConnected() { return isSdkConnected(); } /** 已连接且身份正确则直接恢复监听,避免重复 connect 触发 408 */ function adoptExistingConnection(app, identityType, userId) { app.globalData.goEasyConnection.status = 'connected'; app.globalData.goEasyConnection.userId = userId; app.globalData.goEasyConnection.identityType = identityType; app.globalData.goEasyConnection.autoReconnect = true; app.globalData.goEasyConnection.reconnectAttempts = 0; saveConnectionState(app); startHeartbeat(app); setupMessageListeners(app); loadConversations(app); syncConnectionStatus(app); } /** 根据 SDK 真实状态同步 UI(避免已连接仍显示未连接) */ function syncConnectionStatus(app) { if (app.globalData.chatEnabled === false) { app.emitEvent('connectionChanged', { status: 'disabled', reason: 'chat_disabled' }); return; } const expectedId = getExpectedImUserId(app); if (!expectedId) { app.emitEvent('connectionChanged', { status: 'disconnected', reason: 'no_uid' }); return; } const cur = getCurrentGoEasyUserId(); if (isSdkConnected() && (!cur || cur === expectedId)) { app.globalData.goEasyConnection.status = 'connected'; app.globalData.goEasyConnection.userId = cur || expectedId; app.globalData.goEasyConnection.autoReconnect = true; setupMessageListeners(app); loadConversations(app); restoreCachedBadge(app); app.emitEvent('connectionChanged', { status: 'connected' }); return; } const st = app.globalData.goEasyConnection.status; if (st === 'connecting' || st === 'reconnecting') { app.emitEvent('connectionChanged', { status: st }); } else { app.emitEvent('connectionChanged', { status: 'disconnected' }); } } function ensureConnection(app) { if (!wx.goEasy?.connect) return; if (app.globalData.chatEnabled === false) { syncConnectionStatus(app); return; } const expectedId = getExpectedImUserId(app); if (!expectedId) return; const role = app.globalData.currentRole || 'normal'; if (isImConnected()) { const cur = getCurrentGoEasyUserId(); if (cur === expectedId) { setupMessageListeners(app); startHeartbeat(app); loadConversations(app); syncConnectionStatus(app); return; } connectForCurrentRole(app); return; } app.globalData.goEasyConnection.autoReconnect = true; app.emitEvent('connectionChanged', { status: 'connecting' }); const saved = getSavedConnection(app); if (saved && saved.userId === expectedId && saved.identityType === role) { connectWithIdentity(app, saved.identityType, saved.userId, true); return; } connectForCurrentRole(app); } async function ensureImForRole(app, role) { const uid = wx.getStorageSync('uid'); if (!uid) throw new Error('未登录'); const targetUserId = getImUserIdForRole(role, uid); if (!targetUserId) throw new Error('无效身份'); if (isImConnected() && getCurrentGoEasyUserId() === targetUserId) { setupMessageListeners(app); loadConversations(app); syncConnectionStatus(app); return; } if (app.globalData.currentRole !== role) { app.globalData.currentRole = role; wx.setStorageSync('currentRole', role); } await connectWithIdentity(app, role, targetUserId, true); } function getSavedConnection(app) { try { const saved = wx.getStorageSync(app.globalData.goEasyConnection.cacheKeys.savedConnection); if (!saved) return null; if (typeof saved === 'string') return JSON.parse(saved); return saved; } catch (error) { console.error('获取保存的连接信息失败:', error); return null; } } function saveConnectionState(app) { try { const { goEasyConnection } = app.globalData; const connectionState = { userId: goEasyConnection.userId, identityType: goEasyConnection.identityType, lastConnectTime: Date.now(), autoReconnect: goEasyConnection.autoReconnect, status: goEasyConnection.status, }; wx.setStorageSync(goEasyConnection.cacheKeys.savedConnection, JSON.stringify(connectionState)); } catch (error) { console.warn('保存连接状态失败:', error); } } function clearSavedConnection(app) { const { cacheKeys } = app.globalData.goEasyConnection; wx.removeStorageSync(cacheKeys.savedConnection); wx.removeStorageSync(cacheKeys.userId); wx.removeStorageSync(cacheKeys.identityType); app.globalData.goEasyConnection.userId = ''; app.globalData.goEasyConnection.identityType = ''; app.globalData.goEasyConnection.lastConnectTime = 0; } function disconnectGoEasy(app, options = {}) { const silent = options.silent === true; const keepAutoReconnect = options.keepAutoReconnect === true; return withImLock(() => new Promise((resolve) => { if (silent) app._imSuppressDisconnectEvent = true; app.globalData.goEasyConnection.status = 'disconnected'; if (!keepAutoReconnect) { app.globalData.goEasyConnection.autoReconnect = false; } stopHeartbeat(app); removeGlobalMessageListeners(); const finish = () => { clearSavedConnection(app); if (!keepAutoReconnect) { app.globalData.messageManager.unreadTotal = 0; updateTabBarBadge(app, 0); } if (!silent) { app.emitEvent('connectionChanged', { status: 'disconnected', manual: true }); } app._imSuppressDisconnectEvent = false; resolve(); }; if (wx.goEasy && wx.goEasy.disconnect) { wx.goEasy.disconnect({ onSuccess: finish, onFailed: finish, }); } else { finish(); } })); } async function connectWithIdentity(app, identityType, userId, isAutoRestore = false) { if (!wx.goEasy?.connect) { return Promise.reject(new Error('聊天服务未就绪')); } return withImLock(async () => { if (!isAutoRestore) { const quanxian = await jianquanxian(app); if (!quanxian.allowed) { throw new Error(quanxian.reason || '无聊天权限'); } } else { const uid = wx.getStorageSync('uid'); if (!uid) throw new Error('未登录'); } const currentId = getCurrentGoEasyUserId(); if (isSdkConnected()) { if (!currentId || currentId === userId) { adoptExistingConnection(app, identityType, userId); return; } await disconnectGoEasy(app, { silent: true, keepAutoReconnect: true }); app.globalData.goEasyConnection.autoReconnect = true; await new Promise((r) => setTimeout(r, 400)); } if (currentId && currentId !== userId && !isSdkConnected()) { await disconnectGoEasy(app, { silent: true, keepAutoReconnect: true }); app.globalData.goEasyConnection.autoReconnect = true; await new Promise((r) => setTimeout(r, 400)); } app.globalData.goEasyConnection.autoReconnect = true; app.globalData.goEasyConnection.status = 'connecting'; app.globalData.goEasyConnection.userId = userId; app.globalData.goEasyConnection.identityType = identityType; if (!isAutoRestore) app.globalData.goEasyConnection.reconnectAttempts = 0; saveConnectionState(app); app.emitEvent('connectionChanged', { status: 'connecting', identityType, userId }); const uid = wx.getStorageSync('uid'); const role = identityType || app.globalData.currentRole || 'normal'; const freshUser = getFreshImUser(app, role, uid); const currentUser = app.globalData.currentUser || { id: uid, name: freshUser.name, avatar: freshUser.avatar, }; return new Promise((resolve, reject) => { wx.goEasy.connect({ id: userId, data: { name: freshUser.name || currentUser.name, avatar: freshUser.avatar || currentUser.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang), identityType, }, onSuccess: () => { app.globalData.goEasyConnection.status = 'connected'; app.globalData.goEasyConnection.lastConnectTime = Date.now(); app.globalData.goEasyConnection.reconnectAttempts = 0; saveConnectionState(app); startHeartbeat(app); setupMessageListeners(app); loadConversations(app); app.emitEvent('connectionChanged', { status: 'connected', identityType, userId }); resolve(); }, onFailed: (error) => { console.error('GoEasy连接失败:', error); // 408 = 已连接,勿再 connect,直接复用当前连接 if (error.code === 408) { adoptExistingConnection(app, identityType, userId); resolve(); return; } if (error.code === 900) { app.globalData.goEasyConnection.autoReconnect = false; app.globalData.goEasyConnection.status = 'disconnected'; clearSavedConnection(app); stopHeartbeat(app); wx.showModal({ title: '消息功能暂不可用', content: '当前使用人数爆满,消息功能暂时无法使用。请联系管理员升级套餐后重试。', showCancel: false, confirmText: '我知道了', }); app.emitEvent('connectionChanged', { status: 'disconnected', error, reason: 'overcapacity' }); reject(error); return; } app.globalData.goEasyConnection.status = 'reconnecting'; app.emitEvent('connectionChanged', { status: 'reconnecting', error, isAutoRestore }); const { autoReconnect, reconnectAttempts, maxReconnectAttempts } = app.globalData.goEasyConnection; if (autoReconnect && reconnectAttempts < maxReconnectAttempts && !isSdkConnected()) { app.globalData.goEasyConnection.reconnectAttempts++; const delay = app.globalData.goEasyConnection.config.reconnectDelay * reconnectAttempts; setTimeout(() => { if (app.globalData.goEasyConnection.autoReconnect) { connectWithIdentity(app, identityType, userId, true).then(resolve).catch(reject); } }, delay); } else { reject(error); } }, }); }); }); } async function connectForCurrentRole(app) { const uid = wx.getStorageSync('uid'); if (!uid) return; const role = app.globalData.currentRole || 'normal'; const targetUserId = getImUserIdForRole(role, uid); const currentUserId = getCurrentGoEasyUserId(); if (currentUserId === targetUserId && isImConnected()) { setupMessageListeners(app); startHeartbeat(app); loadConversations(app); syncConnectionStatus(app); return; } if (currentUserId && currentUserId !== targetUserId) { await disconnectGoEasy(app, { silent: true, keepAutoReconnect: true }); app.globalData.goEasyConnection.autoReconnect = true; } await connectWithIdentity(app, role, targetUserId, true); } async function switchRoleAndReconnect(app, newRole) { try { await withImLock(async () => { await disconnectGoEasy(app, { silent: true, keepAutoReconnect: true }); app.globalData.currentRole = newRole; wx.setStorageSync('currentRole', newRole); app.globalData.goEasyConnection.autoReconnect = true; app.globalData.goEasyConnection.reconnectAttempts = 0; const uid = wx.getStorageSync('uid'); const targetUserId = getImUserIdForRole(newRole, uid); // 切换身份用轻量连接,避免鉴权接口失败把 IM 永久断开 await connectWithIdentity(app, newRole, targetUserId, true); }); return true; } catch (error) { console.warn('切换身份 IM 重连失败', error); syncConnectionStatus(app); throw error; } } function startHeartbeat(app) { const { goEasyConnection } = app.globalData; stopHeartbeat(app); let convTick = 0; goEasyConnection.heartbeatInterval = setInterval(() => { if (goEasyConnection.status === 'connected' && wx.goEasy?.im) { if (isSdkConnected()) { convTick += 1; if (convTick % 3 === 0) { loadConversations(app); } } else { goEasyConnection.status = 'reconnecting'; app.emitEvent('connectionChanged', { status: 'reconnecting' }); attemptReconnect(app); } } else if (goEasyConnection.autoReconnect && !isSdkConnected()) { ensureConnection(app); } }, goEasyConnection.config.heartbeatInterval); } function stopHeartbeat(app) { if (app.globalData.goEasyConnection.heartbeatInterval) { clearInterval(app.globalData.goEasyConnection.heartbeatInterval); app.globalData.goEasyConnection.heartbeatInterval = null; } } function pauseHeartbeat(app) { stopHeartbeat(app); } function attemptReconnect(app) { const { goEasyConnection } = app.globalData; if (!goEasyConnection.autoReconnect || isSdkConnected()) return; if (goEasyConnection.reconnectAttempts >= goEasyConnection.maxReconnectAttempts) { goEasyConnection.reconnectAttempts = 0; } if (goEasyConnection.userId && goEasyConnection.identityType) { connectWithIdentity(app, goEasyConnection.identityType, goEasyConnection.userId, true); } else { connectForCurrentRole(app); } } function removeGlobalMessageListeners() { if (!wx.goEasy?.im) return; if (_globalPrivateHandler) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _globalPrivateHandler); _globalPrivateHandler = null; } if (_globalGroupHandler) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _globalGroupHandler); _globalGroupHandler = null; } if (_globalConvHandler) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _globalConvHandler); _globalConvHandler = null; } } function setupMessageListeners(app) { if (!wx.goEasy?.im || typeof wx.goEasy.im.on !== 'function') { return; } removeGlobalMessageListeners(); _globalPrivateHandler = (message) => { app.handleNewMessage(message); }; _globalGroupHandler = (message) => { app.handleNewMessage(message); }; _globalConvHandler = (conversations) => { const normalized = normalizeConversationPayload(conversations); const unreadTotal = normalized?.unreadTotal ?? conversations?.unreadTotal ?? 0; applyUnreadTotal(app, unreadTotal); if (normalized && typeof app.emitEvent === 'function') { app.emitEvent('conversationsUpdated', normalized); } }; wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _globalPrivateHandler); wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _globalGroupHandler); wx.goEasy.im.on(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _globalConvHandler); } function handleNewMessage(app, message) { const messageId = message.messageId || message.id; const fingerprint = `${message.senderId}_${message.timestamp}_${message.type}`; const msgKey = messageId || fingerprint; if (!app._processedMessages) app._processedMessages = new Set(); if (app._processedMessages.has(msgKey)) return; app._processedMessages.add(msgKey); if (app._processedMessages.size > 300) { app._processedMessages.delete(app._processedMessages.values().next().value); } cacheMessage(app, message); loadConversations(app); if (shouldShowNotification(app, message)) { let notificationType = 'private'; let extraData = {}; if (message.groupId) { notificationType = 'group'; extraData = { groupId: message.groupId, orderId: message.orderId || '', groupName: '', groupAvatar: '', isCross: message.isCross || 0, }; const groupInfo = app.globalData.groupInfoMap?.[message.groupId]; if (groupInfo) { extraData.groupName = groupInfo.name || ''; extraData.groupAvatar = groupInfo.avatar || ''; } } else if (message.teamId) { notificationType = 'cs'; extraData = { teamId: message.teamId }; } showNotificationDirect(app, { senderId: message.senderId, senderName: message.senderData?.name || '用户', avatar: message.senderData?.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang), content: formatMessageForNotification(message), message, notificationType, ...extraData, }); } } function shouldShowNotification(app, message) { if (app.globalData.messageManager.notificationMuted) return false; if (isDoNotDisturbTime(app)) return false; if (isMessageFromCurrentChat(app, message)) return false; return true; } function isDoNotDisturbTime(app) { const { doNotDisturb, doNotDisturbStart, doNotDisturbEnd } = app.globalData.messageManager; if (!doNotDisturb) return false; const now = new Date(); const currentTime = now.getHours() * 60 + now.getMinutes(); const [sH, sM] = doNotDisturbStart.split(':').map(Number); const [eH, eM] = doNotDisturbEnd.split(':').map(Number); const startTime = sH * 60 + sM; const endTime = eH * 60 + eM; if (startTime < endTime) { return currentTime >= startTime && currentTime < endTime; } return currentTime >= startTime || currentTime < endTime; } function isMessageFromCurrentChat(app, message) { const pageState = app.globalData.pageState || {}; if (!pageState.isInChatPage || !pageState.currentChatId) return false; if (message.groupId) { return message.groupId === pageState.currentChatId; } const myId = app.globalData.goEasyConnection?.userId || getCurrentGoEasyUserId(); if (!myId) return false; const peerId = message.senderId === myId ? message.receiverId : message.senderId; return peerId === pageState.currentChatId || message.senderId === pageState.currentChatId; } function showNotificationDirect(app, data) { if (app.globalData.globalNotification?.show) { try { app.globalData.globalNotification.show(data); return; } catch (error) { console.warn('全局通知显示失败:', error); } } app.emitEvent('showNotification', data); } function startImWhenReady(app) { if (!wx.getStorageSync('uid')) return; app.globalData.goEasyConnection.autoReconnect = true; restoreCachedBadge(app); ensureConnection(app); setTimeout(() => { if (isSdkConnected()) { setupMessageListeners(app); loadConversations(app); } restoreCachedBadge(app); }, 450); } function restoreCachedBadge(app) { const { messageManager } = app.globalData; let unread = messageManager.unreadTotal; if (unread == null || unread === '') { try { const saved = wx.getStorageSync(messageManager.cacheKeys.unreadTotal); if (saved !== '' && saved != null) unread = Number(saved) || 0; } catch (e) {} } if (!unread) { try { const badgeStr = wx.getStorageSync('tabBarMessageBadge'); if (badgeStr && badgeStr !== '0') { unread = badgeStr === '99+' ? 100 : (parseInt(badgeStr, 10) || 0); } } catch (e) {} } if (unread > 0) { updateTabBarBadge(app, unread); } else if (messageManager.tabBarBadgeText) { app.emitEvent('tabBarBadgeChanged', { index: messageManager.tabBarIndex, badgeText: messageManager.tabBarBadgeText, showRedDot: true, forceUpdate: true, }); updateTabBarDirectly(app, messageManager.tabBarBadgeText); } } function applyUnreadTotal(app, unreadTotal) { const prev = app.globalData.messageManager.unreadTotal; const connected = isSdkConnected(); if (unreadTotal === 0 && !connected && prev > 0) { restoreCachedBadge(app); return; } app.globalData.messageManager.unreadTotal = unreadTotal; updateTabBarBadge(app, unreadTotal); wx.setStorageSync(app.globalData.messageManager.cacheKeys.unreadTotal, unreadTotal); if (prev !== unreadTotal) { app.emitEvent('unreadCountChanged', { unreadTotal }); } } async function updateUnreadCount(app, customCount) { let unreadTotal = customCount; if (unreadTotal === undefined) { try { const result = await getUnreadCount(app); unreadTotal = result.unreadTotal || 0; } catch (error) { console.error('获取未读数失败:', error); return; } } applyUnreadTotal(app, unreadTotal); } function getUnreadCount(app) { return new Promise((resolve, reject) => { if (!getCurrentGoEasyUserId()) { resolve({ unreadTotal: 0 }); return; } wx.goEasy.im.latestConversations({ onSuccess: (result) => resolve({ unreadTotal: result.unreadTotal || 0, conversations: result.content?.conversations || [], }), onFailed: reject, }); }); } function formatMessageForNotification(message) { switch (message.type) { case 'text': return message.payload.text || '[文本]'; case 'image': return '[图片]'; case 'audio': return '[语音]'; case 'video': return '[视频]'; case 'file': return '[文件]'; case 'order': return '[订单]'; default: return '[新消息]'; } } function cacheMessage(app, message) { if (message.groupId && message.type === 'text' && message.payload?.text) { const card = parseOrderCardText(message.payload.text); if (card && card.zhuangtai != null) { if (!app.globalData.groupInfoMap) app.globalData.groupInfoMap = {}; const meta = { ...(app.globalData.groupInfoMap[message.groupId] || {}), orderId: card.orderId, orderZhuangtai: card.zhuangtai, orderDesc: card.jieshao, }; app.globalData.groupInfoMap[message.groupId] = meta; persistGroupMeta(app, message.groupId, meta); } } const { latestMessages } = app.globalData.messageManager; latestMessages.unshift({ id: message.messageId, type: message.type, senderId: message.senderId, senderName: message.senderData?.name, content: formatMessageForNotification(message), timestamp: message.timestamp || Date.now(), conversationId: message.conversationId, }); if (latestMessages.length > 50) latestMessages.length = 50; wx.setStorageSync(app.globalData.messageManager.cacheKeys.latestMessages, latestMessages); } function normalizeConversationPayload(result) { if (!result) return null; const content = result.content || result; const list = content.conversations || result.conversations; if (!list || !Array.isArray(list)) return null; return { conversations: list, unreadTotal: content.unreadTotal ?? result.unreadTotal ?? 0, }; } function loadConversations(app) { if (_loadConvTimer) clearTimeout(_loadConvTimer); _loadConvTimer = setTimeout(() => { _loadConvTimer = null; loadConversationsNow(app); }, 280); } function loadConversationsNow(app) { if (_loadConvInFlight) return; if (!isSdkConnected() || !wx.goEasy?.im?.latestConversations) { restoreCachedBadge(app); if (app.globalData.goEasyConnection.autoReconnect) { ensureConnection(app); } return; } _loadConvInFlight = true; wx.goEasy.im.latestConversations({ onSuccess: (result) => { _loadConvInFlight = false; const normalized = normalizeConversationPayload(result); if (result.unreadTotal !== undefined) { applyUnreadTotal(app, result.unreadTotal); } else if (normalized) { applyUnreadTotal(app, normalized.unreadTotal); } if (normalized && typeof app.emitEvent === 'function') { app.emitEvent('conversationsUpdated', normalized); } const conversations = normalized?.conversations || []; if (!app.globalData.groupInfoMap) app.globalData.groupInfoMap = {}; conversations.forEach((c) => { if (c.type === 'group' && c.groupId) { app.globalData.groupInfoMap[c.groupId] = c.data || {}; } }); _ensureGroupSubscriptions(app, conversations); }, onFailed: (error) => { _loadConvInFlight = false; console.error('加载会话列表失败:', error); restoreCachedBadge(app); if (!isSdkConnected() && app.globalData.goEasyConnection.autoReconnect) { attemptReconnect(app); } }, }); } function _ensureGroupSubscriptions(app, conversations) { const groupIds = conversations .filter((c) => c.type === 'group' && c.groupId) .map((c) => c.groupId); if (groupIds.length === 0) return; if (!wx.goEasy?.im || typeof wx.goEasy.im.subscribeGroup !== 'function') return; wx.goEasy.im.subscribeGroup({ groupIds, onSuccess: () => {}, onFailed: (error) => console.error('全局订阅群组失败:', error), }); } function updateCurrentPageState(app) { try { const pages = getCurrentPages(); if (pages.length > 0) { const currentPage = pages[pages.length - 1]; const chatPages = [ 'pages/chat/chat', 'pages/group-chat/group-chat', 'pages/cs-chat/cs-chat', 'pages/liaotian/liaotian', 'pages/qunliaotian/qunliaotian', 'pages/kefuliaotian/kefuliaotian', ]; app.globalData.pageState.currentPage = currentPage.route; app.globalData.pageState.isInChatPage = chatPages.indexOf(currentPage.route) >= 0; app.globalData.pageState.lastPageUpdate = Date.now(); } } catch (error) { console.warn('更新页面状态失败:', error); } } function saveUserSettings(app) { try { wx.setStorageSync(app.globalData.messageManager.cacheKeys.messageSettings, { soundEnabled: app.globalData.messageManager.soundEnabled, vibrationEnabled: app.globalData.messageManager.vibrationEnabled, doNotDisturb: app.globalData.messageManager.doNotDisturb, doNotDisturbStart: app.globalData.messageManager.doNotDisturbStart, doNotDisturbEnd: app.globalData.messageManager.doNotDisturbEnd, notificationMuted: app.globalData.messageManager.notificationMuted, notificationStyle: app.globalData.messageManager.notificationStyle, }); } catch (error) { console.warn('保存用户设置失败:', error); } } function toggleDoNotDisturb(app, enabled) { app.globalData.messageManager.doNotDisturb = enabled; saveUserSettings(app); wx.showToast({ title: `免打扰${enabled ? '开启' : '关闭'}`, icon: 'success', duration: 2000 }); } function toggleNotificationMute(app, enabled) { app.globalData.messageManager.notificationMuted = enabled; wx.setStorageSync(app.globalData.messageManager.cacheKeys.notificationMuted, enabled); saveUserSettings(app); wx.showToast({ title: `通知${enabled ? '静音' : '开启'}`, icon: 'success', duration: 2000 }); } function closeNotification(app) { app.emitEvent('hideNotification'); } function updateTabBarBadge(app, unreadCount) { const { messageManager } = app.globalData; if (!messageManager.showTabBarBadge) return; let badgeText = ''; if (unreadCount > 0) { badgeText = unreadCount > 99 ? '99+' : unreadCount.toString(); } messageManager.tabBarBadgeText = badgeText; app.emitEvent('tabBarBadgeChanged', { index: messageManager.tabBarIndex, badgeText, showRedDot: unreadCount > 0, forceUpdate: true, }); updateTabBarDirectly(app, badgeText); saveTabBarBadgeToStorage(app, badgeText); } function updateTabBarDirectly(app, badgeText) { try { const pages = getCurrentPages(); for (let i = pages.length - 1; i >= 0; i--) { const tabBar = pages[i].selectComponent('#custom-tab-bar') || pages[i].selectComponent('tab-bar'); if (tabBar && tabBar.setData) { tabBar.setData({ badgeText }); break; } } } catch (error) { console.warn('直接更新TabBar失败:', error); } } function saveTabBarBadgeToStorage(app, badgeText) { try { wx.setStorageSync('tabBarMessageBadge', badgeText); } catch (error) { console.warn('保存TabBar徽章失败:', error); } } module.exports = { initGlobalMessageSystem };