import { formatDate, formatPattern } from '@/utils/formatDate'; import { API_BASE_URL, getSignature } from '../utils/http'; import { TextDecoder } from 'text-encoding'; let requestTask = null; let messageQueue = []; let printFlag = false; // 打印标识 let intervalFlag = null; let requestTaskFlag = false; let chunkedValid = false; const parseEvent = (eventData: any) => { let data = ''; const lines = eventData.split('\n'); if (!lines) { return null; } lines.forEach((line: any) => { if (line.startsWith('data:')) { data += eventData.slice(5).trim(); } }); return data || null; }; const stopSSE = () => { if (requestTask) { requestTask.abort(); } if (intervalFlag) { clearInterval(intervalFlag); } messageQueue = []; printFlag = false; intervalFlag = null; }; /** * 判断字符串是否为 JSON * @param str */ const isJSON = (str: string): boolean => { try { if (!isNaN(parseFloat(str)) && isFinite(Number(str))) { return false; } JSON.parse(str); return true; } catch { return false; } }; const printText = (callback: Function = () => {}) => { if (printFlag) return; printFlag = true; intervalFlag = setInterval(() => { try { if (messageQueue.length) { let resultData = messageQueue.shift(); switch (resultData.event.value) { case 'conversation.chat.created': callback({ type: 'id', id: resultData.chat.id, value: resultData.chat.conversationID }); break; case 'conversation.message.delta': let content = resultData.message.content; if (!isJSON(content)) { callback({ type: 'text', value: content }); } break; case 'conversation.message.completed': // 完整回答 if (resultData.message.contentType.value == 'card') { let MessageContent = JSON.parse(resultData.message.content); let messageData = JSON.parse(MessageContent.data ?? '{}'); let cardList: any[] = []; if (typeof messageData.variables == 'object' && messageData.variables) { Object.keys(messageData.variables).forEach(key => { let itemContent = messageData.variables[key]; if (itemContent.defaultValue && itemContent.defaultValue.source) { cardList = cardList.concat(itemContent.defaultValue.source); } }); } if (cardList.length) { callback({ type: 'source', value: cardList }); } } else if (resultData.message.contentType.value == 'text') { // 处理 Coze 返回的 resources 数据(网络搜索结果) if (isJSON(resultData.message.content)) { let MessageContent = JSON.parse(resultData.message.content); let cardList: any[] = []; if (Array.isArray(MessageContent) && MessageContent.length) { MessageContent.forEach((itemContent: any) => { if (itemContent.resource == 'net') { cardList.push({ logo_url: itemContent.icon ?? '', title: itemContent.title ?? '', summary: itemContent.summary ?? '', url: itemContent.url ?? '' }); } }); } if (cardList.length) { callback({ type: 'source', value: cardList }); } } } break; case 'conversation.chat.completed': callback({ type: 'completed', value: '' }); stopSSE(); break; case 'conversation.chat.done': callback({ type: 'done', value: '' }); stopSSE(); break; } } } catch (error) { stopSSE(); } }, 50); }; const connetSSE = async (message: string, conversationId: string, callback: Function = () => {}) => { try { let hTimestamp = formatDate(formatPattern.h_timestamp); let params = { message: encodeURIComponent(message), conversationId: conversationId }; let buffer = ''; requestTask = uni.request({ url: API_BASE_URL + '/v1/chat/sendMsg', enableChunked: true, // 启用分块传输 data: params, timeout: 600000, header: { 'Content-Type': 'text/event-stream;charset=utf-8', // 根据服务器要求设置请求头 'h-timestamp': hTimestamp, 'h-sign': getSignature(params, hTimestamp) }, success: (res) => { console.log('SSE请求完成', res); if (!chunkedValid) { let eventEndIndex: number; let content = res.data; while ((eventEndIndex = content.indexOf('\n\n')) !== -1) { if (!requestTaskFlag) { return; } const eventData = content.slice(0, eventEndIndex); content = content.slice(eventEndIndex + 2); // 移除已处理的数据 // 解析事件内容 const message = parseEvent(eventData); if (requestTaskFlag && message) { messageQueue.push(JSON.parse(message)); // 向列表中添加元素 } requestTaskFlag && printText(callback); // 开始打印 } } }, fail: (err) => { console.error('SSE请求失败', err); // let message = ''; // if (err.errMsg == 'request:fail timeout') { // message = '网络请求超时,请稍后再试...'; // } else { // message = '网络请求异常,请稍后再试:' + err.errMsg; // } // uni.showToast({ // title: message, // icon: 'none' // }); stopSSE(); callback({ type: 'done', value: '' }); } }); const Utf8Decoder = new TextDecoder('utf-8'); requestTask.onChunkReceived((res: any) => { chunkedValid = true; let chunk = Utf8Decoder.decode(res.data); buffer += chunk; // 解析缓冲区中的事件(按 \n\n 分割) let eventEndIndex: number; while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) { if (!requestTaskFlag) { return; } const eventData = buffer.slice(0, eventEndIndex); buffer = buffer.slice(eventEndIndex + 2); // 移除已处理的数据 // 解析事件内容 const message = parseEvent(eventData); if (requestTaskFlag && message) { messageQueue.push(JSON.parse(message)); // 向列表中添加元素 } requestTaskFlag && printText(callback); // 开始打印 } }); } catch (error) { console.error('请求流数据时出错2:', error); } }; export const MessageApi = { sendMessage: (message: string, conversationId: string, callback: Function = () => {}) => { try { requestTaskFlag = true; connetSSE(message, conversationId, callback); } catch (error) { console.log(error); } }, stop() { requestTaskFlag = false; stopSSE(); } };