# PreferenceService架构优化 ## 📋 优化背景 在存储键架构重构过程中,发现了一个重要的架构不一致性问题: ### 问题描述 用户提出了一个关键问题:**"为什么exportAllData的时候要对preferenceService特别处理呢?preferenceService直接提供一个获取所有数据的接口不就好了?其他几个manager都是这样的"** ### 架构不一致性分析 #### 其他Manager的统一模式 ```typescript // 所有其他服务都提供批量获取接口 const models = await this.modelManager.getAllModels(); const userTemplates = await this.templateManager.listTemplates(); const history = await this.historyManager.getAllRecords(); ``` #### PreferenceService的特殊处理(问题) ```typescript // ❌ 原有的特殊处理方式 for (const key of PREFERENCE_BASED_KEYS) { const value = await this.preferenceService.get(key, null); if (value !== null) { userSettings[key] = String(value); } } ``` ## 🎯 优化方案 ### 1. 添加批量获取接口 为PreferenceService添加`getAll()`方法,保持与其他Manager的接口一致性: ```typescript export interface IPreferenceService { // 现有方法... /** * 获取所有偏好设置 * @returns 包含所有偏好设置的键值对对象 */ getAll(): Promise>; } ``` ### 2. 实现批量获取逻辑 ```typescript async getAll(): Promise> { try { const allKeys = await this.keys(); const result: Record = {}; for (const key of allKeys) { try { const value = await this.get(key, null); if (value !== null) { result[key] = String(value); } } catch (error) { console.warn(`Failed to get preference for key "${key}":`, error); // 继续处理其他键,不因单个键失败而中断 } } return result; } catch (error) { console.error('Error getting all preferences:', error); throw new Error(`Failed to get all preferences: ${error}`); } } ``` ### 3. 简化DataManager导出逻辑 ```typescript // ✅ 优化后的统一处理方式 async exportAllData(): Promise { // 获取所有偏好设置(统一接口) const userSettings = await this.preferenceService.getAll(); // 获取其他数据(统一接口) const models = await this.modelManager.getAllModels(); const userTemplates = await this.templateManager.listTemplates(); const history = await this.historyManager.getAllRecords(); return { version: 1, data: { userSettings, models, userTemplates, history } }; } ``` ## 📊 优化效果 ### 架构一致性 所有服务现在都遵循相同的接口模式: | 服务 | 批量获取方法 | 返回类型 | |------|-------------|----------| | ModelManager | `getAllModels()` | `ModelConfig[]` | | TemplateManager | `listTemplates()` | `Template[]` | | HistoryManager | `getAllRecords()` | `PromptRecord[]` | | **PreferenceService** | **`getAll()`** | **`Record`** | ### 代码简化 - **移除了存储键分类常量** - 不再需要`PREFERENCE_BASED_KEYS`和`DIRECT_STORAGE_KEYS` - **简化了DataManager逻辑** - 从复杂的分类处理变为统一的批量调用 - **减少了维护成本** - 新增偏好设置不需要更新DataManager ### 性能提升 - **减少异步调用次数** - 从多次`get()`调用变为一次`getAll()`调用 - **批量处理更高效** - 一次性获取所有数据,减少存储访问次数 - **错误处理更健壮** - 单个键失败不影响其他键的获取 ## 🔧 实现细节 ### 错误处理策略 ```typescript // 健壮的错误处理:单个键失败不影响整体 for (const key of allKeys) { try { const value = await this.get(key, null); if (value !== null) { result[key] = String(value); } } catch (error) { console.warn(`Failed to get preference for key "${key}":`, error); // 继续处理其他键 } } ``` ### 数据类型统一 ```typescript // 所有值都转换为字符串,保持JSON导出的一致性 result[key] = String(value); ``` ### 前缀处理透明化 - `getAll()`返回的键名是原始键名(不带`pref:`前缀) - 内部前缀处理对调用者完全透明 - 保持了PreferenceService的封装性 ## 🧪 测试覆盖 为新的`getAll()`方法添加了完整的测试覆盖: ```typescript describe('批量操作', () => { it('should get all preferences', async () => { await preferenceService.set('app:settings:ui:theme-id', 'dark'); await preferenceService.set('app:settings:ui:preferred-language', 'zh-CN'); const allPreferences = await preferenceService.getAll(); expect(allPreferences).toEqual({ 'app:settings:ui:theme-id': 'dark', 'app:settings:ui:preferred-language': 'zh-CN' }); }); it('should handle errors gracefully in getAll', async () => { // 测试错误处理逻辑 }); }); ``` ## 🚀 最佳实践总结 ### 1. 接口一致性原则 - **同类型服务应提供一致的接口模式** - **批量操作比逐个操作更高效和简洁** - **避免在上层代码中进行特殊处理** ### 2. 错误处理策略 - **批量操作中单个项目失败不应影响整体** - **提供详细的错误日志便于调试** - **保持操作的原子性和一致性** ### 3. 封装性设计 - **内部实现细节(如前缀)对外部透明** - **接口设计应符合调用者的期望** - **保持向后兼容性** ## 📝 相关文件 ### 修改的文件 - `packages/core/src/services/preference/types.ts` - 添加getAll接口 - `packages/core/src/services/preference/service.ts` - 实现getAll方法 - `packages/core/src/services/data/manager.ts` - 简化导出逻辑 - `packages/core/tests/unit/preference/service.test.ts` - 新增测试文件 ### 移除的复杂性 - 删除了`PREFERENCE_BASED_KEYS`和`DIRECT_STORAGE_KEYS`常量 - 简化了DataManager的存储键分类逻辑 - 统一了导入导出的处理方式 ## 🎉 总结 这次优化体现了**"保持架构一致性"**的重要性: 1. **识别不一致性** - 用户的观察非常准确,指出了架构问题 2. **统一接口模式** - 所有Manager都提供批量获取接口 3. **简化上层逻辑** - DataManager不再需要特殊处理 4. **提升性能和可维护性** - 更少的代码,更好的性能 这是一个很好的例子,说明了**用户反馈如何推动架构改进**,以及**简单一致的设计比复杂特殊处理更优雅**。