# 全局世界书数据同步问题修复 ## 🐛 问题描述 **现象**: 当世界书文件被删除后,LocalStorage中仍然保存着该世界书的全局状态,导致前端显示一个不存在的"幽灵"世界书。 **原因**: `fetchWorldBooks` 函数从LocalStorage加载全局世界书后,没有清理那些已经不存在于后端的世界书。 --- ## ✅ 解决方案 ### 修复位置 **文件**: `frontend/src/Store/SideBarLeft/WorldBookSlice.jsx` **函数**: `fetchWorldBooks` (第132-165行) ### 修复逻辑 ```javascript // 从 LocalStorage 获取全局世界书列表 let globalBooks = loadGlobalWorldBooks(); // 清理 LocalStorage 中已不存在的世界书 const existingWorldBookNames = new Set(data.map(wb => wb.name)); const cleanedGlobalBooks = globalBooks.filter(wb => existingWorldBookNames.has(wb.name)); // 如果有被清理的项,更新 LocalStorage if (cleanedGlobalBooks.length !== globalBooks.length) { console.log(`清理了 ${globalBooks.length - cleanedGlobalBooks.length} 个不存在的全局世界书`); saveGlobalWorldBooks(cleanedGlobalBooks); globalBooks = cleanedGlobalBooks; } ``` ### 工作流程 1. **获取后端数据**: 调用 `GET /api/worldbooks/` 获取所有存在的世界书 2. **加载LocalStorage**: 从LocalStorage读取全局世界书列表 3. **对比清理**: 过滤掉LocalStorage中存在但后端不存在的世界书 4. **更新存储**: 如果发现有被清理的项,更新LocalStorage 5. **更新State**: 将清理后的列表设置到State中 --- ## 🧪 测试场景 ### 场景1: 正常情况 **步骤**: 1. 创建世界书A和B 2. 将A和B都设为全局 3. 刷新页面 **预期结果**: - ✅ 全局区域显示A和B - ✅ LocalStorage中有A和B ### 场景2: 文件被删除 **步骤**: 1. 创建世界书A和B 2. 将A和B都设为全局 3. 手动删除世界书A的文件(或其他方式删除) 4. 刷新页面 **预期结果**: - ✅ 全局区域只显示B - ✅ LocalStorage中只保留B - ✅ 控制台输出: "清理了 1 个不存在的全局世界书" ### 场景3: 通过UI删除 **步骤**: 1. 创建世界书A和B 2. 将A和B都设为全局 3. 在前端UI中删除世界书A 4. 刷新页面 **预期结果**: - ✅ 全局区域只显示B - ✅ LocalStorage中只保留B - ✅ 无错误信息 --- ## 📊 数据流图 ``` ┌─────────────────┐ │ 页面加载/切换 │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ fetchWorldBooks │ └────────┬────────┘ │ ├──► GET /api/worldbooks/ ──► 后端返回现有世界书列表 │ ├──► loadGlobalWorldBooks() ──► 从LocalStorage读取 │ ├──► 对比两个列表 │ ├─ 存在于LocalStorage但不存在于后端 → 清理 │ └─ 存在于两者 → 保留 │ ├──► saveGlobalWorldBooks() ──► 更新LocalStorage(如有变化) │ └──► set state ──► 更新UI ``` --- ## 🔍 关键代码说明 ### 1. 使用Set提高查找效率 ```javascript const existingWorldBookNames = new Set(data.map(wb => wb.name)); ``` - 将后端返回的世界书名称转换为Set - Set的查找时间复杂度为O(1),比数组的O(n)更高效 ### 2. 过滤清理 ```javascript const cleanedGlobalBooks = globalBooks.filter(wb => existingWorldBookNames.has(wb.name) ); ``` - 只保留那些在后端也存在的世界书 - 自动移除"幽灵"世界书 ### 3. 条件更新 ```javascript if (cleanedGlobalBooks.length !== globalBooks.length) { console.log(`清理了 ${globalBooks.length - cleanedGlobalBooks.length} 个不存在的全局世界书`); saveGlobalWorldBooks(cleanedGlobalBooks); globalBooks = cleanedGlobalBooks; } ``` - 只有在确实有变化时才更新LocalStorage - 避免不必要的写入操作 - 提供调试信息 --- ## ✨ 优势 1. **自动清理**: 无需手动干预,自动同步LocalStorage和后端数据 2. **性能优化**: 使用Set提高查找效率 3. **用户友好**: 静默清理,只在控制台输出日志 4. **数据一致性**: 确保LocalStorage中的数据始终与后端保持一致 5. **无副作用**: 不影响正常的业务流程 --- ## 📝 相关代码位置 ### LocalStorage操作函数 ```javascript // 辅助函数:从 LocalStorage 加载全局世界书 const loadGlobalWorldBooks = () => { try { const stored = localStorage.getItem(GLOBAL_WORLDBOOKS_KEY); return stored ? JSON.parse(stored) : []; } catch (error) { console.error('加载全局世界书失败:', error); return []; } }; // 辅助函数:保存全局世界书到 LocalStorage const saveGlobalWorldBooks = (globalBooks) => { try { localStorage.setItem(GLOBAL_WORLDBOOKS_KEY, JSON.stringify(globalBooks)); } catch (error) { console.error('保存全局世界书失败:', error); } }; ``` ### 删除世界书时的清理 ```javascript // deleteWorldBook 函数中已经有清理逻辑 deleteWorldBook: async (name) => { // ... const filteredGlobalBooks = state.globalWorldBooks.filter(wb => wb.name !== name); saveGlobalWorldBooks(filteredGlobalBooks); // ... } ``` --- ## 🎯 总结 ✅ **问题已修复**: 全局世界书现在会自动清理不存在的项 ✅ **数据同步**: LocalStorage与后端数据保持一致 ✅ **用户体验**: 不再显示"幽灵"世界书 ✅ **代码健壮**: 增加了数据一致性检查机制