924 lines
31 KiB
JavaScript
924 lines
31 KiB
JavaScript
// 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 } 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);
|
||
|
||
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 uid = wx.getStorageSync('uid');
|
||
if (!uid) return '';
|
||
const role = app.globalData.currentRole || '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;
|
||
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;
|
||
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) {
|
||
wx.goEasy.im.ping({
|
||
onSuccess: () => {
|
||
convTick += 1;
|
||
if (convTick % 3 === 0) {
|
||
loadConversations(app);
|
||
}
|
||
},
|
||
onFailed: (error) => {
|
||
console.error('心跳失败:', error);
|
||
if (isSdkConnected()) return;
|
||
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);
|
||
|
||
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 applyUnreadTotal(app, unreadTotal) {
|
||
const prev = app.globalData.messageManager.unreadTotal;
|
||
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) 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);
|
||
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/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');
|
||
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 };
|