()
| 214 | * @returns 需要迁移的数据库数量、列表、最低版本和需要强制修复的列表 |
| 215 | */ |
| 216 | export function checkMigrationNeeded(): { |
| 217 | count: number |
| 218 | sessionIds: string[] |
| 219 | lowestVersion: number |
| 220 | forceRepairIds: string[] |
| 221 | } { |
| 222 | ensureDbDir() |
| 223 | const dbDir = getDbDir() |
| 224 | const files = fs.readdirSync(dbDir).filter((f) => f.endsWith('.db')) |
| 225 | const needsMigrationList: string[] = [] |
| 226 | const forceRepairList: string[] = [] |
| 227 | let lowestVersion = CURRENT_SCHEMA_VERSION |
| 228 | |
| 229 | for (const file of files) { |
| 230 | const sessionId = file.replace('.db', '') |
| 231 | const dbPath = getDbPath(sessionId) |
| 232 | |
| 233 | try { |
| 234 | const db = new Database(dbPath, { readonly: true }) |
| 235 | db.pragma('journal_mode = WAL') |
| 236 | |
| 237 | // 仅迁移聊天会话数据库:这里最小依赖是 meta + message |
| 238 | // 这样可跳过非聊天库,同时避免把 member 缺失的异常库直接误归为“非聊天库” |
| 239 | const requiredTableCount = db |
| 240 | .prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name IN ('meta', 'message')") |
| 241 | .get() as { cnt: number } |
| 242 | const isChatSessionDb = requiredTableCount.cnt === 2 |
| 243 | if (!isChatSessionDb) { |
| 244 | db.close() |
| 245 | continue |
| 246 | } |
| 247 | |
| 248 | // 获取当前 schema_version |
| 249 | const metaTableInfo = db.prepare('PRAGMA table_info(meta)').all() as Array<{ name: string }> |
| 250 | const hasVersionColumn = metaTableInfo.some((col) => col.name === 'schema_version') |
| 251 | let dbVersion = 0 |
| 252 | if (hasVersionColumn) { |
| 253 | const result = db.prepare('SELECT schema_version FROM meta LIMIT 1').get() as |
| 254 | | { schema_version: number | null } |
| 255 | | undefined |
| 256 | dbVersion = result?.schema_version ?? 0 |
| 257 | } |
| 258 | |
| 259 | // 检查 message 表是否有 reply_to_message_id 列 |
| 260 | const messageTableInfo = db.prepare('PRAGMA table_info(message)').all() as Array<{ name: string }> |
| 261 | const hasReplyColumn = messageTableInfo.some((col) => col.name === 'reply_to_message_id') |
| 262 | |
| 263 | if (needsMigration(db)) { |
| 264 | needsMigrationList.push(sessionId) |
| 265 | lowestVersion = Math.min(lowestVersion, dbVersion) |
| 266 | } else if (!hasReplyColumn) { |
| 267 | // 特殊情况:版本号已更新但列不存在,需要强制修复 |
| 268 | needsMigrationList.push(sessionId) |
| 269 | forceRepairList.push(sessionId) |
| 270 | lowestVersion = Math.min(lowestVersion, dbVersion) |
| 271 | } |
| 272 | |
| 273 | db.close() |
no test coverage detected