import { DatabaseInterface, UploadTask } from './types'; export class SQLiteDatabase implements DatabaseInterface { private db: any; constructor() { // 动态导入,避免在Web环境下加载 try { const SQLite = require('expo-sqlite'); this.db = SQLite.openDatabaseSync('upload_status.db'); this.db.execSync('PRAGMA busy_timeout = 5000;'); } catch (error) { console.error('Failed to initialize SQLite:', error); throw new Error('SQLite is not available in this environment'); } } async initUploadTable(): Promise { console.log('Initializing upload tasks table...'); await this.db.execAsync(` CREATE TABLE IF NOT EXISTS upload_tasks ( uri TEXT PRIMARY KEY NOT NULL, filename TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', progress INTEGER NOT NULL DEFAULT 0, file_id TEXT, created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) ); `); // Add created_at column to existing table if it doesn't exist const columns = await this.db.getAllAsync('PRAGMA table_info(upload_tasks);'); const columnExists = columns.some((column: any) => column.name === 'created_at'); if (!columnExists) { console.log('Adding created_at column to upload_tasks table...'); await this.db.execAsync(`ALTER TABLE upload_tasks ADD COLUMN created_at INTEGER;`); await this.db.execAsync(`UPDATE upload_tasks SET created_at = (strftime('%s', 'now')) WHERE created_at IS NULL;`); console.log('created_at column added and populated.'); } console.log('Upload tasks table initialized'); await this.db.execAsync(` CREATE TABLE IF NOT EXISTS app_state ( key TEXT PRIMARY KEY NOT NULL, value TEXT ); `); console.log('App state table initialized'); } async insertUploadTask(task: Omit): Promise { console.log('Inserting upload task:', task.uri); await this.db.runAsync( 'INSERT OR REPLACE INTO upload_tasks (uri, filename, status, progress, file_id, created_at) VALUES (?, ?, ?, ?, ?, ?)', [task.uri, task.filename, task.status, task.progress, task.file_id ?? null, Math.floor(Date.now() / 1000)] ); } async getUploadTaskStatus(uri: string): Promise { console.log('Checking upload task status for:', uri); const result = await this.db.getFirstAsync( 'SELECT uri, filename, status, progress, file_id, created_at FROM upload_tasks WHERE uri = ?;', uri ); return result || null; } async updateUploadTaskStatus(uri: string, status: UploadTask['status'], file_id?: string): Promise { if (file_id) { await this.db.runAsync('UPDATE upload_tasks SET status = ?, file_id = ? WHERE uri = ?', [status, file_id, uri]); } else { await this.db.runAsync('UPDATE upload_tasks SET status = ? WHERE uri = ?', [status, uri]); } } async updateUploadTaskProgress(uri: string, progress: number): Promise { await this.db.runAsync('UPDATE upload_tasks SET progress = ? WHERE uri = ?', [progress, uri]); } async getUploadTasks(): Promise { console.log('Fetching all upload tasks... time:', new Date().toLocaleString()); const results = await this.db.getAllAsync( 'SELECT uri, filename, status, progress, file_id, created_at FROM upload_tasks ORDER BY created_at DESC;' ); return results; } async cleanUpUploadTasks(): Promise { console.log('Cleaning up completed/failed upload tasks...'); await this.db.runAsync( "DELETE FROM upload_tasks WHERE status = 'success' OR status = 'failed' OR status = 'skipped';" ); } async getUploadTasksSince(timestamp: number): Promise { const rows = await this.db.getAllAsync( 'SELECT * FROM upload_tasks WHERE created_at >= ? ORDER BY created_at DESC', [timestamp] ); return rows; } async exist_pending_tasks(): Promise { const rows = await this.db.getAllAsync( 'SELECT * FROM upload_tasks WHERE status = "pending" OR status = "uploading"' ); return rows.length > 0; } async filterExistingFiles(fileUris: string[]): Promise { if (fileUris.length === 0) { return []; } const placeholders = fileUris.map(() => '?').join(','); const query = `SELECT uri FROM upload_tasks WHERE uri IN (${placeholders}) AND status = 'success'`; const existingFiles = await this.db.getAllAsync(query, fileUris); const existingUris = new Set(existingFiles.map((f: { uri: string }) => f.uri)); const newFileUris = fileUris.filter(uri => !existingUris.has(uri)); console.log(`[DB] Total files: ${fileUris.length}, Existing successful files: ${existingUris.size}, New files to upload: ${newFileUris.length}`); return newFileUris; } async setAppState(key: string, value: string | null): Promise { console.log(`Setting app state: ${key} = ${value}`); await this.db.runAsync('INSERT OR REPLACE INTO app_state (key, value) VALUES (?, ?)', [key, value]); } async getAppState(key: string): Promise { const result = await this.db.getFirstAsync('SELECT value FROM app_state WHERE key = ?;', key); return result?.value ?? null; } async executeSql(sql: string, params: any[] = []): Promise { try { const isSelect = sql.trim().toLowerCase().startsWith('select'); if (isSelect) { const results = this.db.getAllSync(sql, params); return results; } else { const result = this.db.runSync(sql, params); return { changes: result.changes, lastInsertRowId: result.lastInsertRowId, }; } } catch (error: any) { console.error("Error executing SQL:", error); return { error: error.message }; } } }