mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-07 13:30:57 +08:00
feat: 数据表升级改为在主进程升级
This commit is contained in:
@@ -121,8 +121,10 @@ export function openDatabase(sessionId: string, readonly = true): Database.Datab
|
||||
/**
|
||||
* 打开数据库并执行迁移(如果需要)
|
||||
* 用于需要写入的场景
|
||||
* @param sessionId 会话ID
|
||||
* @param forceRepair 是否强制修复(即使版本号已是最新也重新执行迁移脚本)
|
||||
*/
|
||||
export function openDatabaseWithMigration(sessionId: string): Database.Database | null {
|
||||
export function openDatabaseWithMigration(sessionId: string, forceRepair = false): Database.Database | null {
|
||||
const dbPath = getDbPath(sessionId)
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
return null
|
||||
@@ -132,7 +134,7 @@ export function openDatabaseWithMigration(sessionId: string): Database.Database
|
||||
db.pragma('journal_mode = WAL')
|
||||
|
||||
// 执行迁移
|
||||
migrateDatabase(db)
|
||||
migrateDatabase(db, forceRepair)
|
||||
|
||||
return db
|
||||
}
|
||||
@@ -368,13 +370,19 @@ export function getDbDirectory(): string {
|
||||
|
||||
/**
|
||||
* 检查是否有数据库需要迁移
|
||||
* @returns 需要迁移的数据库数量和最低版本
|
||||
* @returns 需要迁移的数据库数量、列表、最低版本和需要强制修复的列表
|
||||
*/
|
||||
export function checkMigrationNeeded(): { count: number; sessionIds: string[]; lowestVersion: number } {
|
||||
export function checkMigrationNeeded(): {
|
||||
count: number
|
||||
sessionIds: string[]
|
||||
lowestVersion: number
|
||||
forceRepairIds: string[]
|
||||
} {
|
||||
ensureDbDir()
|
||||
const dbDir = getDbDir()
|
||||
const files = fs.readdirSync(dbDir).filter((f) => f.endsWith('.db'))
|
||||
const needsMigrationList: string[] = []
|
||||
const forceRepairList: string[] = []
|
||||
let lowestVersion = CURRENT_SCHEMA_VERSION
|
||||
|
||||
for (const file of files) {
|
||||
@@ -385,18 +393,28 @@ export function checkMigrationNeeded(): { count: number; sessionIds: string[]; l
|
||||
const db = new Database(dbPath, { readonly: true })
|
||||
db.pragma('journal_mode = WAL')
|
||||
|
||||
// 获取当前 schema_version
|
||||
const metaTableInfo = db.prepare('PRAGMA table_info(meta)').all() as Array<{ name: string }>
|
||||
const hasVersionColumn = metaTableInfo.some((col) => col.name === 'schema_version')
|
||||
let dbVersion = 0
|
||||
if (hasVersionColumn) {
|
||||
const result = db.prepare('SELECT schema_version FROM meta LIMIT 1').get() as
|
||||
| { schema_version: number | null }
|
||||
| undefined
|
||||
dbVersion = result?.schema_version ?? 0
|
||||
}
|
||||
|
||||
// 检查 message 表是否有 reply_to_message_id 列
|
||||
const messageTableInfo = db.prepare('PRAGMA table_info(message)').all() as Array<{ name: string }>
|
||||
const hasReplyColumn = messageTableInfo.some((col) => col.name === 'reply_to_message_id')
|
||||
|
||||
if (needsMigration(db)) {
|
||||
needsMigrationList.push(sessionId)
|
||||
// 获取这个数据库的版本
|
||||
const tableInfo = db.prepare('PRAGMA table_info(meta)').all() as Array<{ name: string }>
|
||||
const hasVersionColumn = tableInfo.some((col) => col.name === 'schema_version')
|
||||
let dbVersion = 0
|
||||
if (hasVersionColumn) {
|
||||
const result = db.prepare('SELECT schema_version FROM meta LIMIT 1').get() as
|
||||
| { schema_version: number | null }
|
||||
| undefined
|
||||
dbVersion = result?.schema_version ?? 0
|
||||
}
|
||||
lowestVersion = Math.min(lowestVersion, dbVersion)
|
||||
} else if (!hasReplyColumn) {
|
||||
// 特殊情况:版本号已更新但列不存在,需要强制修复
|
||||
needsMigrationList.push(sessionId)
|
||||
forceRepairList.push(sessionId)
|
||||
lowestVersion = Math.min(lowestVersion, dbVersion)
|
||||
}
|
||||
|
||||
@@ -406,7 +424,7 @@ export function checkMigrationNeeded(): { count: number; sessionIds: string[]; l
|
||||
}
|
||||
}
|
||||
|
||||
return { count: needsMigrationList.length, sessionIds: needsMigrationList, lowestVersion }
|
||||
return { count: needsMigrationList.length, sessionIds: needsMigrationList, lowestVersion, forceRepairIds: forceRepairList }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,7 +432,8 @@ export function checkMigrationNeeded(): { count: number; sessionIds: string[]; l
|
||||
* @returns 迁移结果
|
||||
*/
|
||||
export function migrateAllDatabases(): { success: boolean; migratedCount: number; error?: string } {
|
||||
const { sessionIds } = checkMigrationNeeded()
|
||||
const { sessionIds, forceRepairIds } = checkMigrationNeeded()
|
||||
const forceRepairSet = new Set(forceRepairIds)
|
||||
|
||||
if (sessionIds.length === 0) {
|
||||
return { success: true, migratedCount: 0 }
|
||||
@@ -424,7 +443,8 @@ export function migrateAllDatabases(): { success: boolean; migratedCount: number
|
||||
|
||||
for (const sessionId of sessionIds) {
|
||||
try {
|
||||
const db = openDatabaseWithMigration(sessionId)
|
||||
const needsForceRepair = forceRepairSet.has(sessionId)
|
||||
const db = openDatabaseWithMigration(sessionId, needsForceRepair)
|
||||
if (db) {
|
||||
db.close()
|
||||
migratedCount++
|
||||
|
||||
@@ -136,39 +136,36 @@ function setSchemaVersion(db: Database.Database, version: number): void {
|
||||
* 自动检测当前版本并执行所有需要的迁移
|
||||
*
|
||||
* @param db 数据库连接
|
||||
* @param forceRepair 是否强制修复(即使版本号已是最新也重新执行迁移脚本)
|
||||
* @returns 是否执行了迁移
|
||||
*/
|
||||
export function migrateDatabase(db: Database.Database): boolean {
|
||||
export function migrateDatabase(db: Database.Database, forceRepair = false): boolean {
|
||||
const currentVersion = getSchemaVersion(db)
|
||||
|
||||
// 已是最新版本,无需迁移
|
||||
if (currentVersion >= CURRENT_SCHEMA_VERSION) {
|
||||
// 如果不是强制修复模式,检查版本号
|
||||
if (!forceRepair && currentVersion >= CURRENT_SCHEMA_VERSION) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 获取需要执行的迁移
|
||||
const pendingMigrations = migrations.filter((m) => m.version > currentVersion)
|
||||
// 如果是强制修复,从 version 0 开始执行所有迁移
|
||||
const pendingMigrations = forceRepair
|
||||
? migrations
|
||||
: migrations.filter((m) => m.version > currentVersion)
|
||||
|
||||
if (pendingMigrations.length === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[Migration] 数据库版本 ${currentVersion} -> ${CURRENT_SCHEMA_VERSION},执行 ${pendingMigrations.length} 个迁移`
|
||||
)
|
||||
|
||||
// 在事务中执行所有迁移
|
||||
const migrate = db.transaction(() => {
|
||||
for (const migration of pendingMigrations) {
|
||||
console.log(`[Migration] 执行迁移 v${migration.version}: ${migration.description}`)
|
||||
migration.up(db)
|
||||
setSchemaVersion(db, migration.version)
|
||||
}
|
||||
})
|
||||
|
||||
migrate()
|
||||
|
||||
console.log(`[Migration] 迁移完成,当前版本: ${CURRENT_SCHEMA_VERSION}`)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import mainIpcMain from './ipcMain'
|
||||
import { initAnalytics, trackDailyActive } from './analytics'
|
||||
import { initProxy } from './network/proxy'
|
||||
import { needsLegacyMigration, migrateFromLegacyDir, ensureAppDirs } from './paths'
|
||||
import { migrateAllDatabases, checkMigrationNeeded } from './database/core'
|
||||
|
||||
class MainProcess {
|
||||
mainWindow: BrowserWindow | null
|
||||
@@ -54,6 +55,9 @@ class MainProcess {
|
||||
// 确保应用目录存在
|
||||
ensureAppDirs()
|
||||
|
||||
// 执行数据库 schema 迁移(确保所有数据库在 Worker 查询前已是最新 schema)
|
||||
this.migrateDatabasesIfNeeded()
|
||||
|
||||
initProxy() // 初始化代理配置
|
||||
|
||||
// 注册应用协议
|
||||
@@ -81,6 +85,21 @@ class MainProcess {
|
||||
}
|
||||
}
|
||||
|
||||
// 执行数据库 schema 迁移(静默迁移)
|
||||
migrateDatabasesIfNeeded() {
|
||||
try {
|
||||
const { count } = checkMigrationNeeded()
|
||||
if (count > 0) {
|
||||
const result = migrateAllDatabases()
|
||||
if (!result.success) {
|
||||
console.error('[Main] Database schema migration failed:', result.error)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Main] Error in migrateDatabasesIfNeeded:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建主窗口
|
||||
async createWindow() {
|
||||
this.mainWindow = new BrowserWindow({
|
||||
|
||||
Reference in New Issue
Block a user