重构聊天消息处理和分页逻辑。为消息记录参数引入一个新的接口,以增强类型安全性。更新获取聊天消息和聊天室消息的API调用,以支持分页。通过删除不必要的刷新触发器和优化会话状态管理来优化MessageList组件。改进加载状态处理,并确保组件之间消息加载行为的一致性。

This commit is contained in:
超级老白兔
2025-11-14 18:23:54 +08:00
parent 8d5869e6c2
commit 4684e880b1
9 changed files with 448 additions and 212 deletions

View File

@@ -35,26 +35,38 @@ export function updateConfig(params) {
return request2("/api/WechatFriend/updateConfig", params, "PUT");
}
//获取聊天记录-2 获取列表
export function getChatMessages(params: {
wechatAccountId: number;
wechatFriendId?: number;
wechatChatroomId?: number;
From: number;
To: number;
Count: number;
olderData: boolean;
}) {
export interface messreocrParams {
From?: number | string;
To?: number | string;
/**
* 当前页码,从 1 开始
*/
page?: number;
/**
* 每页条数
*/
limit?: number;
/**
* 群id
*/
wechatChatroomId?: number | string;
/**
* 好友id
*/
wechatFriendId?: number | string;
/**
* 微信账号ID
*/
wechatAccountId?: number | string;
/**
* 关键词、类型等扩展参数
*/
[property: string]: any;
}
export function getChatMessages(params: messreocrParams) {
return request("/v1/kefu/message/details", params, "GET");
}
export function getChatroomMessages(params: {
wechatAccountId: number;
wechatFriendId?: number;
wechatChatroomId?: number;
From: number;
To: number;
Count: number;
olderData: boolean;
}) {
export function getChatroomMessages(params: messreocrParams) {
return request("/v1/kefu/message/details", params, "GET");
}
//=====================旧==============================

View File

@@ -9,7 +9,6 @@ import {
import { useCurrentContact } from "@/store/module/weChat/weChat";
import { ContactManager } from "@/utils/dbAction/contact";
import { MessageManager } from "@/utils/dbAction/message";
import { triggerRefresh } from "@/store/module/weChat/message";
import { useUserStore } from "@/store/module/user";
import { useWeChatStore } from "@/store/module/weChat/weChat";
const { TextArea } = Input;
@@ -110,10 +109,7 @@ const ToContract: React.FC<ToContractProps> = ({
await ContactManager.deleteContact(currentContact.id);
console.log("✅ 已从联系人数据库删除");
// 3. 触发会话列表刷新
triggerRefresh();
// 4. 清空当前选中的联系人(关闭聊天窗口)
// 3. 清空当前选中的联系人(关闭聊天窗口)
clearCurrentContact();
message.success("转接成功,已清理本地数据");
@@ -167,10 +163,7 @@ const ToContract: React.FC<ToContractProps> = ({
await ContactManager.deleteContact(currentContact.id);
console.log("✅ 已从联系人数据库删除");
// 3. 触发会话列表刷新
triggerRefresh();
// 4. 清空当前选中的联系人(关闭聊天窗口)
// 3. 清空当前选中的联系人(关闭聊天窗口)
clearCurrentContact();
message.success("转回成功,已清理本地数据");

View File

@@ -145,6 +145,9 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
const [selectedRecords, setSelectedRecords] = useState<ChatRecord[]>([]);
const currentMessages = useWeChatStore(state => state.currentMessages);
const currentMessagesHasMore = useWeChatStore(
state => state.currentMessagesHasMore,
);
const loadChatMessages = useWeChatStore(state => state.loadChatMessages);
const messagesLoading = useWeChatStore(state => state.messagesLoading);
@@ -552,8 +555,14 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
};
// 用于分组消息并添加时间戳的辅助函数
const groupMessagesByTime = (messages: ChatRecord[]) => {
return messages
const groupMessagesByTime = (messages: ChatRecord[] | null | undefined) => {
const safeMessages = Array.isArray(messages)
? messages
: Array.isArray((messages as any)?.list)
? ((messages as any).list as ChatRecord[])
: [];
return safeMessages
.filter(msg => msg !== null && msg !== undefined) // 过滤掉null和undefined的消息
.map(msg => ({
time: formatWechatTime(String(msg?.wechatTime)),
@@ -680,33 +689,10 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
);
};
const loadMoreMessages = () => {
// 兼容性处理:检查消息数组和时间戳
if (!currentMessages || currentMessages.length === 0) {
console.warn("No messages available for loading more");
if (messagesLoading || !currentMessagesHasMore) {
return;
}
const firstMessage = currentMessages[0];
if (!firstMessage || !firstMessage.createTime) {
console.warn("Invalid message or createTime");
return;
}
// 兼容性处理:确保时间戳格式正确
let timestamp;
try {
const date = new Date(firstMessage.createTime);
if (isNaN(date.getTime())) {
console.warn("Invalid createTime format:", firstMessage.createTime);
return;
}
timestamp = date.getTime() - 24 * 36000 * 1000;
} catch (error) {
console.error("Error parsing createTime:", error);
return;
}
loadChatMessages(false, timestamp);
loadChatMessages(false);
};
const handleForwardMessage = (messageData: ChatRecord) => {
@@ -785,8 +771,17 @@ const MessageRecord: React.FC<MessageRecordProps> = ({ contract }) => {
return (
<div className={styles.messagesContainer}>
<div className={styles.loadMore} onClick={() => loadMoreMessages()}>
{messagesLoading ? <LoadingOutlined /> : ""}
<div
className={styles.loadMore}
onClick={() => loadMoreMessages()}
style={{
cursor:
currentMessagesHasMore && !messagesLoading ? "pointer" : "default",
opacity: currentMessagesHasMore ? 1 : 0.6,
}}
>
{currentMessagesHasMore ? "点击加载更早的信息" : "已经没有更早的消息了"}
{messagesLoading ? <LoadingOutlined /> : ""}
</div>
{groupMessagesByTime(currentMessages).map((group, groupIndex) => (
<React.Fragment key={`group-${groupIndex}`}>

View File

@@ -15,7 +15,7 @@ import {
getWechatFriendDetail,
getWechatChatroomDetail,
} from "./api";
import { useMessageStore, triggerRefresh } from "@weChatStore/message";
import { useMessageStore } from "@weChatStore/message";
import { useWebSocketStore } from "@storeModule/websocket/websocket";
import { useCustomerStore } from "@weChatStore/customer";
import { useContactStore } from "@weChatStore/contacts";
@@ -39,14 +39,12 @@ const MessageList: React.FC<MessageListProps> = () => {
// Store状态
const {
loading,
refreshTrigger,
hasLoadedOnce,
setLoading,
setHasLoadedOnce,
sessions,
setSessions: setSessionState,
} = useMessageStore();
// 组件内部状态:会话列表数据
const [sessions, setSessions] = useState<ChatSession[]>([]);
const [filteredSessions, setFilteredSessions] = useState<ChatSession[]>([]);
// 右键菜单相关状态
@@ -74,6 +72,8 @@ const MessageList: React.FC<MessageListProps> = () => {
});
const contextMenuRef = useRef<HTMLDivElement>(null);
const previousUserIdRef = useRef<number | null>(null);
const loadRequestRef = useRef(0);
// 右键菜单事件处理
const handleContextMenu = (e: React.MouseEvent, session: ChatSession) => {
@@ -105,7 +105,7 @@ const MessageList: React.FC<MessageListProps> = () => {
try {
// 1. 立即更新UI并重新排序乐观更新
setSessions(prev => {
setSessionState(prev => {
const updatedSessions = prev.map(s =>
s.id === session.id
? {
@@ -141,7 +141,7 @@ const MessageList: React.FC<MessageListProps> = () => {
message.success(`${newPinned === 1 ? "置顶" : "取消置顶"}成功`);
} catch (error) {
// 4. 失败时回滚UI
setSessions(prev =>
setSessionState(prev =>
prev.map(s =>
s.id === session.id
? { ...s, config: { ...s.config, top: currentPinned } }
@@ -162,7 +162,7 @@ const MessageList: React.FC<MessageListProps> = () => {
onOk: async () => {
try {
// 1. 立即从UI移除
setSessions(prev => prev.filter(s => s.id !== session.id));
setSessionState(prev => prev.filter(s => s.id !== session.id));
// 2. 后台调用API
await updateConfig({
@@ -180,7 +180,7 @@ const MessageList: React.FC<MessageListProps> = () => {
message.success("删除成功");
} catch (error) {
// 4. 失败时恢复UI
setSessions(prev => [...prev, session]);
setSessionState(prev => [...prev, session]);
message.error("删除失败");
}
@@ -212,7 +212,7 @@ const MessageList: React.FC<MessageListProps> = () => {
try {
// 1. 立即更新UI
setSessions(prev =>
setSessionState(prev =>
prev.map(s =>
s.id === session.id ? { ...s, conRemark: editRemarkModal.remark } : s,
),
@@ -258,7 +258,7 @@ const MessageList: React.FC<MessageListProps> = () => {
message.success("备注更新成功");
} catch (error) {
// 4. 失败时回滚UI
setSessions(prev =>
setSessionState(prev =>
prev.map(s =>
s.id === session.id ? { ...s, conRemark: oldRemark } : s,
),
@@ -357,112 +357,93 @@ const MessageList: React.FC<MessageListProps> = () => {
`会话同步完成: 新增${syncResult.added}, 更新${syncResult.updated}, 删除${syncResult.deleted}`,
);
// 如果有数据变更触发UI刷新
if (
syncResult.added > 0 ||
syncResult.updated > 0 ||
syncResult.deleted > 0
) {
triggerRefresh();
}
// 会话管理器会在有变更触发订阅回调
} catch (error) {
console.error("同步服务器数据失败:", error);
}
};
// 切换账号时重置加载状态
useEffect(() => {
if (!currentUserId) return;
if (previousUserIdRef.current === currentUserId) return;
previousUserIdRef.current = currentUserId;
setHasLoadedOnce(false);
setSessionState([]);
}, [currentUserId, setHasLoadedOnce, setSessionState]);
// 初始化加载会话列表
useEffect(() => {
if (!currentUserId || currentUserId === 0) {
console.warn("currentUserId 无效,跳过加载:", currentUserId);
return;
}
let isCancelled = false;
const requestId = ++loadRequestRef.current;
const initializeSessions = async () => {
if (!currentUserId || currentUserId === 0) {
console.warn("currentUserId 无效,跳过加载:", currentUserId);
return;
}
// 如果已经加载过一次,只从本地数据库读取,不请求接口
if (hasLoadedOnce) {
console.log("已加载过,只从本地数据库读取");
setLoading(true); // 显示骨架屏
try {
const cachedSessions =
await MessageManager.getUserSessions(currentUserId);
console.log("从本地加载会话数:", cachedSessions.length);
// 如果本地数据为空,重置 hasLoadedOnce 并重新加载
if (cachedSessions.length === 0) {
console.warn("本地数据为空,重置加载状态并重新加载");
setHasLoadedOnce(false);
// 不 return继续执行下面的首次加载逻辑
} else {
setSessions(cachedSessions);
setLoading(false); // 数据加载完成,关闭骨架屏
return;
}
} catch (error) {
console.error("从本地加载会话列表失败:", error);
setLoading(false);
return;
}
}
console.log("首次加载,开始初始化...");
setLoading(true);
try {
// 1. 优先从本地数据库加载
const cachedSessions =
await MessageManager.getUserSessions(currentUserId);
console.log("本地缓存会话数:", cachedSessions.length);
if (isCancelled || loadRequestRef.current !== requestId) {
return;
}
if (cachedSessions.length > 0) {
// 有缓存数据,立即显示
console.log("有缓存数据,立即显示");
setSessions(cachedSessions);
setLoading(false);
setSessionState(cachedSessions);
}
// 2. 后台静默同步(不显示同步提示)
console.log("后台静默同步中...");
const needsFullSync = cachedSessions.length === 0 || !hasLoadedOnce;
if (needsFullSync) {
await syncWithServer();
setHasLoadedOnce(true); // 标记已加载过
console.log("同步完成");
if (isCancelled || loadRequestRef.current !== requestId) {
return;
}
setHasLoadedOnce(true);
} else {
// 无缓存直接API加载
console.log("无缓存,从服务器加载...");
await syncWithServer();
const newSessions =
await MessageManager.getUserSessions(currentUserId);
console.log("从服务器加载会话数:", newSessions.length);
setSessions(newSessions);
setLoading(false);
setHasLoadedOnce(true); // 标记已加载过
syncWithServer().catch(error => {
console.error("后台同步失败:", error);
});
}
} catch (error) {
console.error("初始化会话列表失败:", error);
setLoading(false);
if (!isCancelled) {
console.error("初始化会话列表失败:", error);
}
} finally {
if (!isCancelled && loadRequestRef.current === requestId) {
setLoading(false);
}
}
};
initializeSessions();
return () => {
isCancelled = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentUserId]);
// 监听refreshTrigger重新查询数据库
// 订阅数据库变更自动更新Store
useEffect(() => {
const refreshSessions = async () => {
if (!currentUserId || refreshTrigger === 0) return;
if (!currentUserId) {
return;
}
try {
const updatedSessions =
await MessageManager.getUserSessions(currentUserId);
setSessions(updatedSessions);
} catch (error) {
console.error("刷新会话列表失败:", error);
}
};
const unsubscribe = MessageManager.onSessionsUpdate(
({ userId: ownerId, sessions: updatedSessions }) => {
if (ownerId !== currentUserId) return;
setSessionState(updatedSessions);
},
);
refreshSessions();
}, [refreshTrigger, currentUserId]);
return unsubscribe;
}, [currentUserId, setSessionState]);
// 根据客服和搜索关键词筛选会话
useEffect(() => {
@@ -689,8 +670,7 @@ const MessageList: React.FC<MessageListProps> = () => {
}
}
// 触发静默刷新:通知组件从数据库重新查询
triggerRefresh();
// MessageManager 的回调会自动把最新数据发给 Store
};
window.addEventListener(
@@ -718,7 +698,7 @@ const MessageList: React.FC<MessageListProps> = () => {
// 标记为已读(不更新时间和排序)
if (session.config.unreadCount > 0) {
// 立即更新UI只更新未读数量
setSessions(prev =>
setSessionState(prev =>
prev.map(s =>
s.id === session.id
? { ...s, config: { ...s.config, unreadCount: 0 } }

View File

@@ -1,3 +1,5 @@
import { ChatSession } from "@/utils/db";
export interface Message {
id: number;
wechatId: string;
@@ -26,13 +28,15 @@ export interface Message {
}
//Store State - 会话列表状态管理(不存储数据,只管理状态)
export type SessionsUpdater =
| ChatSession[]
| ((previous: ChatSession[]) => ChatSession[]);
export interface MessageState {
//加载状态
loading: boolean;
//后台同步状态
refreshing: boolean;
//刷新触发器(用于通知组件重新查询数据库)
refreshTrigger: number;
//最后刷新时间
lastRefreshTime: string | null;
//是否已经加载过一次(避免重复请求)
@@ -42,8 +46,6 @@ export interface MessageState {
setLoading: (loading: boolean) => void;
//设置同步状态
setRefreshing: (refreshing: boolean) => void;
//触发刷新(通知组件重新查询)
triggerRefresh: () => void;
//设置已加载标识
setHasLoadedOnce: (loaded: boolean) => void;
//重置加载状态(用于登出或切换用户)
@@ -60,4 +62,16 @@ export interface MessageState {
updateMessageStatus: (messageId: number, status: string) => void;
//更新当前选中的消息(废弃,保留兼容)
updateCurrentMessage: (message: Message) => void;
// ==================== 新的会话数据接口 ====================
// 当前会话列表
sessions: ChatSession[];
// 设置或更新会话列表(支持回调写法)
setSessions: (updater: SessionsUpdater) => void;
// 新增或替换某个会话
upsertSession: (session: ChatSession) => void;
// 按 ID 和类型移除会话
removeSessionById: (sessionId: number, type: ChatSession["type"]) => void;
// 清空所有会话(登出/切账号使用)
clearSessions: () => void;
}

View File

@@ -1,6 +1,43 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { Message, MessageState } from "./message.data";
import { ChatSession } from "@/utils/db";
import { Message, MessageState, SessionsUpdater } from "./message.data";
const computeSortKey = (session: ChatSession) => {
const isTop = session.config?.top ? 1 : 0;
const timestamp = new Date(session.lastUpdateTime || new Date()).getTime();
const displayName = (
session.conRemark ||
session.nickname ||
(session as any).wechatId ||
""
).toLowerCase();
return `${isTop}|${timestamp}|${displayName}`;
};
const normalizeSessions = (sessions: ChatSession[]) => {
if (!Array.isArray(sessions)) {
return [];
}
return [...sessions]
.map(session => ({
...session,
sortKey: computeSortKey(session),
}))
.sort((a, b) => b.sortKey.localeCompare(a.sortKey));
};
const resolveUpdater = (
updater: SessionsUpdater,
previous: ChatSession[],
): ChatSession[] => {
if (typeof updater === "function") {
return updater(previous);
}
return updater;
};
/**
* 会话列表状态管理Store
@@ -13,24 +50,18 @@ export const useMessageStore = create<MessageState>()(
// ==================== 新增状态管理 ====================
loading: false,
refreshing: false,
refreshTrigger: 0,
lastRefreshTime: null,
hasLoadedOnce: false,
setLoading: (loading: boolean) => set({ loading }),
setRefreshing: (refreshing: boolean) => set({ refreshing }),
triggerRefresh: () =>
set({
refreshTrigger: get().refreshTrigger + 1,
lastRefreshTime: new Date().toISOString(),
}),
setHasLoadedOnce: (loaded: boolean) => set({ hasLoadedOnce: loaded }),
resetLoadState: () =>
set({
hasLoadedOnce: false,
loading: false,
refreshing: false,
refreshTrigger: 0,
sessions: [],
}),
// ==================== 保留原有接口(向后兼容) ====================
@@ -45,6 +76,45 @@ export const useMessageStore = create<MessageState>()(
message.id === messageId ? { ...message, status } : message,
),
}),
// ==================== 会话数据接口 ====================
sessions: [],
setSessions: (updater: SessionsUpdater) =>
set(state => ({
sessions: normalizeSessions(resolveUpdater(updater, state.sessions)),
lastRefreshTime: new Date().toISOString(),
})),
upsertSession: (session: ChatSession) =>
set(state => {
const next = [...state.sessions];
const index = next.findIndex(
s => s.id === session.id && s.type === session.type,
);
if (index > -1) {
next[index] = session;
} else {
next.push(session);
}
return {
sessions: normalizeSessions(next),
lastRefreshTime: new Date().toISOString(),
};
}),
removeSessionById: (sessionId: number, type: ChatSession["type"]) =>
set(state => ({
sessions: normalizeSessions(
state.sessions.filter(
s => !(s.id === sessionId && s.type === type),
),
),
lastRefreshTime: new Date().toISOString(),
})),
clearSessions: () =>
set({
sessions: [],
lastRefreshTime: new Date().toISOString(),
}),
}),
{
name: "message-storage",
@@ -105,11 +175,6 @@ export const setLoading = (loading: boolean) =>
export const setRefreshing = (refreshing: boolean) =>
useMessageStore.getState().setRefreshing(refreshing);
/**
* 触发刷新(通知组件重新查询数据库)
*/
export const triggerRefresh = () => useMessageStore.getState().triggerRefresh();
/**
* 设置已加载标识
* @param loaded 是否已加载

View File

@@ -40,6 +40,12 @@ export interface WeChatState {
// ==================== 聊天消息管理 ====================
/** 当前聊天的消息列表 */
currentMessages: ChatRecord[];
/** 当前聊天记录分页页码 */
currentMessagesPage: number;
/** 单页消息条数 */
currentMessagesPageSize: number;
/** 是否还有更多历史消息 */
currentMessagesHasMore: boolean;
/** 添加新消息 */
addMessage: (message: ChatRecord) => void;
/** 更新指定消息 */
@@ -83,7 +89,7 @@ export interface WeChatState {
// ==================== 消息加载方法 ====================
/** 加载聊天消息 */
loadChatMessages: (Init: boolean, To?: number) => Promise<void>;
loadChatMessages: (Init: boolean, pageOverride?: number) => Promise<void>;
/** 搜索消息 */
SearchMessage: (params: {
From: number;

View File

@@ -28,6 +28,7 @@ let pendingMessages: ChatRecord[] = []; // 待处理的消息队列
let currentAiGenerationId: string | null = null; // 当前AI生成的唯一ID
const AI_REQUEST_DELAY = 3000; // 3秒延迟
const FILE_MESSAGE_TYPE = "file";
const DEFAULT_MESSAGE_PAGE_SIZE = 20;
type FileMessagePayload = {
type?: string;
@@ -120,6 +121,108 @@ const isFileLikeMessage = (msg: ChatRecord): boolean => {
return false;
};
const normalizeMessages = (source: any): ChatRecord[] => {
if (Array.isArray(source)) {
return source;
}
if (Array.isArray(source?.list)) {
return source.list;
}
return [];
};
const parseTimeValue = (value: unknown): number => {
if (value === null || value === undefined) {
return 0;
}
if (typeof value === "number") {
return value;
}
if (typeof value === "string") {
const numeric = Number(value);
if (!Number.isNaN(numeric)) {
return numeric;
}
const parsed = Date.parse(value);
if (!Number.isNaN(parsed)) {
return parsed;
}
}
if (value instanceof Date) {
return value.getTime();
}
return 0;
};
const getMessageTimestamp = (msg: ChatRecord): number => {
const candidates = [
(msg as any)?.wechatTime,
(msg as any)?.createTime,
(msg as any)?.msgTime,
(msg as any)?.timestamp,
(msg as any)?.time,
];
for (const candidate of candidates) {
const parsed = parseTimeValue(candidate);
if (parsed) {
return parsed;
}
}
return typeof msg.id === "number" ? msg.id : 0;
};
const sortMessagesByTime = (messages: ChatRecord[]): ChatRecord[] => {
return [...messages].sort(
(a, b) => getMessageTimestamp(a) - getMessageTimestamp(b),
);
};
const resolvePaginationState = (
source: any,
requestedPage: number,
requestedLimit: number,
listLength: number,
) => {
const page =
typeof source?.page === "number"
? source.page
: typeof source?.current === "number"
? source.current
: requestedPage;
const limit =
typeof source?.limit === "number"
? source.limit
: typeof source?.pageSize === "number"
? source.pageSize
: requestedLimit;
let hasMore: boolean;
if (typeof source?.hasNext === "boolean") {
hasMore = source.hasNext;
} else if (typeof source?.hasNextPage === "boolean") {
hasMore = source.hasNextPage;
} else if (typeof source?.pages === "number") {
hasMore = page < source.pages;
} else if (typeof source?.total === "number" && limit > 0) {
hasMore = page * limit < source.total;
} else {
hasMore = listLength >= limit && listLength > 0;
}
if (listLength === 0) {
hasMore = false;
}
return {
page,
limit: limit || requestedLimit || DEFAULT_MESSAGE_PAGE_SIZE,
hasMore,
};
};
const normalizeFilePayload = (
payload: FileMessagePayload | null | undefined,
msg: ChatRecord,
@@ -348,6 +451,10 @@ export const useWeChatStore = create<WeChatState>()(
currentContract: null,
/** 当前聊天的消息列表 */
currentMessages: [],
/** 当前消息分页信息 */
currentMessagesPage: 1,
currentMessagesPageSize: DEFAULT_MESSAGE_PAGE_SIZE,
currentMessagesHasMore: true,
// ==================== 聊天消息管理方法 ====================
/** 添加新消息到当前聊天 */
@@ -429,7 +536,13 @@ export const useWeChatStore = create<WeChatState>()(
aiRequestTimer = null;
}
pendingMessages = [];
set({ currentContract: null, currentMessages: [] });
set({
currentContract: null,
currentMessages: [],
currentMessagesPage: 1,
currentMessagesHasMore: true,
currentMessagesPageSize: DEFAULT_MESSAGE_PAGE_SIZE,
});
},
/** 设置当前联系人并加载相关数据 */
setCurrentContact: (contract: ContractData | weChatGroup) => {
@@ -443,7 +556,13 @@ export const useWeChatStore = create<WeChatState>()(
const state = useWeChatStore.getState();
// 切换联系人时清空当前消息,等待重新加载
set({ currentMessages: [], isLoadingAiChat: false });
set({
currentMessages: [],
currentMessagesPage: 1,
currentMessagesHasMore: true,
currentMessagesPageSize: DEFAULT_MESSAGE_PAGE_SIZE,
isLoadingAiChat: false,
});
const params: any = {};
@@ -468,62 +587,91 @@ export const useWeChatStore = create<WeChatState>()(
id: contract.id,
config: { chat: true },
});
state.loadChatMessages(true, 4704624000000);
state.loadChatMessages(true);
},
// ==================== 消息加载方法 ====================
/** 加载聊天消息 */
loadChatMessages: async (Init: boolean, To?: number) => {
loadChatMessages: async (Init: boolean, pageOverride?: number) => {
const state = useWeChatStore.getState();
const contact = state.currentContract;
set({ messagesLoading: true });
set({ isLoadingData: Init });
if (!contact) {
return;
}
if (!Init && !state.currentMessagesHasMore) {
return;
}
const nextPage = Init
? 1
: (pageOverride ?? state.currentMessagesPage + 1);
const limit =
state.currentMessagesPageSize || DEFAULT_MESSAGE_PAGE_SIZE;
if (state.messagesLoading && !Init) {
return;
}
set({
messagesLoading: true,
isLoadingData: Init,
});
try {
const params: any = {
wechatAccountId: contact.wechatAccountId,
From: 1,
To: To || +new Date(),
Count: 20,
olderData: true,
page: nextPage,
limit,
};
if ("chatroomId" in contact && contact.chatroomId) {
// 群聊消息加载
const isGroup =
"chatroomId" in contact && Boolean(contact.chatroomId);
if (isGroup) {
params.wechatChatroomId = contact.id;
const messages = await getChatroomMessages(params);
const currentGroupMembers = await getGroupMembers({
} else {
params.wechatFriendId = contact.id;
}
const response = isGroup
? await getChatroomMessages(params)
: await getChatMessages(params);
const normalizedMessages = normalizeMessages(response);
const sortedMessages = sortMessagesByTime(normalizedMessages);
const paginationMeta = resolvePaginationState(
response,
nextPage,
limit,
sortedMessages.length,
);
let nextGroupMembers = state.currentGroupMembers;
if (Init && isGroup) {
nextGroupMembers = await getGroupMembers({
id: contact.id,
});
if (Init) {
set({ currentMessages: messages || [], currentGroupMembers });
} else {
set({
currentMessages: [
...(messages.list || []),
...state.currentMessages,
],
});
}
} else {
// 私聊消息加载
params.wechatFriendId = contact.id;
const messages = await getChatMessages(params);
if (Init) {
set({ currentMessages: messages || [] });
} else {
set({
currentMessages: [
...(messages.list || []),
...state.currentMessages,
],
});
}
}
set({ messagesLoading: false });
set(current => ({
currentMessages: Init
? sortedMessages
: [...sortedMessages, ...current.currentMessages],
currentGroupMembers:
Init && isGroup ? nextGroupMembers : current.currentGroupMembers,
currentMessagesPage: paginationMeta.page,
currentMessagesPageSize: paginationMeta.limit,
currentMessagesHasMore: paginationMeta.hasMore,
}));
} catch (error) {
console.error("获取聊天消息失败:", error);
} finally {
set({ messagesLoading: false });
set({
messagesLoading: false,
isLoadingData: false,
});
}
},
@@ -546,11 +694,11 @@ export const useWeChatStore = create<WeChatState>()(
try {
const params: any = {
wechatAccountId: contact.wechatAccountId,
keyword,
From,
To,
keyword,
Count,
olderData: true,
page: 1,
limit: Count,
};
if ("chatroomId" in contact && contact.chatroomId) {
@@ -560,12 +708,23 @@ export const useWeChatStore = create<WeChatState>()(
const currentGroupMembers = await getGroupMembers({
id: contact.id,
});
set({ currentMessages: messages || [], currentGroupMembers });
set({
currentMessages: sortMessagesByTime(normalizeMessages(messages)),
currentGroupMembers,
currentMessagesPage: 1,
currentMessagesHasMore: false,
currentMessagesPageSize: Count || state.currentMessagesPageSize,
});
} else {
// 私聊消息搜索
params.wechatFriendId = contact.id;
const messages = await getChatMessages(params);
set({ currentMessages: messages || [] });
set({
currentMessages: sortMessagesByTime(normalizeMessages(messages)),
currentMessagesPage: 1,
currentMessagesHasMore: false,
currentMessagesPageSize: Count || state.currentMessagesPageSize,
});
}
set({ messagesLoading: false });
} catch (error) {
@@ -831,6 +990,9 @@ export const useWeChatStore = create<WeChatState>()(
set({
currentContract: null,
currentMessages: [],
currentMessagesPage: 1,
currentMessagesHasMore: true,
currentMessagesPageSize: DEFAULT_MESSAGE_PAGE_SIZE,
messagesLoading: false,
});
},

View File

@@ -25,8 +25,15 @@ const serializeExtendFields = (value: any) => {
return "{}";
};
interface SessionUpdatePayload {
userId: number;
sessions: ChatSession[];
}
export class MessageManager {
private static updateCallbacks = new Set<(sessions: ChatSession[]) => void>();
private static updateCallbacks = new Set<
(payload: SessionUpdatePayload) => void
>();
// ==================== 回调管理 ====================
@@ -35,9 +42,11 @@ export class MessageManager {
* @param callback 回调函数
* @returns 取消注册的函数
*/
static onSessionsUpdate(callback: (sessions: ChatSession[]) => void) {
static onSessionsUpdate(callback: (payload: SessionUpdatePayload) => void) {
this.updateCallbacks.add(callback);
return () => this.updateCallbacks.delete(callback);
return () => {
this.updateCallbacks.delete(callback);
};
}
/**
@@ -49,7 +58,7 @@ export class MessageManager {
const sessions = await this.getUserSessions(userId);
this.updateCallbacks.forEach(callback => {
try {
callback(sessions);
callback({ userId, sessions });
} catch (error) {
console.error("会话更新回调执行失败:", error);
}