Files
a_long/utils/chat-core.js
2026-06-14 02:38:05 +08:00

662 lines
23 KiB
JavaScript

// 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 };