1065 lines
31 KiB
JavaScript
1065 lines
31 KiB
JavaScript
// API服务文件
|
||
import { useUserStore } from '@/stores/user.js';
|
||
import { API_CONFIG, getApiUrl } from '@/utils/config.js';
|
||
|
||
// 图片URL处理函数 - 处理小程序中图片路径问题
|
||
export const getResourceUrl = (url) => {
|
||
if (!url || typeof url !== 'string') {
|
||
return '/static/default-avatar.png';
|
||
}
|
||
|
||
// 如果是完整的http/https URL,直接返回
|
||
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||
return url;
|
||
}
|
||
|
||
// 如果是相对路径,拼接完整的服务器地址
|
||
if (url.startsWith('/file/')) {
|
||
return API_CONFIG.BASE_URL + url;
|
||
}
|
||
|
||
// 如果是其他相对路径,也拼接服务器地址
|
||
if (url.startsWith('/')) {
|
||
return API_CONFIG.BASE_URL + url;
|
||
}
|
||
|
||
// 默认返回原路径
|
||
return url;
|
||
};
|
||
|
||
// 文本清理函数 - 只保留文字和标点符号
|
||
export const cleanText = (text) => {
|
||
if (!text || typeof text !== 'string') {
|
||
return '';
|
||
}
|
||
|
||
// 去除所有HTML标签
|
||
let cleaned = text.replace(/<[^>]*>/g, '');
|
||
|
||
// 去除Markdown格式标记
|
||
cleaned = cleaned
|
||
// 去除粗体标记 **text** 或 __text__
|
||
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
||
.replace(/__([^_]+)__/g, '$1')
|
||
// 去除斜体标记 *text* 或 _text_
|
||
.replace(/\*([^*]+)\*/g, '$1')
|
||
.replace(/_([^_]+)_/g, '$1')
|
||
// 去除删除线标记 ~~text~~
|
||
.replace(/~~([^~]+)~~/g, '$1')
|
||
// 去除代码标记 `code`
|
||
.replace(/`([^`]+)`/g, '$1')
|
||
// 去除链接标记 [text](url)
|
||
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
||
// 去除图片标记 
|
||
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
|
||
// 去除标题标记 # ## ###
|
||
.replace(/^#{1,6}\s*/gm, '')
|
||
// 去除列表标记 - * +
|
||
.replace(/^[\s]*[-*+]\s*/gm, '')
|
||
// 去除引用标记 >
|
||
.replace(/^>\s*/gm, '')
|
||
// 去除水平线标记 --- 或 ***
|
||
.replace(/^[-*]{3,}$/gm, '');
|
||
|
||
// 去除多余的空白字符和换行
|
||
cleaned = cleaned
|
||
// 将多个连续空格和换行替换为单个空格
|
||
.replace(/\s+/g, ' ')
|
||
// 去除行首行尾空格
|
||
.trim();
|
||
|
||
// 去除特殊字符(保留中文、英文、数字、标点符号,以及分隔符 &)
|
||
cleaned = cleaned.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s\.,;:!?()()【】""''""''、,。!?;:&]/g, '');
|
||
|
||
return cleaned;
|
||
};
|
||
|
||
// 检查用户登录状态
|
||
const checkLoginStatus = () => {
|
||
const userStore = useUserStore();
|
||
const customToken = uni.getStorageSync('custom_token');
|
||
const userToken = uni.getStorageSync('user_token');
|
||
const userInfo = uni.getStorageSync('userInfo');
|
||
|
||
// 解析userInfo,检查是否有有效的token
|
||
let userInfoToken = '';
|
||
if (userInfo) {
|
||
try {
|
||
const parsedUserInfo = JSON.parse(userInfo);
|
||
userInfoToken = parsedUserInfo.token || '';
|
||
} catch (e) {
|
||
console.warn('解析userInfo失败:', e);
|
||
}
|
||
}
|
||
|
||
// 只有存在有效token才认为已登录
|
||
const hasValidToken = !!(userStore.token || customToken || userToken || userInfoToken);
|
||
|
||
return {
|
||
isLoggedIn: hasValidToken,
|
||
token: userStore.token || customToken || userToken || userInfoToken
|
||
};
|
||
};
|
||
|
||
// 请求拦截器
|
||
const request = (options) => {
|
||
return new Promise((resolve, reject) => {
|
||
const userStore = useUserStore();
|
||
const loginStatus = checkLoginStatus();
|
||
|
||
// 默认配置
|
||
const defaultOptions = {
|
||
url: getApiUrl(options.url),
|
||
method: options.method || 'GET',
|
||
header: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': loginStatus.token ? (loginStatus.token.startsWith('Bearer ') ? loginStatus.token : 'Bearer ' + loginStatus.token) : '',
|
||
...options.header
|
||
},
|
||
data: options.data || {},
|
||
timeout: options.timeout || API_CONFIG.TIMEOUT
|
||
};
|
||
|
||
// 发送请求
|
||
uni.request({
|
||
...defaultOptions,
|
||
success: (res) => {
|
||
console.log('API请求成功:', res);
|
||
console.log('响应状态码:', res.statusCode);
|
||
console.log('响应数据:', res.data);
|
||
|
||
// 处理响应
|
||
if (res.statusCode === 200) {
|
||
resolve(res.data);
|
||
} else {
|
||
reject({
|
||
code: res.statusCode,
|
||
message: res.data?.message || '请求失败',
|
||
data: res.data
|
||
});
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('API请求失败:', err);
|
||
reject({
|
||
code: -1,
|
||
message: '网络请求失败',
|
||
error: err
|
||
});
|
||
}
|
||
});
|
||
});
|
||
};
|
||
|
||
// AI聊天API
|
||
export const chatAPI = {
|
||
// 同步聊天接口
|
||
syncChat: async (params) => {
|
||
const loginStatus = checkLoginStatus();
|
||
|
||
// 如果用户未登录,直接返回失败,让前端使用本地模拟
|
||
if (!loginStatus.isLoggedIn) {
|
||
console.log('用户未登录,跳过API调用,使用本地模拟');
|
||
return {
|
||
success: false,
|
||
error: { message: '用户未登录,使用本地模拟回复' },
|
||
isAnonymous: true
|
||
};
|
||
}
|
||
|
||
try {
|
||
const requestData = {
|
||
message: params.message,
|
||
useFunctionCall: false,
|
||
modelId: params.modelId || null, // 支持传入modelId,默认为null使用后端默认
|
||
templateId: params.templateId || params.characterId, // 支持templateId参数
|
||
sessionId: params.sessionId || params.conversationId || null // 支持sessionId参数,conversationId作为备选
|
||
};
|
||
|
||
console.log('发送AI聊天请求,参数:', requestData);
|
||
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.CHAT_SYNC,
|
||
method: 'POST',
|
||
data: requestData
|
||
});
|
||
|
||
console.log('API原始响应:', response);
|
||
|
||
// 处理不同的响应格式 - 参考chat.vue的实现
|
||
let processedResponse = null;
|
||
|
||
// 如果响应是字符串,直接返回
|
||
if (typeof response === 'string') {
|
||
processedResponse = response;
|
||
}
|
||
// 如果响应是对象,尝试提取AI回复
|
||
else if (typeof response === 'object' && response !== null) {
|
||
// 优先处理根级别的response字段(根据实际后端响应结构)
|
||
if (response.response && typeof response.response === 'string') {
|
||
processedResponse = response.response;
|
||
console.log('从根级别字段 response 提取回复:', processedResponse);
|
||
}
|
||
// 备用:处理嵌套结构:res.data.data.response
|
||
else if (response.data && response.data.data && response.data.data.response) {
|
||
processedResponse = response.data.data.response;
|
||
console.log('从嵌套结构 data.data.response 提取回复:', processedResponse);
|
||
}
|
||
// 备用:直接使用 data.response
|
||
else if (response.data && response.data.response) {
|
||
processedResponse = response.data.response;
|
||
console.log('从字段 data.response 提取回复:', processedResponse);
|
||
}
|
||
// 检查是否为状态消息
|
||
else if (response.message) {
|
||
if (response.message === '对话成功') {
|
||
// 后端返回成功消息,使用默认回复
|
||
processedResponse = '我收到了你的消息,很高兴和你聊天!';
|
||
console.log('检测到对话成功状态,使用默认回复');
|
||
} else {
|
||
// 如果后端返回了错误信息,抛出错误
|
||
throw new Error(response.message);
|
||
}
|
||
}
|
||
// 检查data中的message字段
|
||
else if (response.data && response.data.message) {
|
||
if (response.data.message === '对话成功') {
|
||
// 后端返回成功消息,使用默认回复
|
||
processedResponse = '我收到了你的消息,很高兴和你聊天!';
|
||
console.log('检测到对话成功状态,使用默认回复');
|
||
} else {
|
||
// 如果后端返回了错误信息,抛出错误
|
||
throw new Error(response.data.message);
|
||
}
|
||
}
|
||
// 尝试其他可能的字段名
|
||
else {
|
||
const possibleFields = ['message', 'response', 'content', 'reply', 'answer', 'text'];
|
||
|
||
for (const field of possibleFields) {
|
||
if (response[field] && typeof response[field] === 'string') {
|
||
processedResponse = response[field];
|
||
console.log(`从字段 ${field} 提取回复:`, processedResponse);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果还是没找到,尝试在data中查找
|
||
if (!processedResponse && response.data) {
|
||
for (const field of possibleFields) {
|
||
if (response.data[field] && typeof response.data[field] === 'string') {
|
||
processedResponse = response.data[field];
|
||
console.log(`从字段 data.${field} 提取回复:`, processedResponse);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果仍然没有找到有效回复,使用默认回复
|
||
if (!processedResponse) {
|
||
processedResponse = '我收到了你的消息,很高兴和你聊天!';
|
||
console.log('未找到有效回复,使用默认回复');
|
||
}
|
||
|
||
// 清理文本,只保留文字和标点符号
|
||
const cleanedResponse = cleanText(processedResponse);
|
||
console.log('原始回复:', processedResponse);
|
||
console.log('清理后回复:', cleanedResponse);
|
||
|
||
return {
|
||
success: true,
|
||
data: cleanedResponse,
|
||
originalResponse: response
|
||
};
|
||
} catch (error) {
|
||
console.error('AI聊天API调用失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 异步聊天接口(如果需要)
|
||
asyncChat: async (params) => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.CHAT_ASYNC,
|
||
method: 'POST',
|
||
data: {
|
||
message: params.message,
|
||
characterId: params.characterId,
|
||
conversationId: params.conversationId || null,
|
||
...params
|
||
}
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('AI异步聊天API调用失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 获取聊天历史
|
||
getChatHistory: async (conversationId) => {
|
||
try {
|
||
const response = await request({
|
||
url: `${API_CONFIG.ENDPOINTS.CHAT_HISTORY}/${conversationId}`,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取聊天历史失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 创建新对话
|
||
createConversation: async (characterId) => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.CHAT_CONVERSATION,
|
||
method: 'POST',
|
||
data: {
|
||
characterId: characterId
|
||
}
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('创建对话失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 清空会话上下文
|
||
clearSession: async (sessionId) => {
|
||
try {
|
||
const response = await request({
|
||
url: `${API_CONFIG.ENDPOINTS.CHAT_SESSION}/${sessionId}`,
|
||
method: 'DELETE'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('清空会话API调用失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 获取历史消息(根据sessionId查询全部)
|
||
getHistoryMessages: async (sessionId) => {
|
||
const loginStatus = checkLoginStatus();
|
||
|
||
// 如果用户未登录,直接返回空数组
|
||
if (!loginStatus.isLoggedIn) {
|
||
console.log('用户未登录,无法获取历史消息');
|
||
return {
|
||
success: true,
|
||
data: [],
|
||
isAnonymous: true
|
||
};
|
||
}
|
||
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.MESSAGE_HISTORY,
|
||
method: 'GET',
|
||
data: {
|
||
sessionId: sessionId
|
||
}
|
||
});
|
||
|
||
console.log('历史消息API响应:', response);
|
||
|
||
// 处理响应数据
|
||
let messageList = [];
|
||
if (response && response.data) {
|
||
// 直接是数组
|
||
if (Array.isArray(response.data)) {
|
||
messageList = response.data;
|
||
}
|
||
// 可能嵌套在data字段中
|
||
else if (response.data.data && Array.isArray(response.data.data)) {
|
||
messageList = response.data.data;
|
||
}
|
||
} else if (Array.isArray(response)) {
|
||
messageList = response;
|
||
}
|
||
|
||
console.log('解析后的历史消息数量:', messageList.length);
|
||
|
||
return {
|
||
success: true,
|
||
data: messageList
|
||
};
|
||
} catch (error) {
|
||
console.error('获取历史消息失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error,
|
||
data: []
|
||
};
|
||
}
|
||
}
|
||
};
|
||
|
||
// 语音相关API
|
||
export const voiceAPI = {
|
||
// 1. 文本转语音 - TTS功能
|
||
textToSpeech: async (text, options = {}) => {
|
||
try {
|
||
const loginStatus = checkLoginStatus();
|
||
|
||
if (!loginStatus.isLoggedIn) {
|
||
return {
|
||
success: false,
|
||
error: { message: '用户未登录' }
|
||
};
|
||
}
|
||
|
||
console.log('开始文本转语音:', text);
|
||
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.TTS,
|
||
method: 'POST',
|
||
data: {
|
||
text: text,
|
||
voiceStyle: options.voiceStyle || 'default',
|
||
modelId: options.modelId || null,
|
||
templateId: options.templateId || null
|
||
}
|
||
});
|
||
|
||
console.log('文本转语音响应:', response);
|
||
|
||
// 处理响应数据
|
||
let audioUrl = null;
|
||
if (response.data && response.data.audioUrl) {
|
||
audioUrl = response.data.audioUrl;
|
||
} else if (response.audioUrl) {
|
||
audioUrl = response.audioUrl;
|
||
} else if (response.data && response.data.url) {
|
||
audioUrl = response.data.url;
|
||
} else if (response.url) {
|
||
audioUrl = response.url;
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
data: {
|
||
audioUrl: audioUrl,
|
||
originalResponse: response
|
||
}
|
||
};
|
||
} catch (error) {
|
||
console.error('文本转语音失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 2. 对话+语音合成 - 对话后转语音
|
||
chatWithTTS: async (message, options = {}) => {
|
||
try {
|
||
const loginStatus = checkLoginStatus();
|
||
|
||
if (!loginStatus.isLoggedIn) {
|
||
return {
|
||
success: false,
|
||
error: { message: '用户未登录' }
|
||
};
|
||
}
|
||
|
||
console.log('开始对话+语音合成:', message);
|
||
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.ANSWER_TTS,
|
||
method: 'POST',
|
||
data: {
|
||
message: message,
|
||
modelId: options.modelId || null,
|
||
templateId: options.templateId || null,
|
||
voiceStyle: options.voiceStyle || 'default'
|
||
}
|
||
});
|
||
|
||
console.log('对话+语音合成响应:', response);
|
||
|
||
// 处理响应数据
|
||
let aiResponse = null;
|
||
let audioUrl = null;
|
||
|
||
if (response.data) {
|
||
aiResponse = response.data.response || response.data.text || response.data.message;
|
||
audioUrl = response.data.audioUrl || response.data.url;
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
data: {
|
||
aiResponse: aiResponse,
|
||
audioUrl: audioUrl,
|
||
originalResponse: response
|
||
}
|
||
};
|
||
} catch (error) {
|
||
console.error('对话+语音合成失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 3. 语音对话 - 完整语音交互流程(前端发送aac格式,后端转换为wav处理)
|
||
voiceChat: async (filePath, options = {}) => {
|
||
try {
|
||
const loginStatus = checkLoginStatus();
|
||
|
||
// 如果用户未登录,直接返回失败,让前端使用降级处理
|
||
if (!loginStatus.isLoggedIn) {
|
||
console.log('用户未登录,语音对话跳过API调用');
|
||
return {
|
||
success: false,
|
||
error: { message: '用户未登录,使用降级处理' }
|
||
};
|
||
}
|
||
|
||
console.log('开始语音对话,文件路径:', filePath);
|
||
console.log('注意:前端发送aac格式音频,后端需要转换为wav格式进行处理');
|
||
|
||
// 构建认证头
|
||
let authHeader = '';
|
||
if (loginStatus.token) {
|
||
authHeader = loginStatus.token.startsWith('Bearer ') ? loginStatus.token : 'Bearer ' + loginStatus.token;
|
||
}
|
||
|
||
return new Promise((resolve) => {
|
||
uni.uploadFile({
|
||
url: getApiUrl(API_CONFIG.ENDPOINTS.VOICE_CHAT),
|
||
filePath: filePath,
|
||
name: 'audio',
|
||
header: authHeader ? {
|
||
'Authorization': authHeader
|
||
} : {},
|
||
formData: {
|
||
modelId: options.modelId || null,
|
||
templateId: options.templateId || null,
|
||
voiceStyle: options.voiceStyle || 'default'
|
||
},
|
||
success: (res) => {
|
||
console.log('语音对话上传成功:', res);
|
||
|
||
try {
|
||
const data = JSON.parse(res.data);
|
||
console.log('语音对话响应数据:', data);
|
||
|
||
if (data.code === 200) {
|
||
// 根据后端实际返回结构提取字段
|
||
let aiResponse = null;
|
||
let userText = null;
|
||
let audioUrl = null;
|
||
|
||
// 从 data.llmResult.response 提取AI回复
|
||
if (data.data && data.data.llmResult && data.data.llmResult.response) {
|
||
aiResponse = data.data.llmResult.response;
|
||
}
|
||
|
||
// 从 data.sttResult.text 提取用户文本(语音转文字)
|
||
if (data.data && data.data.sttResult && data.data.sttResult.text) {
|
||
userText = data.data.sttResult.text;
|
||
}
|
||
|
||
// 从 data.ttsResult.audioPath 提取音频路径
|
||
if (data.data && data.data.ttsResult && data.data.ttsResult.audioPath) {
|
||
audioUrl = data.data.ttsResult.audioPath;
|
||
}
|
||
|
||
// 备用字段提取(保持向后兼容)
|
||
if (!aiResponse) {
|
||
if (data.response && typeof data.response === 'string') {
|
||
aiResponse = data.response;
|
||
} else if (data.data && data.data.response) {
|
||
aiResponse = data.data.response;
|
||
}
|
||
}
|
||
|
||
if (!userText) {
|
||
userText = data.userText || data.data?.userText || data.data?.text || data.data?.user_text || data.data?.recognizedText || data.data?.transcription;
|
||
}
|
||
|
||
if (!audioUrl) {
|
||
audioUrl = data.audioPath || data.audioUrl || data.data?.audioUrl || data.data?.url || data.data?.audio_url || data.data?.speechUrl || data.data?.ttsUrl || data.data?.audioPath;
|
||
}
|
||
|
||
// 清理AI回复文本
|
||
const cleanedAiResponse = cleanText(aiResponse);
|
||
console.log('原始AI回复:', aiResponse);
|
||
console.log('清理后AI回复:', cleanedAiResponse);
|
||
|
||
resolve({
|
||
success: true,
|
||
data: {
|
||
userText: userText,
|
||
aiResponse: cleanedAiResponse,
|
||
audioUrl: audioUrl
|
||
}
|
||
});
|
||
} else {
|
||
resolve({
|
||
success: false,
|
||
error: { message: data.message || '语音对话失败' }
|
||
});
|
||
}
|
||
} catch (parseError) {
|
||
console.error('解析语音对话响应失败:', parseError);
|
||
resolve({
|
||
success: false,
|
||
error: { message: '解析响应失败' }
|
||
});
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('语音对话上传失败:', err);
|
||
resolve({
|
||
success: false,
|
||
error: err
|
||
});
|
||
}
|
||
});
|
||
});
|
||
} catch (error) {
|
||
console.error('语音对话失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 4. 音频文件上传语音对话(前端发送aac格式,后端转换为wav处理)
|
||
uploadVoiceChat: async (filePath, options = {}) => {
|
||
try {
|
||
const loginStatus = checkLoginStatus();
|
||
|
||
// 如果用户未登录,直接返回失败,让前端使用降级处理
|
||
if (!loginStatus.isLoggedIn) {
|
||
console.log('用户未登录,上传音频文件语音对话跳过API调用');
|
||
return {
|
||
success: false,
|
||
error: { message: '用户未登录,使用降级处理' }
|
||
};
|
||
}
|
||
|
||
console.log('开始上传音频文件语音对话,文件路径:', filePath);
|
||
console.log('注意:前端发送aac格式音频,后端需要转换为wav格式进行处理');
|
||
|
||
// 构建认证头
|
||
let authHeader = '';
|
||
if (loginStatus.token) {
|
||
authHeader = loginStatus.token.startsWith('Bearer ') ? loginStatus.token : 'Bearer ' + loginStatus.token;
|
||
}
|
||
|
||
return new Promise((resolve) => {
|
||
uni.uploadFile({
|
||
url: getApiUrl(API_CONFIG.ENDPOINTS.UPLOAD_VOICE_CHAT),
|
||
filePath: filePath,
|
||
name: 'audio',
|
||
header: authHeader ? {
|
||
'Authorization': authHeader
|
||
} : {},
|
||
formData: {
|
||
modelId: options.modelId || null,
|
||
templateId: options.templateId || null,
|
||
voiceStyle: options.voiceStyle || 'default'
|
||
},
|
||
success: (res) => {
|
||
console.log('上传音频文件语音对话成功:', res);
|
||
|
||
try {
|
||
const data = JSON.parse(res.data);
|
||
console.log('上传音频文件语音对话响应数据:', data);
|
||
|
||
if (data.code === 200) {
|
||
// 根据后端实际返回结构提取字段
|
||
let aiResponse = null;
|
||
let userText = null;
|
||
let audioUrl = null;
|
||
|
||
// 从 data.llmResult.response 提取AI回复
|
||
if (data.data && data.data.llmResult && data.data.llmResult.response) {
|
||
aiResponse = data.data.llmResult.response;
|
||
}
|
||
|
||
// 从 data.sttResult.text 提取用户文本(语音转文字)
|
||
if (data.data && data.data.sttResult && data.data.sttResult.text) {
|
||
userText = data.data.sttResult.text;
|
||
}
|
||
|
||
// 从 data.ttsResult.audioPath 提取音频路径
|
||
if (data.data && data.data.ttsResult && data.data.ttsResult.audioPath) {
|
||
audioUrl = data.data.ttsResult.audioPath;
|
||
}
|
||
|
||
// 备用字段提取(保持向后兼容)
|
||
if (!aiResponse) {
|
||
if (data.response && typeof data.response === 'string') {
|
||
aiResponse = data.response;
|
||
} else if (data.data && data.data.response) {
|
||
aiResponse = data.data.response;
|
||
}
|
||
}
|
||
|
||
if (!userText) {
|
||
userText = data.userText || data.data?.userText || data.data?.text || data.data?.user_text || data.data?.recognizedText || data.data?.transcription;
|
||
}
|
||
|
||
if (!audioUrl) {
|
||
audioUrl = data.audioPath || data.audioUrl || data.data?.audioUrl || data.data?.url || data.data?.audio_url || data.data?.speechUrl || data.data?.ttsUrl || data.data?.audioPath;
|
||
}
|
||
|
||
// 清理AI回复文本
|
||
const cleanedAiResponse = cleanText(aiResponse);
|
||
console.log('原始AI回复:', aiResponse);
|
||
console.log('清理后AI回复:', cleanedAiResponse);
|
||
|
||
resolve({
|
||
success: true,
|
||
data: {
|
||
userText: userText,
|
||
aiResponse: cleanedAiResponse,
|
||
audioUrl: audioUrl
|
||
}
|
||
});
|
||
} else {
|
||
resolve({
|
||
success: false,
|
||
error: { message: data.message || '上传音频文件语音对话失败' }
|
||
});
|
||
}
|
||
} catch (parseError) {
|
||
console.error('解析上传音频文件语音对话响应失败:', parseError);
|
||
resolve({
|
||
success: false,
|
||
error: { message: '解析响应失败' }
|
||
});
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('上传音频文件语音对话失败:', err);
|
||
resolve({
|
||
success: false,
|
||
error: err
|
||
});
|
||
}
|
||
});
|
||
});
|
||
} catch (error) {
|
||
console.error('上传音频文件语音对话失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 兼容性方法:语音识别(降级处理)
|
||
speechToText: async (filePath) => {
|
||
console.warn('speechToText 方法已废弃,请使用 voiceChat 或 uploadVoiceChat');
|
||
// 降级处理:使用语音对话接口
|
||
const result = await voiceAPI.voiceChat(filePath);
|
||
if (result.success) {
|
||
return {
|
||
success: true,
|
||
data: result.data.userText
|
||
};
|
||
}
|
||
return result;
|
||
}
|
||
};
|
||
|
||
// 充值相关API
|
||
export const rechargeAPI = {
|
||
// 获取用户余额
|
||
getUserBalance: async () => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.RECHARGE_BALANCE,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取用户余额失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 创建充值订单
|
||
createRechargeOrder: async (orderData) => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.RECHARGE_CREATE_ORDER,
|
||
method: 'POST',
|
||
data: {
|
||
amount: orderData.amount,
|
||
paymentMethod: orderData.paymentMethod || 'wechat',
|
||
bonusAmount: 0 // 已取消赠送服务
|
||
}
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('创建充值订单失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 查询订单状态
|
||
getOrderStatus: async (orderId) => {
|
||
try {
|
||
const response = await request({
|
||
url: `${API_CONFIG.ENDPOINTS.RECHARGE_ORDER_STATUS}/${orderId}`,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('查询订单状态失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 获取充值记录
|
||
getRechargeHistory: async (params = {}) => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.RECHARGE_HISTORY,
|
||
method: 'GET',
|
||
data: {
|
||
page: params.page || 1,
|
||
pageSize: params.pageSize || 10,
|
||
startDate: params.startDate,
|
||
endDate: params.endDate
|
||
}
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取充值记录失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
}
|
||
};
|
||
|
||
// 角色相关API
|
||
export const roleAPI = {
|
||
// 获取角色列表
|
||
getRoles: async () => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.ROLE_QUERY,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取角色列表失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 获取角色详情
|
||
getRoleById: async (roleId) => {
|
||
try {
|
||
const response = await request({
|
||
url: `${API_CONFIG.ENDPOINTS.ROLE_DETAIL}?roleId=${roleId}`,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取角色详情失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
}
|
||
};
|
||
|
||
// 配置相关API
|
||
export const configAPI = {
|
||
// 获取所有配置
|
||
getAllConfigs: async () => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.CONFIG_QUERY,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取所有配置失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 获取LLM模型配置
|
||
getModels: async () => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.CONFIG_MODELS,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取LLM模型配置失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 获取STT配置
|
||
getSTTConfigs: async () => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.CONFIG_STT,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取STT配置失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 获取模板配置
|
||
getTemplates: async () => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.CONFIG_TEMPLATES,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取模板配置失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
},
|
||
|
||
// 获取TTS配置
|
||
getTTSConfigs: async () => {
|
||
try {
|
||
const response = await request({
|
||
url: API_CONFIG.ENDPOINTS.CONFIG_TTS,
|
||
method: 'GET'
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
data: response
|
||
};
|
||
} catch (error) {
|
||
console.error('获取TTS配置失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error
|
||
};
|
||
}
|
||
}
|
||
};
|
||
|
||
// 导出默认请求方法
|
||
export default request;
|