// utils/chat-core.js import GoEasy from '../static/lib/goeasy-2.13.24.esm.min'; import { jianquanxian } from './imAuth/jianquanxian'; let _globalPrivateHandler = null; let _globalGroupHandler = null; let _globalConvHandler = null; 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); loadUserSettings(app); initNetworkListener(app); initAppStateListener(app); checkAndRestoreConnection(app); setupEventSystem(app); setupMessageListeners(app); } function getCurrentGoEasyUserId() { return (wx.goEasy && wx.goEasy.im && wx.goEasy.im.userId) || null; } function loadUserSettings(app) { try { const messageSettings = wx.getStorageSync(app.globalData.messageManager.cacheKeys.messageSettings); if (messageSettings) { app.globalData.messageManager = { ...app.globalData.messageManager, ...messageSettings }; } const connectionSettings = wx.getStorageSync(app.globalData.goEasyConnection.cacheKeys.savedConnection); if (connectionSettings) { app.globalData.goEasyConnection = { ...app.globalData.goEasyConnection, ...connectionSettings }; } const savedUnread = wx.getStorageSync(app.globalData.messageManager.cacheKeys.unreadTotal); if (savedUnread !== '') { app.globalData.messageManager.unreadTotal = savedUnread; updateTabBarBadge(app, savedUnread); } const savedMessages = wx.getStorageSync(app.globalData.messageManager.cacheKeys.latestMessages); if (savedMessages) { app.globalData.messageManager.latestMessages = savedMessages; } const notificationMuted = wx.getStorageSync(app.globalData.messageManager.cacheKeys.notificationMuted); if (notificationMuted !== '') { app.globalData.messageManager.notificationMuted = notificationMuted; } } catch (error) { console.warn('加载用户设置失败:', error); } } function initNetworkListener(app) { wx.onNetworkStatusChange((res) => { if (res.isConnected) { ensureConnection(app); } else { app.globalData.goEasyConnection.status = 'disconnected'; app.emitEvent('connectionChanged', { status: 'disconnected', reason: 'network' }); } }); } function initAppStateListener(app) { wx.onAppShow(() => { ensureConnection(app); updateCurrentPageState(app); // ✅ 连接正常时,主动刷一次未读角标 if (wx.goEasy.getConnectionStatus && wx.goEasy.getConnectionStatus() === 'connected') { loadConversations(app); } }); 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); } else { ensureConnection(app); } } } catch (error) { console.warn('检查保存的连接信息失败:', error); } } function setupEventSystem(app) { app.globalData.eventListeners = {}; } // 🔥 修改点:不依赖 saved 缓存,只要连接状态正常就刷新角标 function ensureConnection(app) { const connectionStatus = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected'; if (connectionStatus === 'connected' || connectionStatus === 'reconnected') { setupMessageListeners(app); loadConversations(app); // 每次进入前台都刷新未读 return; } // 连接不存在时尝试恢复 const saved = getSavedConnection(app); if (!saved || !saved.userId || !saved.identityType) return; connectWithIdentity(app, saved.identityType, saved.userId, true); } function getSavedConnection(app) { try { const saved = wx.getStorageSync(app.globalData.goEasyConnection.cacheKeys.savedConnection); return saved ? JSON.parse(saved) : null; } 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) { return new Promise((resolve) => { app.globalData.goEasyConnection.status = 'disconnected'; app.globalData.goEasyConnection.autoReconnect = false; stopHeartbeat(app); removeGlobalMessageListeners(); if (wx.goEasy && wx.goEasy.disconnect) { wx.goEasy.disconnect({ onSuccess: () => { clearSavedConnection(app); app.globalData.messageManager.unreadTotal = 0; updateTabBarBadge(app, 0); app.emitEvent('connectionChanged', { status: 'disconnected', manual: true }); resolve(); }, onFailed: () => { clearSavedConnection(app); app.globalData.messageManager.unreadTotal = 0; updateTabBarBadge(app, 0); resolve(); } }); } else { clearSavedConnection(app); app.globalData.messageManager.unreadTotal = 0; updateTabBarBadge(app, 0); resolve(); } }); } async function connectWithIdentity(app, identityType, userId, isAutoRestore = false) { const quanxian = await jianquanxian(app); if (!quanxian.allowed) return Promise.reject(quanxian.reason); 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); const uid = wx.getStorageSync('uid'); const currentUser = app.globalData.currentUser || { id: uid, name: '用户' + (uid ? uid.substring(0, 6) : ''), avatar: app.globalData.ossImageUrl + app.globalData.morentouxiang, }; return new Promise((resolve, reject) => { wx.goEasy.connect({ id: userId, data: { name: currentUser.name, avatar: currentUser.avatar, 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 }); if (!isAutoRestore) { wx.showToast({ title: '连接成功', icon: 'success', duration: 2000 }); } resolve(); }, onFailed: (error) => { console.error('GoEasy连接失败:', error); 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 = 'disconnected'; app.emitEvent('connectionChanged', { status: 'disconnected', error, isAutoRestore }); const { autoReconnect, reconnectAttempts, maxReconnectAttempts } = app.globalData.goEasyConnection; if (autoReconnect && reconnectAttempts < maxReconnectAttempts) { app.globalData.goEasyConnection.reconnectAttempts++; setTimeout(() => { if (app.globalData.goEasyConnection.autoReconnect) { connectWithIdentity(app, identityType, userId, true); } }, app.globalData.goEasyConnection.config.reconnectDelay); reject(error); } else { reject(error); } }, }); }); } function connectForCurrentRole(app) { const uid = wx.getStorageSync('uid'); if (!uid) return; const role = app.globalData.currentRole || 'normal'; const prefixMap = { normal: 'Boss', dashou: 'Ds', shangjia: 'Sj', guanshi: 'Gs', zuzhang: 'Zz' , kaoheguan: 'Kh'}; const prefix = prefixMap[role] || 'Boss'; const targetUserId = prefix + uid; const currentUserId = getCurrentGoEasyUserId(); if (currentUserId === targetUserId) { loadConversations(app); setupMessageListeners(app); return; } if (currentUserId) { wx.goEasy.disconnect({ onSuccess: () => {}, onFailed: () => {} }); } connectWithIdentity(app, role, targetUserId, false); } async function switchRoleAndReconnect(app, newRole) { try { await disconnectGoEasy(app); app.globalData.currentRole = newRole; wx.setStorageSync('currentRole', newRole); const uid = wx.getStorageSync('uid'); const prefixMap = { normal: 'Boss', dashou: 'Ds', shangjia: 'Sj', guanshi: 'Gs', zuzhang: 'Zz' , kaoheguan: 'Kh'}; const prefix = prefixMap[newRole] || 'Boss'; const targetUserId = prefix + uid; await connectWithIdentity(app, newRole, targetUserId, false); return true; } catch (error) { wx.showToast({ title: '聊天功能不可用,可能是人数爆满或未充值会员', icon: 'none' }); throw error; } } function startHeartbeat(app) { const { goEasyConnection } = app.globalData; stopHeartbeat(app); goEasyConnection.heartbeatInterval = setInterval(() => { if (goEasyConnection.status === 'connected' && wx.goEasy?.im) { wx.goEasy.im.ping({ onSuccess: () => {}, onFailed: (error) => { console.error('心跳失败:', error); goEasyConnection.status = 'reconnecting'; attemptReconnect(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) return; if (goEasyConnection.userId && goEasyConnection.identityType) { connectWithIdentity(app, goEasyConnection.identityType, goEasyConnection.userId, true); } } /* ---------- 消息监听器 ---------- */ function removeGlobalMessageListeners() { 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) { removeGlobalMessageListeners(); _globalPrivateHandler = (message) => { app.handleNewMessage(message); }; _globalGroupHandler = (message) => { app.handleNewMessage(message); }; _globalConvHandler = (conversations) => { const unreadTotal = conversations.unreadTotal || 0; app.globalData.messageManager.unreadTotal = unreadTotal; updateTabBarBadge(app, unreadTotal); wx.setStorageSync(app.globalData.messageManager.cacheKeys.unreadTotal, unreadTotal); if (typeof app.emitEvent === 'function') { app.emitEvent('conversationsUpdated', conversations); } }; 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 > 200) { app._processedMessages.delete(app._processedMessages.values().next().value); } const now = Date.now(); const msgTime = message.timestamp || now; if (now - msgTime > 60000) { cacheMessage(app, message); return; } cacheMessage(app, message); if (shouldShowNotification(app)) { 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) { if (app.globalData.messageManager.notificationMuted) return false; if (isDoNotDisturbTime(app)) return false; if (app.globalData.pageState.isInChatPage) { const pages = getCurrentPages(); if (pages.length > 0) { const currentPage = pages[pages.length - 1]; if (currentPage.data && currentPage.data.currentChatId) { if (isMessageFromCurrentChat(app, currentPage.data.currentChatId)) 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; } else { return currentTime >= startTime || currentTime < endTime; } } function isMessageFromCurrentChat(app, currentChatId) { return false; } 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); } 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; } } app.globalData.messageManager.unreadTotal = unreadTotal; updateTabBarBadge(app, unreadTotal); wx.setStorageSync(app.globalData.messageManager.cacheKeys.unreadTotal, unreadTotal); app.emitEvent('unreadCountChanged', { 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) { 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 loadConversations(app) { if (!getCurrentGoEasyUserId()) return; wx.goEasy.im.latestConversations({ onSuccess: (result) => { if (result.unreadTotal !== undefined) { updateUnreadCount(app, result.unreadTotal); } app.emitEvent('conversationsUpdated', result); const conversations = result?.content?.conversations || result?.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) => console.error('加载会话列表失败:', error), }); } 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') { console.error('subscribeGroup 不可用'); return; } wx.goEasy.im.subscribeGroup({ groupIds: groupIds, onSuccess: () => {}, onFailed: (error) => console.error('全局订阅群组失败:', error), }); } function updateCurrentPageState(app) { try { const pages = getCurrentPages(); if (pages.length > 0) { const currentPage = pages[pages.length - 1]; app.globalData.pageState.currentPage = currentPage.route; app.globalData.pageState.isInChatPage = currentPage.route === 'pages/liaotian/liaotian'; 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(); if (pages.length > 0) { const tabBar = pages[pages.length - 1].selectComponent('#custom-tab-bar'); if (tabBar && tabBar.setData) tabBar.setData({ badgeText }); } } catch (error) { console.warn('直接更新TabBar失败:', error); } } function saveTabBarBadgeToStorage(app, badgeText) { try { wx.setStorageSync('tabBarMessageBadge', badgeText); } catch (error) { console.warn('保存TabBar徽章失败:', error); } } module.exports = { initGlobalMessageSystem };