Files
xingque/utils/chat-core.js
2026-07-09 00:17:03 +08:00

992 lines
33 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 };