From f8dfad26a4e9dd3457274e4ffa0a659be426f462 Mon Sep 17 00:00:00 2001 From: zhaojunlong <5482498@qq.com> Date: Thu, 19 Mar 2026 15:12:55 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(resultStore):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E6=96=87=E4=BB=B6=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=92=8C=E5=85=BC=E5=AE=B9=E6=80=A7=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加对旧版 results.sqlite 到新版 results.db 的自动迁移支持 - 实现数据库连接时的错误处理和日志记录 - 修改数据库事务模式为 DELETE 模式以提高稳定性 - 增加对临时 WAL 和 SHM 文件的清理处理 - 提供环境变量配置的数据库路径优先级支持 ``` --- src/resultStore.js | 75 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/src/resultStore.js b/src/resultStore.js index 628587c..2065b83 100644 --- a/src/resultStore.js +++ b/src/resultStore.js @@ -1,15 +1,23 @@ import Database from 'better-sqlite3'; -import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; +import { + copyFileSync, + existsSync, + mkdirSync, + readFileSync, + renameSync, + unlinkSync, + writeFileSync, +} from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const DB_PATH = - process.env.APP_DB_PATH || - process.env.RESULTS_DB_PATH || - join(__dirname, '..', 'data', 'results.sqlite'); +const DEFAULT_DB_DIR = join(__dirname, '..', 'data'); +const DEFAULT_DB_PATH = join(DEFAULT_DB_DIR, 'results.db'); +const LEGACY_DB_PATH = join(DEFAULT_DB_DIR, 'results.sqlite'); +const DB_PATH = resolveDbPath(); const CONFIG_PATH = join(__dirname, '..', 'config.json'); const MAX_RESULT_RECORDS = 500; const DEFAULT_TASK_MODE = 'qwen3.5-plus'; @@ -22,6 +30,55 @@ function clone(value) { return JSON.parse(JSON.stringify(value)); } +function removeFileIfExists(filePath) { + if (!existsSync(filePath)) return; + unlinkSync(filePath); +} + +function migrateLegacyDbIfNeeded(nextPath, legacyPath) { + if (existsSync(nextPath) || !existsSync(legacyPath)) return nextPath; + + const legacyDb = new Database(legacyPath); + + try { + legacyDb.pragma('wal_checkpoint(TRUNCATE)'); + } catch (_error) { + // Ignore checkpoint failures and still attempt to switch to single-file mode. + } + + legacyDb.pragma('journal_mode = DELETE'); + legacyDb.close(); + + try { + renameSync(legacyPath, nextPath); + } catch (_error) { + copyFileSync(legacyPath, nextPath); + } + + removeFileIfExists(`${legacyPath}-shm`); + removeFileIfExists(`${legacyPath}-wal`); + + return nextPath; +} + +function resolveDbPath() { + const explicitPath = process.env.APP_DB_PATH || process.env.RESULTS_DB_PATH; + if (explicitPath) return explicitPath; + + mkdirSync(DEFAULT_DB_DIR, { recursive: true }); + + try { + return migrateLegacyDbIfNeeded(DEFAULT_DB_PATH, LEGACY_DB_PATH); + } catch (error) { + if (existsSync(LEGACY_DB_PATH)) { + console.warn(`[resultStore] Legacy database migration skipped: ${error.message}`); + return LEGACY_DB_PATH; + } + + throw error; + } +} + function generateResultId() { return `result-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`; } @@ -141,7 +198,13 @@ function getDb() { mkdirSync(dirname(DB_PATH), { recursive: true }); db = new Database(DB_PATH); - db.pragma('journal_mode = WAL'); + + try { + db.pragma('journal_mode = DELETE'); + } catch (error) { + console.warn(`[resultStore] Database journal mode unchanged: ${error.message}`); + } + return db; }