feat(preferences): auto-generate preferences configuration from classification.json

This commit introduces an auto-generated preferences configuration file, replacing the previous manual definitions. The new structure is based on the latest classification.json and includes comprehensive settings for various application features. The auto-generation process ensures that the preferences are up-to-date and consistent with the defined classifications.
This commit is contained in:
fullex
2025-08-09 16:44:35 +08:00
parent 21e40db086
commit 4e3f8a8f76
6 changed files with 2055 additions and 18 deletions
@@ -0,0 +1,152 @@
/**
* Auto-generated ElectronStore to Preferences migration
* Generated at: 2025-08-09T07:20:05.910Z
*
* === AUTO-GENERATED CONTENT START ===
*/
import dbService from '@data/db/DbService'
import { loggerService } from '@logger'
import { configManager } from '@main/services/ConfigManager'
import type { MigrationResult } from './index'
import { TypeConverter } from './utils/typeConverters'
const logger = loggerService.withContext('ElectronStoreMigrator')
// 键映射表
const KEY_MAPPINGS = [
{
originalKey: 'Language',
targetKey: 'app.language',
sourceCategory: 'Language',
type: 'unknown',
defaultValue: null
},
{
originalKey: 'SelectionAssistantFollowToolbar',
targetKey: 'feature.selection.follow_toolbar',
sourceCategory: 'SelectionAssistantFollowToolbar',
type: 'unknown',
defaultValue: null
},
{
originalKey: 'SelectionAssistantRemeberWinSize',
targetKey: 'feature.selection.remember_win_size',
sourceCategory: 'SelectionAssistantRemeberWinSize',
type: 'unknown',
defaultValue: null
},
{
originalKey: 'ZoomFactor',
targetKey: 'app.zoom_factor',
sourceCategory: 'ZoomFactor',
type: 'unknown',
defaultValue: null
}
] as const
export class ElectronStoreMigrator {
private typeConverter: TypeConverter
constructor() {
this.typeConverter = new TypeConverter()
}
/**
* 执行ElectronStore到preferences的迁移
*/
async migrate(): Promise<MigrationResult> {
logger.info('开始ElectronStore迁移', { totalItems: KEY_MAPPINGS.length })
const result: MigrationResult = {
success: true,
migratedCount: 0,
errors: [],
source: 'electronStore'
}
for (const mapping of KEY_MAPPINGS) {
try {
await this.migrateItem(mapping)
result.migratedCount++
} catch (error) {
logger.error('迁移单项失败', { mapping, error })
result.errors.push({
key: mapping.originalKey,
error: error instanceof Error ? error.message : String(error)
})
result.success = false
}
}
logger.info('ElectronStore迁移完成', result)
return result
}
/**
* 迁移单个配置项
*/
private async migrateItem(mapping: (typeof KEY_MAPPINGS)[0]): Promise<void> {
const { originalKey, targetKey, type, defaultValue } = mapping
// 从ElectronStore读取原始值
const originalValue = configManager.get(originalKey)
if (originalValue === undefined || originalValue === null) {
// 如果原始值不存在,使用默认值
if (defaultValue !== null && defaultValue !== undefined) {
const convertedValue = this.typeConverter.convert(defaultValue, type)
await dbService.setPreference('default', targetKey, convertedValue)
logger.debug('使用默认值迁移', { originalKey, targetKey, defaultValue: convertedValue })
}
return
}
// 类型转换
const convertedValue = this.typeConverter.convert(originalValue, type)
// 写入preferences表
await dbService.setPreference('default', targetKey, convertedValue)
logger.debug('成功迁移配置项', {
originalKey,
targetKey,
originalValue,
convertedValue
})
}
/**
* 验证迁移结果
*/
async validateMigration(): Promise<boolean> {
logger.info('开始验证ElectronStore迁移结果')
for (const mapping of KEY_MAPPINGS) {
const { targetKey } = mapping
try {
const value = await dbService.getPreference('default', targetKey)
if (value === null) {
logger.error('验证失败:配置项不存在', { targetKey })
return false
}
} catch (error) {
logger.error('验证失败:读取配置项错误', { targetKey, error })
return false
}
}
logger.info('ElectronStore迁移验证成功')
return true
}
}
// === AUTO-GENERATED CONTENT END ===
/**
* 迁移统计:
* - ElectronStore配置项: 4
* - 包含的原始键: Language, SelectionAssistantFollowToolbar, SelectionAssistantRemeberWinSize, ZoomFactor
*/
+132
View File
@@ -0,0 +1,132 @@
/**
* Auto-generated migration index
* Generated at: 2025-08-09T07:20:05.909Z
*
* This file is automatically generated from classification.json
* To update this file, modify classification.json and run:
* node .claude/data-classify/scripts/generate-migration.js
*
* === AUTO-GENERATED CONTENT START ===
*/
import { ElectronStoreMigrator } from './electronStoreToPreferences'
import { ReduxMigrator } from './reduxToPreferences'
import { loggerService } from '@logger'
const logger = loggerService.withContext('MigrationManager')
export interface MigrationResult {
success: boolean
migratedCount: number
errors: Array<{
key: string
error: string
}>
source: 'electronStore' | 'redux'
}
export interface MigrationSummary {
totalItems: number
successCount: number
errorCount: number
electronStore: MigrationResult
redux: MigrationResult
}
export class MigrationManager {
private electronStoreMigrator: ElectronStoreMigrator
private reduxMigrator: ReduxMigrator
constructor() {
this.electronStoreMigrator = new ElectronStoreMigrator()
this.reduxMigrator = new ReduxMigrator()
}
/**
* 执行完整的preferences迁移
* @returns 迁移摘要
*/
async migrateAllPreferences(): Promise<MigrationSummary> {
logger.info('开始完整preferences迁移')
try {
// 并行执行两个迁移器
const [electronStoreResult, reduxResult] = await Promise.all([
this.electronStoreMigrator.migrate(),
this.reduxMigrator.migrate()
])
const summary: MigrationSummary = {
totalItems: 158,
successCount: electronStoreResult.migratedCount + reduxResult.migratedCount,
errorCount: electronStoreResult.errors.length + reduxResult.errors.length,
electronStore: electronStoreResult,
redux: reduxResult
}
if (summary.errorCount > 0) {
logger.warn('迁移完成但有错误', { summary })
} else {
logger.info('迁移完全成功', { summary })
}
return summary
} catch (error) {
logger.error('迁移过程中发生致命错误', error)
throw error
}
}
/**
* 验证迁移结果
* @param summary 迁移摘要
* @returns 是否验证成功
*/
async validateMigration(summary: MigrationSummary): Promise<boolean> {
logger.info('开始验证迁移结果')
// 基本验证:检查成功率
const successRate = summary.successCount / summary.totalItems
if (successRate < 0.95) { // 要求95%以上成功率
logger.error('迁移成功率过低', { successRate, summary })
return false
}
// 验证关键配置项是否存在
const criticalKeys = [
'app.theme.mode',
'app.language',
'app.user.id',
'feature.quick_assistant.enabled',
'chat.message.font_size'
]
try {
const dbServiceModule = await import('@main/db/DbService')
const dbService = dbServiceModule.default
for (const key of criticalKeys) {
const result = await dbService.getPreference('default', key)
if (result === null) {
logger.error('关键配置项迁移失败', { key })
return false
}
}
logger.info('迁移验证成功')
return true
} catch (error) {
logger.error('验证过程中发生错误', error)
return false
}
}
}
// === AUTO-GENERATED CONTENT END ===
/**
* 生成统计:
* - 总迁移项: 158
* - ElectronStore项: 4
* - Redux项: 154
*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,72 @@
/**
* Migration helper utilities
* Generated at: 2025-08-09T07:20:05.912Z
*/
import { loggerService } from '@logger'
const logger = loggerService.withContext('MigrationHelpers')
export interface BackupInfo {
timestamp: string
version: string
dataSize: number
backupPath: string
}
export class MigrationHelpers {
/**
* 创建数据备份
*/
static async createBackup(): Promise<BackupInfo> {
logger.info('开始创建数据备份')
// 实现备份逻辑
const timestamp = new Date().toISOString()
const backupInfo: BackupInfo = {
timestamp,
version: process.env.npm_package_version || 'unknown',
dataSize: 0,
backupPath: ''
}
// TODO: 实现具体的备份逻辑
logger.info('数据备份完成', backupInfo)
return backupInfo
}
/**
* 验证备份完整性
*/
static async validateBackup(backupInfo: BackupInfo): Promise<boolean> {
logger.info('验证备份完整性', backupInfo)
// TODO: 实现备份验证逻辑
return true
}
/**
* 恢复备份
*/
static async restoreBackup(backupInfo: BackupInfo): Promise<boolean> {
logger.info('开始恢复备份', backupInfo)
// TODO: 实现备份恢复逻辑
logger.info('备份恢复完成')
return true
}
/**
* 清理临时文件
*/
static async cleanup(): Promise<void> {
logger.info('清理迁移临时文件')
// TODO: 实现清理逻辑
logger.info('清理完成')
}
}
@@ -0,0 +1,125 @@
/**
* Type conversion utilities for migration
* Generated at: 2025-08-09T07:20:05.912Z
*/
import { loggerService } from '@logger'
const logger = loggerService.withContext('TypeConverter')
export class TypeConverter {
/**
* 转换值到指定类型
*/
convert(value: any, targetType: string): any {
if (value === null || value === undefined) {
return null
}
try {
switch (targetType) {
case 'boolean':
return this.toBoolean(value)
case 'string':
return this.toString(value)
case 'number':
return this.toNumber(value)
case 'array':
case 'unknown[]':
return this.toArray(value)
case 'object':
case 'Record<string, unknown>':
return this.toObject(value)
default:
// 未知类型,保持原样
logger.debug('未知类型,保持原值', { targetType, value })
return value
}
} catch (error) {
logger.error('类型转换失败', { value, targetType, error })
return value
}
}
private toBoolean(value: any): boolean {
if (typeof value === 'boolean') {
return value
}
if (typeof value === 'string') {
const lower = value.toLowerCase()
return lower === 'true' || lower === '1' || lower === 'yes'
}
if (typeof value === 'number') {
return value !== 0
}
return Boolean(value)
}
private toString(value: any): string {
if (typeof value === 'string') {
return value
}
if (typeof value === 'number' || typeof value === 'boolean') {
return String(value)
}
if (typeof value === 'object') {
return JSON.stringify(value)
}
return String(value)
}
private toNumber(value: any): number {
if (typeof value === 'number') {
return value
}
if (typeof value === 'string') {
const parsed = parseFloat(value)
if (isNaN(parsed)) {
logger.warn('字符串无法转换为数字', { value })
return 0
}
return parsed
}
if (typeof value === 'boolean') {
return value ? 1 : 0
}
return 0
}
private toArray(value: any): any[] {
if (Array.isArray(value)) {
return value
}
if (typeof value === 'string') {
try {
const parsed = JSON.parse(value)
return Array.isArray(parsed) ? parsed : [value]
} catch {
return [value]
}
}
return [value]
}
private toObject(value: any): Record<string, any> {
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return value
}
if (typeof value === 'string') {
try {
const parsed = JSON.parse(value)
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
? parsed
: { value }
} catch {
return { value }
}
}
return { value }
}
}