docs/architecture/preference-service-optimization.md
在存储键架构重构过程中,发现了一个重要的架构不一致性问题:
用户提出了一个关键问题:"为什么exportAllData的时候要对preferenceService特别处理呢?preferenceService直接提供一个获取所有数据的接口不就好了?其他几个manager都是这样的"
// 所有其他服务都提供批量获取接口
const models = await this.modelManager.getAllModels();
const userTemplates = await this.templateManager.listTemplates();
const history = await this.historyManager.getAllRecords();
// ❌ 原有的特殊处理方式
for (const key of PREFERENCE_BASED_KEYS) {
const value = await this.preferenceService.get(key, null);
if (value !== null) {
userSettings[key] = String(value);
}
}
为PreferenceService添加getAll()方法,保持与其他Manager的接口一致性:
export interface IPreferenceService {
// 现有方法...
/**
* 获取所有偏好设置
* @returns 包含所有偏好设置的键值对对象
*/
getAll(): Promise<Record<string, string>>;
}
async getAll(): Promise<Record<string, string>> {
try {
const allKeys = await this.keys();
const result: Record<string, string> = {};
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}`);
}
}
// ✅ 优化后的统一处理方式
async exportAllData(): Promise<ExportData> {
// 获取所有偏好设置(统一接口)
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<string, string> |
PREFERENCE_BASED_KEYS和DIRECT_STORAGE_KEYSget()调用变为一次getAll()调用// 健壮的错误处理:单个键失败不影响整体
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);
// 继续处理其他键
}
}
// 所有值都转换为字符串,保持JSON导出的一致性
result[key] = String(value);
getAll()返回的键名是原始键名(不带pref:前缀)为新的getAll()方法添加了完整的测试覆盖:
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 () => {
// 测试错误处理逻辑
});
});
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常量这次优化体现了**"保持架构一致性"**的重要性:
这是一个很好的例子,说明了用户反馈如何推动架构改进,以及简单一致的设计比复杂特殊处理更优雅。