//! 语音识别历史 SQLite use std::path::PathBuf; use std::sync::Mutex; use chrono::Utc; use rusqlite::{params, Connection}; use serde::Serialize; use thiserror::Error; const MAX_RECORDS: i64 = 50; #[derive(Debug, Error)] pub enum AsrDbError { #[error("{0}")] Db(#[from] rusqlite::Error), #[error("{0}")] Message(String), } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct AsrHistoryRecord { pub id: i64, pub name: String, pub source_path: String, pub result_text: String, pub created_at: String, } #[derive(Debug)] pub struct AsrDb { conn: Mutex, } impl AsrDb { pub fn open(path: PathBuf) -> Result { if let Some(parent) = path.parent() { std::fs::create_dir_all(parent).map_err(|e| { AsrDbError::Message(format!("创建数据库目录失败: {e}")) })?; } let conn = Connection::open(path)?; conn.execute_batch( r#" PRAGMA journal_mode = WAL; CREATE TABLE IF NOT EXISTS asr_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL DEFAULT '', source_path TEXT NOT NULL, result_text TEXT NOT NULL DEFAULT '', created_at TEXT NOT NULL ); "#, )?; Ok(Self { conn: Mutex::new(conn), }) } pub fn list(&self) -> Result, AsrDbError> { let conn = self.conn.lock().map_err(|_| { AsrDbError::Message("ASR 数据库锁异常".into()) })?; let mut stmt = conn.prepare( "SELECT id, name, source_path, result_text, created_at FROM asr_history ORDER BY id DESC LIMIT ?1", )?; let rows = stmt.query_map(params![MAX_RECORDS], |row| { Ok(AsrHistoryRecord { id: row.get(0)?, name: row.get(1)?, source_path: row.get(2)?, result_text: row.get(3)?, created_at: row.get(4)?, }) })?; let mut items = Vec::new(); for row in rows { items.push(row?); } Ok(items) } pub fn insert( &self, name: &str, source_path: &str, result_text: &str, ) -> Result { let source_path = source_path.trim(); if source_path.is_empty() { return Err(AsrDbError::Message("源文件路径不能为空".into())); } let name = name.trim(); let name = if name.is_empty() { source_path .replace('\\', "/") .rsplit('/') .next() .unwrap_or("语音识别") .to_string() } else { name.to_string() }; let created_at = Utc::now().to_rfc3339(); let conn = self.conn.lock().map_err(|_| { AsrDbError::Message("ASR 数据库锁异常".into()) })?; conn.execute( "INSERT INTO asr_history (name, source_path, result_text, created_at) VALUES (?1, ?2, ?3, ?4)", params![name, source_path, result_text.trim(), created_at], )?; let id = conn.last_insert_rowid(); self.prune_excess(&conn)?; Ok(AsrHistoryRecord { id, name, source_path: source_path.to_string(), result_text: result_text.trim().to_string(), created_at, }) } pub fn update_name(&self, id: i64, name: &str) -> Result<(), AsrDbError> { let name = name.trim(); if name.is_empty() { return Err(AsrDbError::Message("名称不能为空".into())); } let conn = self.conn.lock().map_err(|_| { AsrDbError::Message("ASR 数据库锁异常".into()) })?; let n = conn.execute( "UPDATE asr_history SET name = ?1 WHERE id = ?2", params![name, id], )?; if n == 0 { return Err(AsrDbError::Message("记录不存在".into())); } Ok(()) } fn prune_excess(&self, conn: &Connection) -> Result<(), AsrDbError> { let count: i64 = conn.query_row( "SELECT COUNT(1) FROM asr_history", [], |row| row.get(0), )?; if count <= MAX_RECORDS { return Ok(()); } let excess = count - MAX_RECORDS; conn.execute( "DELETE FROM asr_history WHERE id IN ( SELECT id FROM asr_history ORDER BY id ASC LIMIT ?1 )", params![excess], )?; Ok(()) } pub fn delete(&self, id: i64) -> Result { let conn = self.conn.lock().map_err(|_| { AsrDbError::Message("ASR 数据库锁异常".into()) })?; let n = conn.execute("DELETE FROM asr_history WHERE id = ?1", params![id])?; Ok(n > 0) } }