diff --git a/pages/accept-order/accept-order.js b/pages/accept-order/accept-order.js
index c23fe70..7126cd5 100644
--- a/pages/accept-order/accept-order.js
+++ b/pages/accept-order/accept-order.js
@@ -9,6 +9,7 @@ import PopupService from '../../services/popupService.js';
import { reconnectForRole } from '../../utils/role-tab-bar.js';
import { ensurePhoneAuth } from '../../utils/phone-auth.js';
import { fetchGonggaoLunbo, isGonggaoCacheValid } from '../../utils/display-config.js';
+import { checkPenaltyForGrab } from '../../utils/grab-order-gate.js';
Page({
data: {
@@ -61,8 +62,18 @@ Page({
async onLoad() {
if (app.globalData._acceptOrderSessionReady && app.globalData._acceptOrderPageCache) {
- const cache = app.globalData._acceptOrderPageCache;
- this.setData({ ...cache });
+ const cache = { ...app.globalData._acceptOrderPageCache };
+ this.data.shangpinleixing = cache.shangpinleixing || [];
+ const dingdanList = Array.isArray(cache.dingdanList)
+ ? cache.dingdanList.map((item) => this.processDingdanItem(item))
+ : [];
+ this.setData({
+ ...cache,
+ shangpinleixing: cache.shangpinleixing || [],
+ dingdanList,
+ bankuaiBiaoqian: cache.bankuaiBiaoqian || [],
+ lunboList: cache.lunboList || [],
+ });
if ((!cache.lunboList || cache.lunboList.length === 0) && !cache.gonggao && isGonggaoCacheValid(app)) {
const lunboList = this.processLunboUrls(app.globalData.shangpinlunbo);
const gonggao = app.globalData.shangpingonggao || '';
@@ -72,6 +83,12 @@ Page({
this.loadGlobalStatus();
this.registerNotificationComponent();
await this.syncDashouProfileFromServer();
+ if (!this.data.shangpinleixing || this.data.shangpinleixing.length === 0) {
+ await this.loadShangpinLeixing(true, false, false);
+ } else if (this.data.xuanzhongLeixingId) {
+ await this.loadDingdanList(true, true);
+ }
+ this.persistPageCache();
this._skipShowRefresh = true;
return;
}
@@ -546,6 +563,19 @@ Page({
const dingdanItem = e.currentTarget.dataset.item;
if (!dingdanItem) return;
+ const penalty = await checkPenaltyForGrab();
+ if (penalty.blocked) {
+ wx.showToast({
+ title: penalty.msg,
+ icon: 'none',
+ duration: 2000,
+ });
+ setTimeout(() => {
+ wx.navigateTo({ url: penalty.url });
+ }, 500);
+ return;
+ }
+
await this.checkExamStatus();
if (this.data.examRequired && !this.data.examPassed) {
diff --git a/pages/accept-order/accept-order.json b/pages/accept-order/accept-order.json
index d496762..024a2e6 100644
--- a/pages/accept-order/accept-order.json
+++ b/pages/accept-order/accept-order.json
@@ -1,12 +1,12 @@
{
"navigationBarTitleText": "抢单大厅",
- "navigationBarBackgroundColor": "#f7dc51",
- "backgroundColor": "#f7dc51",
- "backgroundColorTop": "#f7dc51",
+ "navigationBarBackgroundColor": "#c4b5fd",
+ "backgroundColor": "#c4b5fd",
+ "backgroundColorTop": "#c4b5fd",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": false,
"backgroundTextStyle": "dark",
- "backgroundColor": "#fff8e1",
+ "backgroundColor": "#f5f3ff",
"usingComponents": {
"chenghao-tag": "/components/chenghao-tag/chenghao-tag",
"global-notification": "/components/global-notification/global-notification"
diff --git a/pages/accept-order/accept-order.wxml b/pages/accept-order/accept-order.wxml
index 4088337..d0a2cc6 100644
--- a/pages/accept-order/accept-order.wxml
+++ b/pages/accept-order/accept-order.wxml
@@ -1,4 +1,4 @@
-
+
@@ -18,7 +18,7 @@
refresher-default-style="black"
- refresher-background="#fff8e1"
+ refresher-background="#f5f3ff"
refresher-triggered="{{scrollViewRefreshing}}"
@@ -56,7 +56,7 @@
indicator-color="rgba(255,255,255,0.6)"
- indicator-active-color="#ffd061"
+ indicator-active-color="#9333ea"
autoplay="{{lunboList.length > 1}}"
diff --git a/pages/accept-order/accept-order.wxss b/pages/accept-order/accept-order.wxss
index 394b5ff..c31f877 100644
--- a/pages/accept-order/accept-order.wxss
+++ b/pages/accept-order/accept-order.wxss
@@ -3,7 +3,7 @@
@import '../../styles/dashou-xym-order-card.wxss';
page {
- background-color: #f7dc51;
+ background-color: #c4b5fd;
}
.qiangdan-page {
diff --git a/pages/dashou-exam/dashou-exam.json b/pages/dashou-exam/dashou-exam.json
index 34f1557..26876a8 100644
--- a/pages/dashou-exam/dashou-exam.json
+++ b/pages/dashou-exam/dashou-exam.json
@@ -1,6 +1,6 @@
{
"usingComponents": {},
"navigationBarTitleText": "接单考试",
- "navigationBarBackgroundColor": "#f7dc51",
+ "navigationBarBackgroundColor": "#c4b5fd",
"navigationBarTextStyle": "black"
}
diff --git a/pages/dashou-exam/dashou-exam.wxss b/pages/dashou-exam/dashou-exam.wxss
index a7f56b3..8227bc8 100644
--- a/pages/dashou-exam/dashou-exam.wxss
+++ b/pages/dashou-exam/dashou-exam.wxss
@@ -1,6 +1,6 @@
.exam-page {
min-height: 100vh;
- background: linear-gradient(180deg, #fff8e1 0%, #fff3d6 100%);
+ background: linear-gradient(180deg, #f5f3ff 0%, #fff3d6 100%);
padding: 24rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
@@ -29,12 +29,12 @@
}
.btn-primary {
- background: linear-gradient(90deg, #f7dc51, #ffc0a3);
+ background: linear-gradient(90deg, #c4b5fd, #a78bfa);
padding: 22rpx 48rpx;
border-radius: 48rpx;
font-weight: 600;
font-size: 30rpx;
- color: #492f00;
+ color: #6d28d9;
text-align: center;
}
@@ -83,7 +83,7 @@
.progress {
font-size: 28rpx;
- color: #492f00;
+ color: #6d28d9;
font-weight: 600;
}
@@ -98,8 +98,8 @@
display: inline-block;
font-size: 22rpx;
color: #b8860b;
- background: #fff8e1;
- border: 1rpx solid #f7dc51;
+ background: #f5f3ff;
+ border: 1rpx solid #c4b5fd;
border-radius: 8rpx;
padding: 4rpx 16rpx;
margin-bottom: 16rpx;
@@ -139,7 +139,7 @@
}
.opt-active {
- border-color: #f7dc51;
+ border-color: #c4b5fd;
background: #fffdf0;
}
@@ -157,15 +157,15 @@
}
.opt-radio-on {
- border-color: #f7dc51;
- background: #fff8e1;
+ border-color: #c4b5fd;
+ background: #f5f3ff;
}
.opt-radio-dot {
width: 18rpx;
height: 18rpx;
border-radius: 50%;
- background: #f7dc51;
+ background: #c4b5fd;
}
.opt-body {
@@ -177,7 +177,7 @@
.opt-label {
font-weight: 700;
- color: #492f00;
+ color: #6d28d9;
min-width: 40rpx;
font-size: 28rpx;
}
diff --git a/pages/express-order/express-order.json b/pages/express-order/express-order.json
index 7682a2b..ab822a7 100644
--- a/pages/express-order/express-order.json
+++ b/pages/express-order/express-order.json
@@ -4,7 +4,7 @@
"chenghao-tag": "/components/chenghao-tag/chenghao-tag"
},
"backgroundTextStyle": "dark",
- "navigationBarBackgroundColor": "#fff8e1",
+ "navigationBarBackgroundColor": "#f5f3ff",
"navigationBarTitleText": "常规发单",
"navigationBarTextStyle": "black",
"backgroundColor": "#f5f5f5",
diff --git a/pages/express-order/express-order.wxss b/pages/express-order/express-order.wxss
index 4584d49..4616d7b 100644
--- a/pages/express-order/express-order.wxss
+++ b/pages/express-order/express-order.wxss
@@ -290,7 +290,7 @@ page {
.filter-switch.active {
background: linear-gradient(180deg, #fae04d, #ffc0a3);
- color: #492f00;
+ color: #6d28d9;
}
.link-list {
@@ -324,7 +324,7 @@ page {
.copy-link-btn {
padding: 8rpx 20rpx;
background: linear-gradient(180deg, #fae04d, #ffc0a3);
- color: #492f00;
+ color: #6d28d9;
border-radius: 15rpx;
font-size: 22rpx;
}
@@ -339,7 +339,7 @@ page {
padding: 15rpx 40rpx;
background: linear-gradient(180deg, #fae04d, #ffc0a3);
border-radius: 30rpx;
- color: #492f00;
+ color: #6d28d9;
font-size: 26rpx;
}
@@ -374,7 +374,7 @@ page {
.copy-modal-grid-btn,
.edit-modal-grid-btn {
background: linear-gradient(180deg, #fae04d, #ffc0a3);
- color: #492f00;
+ color: #6d28d9;
}
.delete-modal-grid-btn {
@@ -485,7 +485,7 @@ page {
.confirm-add-grid-btn {
background: linear-gradient(180deg, #fae04d, #ffc0a3);
- color: #492f00;
+ color: #6d28d9;
}
.confirm-content {
@@ -539,7 +539,7 @@ page {
.confirm-new-link-grid-btn {
background: linear-gradient(180deg, #fae04d, #ffc0a3);
- color: #492f00;
+ color: #6d28d9;
}
.loading-mask {
@@ -569,7 +569,7 @@ page {
width: 90rpx;
height: 90rpx;
border: 8rpx solid #eee;
- border-top: 8rpx solid #ffd061;
+ border-top: 8rpx solid #9333ea;
border-radius: 50%;
animation: spin 1.2s linear infinite;
margin-bottom: 35rpx;
diff --git a/pages/fighter/fighter.json b/pages/fighter/fighter.json
index a31615c..0f3a586 100644
--- a/pages/fighter/fighter.json
+++ b/pages/fighter/fighter.json
@@ -6,7 +6,7 @@
"pindao-modal": "/components/pindao-modal/pindao-modal"
},
"navigationStyle": "custom",
- "backgroundColor": "#fff8e1",
+ "backgroundColor": "#f5f3ff",
"backgroundTextStyle": "dark",
"enablePullDownRefresh": false,
"onReachBottomDistance": 50
diff --git a/pages/fighter/fighter.wxml b/pages/fighter/fighter.wxml
index b9f6e40..198e4e6 100644
--- a/pages/fighter/fighter.wxml
+++ b/pages/fighter/fighter.wxml
@@ -39,7 +39,7 @@
refresher-enabled="{{true}}"
refresher-threshold="80"
refresher-default-style="black"
- refresher-background="#fff8e1"
+ refresher-background="#f5f3ff"
refresher-triggered="{{scrollViewRefreshing}}"
bindrefresherrefresh="onPullDownRefresh"
>
diff --git a/pages/fighter/fighter.wxss b/pages/fighter/fighter.wxss
index 120d290..b70fa4f 100644
--- a/pages/fighter/fighter.wxss
+++ b/pages/fighter/fighter.wxss
@@ -1,7 +1,7 @@
/* 打手端个人中心 - 逍遥梦 UI(逻辑不变) */
page {
- background: #fff8e1;
+ background: #f5f3ff;
color: #333;
}
@@ -16,7 +16,7 @@ page {
flex-direction: column;
overflow: hidden;
box-sizing: border-box;
- background: linear-gradient(180deg, #f7dc51 0%, #fff 28%, #fff8e1 100%);
+ background: linear-gradient(180deg, #c4b5fd 0%, #fff 28%, #f5f3ff 100%);
position: relative;
}
@@ -126,7 +126,7 @@ page {
padding: 2rpx 12rpx;
border-radius: 8rpx;
background: linear-gradient(180deg, #fae04d, #ffc0a3);
- color: #492f00;
+ color: #6d28d9;
}
.sn-row {
@@ -238,7 +238,7 @@ page {
border: 2rpx solid #fff;
border-radius: 60rpx;
background: linear-gradient(180deg, #fae04d, #ffc0a3);
- color: #492f00;
+ color: #6d28d9;
white-space: nowrap;
}
@@ -272,8 +272,8 @@ page {
border-radius: 16rpx;
font-size: 28rpx;
font-weight: 600;
- color: #492f00;
- background: linear-gradient(180deg, #fff8e1, #ffe8b8);
+ color: #6d28d9;
+ background: linear-gradient(180deg, #f5f3ff, #ffe8b8);
border: 1rpx solid rgba(201, 169, 98, 0.45);
}
@@ -305,7 +305,7 @@ page {
.btn-bg {
background: linear-gradient(180deg, #fae04d, #ffc0a3);
border-radius: 30px;
- color: #492f00;
+ color: #6d28d9;
}
.withdraw {
@@ -334,7 +334,7 @@ page {
.vip-strip-sub {
display: block;
font-size: 22rpx;
- color: rgba(252, 210, 112, 0.65);
+ color: rgba(196, 181, 253, 0.65);
margin-top: 4rpx;
}
@@ -552,7 +552,7 @@ page {
}
.rank-theme-gold {
- background: linear-gradient(135deg, #a8842a 0%, #f5d563 100%);
+ background: linear-gradient(135deg, #a8842a 0%, #c4b5fd 100%);
}
.rank-theme-gold .rank-tile-name,
@@ -608,7 +608,7 @@ page {
margin-bottom: 16rpx;
}
-.unreg-btn-text { color: #492f00; }
+.unreg-btn-text { color: #6d28d9; }
.unreg-btn-secondary {
text-align: center;
@@ -644,7 +644,7 @@ page {
width: 60rpx;
height: 60rpx;
border: 4rpx solid rgba(0, 0, 0, 0.08);
- border-top-color: #ffd061;
+ border-top-color: #9333ea;
border-radius: 50%;
animation: fighterSpin 0.8s linear infinite;
}
diff --git a/pages/group-chat/group-chat.js b/pages/group-chat/group-chat.js
index 9e1aee1..348cae3 100644
--- a/pages/group-chat/group-chat.js
+++ b/pages/group-chat/group-chat.js
@@ -1,8 +1,10 @@
const app = getApp();
import { formatDate } from '../../static/lib/utils';
import { sendGroupMessage } from '../../utils/message-sender';
-import { getLocalImUserId } from '../../utils/im-user.js';
-import { resolveAvatarUrl } from '../../utils/avatar.js';
+import { showConfirmByScene } from '../../utils/scriptService.js';
+import { normalizeGroupMessage, getZhuangtaiText } from '../../utils/group-chat.js';
+import { persistGroupMeta } from '../../utils/im-user.js';
+import { getFreshImUser } from '../../utils/im-user.js';
Page({
data: {
@@ -11,6 +13,10 @@ Page({
groupAvatar: '',
orderId: '',
isCross: 0,
+ orderZhuangtai: null,
+ orderZhuangtaiText: '',
+ orderJine: '',
+ orderJieshao: '',
currentUser: null,
messages: [],
inputText: '',
@@ -28,7 +34,6 @@ Page({
lastTapMsgId: '',
emojiList: ['😀','😁','😂','🤣','😃','😄','😅','😆','😉','😊','😋','😎','😍','😘','😗','😙','😚','🙂','🤗','🤩','🤔','🤨','😐','😑','😶','🙄','😏','😣','😥','😮','🤐','😯','😪','😫','😴','😌','😛','😜','😝','🤤','😒','😓','😔','😕','🙃','🤑','😲','☹','🙁','😖','😞','😟','😤','😢','😭','😦','😧','😨','😩','🤯','😬','😰','😱','🥵','🥶','😳','🤪','😵','😡','😠','🤬','😷','🤒','🤕','🤢','🤮','🤧','😇','🤠','🤡','🤥','🤫','🤭','🧐','🤓','😈','👿','👹','👺','💀','👻','👽','🤖','💩','😺','😸','😹','😻','😼','😽','🙀','😿','😾'],
pendingImage: '',
- pendingImageFile: null,
keyboardHeight: 0,
bottomSafeHeight: 10
},
@@ -41,111 +46,164 @@ Page({
p = { ...p, ...d };
} catch (e) {}
}
- const orderId = String(p.orderId || p.groupId || '');
- const groupId = String(p.groupId || p.orderId || '');
this.setData({
- groupId,
+ groupId: p.groupId,
groupName: p.groupName,
groupAvatar: p.groupAvatar,
- orderId,
- isCross: p.isCross,
+ orderId: p.orderId,
+ isCross: p.isCross
});
wx.setNavigationBarTitle({ title: this.data.groupName });
- const localImId = getLocalImUserId(app);
- const userId = localImId || p.currentUserId;
- if (userId) {
+ // 如果有外部传入的 currentUser 信息,直接用,不再依赖全局状态
+ if (p.currentUserId) {
this.setData({
currentUser: {
- id: userId,
- name: p.currentUserName || app.globalData.currentUser?.name || `用户${wx.getStorageSync('uid') || ''}`,
- avatar: resolveAvatarUrl(p.currentUserAvatar || wx.getStorageSync('touxiang'), app),
- },
+ id: p.currentUserId,
+ name: p.currentUserName || `用户${p.currentUserId.replace(/^[A-Za-z]+/,'')}`,
+ avatar: p.currentUserAvatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
+ }
});
} else {
this.initCurrentUser();
}
- },
-
- initCurrentUser() {
- const imId = getLocalImUserId(app);
- const uid = wx.getStorageSync('uid');
- if (!imId) return;
- this.setData({
- currentUser: {
- id: imId,
- name: app.globalData.currentUser?.name || wx.getStorageSync('nicheng') || `用户${uid}`,
- avatar: resolveAvatarUrl(wx.getStorageSync('touxiang'), app),
- },
- });
- },
- buildImageFile(tempPath, size) {
- return {
- path: tempPath,
- tempFilePath: tempPath,
- size: size || 0,
- };
- },
-
- normalizeMessage(msg) {
- if (!msg) return msg;
- if (!msg.payload) msg.payload = {};
- if (msg.type === 'image') {
- const p = msg.payload;
- if (!p.url) p.url = p.thumbnail || p.thumb || p.imageUrl || '';
+ const meta = (app.globalData.groupInfoMap && app.globalData.groupInfoMap[p.groupId]) || {};
+ const initZt = p.orderZhuangtai != null ? p.orderZhuangtai : meta.orderZhuangtai;
+ if (initZt != null) {
+ this.applyOrderStatus(initZt, {
+ orderId: p.orderId || meta.orderId,
+ jine: p.orderJine || meta.orderJine,
+ jieshao: p.orderDesc || meta.orderDesc,
+ });
}
- return msg;
},
- mergeSentMessage(localId, sent) {
- if (!sent) {
- this.updateMsg(localId, 'success');
+ onShow() {
+ app.globalData.pageState.isInChatPage = true;
+ app.globalData.pageState.currentChatId = this.data.groupId;
+ app.globalData.pageState.isInGroupChat = true;
+ this.ensureChatConnection();
+ },
+
+ onHide() {
+ app.globalData.pageState.isInChatPage = false;
+ app.globalData.pageState.currentChatId = '';
+ app.globalData.pageState.isInGroupChat = false;
+ this.clearPageListeners();
+ },
+
+ applyOrderStatus(zhuangtai, extra) {
+ if (zhuangtai == null && zhuangtai !== 0) return;
+ const patch = {
+ orderZhuangtai: zhuangtai,
+ orderZhuangtaiText: getZhuangtaiText(zhuangtai),
+ };
+ if (extra) {
+ if (extra.jine) patch.orderJine = extra.jine;
+ if (extra.jieshao) patch.orderJieshao = extra.jieshao;
+ if (extra.orderId) patch.orderId = extra.orderId;
+ }
+ this.setData(patch);
+
+ const groupId = this.data.groupId;
+ if (!groupId) return;
+ if (!app.globalData.groupInfoMap) app.globalData.groupInfoMap = {};
+ const meta = { ...(app.globalData.groupInfoMap[groupId] || {}), orderZhuangtai: zhuangtai };
+ if (extra && extra.orderId) meta.orderId = extra.orderId;
+ app.globalData.groupInfoMap[groupId] = meta;
+ persistGroupMeta(app, groupId, meta);
+ },
+
+ applyOrderCardPayload(payload) {
+ if (!payload) return;
+ const cardOrderId = payload.orderId || payload.dingdan_id;
+ if (cardOrderId && this.data.orderId && cardOrderId !== this.data.orderId) return;
+ const zt = payload.zhuangtai != null ? payload.zhuangtai : this.data.orderZhuangtai;
+ payload.zhuangtaiText = getZhuangtaiText(zt);
+ this.applyOrderStatus(zt, {
+ orderId: payload.orderId || payload.dingdan_id || this.data.orderId,
+ jine: payload.jine,
+ jieshao: payload.jieshao,
+ });
+ this.updateOrderBubblesInList(payload);
+ },
+
+ updateOrderBubblesInList(payload) {
+ if (!payload) return;
+ const orderId = payload.orderId || payload.dingdan_id;
+ if (!orderId) return;
+ const messages = this.data.messages.map((m) => {
+ if (m.type !== 'order' || !m.payload) return m;
+ const oid = m.payload.orderId || m.payload.dingdan_id;
+ if (oid !== orderId) return m;
+ return { ...m, payload: { ...m.payload, ...payload, orderId: oid } };
+ });
+ this.setData({ messages });
+ },
+
+ mergeServerMessage(localId, status, serverMsg) {
+ if (!serverMsg) {
+ this.updateMsg(localId, status);
return;
}
+ normalizeGroupMessage(serverMsg);
+ serverMsg.formattedTime = formatDate(serverMsg.timestamp);
+ if (serverMsg.type === 'order') {
+ this.applyOrderCardPayload(serverMsg.payload);
+ }
const msgs = this.data.messages.map((m) => {
if (m.messageId !== localId) return m;
- return this.normalizeMessage({
- ...m,
- ...sent,
- status: 'success',
- messageId: sent.messageId || m.messageId,
- payload: sent.payload || m.payload,
- senderData: m.senderData || sent.senderData,
- });
+ return {
+ ...serverMsg,
+ showTime: m.showTime,
+ status: status || 'success',
+ formattedTime: serverMsg.formattedTime || m.formattedTime,
+ };
});
this.setData({ messages: msgs });
},
- onShow() {
- this.autoConnect();
- },
+ async ensureChatConnection() {
+ const role = app.globalData.currentRole || 'normal';
+ const uid = wx.getStorageSync('uid');
+ const prefixMap = { normal:'Boss', dashou:'Ds', shangjia:'Sj', guanshi:'Gs', zuzhang:'Zz', kaoheguan:'Kh' };
+ const pageUserId = this.data.currentUser?.id;
+ const targetUserId = pageUserId || (prefixMap[role] || 'Boss') + uid;
- onHide() {
- this.clearAllListeners();
- },
-
- autoConnect() {
- const targetUserId = this.data.currentUser?.id || getLocalImUserId(app);
- if (!targetUserId) return;
-
- const afterReady = () => {
- this.subscribeGroupIfNeeded().then(() => {
- this.setupAllListeners();
- this.loadHistory(true);
- this.markGroupMessageAsRead();
- });
+ const onReady = () => {
+ this.subscribeGroupIfNeeded()
+ .then(() => {
+ this.setupAllListeners();
+ return this.loadHistory(true);
+ })
+ .then(() => this.markGroupMessageAsRead());
};
- const status = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected';
- if (status === 'connected' || status === 'reconnected') {
- const currentUserId = wx.goEasy.im ? wx.goEasy.im.userId : null;
- if (currentUserId === targetUserId) {
- afterReady();
- return;
- }
- wx.goEasy.disconnect();
+ const connectedId = app.globalData?.goEasyConnection?.userId || wx.goEasy?.im?.userId;
+ const status = wx.goEasy?.getConnectionStatus?.() || 'disconnected';
+ const imOk = status === 'connected' || status === 'reconnected';
+
+ if (imOk && connectedId === targetUserId) {
+ onReady();
+ return;
}
- this.connectGoEasy(targetUserId);
+
+ try {
+ if (app.ensureImForRole) {
+ await app.ensureImForRole(role);
+ } else if (app.connectWithIdentity) {
+ await app.connectWithIdentity(role, targetUserId, true);
+ }
+ onReady();
+ } catch (e) {
+ const s = wx.goEasy?.getConnectionStatus?.();
+ if (s === 'connected' || s === 'reconnected') onReady();
+ else wx.showToast({ title: '连接失败,请重试', icon: 'none' });
+ }
+ },
+
+ clearPageListeners() {
+ this.clearAllListeners();
},
connectGoEasy(userId) {
@@ -155,14 +213,15 @@ Page({
id: userId,
data: {
name: currentUser.name,
- avatar: resolveAvatarUrl(currentUser.avatar, app),
+ avatar: currentUser.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
},
onSuccess: () => {
- this.subscribeGroupIfNeeded().then(() => {
- this.setupAllListeners();
- this.loadHistory(true);
- this.markGroupMessageAsRead();
- });
+ this.subscribeGroupIfNeeded()
+ .then(() => {
+ this.setupAllListeners();
+ return this.loadHistory(true);
+ })
+ .then(() => this.markGroupMessageAsRead());
},
onFailed: (error) => {
if (error.code === 408) {
@@ -179,14 +238,17 @@ Page({
subscribeGroupIfNeeded() {
const { groupId } = this.data;
- if (!groupId || !wx.goEasy?.im?.subscribeGroup) return Promise.resolve();
+ if (!groupId || !wx.goEasy?.im) return Promise.resolve();
return new Promise((resolve) => {
wx.goEasy.im.subscribeGroup({
- groupIds: [String(groupId)],
- onSuccess: () => resolve(true),
- onFailed: (error) => {
- console.warn('subscribeGroup failed', error);
- resolve(false);
+ groupIds: [groupId],
+ onSuccess: () => {
+ console.log(`[群聊页] 订阅成功: ${groupId}`);
+ resolve();
+ },
+ onFailed: (err) => {
+ console.error(`[群聊页] 订阅失败: ${groupId}`, err);
+ resolve();
},
});
});
@@ -205,6 +267,23 @@ Page({
if (this._deleteHandler) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.MESSAGE_DELETED, this._deleteHandler); this._deleteHandler = null; }
},
+ initCurrentUser() {
+ const uid = wx.getStorageSync('uid');
+ const role = app.globalData.currentRole || 'normal';
+ const fresh = getFreshImUser(app, role, uid);
+ this.setData({ currentUser: {
+ id: fresh.id,
+ name: fresh.name,
+ avatar: fresh.avatar,
+ }});
+ },
+
+ fixAvatar(url) {
+ if (!url) return app.globalData.ossImageUrl + app.globalData.morentouxiang;
+ if (url.startsWith('http')) return url;
+ return app.globalData.ossImageUrl + url;
+ },
+
isConnected() {
const s = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected';
return s === 'connected' || s === 'reconnected';
@@ -231,12 +310,14 @@ Page({
let list = res.content || [];
list.sort((a, b) => a.timestamp - b.timestamp);
list.forEach((m, idx) => {
- m = this.normalizeMessage(m);
- list[idx] = m;
+ normalizeGroupMessage(m);
m.formattedTime = formatDate(m.timestamp);
if (idx === 0) m.showTime = true;
else m.showTime = (m.timestamp - list[idx-1].timestamp) / 60000 > 5;
if (!m.senderData) m.senderData = { name: '未知用户', avatar: '' };
+ if (m.type === 'order' && m.payload) {
+ this.applyOrderCardPayload(m.payload);
+ }
});
let final = refresh ? list : [...list, ...this.data.messages];
if (!refresh) {
@@ -267,17 +348,21 @@ Page({
if (this._msgHandler) wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, this._msgHandler);
this._msgHandler = (msg) => {
if (msg.groupId !== this.data.groupId) return;
- if (msg.senderId === this.data.currentUser.id) return;
- msg = this.normalizeMessage(msg);
+ normalizeGroupMessage(msg);
+ if (msg.type === 'order' && msg.payload) {
+ this.applyOrderCardPayload(msg.payload);
+ }
+ if (msg.senderId === this.data.currentUser.id && msg.type !== 'order') return;
msg.formattedTime = formatDate(msg.timestamp);
const msgs = this.data.messages;
+ if (msgs.some((m) => m.messageId === msg.messageId)) return;
const last = msgs.length ? msgs[msgs.length-1] : null;
msg.showTime = last ? (msg.timestamp - last.timestamp) / 60000 > 5 : true;
this.setData({ messages: [...msgs, msg], scrollToView: 'msg-bottom' });
this.markGroupMessageAsRead();
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, this._msgHandler);
- this.markGroupMessageAsRead();
+ this.markGroupMessageAsRead(); // ← 收到新消息就标记已读
},
markGroupMessageAsRead() {
@@ -312,31 +397,21 @@ Page({
chooseImage() {
const that = this;
- wx.chooseMedia({
- count: 1,
- mediaType: ['image'],
- sizeType: ['compressed'],
- sourceType: ['album', 'camera'],
- success(res) {
- const file = (res.tempFiles && res.tempFiles[0]) || null;
- if (!file || !file.tempFilePath) return;
- that.setData({
- pendingImage: file.tempFilePath,
- pendingImageFile: that.buildImageFile(file.tempFilePath, file.size),
- });
- },
- fail() {
- wx.showToast({ title: '选择图片失败', icon: 'none' });
- },
+ wx.chooseImage({
+ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'],
+ success(res) { that.setData({ pendingImage: res.tempFilePaths[0] }); }
});
},
- clearPendingImage() { this.setData({ pendingImage: '', pendingImageFile: null }); },
+ clearPendingImage() { this.setData({ pendingImage: '' }); },
sendMessage() {
if (this.data.pendingImage) {
- this.sendImageMsg(this.data.pendingImage, this.data.pendingImageFile);
- this.setData({ pendingImage: '', pendingImageFile: null });
+ const file = this.data.pendingImage;
+ showConfirmByScene('chat_image_confirm', () => {
+ this.sendImageMsg(file);
+ this.setData({ pendingImage: '' });
+ });
return;
}
const text = this.data.inputText.trim();
@@ -367,61 +442,18 @@ Page({
}
that.setData({ messages: [...msgs, localMsg], scrollToView: 'msg-bottom' });
},
- onSuccess: (messageId, status) => {
- that.updateMsg(messageId, status);
+ onSuccess: (messageId, status, serverMsg) => {
+ that.mergeServerMessage(messageId, status, serverMsg);
}
});
},
- sendImageMsg(filePath, fileObj) {
+ sendImageMsg(file) {
const that = this;
- const { currentUser, groupId, orderId, isCross, groupName, groupAvatar } = that.data;
- if (!filePath || !currentUser || !groupId) return;
-
- if (isCross === 0) {
- if (!wx.goEasy || !wx.goEasy.im) {
- wx.showToast({ title: '聊天未连接', icon: 'none' });
- return;
- }
- const avatar = resolveAvatarUrl(currentUser.avatar, app);
- const name = currentUser.name || '用户';
- const localId = `image-${Date.now()}`;
- const imageFile = fileObj || this.buildImageFile(filePath, 0);
- const localMsg = {
- messageId: localId,
- type: 'image',
- timestamp: Date.now(),
- senderId: currentUser.id,
- groupId,
- senderData: { name, avatar },
- payload: { url: filePath },
- status: 'sending',
- formattedTime: formatDate(Date.now()),
- showTime: this.needShow(Date.now()),
- };
- that.setData({ messages: [...that.data.messages, localMsg], scrollToView: 'msg-bottom' });
- const toData = { name: groupName || '订单群聊', avatar: groupAvatar || avatar, isCross: 0 };
- if (orderId) toData.orderId = orderId;
- const imgMsg = wx.goEasy.im.createImageMessage({
- file: imageFile,
- to: { type: wx.GoEasy.IM_SCENE.GROUP, id: groupId, data: toData },
- });
- wx.goEasy.im.sendMessage({
- message: imgMsg,
- onSuccess: (sent) => that.mergeSentMessage(localId, sent),
- onFailed: (error) => {
- console.error('[群聊] 图片发送失败', error);
- that.updateMsg(localId, 'failed');
- wx.showToast({ title: '图片发送失败', icon: 'none' });
- },
- });
- return;
- }
-
sendGroupMessage({
msgData: {
type: 'image',
- filePath,
+ filePath: file,
currentUser: that.data.currentUser,
groupId: that.data.groupId,
orderId: that.data.orderId,
@@ -438,11 +470,8 @@ Page({
}
that.setData({ messages: [...msgs, localMsg], scrollToView: 'msg-bottom' });
},
- onSuccess: (messageId, status) => {
- that.updateMsg(messageId, status);
- if (status === 'failed') {
- wx.showToast({ title: '图片发送失败', icon: 'none' });
- }
+ onSuccess: (messageId, status, serverMsg) => {
+ that.mergeServerMessage(messageId, status, serverMsg);
}
});
},
@@ -477,7 +506,7 @@ Page({
const msgId = e.currentTarget.dataset.messageid;
if (!msgId) return;
const msg = this.data.messages.find(m => m.messageId === msgId);
- if (!msg || msg.messageId.startsWith('local-')) return;
+ if (!msg || String(msg.messageId).startsWith('local-')) return;
const itemList = ['复制'];
if (msg.senderId === this.data.currentUser.id && (Date.now() - msg.timestamp < 120000) && !msg.recalled) {
itemList.push('撤回');
diff --git a/pages/merchant-data-stats/merchant-data-stats.wxss b/pages/merchant-data-stats/merchant-data-stats.wxss
index f9d7bdd..338894b 100644
--- a/pages/merchant-data-stats/merchant-data-stats.wxss
+++ b/pages/merchant-data-stats/merchant-data-stats.wxss
@@ -1,6 +1,6 @@
@import '../../styles/shangjia-xym-common.wxss';
-page { background: #fff8e1; }
+page { background: #f5f3ff; }
.sub-scroll { height: calc(100vh - 120rpx); }
diff --git a/pages/merchant-dispatch/merchant-dispatch.json b/pages/merchant-dispatch/merchant-dispatch.json
index c01f2ef..3278739 100644
--- a/pages/merchant-dispatch/merchant-dispatch.json
+++ b/pages/merchant-dispatch/merchant-dispatch.json
@@ -4,7 +4,7 @@
"popup-notice": "/components/popup-notice/popup-notice"
},
"backgroundTextStyle": "dark",
- "navigationBarBackgroundColor": "#fff8e1",
+ "navigationBarBackgroundColor": "#f5f3ff",
"navigationBarTitleText": "自定义发单",
"navigationBarTextStyle": "black",
"backgroundColor": "#f5f5f5",
diff --git a/pages/merchant-dispatch/merchant-dispatch.wxml b/pages/merchant-dispatch/merchant-dispatch.wxml
index b535bc2..600f22e 100644
--- a/pages/merchant-dispatch/merchant-dispatch.wxml
+++ b/pages/merchant-dispatch/merchant-dispatch.wxml
@@ -159,7 +159,7 @@
选填
-
+
{{commissionEnabled ? '已开启' : '未开启'}}
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) => {
- // 408 多为 GoEasy 节点超时,开发者工具常见,不影响订单等业务接口
- if (error && error.code === 408) {
- console.warn('GoEasy 连接超时(408),将使用本地会话缓存');
- bindGlobalListenersIfNeeded(app, userId);
- loadConversations(app);
- return;
- }
- console.warn('GoEasy连接失败', error);
- 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('#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 };
+// 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 };
diff --git a/utils/grab-order-gate.js b/utils/grab-order-gate.js
new file mode 100644
index 0000000..5fc5799
--- /dev/null
+++ b/utils/grab-order-gate.js
@@ -0,0 +1,25 @@
+/**
+ * 抢单前检查(仅点击抢单时调用)
+ */
+import request from './request.js';
+
+/** 检查是否有待处理罚款(待缴纳 / 申诉中) */
+export async function checkPenaltyForGrab() {
+ try {
+ const res = await request({ url: '/yonghu/cffktjhq', method: 'POST' });
+ if (res?.data?.code === 0) {
+ const fk = res.data.data?.fakuan || {};
+ const pending = Number(fk.daijiaona || 0) + Number(fk.shensuzhong || 0);
+ if (pending > 0) {
+ return {
+ blocked: true,
+ msg: '您有未缴纳或申诉中的罚单,请先处理后再抢单',
+ url: '/pages/penalty/penalty/penalty',
+ };
+ }
+ }
+ } catch (e) {
+ console.error('checkPenaltyForGrab failed', e);
+ }
+ return { blocked: false };
+}
diff --git a/utils/group-chat.js b/utils/group-chat.js
new file mode 100644
index 0000000..f259133
--- /dev/null
+++ b/utils/group-chat.js
@@ -0,0 +1,241 @@
+/**
+ * 打手-商家/老板配对群:group_Ds{打手}_Sj{商家}
+ */
+export const ORDER_CARD_PREFIX = '[ORDER_CARD]';
+
+const ZHUANGTAI_MAP = {
+ 1: '已下单', 2: '进行中', 3: '已完成', 4: '退款中',
+ 5: '已退款', 6: '退款失败', 7: '指定中', 8: '结算中',
+};
+
+export function getZhuangtaiText(zhuangtai) {
+ if (zhuangtai == null || zhuangtai === '') return '';
+ const n = Number(zhuangtai);
+ return ZHUANGTAI_MAP[n] || ZHUANGTAI_MAP[zhuangtai] || '';
+}
+
+export function buildDashouShangjiaGroupId(dashouUid, shangjiaUid) {
+ return `group_Ds${dashouUid}_Sj${shangjiaUid}`;
+}
+
+export function buildDashouBossGroupId(dashouUid, bossUid) {
+ return `group_Ds${dashouUid}_Boss${bossUid}`;
+}
+
+export function resolveLocalGroupId(identityType, myUid, partnerUid, fadanPingtai, orderId, isCross) {
+ const cross = isCross === 1 || isCross === true || String(isCross) === '1';
+ if (cross && orderId) {
+ return `group_${orderId}`;
+ }
+ if (!myUid) return orderId ? `group_${orderId}` : null;
+ if (!partnerUid) return orderId ? `group_${orderId}` : null;
+ if (identityType === 'shangjia') {
+ return buildDashouShangjiaGroupId(partnerUid, myUid);
+ }
+ if (identityType === 'dashou') {
+ if (fadanPingtai === 2 || String(fadanPingtai) === '2') {
+ return buildDashouShangjiaGroupId(myUid, partnerUid);
+ }
+ return buildDashouBossGroupId(myUid, partnerUid);
+ }
+ return orderId ? `group_${orderId}` : null;
+}
+
+export function isPairGroupId(groupId) {
+ return /^group_Ds\w+_(Sj|Boss)\w+$/.test(groupId || '');
+}
+
+export function parsePairGroupId(groupId) {
+ if (!groupId) return null;
+ let m = /^group_Ds(\w+)_Sj(\w+)$/.exec(groupId);
+ if (m) return { dashouId: `Ds${m[1]}`, partnerId: `Sj${m[2]}`, partnerRole: 'shangjia' };
+ m = /^group_Ds(\w+)_Boss(\w+)$/.exec(groupId);
+ if (m) return { dashouId: `Ds${m[1]}`, partnerId: `Boss${m[2]}`, partnerRole: 'boss' };
+ return null;
+}
+
+export function getCounterpartGoEasyId(groupId, myGoEasyId) {
+ const parsed = parsePairGroupId(groupId);
+ if (!parsed || !myGoEasyId) return null;
+ if (myGoEasyId === parsed.dashouId) return parsed.partnerId;
+ if (myGoEasyId === parsed.partnerId) return parsed.dashouId;
+ return null;
+}
+
+function fixAvatarUrl(avatar, defaultAvatar, ossBase) {
+ if (!avatar) return defaultAvatar;
+ if (avatar.startsWith('http')) return avatar;
+ return (ossBase || '') + avatar.replace(/^\//, '');
+}
+
+/** 根据群 to.data 与当前身份,解析对方/双方展示信息 */
+export function applyPairMetaToConversation(item, myGoEasyId, defaultAvatar, ossBase) {
+ if (!item || !item.data) return;
+ const d = item.data;
+
+ if (d.dashouGoEasyId && d.partnerGoEasyId) {
+ const isDashou = myGoEasyId === d.dashouGoEasyId;
+ const isPartner = myGoEasyId === d.partnerGoEasyId;
+ d.dashouNicheng = d.dashouName || d.dashouNicheng || '';
+ d.partnerNicheng = d.partnerName || d.partnerNicheng || '';
+ d.dashouAvatar = fixAvatarUrl(d.dashouAvatar, defaultAvatar, ossBase);
+ d.partnerAvatar = fixAvatarUrl(d.partnerAvatar, defaultAvatar, ossBase);
+
+ if (isDashou) {
+ d.counterpartId = d.partnerGoEasyId;
+ d.counterpartYonghuid = d.partnerYonghuid || d.partnerGoEasyId.replace(/^(Sj|Boss)/, '');
+ d.partnerRoleLabel = d.partnerGoEasyId.startsWith('Boss') ? '老板' : '商家';
+ d.name = d.partnerNicheng || `${d.partnerRoleLabel}${d.counterpartYonghuid}`;
+ d.avatar = d.partnerAvatar || defaultAvatar;
+ } else if (isPartner) {
+ d.counterpartId = d.dashouGoEasyId;
+ d.counterpartYonghuid = d.dashouYonghuid || d.dashouGoEasyId.replace(/^Ds/, '');
+ d.name = d.dashouNicheng || `打手${d.counterpartYonghuid}`;
+ d.avatar = d.dashouAvatar || defaultAvatar;
+ } else {
+ const cpId = getCounterpartGoEasyId(item.groupId, myGoEasyId);
+ if (cpId === d.partnerGoEasyId) {
+ d.name = d.partnerNicheng;
+ d.avatar = d.partnerAvatar || defaultAvatar;
+ } else if (cpId === d.dashouGoEasyId) {
+ d.name = d.dashouNicheng;
+ d.avatar = d.dashouAvatar || defaultAvatar;
+ }
+ }
+ return;
+ }
+
+ const counterpartId = getCounterpartGoEasyId(item.groupId, myGoEasyId);
+ if (counterpartId) {
+ d.counterpartId = counterpartId;
+ d.counterpartYonghuid = counterpartId.replace(/^(Ds|Sj|Boss)/, '');
+ if (!d.name || d.name === item.groupId) {
+ d.name = `用户${d.counterpartYonghuid.slice(-6)}`;
+ }
+ }
+
+ if (item.lastMessage && item.lastMessage.senderId && item.lastMessage.senderId !== myGoEasyId) {
+ const sd = item.lastMessage.senderData || {};
+ if (sd.avatar) d.avatar = fixAvatarUrl(sd.avatar, defaultAvatar, ossBase);
+ if (sd.name) d.name = sd.name;
+ }
+
+ d.avatar = fixAvatarUrl(d.avatar, defaultAvatar, ossBase);
+}
+
+export function parseOrderCardText(text) {
+ if (!text || !text.startsWith(ORDER_CARD_PREFIX)) return null;
+ try {
+ const data = JSON.parse(text.slice(ORDER_CARD_PREFIX.length));
+ return {
+ orderId: data.dingdan_id,
+ jieshao: data.jieshao || '',
+ jine: data.jine || '',
+ beizhu: data.beizhu || '',
+ zhuangtai: data.zhuangtai,
+ nicheng: data.nicheng || '',
+ create_time: data.create_time || '',
+ };
+ } catch (e) {
+ return null;
+ }
+}
+
+import { getChatImageUrl } from '../static/lib/utils.js';
+
+export function normalizeGroupMessage(msg) {
+ if (!msg) return msg;
+ if (msg.type === 'text' && msg.payload && msg.payload.text) {
+ const card = parseOrderCardText(msg.payload.text);
+ if (card) {
+ msg.type = 'order';
+ msg.payload = card;
+ }
+ }
+ if (msg.type === 'custom' && msg.payload) {
+ const inner = msg.payload.payload || msg.payload;
+ if (inner && (inner.orderId || inner.dingdan_id)) {
+ msg.type = 'order';
+ msg.payload = {
+ orderId: inner.orderId || inner.dingdan_id,
+ jieshao: inner.jieshao || '',
+ jine: inner.jine || '',
+ beizhu: inner.beizhu || '',
+ zhuangtai: inner.zhuangtai,
+ nicheng: inner.nicheng || '',
+ create_time: inner.create_time || '',
+ };
+ }
+ }
+ if (msg.type === 'image') {
+ const url = getChatImageUrl(msg);
+ msg.payload = { ...(msg.payload || {}), url };
+ msg.imageUrl = url;
+ }
+ return msg;
+}
+
+export function formatLastMessagePreview(msg) {
+ if (!msg) return '';
+ if (msg.type === 'text' && msg.payload && msg.payload.text) {
+ const card = parseOrderCardText(msg.payload.text);
+ if (card) return `[订单] ${card.jieshao || card.orderId}`;
+ return msg.payload.text;
+ }
+ if (msg.type === 'order' && msg.payload) {
+ return `[订单] ${msg.payload.jieshao || msg.payload.orderId || ''}`;
+ }
+ if (msg.type === 'image') return '[图片]';
+ return '[消息]';
+}
+
+export function enrichGroupConversation(item, myGoEasyId, defaultAvatar, ossBase) {
+ if (!item || item.type !== 'group') return item;
+ if (!item.data) item.data = {};
+
+ applyPairMetaToConversation(item, myGoEasyId, defaultAvatar, ossBase);
+
+ if (item.data.orderZhuangtai != null) {
+ item.data.orderZhuangtaiText = ZHUANGTAI_MAP[item.data.orderZhuangtai] || '';
+ }
+
+ item.displayLastMsg = formatLastMessagePreview(item.lastMessage);
+ if (!item.data.avatar) item.data.avatar = defaultAvatar;
+ return item;
+}
+
+export function isOrderGroupConversation(c) {
+ if (!c || c.type !== 'group') return false;
+ if (c.data && c.data.orderId) return true;
+ if (c.data && c.data.dashouGoEasyId) return true;
+ return isPairGroupId(c.groupId) || /^group_\w+$/.test(c.groupId || '');
+}
+
+export function getConversationSortTime(conversation, openTimes) {
+ const id = conversation.groupId || conversation.userId || '';
+ const msgTs = conversation.lastMessage?.timestamp || 0;
+ const openTs = (openTimes && id && openTimes[id]) ? openTimes[id] : 0;
+ return Math.max(msgTs, openTs);
+}
+
+export function sortConversations(list, openTimes) {
+ return list.sort((a, b) => {
+ if (a.top && !b.top) return -1;
+ if (!a.top && b.top) return 1;
+ if (a.unread > 0 && b.unread <= 0) return -1;
+ if (a.unread <= 0 && b.unread > 0) return 1;
+ const ta = getConversationSortTime(a, openTimes);
+ const tb = getConversationSortTime(b, openTimes);
+ return tb - ta;
+ });
+}
+
+export function recordConversationOpen(conversation) {
+ const id = conversation?.groupId || conversation?.userId;
+ if (!id) return;
+ try {
+ const times = wx.getStorageSync('conversationOpenTimes') || {};
+ times[id] = Date.now();
+ wx.setStorageSync('conversationOpenTimes', times);
+ } catch (e) {}
+}
diff --git a/utils/xiaoxilj.js b/utils/xiaoxilj.js
index 8f330cc..e4eda3f 100644
--- a/utils/xiaoxilj.js
+++ b/utils/xiaoxilj.js
@@ -1,218 +1,269 @@
-/**
- * 连接管理模块 - 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. 从会话列表查找群组;找不到则用订单号作为 groupId(首聊场景)
- const orderIdStr = String(orderId);
- let realGroupId = orderIdStr;
- try {
- const result = await new Promise((resolve, reject) => {
- wx.goEasy.im.latestConversations({
- onSuccess: (res) => {
- const conversations = res.content?.conversations || [];
- const found = conversations.find((c) => {
- if (c.type !== 'group') return false;
- if (c.groupId === orderIdStr) return true;
- const data = c.data || {};
- return String(data.orderId || '') === orderIdStr;
- });
- resolve(found ? found.groupId : orderIdStr);
- },
- onFailed: reject,
- });
- });
- realGroupId = result || orderIdStr;
- } catch (err) {
- console.warn('获取会话列表失败,使用订单号作为群ID', err);
- realGroupId = orderIdStr;
- }
-
- // 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: orderIdStr,
- groupName: groupName || '订单群聊',
- groupAvatar: groupAvatar || '',
- isCross: isCross || 0,
- currentUserId: userId,
- currentUserName: app.globalData.currentUser?.name || `用户${uid}`,
- currentUserAvatar: avatar,
- };
- const path = '/pages/group-chat/group-chat?data=' + encodeURIComponent(JSON.stringify(param));
- wx.navigateTo({ url: path });
- }
-}
-
-export default new ConnectionManager();
\ No newline at end of file
+/**
+ * 订单群聊跳转:先确定群 ID,IM 连接失败也尽量能进聊天页
+ */
+import request from './request';
+import { persistGroupMeta } from './im-user.js';
+import { resolveLocalGroupId } from './group-chat.js';
+
+const app = getApp();
+
+function formatError(err) {
+ if (!err) return '进入聊天失败';
+ if (typeof err === 'string') return err;
+ if (err.message) return err.message;
+ if (err.content) return String(err.content);
+ if (err.msg) return String(err.msg);
+ if (err.code) return `连接失败(${err.code})`;
+ try {
+ return JSON.stringify(err);
+ } catch (e) {
+ return '进入聊天失败';
+ }
+}
+
+function withTimeout(promise, ms, errMsg) {
+ return new Promise((resolve, reject) => {
+ const timer = setTimeout(() => reject(new Error(errMsg || '操作超时')), ms);
+ promise
+ .then((v) => { clearTimeout(timer); resolve(v); })
+ .catch((e) => { clearTimeout(timer); reject(e); });
+ });
+}
+
+function getConnectedUserId() {
+ const fromState = app.globalData?.goEasyConnection?.userId;
+ const fromIm = wx.goEasy?.im?.userId;
+ return fromState || fromIm || '';
+}
+
+function isImConnected() {
+ const status = wx.goEasy?.getConnectionStatus?.() || 'disconnected';
+ return status === 'connected' || status === 'reconnected';
+}
+
+async function ensureIdentityConnection(identityType, userId) {
+ app.globalData.currentRole = identityType;
+ wx.setStorageSync('currentRole', identityType);
+
+ const uid = userId.replace(/^(Ds|Sj|Boss|Gs|Zz|Kh)/, '');
+ const avatar = wx.getStorageSync('touxiang') ||
+ (app.globalData.ossImageUrl + app.globalData.morentouxiang);
+ app.globalData.currentUser = {
+ id: userId,
+ name: app.globalData.currentUser?.name || `用户${uid}`,
+ avatar: avatar,
+ };
+
+ if (!app.globalData.chatEnabled || !wx.goEasy?.im) {
+ throw new Error('聊天服务未就绪,请稍后重试');
+ }
+
+ if (isImConnected() && getConnectedUserId() === userId) {
+ return;
+ }
+
+ const connectedId = getConnectedUserId();
+ if (connectedId && connectedId !== userId && app.disconnectGoEasy) {
+ await app.disconnectGoEasy();
+ }
+
+ if (!app.connectWithIdentity) {
+ throw new Error('聊天功能未初始化');
+ }
+
+ await withTimeout(
+ app.connectWithIdentity(identityType, userId, true),
+ 12000,
+ 'IM连接超时'
+ );
+
+ if (!isImConnected()) {
+ throw new Error('IM连接失败,请检查网络后重试');
+ }
+}
+
+async function prepareGroupChat(identityType, userId, orderId, partnerUid, fadanPingtai, groupName, isCross) {
+ let chatData = null;
+
+ try {
+ const res = await withTimeout(
+ request({
+ url: '/dingdan/ltdhzb',
+ method: 'POST',
+ data: {
+ dingdan_id: orderId,
+ identityType,
+ push_order_card: false,
+ },
+ }),
+ 12000,
+ '准备群聊超时'
+ );
+ const body = res?.data || {};
+ if (body.code === 0 && body.data?.groupId) {
+ chatData = body.data;
+ } else if (body.msg) {
+ console.warn('ltdhzb:', body.msg);
+ }
+ } catch (e) {
+ console.warn('ltdhzb 请求失败,尝试本地 groupId', e);
+ }
+
+ if (!chatData?.groupId) {
+ const myUid = userId.replace(/^(Ds|Sj|Boss)/, '');
+ let localGroupId = resolveLocalGroupId(
+ identityType, myUid, partnerUid, fadanPingtai, orderId, isCross
+ );
+ if (!localGroupId && orderId) {
+ localGroupId = `group_${orderId}`;
+ }
+ if (!localGroupId) {
+ throw new Error('无法确定群聊,请确认订单已接单且对方信息完整');
+ }
+ chatData = {
+ groupId: localGroupId,
+ orderId,
+ groupName: groupName || '订单群聊',
+ isCross: isCross || fadanPingtai || 0,
+ };
+ try {
+ await request({
+ url: '/dingdan/ltdhzb',
+ method: 'POST',
+ data: { dingdan_id: orderId, identityType, push_order_card: false },
+ });
+ } catch (e) { /* ignore */ }
+ }
+
+ return chatData;
+}
+
+class ConnectionManager {
+ async connectAndChat(params) {
+ const { identityType, userId, targetUser } = params;
+ if (!identityType || !userId || !targetUser || !targetUser.id) {
+ throw new Error('参数不完整');
+ }
+
+ await ensureIdentityConnection(identityType, userId);
+
+ 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 };
+ wx.navigateTo({ url: '/pages/chat/chat?data=' + encodeURIComponent(JSON.stringify(param)) });
+ }
+
+ async connectToGroupChat(params) {
+ const {
+ identityType, userId, orderId, groupName, groupAvatar, isCross,
+ partnerUid, fadanPingtai,
+ } = params;
+
+ if (!identityType || !userId || !orderId) {
+ throw new Error('参数不完整');
+ }
+
+ app.globalData.currentRole = identityType;
+ wx.setStorageSync('currentRole', identityType);
+
+ wx.showLoading({ title: '建立联系中...', mask: true });
+
+ try {
+ const chatData = await prepareGroupChat(
+ identityType, userId, orderId, partnerUid, fadanPingtai || isCross, groupName, isCross
+ );
+
+ const realGroupId = chatData.groupId;
+ let avatar = chatData.counterpartAvatar || chatData.groupAvatar || groupAvatar || '';
+ if (avatar && !avatar.startsWith('http')) {
+ avatar = app.globalData.ossImageUrl + avatar.replace(/^\//, '');
+ }
+
+ if (!app.globalData.groupInfoMap) app.globalData.groupInfoMap = {};
+ const meta = {
+ name: chatData.counterpartName || chatData.groupName || groupName,
+ avatar,
+ orderId: chatData.orderId || orderId,
+ orderDesc: chatData.orderDesc || '',
+ orderZhuangtai: chatData.orderZhuangtai,
+ dashouGoEasyId: chatData.dashouGoEasyId,
+ partnerGoEasyId: chatData.partnerGoEasyId,
+ dashouYonghuid: chatData.dashouYonghuid,
+ partnerYonghuid: chatData.partnerYonghuid,
+ dashouName: chatData.dashouName,
+ partnerName: chatData.partnerName,
+ dashouAvatar: chatData.dashouAvatar,
+ partnerAvatar: chatData.partnerAvatar,
+ counterpartId: chatData.counterpartId,
+ counterpartYonghuid: chatData.counterpartYonghuid,
+ };
+ app.globalData.groupInfoMap[realGroupId] = meta;
+ persistGroupMeta(app, realGroupId, meta);
+
+ let imReady = false;
+ try {
+ await ensureIdentityConnection(identityType, userId);
+ imReady = true;
+ try {
+ await withTimeout(
+ new Promise((resolve, reject) => {
+ wx.goEasy.im.subscribeGroup({
+ groupIds: [realGroupId],
+ onSuccess: () => resolve(),
+ onFailed: (error) => reject(error),
+ });
+ }),
+ 8000,
+ '订阅群聊超时'
+ );
+ } catch (subErr) {
+ console.warn('订阅群聊失败,仍尝试进入页面', subErr);
+ }
+ } catch (imErr) {
+ console.warn('IM连接失败,仍进入聊天页由页面重连', imErr);
+ }
+
+ const uid = userId.replace(/^(Ds|Sj|Boss)/, '');
+ const param = {
+ groupId: realGroupId,
+ orderId: chatData.orderId || orderId,
+ groupName: chatData.counterpartName || chatData.groupName || groupName || '订单群聊',
+ groupAvatar: avatar,
+ isCross: chatData.isCross != null ? chatData.isCross : (isCross || 0),
+ currentUserId: userId,
+ currentUserName: app.globalData.currentUser?.name || `用户${uid}`,
+ currentUserAvatar: app.globalData.currentUser?.avatar ||
+ (wx.getStorageSync('touxiang') || (app.globalData.ossImageUrl + app.globalData.morentouxiang)),
+ orderZhuangtai: chatData.orderZhuangtai,
+ orderJine: chatData.orderJine,
+ orderDesc: chatData.orderDesc || '',
+ imReady,
+ };
+
+ wx.hideLoading();
+ wx.navigateTo({
+ url: '/pages/group-chat/group-chat?data=' + encodeURIComponent(JSON.stringify(param)),
+ fail: (navErr) => {
+ wx.showToast({ title: formatError(navErr), icon: 'none', duration: 2500 });
+ },
+ });
+ } catch (err) {
+ wx.hideLoading();
+ console.error('跳转群聊失败:', err);
+ wx.showToast({ title: formatError(err), icon: 'none', duration: 2500 });
+ throw err;
+ }
+ }
+}
+
+export default new ConnectionManager();