From d5c759fb1c54679a27c20a3a2ed2c7e082820462 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期二, 14 一月 2025 18:10:13 +0800 Subject: [PATCH] 消息同步 --- src/layout/component/aside.vue | 1 src/components/chat/Chat.vue | 10 +- src/components/chat/hooks/useLoadData.ts | 1 src/utils/sse/SSEClient.ts | 23 +++++ src/components/chat/hooks/useSyncMsg.ts | 119 ++++++++++++----------------- customer_list/ch/static/config/globalConfig.chengtou.js | 48 ++++++++++++ src/stores/global.ts | 23 +++++ src/router/backEnd.ts | 2 src/stores/chatRoom.ts | 7 + 9 files changed, 157 insertions(+), 77 deletions(-) diff --git a/customer_list/ch/static/config/globalConfig.chengtou.js b/customer_list/ch/static/config/globalConfig.chengtou.js new file mode 100644 index 0000000..b532b46 --- /dev/null +++ b/customer_list/ch/static/config/globalConfig.chengtou.js @@ -0,0 +1,48 @@ +window.globalConfig = { + // 椤圭洰鍚嶇О + Name: 'WI姘村姟鏅鸿兘', + // 椤圭洰鐗堟湰鍙� + Version: '1.0.0242', + // 缃戠珯澶囨鍙� + ICPLicense: '娌狪CP澶�14049296鍙�-2', + + WebApiUrl: { + MainUrl: 'https://widev.cpolar.top/ai_dev_chengtou/', + AuthUrl: 'http://47.100.245.85:8190/', + // 鐪熷疄璁块棶鐨勭敓鎴愮幆澧冮摼鎺ュ湴鍧� + MobileProdShareRealUrl: 'https://wi.beng35.com/mobile/chengtou', + // 鐪熷疄璁块棶鐨勬祴璇曠幆澧冮摼鎺ュ湴鍧� + MobileTestShareRealUrl: 'https://wi.beng35.com/mobile/chengtou', + // 鍒嗕韩閾炬帴 + ShareUrl: 'share/index.html', + }, + SoftWareInfo: { + // 缃戠珯涓绘爣棰橈紙鑿滃崟瀵艰埅銆佹祻瑙堝櫒褰撳墠缃戦〉鏍囬锛� + globalTitle: 'WI姘村姟鏅鸿兘', + // 缃戠珯鍓爣棰橈紙鐧诲綍椤甸《閮ㄦ枃瀛楋級 + globalViceTitle: 'WI姘村姟鏅鸿兘', + // 缃戠珯鍓爣棰橈紙鐧诲綍椤甸《閮ㄦ枃瀛楋級 + globalViceTitleMsg: '鎷夌摝閿″崱鏂囨櫤鑳界鎶�鏈夐檺鍏徃', + // tab 椤� icon + favicon: './favicon.ico', + + // 鐧诲綍宸︿晶 logo + logoMini: './static/images/logo/logoWithNoName.png', + // 鐧诲綍宸︿晶鍥剧墖 + loginMain: './static/images/login/login-main.svg', + // 鐧诲綍鑳屾櫙 + loginBg: './static/images/login/login-bg.svg', + + // 涓婚〉闈㈠乏涓� logo + logoTopMenu: './static/images/logo/logo-mini-white.svg', + }, + // 瀵规帴鏉冮檺绯荤粺鐩稿叧 + Auth: { + // 鐧诲綍杞欢缂栫爜 + SoftWareCode: 'Istation_web_demo', + + // 鐧诲綍淇℃伅 + Message: '', + }, + Mode: 'prod', +}; diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue index 43f5f77..1247ef7 100644 --- a/src/components/chat/Chat.vue +++ b/src/components/chat/Chat.vue @@ -49,7 +49,7 @@ import moment from 'moment'; import { computed, onActivated, onMounted, ref } from 'vue'; import { loadAmisSource } from '../amis/load'; -import { useScrollLoad } from './hooks/useScrollLoad'; +import { useScrollLoad } from './hooks/useScrollLoad'; import type { ChatContent } from './model/types'; import { AnswerState, AnswerType, RoleEnum, type ChatMessage } from './model/types'; import { getShareChatJsonByPost, questionStreamByPost } from '/@/api/ai/chat'; @@ -402,9 +402,11 @@ updateLoadIndex(); userItem.historyId = questionRes?.history_id; + const current = moment().format('YYYY-MM-DD HH:mm:ss'); + userItem.createTime = current; userItem.content.values = questionRes?.question ?? userItem.content.values; assistantItem.historyId = questionRes?.history_id; - const currentTime = formatShowTimeYear(moment().format('YYYY-MM-DD HH:mm:ss')); + const currentTime = formatShowTimeYear(current); assistantItem.createTime = currentTime; assistantItem.content = resMsgContent; setTimeout(() => { @@ -418,7 +420,6 @@ sendChatMessage(messageContent.value); }; - const { loadRangeData, onChatListScroll, moreIsLoading, updateLoadIndex } = useScrollLoad({ container: chatListDom, historyGroupId: currentRouteId, @@ -426,8 +427,9 @@ loadReplyData, }); useSyncMsg({ - msgList: messageList, + msgList: computedMessageList, updateLoadIndex, + historyGroupId: currentRouteId, }); const chatListLoading = ref(true); diff --git a/src/components/chat/hooks/useLoadData.ts b/src/components/chat/hooks/useLoadData.ts index d4ce2ad..f7db954 100644 --- a/src/components/chat/hooks/useLoadData.ts +++ b/src/components/chat/hooks/useLoadData.ts @@ -143,6 +143,7 @@ const tmpMessageList: ChatMessage[] = userMsg.map((item) => { return { historyId: item.history_id, + createTime: item.create_time, role: RoleEnum.user, content: { type: AnswerType.Text, diff --git a/src/components/chat/hooks/useSyncMsg.ts b/src/components/chat/hooks/useSyncMsg.ts index 1403909..4e5c82d 100644 --- a/src/components/chat/hooks/useSyncMsg.ts +++ b/src/components/chat/hooks/useSyncMsg.ts @@ -1,85 +1,68 @@ -import { reverse } from 'lodash-es'; -import { type Ref } from 'vue'; -import type { ChatMessage } from '../model/types'; -import { MAIN_URL } from '/@/constants'; -import { SSEClient } from '/@/utils/sse/SSEClient'; -import { Local } from '/@/utils/storage'; -import { accessSessionKey } from '/@/utils/request'; +import { onActivated, onDeactivated, unref, type Ref } from 'vue'; +import { RoleEnum, type ChatMessage } from '../model/types'; +import { QueryHistoryDetail } from '/@/api/ai/chat'; +import { sseClient } from '/@/stores/global'; +import { LOAD_CHAT_LIMIT } from '../constants'; +import { differenceBy } from 'lodash-es'; type UseSyncMsgOptions = { updateLoadIndex: (addCount: number) => void; msgList: Ref<ChatMessage[]>; + historyGroupId: string | Ref<string>; }; export const useSyncMsg = (options: UseSyncMsgOptions) => { - const { updateLoadIndex, msgList } = options; - // 鍒涘缓瀹炰緥 - const sseClient = new SSEClient( - `${MAIN_URL}chat/connect_broadcast_chat`, + const { updateLoadIndex, msgList, historyGroupId } = options; - {}, - { - onMessage: (data) => { - return; - const recentIds = reverse([ - { id: 'a1b2c3d4', time: '2024-03-27 15:42:33' }, - { id: 'e5f6g7h8', time: '2024-02-15 09:23:45' }, - { id: 'i9j0k1l2', time: '2024-05-08 14:37:21' }, - { id: 'm3n4o5p6', time: '2024-01-30 11:55:16' }, - { id: 'q7r8s9t0', time: '2024-07-12 16:48:59' }, - { id: 'u1v2w3x4', time: '2024-04-03 10:15:27' }, - { id: 'y5z6a7b8', time: '2024-06-21 13:29:44' }, - { id: 'c9d0e1f2', time: '2024-08-09 17:52:38' }, - { id: 'g3h4i5j6', time: '2024-09-14 12:33:51' }, - { id: 'k7l8m9n0', time: '2024-10-25 08:19:07' }, - ]); - // const userHistoryIds = reverse(msgList.value.filter((item) => item.role === RoleEnum.user).map((item) => item.historyId)); - const userHistoryIds = reverse([ - { id: 'a1b2c3d4', time: '2024-03-27 15:42:33' }, - { id: 'e5f6g7h8', time: '2024-02-15 09:23:45' }, - // {id: 'i9j0k1l2', time: '2024-05-08 14:37:21'}, - // {id: 'm3n4o5p6', time: '2024-01-30 11:55:16'}, - { id: 'q7r8s9t0', time: '2024-07-12 16:48:59' }, - // {id: 'u1v2w3x4', time: '2024-04-03 10:15:27'}, - { id: 'y5z6a7b8', time: '2024-06-21 13:29:44' }, - { id: 'c9d0e1f2', time: '2024-08-09 17:52:38' }, - { id: 'g3h4i5j6', time: '2024-09-14 12:33:51' }, - // {id: 'k7l8m9n0', time: '2024-10-25 08:19:07'}, - ]); + // const loadRangeData = async (lastEnd = nextUserMsgEndIndex) => { + // const res = await QueryHistoryDetail({ + // history_group_id: unref(historyGroupId), + // last_end: lastEnd, + // last_count: LOAD_CHAT_LIMIT, + // }); + // const result: ChatMessage[] = res.details ?? []; + // if (result.length) { + // nextUserMsgEndIndex += result.length; + // const rangeMsgList = await loadReplyData(res.details); + // messageList.value.unshift(...rangeMsgList); + // } else { + // isNoMore = true; + // } - // 鑾峰彇鏈悓姝ョ殑娑堟伅 - const unsyncedMessages = findUnsyncedMessages(recentIds, userHistoryIds); - console.log('鏈悓姝ョ殑娑堟伅:', unsyncedMessages); - }, - onError: (error) => { - console.error('SSE error:', error); - }, - onOpen: () => { - console.log('SSE connection opened'); - }, - onClose: () => { - console.log('SSE connection closed'); - }, - onRetry: (retryCount) => { - console.log(`Retrying connection (${retryCount})`); - }, + // return result; + // }; + const historyUpdate = async (data: any) => { + if (!data) return; + const groupId = unref(historyGroupId); + if (!groupId) return; + if (data?.type === 'chat_history_id') { + const recentIds = data.id_list ?? []; + const userHistoryIds = msgList.value + .filter((item) => item.role === RoleEnum.user) + .map((item) => ({ id: item.historyId, time: item.createTime })); + const unSyncedHistoryIds = differenceBy(recentIds, userHistoryIds, 'id'); + console.log('unSyncedHistoryIds', unSyncedHistoryIds); } - ); - sseClient.connect({ - websessionid: Local.get(accessSessionKey), + + const res = await QueryHistoryDetail({ + history_group_id: groupId, + last_end: 0, + last_count: LOAD_CHAT_LIMIT, + }); + console.log('historyUpdate', data, groupId); + }; + + onActivated(() => { + console.log('onActivated'); + sseClient.subscribe(historyUpdate); }); - // onMounted(() => { - // sseClient.connect({}); - // }); + onDeactivated(() => { + console.log('onDeactivated'); + sseClient.unsubscribe(historyUpdate); + }); // onUnmounted(() => { // sseClient.disconnect(); // }); - - return { - reconnect: () => sseClient.reconnect(), - disconnect: () => sseClient.disconnect(), - isConnected: () => sseClient.isConnected(), - }; }; diff --git a/src/layout/component/aside.vue b/src/layout/component/aside.vue index 8a8fb58..5fc7c1c 100644 --- a/src/layout/component/aside.vue +++ b/src/layout/component/aside.vue @@ -17,6 +17,7 @@ import { useThemeConfig } from '/@/stores/themeConfig'; import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'; import mittBus from '/@/utils/mitt'; +import '/@/stores/global'; // 寮曞叆缁勪欢 const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue')); diff --git a/src/router/backEnd.ts b/src/router/backEnd.ts index 078e7d8..79bf7e5 100644 --- a/src/router/backEnd.ts +++ b/src/router/backEnd.ts @@ -21,7 +21,7 @@ * @link 鍙傝�冿細https://cn.vitejs.dev/guide/features.html#json */ const layoutModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}'); -const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}'); +const viewsModules: any = import.meta.glob(['../views/**/*.{vue,tsx}', '!../views/project/(common)/**/*']); const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layoutModules }, { ...viewsModules }); /** diff --git a/src/stores/chatRoom.ts b/src/stores/chatRoom.ts index 42d98d3..4a4be26 100644 --- a/src/stores/chatRoom.ts +++ b/src/stores/chatRoom.ts @@ -7,6 +7,9 @@ import { gotoRoute } from '../utils/route'; import { Local } from '../utils/storage'; import { PingLogin } from '../api/system'; +import { SSEClient } from '../utils/sse/SSEClient'; +import { accessSessionKey } from '../utils/request'; +import { MAIN_URL } from '../constants'; /** * Room 鍏宠仈鐨勪竴浜涢厤缃� @@ -240,8 +243,6 @@ }); }; - - export const pingLogin = async () => { // 5鍒嗛挓 const interval = 1000 * 60 * 5; @@ -255,3 +256,5 @@ }, interval); return timer; }; + + diff --git a/src/stores/global.ts b/src/stores/global.ts new file mode 100644 index 0000000..54d61a6 --- /dev/null +++ b/src/stores/global.ts @@ -0,0 +1,23 @@ +import { MAIN_URL } from '../constants'; +import { accessSessionKey } from '../utils/request'; +import { SSEClient } from '../utils/sse/SSEClient'; +import { Local } from '../utils/storage'; + +/** + * 杩炴帴娑堟伅鍚屾鏈嶅姟 + * @returns + */ +const connectMsgSyncService = () => { + if (!Local.get(accessSessionKey)) return; + // 鍒涘缓瀹炰緥 + const sseClient = new SSEClient( + `${MAIN_URL}chat/connect_broadcast_chat`, + // `${MAIN_URL}events` + ); + sseClient.connect({ + websessionid: Local.get(accessSessionKey), + }); + return sseClient; +}; + +export const sseClient = connectMsgSyncService(); diff --git a/src/utils/sse/SSEClient.ts b/src/utils/sse/SSEClient.ts index e277c30..5a21369 100644 --- a/src/utils/sse/SSEClient.ts +++ b/src/utils/sse/SSEClient.ts @@ -3,6 +3,7 @@ import { SESSION_KEY } from '../request'; import { Local } from '../storage'; import { debounce } from 'lodash-es'; + export interface SSEOptions { /** 閲嶈瘯寤惰繜(ms) */ retryDelay?: number; @@ -28,10 +29,13 @@ onRetry?: () => void; } +export type MessageHandler = (data: any) => void; + export class SSEClient { private eventSource: EventSource | null = null; private reconnectTimeout: number | null = null; private abortController: AbortController | null = null; + private messageHandlers: Set<MessageHandler> = new Set(); constructor(private url: string, private options: SSEOptions = {}, private callbacks: SSEEventCallbacks = {}) { // 璁剧疆榛樿鍊� @@ -44,6 +48,20 @@ }, ...options, }; + } + + /** + * 璁㈤槄娑堟伅 + */ + subscribe(handler: MessageHandler): void { + this.messageHandlers.add(handler); + } + + /** + * 鍙栨秷璁㈤槄娑堟伅 + */ + unsubscribe(handler: MessageHandler): void { + this.messageHandlers.delete(handler); } /** @@ -86,6 +104,9 @@ this.disconnect(); return; } + // 閫氱煡鎵�鏈夎闃呰�� + this.messageHandlers.forEach(handler => handler(data)); + // 璋冪敤鍘熸湁鐨勫洖璋� this.callbacks.onMessage?.(data); } catch (error) { console.error('Failed to parse SSE data:', error); @@ -113,8 +134,6 @@ this.disconnect(); } } - - /** * 鏂紑杩炴帴 -- Gitblit v1.9.3