前端重构实现
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* AI 服务模块
|
||||
* 封装 OpenRouter API 调用
|
||||
*/
|
||||
|
||||
const API_KEY = "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";
|
||||
const BASE_URL = "https://openrouter.ai/api/v1/chat/completions";
|
||||
|
||||
/**
|
||||
* 调用 AI API
|
||||
* @param {string} prompt - 用户提示
|
||||
* @param {string} systemMsg - 系统消息
|
||||
* @returns {Promise<string>} AI 响应内容
|
||||
*/
|
||||
const fetchAI = async (prompt, systemMsg) => {
|
||||
try {
|
||||
const response = await fetch(BASE_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${API_KEY}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "deepseek/deepseek-chat-v3-0324:free",
|
||||
messages: [
|
||||
{ role: "system", content: systemMsg },
|
||||
{ role: "user", content: prompt }
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return data.choices[0].message.content;
|
||||
} catch (error) {
|
||||
console.error('AI API Error:', error);
|
||||
return "(AI 暂时陷入了沉思,请稍后再试)";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 分析生命事件
|
||||
* @param {Object} event - 事件对象 { title, time, content }
|
||||
* @returns {Promise<string>} AI 分析反馈
|
||||
*/
|
||||
export const analyzeLifeEvent = async (event) => {
|
||||
const system = "你是一位温柔的生命引路人,擅长从平凡事件中发掘成长的力量。请分析用户记录的事件,提供情感价值、成长总结和疗愈鼓励。保持字数在150字左右。";
|
||||
const prompt = `事件标题:${event.title}\n时间:${event.time}\n内容:${event.content}`;
|
||||
return fetchAI(prompt, system);
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成爽文剧本
|
||||
* @param {Object} params - 参数对象 { theme, style, length, character }
|
||||
* @param {Array} events - 生命事件数组
|
||||
* @returns {Promise<string>} 生成的剧本内容
|
||||
*/
|
||||
export const generateEpicScript = async (params, events = []) => {
|
||||
const system = `你是一位金牌爽文编剧。根据用户的角色设定和过往经历,生成一段符合用户设定、充满爽感的未来人生剧本。剧本必须包含起承转合,使用【标题】标记段落。`;
|
||||
|
||||
const charInfo = `姓名:${params.character.nickname}, 性格:${params.character.mbti}, 兴趣:${params.character.hobbies?.join(',') || ''}, 星座:${params.character.zodiac}`;
|
||||
const eventSummary = events.map(e => e.title).join(', ');
|
||||
|
||||
const prompt = `角色信息:${charInfo}\n过往经历关键词:${eventSummary}\n用户指定主题:${params.theme}\n指定风格:${params.style}\n篇幅要求:${params.length}\n\n请以此创作一段热血、精彩的人生剧本。`;
|
||||
|
||||
return fetchAI(prompt, system);
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成实现路径
|
||||
* @param {string} script - 剧本内容
|
||||
* @returns {Promise<string>} 生成的路径内容
|
||||
*/
|
||||
export const generatePath = async (script) => {
|
||||
const system = "你是一位人生规划导师。请将用户生成的剧本拆解为现实中可操作的路径。使用【阶段名称】加上具体建议。务必客观、可执行。";
|
||||
return fetchAI(script, system);
|
||||
};
|
||||
|
||||
export default {
|
||||
analyzeLifeEvent,
|
||||
generateEpicScript,
|
||||
generatePath
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* API 配置
|
||||
* 创建 axios 实例并配置拦截器
|
||||
*/
|
||||
|
||||
// API 基础地址
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080';
|
||||
|
||||
/**
|
||||
* 创建 axios 实例
|
||||
*/
|
||||
const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 请求拦截器
|
||||
* 自动添加 token 到请求头
|
||||
*/
|
||||
api.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('access_token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 响应拦截器
|
||||
* 统一处理响应和错误
|
||||
*/
|
||||
api.interceptors.response.use(
|
||||
(response) => {
|
||||
const { data } = response;
|
||||
// 后端返回格式: { code, message, data }
|
||||
if (data.code === 200 || data.code === 0) {
|
||||
return data;
|
||||
}
|
||||
// 业务错误
|
||||
return Promise.reject(new Error(data.message || '请求失败'));
|
||||
},
|
||||
(error) => {
|
||||
// 网络错误或服务器错误
|
||||
if (error.response) {
|
||||
const { status, data } = error.response;
|
||||
if (status === 401) {
|
||||
// token 过期,清除登录状态
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
window.location.href = '/';
|
||||
}
|
||||
return Promise.reject(new Error(data?.message || `请求失败: ${status}`));
|
||||
}
|
||||
return Promise.reject(new Error(error.message || '网络错误'));
|
||||
}
|
||||
);
|
||||
|
||||
export default api;
|
||||
@@ -0,0 +1,131 @@
|
||||
import api from './api';
|
||||
|
||||
/**
|
||||
* 认证服务
|
||||
* 处理登录、注册、验证码等认证相关接口
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取短信验证码
|
||||
* @param {string} phone - 手机号
|
||||
* @returns {Promise<Object>} 验证码响应
|
||||
*/
|
||||
export const getSmsCode = async (phone) => {
|
||||
const response = await api.get('/auth/sms-code', {
|
||||
params: { phone }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户登录(手机号 + 验证码)
|
||||
* @param {Object} params - 登录参数
|
||||
* @param {string} params.phone - 手机号
|
||||
* @param {string} params.smsCode - 短信验证码
|
||||
* @returns {Promise<Object>} 登录响应(包含 token)
|
||||
*/
|
||||
export const login = async ({ phone, smsCode }) => {
|
||||
const response = await api.post('/auth/login', {
|
||||
phone,
|
||||
smsCode
|
||||
});
|
||||
|
||||
// 保存 token
|
||||
if (response.data) {
|
||||
const { accessToken, refreshToken } = response.data;
|
||||
if (accessToken) {
|
||||
localStorage.setItem('access_token', accessToken);
|
||||
}
|
||||
if (refreshToken) {
|
||||
localStorage.setItem('refresh_token', refreshToken);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const logout = async () => {
|
||||
try {
|
||||
await api.post('/auth/logout');
|
||||
} finally {
|
||||
// 无论成功失败都清除本地 token
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新 token
|
||||
* @returns {Promise<Object>} 新的 token
|
||||
*/
|
||||
export const refreshToken = async () => {
|
||||
const refreshToken = localStorage.getItem('refresh_token');
|
||||
if (!refreshToken) {
|
||||
throw new Error('No refresh token');
|
||||
}
|
||||
|
||||
const response = await api.post('/auth/refreshToken', {
|
||||
refreshToken
|
||||
});
|
||||
|
||||
// 更新 token
|
||||
if (response.data) {
|
||||
const { accessToken, refreshToken: newRefreshToken } = response.data;
|
||||
if (accessToken) {
|
||||
localStorage.setItem('access_token', accessToken);
|
||||
}
|
||||
if (newRefreshToken) {
|
||||
localStorage.setItem('refresh_token', newRefreshToken);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 验证 token 是否有效
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export const validateToken = async () => {
|
||||
try {
|
||||
const response = await api.get('/auth/validateToken');
|
||||
return response.data === true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
* @returns {Promise<Object>} 用户信息
|
||||
*/
|
||||
export const getCurrentUserInfo = async () => {
|
||||
const response = await api.get('/auth/userInfo');
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查手机号是否已注册
|
||||
* @param {string} phone - 手机号
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export const checkPhone = async (phone) => {
|
||||
const response = await api.get('/auth/checkPhone', {
|
||||
params: { phone }
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getSmsCode,
|
||||
login,
|
||||
logout,
|
||||
refreshToken,
|
||||
validateToken,
|
||||
getCurrentUserInfo,
|
||||
checkPhone
|
||||
};
|
||||
@@ -0,0 +1,215 @@
|
||||
import api from './api';
|
||||
|
||||
/**
|
||||
* 爽文剧本服务
|
||||
* 处理剧本的增删改查
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取当前用户的所有剧本
|
||||
* @returns {Promise<Array>} 剧本列表
|
||||
*/
|
||||
export const getScriptList = async () => {
|
||||
const response = await api.get('/epicScript/listAll');
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页获取剧本
|
||||
* @param {Object} params - 分页参数
|
||||
* @param {number} params.pageNum - 页码
|
||||
* @param {number} params.pageSize - 每页数量
|
||||
* @returns {Promise<Object>} 分页结果
|
||||
*/
|
||||
export const getScriptPage = async ({ pageNum = 1, pageSize = 10 }) => {
|
||||
const response = await api.get('/epicScript/page', {
|
||||
params: { pageNum, pageSize }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取剧本详情
|
||||
* @param {string} id - 剧本ID
|
||||
* @returns {Promise<Object>} 剧本详情
|
||||
*/
|
||||
export const getScriptById = async (id) => {
|
||||
const response = await api.get('/epicScript/detail', {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建剧本
|
||||
* @param {Object} scriptData - 剧本数据
|
||||
* @returns {Promise<Object>} 创建的剧本
|
||||
*/
|
||||
export const createScript = async (scriptData) => {
|
||||
const requestData = transformToBackendFormat(scriptData);
|
||||
const response = await api.post('/epicScript/create', requestData);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新剧本
|
||||
* @param {Object} scriptData - 剧本数据(必须包含 id)
|
||||
* @returns {Promise<Object>} 更新后的剧本
|
||||
*/
|
||||
export const updateScript = async (scriptData) => {
|
||||
const requestData = transformToBackendFormat(scriptData);
|
||||
const response = await api.put('/epicScript/update', requestData);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 选中剧本
|
||||
* @param {string} id - 剧本ID
|
||||
* @returns {Promise<Object>} 选中的剧本
|
||||
*/
|
||||
export const selectScript = async (id) => {
|
||||
const response = await api.put('/epicScript/select', null, {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除剧本
|
||||
* @param {string} id - 剧本ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const deleteScript = async (id) => {
|
||||
const response = await api.delete('/epicScript/delete', {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 将前端数据格式转换为后端格式
|
||||
* @param {Object} frontendData - 前端数据
|
||||
* @returns {Object} 后端格式数据
|
||||
*/
|
||||
const transformToBackendFormat = (frontendData) => {
|
||||
const {
|
||||
id,
|
||||
theme,
|
||||
style,
|
||||
length,
|
||||
content,
|
||||
isSelected
|
||||
} = frontendData;
|
||||
|
||||
// 解析内容生成标题和各部分
|
||||
let title = theme || '我的剧本';
|
||||
let plotIntro = '';
|
||||
let plotTurning = '';
|
||||
let plotClimax = '';
|
||||
let plotEnding = '';
|
||||
|
||||
if (content) {
|
||||
// 尝试从内容中提取各部分
|
||||
const sections = content.split(/【[^】]+】/);
|
||||
const titles = content.match(/【[^】]+】/g) || [];
|
||||
|
||||
titles.forEach((t, index) => {
|
||||
const sectionContent = sections[index + 1]?.trim() || '';
|
||||
if (t.includes('序幕') || t.includes('低谷')) {
|
||||
plotIntro = sectionContent;
|
||||
} else if (t.includes('转折') || t.includes('契机')) {
|
||||
plotTurning = sectionContent;
|
||||
} else if (t.includes('高潮') || t.includes('抉择')) {
|
||||
plotClimax = sectionContent;
|
||||
} else if (t.includes('结局') || t.includes('开始')) {
|
||||
plotEnding = sectionContent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
theme,
|
||||
style,
|
||||
length,
|
||||
plotIntro,
|
||||
plotTurning,
|
||||
plotClimax,
|
||||
plotEnding,
|
||||
plotJson: content ? { fullContent: content } : null,
|
||||
isSelected
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 将后端数据格式转换为前端格式
|
||||
* @param {Object} backendData - 后端数据
|
||||
* @returns {Object} 前端格式数据
|
||||
*/
|
||||
export const transformToFrontendFormat = (backendData) => {
|
||||
if (!backendData) return null;
|
||||
|
||||
const {
|
||||
id,
|
||||
userId,
|
||||
title,
|
||||
theme,
|
||||
style,
|
||||
length,
|
||||
plotIntro,
|
||||
plotTurning,
|
||||
plotClimax,
|
||||
plotEnding,
|
||||
plotJson,
|
||||
isSelected,
|
||||
createTime
|
||||
} = backendData;
|
||||
|
||||
// 重建完整内容
|
||||
let content = '';
|
||||
if (plotJson?.fullContent) {
|
||||
content = plotJson.fullContent;
|
||||
} else {
|
||||
const parts = [];
|
||||
if (plotIntro) parts.push(`【序幕:低谷回响】\n${plotIntro}`);
|
||||
if (plotTurning) parts.push(`【转折:契机出现】\n${plotTurning}`);
|
||||
if (plotClimax) parts.push(`【高潮:命运抉择】\n${plotClimax}`);
|
||||
if (plotEnding) parts.push(`【结局:新的开始】\n${plotEnding}`);
|
||||
content = parts.join('\n\n');
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
userId,
|
||||
title: title || theme || '未命名剧本',
|
||||
theme: theme || '',
|
||||
style: style || '',
|
||||
length: length || 'medium',
|
||||
content,
|
||||
isSelected: isSelected || false,
|
||||
date: createTime ? new Date(createTime).toLocaleDateString() : new Date().toLocaleDateString()
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量转换后端数据为前端格式
|
||||
* @param {Array} backendList - 后端数据列表
|
||||
* @returns {Array} 前端格式数据列表
|
||||
*/
|
||||
export const transformListToFrontend = (backendList) => {
|
||||
if (!Array.isArray(backendList)) return [];
|
||||
return backendList.map(transformToFrontendFormat);
|
||||
};
|
||||
|
||||
export default {
|
||||
getScriptList,
|
||||
getScriptPage,
|
||||
getScriptById,
|
||||
createScript,
|
||||
updateScript,
|
||||
selectScript,
|
||||
deleteScript,
|
||||
transformToFrontendFormat,
|
||||
transformListToFrontend
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 服务层统一导出
|
||||
*/
|
||||
export { default as api } from './api';
|
||||
export { default as authService } from './auth';
|
||||
export { default as userProfileService } from './userProfile';
|
||||
export { default as lifeEventService } from './lifeEvent';
|
||||
export { default as epicScriptService } from './epicScript';
|
||||
export { default as lifePathService } from './lifePath';
|
||||
export { default as aiService } from './ai';
|
||||
|
||||
// 导出各服务的具体方法
|
||||
export * from './auth';
|
||||
export * from './userProfile';
|
||||
export * from './lifeEvent';
|
||||
export * from './epicScript';
|
||||
export * from './lifePath';
|
||||
export * from './ai';
|
||||
@@ -0,0 +1,164 @@
|
||||
import api from './api';
|
||||
|
||||
/**
|
||||
* 生命事件服务
|
||||
* 处理生命事件的增删改查
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取当前用户的所有生命事件
|
||||
* @returns {Promise<Array>} 生命事件列表
|
||||
*/
|
||||
export const getEventList = async () => {
|
||||
const response = await api.get('/lifeEvent/list');
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页获取生命事件
|
||||
* @param {Object} params - 分页参数
|
||||
* @param {number} params.pageNum - 页码
|
||||
* @param {number} params.pageSize - 每页数量
|
||||
* @returns {Promise<Object>} 分页结果
|
||||
*/
|
||||
export const getEventPage = async ({ pageNum = 1, pageSize = 10 }) => {
|
||||
const response = await api.get('/lifeEvent/page', {
|
||||
params: { pageNum, pageSize }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取生命事件详情
|
||||
* @param {string} id - 事件ID
|
||||
* @returns {Promise<Object>} 事件详情
|
||||
*/
|
||||
export const getEventById = async (id) => {
|
||||
const response = await api.get('/lifeEvent/detail', {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建生命事件
|
||||
* @param {Object} eventData - 事件数据
|
||||
* @returns {Promise<Object>} 创建的事件
|
||||
*/
|
||||
export const createEvent = async (eventData) => {
|
||||
const requestData = transformToBackendFormat(eventData);
|
||||
const response = await api.post('/lifeEvent/create', requestData);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新生命事件
|
||||
* @param {Object} eventData - 事件数据(必须包含 id)
|
||||
* @returns {Promise<Object>} 更新后的事件
|
||||
*/
|
||||
export const updateEvent = async (eventData) => {
|
||||
const requestData = transformToBackendFormat(eventData);
|
||||
const response = await api.put('/lifeEvent/update', requestData);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除生命事件
|
||||
* @param {string} id - 事件ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const deleteEvent = async (id) => {
|
||||
const response = await api.delete('/lifeEvent/delete', {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 将前端数据格式转换为后端格式
|
||||
* @param {Object} frontendData - 前端数据
|
||||
* @returns {Object} 后端格式数据
|
||||
*/
|
||||
const transformToBackendFormat = (frontendData) => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
time,
|
||||
content,
|
||||
aiFeedback,
|
||||
eventType = 'daily_log',
|
||||
emotionType,
|
||||
emotionScore,
|
||||
tags
|
||||
} = frontendData;
|
||||
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
eventDate: time,
|
||||
content,
|
||||
aiReply: aiFeedback,
|
||||
eventType,
|
||||
emotionType,
|
||||
emotionScore,
|
||||
tags
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 将后端数据格式转换为前端格式
|
||||
* @param {Object} backendData - 后端数据
|
||||
* @returns {Object} 前端格式数据
|
||||
*/
|
||||
export const transformToFrontendFormat = (backendData) => {
|
||||
if (!backendData) return null;
|
||||
|
||||
const {
|
||||
id,
|
||||
userId,
|
||||
title,
|
||||
eventDate,
|
||||
content,
|
||||
aiReply,
|
||||
eventType,
|
||||
emotionType,
|
||||
emotionScore,
|
||||
tags,
|
||||
createTime
|
||||
} = backendData;
|
||||
|
||||
return {
|
||||
id,
|
||||
userId,
|
||||
title: title || '',
|
||||
time: eventDate || '',
|
||||
content: content || '',
|
||||
aiFeedback: aiReply || '',
|
||||
eventType: eventType || 'daily_log',
|
||||
emotionType,
|
||||
emotionScore,
|
||||
tags: tags || [],
|
||||
createTime
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量转换后端数据为前端格式
|
||||
* @param {Array} backendList - 后端数据列表
|
||||
* @returns {Array} 前端格式数据列表
|
||||
*/
|
||||
export const transformListToFrontend = (backendList) => {
|
||||
if (!Array.isArray(backendList)) return [];
|
||||
return backendList.map(transformToFrontendFormat);
|
||||
};
|
||||
|
||||
export default {
|
||||
getEventList,
|
||||
getEventPage,
|
||||
getEventById,
|
||||
createEvent,
|
||||
updateEvent,
|
||||
deleteEvent,
|
||||
transformToFrontendFormat,
|
||||
transformListToFrontend
|
||||
};
|
||||
@@ -0,0 +1,211 @@
|
||||
import api from './api';
|
||||
|
||||
/**
|
||||
* 实现路径服务
|
||||
* 处理路径的增删改查
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取当前用户的所有路径
|
||||
* @returns {Promise<Array>} 路径列表
|
||||
*/
|
||||
export const getPathList = async () => {
|
||||
const response = await api.get('/lifePath/listAll');
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页获取路径
|
||||
* @param {Object} params - 分页参数
|
||||
* @param {number} params.pageNum - 页码
|
||||
* @param {number} params.pageSize - 每页数量
|
||||
* @returns {Promise<Object>} 分页结果
|
||||
*/
|
||||
export const getPathPage = async ({ pageNum = 1, pageSize = 10 }) => {
|
||||
const response = await api.get('/lifePath/page', {
|
||||
params: { pageNum, pageSize }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据剧本ID获取路径
|
||||
* @param {string} scriptId - 剧本ID
|
||||
* @returns {Promise<Object>} 路径详情
|
||||
*/
|
||||
export const getPathByScriptId = async (scriptId) => {
|
||||
const response = await api.get('/lifePath/byScript', {
|
||||
params: { scriptId }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取路径详情
|
||||
* @param {string} id - 路径ID
|
||||
* @returns {Promise<Object>} 路径详情
|
||||
*/
|
||||
export const getPathById = async (id) => {
|
||||
const response = await api.get('/lifePath/detail', {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建路径
|
||||
* @param {Object} pathData - 路径数据
|
||||
* @returns {Promise<Object>} 创建的路径
|
||||
*/
|
||||
export const createPath = async (pathData) => {
|
||||
const requestData = transformToBackendFormat(pathData);
|
||||
const response = await api.post('/lifePath/create', requestData);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新路径
|
||||
* @param {Object} pathData - 路径数据(必须包含 id)
|
||||
* @returns {Promise<Object>} 更新后的路径
|
||||
*/
|
||||
export const updatePath = async (pathData) => {
|
||||
const requestData = transformToBackendFormat(pathData);
|
||||
const response = await api.put('/lifePath/update', requestData);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除路径
|
||||
* @param {string} id - 路径ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const deletePath = async (id) => {
|
||||
const response = await api.delete('/lifePath/delete', {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 将前端数据格式转换为后端格式
|
||||
* @param {Object} frontendData - 前端数据
|
||||
* @returns {Object} 后端格式数据
|
||||
*/
|
||||
const transformToBackendFormat = (frontendData) => {
|
||||
const {
|
||||
id,
|
||||
scriptId,
|
||||
title,
|
||||
description,
|
||||
content,
|
||||
status = 'active',
|
||||
progress = 0
|
||||
} = frontendData;
|
||||
|
||||
// 解析内容为步骤列表
|
||||
let steps = [];
|
||||
if (content) {
|
||||
// 尝试解析文本内容为步骤
|
||||
const stepMatches = content.match(/(\d+)\.\s*([^::]+)[::]\s*([^\n]+)/g);
|
||||
if (stepMatches) {
|
||||
steps = stepMatches.map((match, index) => {
|
||||
const parts = match.match(/(\d+)\.\s*([^::]+)[::]\s*(.+)/);
|
||||
return {
|
||||
phase: `阶段${index + 1}`,
|
||||
time: parts?.[2]?.trim() || '',
|
||||
content: parts?.[3]?.trim() || match,
|
||||
action: '',
|
||||
resources: '',
|
||||
habit: ''
|
||||
};
|
||||
});
|
||||
} else {
|
||||
// 按换行分割
|
||||
const lines = content.split('\n').filter(line => line.trim());
|
||||
steps = lines.map((line, index) => ({
|
||||
phase: `阶段${index + 1}`,
|
||||
time: '',
|
||||
content: line.trim(),
|
||||
action: '',
|
||||
resources: '',
|
||||
habit: ''
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
scriptId,
|
||||
title: title || '实现路径',
|
||||
description,
|
||||
steps,
|
||||
status,
|
||||
progress
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 将后端数据格式转换为前端格式
|
||||
* @param {Object} backendData - 后端数据
|
||||
* @returns {Object} 前端格式数据
|
||||
*/
|
||||
export const transformToFrontendFormat = (backendData) => {
|
||||
if (!backendData) return null;
|
||||
|
||||
const {
|
||||
id,
|
||||
userId,
|
||||
scriptId,
|
||||
title,
|
||||
description,
|
||||
steps,
|
||||
status,
|
||||
progress,
|
||||
createTime
|
||||
} = backendData;
|
||||
|
||||
// 将步骤列表转换为文本内容
|
||||
let content = '';
|
||||
if (Array.isArray(steps) && steps.length > 0) {
|
||||
content = steps.map((step, index) => {
|
||||
const phase = step.phase || `阶段${index + 1}`;
|
||||
const time = step.time ? `(${step.time})` : '';
|
||||
return `${index + 1}. ${phase}${time}:${step.content || ''}`;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
userId,
|
||||
scriptId,
|
||||
title: title || '实现路径',
|
||||
description: description || '',
|
||||
content,
|
||||
steps: steps || [],
|
||||
status: status || 'active',
|
||||
progress: progress || 0,
|
||||
createTime
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量转换后端数据为前端格式
|
||||
* @param {Array} backendList - 后端数据列表
|
||||
* @returns {Array} 前端格式数据列表
|
||||
*/
|
||||
export const transformListToFrontend = (backendList) => {
|
||||
if (!Array.isArray(backendList)) return [];
|
||||
return backendList.map(transformToFrontendFormat);
|
||||
};
|
||||
|
||||
export default {
|
||||
getPathList,
|
||||
getPathPage,
|
||||
getPathByScriptId,
|
||||
getPathById,
|
||||
createPath,
|
||||
updatePath,
|
||||
deletePath,
|
||||
transformToFrontendFormat,
|
||||
transformListToFrontend
|
||||
};
|
||||
@@ -0,0 +1,172 @@
|
||||
import api from './api';
|
||||
|
||||
/**
|
||||
* 用户档案服务
|
||||
* 处理用户档案的增删改查
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取当前用户档案
|
||||
* @returns {Promise<Object|null>} 用户档案
|
||||
*/
|
||||
export const getCurrentProfile = async () => {
|
||||
const response = await api.get('/user-profile/me');
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建用户档案
|
||||
* @param {Object} profileData - 档案数据
|
||||
* @returns {Promise<Object>} 创建的档案
|
||||
*/
|
||||
export const createProfile = async (profileData) => {
|
||||
// 转换前端数据格式为后端格式
|
||||
const requestData = transformToBackendFormat(profileData);
|
||||
const response = await api.post('/user-profile/create', requestData);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新用户档案
|
||||
* @param {Object} profileData - 档案数据(必须包含 id)
|
||||
* @returns {Promise<Object>} 更新后的档案
|
||||
*/
|
||||
export const updateProfile = async (profileData) => {
|
||||
const requestData = transformToBackendFormat(profileData);
|
||||
const response = await api.put('/user-profile/update', requestData);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除用户档案
|
||||
* @param {string} id - 档案ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const deleteProfile = async (id) => {
|
||||
const response = await api.delete('/user-profile/delete', {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取档案详情
|
||||
* @param {string} id - 档案ID
|
||||
* @returns {Promise<Object>} 档案详情
|
||||
*/
|
||||
export const getProfileById = async (id) => {
|
||||
const response = await api.get('/user-profile/detail', {
|
||||
params: { id }
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* 将前端数据格式转换为后端格式
|
||||
* @param {Object} frontendData - 前端数据
|
||||
* @returns {Object} 后端格式数据
|
||||
*/
|
||||
const transformToBackendFormat = (frontendData) => {
|
||||
const {
|
||||
id,
|
||||
nickname,
|
||||
gender,
|
||||
zodiac,
|
||||
mbti,
|
||||
hobbies,
|
||||
childhood,
|
||||
joy,
|
||||
low,
|
||||
future
|
||||
} = frontendData;
|
||||
|
||||
return {
|
||||
id,
|
||||
nickname,
|
||||
gender,
|
||||
zodiac,
|
||||
mbti,
|
||||
// 兴趣爱好转为 JSON 字符串
|
||||
hobbies: Array.isArray(hobbies) ? JSON.stringify(hobbies) : hobbies,
|
||||
// 童年经历
|
||||
childhoodDate: childhood?.date || null,
|
||||
childhoodContent: childhood?.text || null,
|
||||
// 高光时刻(对应前端的 joy)
|
||||
peakDate: joy?.date || null,
|
||||
peakContent: joy?.text || null,
|
||||
// 低谷时期(对应前端的 low)
|
||||
valleyDate: low?.date || null,
|
||||
valleyContent: low?.text || null,
|
||||
// 未来期许
|
||||
futureVision: future?.vision || null,
|
||||
// 理想生活状态
|
||||
idealLife: future?.ideal || null
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 将后端数据格式转换为前端格式
|
||||
* @param {Object} backendData - 后端数据
|
||||
* @returns {Object} 前端格式数据
|
||||
*/
|
||||
export const transformToFrontendFormat = (backendData) => {
|
||||
if (!backendData) return null;
|
||||
|
||||
const {
|
||||
id,
|
||||
userId,
|
||||
nickname,
|
||||
gender,
|
||||
zodiac,
|
||||
mbti,
|
||||
hobbies,
|
||||
childhoodDate,
|
||||
childhoodContent,
|
||||
peakDate,
|
||||
peakContent,
|
||||
valleyDate,
|
||||
valleyContent,
|
||||
futureVision,
|
||||
idealLife
|
||||
} = backendData;
|
||||
|
||||
return {
|
||||
id,
|
||||
userId,
|
||||
nickname: nickname || '',
|
||||
gender: gender || '',
|
||||
zodiac: zodiac || '',
|
||||
mbti: mbti || '',
|
||||
// 兴趣爱好从 JSON 字符串解析
|
||||
hobbies: hobbies ? (typeof hobbies === 'string' ? JSON.parse(hobbies) : hobbies) : [],
|
||||
// 童年经历
|
||||
childhood: {
|
||||
date: childhoodDate || '',
|
||||
text: childhoodContent || ''
|
||||
},
|
||||
// 高光时刻
|
||||
joy: {
|
||||
date: peakDate || '',
|
||||
text: peakContent || ''
|
||||
},
|
||||
// 低谷时期
|
||||
low: {
|
||||
date: valleyDate || '',
|
||||
text: valleyContent || ''
|
||||
},
|
||||
// 未来期许
|
||||
future: {
|
||||
vision: futureVision || '',
|
||||
ideal: idealLife || ''
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
getCurrentProfile,
|
||||
createProfile,
|
||||
updateProfile,
|
||||
deleteProfile,
|
||||
getProfileById,
|
||||
transformToFrontendFormat
|
||||
};
|
||||
Reference in New Issue
Block a user