// ZCLAW 数据库管理 import Database from 'better-sqlite3'; import { existsSync, mkdirSync } from 'fs'; import { dirname } from 'path'; import { SCHEMA_SQL } from './schema'; import { createLogger } from '../utils/logger'; const log = createLogger('Database'); let _db: Database.Database | null = null; export function initDatabase(dbPath: string): Database.Database { // 确保目录存在 const dir = dirname(dbPath); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } _db = new Database(dbPath); // 启用 WAL 模式提升性能 _db.pragma('journal_mode = WAL'); _db.pragma('foreign_keys = ON'); // 创建表 _db.exec(SCHEMA_SQL); log.info(`Database initialized at ${dbPath}`); return _db; } export function getDatabase(): Database.Database { if (!_db) { throw new Error('Database not initialized. Call initDatabase() first.'); } return _db; } export function closeDatabase(): void { if (_db) { _db.close(); _db = null; log.info('Database closed'); } } // 通用 CRUD 辅助 export class BaseDAO> { constructor( protected tableName: string, protected db: Database.Database = getDatabase() ) {} findById(id: string): T | undefined { return this.db.prepare(`SELECT * FROM ${this.tableName} WHERE id = ?`).get(id) as T | undefined; } findAll(where?: string, params?: any[]): T[] { const sql = where ? `SELECT * FROM ${this.tableName} WHERE ${where}` : `SELECT * FROM ${this.tableName}`; return (params ? this.db.prepare(sql).all(...params) : this.db.prepare(sql).all()) as T[]; } insert(data: Partial): void { const keys = Object.keys(data); const placeholders = keys.map(() => '?').join(', '); const sql = `INSERT OR REPLACE INTO ${this.tableName} (${keys.join(', ')}) VALUES (${placeholders})`; this.db.prepare(sql).run(...Object.values(data)); } update(id: string, data: Partial): void { const sets = Object.keys(data).map(k => `${k} = ?`).join(', '); const sql = `UPDATE ${this.tableName} SET ${sets} WHERE id = ?`; this.db.prepare(sql).run(...Object.values(data), id); } delete(id: string): void { this.db.prepare(`DELETE FROM ${this.tableName} WHERE id = ?`).run(id); } count(where?: string, params?: any[]): number { const sql = where ? `SELECT COUNT(*) as count FROM ${this.tableName} WHERE ${where}` : `SELECT COUNT(*) as count FROM ${this.tableName}`; const result = params ? this.db.prepare(sql).get(...params) as { count: number } : this.db.prepare(sql).get() as { count: number }; return result.count; } }