Commit 0367c92e authored by 水玉婷's avatar 水玉婷
Browse files

feat:单次连接

parent 4a8d1aff
......@@ -111,7 +111,7 @@
:appCode="props?.appCode"
:apiBaseUrl="props?.apiBaseUrl"
@audio="handleVoiceAudio"
@error="handleVoiceError" class="voice-recognition-wrapper" />
@error="handleVoiceError" class="voice-recognition-wrapper" />
<textarea ref="textarea" v-model="messageText" placeholder="输入消息..." @keypress="handleKeyPress"
@input="adjustTextareaHeight"></textarea>
......@@ -262,6 +262,12 @@ const handleSSEMessage = (data: SSEData, isSimulated: boolean = false) => {
}
}
// 处理状态29:当前会话结束,关闭SSE连接
if (data.status === 29) {
console.log('收到状态29,当前会话结束,关闭SSE连接');
closeSSE();
}
if (result.recordId && currentAIResponse.value) {
currentAIResponse.value.recordId = result.recordId;
currentAIResponse.value.promptTokens = result.promptTokens;
......@@ -320,11 +326,15 @@ const handleVoiceError = (error: string) => {
};
// 开始对话函数 - 修改为在发送消息时调用
const startConversation = () => {
const startConversation = async (): Promise<void> => {
if (!hasStartedConversation.value) {
console.log('开始对话,初始化SSE连接');
// 每次发送消息时重新初始化SSE连接,并等待连接建立完成
hasStartedConversation.value = true;
console.log('✅ SSE连接建立完成,可以发送消息');
}
await initSSE();
};
// 定义消息类型
......@@ -383,7 +393,7 @@ const sendMessage = async (type: MessageType = 'text', params: MessageParams = {
}
// 开始对话
startConversation();
await startConversation();
isAIResponding.value = false;
isInThinkingMode.value = false;
......@@ -686,8 +696,6 @@ defineExpose({
// 生命周期
onMounted(() => {
console.log('组件挂载,初始 dialogSessionId:', props.dialogSessionId);
initSSE();
scrollToBottom();
if (props.dialogSessionId) {
getChatRecord(props.dialogSessionId);
}
......
import { EventSourcePolyfill } from 'event-source-polyfill';
import { ref, Ref } from 'vue';
// SSE数据类型定义
export interface SSEData {
......@@ -27,25 +26,15 @@ export interface SSEHandlers {
onReconnect?: (newDialogSessionId: string) => void;
}
// SSE服务类 - 专注于SSE连接管理
// SSE服务类 - 简化的连接管理(每次发消息时建立连接)
export class SSEService {
private eventSource: EventSourcePolyfill | null = null;
private config: SSEServiceConfig;
private handlers: SSEHandlers;
private isReconnecting: Ref<boolean> = ref(false);
private timeArr: NodeJS.Timeout[] = [];
// keepalive相关状态
private hasReceivedKeepalive: boolean = false; // 标记是否收到过keepalive事件
// 页面刷新监听器相关
private beforeUnloadHandler: (() => void) | null = null;
private isPageRefreshing: boolean = false;
constructor(config: SSEServiceConfig, handlers: SSEHandlers = {}) {
this.config = config;
this.handlers = handlers;
this.setupPageRefreshListener();
}
// 初始化SSE连接
......@@ -59,12 +48,9 @@ export class SSEService {
'x-app-code': this.config.appCode || '',
},
withCredentials: true,
connectionTimeout: 60000,
heartbeatTimeout: 60000,
});
this.eventSource.onopen = (event) => {
// 移除这里的日志,只在外部处理器中打印
if (this.handlers.onOpen) {
this.handlers.onOpen(event);
}
......@@ -83,57 +69,15 @@ export class SSEService {
}
});
// 监听keepalive事件
this.eventSource.addEventListener('keepalive', (event) => {
this.hasReceivedKeepalive = true; // 标记已收到keepalive
console.log('💓 收到keepalive事件,连接正常');
});
this.eventSource.onerror = (error) => {
console.error('SSE error:', error);
// 检查是否为页面刷新导致的错误
if (this.isPageRefreshing) {
console.log('💡 页面刷新中,忽略SSE错误');
return; // 页面刷新时忽略所有错误
}
// 检查是否为"No activity"错误 - 使用更宽松的检测条件
const errorString = String(error).toLowerCase();
// 检测各种可能的"No activity"错误变体
const isNoActivityError =
errorString.includes('no activity') ||
errorString.includes('no activity within') ||
errorString.includes('milliseconds') && errorString.includes('reconnecting') ||
errorString.includes('60000') && errorString.includes('reconnecting');
// 如果是"No activity"错误且收到过keepalive,说明连接正常,忽略错误
if (isNoActivityError && this.hasReceivedKeepalive) {
this.hasReceivedKeepalive = false; // 重置状态,等待下一次keepalive
return; // 不关闭连接,不触发重连,完全退出错误处理
}
// 如果只是"No activity"错误但没有收到keepalive,也尝试忽略(可能是误报)
if (isNoActivityError) {
return; // 不关闭连接,不触发重连
}
// 如果不是"No activity"错误,则执行正常的错误处理
// 简化错误处理:直接调用错误处理器并关闭连接
if (this.handlers.onError) {
this.handlers.onError(error);
}
this.closeSSE();
// 添加错误重连逻辑(仅在非页面刷新状态下)
if (!this.isReconnecting.value && !this.isPageRefreshing) {
setTimeout(() => {
if (dialogSessionId) {
this.reconnectSSE(dialogSessionId);
}
}, 3000);
}
};
} catch (err) {
......@@ -141,24 +85,11 @@ export class SSEService {
}
}
// 重新连接SSE
// 重新连接SSE(简化版本,直接重新初始化连接)
public reconnectSSE(newDialogSessionId: string): void {
if (this.isReconnecting.value) {
return;
}
this.isReconnecting.value = true;
console.log('开始重连SSE,新的dialogSessionId:', newDialogSessionId);
console.log('重新连接SSE,新的dialogSessionId:', newDialogSessionId);
this.closeSSE();
const reconnectTimeout = setTimeout(() => {
this.initSSE(newDialogSessionId);
setTimeout(() => {
this.isReconnecting.value = false;
}, 2000);
}, 500);
this.timeArr.push(reconnectTimeout);
this.initSSE(newDialogSessionId);
if (this.handlers.onReconnect) {
this.handlers.onReconnect(newDialogSessionId);
......@@ -175,17 +106,6 @@ export class SSEService {
console.warn('关闭SSE连接时出错:', err);
}
}
// 清理所有定时器
this.timeArr.forEach(timeout => {
clearTimeout(timeout);
});
this.timeArr = [];
}
// 获取重连状态
public getIsReconnecting(): boolean {
return this.isReconnecting.value;
}
// 获取当前SSE连接状态
......@@ -199,40 +119,6 @@ export class SSEService {
// 清理资源
public destroy(): void {
this.closeSSE();
this.timeArr.forEach((item) => {
clearTimeout(item);
});
this.timeArr = [];
this.removePageRefreshListener();
}
// 设置页面刷新监听器
private setupPageRefreshListener(): void {
if (typeof window !== 'undefined') {
// 监听页面卸载事件
this.beforeUnloadHandler = () => {
this.isPageRefreshing = true;
console.log('💡 检测到页面刷新,主动关闭SSE连接');
this.closeSSE();
};
// 添加多个事件监听器,确保能捕获各种页面关闭场景
window.addEventListener('beforeunload', this.beforeUnloadHandler);
}
}
// 移除页面刷新监听器
private removePageRefreshListener(): void {
if (typeof window !== 'undefined') {
if (this.beforeUnloadHandler) {
window.removeEventListener('beforeunload', this.beforeUnloadHandler);
this.beforeUnloadHandler = null;
}
}
}
// 检查是否正在页面刷新
public getIsPageRefreshing(): boolean {
return this.isPageRefreshing;
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment