|
|
@@ -7,7 +7,8 @@ import { MatInputModule } from '@angular/material/input';
|
|
7
|
7
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
8
|
8
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
9
|
9
|
import { FormsModule } from '@angular/forms';
|
|
10
|
|
-import { Subscription } from 'rxjs';
|
|
|
10
|
+import { Subject, Subscription, of } from 'rxjs';
|
|
|
11
|
+import { distinctUntilChanged, switchMap, takeUntil, tap, catchError } from 'rxjs/operators';
|
|
11
|
12
|
import { ConversationService } from '../services/conversation.service';
|
|
12
|
13
|
import { SessionService } from '../services/session.service';
|
|
13
|
14
|
import { EventService } from '../services/event.service';
|
|
|
@@ -48,6 +49,12 @@ export class ConversationComponent implements OnInit, OnDestroy, AfterViewChecke
|
|
48
|
49
|
private shouldScroll = false;
|
|
49
|
50
|
private isInThinking = false; // 是否正在思考过程中
|
|
50
|
51
|
private thinkingStartIndex = 0; // 思考内容的起始位置(在消息内容中的索引)
|
|
|
52
|
+
|
|
|
53
|
+ // RxJS Subjects for reactive session management
|
|
|
54
|
+ private destroy$ = new Subject<void>();
|
|
|
55
|
+ private sessionChanged$ = new Subject<string>();
|
|
|
56
|
+ private currentLoadingSession: string | null = null;
|
|
|
57
|
+ private isLoadingMessages = false;
|
|
51
|
58
|
|
|
52
|
59
|
constructor(
|
|
53
|
60
|
private conversationService: ConversationService,
|
|
|
@@ -56,9 +63,31 @@ export class ConversationComponent implements OnInit, OnDestroy, AfterViewChecke
|
|
56
|
63
|
) {}
|
|
57
|
64
|
|
|
58
|
65
|
ngOnInit() {
|
|
59
|
|
- // 订阅活动会话变化
|
|
|
66
|
+ console.log('🔍 [ConversationComponent] 初始化,instanceId:', this.instanceId);
|
|
|
67
|
+
|
|
|
68
|
+ // 立即检查当前是否有活动会话(处理组件在会话设置后初始化的情况)
|
|
|
69
|
+ const currentSession = this.sessionService.getActiveSession();
|
|
|
70
|
+ if (currentSession) {
|
|
|
71
|
+ console.log('🔍 [ConversationComponent] 初始化时已有活动会话:', currentSession.id);
|
|
|
72
|
+ this.activeSession = currentSession;
|
|
|
73
|
+ this.loadMessages(currentSession.id);
|
|
|
74
|
+ if (this.instanceId) {
|
|
|
75
|
+ this.eventService.registerSessionInstanceMapping(currentSession.id, this.instanceId);
|
|
|
76
|
+ }
|
|
|
77
|
+ this.subscribeToSessionEvents(currentSession.id);
|
|
|
78
|
+ }
|
|
|
79
|
+
|
|
|
80
|
+ // 订阅活动会话变化 - 使用响应式管道避免重复加载
|
|
60
|
81
|
this.subscriptions.add(
|
|
61
|
|
- this.sessionService.activeSession$.subscribe(session => {
|
|
|
82
|
+ this.sessionService.activeSession$.pipe(
|
|
|
83
|
+ takeUntil(this.destroy$),
|
|
|
84
|
+ // 避免相同会话重复触发
|
|
|
85
|
+ distinctUntilChanged((prev, curr) => {
|
|
|
86
|
+ const prevId = prev?.id;
|
|
|
87
|
+ const currId = curr?.id;
|
|
|
88
|
+ return prevId === currId;
|
|
|
89
|
+ })
|
|
|
90
|
+ ).subscribe(session => {
|
|
62
|
91
|
console.log('🔍 [ConversationComponent] 活动会话变化:', session?.id);
|
|
63
|
92
|
|
|
64
|
93
|
// 会话切换时取消当前正在进行的流式请求
|
|
|
@@ -69,6 +98,7 @@ export class ConversationComponent implements OnInit, OnDestroy, AfterViewChecke
|
|
69
|
98
|
|
|
70
|
99
|
this.activeSession = session;
|
|
71
|
100
|
if (session) {
|
|
|
101
|
+ console.log('🔍 [ConversationComponent] 加载会话消息:', session.id);
|
|
72
|
102
|
this.loadMessages(session.id);
|
|
73
|
103
|
// 如果实例ID存在,注册会话-实例映射
|
|
74
|
104
|
if (this.instanceId) {
|
|
|
@@ -77,6 +107,7 @@ export class ConversationComponent implements OnInit, OnDestroy, AfterViewChecke
|
|
77
|
107
|
// 订阅该会话的事件
|
|
78
|
108
|
this.subscribeToSessionEvents(session.id);
|
|
79
|
109
|
} else {
|
|
|
110
|
+ console.log('🔍 [ConversationComponent] 无活动会话,清空消息');
|
|
80
|
111
|
this.messages = [];
|
|
81
|
112
|
// 取消会话事件订阅
|
|
82
|
113
|
this.unsubscribeFromSessionEvents();
|
|
|
@@ -260,6 +291,17 @@ export class ConversationComponent implements OnInit, OnDestroy, AfterViewChecke
|
|
260
|
291
|
ngOnDestroy() {
|
|
261
|
292
|
this.subscriptions.unsubscribe();
|
|
262
|
293
|
this.cancelCurrentStream();
|
|
|
294
|
+
|
|
|
295
|
+ // 清理消息加载订阅
|
|
|
296
|
+ if (this.loadMessagesSubscription) {
|
|
|
297
|
+ this.loadMessagesSubscription.unsubscribe();
|
|
|
298
|
+ this.loadMessagesSubscription = null;
|
|
|
299
|
+ }
|
|
|
300
|
+
|
|
|
301
|
+ // 清理RxJS Subjects
|
|
|
302
|
+ this.destroy$.next();
|
|
|
303
|
+ this.destroy$.complete();
|
|
|
304
|
+ this.sessionChanged$.complete();
|
|
263
|
305
|
}
|
|
264
|
306
|
|
|
265
|
307
|
// 取消当前的流式请求
|
|
|
@@ -289,30 +331,76 @@ export class ConversationComponent implements OnInit, OnDestroy, AfterViewChecke
|
|
289
|
331
|
this.isInThinking = false;
|
|
290
|
332
|
this.thinkingStartIndex = 0;
|
|
291
|
333
|
}
|
|
|
334
|
+
|
|
|
335
|
+ // 安全的计时器函数,避免重复计时器错误
|
|
|
336
|
+ private safeTimeStart(label: string) {
|
|
|
337
|
+ try {
|
|
|
338
|
+ console.timeEnd(label);
|
|
|
339
|
+ } catch(e) {
|
|
|
340
|
+ // 忽略计时器不存在的错误
|
|
|
341
|
+ }
|
|
|
342
|
+ console.time(label);
|
|
|
343
|
+ }
|
|
|
344
|
+
|
|
|
345
|
+ private safeTimeEnd(label: string) {
|
|
|
346
|
+ try {
|
|
|
347
|
+ console.timeEnd(label);
|
|
|
348
|
+ } catch(e) {
|
|
|
349
|
+ // 忽略计时器不存在的错误
|
|
|
350
|
+ }
|
|
|
351
|
+ }
|
|
292
|
352
|
|
|
|
353
|
+ private loadMessagesSubscription: Subscription | null = null;
|
|
|
354
|
+
|
|
293
|
355
|
loadMessages(sessionId: string) {
|
|
294
|
|
- console.log('🔍 [ConversationComponent] 加载会话消息,sessionId:', sessionId);
|
|
|
356
|
+ console.log('🔍 [ConversationComponent] loadMessages 被调用,sessionId:', sessionId);
|
|
|
357
|
+
|
|
|
358
|
+ // 取消之前的加载请求
|
|
|
359
|
+ if (this.loadMessagesSubscription) {
|
|
|
360
|
+ console.log('🔍 [ConversationComponent] 取消之前的消息加载请求');
|
|
|
361
|
+ this.loadMessagesSubscription.unsubscribe();
|
|
|
362
|
+ this.loadMessagesSubscription = null;
|
|
|
363
|
+ }
|
|
|
364
|
+
|
|
|
365
|
+ // 安全地开始计时器
|
|
|
366
|
+ this.safeTimeStart(`[对话消息-加载] ${sessionId}`);
|
|
|
367
|
+ console.log('📊 [对话消息] 开始加载会话消息,sessionId:', sessionId);
|
|
|
368
|
+
|
|
295
|
369
|
this.messages = [];
|
|
296
|
370
|
|
|
297
|
371
|
// 从服务加载历史消息
|
|
298
|
|
- this.subscriptions.add(
|
|
299
|
|
- this.conversationService.getHistory(sessionId, 50).subscribe({
|
|
300
|
|
- next: (historyMessages) => {
|
|
301
|
|
- console.log('🔍 [ConversationComponent] 收到历史消息数量:', historyMessages.length);
|
|
302
|
|
- this.messages = historyMessages;
|
|
303
|
|
-
|
|
304
|
|
- // 按时间排序(最早的在前)
|
|
305
|
|
- this.messages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
306
|
|
-
|
|
307
|
|
- this.shouldScroll = true;
|
|
308
|
|
- },
|
|
309
|
|
- error: (error) => {
|
|
310
|
|
- console.error('🔍 [ConversationComponent] 加载历史消息失败:', error);
|
|
311
|
|
- // 历史消息加载失败,但页面仍然可以正常工作
|
|
312
|
|
- // 用户仍然可以发送新消息
|
|
313
|
|
- }
|
|
314
|
|
- })
|
|
315
|
|
- );
|
|
|
372
|
+ this.loadMessagesSubscription = this.conversationService.getHistory(sessionId, 50).subscribe({
|
|
|
373
|
+ next: (historyMessages) => {
|
|
|
374
|
+ this.safeTimeEnd(`[对话消息-加载] ${sessionId}`);
|
|
|
375
|
+ console.log('📊 [对话消息] 收到历史消息数量:', historyMessages.length);
|
|
|
376
|
+
|
|
|
377
|
+ // 开始消息处理和渲染计时
|
|
|
378
|
+ this.safeTimeStart(`[对话消息-处理] ${sessionId}`);
|
|
|
379
|
+
|
|
|
380
|
+ this.messages = historyMessages;
|
|
|
381
|
+
|
|
|
382
|
+ // 按时间排序(最早的在前)
|
|
|
383
|
+ this.messages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
|
384
|
+
|
|
|
385
|
+ this.shouldScroll = true;
|
|
|
386
|
+
|
|
|
387
|
+ // 使用setTimeout估算渲染完成时间
|
|
|
388
|
+ setTimeout(() => {
|
|
|
389
|
+ this.safeTimeEnd(`[对话消息-处理] ${sessionId}`);
|
|
|
390
|
+ console.log('📊 [对话消息] 消息处理和渲染完成,会话:', sessionId);
|
|
|
391
|
+ }, 0);
|
|
|
392
|
+
|
|
|
393
|
+ // 清理订阅引用
|
|
|
394
|
+ this.loadMessagesSubscription = null;
|
|
|
395
|
+ },
|
|
|
396
|
+ error: (error) => {
|
|
|
397
|
+ this.safeTimeEnd(`[对话消息-加载] ${sessionId}`);
|
|
|
398
|
+ console.error('📊 [对话消息] 加载历史消息失败:', error);
|
|
|
399
|
+ // 历史消息加载失败,但页面仍然可以正常工作
|
|
|
400
|
+ // 用户仍然可以发送新消息
|
|
|
401
|
+ this.loadMessagesSubscription = null;
|
|
|
402
|
+ }
|
|
|
403
|
+ });
|
|
316
|
404
|
}
|
|
317
|
405
|
|
|
318
|
406
|
// 订阅会话事件
|