/** * 对话 API 服务层 * * 封装所有对话相关的后端 API 调用 */ import { getAuthHeaders } from './request'; import { useAuthStore } from '@/stores/auth'; import type { Conversation, Message, MessageContent, ConversationSettings } from '@/types/chat'; // API 端点 const API_BASE = '/api/chat-ui'; const ENDPOINTS = { CONVERSATIONS: `${API_BASE}/conversations`, CONVERSATION: (id: string) => `${API_BASE}/conversations/${id}`, CONVERSATION_MESSAGES: (id: string) => `${API_BASE}/conversations/${id}/messages`, }; // 后端返回的对话数据格式 interface BackendConversation { id: string; userId?: string; title: string; createdAt: number; updatedAt: number; pinned: boolean; archived: boolean; settings?: ConversationSettings; messages?: BackendMessage[]; } // 后端返回的消息数据格式 interface BackendMessage { id: string; role: string; content: MessageContent; timestamp: number; feedback?: { liked?: boolean; disliked?: boolean; copied?: boolean; }; } /** * 获取请求头(包含认证信息) */ function getHeaders(): Record { return getAuthHeaders(); } /** * 将后端对话格式转换为前端格式 */ function transformConversation(backendConv: BackendConversation): Conversation { return { id: backendConv.id, title: backendConv.title, createdAt: backendConv.createdAt, updatedAt: backendConv.updatedAt, pinned: backendConv.pinned, archived: backendConv.archived, settings: backendConv.settings, messages: (backendConv.messages || []).map(transformMessage), }; } /** * 将后端消息格式转换为前端格式 */ function transformMessage(backendMsg: BackendMessage): Message { return { id: backendMsg.id, role: backendMsg.role as 'user' | 'assistant' | 'system', content: backendMsg.content, timestamp: backendMsg.timestamp, feedback: backendMsg.feedback, isStreaming: false, } as Message; } /** * 将前端对话格式转换为后端格式 */ function toBackendFormat(conversation: Partial, userId?: string): Record { const data: Record = {}; if (conversation.id !== undefined) data.id = conversation.id; if (userId !== undefined) data.user_id = userId; // 后端使用下划线命名 if (conversation.title !== undefined) data.title = conversation.title; if (conversation.createdAt !== undefined) data.createdAt = conversation.createdAt; if (conversation.updatedAt !== undefined) data.updatedAt = conversation.updatedAt; if (conversation.pinned !== undefined) data.pinned = conversation.pinned; if (conversation.archived !== undefined) data.archived = conversation.archived; if (conversation.settings !== undefined) data.settings = conversation.settings; if (conversation.messages !== undefined) { data.messages = conversation.messages.map(msg => ({ id: msg.id, role: msg.role, content: msg.content, timestamp: msg.timestamp, feedback: msg.feedback, })); } return data; } /** * 对话 API 服务 */ export const conversationApi = { /** * 获取所有对话列表(不含消息内容) */ async fetchConversations(): Promise { const authStore = useAuthStore(); // 等待 authStore 初始化完成 if (!authStore.isInitialized) { await new Promise((resolve) => { const unwatch = authStore.$subscribe(() => { if (authStore.isInitialized) { unwatch(); resolve(); } }); // 如果已经初始化了,立即 resolve if (authStore.isInitialized) { unwatch(); resolve(); } }); } const userId = authStore.userId; // 构建 URL,添加 user_id 查询参数 const url = userId ? `${ENDPOINTS.CONVERSATIONS}?user_id=${encodeURIComponent(userId)}` : ENDPOINTS.CONVERSATIONS; const response = await fetch(url, { method: 'GET', headers: getHeaders(), }); if (!response.ok) { throw new Error(`获取对话列表失败: HTTP ${response.status}`); } const data: BackendConversation[] = await response.json(); return data.map(transformConversation); }, /** * 获取单个对话(含消息内容) */ async fetchConversation(id: string): Promise { const response = await fetch(ENDPOINTS.CONVERSATION(id), { method: 'GET', headers: getHeaders(), }); if (!response.ok) { if (response.status === 404) { throw new Error('对话不存在'); } throw new Error(`获取对话失败: HTTP ${response.status}`); } const data: BackendConversation = await response.json(); return transformConversation(data); }, /** * 创建新对话 */ async createConversation(data: Partial): Promise { const authStore = useAuthStore(); const userId = authStore.userId || undefined; const response = await fetch(ENDPOINTS.CONVERSATIONS, { method: 'POST', headers: getHeaders(), body: JSON.stringify(toBackendFormat(data, userId)), }); if (!response.ok) { throw new Error(`创建对话失败: HTTP ${response.status}`); } const result: BackendConversation = await response.json(); return transformConversation(result); }, /** * 更新对话(部分更新) */ async updateConversation(id: string, data: Partial): Promise { const response = await fetch(ENDPOINTS.CONVERSATION(id), { method: 'PUT', headers: getHeaders(), body: JSON.stringify(toBackendFormat(data)), }); if (!response.ok) { if (response.status === 404) { throw new Error('对话不存在'); } throw new Error(`更新对话失败: HTTP ${response.status}`); } const result: BackendConversation = await response.json(); return transformConversation(result); }, /** * 保存对话(创建或更新) */ async saveConversation(conversation: Conversation): Promise { const data = toBackendFormat(conversation); const response = await fetch(ENDPOINTS.CONVERSATIONS, { method: 'POST', headers: getHeaders(), body: JSON.stringify(data), }); if (!response.ok) { throw new Error(`保存对话失败: HTTP ${response.status}`); } const result: BackendConversation = await response.json(); return transformConversation(result); }, /** * 删除对话 */ async deleteConversation(id: string): Promise { const response = await fetch(ENDPOINTS.CONVERSATION(id), { method: 'DELETE', headers: getHeaders(), }); if (!response.ok) { if (response.status === 404) { // 对话已不存在,视为成功 return; } throw new Error(`删除对话失败: HTTP ${response.status}`); } }, /** * 添加消息到对话(增量更新) */ async addMessage(conversationId: string, message: Partial): Promise { const response = await fetch(ENDPOINTS.CONVERSATION_MESSAGES(conversationId), { method: 'POST', headers: getHeaders(), body: JSON.stringify({ id: message.id, role: message.role, content: message.content, timestamp: message.timestamp, feedback: message.feedback, }), }); if (!response.ok) { throw new Error(`添加消息失败: HTTP ${response.status}`); } const result: BackendMessage = await response.json(); return transformMessage(result); }, /** * 更新消息 */ async updateMessage(conversationId: string, messageId: string, data: Partial): Promise { const response = await fetch(`${ENDPOINTS.CONVERSATION(conversationId)}/messages/${messageId}`, { method: 'PUT', headers: getHeaders(), body: JSON.stringify({ content: data.content, feedback: data.feedback, }), }); if (!response.ok) { throw new Error(`更新消息失败: HTTP ${response.status}`); } const result: BackendMessage = await response.json(); return transformMessage(result); }, /** * 批量迁移对话数据 */ async migrateConversations(conversations: Conversation[]): Promise<{ success: number; failed: number }> { let success = 0; let failed = 0; for (const conversation of conversations) { try { await this.saveConversation(conversation); success++; } catch (e) { console.error(`迁移对话失败 [${conversation.id}]:`, e); failed++; } } return { success, failed }; }, };