169 lines
5.0 KiB
Rust
169 lines
5.0 KiB
Rust
//! 工作流生成的语音 SQLite 存储
|
|
|
|
use std::path::PathBuf;
|
|
use std::sync::Mutex;
|
|
|
|
use chrono::{Datelike, Local, Timelike};
|
|
use rusqlite::{params, Connection};
|
|
use serde::Serialize;
|
|
use thiserror::Error;
|
|
|
|
const MAX_RECORDS: i64 = 50;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum AudioDbError {
|
|
#[error("{0}")]
|
|
Db(#[from] rusqlite::Error),
|
|
#[error("{0}")]
|
|
Message(String),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct GeneratedAudioRecord {
|
|
pub id: i64,
|
|
pub name: String,
|
|
pub file_path: String,
|
|
pub created_at: String,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct AudioDb {
|
|
conn: Mutex<Connection>,
|
|
}
|
|
|
|
impl AudioDb {
|
|
pub fn open(path: PathBuf) -> Result<Self, AudioDbError> {
|
|
if let Some(parent) = path.parent() {
|
|
std::fs::create_dir_all(parent).map_err(|e| {
|
|
AudioDbError::Message(format!("创建数据库目录失败: {e}"))
|
|
})?;
|
|
}
|
|
let conn = Connection::open(path)?;
|
|
conn.execute_batch(
|
|
r#"
|
|
PRAGMA journal_mode = WAL;
|
|
CREATE TABLE IF NOT EXISTS generated_audios (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL DEFAULT '',
|
|
file_path TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
"#,
|
|
)?;
|
|
Ok(Self {
|
|
conn: Mutex::new(conn),
|
|
})
|
|
}
|
|
|
|
fn default_name() -> String {
|
|
let n = Local::now();
|
|
format!(
|
|
"{:02}-{:02} {:02}:{:02}",
|
|
n.month(),
|
|
n.day(),
|
|
n.hour(),
|
|
n.minute()
|
|
)
|
|
}
|
|
|
|
pub fn list(&self) -> Result<Vec<GeneratedAudioRecord>, AudioDbError> {
|
|
let conn = self.conn.lock().map_err(|_| {
|
|
AudioDbError::Message("语音数据库锁异常".into())
|
|
})?;
|
|
let mut stmt = conn.prepare(
|
|
"SELECT id, name, file_path, created_at FROM generated_audios ORDER BY id DESC LIMIT ?1",
|
|
)?;
|
|
let rows = stmt.query_map(params![MAX_RECORDS], |row| {
|
|
Ok(GeneratedAudioRecord {
|
|
id: row.get(0)?,
|
|
name: row.get(1)?,
|
|
file_path: row.get(2)?,
|
|
created_at: row.get(3)?,
|
|
})
|
|
})?;
|
|
let mut items = Vec::new();
|
|
for row in rows {
|
|
items.push(row?);
|
|
}
|
|
Ok(items)
|
|
}
|
|
|
|
pub fn get(&self, id: i64) -> Result<Option<GeneratedAudioRecord>, AudioDbError> {
|
|
let conn = self.conn.lock().map_err(|_| {
|
|
AudioDbError::Message("语音数据库锁异常".into())
|
|
})?;
|
|
let mut stmt = conn.prepare(
|
|
"SELECT id, name, file_path, created_at FROM generated_audios WHERE id = ?1",
|
|
)?;
|
|
let mut rows = stmt.query_map(params![id], |row| {
|
|
Ok(GeneratedAudioRecord {
|
|
id: row.get(0)?,
|
|
name: row.get(1)?,
|
|
file_path: row.get(2)?,
|
|
created_at: row.get(3)?,
|
|
})
|
|
})?;
|
|
match rows.next() {
|
|
Some(row) => Ok(Some(row?)),
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
|
|
pub fn insert(&self, name: &str, file_path: &str) -> Result<GeneratedAudioRecord, AudioDbError> {
|
|
let file_path = file_path.trim();
|
|
if file_path.is_empty() {
|
|
return Err(AudioDbError::Message("文件路径不能为空".into()));
|
|
}
|
|
let name = name.trim();
|
|
let name = if name.is_empty() {
|
|
Self::default_name()
|
|
} else {
|
|
name.to_string()
|
|
};
|
|
let created_at = chrono::Utc::now().to_rfc3339();
|
|
let conn = self.conn.lock().map_err(|_| {
|
|
AudioDbError::Message("语音数据库锁异常".into())
|
|
})?;
|
|
conn.execute(
|
|
"INSERT INTO generated_audios (name, file_path, created_at) VALUES (?1, ?2, ?3)",
|
|
params![name, file_path, created_at],
|
|
)?;
|
|
let id = conn.last_insert_rowid();
|
|
self.prune_excess(&conn)?;
|
|
Ok(GeneratedAudioRecord {
|
|
id,
|
|
name,
|
|
file_path: file_path.to_string(),
|
|
created_at,
|
|
})
|
|
}
|
|
|
|
fn prune_excess(&self, conn: &Connection) -> Result<(), AudioDbError> {
|
|
let count: i64 = conn.query_row(
|
|
"SELECT COUNT(1) FROM generated_audios",
|
|
[],
|
|
|row| row.get(0),
|
|
)?;
|
|
if count <= MAX_RECORDS {
|
|
return Ok(());
|
|
}
|
|
let excess = count - MAX_RECORDS;
|
|
conn.execute(
|
|
"DELETE FROM generated_audios WHERE id IN (
|
|
SELECT id FROM generated_audios ORDER BY id ASC LIMIT ?1
|
|
)",
|
|
params![excess],
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn delete(&self, id: i64) -> Result<bool, AudioDbError> {
|
|
let conn = self.conn.lock().map_err(|_| {
|
|
AudioDbError::Message("语音数据库锁异常".into())
|
|
})?;
|
|
let n = conn.execute("DELETE FROM generated_audios WHERE id = ?1", params![id])?;
|
|
Ok(n > 0)
|
|
}
|
|
}
|