import { createAsyncThunk } from '@reduxjs/toolkit'; import { get, post, put, del, streamRequest } from '../../services/api'; import { showNotification } from '../notification.slice'; import { addMessage, updateMessage, setCurrentChat } from './chat.slice'; /** * 获取聊天列表 * @param {Object} params - 查询参数 * @param {number} params.page - 页码 * @param {number} params.page_size - 每页数量 */ export const fetchChats = createAsyncThunk('chat/fetchChats', async (params = {}, { rejectWithValue }) => { try { const response = await get('/chat-history/', { params }); // 处理返回格式 if (response && response.code === 200) { return { results: response.data.results, total: response.data.total, page: response.data.page || 1, page_size: response.data.page_size || 10, }; } return { results: [], total: 0, page: 1, page_size: 10 }; } catch (error) { console.error('Error fetching chats:', error); return rejectWithValue(error.response?.data?.message || 'Failed to fetch chats'); } }); /** * 创建新聊天 * @param {Object} chatData - 聊天数据 * @param {string} chatData.knowledge_base_id - 知识库ID * @param {string} chatData.title - 聊天标题 */ export const createChat = createAsyncThunk('chat/createChat', async (chatData, { rejectWithValue }) => { try { const response = await post('/chat-history/', chatData); // 处理返回格式 if (response && response.code === 200) { return response.data.chat; } return response.data?.chat || {}; } catch (error) { return rejectWithValue(error.response?.data?.message || 'Failed to create chat'); } }); /** * 更新聊天 * @param {Object} params - 更新参数 * @param {string} params.id - 聊天ID * @param {Object} params.data - 更新数据 */ export const updateChat = createAsyncThunk('chat/updateChat', async ({ id, data }, { rejectWithValue }) => { try { const response = await put(`/chat-history/${id}/`, data); // 处理返回格式 if (response && response.code === 200) { return response.data.chat; } return response.data?.chat || {}; } catch (error) { return rejectWithValue(error.response?.data?.message || 'Failed to update chat'); } }); /** * 删除聊天 * @param {string} conversationId - 聊天ID */ export const deleteChat = createAsyncThunk('chat/deleteChat', async (conversationId, { rejectWithValue }) => { try { const response = await del(`/chat-history/delete_conversation?conversation_id=${conversationId}`); // 处理返回格式 if (response && response.code === 200) { return conversationId; } return conversationId; } catch (error) { console.error('Error deleting chat:', error); return rejectWithValue(error.response?.data?.message || '删除聊天失败'); } }); /** * 获取可用于聊天的知识库列表 */ export const fetchAvailableDatasets = createAsyncThunk( 'chat/fetchAvailableDatasets', async (_, { rejectWithValue }) => { try { const response = await get('/chat-history/available_datasets/'); if (response && response.code === 200) { return response.data; } return rejectWithValue('获取可用知识库列表失败'); } catch (error) { console.error('Error fetching available datasets:', error); return rejectWithValue(error.response?.data?.message || '获取可用知识库列表失败'); } } ); /** * 创建聊天记录 * @param {Object} params - 聊天参数 * @param {string[]} params.dataset_id_list - 知识库ID列表 * @param {string} params.question - 用户问题 * @param {string} params.conversation_id - 会话ID,可选 */ export const createChatRecord = createAsyncThunk( 'chat/createChatRecord', async ({ question, conversation_id, dataset_id_list }, { dispatch, getState, rejectWithValue }) => { try { // 构建请求数据 const requestBody = { question, dataset_id_list, conversation_id, }; console.log('准备发送聊天请求:', requestBody); // 先添加用户消息到聊天窗口 const userMessageId = Date.now().toString(); dispatch( addMessage({ id: userMessageId, role: 'user', content: question, created_at: new Date().toISOString(), }) ); // 添加临时的助手消息(流式传输期间显示) const assistantMessageId = (Date.now() + 1).toString(); dispatch( addMessage({ id: assistantMessageId, role: 'assistant', content: '', created_at: new Date().toISOString(), is_streaming: true, }) ); let finalMessage = ''; let conversationId = conversation_id; // 使用流式请求函数处理 const result = await streamRequest( '/chat-history/', requestBody, // 处理每个数据块 (chunkText) => { try { const data = JSON.parse(chunkText); console.log('收到聊天数据块:', data); if (data.code === 200) { // 保存会话ID (无论消息类型,只要找到会话ID就保存) if (data.data && data.data.conversation_id && !conversationId) { conversationId = data.data.conversation_id; console.log('获取到会话ID:', conversationId); } // 处理各种可能的消息类型 const messageType = data.message; // 处理部分内容更新 if ((messageType === 'partial' || messageType === '部分') && data.data) { // 累加内容 if (data.data.content !== undefined) { finalMessage += data.data.content; console.log('累加内容:', finalMessage); // 更新消息内容 dispatch( updateMessage({ id: assistantMessageId, content: finalMessage, }) ); } // 处理结束标志 if (data.data.is_end) { console.log('检测到消息结束标志'); dispatch( updateMessage({ id: assistantMessageId, is_streaming: false, }) ); } } // 处理开始流式传输的消息 else if (messageType === '开始流式传输' || messageType === 'start_streaming') { console.log('开始流式传输,会话ID:', data.data?.conversation_id); } // 处理完成消息 else if ( messageType === 'completed' || messageType === '完成' || messageType === 'end_streaming' || messageType === '结束流式传输' ) { console.log('收到完成消息'); dispatch( updateMessage({ id: assistantMessageId, is_streaming: false, }) ); } // 其他类型的消息 else { console.log('收到其他类型消息:', messageType); // 如果有content字段,也尝试更新 if (data.data && data.data.content !== undefined) { finalMessage += data.data.content; dispatch( updateMessage({ id: assistantMessageId, content: finalMessage, }) ); } } } else { console.warn('收到非成功状态码:', data.code, data.message); } } catch (error) { console.error('解析或处理JSON失败:', error, '原始数据:', chunkText); } }, // 处理错误 (error) => { console.error('流式请求错误:', error); dispatch( updateMessage({ id: assistantMessageId, content: `错误: ${error.message || '请求失败'}`, is_streaming: false, }) ); } ); // 确保流式传输结束后标记消息已完成 dispatch( updateMessage({ id: assistantMessageId, is_streaming: false, }) ); // 返回会话信息 const chatInfo = { conversation_id: conversationId || result.conversation_id, success: true, }; // 如果聊天创建成功,添加到历史列表,并设置为当前激活聊天 if (chatInfo.conversation_id) { // 获取知识库信息 const state = getState(); const availableDatasets = state.chat.availableDatasets.items || []; const existingChats = state.chat.history.items || []; // 检查是否已存在此会话ID的记录 const existingChat = existingChats.find((chat) => chat.conversation_id === chatInfo.conversation_id); // 只有在不存在相同会话ID的记录时才创建新的记录 if (!existingChat) { console.log('创建新的聊天sidebar记录:', chatInfo.conversation_id); // 创建一个新的聊天记录对象添加到历史列表 const newChatEntry = { conversation_id: chatInfo.conversation_id, datasets: dataset_id_list.map((id) => { // 尝试查找知识库名称 const formattedId = id.includes('-') ? id : id.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5'); const dataset = availableDatasets.find((ds) => ds.id === formattedId); return { id: formattedId, name: dataset?.name || '新知识库对话', }; }), create_time: new Date().toISOString(), last_message: question, message_count: 2, // 用户问题和助手回复 }; // 更新当前聊天 dispatch({ type: 'chat/fetchChats/fulfilled', payload: { results: [newChatEntry], total: 1, append: true, // 标记为追加,而不是替换 }, }); } else { console.log('聊天sidebar记录已存在,不再创建:', chatInfo.conversation_id); } // 设置为当前聊天 dispatch( setCurrentChat({ conversation_id: chatInfo.conversation_id, datasets: existingChat ? existingChat.datasets : dataset_id_list.map((id) => { const formattedId = id.includes('-') ? id : id.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5'); const dataset = availableDatasets.find((ds) => ds.id === formattedId); return { id: formattedId, name: dataset?.name || '新知识库对话', }; }), }) ); } return chatInfo; } catch (error) { console.error('创建聊天记录失败:', error); // 显示错误通知 dispatch( showNotification({ message: `发送失败: ${error.message || '未知错误'}`, type: 'danger', }) ); return rejectWithValue(error.message || '创建聊天记录失败'); } } ); /** * 获取会话详情 * @param {string} conversationId - 会话ID */ export const fetchConversationDetail = createAsyncThunk( 'chat/fetchConversationDetail', async (conversationId, { rejectWithValue, dispatch, getState }) => { try { // 先检查是否是刚创建的会话 const state = getState(); const createSession = state.chat.createSession || {}; const currentChat = state.chat.currentChat.data; // 如果是刚创建成功的会话,且会话ID匹配,则直接返回现有会话数据 if ( createSession.status === 'succeeded' && createSession.sessionId === conversationId && currentChat?.conversation_id === conversationId ) { console.log('使用新创建的会话数据,跳过详情请求:', conversationId); return currentChat; } const response = await get('/chat-history/conversation_detail', { params: { conversation_id: conversationId }, }); if (response && response.code === 200) { // 如果存在消息,更新Redux状态 if (response.data.messages) { dispatch({ type: 'chat/fetchMessages/fulfilled', payload: response.data.messages, }); } return response.data; } return rejectWithValue('获取会话详情失败'); } catch (error) { // 明确检查是否是404错误 const is404Error = error.response && error.response.status === 404; if (is404Error) { console.log('会话未找到,可能是新创建的会话:', conversationId); return null; } console.error('Error fetching conversation detail:', error); return rejectWithValue(error.response?.data?.message || '获取会话详情失败'); } } ); /** * 创建新会话(仅获取会话ID,不发送消息) * @param {Object} params - 参数 * @param {string[]} params.dataset_id_list - 知识库ID列表 */ export const createConversation = createAsyncThunk( 'chat/createConversation', async ({ dataset_id_list }, { dispatch, getState, rejectWithValue }) => { try { console.log('创建新会话,知识库ID列表:', dataset_id_list); const params = { dataset_id_list: dataset_id_list, }; const response = await post('/chat-history/create_conversation/', params); if (response && response.code === 200) { const conversationData = response.data; console.log('会话创建成功:', conversationData); // 获取知识库信息 const state = getState(); const availableDatasets = state.chat.availableDatasets.items || []; // 创建一个新的聊天记录对象添加到历史列表 const newChatEntry = { conversation_id: conversationData.conversation_id, datasets: dataset_id_list.map((id) => { // 尝试查找知识库名称 const formattedId = id.includes('-') ? id : id.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5'); const dataset = availableDatasets.find((ds) => ds.id === formattedId); return { id: formattedId, name: dataset?.name || '新知识库对话', }; }), create_time: new Date().toISOString(), last_message: '', message_count: 0, }; // 更新聊天历史列表 dispatch({ type: 'chat/fetchChats/fulfilled', payload: { results: [newChatEntry], total: 1, append: true, // 标记为追加,而不是替换 }, }); // 设置为当前聊天 dispatch( setCurrentChat({ conversation_id: conversationData.conversation_id, datasets: newChatEntry.datasets, }) ); return conversationData; } return rejectWithValue('创建会话失败'); } catch (error) { console.error('创建会话失败:', error); // 显示错误通知 dispatch( showNotification({ message: `创建会话失败: ${error.message || '未知错误'}`, type: 'danger', }) ); return rejectWithValue(error.message || '创建会话失败'); } } );