mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-03 12:27:41 +08:00
The data-classification extract/validate/check workflow has completed its staged mission; only the code-generation pipeline remains in use. - package.json: replace extract/validate/validate:gen/check:duplicates/all with an echo notice; keep generate and generate:* intact - rename the four retired scripts with a DO-NOT-USE- prefix (git mv) - README: document the deprecation via a global notice, a status column on the command table, renamed file references, and historical-section markers
837 lines
33 KiB
Markdown
837 lines
33 KiB
Markdown
# 数据分类与代码生成工具
|
||
|
||
Cherry Studio 数据重构项目的自动化工具集,用于管理数据分类和生成 TypeScript 代码。
|
||
|
||
**版本**: 2.0.0
|
||
**更新日期**: 2025-11-28
|
||
|
||
> ⚠️ **阶段性使命已完成(重要)**
|
||
>
|
||
> 本工具中仅 **代码生成(generate)** 流水线仍在使用。数据提取、一致性/生成校验、重复键检查等命令与脚本已完成阶段性使命,**不再支持,请勿再次运行**。
|
||
>
|
||
> | 仍可用 | 已弃用(不再支持) |
|
||
> | --- | --- |
|
||
> | `npm run generate`、`generate:preferences`、`generate:boot-config`、`generate:migration` | `npm run extract`、`validate`、`validate:gen`、`check:duplicates`、`all` |
|
||
>
|
||
> 对应脚本文件已加 `DO-NOT-USE-` 前缀(`DO-NOT-USE-extract-inventory.js`、`DO-NOT-USE-validate-consistency.js`、`DO-NOT-USE-validate-generation.js`、`DO-NOT-USE-check-duplicates.js`);运行已弃用的 npm 命令只会打印一行提示。下文中保留的相关章节仅作历史记录,如有疑问请询问作者。
|
||
|
||
## 概述
|
||
|
||
本工具集提供以下功能:
|
||
|
||
- **数据提取**: 扫描源代码,构建数据清单
|
||
- **分类管理**: 维护分类映射,支持增量更新
|
||
- **代码生成**: 生成 TypeScript 接口和迁移映射
|
||
- **验证检查**: 确保清单与分类之间的一致性
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
v2-refactor-temp/tools/data-classify/
|
||
├── scripts/
|
||
│ ├── lib/
|
||
│ │ └── classificationUtils.js # 共享工具函数(仅被下方已弃用脚本使用)
|
||
│ ├── generate-all.js # 运行所有生成器
|
||
│ ├── generate-preferences.js # 生成 preferenceSchemas.ts
|
||
│ ├── generate-boot-config.js # 生成 bootConfigSchemas.ts
|
||
│ ├── generate-migration.js # 生成 PreferencesMappings.ts + BootConfigMappings.ts
|
||
│ ├── DO-NOT-USE-extract-inventory.js # [已弃用] 从源码提取数据清单
|
||
│ ├── DO-NOT-USE-validate-consistency.js # [已弃用] 验证数据一致性
|
||
│ ├── DO-NOT-USE-validate-generation.js # [已弃用] 验证生成代码质量
|
||
│ └── DO-NOT-USE-check-duplicates.js # [已弃用] 检查重复的目标键
|
||
├── data/
|
||
│ ├── classification.json # 分类映射(自动生成,人工维护)
|
||
│ ├── inventory.json # 数据清单(脚本生成)
|
||
│ └── target-key-definitions.json # 复杂映射的 target key 定义(人工维护)
|
||
├── package.json
|
||
└── README.md # 本文档
|
||
```
|
||
|
||
## 快速开始
|
||
|
||
```bash
|
||
# 进入工具目录
|
||
cd v2-refactor-temp/tools/data-classify
|
||
|
||
# 安装依赖
|
||
npm install
|
||
|
||
# 生成所有代码(当前唯一仍受支持的流程)
|
||
npm run generate
|
||
|
||
# 或仅生成单一目标
|
||
npm run generate:preferences # 仅生成 preferenceSchemas.ts
|
||
npm run generate:boot-config # 仅生成 bootConfigSchemas.ts
|
||
npm run generate:migration # 仅生成 PreferencesMappings.ts + BootConfigMappings.ts
|
||
```
|
||
|
||
> `extract` / `validate` / `validate:gen` / `check:duplicates` / `all` 已弃用,不再支持,详见顶部说明。
|
||
|
||
## 可用脚本
|
||
|
||
| 脚本 | 说明 | 状态 |
|
||
| ------------------------------ | ------------------------------------------------ | ----------- |
|
||
| `npm run generate` | 运行所有代码生成器 | ✅ 可用 |
|
||
| `npm run generate:preferences` | 仅生成 preferenceSchemas.ts | ✅ 可用 |
|
||
| `npm run generate:boot-config` | 仅生成 bootConfigSchemas.ts | ✅ 可用 |
|
||
| `npm run generate:migration` | 仅生成 PreferencesMappings.ts + BootConfigMappings.ts | ✅ 可用 |
|
||
| `npm run extract` | 从源文件提取数据清单 | ⛔ 已弃用 |
|
||
| `npm run validate` | 验证数据一致性 | ⛔ 已弃用 |
|
||
| `npm run validate:gen` | 验证生成代码质量 | ⛔ 已弃用 |
|
||
| `npm run check:duplicates` | 检查重复的目标键 | ⛔ 已弃用 |
|
||
| `npm run all` | 运行完整工作流 | ⛔ 已弃用 |
|
||
|
||
> ⛔ 标记的命令已完成阶段性使命,**不再支持**,运行只会打印提示。详见顶部说明。
|
||
|
||
## 脚本架构
|
||
|
||
> 注:下图与下表为原始完整工作流的历史记录;其中 `extract-inventory` / `validate-consistency` / `validate-generation` / `check-duplicates` 相关脚本已弃用(文件名已加 `DO-NOT-USE-` 前缀),仅 `generate-*` 仍在使用。
|
||
|
||
### 依赖关系图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 共享模块 │
|
||
│ scripts/lib/classificationUtils.js │
|
||
│ - loadClassification() - traverseClassifications() │
|
||
│ - saveClassification() - calculateStats() │
|
||
│ - loadInventory() - normalizeType() │
|
||
│ - extractPreferencesData() - inferTypeFromValue() │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
▲ ▲
|
||
│ │
|
||
┌───────────┘ └───────────┐
|
||
│ │
|
||
┌───────┴───────┐ ┌────────┴────────┐
|
||
│ extract- │ │ validate- │
|
||
│ inventory.js │ │ consistency.js │
|
||
│ │ │ │
|
||
│ 扫描源码 │ │ 检查数据 │
|
||
│ 构建清单 │ │ 一致性 │
|
||
└───────────────┘ └─────────────────┘
|
||
|
||
┌─────────────────────┐
|
||
│ generate-all.js │─────────────────────┬──────────────────────┐
|
||
│ │ │ │
|
||
│ 编排所有生成器 │ │ │
|
||
└─────────────────────┘ │ │
|
||
│ │ │
|
||
│ require() │ require() │ require()
|
||
▼ ▼ ▼
|
||
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
|
||
│ generate- │ │ generate- │ │ generate- │
|
||
│ preferences.js │ │ boot-config.js │ │ migration.js │
|
||
│ │ │ │ │ │
|
||
│ 生成 │ │ 生成 │ │ 生成 │
|
||
│ preferenceSchemas.ts│ │ bootConfigSchemas.ts│ │ PreferencesMappings │
|
||
└─────────────────────┘ └─────────────────────┘ │ BootConfigMappings │
|
||
└─────────────────────┘
|
||
|
||
┌─────────────────────┐ ┌─────────────────────┐
|
||
│ validate- │ │ check- │
|
||
│ generation.js │ │ duplicates.js │
|
||
│ │ │ │
|
||
│ 验证生成代码质量 │ │ 检查重复目标键 │
|
||
│ (独立运行) │ │ (独立运行) │
|
||
└─────────────────────┘ └─────────────────────┘
|
||
```
|
||
|
||
### 脚本详情
|
||
|
||
| 脚本 | 输入 | 输出 | 依赖 |
|
||
| ------------------------- | --------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------ |
|
||
| `generate-preferences.js` | `classification.json` | `preferenceSchemas.ts` | 无 |
|
||
| `generate-boot-config.js` | `classification.json` | `bootConfigSchemas.ts` | 无 |
|
||
| `generate-migration.js` | `classification.json` | `PreferencesMappings.ts`, `BootConfigMappings.ts` | 无 |
|
||
| `generate-all.js` | - | 运行三个生成器 | `generate-preferences.js`, `generate-boot-config.js`, `generate-migration.js` |
|
||
| `DO-NOT-USE-extract-inventory.js` _(已弃用)_ | 源代码文件 | `data/inventory.json` | `classificationUtils.js` |
|
||
| `DO-NOT-USE-validate-consistency.js` _(已弃用)_ | `inventory.json`, `classification.json` | `validation-report.md` | `classificationUtils.js` |
|
||
| `DO-NOT-USE-validate-generation.js` _(已弃用)_ | 生成的 `.ts` 文件 | 控制台输出 | 无 |
|
||
| `DO-NOT-USE-check-duplicates.js` _(已弃用)_ | `classification.json` | 控制台输出 | 无 |
|
||
|
||
## 数据分类工作流
|
||
|
||
> ⚠️ 本节为历史记录:步骤 1(提取)与步骤 4(验证)所用命令已弃用、不再支持,当前仅步骤 3(生成)仍受支持。
|
||
|
||
### 1. 提取数据清单
|
||
|
||
```bash
|
||
npm run extract
|
||
```
|
||
|
||
扫描源文件并提取以下数据源的信息:
|
||
|
||
- **Redux Store**: `src/renderer/store/*.ts`
|
||
- **Electron Store**: `src/main/services/ConfigManager.ts`
|
||
- **LocalStorage**: 所有使用 localStorage 的文件
|
||
- **Dexie 数据库**: `src/renderer/databases/index.ts`
|
||
|
||
> **注意**: `dexieSettings` 数据源中的字符串字面量 key 会被自动提取,但动态 key(如模板字符串拼接的)需要手动维护。详见下方 [dexieSettings 数据源](#dexiesettings-数据源) 章节。
|
||
|
||
### 2. 分类数据
|
||
|
||
编辑 `data/classification.json` 对每个数据项进行分类:
|
||
|
||
```json
|
||
{
|
||
"originalKey": "theme",
|
||
"type": "string",
|
||
"status": "classified",
|
||
"category": "preferences",
|
||
"targetKey": "ui.theme_mode"
|
||
}
|
||
```
|
||
|
||
### 3. 生成代码
|
||
|
||
```bash
|
||
npm run generate
|
||
```
|
||
|
||
生成以下 TypeScript 文件:
|
||
|
||
- `src/shared/data/preference/preferenceSchemas.ts` - 偏好配置类型定义与默认值
|
||
- `src/shared/data/bootConfig/bootConfigSchemas.ts` - 启动配置类型定义与默认值
|
||
- `src/main/data/migration/v2/migrators/mappings/PreferencesMappings.ts` - 偏好迁移映射
|
||
- `src/main/data/migration/v2/migrators/mappings/BootConfigMappings.ts` - 启动配置迁移映射
|
||
|
||
### 4. 验证
|
||
|
||
```bash
|
||
npm run validate
|
||
npm run validate:gen
|
||
```
|
||
|
||
验证内容:
|
||
|
||
- 所有清单项都已分类
|
||
- 没有孤立的分类条目
|
||
- 命名规范一致
|
||
- 没有重复的目标键
|
||
- 生成代码结构正确
|
||
|
||
---
|
||
|
||
## 数据分类标准
|
||
|
||
根据 Cherry Studio 数据重构架构,所有数据需要分类到以下 6 个类别之一:
|
||
|
||
### 1. 偏好配置 (preferences)
|
||
|
||
**判断标准**:
|
||
|
||
- ✅ 影响应用全局行为的配置
|
||
- ✅ 用户可以修改的设置项
|
||
- ✅ 简单的数据类型(boolean/string/number/简单 array/object)
|
||
- ✅ 结构相对稳定,不经常变化
|
||
- ✅ 数据量小,可以重建
|
||
- ✅ 需要在窗口间同步
|
||
|
||
**典型例子**:
|
||
|
||
- `showAssistants`: 是否显示助手面板
|
||
- `theme`: 主题设置(light/dark/system)
|
||
- `fontSize`: 字体大小
|
||
- `language`: 界面语言
|
||
|
||
**命名规范**:
|
||
|
||
- 使用点分隔的层级结构:`ui.fontSize`、`system.language`
|
||
- 分组前缀:`ui.*`(界面)、`system.*`(系统)、`app.*`(应用行为)等
|
||
|
||
### 2. 启动配置 (bootConfig)
|
||
|
||
**判断标准**:
|
||
|
||
- ✅ 必须在 Node.js 进程启动的最早阶段同步加载(早于 `app.whenReady`、早于 lifecycle 的 `BeforeReady` 阶段)
|
||
- ✅ 影响进程级别的行为,一旦进程启动就无法更改
|
||
- ✅ 不能存储在 SQLite 中(数据库由 lifecycle `BeforeReady` 阶段初始化,远晚于 boot config 的加载时机)
|
||
- ✅ 使用同步文件 I/O 读取(`~/.cherrystudio/boot-config.json`,刻意放在 userData 之外,避免鸡生蛋问题)
|
||
|
||
**时序关系**:
|
||
|
||
```
|
||
Boot Config 加载 → bootstrap(初始化 appData 目录)→ app.whenReady → lifecycle BeforeReady(DB 初始化)→ lifecycle WhenReady
|
||
```
|
||
|
||
Boot config 在整个启动链的最前端,为后续所有阶段提供基础配置。
|
||
|
||
**典型例子**:
|
||
|
||
- `disableHardwareAcceleration`: 硬件加速开关(必须在任何 Electron API 调用前设置)
|
||
- 应用路径定义等需要在 bootstrap 阶段就确定的配置
|
||
|
||
**与 preferences 的区别**:
|
||
|
||
| | bootConfig | preferences |
|
||
| --- | --- | --- |
|
||
| 加载时机 | 进程启动最早阶段(同步) | lifecycle `BeforeReady` 阶段(异步) |
|
||
| 存储方式 | JSON 文件(`~/.cherrystudio/boot-config.json`) | SQLite 数据库 |
|
||
| 访问方式(Main) | `bootConfigService.get()` 同步 | `application.get('PreferenceService').get()` |
|
||
| 访问方式(Renderer) | `usePreference('BootConfig.*')` 统一访问 | `usePreference('key')` |
|
||
|
||
### 3. 用户数据 (user_data)
|
||
|
||
**判断标准**:
|
||
|
||
- ✅ 用户创建或输入的内容
|
||
- ✅ 不可丢失的重要数据
|
||
- ✅ 数据量可能很大
|
||
- ✅ 需要完整备份和迁移机制
|
||
- ✅ 可能包含敏感信息
|
||
|
||
**典型例子**:
|
||
|
||
- `topics`: 对话历史
|
||
- `messages`: 消息内容
|
||
- `files`: 用户上传的文件
|
||
- `knowledge_notes`: 知识库笔记
|
||
|
||
**特殊处理**:
|
||
|
||
- 敏感数据需要加密存储
|
||
- 大数据表需要考虑分页和流式处理
|
||
|
||
### 4. 缓存数据 (cache)
|
||
|
||
**判断标准**:
|
||
|
||
- ✅ 可以重新生成的数据
|
||
- ✅ 主要用于性能优化
|
||
- ✅ 丢失后不影响核心功能
|
||
- ✅ 有过期时间或清理机制
|
||
|
||
**典型例子**:
|
||
|
||
- `failed_favicon_*`: 失败的 favicon 缓存
|
||
- 搜索结果缓存
|
||
- 图片预览缓存
|
||
- 模型响应缓存
|
||
|
||
### 5. 运行时数据 (runtime)
|
||
|
||
**判断标准**:
|
||
|
||
- ✅ 内存型数据,不需要持久化
|
||
- ✅ 生命周期 ≤ 应用进程
|
||
- ✅ 应用重启后可以丢失
|
||
- ✅ 临时状态信息
|
||
|
||
**典型例子**:
|
||
|
||
- 当前选中的对话
|
||
- 临时的输入状态
|
||
- UI 组件的展开/折叠状态
|
||
- 网络请求状态
|
||
|
||
### 6. 应用资源 (resources)
|
||
|
||
**判断标准**:
|
||
|
||
- ✅ 静态资源文件
|
||
- ✅ 随应用分发的内容
|
||
- ✅ 不需要用户修改
|
||
- ✅ 暂不考虑重构
|
||
|
||
**典型例子**:
|
||
|
||
- 图标文件
|
||
- 本地化翻译文件
|
||
- 默认配置文件
|
||
- 帮助文档
|
||
|
||
---
|
||
|
||
## 分类决策流程图
|
||
|
||
```
|
||
数据项
|
||
↓
|
||
是否必须在进程启动最早阶段同步加载(早于 lifecycle)?
|
||
↓ 是 ↓ 否
|
||
启动配置 是否用户创建/输入的内容?
|
||
(bootConfig) ↓ 是 ↓ 否
|
||
用户数据 是否需要持久化?
|
||
↓ 否 ↓ 是
|
||
运行时数据 是否可重新生成?
|
||
↓ 是 ↓ 否
|
||
缓存数据 是否用户可修改?
|
||
↓ 是 ↓ 否
|
||
偏好配置 应用资源
|
||
```
|
||
|
||
---
|
||
|
||
## 分类示例
|
||
|
||
### 示例 1: Redux settings.showAssistants
|
||
|
||
```json
|
||
{
|
||
"classifications": {
|
||
"redux": {
|
||
"settings": [
|
||
{
|
||
"originalKey": "showAssistants",
|
||
"type": "boolean",
|
||
"defaultValue": true,
|
||
"status": "classified",
|
||
"category": "preferences",
|
||
"targetKey": "ui.show_assistants"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**分析过程**:
|
||
|
||
1. 数据用途:控制是否显示助手面板
|
||
2. 用户可修改:✅
|
||
3. 影响全局:✅
|
||
4. 数据简单:✅ boolean 类型
|
||
5. 结论:偏好配置
|
||
|
||
### 示例 2: 嵌套结构 (Redux settings with children)
|
||
|
||
```json
|
||
{
|
||
"originalKey": "codeEditor",
|
||
"type": "object",
|
||
"children": [
|
||
{
|
||
"originalKey": "enabled",
|
||
"type": "boolean",
|
||
"defaultValue": true,
|
||
"status": "classified",
|
||
"category": "preferences",
|
||
"targetKey": "code_editor.enabled"
|
||
},
|
||
{
|
||
"originalKey": "fontSize",
|
||
"type": "number",
|
||
"defaultValue": 14,
|
||
"status": "classified",
|
||
"category": "preferences",
|
||
"targetKey": "code_editor.font_size"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**注意**: 父级项不需要 `status`/`category`/`targetKey`,这些只在叶子节点设置。
|
||
|
||
### 示例 3: Dexie topics 表
|
||
|
||
```json
|
||
{
|
||
"originalKey": "topics",
|
||
"type": "table",
|
||
"status": "classified",
|
||
"category": "user_data",
|
||
"targetTable": "topic",
|
||
"notes": "用户对话历史,核心业务数据"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 命名规范
|
||
|
||
偏好配置键必须遵循:`namespace.sub.key_name`
|
||
|
||
**规则**:
|
||
|
||
- 至少 2 个由点分隔的段
|
||
- 仅使用小写字母、数字、下划线
|
||
- 模式:`/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/`
|
||
|
||
**示例**:
|
||
|
||
- `app.theme` (有效)
|
||
- `chat.input.send_shortcut` (有效)
|
||
- `Theme` (无效 - 没有点分隔符)
|
||
- `App.User` (无效 - 大写字母)
|
||
|
||
---
|
||
|
||
## 增量更新策略
|
||
|
||
> ⚠️ 本节描述的提取(`npm run extract`)流程已弃用、不再支持,仅作历史记录。
|
||
|
||
### 核心特性
|
||
|
||
- **保留已分类数据**: 重新运行提取不会丢失已有分类
|
||
- **标记删除项**: 删除的数据项被标记但不移除
|
||
- **自动发现新项**: 新数据项自动添加到待处理列表
|
||
- **自动备份**: 每次运行前自动备份原分类文件
|
||
|
||
### 更新流程
|
||
|
||
1. 代码变更后运行 `npm run extract`
|
||
2. 脚本自动备份 `classification.json` 到 `classification.backup.json`
|
||
3. 脚本识别新增和删除的数据项
|
||
4. 新项添加到 `pending` 数组
|
||
5. 删除项标记为 `status: 'classified-deleted'`
|
||
6. 手动处理新的待处理项
|
||
|
||
---
|
||
|
||
## 文件格式说明
|
||
|
||
### inventory.json 结构
|
||
|
||
```json
|
||
{
|
||
"metadata": {
|
||
"generatedAt": "ISO 日期",
|
||
"version": "版本号"
|
||
},
|
||
"redux": {
|
||
"moduleName": {
|
||
"fieldName": {
|
||
"type": "数据类型",
|
||
"defaultValue": "默认值"
|
||
}
|
||
}
|
||
},
|
||
"electronStore": { ... },
|
||
"localStorage": { ... },
|
||
"dexie": { ... }
|
||
}
|
||
```
|
||
|
||
### classification.json 结构
|
||
|
||
```json
|
||
{
|
||
"metadata": {
|
||
"version": "版本号",
|
||
"lastUpdated": "ISO 日期"
|
||
},
|
||
"classifications": {
|
||
"redux": {
|
||
"moduleName": [
|
||
{
|
||
"originalKey": "字段名",
|
||
"type": "数据类型",
|
||
"status": "classified|pending|classified-deleted",
|
||
"category": "preferences|bootConfig|user_data|cache|runtime|resources",
|
||
"targetKey": "target.key.name"
|
||
}
|
||
]
|
||
},
|
||
"electronStore": { ... },
|
||
"localStorage": { ... },
|
||
"dexieSettings": {
|
||
"settings": [
|
||
{
|
||
"originalKey": "字段名",
|
||
"type": "数据类型",
|
||
"status": "classified|pending",
|
||
"category": "preferences",
|
||
"targetKey": "target.key.name"
|
||
}
|
||
]
|
||
},
|
||
"dexie": { ... }
|
||
}
|
||
}
|
||
```
|
||
|
||
### dexieSettings 数据源
|
||
|
||
`dexieSettings` 是 classification.json 中与 `redux`、`electronStore`、`localStorage`、`dexie` 并列的第五个顶级数据源,专门用于分类 Dexie IndexedDB 中 `settings` 表的 KV 配置项。
|
||
|
||
**与 `dexie` 数据源的区别**:
|
||
|
||
| 数据源 | 用途 | 数据结构 | 典型分类 |
|
||
| --- | --- | --- | --- |
|
||
| `dexie` | Dexie 的业务数据表(files, topics 等) | 表级别,使用 `targetTable` | `user_data` |
|
||
| `dexieSettings` | Dexie 的 `settings` 表(KV 配置) | 字段级别,使用 `targetKey` | `preferences`(目前仅支持此分类) |
|
||
|
||
**classification.json 中的结构**:
|
||
|
||
```json
|
||
{
|
||
"classifications": {
|
||
"dexieSettings": {
|
||
"settings": [
|
||
{
|
||
"originalKey": "settingKeyName",
|
||
"type": "string",
|
||
"defaultValue": "defaultValue",
|
||
"status": "classified",
|
||
"category": "preferences",
|
||
"targetKey": "namespace.key_name"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**在代码生成中的作用**:
|
||
|
||
1. **`generate-preferences.js`**: 作为四大偏好数据源之一(`electronStore`、`redux`、`localStorage`、`dexieSettings`),参与生成 `preferenceSchemas.ts`
|
||
2. **`generate-migration.js`**: 生成独立的 `DEXIE_SETTINGS_MAPPINGS` 映射数组,用于迁移器从 Dexie settings 表读取数据
|
||
|
||
**去重优先级**(当多个数据源映射到相同 targetKey 时):
|
||
|
||
```
|
||
redux (最高) > dexieSettings > localStorage > electronStore (最低)
|
||
```
|
||
|
||
**已知字段清单**(参考 [PR #10162 comment](https://github.com/CherryHQ/cherry-studio/pull/10162#issuecomment-4010796619)):
|
||
|
||
Dexie `settings` 表是一个通用 KV 存储(`{ id: string, value: any }`),所有 `image://` 键由 `ImageStorage` 服务管理。
|
||
|
||
*固定键*:
|
||
|
||
| Key | Value Type | 迁移目标 |
|
||
| --- | --- | --- |
|
||
| `translate:model` | `string` (model id) | preference |
|
||
| `translate:target:language` | `string` (langCode) | preference |
|
||
| `translate:source:language` | `string` (langCode) | preference |
|
||
| `translate:bidirectional:enabled` | `boolean` | preference |
|
||
| `translate:bidirectional:pair` | `[string, string]` (langCode pair) | preference |
|
||
| `translate:scroll:sync` | `boolean` | preference |
|
||
| `translate:markdown:enabled` | `boolean` | preference |
|
||
| `translate:detect:method` | `string` ('franc'/'llm'/'auto') | preference |
|
||
| `pinned:models` | `Model[]` | preference |
|
||
| `image://avatar` | `string` (base64 data URL \| emoji) | preference / file manager |
|
||
|
||
*动态键(基于 pattern)*:
|
||
|
||
| Key Pattern | Value Type | 迁移目标 |
|
||
| --- | --- | --- |
|
||
| `image://provider-${providerId}` | `string` (base64 data URL \| emoji \| `''`) | file manager |
|
||
| `mcp:provider:${provider.key}:servers` | `MCPServer[]` | new table |
|
||
|
||
*已知遗留键(代码中无引用,运行时 IndexedDB 中存在)*:
|
||
|
||
| Key | 说明 |
|
||
| --- | --- |
|
||
| `translate:model:prompt` | 已被 Redux `settings.translateModelPrompt` 取代,已迁移到 preference `feature.translate.model_prompt`,跳过 |
|
||
|
||
**注意事项**:
|
||
|
||
- `extract-inventory.js` 会自动提取 `db.settings.get/put/add()` 调用中的**字符串字面量** key;动态 key(模板字符串拼接)无法自动提取,需要**手动添加**
|
||
- 手动添加的条目在重新提取时会被保留(不会被覆盖或删除)
|
||
- `dexieSettings` 的 `category` 目前仅支持 `preferences`,代码生成脚本只处理该分类
|
||
- `validate-consistency.js` 不会检查 `dexieSettings` 与 inventory 的一致性
|
||
- 迁移时通过 `ctx.sources.dexieSettings.get(mapping.originalKey)` 读取数据
|
||
- 动态键(含 `${}` 模板的 pattern)需要特殊的迁移逻辑,不能用简单 1:1 映射处理
|
||
|
||
### 状态值说明
|
||
|
||
| Status | 说明 | 操作建议 |
|
||
| -------------------- | ---------------- | ---------------------------------------- |
|
||
| `pending` | 待分类 | 需要人工分析并设置 category 和 targetKey |
|
||
| `classified` | 已分类 | 分类完成,可用于代码生成 |
|
||
| `classified-deleted` | 已分类但源已删除 | 源代码中已不存在,保留历史记录 |
|
||
|
||
### targetKey 值说明
|
||
|
||
对于 `status: "classified"` 的项,`targetKey` 有两种情况:
|
||
|
||
| targetKey | 说明 | 生成行为 |
|
||
| --------- | ---- | -------- |
|
||
| 有效值(如 `"ui.theme"`) | 需要迁移到新系统 | 会生成到 preferenceSchemas.ts |
|
||
| `null` | 已分类,但不需要迁移 | 不会生成到 preferenceSchemas.ts |
|
||
|
||
**使用场景**:当一个数据项经过分析后,确定在新系统中不需要该配置(如已废弃、被其他配置替代、或不再需要持久化),应设置 `status: "classified"` 且 `targetKey: null`。这表示"已完成分类决策,决策结果是不加载该项"。
|
||
|
||
---
|
||
|
||
## 复杂映射支持
|
||
|
||
### 概述
|
||
|
||
除了简单的一对一映射(`originalKey → targetKey`),系统还支持复杂的数据转换:
|
||
|
||
1. **对象拆分 (1→N)**: 一个源对象拆分为多个 target keys
|
||
2. **多源合并 (N→1)**: 多个源数据合并为一个或多个目标
|
||
3. **值计算/转换**: 值需要经过计算、格式转换或逻辑处理
|
||
4. **条件映射**: 根据源数据的值决定写入哪些目标
|
||
|
||
### 架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ classification.json (status: classified) │
|
||
│ ───────────────────────────────────────── │
|
||
│ 基础数据源,包含所有简单映射的 target keys │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ target-key-definitions.json │
|
||
│ ───────────────────────────────────────── │
|
||
│ 用途1: 复杂迁移 - 定义需要特殊转换逻辑的 target keys │
|
||
│ 用途2: 纯新增 - 添加 v2 新功能的 preferences(非迁移) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ preferenceSchemas.ts (最终输出) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### target-key-definitions.json
|
||
|
||
用于定义无法通过 `classification.json` 简单映射处理的 preference keys。主要用于两个场景:
|
||
|
||
1. **复杂迁移**: 定义需要特殊转换逻辑(对象拆分、多源合并、值计算等)产生的 target keys
|
||
2. **纯新增(非迁移)**: 添加 v2 新功能的 preferences,这些配置不是从旧代码迁移的
|
||
|
||
**文件结构**:
|
||
|
||
```json
|
||
{
|
||
"metadata": {
|
||
"version": "1.0.0",
|
||
"description": "Target key definitions...",
|
||
"lastUpdated": "2025-01-18"
|
||
},
|
||
"definitions": [
|
||
{
|
||
"targetKey": "app.window.position.x",
|
||
"type": "number",
|
||
"defaultValue": 0,
|
||
"status": "classified",
|
||
"description": "Window X position (from complex mapping)"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**字段说明**:
|
||
|
||
| 字段 | 必填 | 说明 |
|
||
| -------------- | ---- | -------------------------------------------------------- |
|
||
| `targetKey` | ✓ | preference key(必须符合命名规范) |
|
||
| `type` | ✓ | TypeScript 类型(string, number, boolean, 或自定义类型) |
|
||
| `defaultValue` | ✓ | 默认值(支持 `VALUE: ...` 特殊格式) |
|
||
| `status` | ✓ | `classified` 启用,`pending` 禁用 |
|
||
| `description` | | 可选描述 |
|
||
|
||
#### 纯新增(非迁移)场景
|
||
|
||
当需要添加一个全新的 preference(不是从旧代码迁移的 v2 新功能)时,直接在 `definitions` 数组中添加即可。
|
||
|
||
**示例**: 添加 v2 版本新增的功能配置
|
||
|
||
```json
|
||
{
|
||
"definitions": [
|
||
{
|
||
"targetKey": "feature.new_assistant.enabled",
|
||
"type": "boolean",
|
||
"defaultValue": false,
|
||
"status": "classified",
|
||
"description": "启用新助手功能(v2 新增,非迁移)"
|
||
},
|
||
{
|
||
"targetKey": "feature.new_assistant.default_model",
|
||
"type": "string",
|
||
"defaultValue": "gpt-4",
|
||
"status": "classified",
|
||
"description": "新助手默认模型(v2 新增,非迁移)"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
运行 `npm run generate:preferences` 后,这些 keys 会出现在生成的 `preferenceSchemas.ts` 中。
|
||
|
||
**与复杂迁移的区别**:
|
||
- 复杂迁移需要在 `PreferenceTransformers.ts` 和 `ComplexPreferenceMappings.ts` 中实现转换逻辑
|
||
- 纯新增只需要在 `target-key-definitions.json` 中定义,无需额外代码
|
||
|
||
**建议**: 在 `description` 中注明是"复杂迁移"还是"v2 新增,非迁移",便于后续维护。
|
||
|
||
**defaultValue 特殊格式**:
|
||
|
||
与 classification.json 保持一致,支持 `VALUE: ...` 特殊格式:
|
||
|
||
```json
|
||
// 引用枚举值
|
||
{ "defaultValue": "VALUE: PreferenceTypes.ThemeMode.system" }
|
||
// 生成: PreferenceTypes.ThemeMode.system(不加引号)
|
||
|
||
// 引用常量
|
||
{ "defaultValue": "VALUE: TRANSLATE_PROMPT" }
|
||
// 生成: TRANSLATE_PROMPT(不加引号)
|
||
|
||
// 特殊 null 值
|
||
{ "defaultValue": "VALUE: null" }
|
||
// 生成: null
|
||
|
||
// 普通字符串(不使用 VALUE: 前缀)
|
||
{ "defaultValue": "light" }
|
||
// 生成: 'light'(带引号)
|
||
```
|
||
|
||
### 复杂映射实现
|
||
|
||
复杂映射的转换逻辑定义在以下文件中:
|
||
|
||
```
|
||
src/main/data/migration/v2/migrators/
|
||
├── mappings/
|
||
│ ├── PreferencesMappings.ts # 偏好简单映射(自动生成)
|
||
│ ├── BootConfigMappings.ts # 启动配置映射(自动生成)
|
||
│ └── ComplexPreferenceMappings.ts # 复杂映射配置
|
||
├── transformers/
|
||
│ └── PreferenceTransformers.ts # 转换函数实现
|
||
├── PreferencesMigrator.ts # 偏好迁移执行器
|
||
└── BootConfigMigrator.ts # 启动配置迁移执行器
|
||
```
|
||
|
||
**添加复杂映射的步骤**:
|
||
|
||
1. 在 `target-key-definitions.json` 中定义 target keys(设 `status: "classified"`)
|
||
2. 在 `PreferenceTransformers.ts` 中实现转换函数
|
||
3. 在 `ComplexPreferenceMappings.ts` 中添加映射配置
|
||
4. 运行 `npm run generate:preferences` 重新生成 preferenceSchemas.ts
|
||
|
||
### 冲突处理
|
||
|
||
系统采用**严格模式**:如果简单映射和复杂映射的 target key 有冲突,迁移时会报错。
|
||
|
||
解决方法:从简单映射(classification.json)中移除冲突的 key,由复杂映射处理。
|
||
|
||
---
|
||
|
||
## 故障排除
|
||
|
||
### "Module not found" 错误
|
||
|
||
```bash
|
||
cd v2-refactor-temp/tools/data-classify
|
||
npm install
|
||
```
|
||
|
||
### 验证错误
|
||
|
||
1. 检查 `validation-report.md` 了解详情
|
||
2. 修复 `classification.json` 条目
|
||
3. 重新运行验证
|
||
|
||
### 生成代码问题
|
||
|
||
1. ~~运行 `npm run validate:gen` 识别问题~~(已弃用,不再支持)
|
||
2. 检查源分类数据
|
||
3. 使用 `npm run generate` 重新生成
|
||
|
||
### 数据项被错误标记为删除
|
||
|
||
检查提取脚本的模式是否正确匹配代码结构。
|
||
|
||
### 如何恢复意外删除的分类
|
||
|
||
从以下位置恢复 `classification.json`:
|
||
|
||
- 自动备份文件:`classification.backup.json`
|
||
- Git 历史记录
|