chore: 抢单端UI回退前备份当前完整工作区
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
106
utils/chat-history.js
Normal file
106
utils/chat-history.js
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* 聊天历史消息加载(私聊 / 群聊 / 客服共用)
|
||||
* - 排除本地/欢迎语等合成消息的 timestamp,避免下拉加载错位
|
||||
* - 统一 IM 连接等待与 history API 封装
|
||||
*/
|
||||
|
||||
const SYNTHETIC_MSG_ID = /^(local-|img-|order-|welcome-)/;
|
||||
|
||||
export function isHistorySyntheticMessage(msg) {
|
||||
return !!(msg && msg.messageId && SYNTHETIC_MSG_ID.test(String(msg.messageId)));
|
||||
}
|
||||
|
||||
/** 取当前列表里最早一条「真实」服务端消息的时间戳,供 GoEasy history 分页 */
|
||||
export function getOldestHistoryTimestamp(messages, fallback = null) {
|
||||
if (!Array.isArray(messages)) return fallback;
|
||||
let oldest = null;
|
||||
for (const m of messages) {
|
||||
if (!m || m.timestamp == null) continue;
|
||||
if (isHistorySyntheticMessage(m)) continue;
|
||||
if (oldest == null || m.timestamp < oldest) oldest = m.timestamp;
|
||||
}
|
||||
return oldest != null ? oldest : fallback;
|
||||
}
|
||||
|
||||
export function isImConnected() {
|
||||
const status = wx.goEasy?.getConnectionStatus?.() || 'disconnected';
|
||||
return (status === 'connected' || status === 'reconnected') && !!wx.goEasy?.im;
|
||||
}
|
||||
|
||||
/** 等待 IM 连接就绪(下拉加载历史前必须先连上) */
|
||||
export function waitImConnected(app, timeoutMs = 8000) {
|
||||
return new Promise((resolve) => {
|
||||
if (isImConnected()) {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
if (app?.ensureConnection) app.ensureConnection();
|
||||
const start = Date.now();
|
||||
const timer = setInterval(() => {
|
||||
if (isImConnected()) {
|
||||
clearInterval(timer);
|
||||
resolve(true);
|
||||
} else if (Date.now() - start >= timeoutMs) {
|
||||
clearInterval(timer);
|
||||
resolve(false);
|
||||
}
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchHistoryMessages({ scene, id, lastTimestamp, limit = 20 }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!wx.goEasy?.im?.history) {
|
||||
reject(new Error('IM未初始化'));
|
||||
return;
|
||||
}
|
||||
if (!id) {
|
||||
reject(new Error('会话ID为空'));
|
||||
return;
|
||||
}
|
||||
wx.goEasy.im.history({
|
||||
type: scene,
|
||||
id: String(id),
|
||||
lastTimestamp: lastTimestamp ?? null,
|
||||
limit,
|
||||
onSuccess: (res) => resolve(res?.content || []),
|
||||
onFailed: (err) => reject(err || new Error('加载历史消息失败')),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并一页历史消息
|
||||
* @returns {{ messages, hasMore, lastTimestamp }}
|
||||
*/
|
||||
export function mergeHistoryMessages({ incoming, existing, refresh, limit = 20, mapMessage }) {
|
||||
let list = Array.isArray(incoming) ? [...incoming] : [];
|
||||
list.sort((a, b) => a.timestamp - b.timestamp);
|
||||
if (typeof mapMessage === 'function') {
|
||||
list = list.map((m, idx) => mapMessage(m, idx, list));
|
||||
}
|
||||
|
||||
const prev = Array.isArray(existing) ? existing : [];
|
||||
let final = refresh ? list : [...list, ...prev];
|
||||
|
||||
if (!refresh) {
|
||||
for (let i = 0; i < final.length; i += 1) {
|
||||
if (i === 0) final[i].showTime = true;
|
||||
else final[i].showTime = (final[i].timestamp - final[i - 1].timestamp) / 60000 > 5;
|
||||
}
|
||||
}
|
||||
|
||||
const ids = new Set();
|
||||
const unique = [];
|
||||
for (const m of final) {
|
||||
if (!m?.messageId || ids.has(m.messageId)) continue;
|
||||
ids.add(m.messageId);
|
||||
unique.push(m);
|
||||
}
|
||||
|
||||
return {
|
||||
messages: unique,
|
||||
hasMore: list.length >= limit,
|
||||
lastTimestamp: getOldestHistoryTimestamp(unique, null),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user