useChatMessages.ts 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import { useMemo } from 'react';
  2. import { useLoadMoreInfinite, createKey } from '@/utils/loadMoreInfinite';
  3. import { getMessageHistories } from '@/service/chat';
  4. import { useTextChat } from '@/store/textChatStore';
  5. import { formatMessageItem } from '@/utils/messageUtils';
  6. import { TMessage, TRobotMessage, TAnyMessage } from '@/types/bot';
  7. /**
  8. * 聊天消息管理 Hook
  9. * 负责处理消息历史记录获取、消息分组和格式化
  10. */
  11. export const useChatMessages = (agentId: string, isVisitor?: string): {
  12. historyList: TAnyMessage[];
  13. messageList: TAnyMessage[];
  14. parsedList: TAnyMessage[];
  15. groupedMessages: { dt: string; list: TAnyMessage[] }[];
  16. messagesLength: number;
  17. loadMore: () => void;
  18. pageIndex: number;
  19. mutate: (data: any, params: any) => void;
  20. } => {
  21. const messageList = useTextChat((state) => state.list);
  22. // 获取历史聊天记录的 fetcher 函数
  23. const fetcher = async ([_url, { nextId, pageSize }]) => {
  24. const _nextId = nextId ? decodeURIComponent(nextId) : nextId;
  25. const res = await getMessageHistories({
  26. agentId,
  27. startId: _nextId,
  28. pageSize,
  29. });
  30. return res.data;
  31. };
  32. // 使用无限滚动加载历史消息
  33. const { list, loadMore, pageIndex, mutate } = useLoadMoreInfinite<
  34. TMessage[] | TRobotMessage[]
  35. >(createKey(`messeagehistories${isVisitor}${agentId}`), fetcher, {
  36. revalidateOnMount: false
  37. });
  38. // 解析消息体 content,并展平数组
  39. const parsedList = list
  40. .flat()
  41. .filter((item) => !!item.content.length)
  42. .map(formatMessageItem);
  43. // 按 sessionId 分组消息,并记录每组的最早时间
  44. // 最后返回数组,以按组显示聊天记录时间 隔开每一组聊天记录
  45. const groupedMessages = useMemo(() => {
  46. const allMessages = [...[...parsedList].reverse(), ...messageList];
  47. const resultMap = allMessages.reduce((acc, item) => {
  48. const { sessionId, msgTime } = item;
  49. if (!sessionId || !msgTime) {
  50. return acc;
  51. }
  52. let _msgTime = msgTime.replace(/\-/g, "/");
  53. if (!acc[sessionId]) {
  54. acc[sessionId] = {
  55. dt: _msgTime, // 初始化当前组的最早时间
  56. list: [item], // 初始化当前组的记录列表
  57. };
  58. } else {
  59. // 更新最早时间(如果当前记录的 msgTime 更早)
  60. if (new Date(_msgTime) < new Date(acc[sessionId].dt)) {
  61. acc[sessionId].dt = msgTime;
  62. }
  63. // 将记录添加到当前组
  64. acc[sessionId].list.push(item);
  65. }
  66. return acc;
  67. }, {} as Record<string, { dt: string; list: TAnyMessage[] }>);
  68. // 转换为最终数组格式
  69. return Object.values(resultMap);
  70. }, [parsedList, messageList]);
  71. // 消息总长度(用于触发滚动等副作用)
  72. const messagesLength = useMemo(() => groupedMessages.length, [groupedMessages.length]);
  73. return {
  74. // 原始数据(保持原始结构供其他用途)
  75. historyList: parsedList, // 已经展平的消息列表
  76. messageList,
  77. parsedList,
  78. // 处理后的数据
  79. groupedMessages,
  80. messagesLength,
  81. // 操作方法
  82. loadMore,
  83. pageIndex,
  84. mutate,
  85. };
  86. };