修正了 pages 名为拼音的问题

This commit is contained in:
2026-06-13 10:44:02 +08:00
parent 76cc4ac55e
commit 296e41a8a7
249 changed files with 29660 additions and 29505 deletions

View File

@@ -1,400 +1,400 @@
// utils/chat-core.js — 全局 IM 连接、监听、角标(稳定版)
import GoEasy from '../static/lib/goeasy-2.13.24.esm.min';
import { resolveAvatarUrl } from './avatar.js';
import { getLocalImUserId, getLocalImIdentity } from './im-user.js';
let _convListener = null;
let _privateListener = null;
let _groupListener = null;
let _notificationHandler = null;
let _heartbeatTimer = null;
let _listenersBoundForUser = null;
let _lastGroupSubscribeAt = 0;
const _notifiedMessageIds = new Map();
const NOTIFY_MAX_AGE_MS = 15000;
function shouldShowNotification(message) {
if (!message) return false;
const id = message.messageId || `${message.senderId}_${message.timestamp}`;
if (_notifiedMessageIds.has(id)) return false;
if (message.timestamp && Date.now() - message.timestamp > NOTIFY_MAX_AGE_MS) return false;
_notifiedMessageIds.set(id, Date.now());
if (_notifiedMessageIds.size > 400) {
const cutoff = Date.now() - 60000;
_notifiedMessageIds.forEach((t, k) => {
if (t < cutoff) _notifiedMessageIds.delete(k);
});
}
return true;
}
function bindGlobalListenersIfNeeded(app, userId) {
if (_listenersBoundForUser === userId && _convListener) return;
registerGlobalListeners(app);
registerNotification(app);
_listenersBoundForUser = userId;
}
function initGlobalMessageSystem(app) {
app.ensureConnection = () => ensureConnection(app);
app.connectForCurrentRole = () => ensureConnection(app);
app.loadConversations = () => loadConversations(app);
app.disconnectGoEasy = () => disconnectGoEasy(app);
app.switchRoleAndReconnect = (newRole) => switchRoleAndReconnect(app, newRole);
app.updateUnreadCount = (count) => updateTabBarBadge(app, count || 0);
app.reregisterNotification = () => registerNotification(app);
app.startImWhenReady = () => startImWhenReady(app);
if (!app.globalData.messageManager) {
app.globalData.messageManager = {};
}
try {
const muted = wx.getStorageSync('notificationMuted');
if (muted !== undefined) app.globalData.messageManager.notificationMuted = muted;
} catch (e) {}
initAppStateListener(app);
initNetworkListener(app);
startHeartbeat(app);
startImWhenReady(app);
}
/** GoEasy 未就绪时轮询,登录后尽早建立监听 */
function startImWhenReady(app, maxAttempts = 40) {
let attempts = 0;
const tick = () => {
if (!wx.getStorageSync('uid')) return;
attempts += 1;
if (!wx.goEasy) {
if (attempts < maxAttempts) setTimeout(tick, 250);
return;
}
ensureConnection(app);
};
tick();
}
function getCurrentUserId() {
return (wx.goEasy && wx.goEasy.im && wx.goEasy.im.userId) || null;
}
function getConnectionStatus() {
if (!wx.goEasy || !wx.goEasy.getConnectionStatus) return 'disconnected';
return wx.goEasy.getConnectionStatus();
}
function isConnected() {
const s = getConnectionStatus();
return s === 'connected' || s === 'reconnected';
}
/** 统一会话数据结构,避免不同回调格式导致列表空白 */
function normalizeConversationsPayload(result) {
if (!result) return { conversations: [], unreadTotal: 0 };
const content = result.content || result;
const conversations = content.conversations || result.conversations || [];
const unreadTotal = content.unreadTotal ?? result.unreadTotal ?? 0;
return { conversations, unreadTotal };
}
function emitConversationsUpdated(app, result) {
const payload = normalizeConversationsPayload(result);
updateTabBarBadge(app, payload.unreadTotal);
app.emitEvent('conversationsUpdated', {
content: payload,
unreadTotal: payload.unreadTotal,
raw: result,
});
}
async function ensureConnection(app) {
const uid = wx.getStorageSync('uid');
if (!uid || !wx.goEasy) return;
const targetUserId = getLocalImUserId(app);
const identityType = getLocalImIdentity(app);
if (!targetUserId) return;
if (isConnected()) {
const currentUserId = getCurrentUserId();
if (currentUserId === targetUserId) {
bindGlobalListenersIfNeeded(app, targetUserId);
loadConversations(app);
const now = Date.now();
if (now - _lastGroupSubscribeAt > 60000) {
_lastGroupSubscribeAt = now;
await subscribeAllGroups(app);
}
return;
}
try {
wx.goEasy.disconnect();
} catch (e) {}
}
connectGoEasy(app, targetUserId, identityType);
}
function connectGoEasy(app, userId, identityType) {
const defAvatar = resolveAvatarUrl('', app);
const tx = wx.getStorageSync('touxiang') || '';
const uid = wx.getStorageSync('uid') || '';
const nick = wx.getStorageSync('nicheng') || app.globalData.nicheng || app.globalData.dashouNicheng || '';
const name = nick || ('用户' + (uid || '').substring(0, 6));
const avatar = resolveAvatarUrl(tx, app) || defAvatar;
wx.goEasy.connect({
id: userId,
data: {
name,
avatar,
identityType: identityType === 'normal' ? 'boss' : identityType,
},
onSuccess: async () => {
try {
wx.setStorageSync('savedGoEasyConnection', JSON.stringify({
userId,
identityType,
lastConnectTime: Date.now(),
status: 'connected',
}));
} catch (e) {}
bindGlobalListenersIfNeeded(app, userId);
_lastGroupSubscribeAt = Date.now();
await subscribeAllGroups(app);
loadConversations(app);
},
onFailed: (error) => {
console.error('GoEasy连接失败', error);
if (error.code === 408) {
bindGlobalListenersIfNeeded(app, userId);
loadConversations(app);
} else {
wx.showToast({ title: '连接失败,请重试', icon: 'none' });
}
},
});
}
function subscribeAllGroups(app) {
return new Promise((resolve) => {
if (!wx.goEasy || !wx.goEasy.im) return resolve();
wx.goEasy.im.latestConversations({
onSuccess: (result) => {
const payload = normalizeConversationsPayload(result);
const groupIds = payload.conversations
.filter((c) => c.type === 'group' && c.groupId)
.map((c) => c.groupId);
if (groupIds.length && wx.goEasy.im.subscribeGroup) {
wx.goEasy.im.subscribeGroup({
groupIds,
onSuccess: () => resolve(),
onFailed: () => resolve(),
});
} else {
resolve();
}
},
onFailed: () => resolve(),
});
});
}
function registerGlobalListeners(app) {
if (!wx.goEasy || !wx.goEasy.im) return;
if (_convListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
}
if (_privateListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
}
if (_groupListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
}
_convListener = (result) => {
emitConversationsUpdated(app, result);
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
_privateListener = () => {
loadConversations(app);
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
_groupListener = () => {
loadConversations(app);
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
}
function loadConversations(app) {
if (!wx.goEasy || !wx.goEasy.im) return;
if (!getCurrentUserId()) return;
wx.goEasy.im.latestConversations({
onSuccess: (result) => {
emitConversationsUpdated(app, result);
subscribeAllGroups(app);
},
onFailed: (err) => {
console.error('加载会话失败', err);
},
});
}
function updateTabBarBadge(app, unreadCount) {
const badgeText = unreadCount > 0 ? (unreadCount > 99 ? '99+' : String(unreadCount)) : '';
if (!app.globalData.messageManager) app.globalData.messageManager = {};
app.globalData.messageManager.unreadTotal = unreadCount;
app.globalData.messageManager.tabBarBadgeText = badgeText;
app.emitEvent('tabBarBadgeChanged', { badgeText, showRedDot: unreadCount > 0 });
try {
const pages = getCurrentPages();
if (pages.length) {
const page = pages[pages.length - 1];
const tabBar = page.selectComponent('#custom-tab-bar');
if (tabBar && tabBar.setData) tabBar.setData({ badgeText });
if (page.getTabBar) {
const tb = page.getTabBar();
if (tb && tb.setData) tb.setData({ badgeText });
}
}
} catch (e) {}
wx.setStorageSync('messageUnreadTotal', unreadCount);
}
function registerNotification(app) {
if (!wx.goEasy || !wx.goEasy.im) return;
if (_notificationHandler) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
}
_notificationHandler = (message) => {
if (app.globalData.messageManager && app.globalData.messageManager.notificationMuted) return;
if (!shouldShowNotification(message)) return;
const pages = getCurrentPages();
if (pages.length) {
const curPage = pages[pages.length - 1];
const route = curPage.route || '';
if (route === 'pages/liaotian/liaotian' || route === 'pages/qunliaotian/qunliaotian') return;
}
const senderName = message.senderData?.name || (message.senderId || '用户');
let content = '';
if (message.type === 'text') content = message.payload?.text || '[文本]';
else if (message.type === 'image') content = '[图片]';
else if (message.type === 'audio') content = '[语音]';
else content = '[新消息]';
const avatar = resolveAvatarUrl(message.senderData?.avatar, app);
if (app.globalData.globalNotification && app.globalData.globalNotification.show) {
app.globalData.globalNotification.show({
senderName,
content,
avatar,
message,
notificationType: message.groupId ? 'group' : 'private',
groupId: message.groupId,
orderId: message.orderId,
});
} else {
wx.showToast({ title: `${senderName}: ${content}`, icon: 'none', duration: 2000 });
}
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
}
function detachGlobalListeners() {
if (!wx.goEasy || !wx.goEasy.im) return;
if (_convListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
_convListener = null;
}
if (_privateListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
_privateListener = null;
}
if (_groupListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
_groupListener = null;
}
if (_notificationHandler) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
_notificationHandler = null;
}
_listenersBoundForUser = null;
}
function disconnectGoEasy(app) {
stopHeartbeat(app);
detachGlobalListeners();
if (wx.goEasy && wx.goEasy.disconnect) {
wx.goEasy.disconnect({ onSuccess: () => {}, onFailed: () => {} });
}
wx.removeStorageSync('savedGoEasyConnection');
}
async function switchRoleAndReconnect(app, newRole) {
disconnectGoEasy(app);
app.globalData.currentRole = newRole;
wx.setStorageSync('currentRole', newRole);
if (app.globalData.primaryRole !== newRole && ['normal', 'dashou', 'shangjia'].includes(newRole)) {
app.globalData.primaryRole = newRole;
wx.setStorageSync('primaryRole', newRole);
}
startHeartbeat(app);
await ensureConnection(app);
return true;
}
function startHeartbeat(app) {
stopHeartbeat(app);
_heartbeatTimer = setInterval(() => {
if (!wx.getStorageSync('uid')) return;
if (isConnected()) {
loadConversations(app);
} else {
ensureConnection(app);
}
}, 45000);
}
function stopHeartbeat(app) {
if (_heartbeatTimer) {
clearInterval(_heartbeatTimer);
_heartbeatTimer = null;
}
}
function initAppStateListener(app) {
wx.onAppShow(() => {
setTimeout(() => {
if (!wx.getStorageSync('uid')) return;
if (!isConnected()) {
ensureConnection(app);
} else {
loadConversations(app);
setTimeout(() => {
const unread = (app.globalData.messageManager && app.globalData.messageManager.unreadTotal) || 0;
updateTabBarBadge(app, unread);
}, 300);
}
}, 200);
});
}
function initNetworkListener(app) {
wx.onNetworkStatusChange((res) => {
if (res.isConnected) {
setTimeout(() => ensureConnection(app), 1000);
}
});
}
module.exports = { initGlobalMessageSystem };
// utils/chat-core.js — 全局 IM 连接、监听、角标(稳定版)
import GoEasy from '../static/lib/goeasy-2.13.24.esm.min';
import { resolveAvatarUrl } from './avatar.js';
import { getLocalImUserId, getLocalImIdentity } from './im-user.js';
let _convListener = null;
let _privateListener = null;
let _groupListener = null;
let _notificationHandler = null;
let _heartbeatTimer = null;
let _listenersBoundForUser = null;
let _lastGroupSubscribeAt = 0;
const _notifiedMessageIds = new Map();
const NOTIFY_MAX_AGE_MS = 15000;
function shouldShowNotification(message) {
if (!message) return false;
const id = message.messageId || `${message.senderId}_${message.timestamp}`;
if (_notifiedMessageIds.has(id)) return false;
if (message.timestamp && Date.now() - message.timestamp > NOTIFY_MAX_AGE_MS) return false;
_notifiedMessageIds.set(id, Date.now());
if (_notifiedMessageIds.size > 400) {
const cutoff = Date.now() - 60000;
_notifiedMessageIds.forEach((t, k) => {
if (t < cutoff) _notifiedMessageIds.delete(k);
});
}
return true;
}
function bindGlobalListenersIfNeeded(app, userId) {
if (_listenersBoundForUser === userId && _convListener) return;
registerGlobalListeners(app);
registerNotification(app);
_listenersBoundForUser = userId;
}
function initGlobalMessageSystem(app) {
app.ensureConnection = () => ensureConnection(app);
app.connectForCurrentRole = () => ensureConnection(app);
app.loadConversations = () => loadConversations(app);
app.disconnectGoEasy = () => disconnectGoEasy(app);
app.switchRoleAndReconnect = (newRole) => switchRoleAndReconnect(app, newRole);
app.updateUnreadCount = (count) => updateTabBarBadge(app, count || 0);
app.reregisterNotification = () => registerNotification(app);
app.startImWhenReady = () => startImWhenReady(app);
if (!app.globalData.messageManager) {
app.globalData.messageManager = {};
}
try {
const muted = wx.getStorageSync('notificationMuted');
if (muted !== undefined) app.globalData.messageManager.notificationMuted = muted;
} catch (e) {}
initAppStateListener(app);
initNetworkListener(app);
startHeartbeat(app);
startImWhenReady(app);
}
/** GoEasy 未就绪时轮询,登录后尽早建立监听 */
function startImWhenReady(app, maxAttempts = 40) {
let attempts = 0;
const tick = () => {
if (!wx.getStorageSync('uid')) return;
attempts += 1;
if (!wx.goEasy) {
if (attempts < maxAttempts) setTimeout(tick, 250);
return;
}
ensureConnection(app);
};
tick();
}
function getCurrentUserId() {
return (wx.goEasy && wx.goEasy.im && wx.goEasy.im.userId) || null;
}
function getConnectionStatus() {
if (!wx.goEasy || !wx.goEasy.getConnectionStatus) return 'disconnected';
return wx.goEasy.getConnectionStatus();
}
function isConnected() {
const s = getConnectionStatus();
return s === 'connected' || s === 'reconnected';
}
/** 统一会话数据结构,避免不同回调格式导致列表空白 */
function normalizeConversationsPayload(result) {
if (!result) return { conversations: [], unreadTotal: 0 };
const content = result.content || result;
const conversations = content.conversations || result.conversations || [];
const unreadTotal = content.unreadTotal ?? result.unreadTotal ?? 0;
return { conversations, unreadTotal };
}
function emitConversationsUpdated(app, result) {
const payload = normalizeConversationsPayload(result);
updateTabBarBadge(app, payload.unreadTotal);
app.emitEvent('conversationsUpdated', {
content: payload,
unreadTotal: payload.unreadTotal,
raw: result,
});
}
async function ensureConnection(app) {
const uid = wx.getStorageSync('uid');
if (!uid || !wx.goEasy) return;
const targetUserId = getLocalImUserId(app);
const identityType = getLocalImIdentity(app);
if (!targetUserId) return;
if (isConnected()) {
const currentUserId = getCurrentUserId();
if (currentUserId === targetUserId) {
bindGlobalListenersIfNeeded(app, targetUserId);
loadConversations(app);
const now = Date.now();
if (now - _lastGroupSubscribeAt > 60000) {
_lastGroupSubscribeAt = now;
await subscribeAllGroups(app);
}
return;
}
try {
wx.goEasy.disconnect();
} catch (e) {}
}
connectGoEasy(app, targetUserId, identityType);
}
function connectGoEasy(app, userId, identityType) {
const defAvatar = resolveAvatarUrl('', app);
const tx = wx.getStorageSync('touxiang') || '';
const uid = wx.getStorageSync('uid') || '';
const nick = wx.getStorageSync('nicheng') || app.globalData.nicheng || app.globalData.dashouNicheng || '';
const name = nick || ('用户' + (uid || '').substring(0, 6));
const avatar = resolveAvatarUrl(tx, app) || defAvatar;
wx.goEasy.connect({
id: userId,
data: {
name,
avatar,
identityType: identityType === 'normal' ? 'boss' : identityType,
},
onSuccess: async () => {
try {
wx.setStorageSync('savedGoEasyConnection', JSON.stringify({
userId,
identityType,
lastConnectTime: Date.now(),
status: 'connected',
}));
} catch (e) {}
bindGlobalListenersIfNeeded(app, userId);
_lastGroupSubscribeAt = Date.now();
await subscribeAllGroups(app);
loadConversations(app);
},
onFailed: (error) => {
console.error('GoEasy连接失败', error);
if (error.code === 408) {
bindGlobalListenersIfNeeded(app, userId);
loadConversations(app);
} else {
wx.showToast({ title: '连接失败,请重试', icon: 'none' });
}
},
});
}
function subscribeAllGroups(app) {
return new Promise((resolve) => {
if (!wx.goEasy || !wx.goEasy.im) return resolve();
wx.goEasy.im.latestConversations({
onSuccess: (result) => {
const payload = normalizeConversationsPayload(result);
const groupIds = payload.conversations
.filter((c) => c.type === 'group' && c.groupId)
.map((c) => c.groupId);
if (groupIds.length && wx.goEasy.im.subscribeGroup) {
wx.goEasy.im.subscribeGroup({
groupIds,
onSuccess: () => resolve(),
onFailed: () => resolve(),
});
} else {
resolve();
}
},
onFailed: () => resolve(),
});
});
}
function registerGlobalListeners(app) {
if (!wx.goEasy || !wx.goEasy.im) return;
if (_convListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
}
if (_privateListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
}
if (_groupListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
}
_convListener = (result) => {
emitConversationsUpdated(app, result);
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
_privateListener = () => {
loadConversations(app);
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
_groupListener = () => {
loadConversations(app);
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
}
function loadConversations(app) {
if (!wx.goEasy || !wx.goEasy.im) return;
if (!getCurrentUserId()) return;
wx.goEasy.im.latestConversations({
onSuccess: (result) => {
emitConversationsUpdated(app, result);
subscribeAllGroups(app);
},
onFailed: (err) => {
console.error('加载会话失败', err);
},
});
}
function updateTabBarBadge(app, unreadCount) {
const badgeText = unreadCount > 0 ? (unreadCount > 99 ? '99+' : String(unreadCount)) : '';
if (!app.globalData.messageManager) app.globalData.messageManager = {};
app.globalData.messageManager.unreadTotal = unreadCount;
app.globalData.messageManager.tabBarBadgeText = badgeText;
app.emitEvent('tabBarBadgeChanged', { badgeText, showRedDot: unreadCount > 0 });
try {
const pages = getCurrentPages();
if (pages.length) {
const page = pages[pages.length - 1];
const tabBar = page.selectComponent('#custom-tab-bar');
if (tabBar && tabBar.setData) tabBar.setData({ badgeText });
if (page.getTabBar) {
const tb = page.getTabBar();
if (tb && tb.setData) tb.setData({ badgeText });
}
}
} catch (e) {}
wx.setStorageSync('messageUnreadTotal', unreadCount);
}
function registerNotification(app) {
if (!wx.goEasy || !wx.goEasy.im) return;
if (_notificationHandler) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
}
_notificationHandler = (message) => {
if (app.globalData.messageManager && app.globalData.messageManager.notificationMuted) return;
if (!shouldShowNotification(message)) return;
const pages = getCurrentPages();
if (pages.length) {
const curPage = pages[pages.length - 1];
const route = curPage.route || '';
if (route === 'pages/chat/chat' || route === 'pages/group-chat/group-chat') return;
}
const senderName = message.senderData?.name || (message.senderId || '用户');
let content = '';
if (message.type === 'text') content = message.payload?.text || '[文本]';
else if (message.type === 'image') content = '[图片]';
else if (message.type === 'audio') content = '[语音]';
else content = '[新消息]';
const avatar = resolveAvatarUrl(message.senderData?.avatar, app);
if (app.globalData.globalNotification && app.globalData.globalNotification.show) {
app.globalData.globalNotification.show({
senderName,
content,
avatar,
message,
notificationType: message.groupId ? 'group' : 'private',
groupId: message.groupId,
orderId: message.orderId,
});
} else {
wx.showToast({ title: `${senderName}: ${content}`, icon: 'none', duration: 2000 });
}
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
}
function detachGlobalListeners() {
if (!wx.goEasy || !wx.goEasy.im) return;
if (_convListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
_convListener = null;
}
if (_privateListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
_privateListener = null;
}
if (_groupListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
_groupListener = null;
}
if (_notificationHandler) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
_notificationHandler = null;
}
_listenersBoundForUser = null;
}
function disconnectGoEasy(app) {
stopHeartbeat(app);
detachGlobalListeners();
if (wx.goEasy && wx.goEasy.disconnect) {
wx.goEasy.disconnect({ onSuccess: () => {}, onFailed: () => {} });
}
wx.removeStorageSync('savedGoEasyConnection');
}
async function switchRoleAndReconnect(app, newRole) {
disconnectGoEasy(app);
app.globalData.currentRole = newRole;
wx.setStorageSync('currentRole', newRole);
if (app.globalData.primaryRole !== newRole && ['normal', 'dashou', 'shangjia'].includes(newRole)) {
app.globalData.primaryRole = newRole;
wx.setStorageSync('primaryRole', newRole);
}
startHeartbeat(app);
await ensureConnection(app);
return true;
}
function startHeartbeat(app) {
stopHeartbeat(app);
_heartbeatTimer = setInterval(() => {
if (!wx.getStorageSync('uid')) return;
if (isConnected()) {
loadConversations(app);
} else {
ensureConnection(app);
}
}, 45000);
}
function stopHeartbeat(app) {
if (_heartbeatTimer) {
clearInterval(_heartbeatTimer);
_heartbeatTimer = null;
}
}
function initAppStateListener(app) {
wx.onAppShow(() => {
setTimeout(() => {
if (!wx.getStorageSync('uid')) return;
if (!isConnected()) {
ensureConnection(app);
} else {
loadConversations(app);
setTimeout(() => {
const unread = (app.globalData.messageManager && app.globalData.messageManager.unreadTotal) || 0;
updateTabBarBadge(app, unread);
}, 300);
}
}, 200);
});
}
function initNetworkListener(app) {
wx.onNetworkStatusChange((res) => {
if (res.isConnected) {
setTimeout(() => ensureConnection(app), 1000);
}
});
}
module.exports = { initGlobalMessageSystem };

View File

@@ -1,78 +1,78 @@
/** IM 用户 ID三端连接 + 管事/组长/考核官统一走打手 Ds 前缀 */
import { getPrimaryRole } from './primary-role.js';
import { resolveAvatarUrl } from './avatar.js';
import { ensureRoleOnCenterPage } from './role-tab-bar.js';
/** 联系对象前缀:管事/组长/考核官已废弃独立前缀,统一 Ds */
const PEER_PREFIX = {
boss: 'Boss',
normal: 'Boss',
dashou: 'Ds',
shangjia: 'Sj',
guanshi: 'Ds',
zuzhang: 'Ds',
kaoheguan: 'Ds',
};
export function getImPrefixForRole(role) {
return PEER_PREFIX[role] || 'Ds';
}
/** 当前端连接 GoEasy 用的 userId */
export function getLocalImUserId(app) {
app = app || getApp();
const uid = wx.getStorageSync('uid') || '';
if (!uid) return '';
const primary = getPrimaryRole(app);
if (primary === 'dashou') return 'Ds' + uid;
if (primary === 'shangjia') return 'Sj' + uid;
return 'Boss' + uid;
}
export function getLocalImIdentity(app) {
app = app || getApp();
return getPrimaryRole(app) || 'normal';
}
export function buildPeerImUserId(uid, role = 'dashou') {
if (!uid) return '';
return getImPrefixForRole(role) + uid;
}
/** 联系邀请人/管事/组长:统一 Ds 前缀 */
export function buildGuanshiPeerId(uid) {
return buildDashouPeerId(uid);
}
export function buildDashouPeerId(uid) {
return buildPeerImUserId(uid, 'dashou');
}
export function buildInviterPeerId(uid) {
return buildDashouPeerId(uid);
}
export function ensureDashouImReady(page) {
const app = getApp();
ensureRoleOnCenterPage(page, 'dashou');
if (app.ensureConnection) app.ensureConnection();
}
export function openPrivateChat(page, { toUserId, toName, toAvatar, delay = 350 }) {
ensureDashouImReady(page);
const avatar = resolveAvatarUrl(toAvatar);
const param = {
toUserId,
toName: toName || '用户',
toAvatar: avatar,
};
return new Promise((resolve) => {
setTimeout(() => {
wx.navigateTo({
url: '/pages/liaotian/liaotian?data=' + encodeURIComponent(JSON.stringify(param)),
fail: () => wx.showToast({ title: '打开聊天失败', icon: 'none' }),
complete: resolve,
});
}, delay);
});
}
/** IM 用户 ID三端连接 + 管事/组长/考核官统一走打手 Ds 前缀 */
import { getPrimaryRole } from './primary-role.js';
import { resolveAvatarUrl } from './avatar.js';
import { ensureRoleOnCenterPage } from './role-tab-bar.js';
/** 联系对象前缀:管事/组长/考核官已废弃独立前缀,统一 Ds */
const PEER_PREFIX = {
boss: 'Boss',
normal: 'Boss',
dashou: 'Ds',
shangjia: 'Sj',
guanshi: 'Ds',
zuzhang: 'Ds',
kaoheguan: 'Ds',
};
export function getImPrefixForRole(role) {
return PEER_PREFIX[role] || 'Ds';
}
/** 当前端连接 GoEasy 用的 userId */
export function getLocalImUserId(app) {
app = app || getApp();
const uid = wx.getStorageSync('uid') || '';
if (!uid) return '';
const primary = getPrimaryRole(app);
if (primary === 'dashou') return 'Ds' + uid;
if (primary === 'shangjia') return 'Sj' + uid;
return 'Boss' + uid;
}
export function getLocalImIdentity(app) {
app = app || getApp();
return getPrimaryRole(app) || 'normal';
}
export function buildPeerImUserId(uid, role = 'dashou') {
if (!uid) return '';
return getImPrefixForRole(role) + uid;
}
/** 联系邀请人/管事/组长:统一 Ds 前缀 */
export function buildGuanshiPeerId(uid) {
return buildDashouPeerId(uid);
}
export function buildDashouPeerId(uid) {
return buildPeerImUserId(uid, 'dashou');
}
export function buildInviterPeerId(uid) {
return buildDashouPeerId(uid);
}
export function ensureDashouImReady(page) {
const app = getApp();
ensureRoleOnCenterPage(page, 'dashou');
if (app.ensureConnection) app.ensureConnection();
}
export function openPrivateChat(page, { toUserId, toName, toAvatar, delay = 350 }) {
ensureDashouImReady(page);
const avatar = resolveAvatarUrl(toAvatar);
const param = {
toUserId,
toName: toName || '用户',
toAvatar: avatar,
};
return new Promise((resolve) => {
setTimeout(() => {
wx.navigateTo({
url: '/pages/chat/chat?data=' + encodeURIComponent(JSON.stringify(param)),
fail: () => wx.showToast({ title: '打开聊天失败', icon: 'none' }),
complete: resolve,
});
}, delay);
});
}

View File

@@ -1,105 +1,105 @@
/** 三端固定normal | dashou | shangjia认证后不可回退 */
const VALID = ['normal', 'dashou', 'shangjia'];
const LEGACY_CENTER_ROLES = ['guanshi', 'zuzhang', 'kaoheguan'];
/** 旧版管事/组长/考核官主端标识 → 统一回落打手 Tab */
export function migrateLegacyCenterRole(app) {
app = app || getApp();
const cur = wx.getStorageSync('currentRole') || app.globalData.currentRole || '';
const pri = wx.getStorageSync('primaryRole') || '';
const isLegacy = LEGACY_CENTER_ROLES.includes(cur) || LEGACY_CENTER_ROLES.includes(pri);
if (!isLegacy) return false;
const hasDashouSide =
isRoleStatusActive(wx.getStorageSync('dashoustatus'))
|| isRoleStatusActive(wx.getStorageSync('guanshistatus'))
|| isRoleStatusActive(wx.getStorageSync('zuzhangstatus'))
|| isRoleStatusActive(wx.getStorageSync('kaoheguanstatus'));
if (hasDashouSide) {
lockPrimaryRole('dashou', app);
return true;
}
return false;
}
export function isRoleStatusActive(status) {
return Number(status) === 1;
}
/** 读取主端(兼容旧 currentRole / 管事组长落地页) */
export function getPrimaryRole(app) {
app = app || getApp();
const stored = wx.getStorageSync('primaryRole');
if (VALID.includes(stored)) {
return stored;
}
const legacy = wx.getStorageSync('currentRole') || app.globalData.currentRole || 'normal';
if (legacy === 'dashou' || legacy === 'shangjia') {
return legacy;
}
if (legacy === 'guanshi' || legacy === 'zuzhang' || legacy === 'kaoheguan') {
if (isRoleStatusActive(wx.getStorageSync('dashoustatus'))) {
return 'dashou';
}
if (isRoleStatusActive(wx.getStorageSync('shangjiastatus'))) {
return 'shangjia';
}
}
if (isRoleStatusActive(wx.getStorageSync('dashoustatus'))) {
return 'dashou';
}
if (isRoleStatusActive(wx.getStorageSync('shangjiastatus'))) {
return 'shangjia';
}
return 'normal';
}
/** 锁定主端并同步 TabBar 角色 */
export function lockPrimaryRole(role, app) {
if (!VALID.includes(role)) return;
app = app || getApp();
wx.setStorageSync('primaryRole', role);
wx.setStorageSync('currentRole', role);
app.globalData.primaryRole = role;
app.globalData.currentRole = role;
if (app.emitEvent) {
app.emitEvent('currentRoleChanged', { role });
}
}
/** TabBar / IM 使用的有效角色(仅三端) */
export function getTabRole(app) {
return getPrimaryRole(app);
}
export function clearPrimaryRole(app) {
app = app || getApp();
wx.removeStorageSync('primaryRole');
lockPrimaryRole('normal', app);
}
export const PRIMARY_DEFAULT_PAGES = {
normal: '/pages/wode/wode',
dashou: '/pages/jiedan/jiedan',
shangjia: '/pages/sjdingdan/sjdingdan',
};
/** 锁定主端并跳转到该端默认首页,同时建立 IM 长连接 */
export function enterLockedRole(role, app) {
if (!VALID.includes(role) || role === 'normal') return false;
app = app || getApp();
lockPrimaryRole(role, app);
wx.reLaunch({ url: PRIMARY_DEFAULT_PAGES[role] });
setTimeout(() => {
if (app.switchRoleAndReconnect) {
app.switchRoleAndReconnect(role).catch(() => {
if (app.connectForCurrentRole) app.connectForCurrentRole();
});
} else if (app.connectForCurrentRole) {
app.connectForCurrentRole();
}
}, 600);
return true;
}
/** 三端固定normal | dashou | shangjia认证后不可回退 */
const VALID = ['normal', 'dashou', 'shangjia'];
const LEGACY_CENTER_ROLES = ['guanshi', 'zuzhang', 'kaoheguan'];
/** 旧版管事/组长/考核官主端标识 → 统一回落打手 Tab */
export function migrateLegacyCenterRole(app) {
app = app || getApp();
const cur = wx.getStorageSync('currentRole') || app.globalData.currentRole || '';
const pri = wx.getStorageSync('primaryRole') || '';
const isLegacy = LEGACY_CENTER_ROLES.includes(cur) || LEGACY_CENTER_ROLES.includes(pri);
if (!isLegacy) return false;
const hasDashouSide =
isRoleStatusActive(wx.getStorageSync('dashoustatus'))
|| isRoleStatusActive(wx.getStorageSync('guanshistatus'))
|| isRoleStatusActive(wx.getStorageSync('zuzhangstatus'))
|| isRoleStatusActive(wx.getStorageSync('kaoheguanstatus'));
if (hasDashouSide) {
lockPrimaryRole('dashou', app);
return true;
}
return false;
}
export function isRoleStatusActive(status) {
return Number(status) === 1;
}
/** 读取主端(兼容旧 currentRole / 管事组长落地页) */
export function getPrimaryRole(app) {
app = app || getApp();
const stored = wx.getStorageSync('primaryRole');
if (VALID.includes(stored)) {
return stored;
}
const legacy = wx.getStorageSync('currentRole') || app.globalData.currentRole || 'normal';
if (legacy === 'dashou' || legacy === 'shangjia') {
return legacy;
}
if (legacy === 'guanshi' || legacy === 'zuzhang' || legacy === 'kaoheguan') {
if (isRoleStatusActive(wx.getStorageSync('dashoustatus'))) {
return 'dashou';
}
if (isRoleStatusActive(wx.getStorageSync('shangjiastatus'))) {
return 'shangjia';
}
}
if (isRoleStatusActive(wx.getStorageSync('dashoustatus'))) {
return 'dashou';
}
if (isRoleStatusActive(wx.getStorageSync('shangjiastatus'))) {
return 'shangjia';
}
return 'normal';
}
/** 锁定主端并同步 TabBar 角色 */
export function lockPrimaryRole(role, app) {
if (!VALID.includes(role)) return;
app = app || getApp();
wx.setStorageSync('primaryRole', role);
wx.setStorageSync('currentRole', role);
app.globalData.primaryRole = role;
app.globalData.currentRole = role;
if (app.emitEvent) {
app.emitEvent('currentRoleChanged', { role });
}
}
/** TabBar / IM 使用的有效角色(仅三端) */
export function getTabRole(app) {
return getPrimaryRole(app);
}
export function clearPrimaryRole(app) {
app = app || getApp();
wx.removeStorageSync('primaryRole');
lockPrimaryRole('normal', app);
}
export const PRIMARY_DEFAULT_PAGES = {
normal: '/pages/mine/mine',
dashou: '/pages/accept-order/accept-order',
shangjia: '/pages/merchant-orders/merchant-orders',
};
/** 锁定主端并跳转到该端默认首页,同时建立 IM 长连接 */
export function enterLockedRole(role, app) {
if (!VALID.includes(role) || role === 'normal') return false;
app = app || getApp();
lockPrimaryRole(role, app);
wx.reLaunch({ url: PRIMARY_DEFAULT_PAGES[role] });
setTimeout(() => {
if (app.switchRoleAndReconnect) {
app.switchRoleAndReconnect(role).catch(() => {
if (app.connectForCurrentRole) app.connectForCurrentRole();
});
} else if (app.connectForCurrentRole) {
app.connectForCurrentRole();
}
}, 600);
return true;
}

View File

@@ -1,74 +1,74 @@
// utils/request.js
const app = getApp();
function request(options) {
const token = wx.getStorageSync('token');
// 🔥 没有 token 时不发送请求,直接返回一个“空响应”对象,不影响后续代码
if (!token) {
// 返回一个模拟的响应,状态码为 401但不会触发任何弹窗或跳转
return Promise.resolve({
statusCode: 401,
data: { code: 401, msg: '未登录' },
errMsg: 'request:ok'
});
}
const header = {
'Content-Type': 'application/json',
...options.header,
};
if (token) {
header['Authorization'] = 'Bearer ' + token;
}
return new Promise((resolve, reject) => {
wx.request({
url: app.globalData.apiBaseUrl + options.url,
method: options.method,
data: options.data || {},
header,
success: async (res) => {
if (res.statusCode === 401 || res.statusCode === 403) {
// 注意:这里的弹窗逻辑只会在有 token 且 token 失效时触发
// 无 token 的情况已经在上面直接返回,不会走到这里
if (app.globalData.loginPromise) {
try {
await app.globalData.loginPromise;
const retryRes = await request(options);
resolve(retryRes);
} catch (err) {
wx.showModal({
content: '操作失败,请先登录',
success: (modalRes) => {
if (modalRes.confirm) {
wx.removeStorageSync('token');
wx.switchTab({ url: '/pages/wode/wode' });
}
},
});
resolve(res);
}
return;
}
wx.showModal({
content: '操作失败,请先登录',
success: (modalRes) => {
if (modalRes.confirm) {
wx.removeStorageSync('token');
wx.reLaunch({ url: '/pages/wode/wode' });
}
},
});
resolve(res);
return;
}
resolve(res);
},
fail: reject,
});
});
}
// utils/request.js
const app = getApp();
function request(options) {
const token = wx.getStorageSync('token');
// 🔥 没有 token 时不发送请求,直接返回一个“空响应”对象,不影响后续代码
if (!token) {
// 返回一个模拟的响应,状态码为 401但不会触发任何弹窗或跳转
return Promise.resolve({
statusCode: 401,
data: { code: 401, msg: '未登录' },
errMsg: 'request:ok'
});
}
const header = {
'Content-Type': 'application/json',
...options.header,
};
if (token) {
header['Authorization'] = 'Bearer ' + token;
}
return new Promise((resolve, reject) => {
wx.request({
url: app.globalData.apiBaseUrl + options.url,
method: options.method,
data: options.data || {},
header,
success: async (res) => {
if (res.statusCode === 401 || res.statusCode === 403) {
// 注意:这里的弹窗逻辑只会在有 token 且 token 失效时触发
// 无 token 的情况已经在上面直接返回,不会走到这里
if (app.globalData.loginPromise) {
try {
await app.globalData.loginPromise;
const retryRes = await request(options);
resolve(retryRes);
} catch (err) {
wx.showModal({
content: '操作失败,请先登录',
success: (modalRes) => {
if (modalRes.confirm) {
wx.removeStorageSync('token');
wx.switchTab({ url: '/pages/mine/mine' });
}
},
});
resolve(res);
}
return;
}
wx.showModal({
content: '操作失败,请先登录',
success: (modalRes) => {
if (modalRes.confirm) {
wx.removeStorageSync('token');
wx.reLaunch({ url: '/pages/mine/mine' });
}
},
});
resolve(res);
return;
}
resolve(res);
},
fail: reject,
});
});
}
export default request;

View File

@@ -1,255 +1,255 @@
import { clearPrimaryRole } from './primary-role';
import { getLocalImUserId } from './im-user.js';
export function isWechatLoginSuccess(body) {
if (!body) return false;
const code = Number(body.code);
return code === 0 || code === 200;
}
const SESSION_STATUS_KEYS = ['dashoustatus', 'guanshistatus', 'shangjiastatus', 'zuzhangstatus', 'kaoheguanstatus'];
/** 读取 tokenStorage 优先globalData 兜底并回写 Storage */
export function getSessionToken(app) {
app = app || getApp();
let token = wx.getStorageSync('token') || app.globalData.token || '';
if (token && !wx.getStorageSync('token')) {
wx.setStorageSync('token', token);
}
return token;
}
export function isUserLoggedIn(app) {
return !!getSessionToken(app);
}
/** 从 Storage / globalData 汇总展示用用户信息(兼容其他端注册后 nick 未落盘) */
export function getUserProfileForDisplay(app) {
app = app || getApp();
const token = getSessionToken(app);
const uid = wx.getStorageSync('uid') || app.globalData.uid || '';
let nick = wx.getStorageSync('nicheng') || app.globalData.nicheng || '';
if (!nick && app.globalData.dashouNicheng) {
nick = app.globalData.dashouNicheng;
}
const tx = wx.getStorageSync('touxiang') || '';
const def = app.globalData.morentouxiang || '';
const oss = app.globalData.ossImageUrl || '';
let avatarUrl = '';
if (tx) {
avatarUrl = tx.startsWith('http') ? tx : oss + tx;
} else if (def) {
avatarUrl = def.startsWith('http') ? def : oss + def;
}
return {
nick,
uid,
avatarUrl,
isLoggedIn: !!token,
};
}
/** 其他端登录/注册成功后,把 globalData 里已有字段补写回 Storage */
export function backfillUserProfileCache(app) {
app = app || getApp();
if (!isUserLoggedIn()) return;
const profile = getUserProfileForDisplay(app);
if (profile.nick && !wx.getStorageSync('nicheng')) {
wx.setStorageSync('nicheng', profile.nick);
app.globalData.nicheng = profile.nick;
}
if (profile.uid && !wx.getStorageSync('uid')) {
wx.setStorageSync('uid', profile.uid);
}
}
export function applyWechatLoginData(data, page) {
if (!data || typeof data !== 'object') return;
const app = getApp();
syncProfileFields(data);
if (data.token) {
wx.setStorageSync('token', data.token);
}
syncRoleStatuses(data);
syncGroupFields(data);
if (data.zuzhangstatus !== undefined) {
wx.setStorageSync('zuzhangstatus', data.zuzhangstatus);
app.globalData.zuzhangstatus = data.zuzhangstatus;
}
if (data.kaoheguanstatus !== undefined) {
wx.setStorageSync('kaoheguanstatus', data.kaoheguanstatus);
app.globalData.kaoheguanstatus = data.kaoheguanstatus;
}
if (data.dingdantiaoshu) {
app.globalData.dingdanTiaoshu = {
daifuwu: data.dingdantiaoshu.daifuwu || 0,
fuwuzhong: data.dingdantiaoshu.fuwuzhong || 0,
yiwancheng: data.dingdantiaoshu.yiwancheng || 0,
yituikuan: data.dingdantiaoshu.yituikuan || 0,
};
}
if (data.isJinpai !== undefined) {
wx.setStorageSync('isJinpai', Number(data.isJinpai));
}
const targetPage = page || _getCurrentPage();
refreshTabBar(targetPage);
}
function _getCurrentPage() {
const pages = getCurrentPages();
return pages.length ? pages[pages.length - 1] : null;
}
export function isRoleStatusActive(status) {
return Number(status) === 1;
}
export function isCenterPageActive(page, dataFlag, statusKey) {
return page.data[dataFlag] || isRoleStatusActive(wx.getStorageSync(statusKey));
}
export function syncRoleStatuses(data) {
if (!data || typeof data !== 'object') return;
const app = getApp();
const roleFields = ['dashoustatus', 'guanshistatus', 'shangjiastatus', 'zuzhangstatus', 'kaoheguanstatus'];
roleFields.forEach((field) => {
if (Object.prototype.hasOwnProperty.call(data, field)) {
const val = Number(data[field]);
const normalized = Number.isNaN(val) ? 0 : val;
wx.setStorageSync(field, normalized);
app.globalData[field] = normalized;
}
});
}
export function syncProfileFields(data) {
if (!data || typeof data !== 'object') return;
const app = getApp();
['nicheng', 'uid', 'touxiang', 'token'].forEach((field) => {
if (data[field] !== undefined && data[field] !== null) {
wx.setStorageSync(field, data[field]);
app.globalData[field] = data[field];
}
});
}
/** 清除缓存:清空登录与身份,回到点单端「我的」 */
export function clearCacheAndEnterNormal(page) {
resetGuestSession(page, { skipPageReset: true });
wx.reLaunch({ url: '/pages/wode/wode' });
}
/** 清除缓存并重置为点单端(不跳转,供当前已在点单端时使用) */
export function resetGuestSession(page, options = {}) {
const app = getApp();
const configKey = app.CONFIG_CACHE_KEY || 'app_dynamic_config';
let configCache = null;
try {
configCache = wx.getStorageSync(configKey);
} catch (e) {}
wx.clearStorageSync();
if (configCache && typeof configCache === 'object') {
wx.setStorageSync(configKey, configCache);
}
clearPrimaryRole(app);
app.globalData.token = '';
app.globalData.nicheng = '';
app.globalData.uid = '';
app.globalData.loginPromise = null;
app.globalData.currentUser = null;
app.globalData.dashouNicheng = '';
app.globalData.dingdanTiaoshu = { daifuwu: 0, fuwuzhong: 0, yiwancheng: 0, yituikuan: 0 };
app.globalData.dashouqun = '';
app.globalData.dashouqunid = '';
app.globalData.guanshiqun = '';
app.globalData.guanshiqunid = '';
SESSION_STATUS_KEYS.forEach((key) => {
app.globalData[key] = 0;
});
app.globalData.shangpinleixing = [];
app.globalData.shangpinzhuanqu = [];
app.globalData.shangpinliebiao = [];
app.emitEvent('currentRoleChanged', { role: 'normal' });
refreshTabBar(page);
if (!options.skipPageReset && page && page.setData) {
page.setData({
avatarUrl: '',
userNicheng: '',
userUid: '',
showLoginBtn: true,
orderCounts: { daifuwu: 0, fuwuzhong: 0, yiwancheng: 0, yituikuan: 0 },
dashouCertified: false,
shangjiaCertified: false,
});
}
}
export function syncGroupFields(data) {
if (!data || typeof data !== 'object') return;
const app = getApp();
['dashouqun', 'dashouqunid', 'guanshiqun', 'guanshiqunid'].forEach((field) => {
if (data[field] !== undefined) {
wx.setStorageSync(field, data[field]);
app.globalData[field] = data[field];
}
});
}
export function refreshTabBar(page) {
const tryRefresh = () => {
const tabBar = page.getTabBar && page.getTabBar();
if (tabBar && tabBar.refresh) tabBar.refresh();
};
tryRefresh();
[50, 150, 300, 500, 800].forEach((delay) => setTimeout(tryRefresh, delay));
}
export function setRoleAndRefreshTabBar(page, role) {
const app = getApp();
if (app.setCurrentRole) {
app.setCurrentRole(role);
} else {
app.globalData.currentRole = role;
wx.setStorageSync('currentRole', role);
app.emitEvent('currentRoleChanged', { role });
}
refreshTabBar(page);
}
export function reconnectForRole(role) {
const app = getApp();
if (!wx.getStorageSync('uid')) return;
const targetUserId = getLocalImUserId(app);
if (!targetUserId) return;
const status = wx.goEasy?.getConnectionStatus?.() || 'disconnected';
const currentUserId = wx.goEasy?.im?.userId || '';
const connected = status === 'connected' || status === 'reconnected';
if (connected && currentUserId === targetUserId) {
if (app.loadConversations) app.loadConversations();
if (app.ensureConnection) app.ensureConnection();
return;
}
if (app.switchRoleAndReconnect) {
app.switchRoleAndReconnect(role).catch(() => {
if (app.ensureConnection) app.ensureConnection();
});
} else if (app.connectForCurrentRole) {
app.connectForCurrentRole();
} else if (app.ensureConnection) {
app.ensureConnection();
}
}
export function ensureRoleOnCenterPage(page, role) {
setRoleAndRefreshTabBar(page, role);
reconnectForRole(role);
}
import { clearPrimaryRole } from './primary-role';
import { getLocalImUserId } from './im-user.js';
export function isWechatLoginSuccess(body) {
if (!body) return false;
const code = Number(body.code);
return code === 0 || code === 200;
}
const SESSION_STATUS_KEYS = ['dashoustatus', 'guanshistatus', 'shangjiastatus', 'zuzhangstatus', 'kaoheguanstatus'];
/** 读取 tokenStorage 优先globalData 兜底并回写 Storage */
export function getSessionToken(app) {
app = app || getApp();
let token = wx.getStorageSync('token') || app.globalData.token || '';
if (token && !wx.getStorageSync('token')) {
wx.setStorageSync('token', token);
}
return token;
}
export function isUserLoggedIn(app) {
return !!getSessionToken(app);
}
/** 从 Storage / globalData 汇总展示用用户信息(兼容其他端注册后 nick 未落盘) */
export function getUserProfileForDisplay(app) {
app = app || getApp();
const token = getSessionToken(app);
const uid = wx.getStorageSync('uid') || app.globalData.uid || '';
let nick = wx.getStorageSync('nicheng') || app.globalData.nicheng || '';
if (!nick && app.globalData.dashouNicheng) {
nick = app.globalData.dashouNicheng;
}
const tx = wx.getStorageSync('touxiang') || '';
const def = app.globalData.morentouxiang || '';
const oss = app.globalData.ossImageUrl || '';
let avatarUrl = '';
if (tx) {
avatarUrl = tx.startsWith('http') ? tx : oss + tx;
} else if (def) {
avatarUrl = def.startsWith('http') ? def : oss + def;
}
return {
nick,
uid,
avatarUrl,
isLoggedIn: !!token,
};
}
/** 其他端登录/注册成功后,把 globalData 里已有字段补写回 Storage */
export function backfillUserProfileCache(app) {
app = app || getApp();
if (!isUserLoggedIn()) return;
const profile = getUserProfileForDisplay(app);
if (profile.nick && !wx.getStorageSync('nicheng')) {
wx.setStorageSync('nicheng', profile.nick);
app.globalData.nicheng = profile.nick;
}
if (profile.uid && !wx.getStorageSync('uid')) {
wx.setStorageSync('uid', profile.uid);
}
}
export function applyWechatLoginData(data, page) {
if (!data || typeof data !== 'object') return;
const app = getApp();
syncProfileFields(data);
if (data.token) {
wx.setStorageSync('token', data.token);
}
syncRoleStatuses(data);
syncGroupFields(data);
if (data.zuzhangstatus !== undefined) {
wx.setStorageSync('zuzhangstatus', data.zuzhangstatus);
app.globalData.zuzhangstatus = data.zuzhangstatus;
}
if (data.kaoheguanstatus !== undefined) {
wx.setStorageSync('kaoheguanstatus', data.kaoheguanstatus);
app.globalData.kaoheguanstatus = data.kaoheguanstatus;
}
if (data.dingdantiaoshu) {
app.globalData.dingdanTiaoshu = {
daifuwu: data.dingdantiaoshu.daifuwu || 0,
fuwuzhong: data.dingdantiaoshu.fuwuzhong || 0,
yiwancheng: data.dingdantiaoshu.yiwancheng || 0,
yituikuan: data.dingdantiaoshu.yituikuan || 0,
};
}
if (data.isJinpai !== undefined) {
wx.setStorageSync('isJinpai', Number(data.isJinpai));
}
const targetPage = page || _getCurrentPage();
refreshTabBar(targetPage);
}
function _getCurrentPage() {
const pages = getCurrentPages();
return pages.length ? pages[pages.length - 1] : null;
}
export function isRoleStatusActive(status) {
return Number(status) === 1;
}
export function isCenterPageActive(page, dataFlag, statusKey) {
return page.data[dataFlag] || isRoleStatusActive(wx.getStorageSync(statusKey));
}
export function syncRoleStatuses(data) {
if (!data || typeof data !== 'object') return;
const app = getApp();
const roleFields = ['dashoustatus', 'guanshistatus', 'shangjiastatus', 'zuzhangstatus', 'kaoheguanstatus'];
roleFields.forEach((field) => {
if (Object.prototype.hasOwnProperty.call(data, field)) {
const val = Number(data[field]);
const normalized = Number.isNaN(val) ? 0 : val;
wx.setStorageSync(field, normalized);
app.globalData[field] = normalized;
}
});
}
export function syncProfileFields(data) {
if (!data || typeof data !== 'object') return;
const app = getApp();
['nicheng', 'uid', 'touxiang', 'token'].forEach((field) => {
if (data[field] !== undefined && data[field] !== null) {
wx.setStorageSync(field, data[field]);
app.globalData[field] = data[field];
}
});
}
/** 清除缓存:清空登录与身份,回到点单端「我的」 */
export function clearCacheAndEnterNormal(page) {
resetGuestSession(page, { skipPageReset: true });
wx.reLaunch({ url: '/pages/mine/mine' });
}
/** 清除缓存并重置为点单端(不跳转,供当前已在点单端时使用) */
export function resetGuestSession(page, options = {}) {
const app = getApp();
const configKey = app.CONFIG_CACHE_KEY || 'app_dynamic_config';
let configCache = null;
try {
configCache = wx.getStorageSync(configKey);
} catch (e) {}
wx.clearStorageSync();
if (configCache && typeof configCache === 'object') {
wx.setStorageSync(configKey, configCache);
}
clearPrimaryRole(app);
app.globalData.token = '';
app.globalData.nicheng = '';
app.globalData.uid = '';
app.globalData.loginPromise = null;
app.globalData.currentUser = null;
app.globalData.dashouNicheng = '';
app.globalData.dingdanTiaoshu = { daifuwu: 0, fuwuzhong: 0, yiwancheng: 0, yituikuan: 0 };
app.globalData.dashouqun = '';
app.globalData.dashouqunid = '';
app.globalData.guanshiqun = '';
app.globalData.guanshiqunid = '';
SESSION_STATUS_KEYS.forEach((key) => {
app.globalData[key] = 0;
});
app.globalData.shangpinleixing = [];
app.globalData.shangpinzhuanqu = [];
app.globalData.shangpinliebiao = [];
app.emitEvent('currentRoleChanged', { role: 'normal' });
refreshTabBar(page);
if (!options.skipPageReset && page && page.setData) {
page.setData({
avatarUrl: '',
userNicheng: '',
userUid: '',
showLoginBtn: true,
orderCounts: { daifuwu: 0, fuwuzhong: 0, yiwancheng: 0, yituikuan: 0 },
dashouCertified: false,
shangjiaCertified: false,
});
}
}
export function syncGroupFields(data) {
if (!data || typeof data !== 'object') return;
const app = getApp();
['dashouqun', 'dashouqunid', 'guanshiqun', 'guanshiqunid'].forEach((field) => {
if (data[field] !== undefined) {
wx.setStorageSync(field, data[field]);
app.globalData[field] = data[field];
}
});
}
export function refreshTabBar(page) {
const tryRefresh = () => {
const tabBar = page.getTabBar && page.getTabBar();
if (tabBar && tabBar.refresh) tabBar.refresh();
};
tryRefresh();
[50, 150, 300, 500, 800].forEach((delay) => setTimeout(tryRefresh, delay));
}
export function setRoleAndRefreshTabBar(page, role) {
const app = getApp();
if (app.setCurrentRole) {
app.setCurrentRole(role);
} else {
app.globalData.currentRole = role;
wx.setStorageSync('currentRole', role);
app.emitEvent('currentRoleChanged', { role });
}
refreshTabBar(page);
}
export function reconnectForRole(role) {
const app = getApp();
if (!wx.getStorageSync('uid')) return;
const targetUserId = getLocalImUserId(app);
if (!targetUserId) return;
const status = wx.goEasy?.getConnectionStatus?.() || 'disconnected';
const currentUserId = wx.goEasy?.im?.userId || '';
const connected = status === 'connected' || status === 'reconnected';
if (connected && currentUserId === targetUserId) {
if (app.loadConversations) app.loadConversations();
if (app.ensureConnection) app.ensureConnection();
return;
}
if (app.switchRoleAndReconnect) {
app.switchRoleAndReconnect(role).catch(() => {
if (app.ensureConnection) app.ensureConnection();
});
} else if (app.connectForCurrentRole) {
app.connectForCurrentRole();
} else if (app.ensureConnection) {
app.ensureConnection();
}
}
export function ensureRoleOnCenterPage(page, role) {
setRoleAndRefreshTabBar(page, role);
reconnectForRole(role);
}

View File

@@ -1,215 +1,215 @@
/**
* 连接管理模块 - xiaoxilj.js (根治发送失败:跳转前确保群组已订阅)
* 从详情页跳转订单群聊时,提前订阅群组,避免发送消息时订阅未完成而失败
*/
const app = getApp();
function waitForConnection(expectedUserId, timeout = 10000) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
app.off('connectionChanged', handler);
reject(new Error('连接超时'));
}, timeout);
const handler = (event) => {
if (event.status === 'connected') {
if (event.userId === expectedUserId) {
clearTimeout(timer);
app.off('connectionChanged', handler);
resolve();
} else {
console.error(`连接身份不匹配: 期望 ${expectedUserId}, 实际 ${event.userId}`);
wx.removeStorageSync('savedGoEasyConnection');
wx.removeStorageSync('goEasyUserId');
wx.removeStorageSync('currentGoEasyIdentity');
if (app.clearSavedConnection) app.clearSavedConnection();
clearTimeout(timer);
app.off('connectionChanged', handler);
reject(new Error(`连接身份不匹配: 期望 ${expectedUserId}, 实际 ${event.userId}`));
}
} else if (event.status === 'disconnected' && !event.manual) {
wx.removeStorageSync('savedGoEasyConnection');
wx.removeStorageSync('goEasyUserId');
wx.removeStorageSync('currentGoEasyIdentity');
if (app.clearSavedConnection) app.clearSavedConnection();
clearTimeout(timer);
app.off('connectionChanged', handler);
reject(new Error('连接失败'));
}
};
app.on('connectionChanged', handler);
});
}
class ConnectionManager {
// 原有的私聊方法保持不变
async connectAndChat(params) {
const { identityType, userId, targetUser } = params;
if (!identityType || !userId || !targetUser || !targetUser.id) {
throw new Error('参数不完整');
}
if (identityType === 'dashou' && !userId.startsWith('Ds')) {
throw new Error(`打手身份的用户ID必须以Ds开头实际为 ${userId}`);
}
if (identityType === 'shangjia' && !userId.startsWith('Sj')) {
throw new Error(`商家身份的用户ID必须以Sj开头实际为 ${userId}`);
}
if (identityType === 'boss' && !userId.startsWith('Boss')) {
throw new Error(`老板身份的用户ID必须以Boss开头实际为 ${userId}`);
}
wx.removeStorageSync('savedGoEasyConnection');
wx.removeStorageSync('goEasyUserId');
wx.removeStorageSync('currentGoEasyIdentity');
if (app.clearSavedConnection) app.clearSavedConnection();
if (wx.goEasy && wx.goEasy.getConnectionStatus() === 'connected') {
wx.goEasy.disconnect();
}
const waitPromise = waitForConnection(userId);
app.connectWithIdentity(identityType, userId, false);
try {
await waitPromise;
const currentUser = {
id: userId,
name: app.globalData.currentUser?.name || '用户',
avatar: app.globalData.currentUser?.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
};
const to = {
id: targetUser.id,
name: targetUser.name || '用户',
avatar: targetUser.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
};
const param = { to, currentUser };
const path = '/pages/liaotian/liaotian?data=' + encodeURIComponent(JSON.stringify(param));
wx.navigateTo({ url: path });
} catch (err) {
console.error('连接失败:', err);
wx.showToast({ title: err.message || '连接失败', icon: 'none' });
throw err;
}
}
async connectToGroupChat(params) {
const { identityType, userId, orderId, groupName, groupAvatar, isCross } = params;
if (!identityType || !userId || !orderId) {
throw new Error('参数不完整identityType, userId, orderId 必填');
}
// 校验 userId 前缀
if (identityType === 'dashou' && !userId.startsWith('Ds')) {
throw new Error(`打手身份的用户ID必须以Ds开头实际为 ${userId}`);
}
if (identityType === 'shangjia' && !userId.startsWith('Sj')) {
throw new Error(`商家身份的用户ID必须以Sj开头实际为 ${userId}`);
}
if (identityType === 'boss' && !userId.startsWith('Boss')) {
throw new Error(`老板身份的用户ID必须以Boss开头实际为 ${userId}`);
}
// 1. 同步全局角色,确保群聊页初始化正确
app.globalData.currentRole = identityType;
wx.setStorageSync('currentRole', identityType);
const uid = userId.replace(/^(Ds|Sj|Boss|Gs|Zz)/, '');
const avatar = wx.getStorageSync('touxiang') ||
(app.globalData.ossImageUrl + app.globalData.morentouxiang);
app.globalData.currentUser = {
id: userId,
name: app.globalData.currentUser?.name || `用户${uid}`,
avatar: avatar
};
// 2. 确保 GoEasy 已使用目标身份连接
let needConnect = false;
if (!wx.goEasy || !wx.goEasy.getConnectionStatus || wx.goEasy.getConnectionStatus() !== 'connected') {
needConnect = true;
} else {
const currentUserId = wx.goEasy.im?.userId;
if (currentUserId !== userId) {
needConnect = true;
}
}
if (needConnect) {
wx.removeStorageSync('savedGoEasyConnection');
wx.removeStorageSync('goEasyUserId');
if (app.clearSavedConnection) app.clearSavedConnection();
if (wx.goEasy && wx.goEasy.getConnectionStatus() === 'connected') {
wx.goEasy.disconnect();
}
const waitPromise = waitForConnection(userId);
app.connectWithIdentity(identityType, userId, false);
try {
await waitPromise;
} catch (err) {
wx.showToast({ title: '连接失败', icon: 'none' });
throw err;
}
}
// 3. 从会话列表查找真实群组ID
let realGroupId = null;
try {
const result = await new Promise((resolve, reject) => {
wx.goEasy.im.latestConversations({
onSuccess: (res) => {
const conversations = res.content?.conversations || [];
const found = conversations.find(c => c.type === 'group' && c.data && c.data.orderId === orderId);
resolve(found ? found.groupId : null);
},
onFailed: reject
});
});
realGroupId = result;
} catch (err) {
console.error('获取会话列表失败:', err);
wx.showToast({ title: '获取群聊信息失败', icon: 'none' });
throw err;
}
if (!realGroupId) {
wx.showToast({ title: '暂未创建群聊', icon: 'none' });
return;
}
// 4. 🔥 关键:主动订阅群组并等待成功,确保发送消息时订阅已完成
try {
await new Promise((resolve, reject) => {
wx.goEasy.im.subscribeGroup({
groupIds: [realGroupId],
onSuccess: () => {
resolve();
},
onFailed: (error) => {
console.error('订阅群组失败:', error);
reject(error);
}
});
});
} catch (err) {
wx.showToast({ title: '订阅群组失败', icon: 'none' });
throw err;
}
// 5. 跳转群聊页(参数与消息列表完全一致)
const param = {
groupId: realGroupId,
orderId: orderId,
groupName: groupName || '订单群聊',
groupAvatar: groupAvatar || '',
isCross: isCross || 0
};
const path = '/pages/qunliaotian/qunliaotian?data=' + encodeURIComponent(JSON.stringify(param));
wx.navigateTo({ url: path });
}
}
/**
* 连接管理模块 - xiaoxilj.js (根治发送失败:跳转前确保群组已订阅)
* 从详情页跳转订单群聊时,提前订阅群组,避免发送消息时订阅未完成而失败
*/
const app = getApp();
function waitForConnection(expectedUserId, timeout = 10000) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
app.off('connectionChanged', handler);
reject(new Error('连接超时'));
}, timeout);
const handler = (event) => {
if (event.status === 'connected') {
if (event.userId === expectedUserId) {
clearTimeout(timer);
app.off('connectionChanged', handler);
resolve();
} else {
console.error(`连接身份不匹配: 期望 ${expectedUserId}, 实际 ${event.userId}`);
wx.removeStorageSync('savedGoEasyConnection');
wx.removeStorageSync('goEasyUserId');
wx.removeStorageSync('currentGoEasyIdentity');
if (app.clearSavedConnection) app.clearSavedConnection();
clearTimeout(timer);
app.off('connectionChanged', handler);
reject(new Error(`连接身份不匹配: 期望 ${expectedUserId}, 实际 ${event.userId}`));
}
} else if (event.status === 'disconnected' && !event.manual) {
wx.removeStorageSync('savedGoEasyConnection');
wx.removeStorageSync('goEasyUserId');
wx.removeStorageSync('currentGoEasyIdentity');
if (app.clearSavedConnection) app.clearSavedConnection();
clearTimeout(timer);
app.off('connectionChanged', handler);
reject(new Error('连接失败'));
}
};
app.on('connectionChanged', handler);
});
}
class ConnectionManager {
// 原有的私聊方法保持不变
async connectAndChat(params) {
const { identityType, userId, targetUser } = params;
if (!identityType || !userId || !targetUser || !targetUser.id) {
throw new Error('参数不完整');
}
if (identityType === 'dashou' && !userId.startsWith('Ds')) {
throw new Error(`打手身份的用户ID必须以Ds开头实际为 ${userId}`);
}
if (identityType === 'shangjia' && !userId.startsWith('Sj')) {
throw new Error(`商家身份的用户ID必须以Sj开头实际为 ${userId}`);
}
if (identityType === 'boss' && !userId.startsWith('Boss')) {
throw new Error(`老板身份的用户ID必须以Boss开头实际为 ${userId}`);
}
wx.removeStorageSync('savedGoEasyConnection');
wx.removeStorageSync('goEasyUserId');
wx.removeStorageSync('currentGoEasyIdentity');
if (app.clearSavedConnection) app.clearSavedConnection();
if (wx.goEasy && wx.goEasy.getConnectionStatus() === 'connected') {
wx.goEasy.disconnect();
}
const waitPromise = waitForConnection(userId);
app.connectWithIdentity(identityType, userId, false);
try {
await waitPromise;
const currentUser = {
id: userId,
name: app.globalData.currentUser?.name || '用户',
avatar: app.globalData.currentUser?.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
};
const to = {
id: targetUser.id,
name: targetUser.name || '用户',
avatar: targetUser.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
};
const param = { to, currentUser };
const path = '/pages/chat/chat?data=' + encodeURIComponent(JSON.stringify(param));
wx.navigateTo({ url: path });
} catch (err) {
console.error('连接失败:', err);
wx.showToast({ title: err.message || '连接失败', icon: 'none' });
throw err;
}
}
async connectToGroupChat(params) {
const { identityType, userId, orderId, groupName, groupAvatar, isCross } = params;
if (!identityType || !userId || !orderId) {
throw new Error('参数不完整identityType, userId, orderId 必填');
}
// 校验 userId 前缀
if (identityType === 'dashou' && !userId.startsWith('Ds')) {
throw new Error(`打手身份的用户ID必须以Ds开头实际为 ${userId}`);
}
if (identityType === 'shangjia' && !userId.startsWith('Sj')) {
throw new Error(`商家身份的用户ID必须以Sj开头实际为 ${userId}`);
}
if (identityType === 'boss' && !userId.startsWith('Boss')) {
throw new Error(`老板身份的用户ID必须以Boss开头实际为 ${userId}`);
}
// 1. 同步全局角色,确保群聊页初始化正确
app.globalData.currentRole = identityType;
wx.setStorageSync('currentRole', identityType);
const uid = userId.replace(/^(Ds|Sj|Boss|Gs|Zz)/, '');
const avatar = wx.getStorageSync('touxiang') ||
(app.globalData.ossImageUrl + app.globalData.morentouxiang);
app.globalData.currentUser = {
id: userId,
name: app.globalData.currentUser?.name || `用户${uid}`,
avatar: avatar
};
// 2. 确保 GoEasy 已使用目标身份连接
let needConnect = false;
if (!wx.goEasy || !wx.goEasy.getConnectionStatus || wx.goEasy.getConnectionStatus() !== 'connected') {
needConnect = true;
} else {
const currentUserId = wx.goEasy.im?.userId;
if (currentUserId !== userId) {
needConnect = true;
}
}
if (needConnect) {
wx.removeStorageSync('savedGoEasyConnection');
wx.removeStorageSync('goEasyUserId');
if (app.clearSavedConnection) app.clearSavedConnection();
if (wx.goEasy && wx.goEasy.getConnectionStatus() === 'connected') {
wx.goEasy.disconnect();
}
const waitPromise = waitForConnection(userId);
app.connectWithIdentity(identityType, userId, false);
try {
await waitPromise;
} catch (err) {
wx.showToast({ title: '连接失败', icon: 'none' });
throw err;
}
}
// 3. 从会话列表查找真实群组ID
let realGroupId = null;
try {
const result = await new Promise((resolve, reject) => {
wx.goEasy.im.latestConversations({
onSuccess: (res) => {
const conversations = res.content?.conversations || [];
const found = conversations.find(c => c.type === 'group' && c.data && c.data.orderId === orderId);
resolve(found ? found.groupId : null);
},
onFailed: reject
});
});
realGroupId = result;
} catch (err) {
console.error('获取会话列表失败:', err);
wx.showToast({ title: '获取群聊信息失败', icon: 'none' });
throw err;
}
if (!realGroupId) {
wx.showToast({ title: '暂未创建群聊', icon: 'none' });
return;
}
// 4. 🔥 关键:主动订阅群组并等待成功,确保发送消息时订阅已完成
try {
await new Promise((resolve, reject) => {
wx.goEasy.im.subscribeGroup({
groupIds: [realGroupId],
onSuccess: () => {
resolve();
},
onFailed: (error) => {
console.error('订阅群组失败:', error);
reject(error);
}
});
});
} catch (err) {
wx.showToast({ title: '订阅群组失败', icon: 'none' });
throw err;
}
// 5. 跳转群聊页(参数与消息列表完全一致)
const param = {
groupId: realGroupId,
orderId: orderId,
groupName: groupName || '订单群聊',
groupAvatar: groupAvatar || '',
isCross: isCross || 0
};
const path = '/pages/group-chat/group-chat?data=' + encodeURIComponent(JSON.stringify(param));
wx.navigateTo({ url: path });
}
}
export default new ConnectionManager();