import * as SQLite from 'expo-sqlite'; import { LockManager } from './LockManager'; import { DB_CONFIG, getDbName } from './DatabaseConfig'; import { runMigrations } from '../schema/MigrationManager'; type SQLiteDatabase = SQLite.SQLiteDatabase; class DatabaseManager { private static instance: DatabaseManager; private db: SQLiteDatabase | null = null; private currentUserId: string | null = null; private initPromise: Promise | null = null; private initTargetUserId: string | null = null; private isInitialized = false; static getInstance(): DatabaseManager { if (!DatabaseManager.instance) { DatabaseManager.instance = new DatabaseManager(); } return DatabaseManager.instance; } async initialize(userId?: string | null): Promise { const targetUserId = userId || null; const callId = Date.now(); console.log(`[DatabaseManager ${callId}] initialize 开始, userId=${targetUserId}, 当前db=${!!this.db}, 当前userId=${this.currentUserId}`); if (this.db && this.currentUserId === targetUserId) { console.log(`[DatabaseManager ${callId}] 已是目标数据库,直接返回`); this.isInitialized = true; return; } if (this.initPromise && this.initTargetUserId === targetUserId) { console.log(`[DatabaseManager ${callId}] 等待进行中的初始化`); await this.initPromise; console.log(`[DatabaseManager ${callId}] 初始化完成`); return; } if (this.initPromise) { console.log(`[DatabaseManager ${callId}] 等待其他初始化完成`); await this.initPromise.catch(() => {}); } if (this.db && this.currentUserId === targetUserId) { console.log(`[DatabaseManager ${callId}] 其他初始化已完成目标,直接返回`); this.isInitialized = true; return; } this.initTargetUserId = targetUserId; console.log(`[DatabaseManager ${callId}] 开始实际初始化`); this.initPromise = LockManager.withMutex(async () => { const mutexId = callId; console.log(`[DatabaseManager ${mutexId}] 获得互斥锁`); if (this.db && this.currentUserId === targetUserId) { console.log(`[DatabaseManager ${mutexId}] 互斥锁内检查:已是目标数据库`); this.isInitialized = true; return; } const isWeb = typeof window !== 'undefined'; if (this.db) { console.log(`[DatabaseManager ${mutexId}] 关闭旧数据库连接...`); try { await this.db.closeAsync(); console.log(`[DatabaseManager ${mutexId}] 旧连接已关闭`); } catch (e) { console.warn(`[DatabaseManager ${mutexId}] 关闭旧连接失败:`, e); } this.db = null; this.currentUserId = null; this.isInitialized = false; if (isWeb) { await new Promise(r => setTimeout(r, DB_CONFIG.WEB_CLOSE_DELAY)); } } const dbName = getDbName(targetUserId); console.log(`[DatabaseManager ${mutexId}] 打开数据库: ${dbName}`); const database = await this.openDatabaseWithRetry(dbName); console.log(`[DatabaseManager ${mutexId}] 数据库已打开,执行迁移`); await runMigrations(database); console.log(`[DatabaseManager ${mutexId}] 迁移完成`); this.db = database; this.currentUserId = targetUserId; this.isInitialized = true; console.log(`[DatabaseManager ${mutexId}] 初始化完成`); }).finally(() => { console.log(`[DatabaseManager ${callId}] 清理初始化状态`); this.initPromise = null; this.initTargetUserId = null; }); await this.initPromise; console.log(`[DatabaseManager ${callId}] initialize 返回`); } private async openDatabaseWithRetry(dbName: string): Promise { const isWeb = typeof window !== 'undefined'; return LockManager.withWebLock(`${DB_CONFIG.LOCK_PREFIX}${dbName}`, async () => { for (let attempt = 0; attempt <= DB_CONFIG.MAX_RETRIES; attempt++) { try { const database = await SQLite.openDatabaseAsync(dbName); console.log(`[DatabaseManager] 成功打开数据库 (尝试 ${attempt + 1}/${DB_CONFIG.MAX_RETRIES + 1})`); return database; } catch (error) { const msg = String(error); console.error(`[DatabaseManager] 打开失败 (尝试 ${attempt + 1}/${DB_CONFIG.MAX_RETRIES + 1}):`, msg); if (attempt < DB_CONFIG.MAX_RETRIES) { const delay = isWeb ? DB_CONFIG.WEB_RETRY_DELAY_BASE * (attempt + 1) : DB_CONFIG.NATIVE_RETRY_DELAY; console.warn(`[DatabaseManager] ${delay}ms 后重试...`); await new Promise(r => setTimeout(r, delay)); } else { throw error; } } } throw new Error('打开数据库失败'); }); } async close(): Promise { if (this.initPromise) { await this.initPromise.catch(() => {}); } const dbName = getDbName(this.currentUserId); await LockManager.withWebLock(`${DB_CONFIG.LOCK_PREFIX}${dbName}`, async () => { await LockManager.withMutex(async () => { if (!this.db) return; try { await this.db.closeAsync(); console.log('[DatabaseManager] 数据库已关闭'); if (typeof window !== 'undefined') { await new Promise(r => setTimeout(r, DB_CONFIG.WEB_FINAL_CLOSE_DELAY)); } } catch (e) { console.warn('[DatabaseManager] 关闭失败:', e); } this.db = null; this.currentUserId = null; this.isInitialized = false; }); }); } async getDb(timeout: number = 10000): Promise { if (this.isInitialized && this.db) return this.db; if (this.initPromise) { const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('等待数据库初始化超时')), timeout) ); await Promise.race([this.initPromise, timeoutPromise]); if (this.db) return this.db; } throw new Error('数据库未初始化,请先调用 initialize(userId)'); } getDbSync(): SQLiteDatabase | null { return this.db; } getCurrentUserId(): string | null { return this.currentUserId; } isReady(): boolean { return this.isInitialized && this.db !== null; } async withMutex(fn: () => Promise): Promise { return LockManager.withMutex(fn); } } export const databaseManager = DatabaseManager.getInstance(); export { DatabaseManager };