mirror of
https://github.com/linshenkx/prompt-optimizer.git
synced 2026-05-09 23:21:53 +08:00
## 核心架构重构 - 创建IImportExportable接口,定义统一的导入导出规范 - 新增ImportExportError错误类,提供专门的错误处理 - 将导入导出逻辑从集中式DataManager分散到各个服务 - DataManager职责精简:从375行减至67行(-308行),仅负责协调 ## 存储键架构优化 - 移动storage-keys.ts从ui包到core包,实现统一管理 - 修复存储键双重用途问题:物理存储键vs逻辑JSON导出键 - PreferenceService添加'pref:'前缀处理物理存储 - 解决数据导出不完整的关键架构缺陷 ## 服务层改造 ### ModelManager - 实现IImportExportable接口(+209行,-153行) - 添加exportData/importData/validateData方法 - 保持向后兼容的数据格式 ### TemplateManager - 实现分布式导入导出逻辑 - 移除过度设计的configurable storageKey - 统一使用PreferenceService管理用户偏好 ### HistoryManager - 添加完整的导入导出实现 - 支持数据验证和错误处理 ### PreferenceService - 实现统一的用户设置导入导出 - 处理存储键前缀转换逻辑 - 支持builtin-template-language等核心设置 ## Electron桌面端更新 - main.js: 新增148行IPC处理逻辑 - preload.js: 新增177行API暴露 - 更新所有service proxy类支持新接口 - 保持IPC通信的类型安全 ## 测试体系完善 - 新增各服务专门的import-export.test.ts文件 - 创建data/import-export-integration.test.ts集成测试 - 建立AI自动化测试框架验证存储键一致性 - 更新现有测试适配新架构 ## 文档与架构说明 - 创建import-export-interface-design.md设计文档 - 添加storage-key-architecture.md架构说明 - 建立AI自动化测试文档体系 - 更新workspace文档记录重构过程 BREAKING CHANGE: 导入导出接口从集中式DataManager重构为分布式服务实现, 各服务现在必须实现IImportExportable接口,存储键架构发生变化
6.4 KiB
6.4 KiB
PreferenceService架构优化
📋 优化背景
在存储键架构重构过程中,发现了一个重要的架构不一致性问题:
问题描述
用户提出了一个关键问题:"为什么exportAllData的时候要对preferenceService特别处理呢?preferenceService直接提供一个获取所有数据的接口不就好了?其他几个manager都是这样的"
架构不一致性分析
其他Manager的统一模式
// 所有其他服务都提供批量获取接口
const models = await this.modelManager.getAllModels();
const userTemplates = await this.templateManager.listTemplates();
const history = await this.historyManager.getAllRecords();
PreferenceService的特殊处理(问题)
// ❌ 原有的特殊处理方式
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的接口一致性:
export interface IPreferenceService {
// 现有方法...
/**
* 获取所有偏好设置
* @returns 包含所有偏好设置的键值对对象
*/
getAll(): Promise<Record<string, string>>;
}
2. 实现批量获取逻辑
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}`);
}
}
3. 简化DataManager导出逻辑
// ✅ 优化后的统一处理方式
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_KEYS - 简化了DataManager逻辑 - 从复杂的分类处理变为统一的批量调用
- 减少了维护成本 - 新增偏好设置不需要更新DataManager
性能提升
- 减少异步调用次数 - 从多次
get()调用变为一次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:前缀)- 内部前缀处理对调用者完全透明
- 保持了PreferenceService的封装性
🧪 测试覆盖
为新的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 () => {
// 测试错误处理逻辑
});
});
🚀 最佳实践总结
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的存储键分类逻辑
- 统一了导入导出的处理方式
🎉 总结
这次优化体现了**"保持架构一致性"**的重要性:
- 识别不一致性 - 用户的观察非常准确,指出了架构问题
- 统一接口模式 - 所有Manager都提供批量获取接口
- 简化上层逻辑 - DataManager不再需要特殊处理
- 提升性能和可维护性 - 更少的代码,更好的性能
这是一个很好的例子,说明了用户反馈如何推动架构改进,以及简单一致的设计比复杂特殊处理更优雅。