对话、重新生成、评价完成
This commit is contained in:
@@ -55,8 +55,8 @@ export const aiAgentConfigApi = {
|
||||
* 获取启用的智能体列表
|
||||
* @returns Promise<ResultDomain<AiAgentConfig[]>>
|
||||
*/
|
||||
async listEnabledAgents(): Promise<ResultDomain<AiAgentConfig[]>> {
|
||||
const response = await api.get<AiAgentConfig[]>('/ai/agent/enabled');
|
||||
async listEnabledAgents(): Promise<ResultDomain<AiAgentConfig>> {
|
||||
const response = await api.get<AiAgentConfig>('/ai/agent/enabled');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -76,8 +76,8 @@ export const aiAgentConfigApi = {
|
||||
* @param pageParam 分页参数
|
||||
* @returns Promise<PageDomain<AiAgentConfig>>
|
||||
*/
|
||||
async pageAgents(filter: Partial<AiAgentConfig>, pageParam: PageParam): Promise<PageDomain<AiAgentConfig>> {
|
||||
const response = await api.post<PageDomain<AiAgentConfig>>('/ai/agent/page', {
|
||||
async pageAgents(filter: Partial<AiAgentConfig>, pageParam: PageParam): Promise<ResultDomain<AiAgentConfig>> {
|
||||
const response = await api.post<AiAgentConfig>('/ai/agent/page', {
|
||||
filter,
|
||||
pageParam
|
||||
});
|
||||
@@ -91,7 +91,9 @@ export const aiAgentConfigApi = {
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async updateAgentStatus(agentId: string, status: number): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.put<boolean>(`/ai/agent/${agentId}/status`, { status });
|
||||
const response = await api.put<boolean>(`/ai/agent/${agentId}/status`, null, {
|
||||
params: { status }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -103,9 +105,8 @@ export const aiAgentConfigApi = {
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async updateDifyConfig(agentId: string, difyAppId: string, difyApiKey: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.put<boolean>(`/ai/agent/${agentId}/dify`, {
|
||||
difyAppId,
|
||||
difyApiKey
|
||||
const response = await api.put<boolean>(`/ai/agent/${agentId}/dify`, null, {
|
||||
params: { difyAppId, difyApiKey }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -7,12 +7,8 @@
|
||||
import { api } from '@/apis/index';
|
||||
import type {
|
||||
AiConversation,
|
||||
AiMessage,
|
||||
ConversationSearchParams,
|
||||
MessageSearchParams,
|
||||
UserChatStatistics,
|
||||
ConversationStatistics,
|
||||
BatchExportParams,
|
||||
ResultDomain,
|
||||
PageDomain
|
||||
} from '@/types';
|
||||
@@ -27,8 +23,9 @@ export const chatHistoryApi = {
|
||||
* @returns Promise<PageDomain<AiConversation>>
|
||||
*/
|
||||
async pageUserConversations(params: ConversationSearchParams): Promise<PageDomain<AiConversation>> {
|
||||
const response = await api.post<PageDomain<AiConversation>>('/ai/history/conversations/page', params);
|
||||
return response.data;
|
||||
// 后端直接返回PageDomain,不需要再次包装
|
||||
const response = await api.post('/ai/chat/history/conversations/page', params);
|
||||
return response.data as PageDomain<AiConversation>;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -37,18 +34,9 @@ export const chatHistoryApi = {
|
||||
* @returns Promise<PageDomain<AiConversation>>
|
||||
*/
|
||||
async searchConversations(params: MessageSearchParams): Promise<PageDomain<AiConversation>> {
|
||||
const response = await api.post<PageDomain<AiConversation>>('/ai/history/conversations/search', params);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 搜索消息内容(全文搜索)
|
||||
* @param params 搜索参数
|
||||
* @returns Promise<PageDomain<AiMessage>>
|
||||
*/
|
||||
async searchMessages(params: MessageSearchParams): Promise<PageDomain<AiMessage>> {
|
||||
const response = await api.post<PageDomain<AiMessage>>('/ai/history/messages/search', params);
|
||||
return response.data;
|
||||
// 后端直接返回PageDomain,不需要再次包装
|
||||
const response = await api.post('/ai/chat/history/search', params);
|
||||
return response.data as PageDomain<AiConversation>;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -58,7 +46,8 @@ export const chatHistoryApi = {
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async toggleFavorite(conversationId: string, isFavorite: boolean): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.put<boolean>(`/ai/history/conversation/${conversationId}/favorite`, {
|
||||
const response = await api.put<boolean>('/ai/chat/history/conversation/favorite', {
|
||||
conversationId,
|
||||
isFavorite
|
||||
});
|
||||
return response.data;
|
||||
@@ -71,7 +60,8 @@ export const chatHistoryApi = {
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async togglePin(conversationId: string, isPinned: boolean): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.put<boolean>(`/ai/history/conversation/${conversationId}/pin`, {
|
||||
const response = await api.put<boolean>('/ai/chat/history/conversation/pin', {
|
||||
conversationId,
|
||||
isPinned
|
||||
});
|
||||
return response.data;
|
||||
@@ -83,8 +73,8 @@ export const chatHistoryApi = {
|
||||
* @returns Promise<ResultDomain<number>>
|
||||
*/
|
||||
async batchDeleteConversations(conversationIds: string[]): Promise<ResultDomain<number>> {
|
||||
const response = await api.post<number>('/ai/history/conversations/batch-delete', {
|
||||
conversationIds
|
||||
const response = await api.delete<number>('/ai/chat/history/conversations/batch', {
|
||||
data: { conversationIds }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
@@ -92,10 +82,10 @@ export const chatHistoryApi = {
|
||||
/**
|
||||
* 获取用户的对话统计信息
|
||||
* @param userId 用户ID(可选,默认当前用户)
|
||||
* @returns Promise<ResultDomain<UserChatStatistics>>
|
||||
* @returns Promise<ResultDomain<any>>
|
||||
*/
|
||||
async getUserChatStatistics(userId?: string): Promise<ResultDomain<UserChatStatistics>> {
|
||||
const response = await api.get<UserChatStatistics>('/ai/history/statistics/user', {
|
||||
async getUserChatStatistics(userId?: string): Promise<ResultDomain<any>> {
|
||||
const response = await api.get<any>('/ai/chat/history/statistics', {
|
||||
params: { userId }
|
||||
});
|
||||
return response.data;
|
||||
@@ -104,10 +94,10 @@ export const chatHistoryApi = {
|
||||
/**
|
||||
* 获取会话的详细统计
|
||||
* @param conversationId 会话ID
|
||||
* @returns Promise<ResultDomain<ConversationStatistics>>
|
||||
* @returns Promise<ResultDomain<any>>
|
||||
*/
|
||||
async getConversationStatistics(conversationId: string): Promise<ResultDomain<ConversationStatistics>> {
|
||||
const response = await api.get<ConversationStatistics>(`/ai/history/statistics/conversation/${conversationId}`);
|
||||
async getConversationStatistics(conversationId: string): Promise<ResultDomain<any>> {
|
||||
const response = await api.get<any>(`/ai/chat/history/conversation/${conversationId}/statistics`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -117,7 +107,7 @@ export const chatHistoryApi = {
|
||||
* @returns Promise<ResultDomain<string>>
|
||||
*/
|
||||
async exportConversationAsMarkdown(conversationId: string): Promise<ResultDomain<string>> {
|
||||
const response = await api.get<string>(`/ai/history/export/markdown/${conversationId}`);
|
||||
const response = await api.get<string>(`/ai/chat/history/export/markdown/${conversationId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -127,47 +117,7 @@ export const chatHistoryApi = {
|
||||
* @returns Promise<ResultDomain<string>>
|
||||
*/
|
||||
async exportConversationAsJson(conversationId: string): Promise<ResultDomain<string>> {
|
||||
const response = await api.get<string>(`/ai/history/export/json/${conversationId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量导出会话
|
||||
* @param params 导出参数
|
||||
* @returns Promise<ResultDomain<string>>
|
||||
*/
|
||||
async batchExportConversations(params: BatchExportParams): Promise<ResultDomain<string>> {
|
||||
const response = await api.post<string>('/ai/history/export/batch', params);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 下载导出文件
|
||||
* @param conversationId 会话ID
|
||||
* @param format 格式(markdown/json)
|
||||
*/
|
||||
downloadExport(conversationId: string, format: 'markdown' | 'json'): void {
|
||||
const url = `${api.defaults.baseURL}/ai/history/export/download/${conversationId}?format=${format}`;
|
||||
window.open(url, '_blank');
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量下载导出文件
|
||||
* @param conversationIds 会话ID列表
|
||||
* @param format 格式(markdown/json)
|
||||
*/
|
||||
batchDownloadExport(conversationIds: string[], format: 'markdown' | 'json'): void {
|
||||
const url = `${api.defaults.baseURL}/ai/history/export/batch-download?format=${format}&ids=${conversationIds.join(',')}`;
|
||||
window.open(url, '_blank');
|
||||
},
|
||||
|
||||
/**
|
||||
* 清理过期会话(软删除超过N天的会话)
|
||||
* @param days 天数
|
||||
* @returns Promise<ResultDomain<number>>
|
||||
*/
|
||||
async cleanExpiredConversations(days: number): Promise<ResultDomain<number>> {
|
||||
const response = await api.post<number>('/ai/history/clean', { days });
|
||||
const response = await api.get<string>(`/ai/chat/history/export/json/${conversationId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -176,20 +126,8 @@ export const chatHistoryApi = {
|
||||
* @param limit 数量限制(默认10)
|
||||
* @returns Promise<ResultDomain<AiConversation>>
|
||||
*/
|
||||
async getRecentConversations(limit?: number): Promise<ResultDomain<AiConversation>> {
|
||||
const response = await api.get<AiConversation>('/ai/history/recent', {
|
||||
params: { limit }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取热门会话(基于消息数或Token数)
|
||||
* @param limit 数量限制(默认10)
|
||||
* @returns Promise<ResultDomain<AiConversation>>
|
||||
*/
|
||||
async getPopularConversations(limit?: number): Promise<ResultDomain<AiConversation>> {
|
||||
const response = await api.get<AiConversation>('/ai/history/popular', {
|
||||
async getRecentConversations(limit = 10): Promise<ResultDomain<AiConversation>> {
|
||||
const response = await api.get<AiConversation>('/ai/chat/history/recent', {
|
||||
params: { limit }
|
||||
});
|
||||
return response.data;
|
||||
|
||||
@@ -9,7 +9,6 @@ import type {
|
||||
AiConversation,
|
||||
AiMessage,
|
||||
ChatRequest,
|
||||
ChatResponse,
|
||||
ResultDomain,
|
||||
StreamCallback
|
||||
} from '@/types';
|
||||
@@ -19,43 +18,102 @@ import type {
|
||||
*/
|
||||
export const chatApi = {
|
||||
/**
|
||||
* 流式对话(SSE)
|
||||
* 流式对话(SSE)- 使用fetch支持Authorization
|
||||
* @param request 对话请求
|
||||
* @param callback 流式回调
|
||||
* @returns Promise<ResultDomain<AiMessage>>
|
||||
*/
|
||||
async streamChat(request: ChatRequest, callback?: StreamCallback): Promise<ResultDomain<AiMessage>> {
|
||||
const token = localStorage.getItem('token');
|
||||
const tokenData = token ? JSON.parse(token) : null;
|
||||
return new Promise((resolve, reject) => {
|
||||
// 使用相对路径走Vite代理,避免跨域
|
||||
const eventSource = new EventSource(
|
||||
`${api.defaults.baseURL}/ai/chat/stream?` +
|
||||
`/api/ai/chat/stream?` +
|
||||
new URLSearchParams({
|
||||
agentId: request.agentId,
|
||||
conversationId: request.conversationId || '',
|
||||
query: request.query,
|
||||
knowledgeIds: request.knowledgeIds?.join(',') || ''
|
||||
knowledgeIds: request.knowledgeIds?.join(',') || '',
|
||||
token: tokenData?.value || ''
|
||||
})
|
||||
);
|
||||
|
||||
let fullMessage = '';
|
||||
// 通知外部EventSource已创建
|
||||
callback?.onStart?.(eventSource);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let fullMessage = ''; // 累积完整消息内容
|
||||
|
||||
// 监听初始化事件(包含messageId和conversationId)
|
||||
eventSource.addEventListener('init', (event) => {
|
||||
try {
|
||||
const initData = JSON.parse(event.data);
|
||||
console.log('[初始化数据]', initData);
|
||||
// 通知外部保存messageId(用于停止生成)
|
||||
if (callback?.onInit) {
|
||||
callback.onInit(initData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('解析init事件失败:', event.data);
|
||||
}
|
||||
});
|
||||
|
||||
// 监听标准消息事件
|
||||
eventSource.addEventListener('message', (event) => {
|
||||
const data = event.data;
|
||||
fullMessage += data;
|
||||
callback?.onMessage?.(data);
|
||||
});
|
||||
|
||||
// 监听结束事件
|
||||
eventSource.addEventListener('end', (event) => {
|
||||
const metadata = JSON.parse(event.data);
|
||||
callback?.onMessageEnd?.(metadata);
|
||||
eventSource.close();
|
||||
|
||||
resolve({
|
||||
code: 200,
|
||||
success: true,
|
||||
login: true,
|
||||
auth: true,
|
||||
data: metadata as AiMessage,
|
||||
message: '对话成功'
|
||||
});
|
||||
});
|
||||
|
||||
// 监听所有Dify原始事件(workflow_started, node_started等)
|
||||
const difyEventTypes = [
|
||||
'dify_workflow_started',
|
||||
'dify_node_started',
|
||||
'dify_node_finished',
|
||||
'dify_workflow_finished',
|
||||
'dify_message',
|
||||
'dify_agent_message',
|
||||
'dify_message_end',
|
||||
'dify_message_file',
|
||||
'dify_agent_thought',
|
||||
'dify_ping'
|
||||
];
|
||||
|
||||
difyEventTypes.forEach(eventType => {
|
||||
eventSource.addEventListener(eventType, (event: any) => {
|
||||
try {
|
||||
const eventData = JSON.parse(event.data);
|
||||
console.log(`[Dify事件] ${eventType}:`, eventData);
|
||||
|
||||
// 调用自定义的Dify事件回调
|
||||
if (callback?.onDifyEvent) {
|
||||
const cleanEventType = eventType.replace('dify_', '');
|
||||
callback.onDifyEvent(cleanEventType, eventData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`解析Dify事件失败 ${eventType}:`, event.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 监听错误事件
|
||||
eventSource.addEventListener('error', (event: any) => {
|
||||
const error = new Error(event.data || '对话失败');
|
||||
callback?.onError?.(error);
|
||||
@@ -64,7 +122,7 @@ export const chatApi = {
|
||||
});
|
||||
|
||||
eventSource.onerror = (error) => {
|
||||
callback?.onError?.(error as Error);
|
||||
callback?.onError?.(error as unknown as Error);
|
||||
eventSource.close();
|
||||
reject(error);
|
||||
};
|
||||
@@ -82,7 +140,7 @@ export const chatApi = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止对话生成
|
||||
* 停止对话生成(通过消息ID)
|
||||
* @param messageId 消息ID
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
@@ -91,6 +149,20 @@ export const chatApi = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止对话生成(通过Dify TaskID)
|
||||
* @param taskId Dify任务ID
|
||||
* @param agentId 智能体ID
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async stopChatByTaskId(taskId: string, agentId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>('/ai/chat/stop-by-taskid', {
|
||||
taskId,
|
||||
agentId
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建新会话
|
||||
* @param agentId 智能体ID
|
||||
@@ -152,8 +224,8 @@ export const chatApi = {
|
||||
* @param conversationId 会话ID
|
||||
* @returns Promise<ResultDomain<AiMessage[]>>
|
||||
*/
|
||||
async listMessages(conversationId: string): Promise<ResultDomain<AiMessage[]>> {
|
||||
const response = await api.get<AiMessage[]>(`/ai/chat/conversation/${conversationId}/messages`);
|
||||
async listMessages(conversationId: string): Promise<ResultDomain<AiMessage>> {
|
||||
const response = await api.get<AiMessage>(`/ai/chat/conversation/${conversationId}/messages`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -168,46 +240,112 @@ export const chatApi = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 重新生成回答
|
||||
* 重新生成回答(SSE流式)
|
||||
* @param messageId 原消息ID
|
||||
* @param callback 流式回调(可选)
|
||||
* @param callback 流式回调
|
||||
* @returns Promise<ResultDomain<AiMessage>>
|
||||
*/
|
||||
async regenerateAnswer(messageId: string, callback?: StreamCallback): Promise<ResultDomain<AiMessage>> {
|
||||
if (callback) {
|
||||
// 使用流式方式重新生成
|
||||
return new Promise((resolve, reject) => {
|
||||
const eventSource = new EventSource(
|
||||
`${api.defaults.baseURL}/ai/chat/regenerate/${messageId}?stream=true`
|
||||
);
|
||||
const token = localStorage.getItem('token');
|
||||
const tokenData = token ? JSON.parse(token) : null;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// 使用相对路径走Vite代理,SSE流式推送
|
||||
const eventSource = new EventSource(
|
||||
`/api/ai/chat/regenerate/${messageId}?` +
|
||||
new URLSearchParams({
|
||||
token: tokenData?.value || ''
|
||||
})
|
||||
);
|
||||
|
||||
eventSource.addEventListener('message', (event) => {
|
||||
callback.onMessage?.(event.data);
|
||||
});
|
||||
// 通知外部EventSource已创建
|
||||
callback?.onStart?.(eventSource);
|
||||
|
||||
eventSource.addEventListener('end', (event) => {
|
||||
const metadata = JSON.parse(event.data);
|
||||
callback.onMessageEnd?.(metadata);
|
||||
eventSource.close();
|
||||
resolve({
|
||||
success: true,
|
||||
data: metadata as AiMessage,
|
||||
message: '重新生成成功'
|
||||
});
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let fullMessage = ''; // 累积完整消息内容
|
||||
|
||||
eventSource.addEventListener('error', (event: any) => {
|
||||
const error = new Error(event.data || '重新生成失败');
|
||||
callback.onError?.(error);
|
||||
eventSource.close();
|
||||
reject(error);
|
||||
// 监听初始化事件(包含messageId和conversationId)
|
||||
eventSource.addEventListener('init', (event) => {
|
||||
try {
|
||||
const initData = JSON.parse(event.data);
|
||||
console.log('[初始化数据-重新生成]', initData);
|
||||
// 通知外部保存messageId(用于停止生成)
|
||||
if (callback?.onInit) {
|
||||
callback.onInit(initData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('解析init事件失败:', event.data);
|
||||
}
|
||||
});
|
||||
|
||||
// 监听标准消息事件
|
||||
eventSource.addEventListener('message', (event) => {
|
||||
const data = event.data;
|
||||
fullMessage += data;
|
||||
callback?.onMessage?.(data);
|
||||
});
|
||||
|
||||
// 监听结束事件
|
||||
eventSource.addEventListener('end', (event) => {
|
||||
const metadata = JSON.parse(event.data);
|
||||
callback?.onMessageEnd?.(metadata);
|
||||
eventSource.close();
|
||||
|
||||
resolve({
|
||||
code: 200,
|
||||
success: true,
|
||||
login: true,
|
||||
auth: true,
|
||||
data: metadata as AiMessage,
|
||||
message: '重新生成成功'
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// 使用阻塞方式重新生成
|
||||
const response = await api.post<AiMessage>(`/ai/chat/regenerate/${messageId}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// 监听所有Dify原始事件(workflow_started, node_started等)
|
||||
const difyEventTypes = [
|
||||
'dify_workflow_started',
|
||||
'dify_node_started',
|
||||
'dify_node_finished',
|
||||
'dify_workflow_finished',
|
||||
'dify_message',
|
||||
'dify_agent_message',
|
||||
'dify_message_end',
|
||||
'dify_message_file',
|
||||
'dify_agent_thought',
|
||||
'dify_ping'
|
||||
];
|
||||
|
||||
difyEventTypes.forEach(eventType => {
|
||||
eventSource.addEventListener(eventType, (event: any) => {
|
||||
try {
|
||||
const eventData = JSON.parse(event.data);
|
||||
console.log(`[Dify事件] ${eventType}:`, eventData);
|
||||
|
||||
// 调用自定义的Dify事件回调
|
||||
if (callback?.onDifyEvent) {
|
||||
const cleanEventType = eventType.replace('dify_', '');
|
||||
callback.onDifyEvent(cleanEventType, eventData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`解析Dify事件失败 ${eventType}:`, event.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 监听错误事件
|
||||
eventSource.addEventListener('error', (event: any) => {
|
||||
const error = new Error(event.data || '重新生成失败');
|
||||
callback?.onError?.(error);
|
||||
eventSource.close();
|
||||
reject(error);
|
||||
});
|
||||
|
||||
eventSource.onerror = (error) => {
|
||||
callback?.onError?.(error as unknown as Error);
|
||||
eventSource.close();
|
||||
reject(error);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
*/
|
||||
|
||||
import { api } from '@/apis/index';
|
||||
import type { AiUploadFile, ResultDomain, FileUploadResponse, PageDomain, PageParam } from '@/types';
|
||||
|
||||
import type { AiUploadFile, ResultDomain, FileUploadResponse, PageParam } from '@/types';
|
||||
/**
|
||||
* 文件上传API服务
|
||||
*/
|
||||
@@ -22,11 +21,7 @@ export const fileUploadApi = {
|
||||
formData.append('file', file);
|
||||
formData.append('knowledgeId', knowledgeId);
|
||||
|
||||
const response = await api.post<FileUploadResponse>('/ai/file/upload', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
const response = await api.post<FileUploadResponse>('/ai/file/upload', formData);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -43,11 +38,7 @@ export const fileUploadApi = {
|
||||
});
|
||||
formData.append('knowledgeId', knowledgeId);
|
||||
|
||||
const response = await api.post<FileUploadResponse[]>('/ai/file/batch-upload', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
const response = await api.post<FileUploadResponse[]>('/ai/file/batch-upload', formData);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -85,10 +76,10 @@ export const fileUploadApi = {
|
||||
* 分页查询文件
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @returns Promise<PageDomain<AiUploadFile>>
|
||||
* @returns Promise<ResultDomain<AiUploadFile>>
|
||||
*/
|
||||
async pageFiles(filter: Partial<AiUploadFile>, pageParam: PageParam): Promise<PageDomain<AiUploadFile>> {
|
||||
const response = await api.post<PageDomain<AiUploadFile>>('/ai/file/page', {
|
||||
async pageFiles(filter: Partial<AiUploadFile>, pageParam: PageParam): Promise<ResultDomain<AiUploadFile>> {
|
||||
const response = await api.post<AiUploadFile>('/ai/file/page', {
|
||||
filter,
|
||||
pageParam
|
||||
});
|
||||
|
||||
@@ -76,8 +76,8 @@ export const knowledgeApi = {
|
||||
* @param pageParam 分页参数
|
||||
* @returns Promise<PageDomain<AiKnowledge>>
|
||||
*/
|
||||
async pageKnowledges(filter: Partial<AiKnowledge>, pageParam: PageParam): Promise<PageDomain<AiKnowledge>> {
|
||||
const response = await api.post<PageDomain<AiKnowledge>>('/ai/knowledge/page', {
|
||||
async pageKnowledges(filter: Partial<AiKnowledge>, pageParam: PageParam): Promise<ResultDomain<AiKnowledge>> {
|
||||
const response = await api.post<AiKnowledge>('/ai/knowledge/page', {
|
||||
filter,
|
||||
pageParam
|
||||
});
|
||||
@@ -108,6 +108,7 @@ export const knowledgeApi = {
|
||||
* 设置知识库权限
|
||||
* @param params 权限参数
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
|
||||
*/
|
||||
async setPermissions(params: KnowledgePermissionParams): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>('/ai/knowledge/permissions', params);
|
||||
@@ -144,3 +145,4 @@ export const knowledgeApi = {
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,26 +18,45 @@ interface CustomAxiosRequestConfig extends Partial<InternalAxiosRequestConfig> {
|
||||
* Token管理
|
||||
*/
|
||||
export const TokenManager = {
|
||||
/** 获取token(优先从localStorage,其次sessionStorage) */
|
||||
/** 获取token(从localStorage获取并检查过期) */
|
||||
getToken(): string | null {
|
||||
return localStorage.getItem('token') || sessionStorage.getItem('token');
|
||||
},
|
||||
|
||||
/** 设置token(根据rememberMe决定存储位置) */
|
||||
setToken(token: string, rememberMe = false): void {
|
||||
if (rememberMe) {
|
||||
localStorage.setItem('token', token);
|
||||
sessionStorage.removeItem('token'); // 清除sessionStorage中的旧token
|
||||
} else {
|
||||
sessionStorage.setItem('token', token);
|
||||
localStorage.removeItem('token'); // 清除localStorage中的旧token
|
||||
const itemStr = localStorage.getItem('token');
|
||||
if (!itemStr) return null;
|
||||
|
||||
try {
|
||||
const item = JSON.parse(itemStr);
|
||||
const now = Date.now();
|
||||
|
||||
// 检查是否过期
|
||||
if (item.timestamp && item.expiresIn) {
|
||||
if (now - item.timestamp > item.expiresIn) {
|
||||
// 已过期,删除
|
||||
localStorage.removeItem('token');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return item.value || itemStr; // 兼容旧数据
|
||||
} catch {
|
||||
// 如果不是JSON格式,直接返回(兼容旧数据)
|
||||
return itemStr;
|
||||
}
|
||||
},
|
||||
|
||||
/** 移除token(两个存储都清除) */
|
||||
/** 设置token(始终使用localStorage,根据rememberMe设置过期时间) */
|
||||
setToken(token: string, rememberMe = false): void {
|
||||
const data = {
|
||||
value: token,
|
||||
timestamp: Date.now(),
|
||||
// 如果不勾选"记住我",设置1天过期时间;勾选则7天
|
||||
expiresIn: rememberMe ? 7 * 24 * 60 * 60 * 1000 : 1 * 24 * 60 * 60 * 1000
|
||||
};
|
||||
localStorage.setItem('token', JSON.stringify(data));
|
||||
},
|
||||
|
||||
/** 移除token */
|
||||
removeToken(): void {
|
||||
localStorage.removeItem('token');
|
||||
sessionStorage.removeItem('token');
|
||||
},
|
||||
|
||||
/** 检查是否有token */
|
||||
@@ -81,6 +100,11 @@ request.interceptors.request.use(
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
// 自动处理 FormData:删除 Content-Type,让浏览器自动设置(包含 boundary)
|
||||
if (config.data instanceof FormData && config.headers) {
|
||||
delete config.headers['Content-Type'];
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
|
||||
Reference in New Issue
Block a user