| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- 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();
- }
- };
|