Files
webUI/src/utils/api.js
2025-11-24 18:44:17 +08:00

1065 lines
31 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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')
// 去除图片标记 ![alt](url)
.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;