225 lines
5.4 KiB
JavaScript
225 lines
5.4 KiB
JavaScript
/**
|
||
* 故事数据管理器
|
||
*/
|
||
import { get, post } from '../utils/http';
|
||
|
||
export default class StoryManager {
|
||
constructor() {
|
||
this.storyList = [];
|
||
this.currentStory = null;
|
||
this.currentNodeKey = 'start';
|
||
this.categories = [];
|
||
this.pathHistory = []; // 记录用户走过的路径
|
||
}
|
||
|
||
/**
|
||
* 加载故事列表
|
||
*/
|
||
async loadStoryList(options = {}) {
|
||
try {
|
||
this.storyList = await get('/stories', options);
|
||
return this.storyList;
|
||
} catch (error) {
|
||
console.error('加载故事列表失败:', error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载热门故事
|
||
*/
|
||
async loadHotStories(limit = 10) {
|
||
try {
|
||
return await get('/stories/hot', { limit });
|
||
} catch (error) {
|
||
console.error('加载热门故事失败:', error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载分类列表
|
||
*/
|
||
async loadCategories() {
|
||
try {
|
||
this.categories = await get('/stories/categories');
|
||
return this.categories;
|
||
} catch (error) {
|
||
console.error('加载分类失败:', error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载故事详情
|
||
*/
|
||
async loadStoryDetail(storyId) {
|
||
try {
|
||
this.currentStory = await get(`/stories/${storyId}`);
|
||
this.currentNodeKey = 'start';
|
||
this.pathHistory = []; // 重置路径历史
|
||
|
||
// 记录游玩次数
|
||
await post(`/stories/${storyId}/play`);
|
||
|
||
return this.currentStory;
|
||
} catch (error) {
|
||
console.error('加载故事详情失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前节点
|
||
*/
|
||
getCurrentNode() {
|
||
if (!this.currentStory || !this.currentStory.nodes) return null;
|
||
return this.currentStory.nodes[this.currentNodeKey];
|
||
}
|
||
|
||
/**
|
||
* 选择选项,前进到下一个节点
|
||
*/
|
||
selectChoice(choiceIndex) {
|
||
const currentNode = this.getCurrentNode();
|
||
if (!currentNode || !currentNode.choices || !currentNode.choices[choiceIndex]) {
|
||
return null;
|
||
}
|
||
|
||
const choice = currentNode.choices[choiceIndex];
|
||
|
||
// 记录路径历史
|
||
this.pathHistory.push({
|
||
nodeKey: this.currentNodeKey,
|
||
content: currentNode.content,
|
||
choice: choice.text
|
||
});
|
||
|
||
this.currentNodeKey = choice.nextNodeKey;
|
||
|
||
return this.getCurrentNode();
|
||
}
|
||
|
||
/**
|
||
* 检查当前节点是否为结局
|
||
*/
|
||
isEnding() {
|
||
const currentNode = this.getCurrentNode();
|
||
return currentNode && currentNode.is_ending;
|
||
}
|
||
|
||
/**
|
||
* 获取结局信息
|
||
*/
|
||
getEndingInfo() {
|
||
const currentNode = this.getCurrentNode();
|
||
if (!currentNode || !currentNode.is_ending) return null;
|
||
|
||
return {
|
||
name: currentNode.ending_name,
|
||
score: currentNode.ending_score,
|
||
type: currentNode.ending_type,
|
||
content: currentNode.content
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 重置故事进度
|
||
*/
|
||
resetStory() {
|
||
this.currentNodeKey = 'start';
|
||
}
|
||
|
||
/**
|
||
* 点赞故事
|
||
*/
|
||
async likeStory(like = true) {
|
||
if (!this.currentStory) return;
|
||
await post(`/stories/${this.currentStory.id}/like`, { like });
|
||
}
|
||
|
||
/**
|
||
* AI改写结局
|
||
*/
|
||
async rewriteEnding(storyId, ending, prompt) {
|
||
try {
|
||
const result = await post(`/stories/${storyId}/rewrite`, {
|
||
ending_name: ending?.name,
|
||
ending_content: ending?.content,
|
||
prompt: prompt
|
||
}, { timeout: 60000 });
|
||
return result;
|
||
} catch (error) {
|
||
console.error('AI改写失败:', error?.errMsg || error?.message || JSON.stringify(error));
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* AI改写中间章节,生成新的剧情分支
|
||
* @returns {Object|null} 成功返回新节点,失败返回 null(不改变当前状态)
|
||
*/
|
||
async rewriteBranch(storyId, prompt, userId) {
|
||
try {
|
||
const currentNode = this.getCurrentNode();
|
||
const result = await post(`/stories/${storyId}/rewrite-branch`, {
|
||
userId: userId,
|
||
currentNodeKey: this.currentNodeKey,
|
||
pathHistory: this.pathHistory,
|
||
currentContent: currentNode?.content || '',
|
||
prompt: prompt
|
||
}, { timeout: 300000 }); // 5分钟超时,AI生成需要较长时间
|
||
|
||
// 检查是否有有效的 nodes
|
||
if (result && result.nodes) {
|
||
// AI 成功,将新分支合并到当前故事中
|
||
Object.assign(this.currentStory.nodes, result.nodes);
|
||
// 跳转到新分支的入口节点
|
||
this.currentNodeKey = result.entryNodeKey || 'branch_1';
|
||
return this.getCurrentNode();
|
||
}
|
||
|
||
// AI 失败,返回 null
|
||
console.log('AI服务不可用:', result?.error || '未知错误');
|
||
return null;
|
||
} catch (error) {
|
||
console.error('AI改写分支失败:', error?.errMsg || error?.message || JSON.stringify(error));
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* AI续写故事
|
||
*/
|
||
async continueStory(storyId, prompt) {
|
||
try {
|
||
const result = await post(`/stories/${storyId}/continue`, {
|
||
current_node_key: this.currentNodeKey,
|
||
prompt: prompt
|
||
});
|
||
return result;
|
||
} catch (error) {
|
||
console.error('AI续写失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* AI创作新故事
|
||
*/
|
||
async createStory(params) {
|
||
try {
|
||
const result = await post('/stories/ai-create', {
|
||
genre: params.genre,
|
||
keywords: params.keywords,
|
||
protagonist: params.protagonist,
|
||
conflict: params.conflict
|
||
});
|
||
return result;
|
||
} catch (error) {
|
||
console.error('AI创作失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
}
|