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