Files
ai_game/client/js/data/StoryManager.js

305 lines
7.0 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.

/**
* 故事数据管理器
*/
import { get, post, request } 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';
this.pathHistory = []; // 清空路径历史
}
/**
* 点赞故事
*/
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} 成功返回草稿ID失败返回 null
*/
async rewriteBranchAsync(storyId, prompt, userId) {
try {
// 先标记之前的未读草稿为已读,避免轮询弹出之前的通知
await this.markAllDraftsRead(userId);
const currentNode = this.getCurrentNode();
const result = await post(`/drafts`, {
userId: userId,
storyId: storyId,
currentNodeKey: this.currentNodeKey,
pathHistory: this.pathHistory,
currentContent: currentNode?.content || '',
prompt: prompt
}, { timeout: 30000 });
if (result && result.draftId) {
return result;
}
return null;
} catch (error) {
console.error('AI改写提交失败:', error?.errMsg || error?.message || JSON.stringify(error));
return null;
}
}
/**
* 获取用户草稿列表
*/
async getDrafts(userId) {
try {
const result = await get(`/drafts?userId=${userId}`);
return result || [];
} catch (error) {
console.error('获取草稿列表失败:', error);
return [];
}
}
/**
* 检查是否有新完成的草稿
*/
async checkNewDrafts(userId) {
try {
const result = await get(`/drafts/check-new?userId=${userId}`);
return result || { hasNew: false, count: 0, drafts: [] };
} catch (error) {
console.error('检查新草稿失败:', error);
return { hasNew: false, count: 0, drafts: [] };
}
}
/**
* 批量标记所有未读草稿为已读
*/
async markAllDraftsRead(userId) {
try {
await request({ url: `/drafts/batch-read?userId=${userId}`, method: 'PUT' });
return true;
} catch (error) {
console.error('批量标记已读失败:', error);
return false;
}
}
/**
* 获取草稿详情
*/
async getDraftDetail(draftId) {
try {
const result = await get(`/drafts/${draftId}`);
return result;
} catch (error) {
console.error('获取草稿详情失败:', error);
return null;
}
}
/**
* 删除草稿
*/
async deleteDraft(draftId, userId) {
try {
const result = await request({
url: `/drafts/${draftId}?userId=${userId}`,
method: 'DELETE'
});
return true;
} catch (error) {
console.error('删除草稿失败:', error);
return false;
}
}
/**
* 从草稿加载并播放 AI 生成的内容
*/
loadDraftContent(draft) {
if (!draft || !draft.aiNodes) return null;
// 将 AI 生成的节点合并到当前故事
if (this.currentStory) {
Object.assign(this.currentStory.nodes, draft.aiNodes);
this.currentNodeKey = draft.entryNodeKey || 'branch_1';
return this.getCurrentNode();
}
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;
}
}
}