diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index aedf1b9..3d4a71e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -488,6 +488,17 @@ dependencies = [ "rand_core", ] +[[package]] +name = "chardetng" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de944a44b5064ee5d3a5ceccc49a41bfec50f2580e66f82e87703acdb88b53" +dependencies = [ + "cfg-if", + "encoding_rs", + "memchr", +] + [[package]] name = "chrono" version = "0.4.45" @@ -1024,6 +1035,15 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "endi" version = "1.1.1" @@ -3829,7 +3849,10 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" name = "stealthreader" version = "0.1.0" dependencies = [ + "chardetng", "chrono", + "encoding_rs", + "regex", "serde", "serde_json", "sqlx", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 61a3615..e6f9852 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -36,3 +36,6 @@ sqlx = { version = "0.9.0", features = [ ] } chrono = "0.4.45" tauri-plugin-dialog = "2.7.2" +encoding_rs = "0.8.35" +regex = "1.13.1" +chardetng = "1.0.0" diff --git a/src-tauri/src/commands/book.rs b/src-tauri/src/commands/book.rs index bf3bdc9..1f72208 100644 --- a/src-tauri/src/commands/book.rs +++ b/src-tauri/src/commands/book.rs @@ -1,7 +1,25 @@ use tauri_helper::auto_collect_command; +use crate::models::ApiResponse; + +/** + * 导入书籍 + * @param path 书籍文件路径 + * @returns 导入结果 + */ #[tauri::command] #[auto_collect_command] -pub async fn import_book() -> Result<(), String> { - Ok(()) -} \ No newline at end of file +pub async fn import_book(path: String) -> Result, String> { + let content = std::fs::read_to_string(&path).map_err(|e| e.to_string()); + Ok(ApiResponse::success(())) +} + +/** + * 获取书籍列表 + * @returns 书籍列表 + */ +#[tauri::command] +#[auto_collect_command] +pub async fn book_list() -> Result>, String> { + Ok(ApiResponse::success(Vec::new())) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5451547..f8c386e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -13,7 +13,7 @@ pub fn run() { .plugin(tauri_plugin_dialog::init()) .setup(|app| { let pool = tauri::async_runtime::block_on(async { - init_pool().await.expect("sqlite数据库连接池初始化失败!") + init_pool(app.handle()).await.expect("sqlite数据库连接池初始化失败!") }); app.manage(pool); Ok(()) diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs index 92f9fa4..36307f5 100644 --- a/src-tauri/src/models.rs +++ b/src-tauri/src/models.rs @@ -45,3 +45,36 @@ impl ApiResponse { } } } + +#[derive(Debug, Serialize)] +pub struct BookInfo { + pub id: i64, + pub title: String, + pub author: String, + pub cover: String, + pub total_chapters: i32, + pub total_chars: i32, + pub introduction: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Chapter { + pub id: i64, + pub number: i32, + pub title: String, + pub content: String, + pub char_count: i32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Book { + pub id: Option, + pub title: String, + pub author: String, + pub cover: String, + pub introduction: String, + pub file_path: String, + pub total_chapters: i32, + pub total_chars: i32, + pub chapters: Vec, +} \ No newline at end of file diff --git a/src-tauri/src/services/db.rs b/src-tauri/src/services/db.rs index 0f201a9..08d1d1b 100644 --- a/src-tauri/src/services/db.rs +++ b/src-tauri/src/services/db.rs @@ -1,10 +1,50 @@ -use sqlx::pool::PoolOptions; -use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}; +use sqlx::sqlite::{SqlitePool, SqlitePoolOptions}; +use tauri::{AppHandle, Manager}; -pub async fn init_pool() -> Result { - SqlitePoolOptions::new() +pub async fn init_pool(app: &AppHandle) -> Result { + // 数据库文件路径 + let app_dir = app.path().app_data_dir().expect("无法获取APP数据目录"); + std::fs::create_dir_all(&app_dir).unwrap(); + let db_path = app_dir.join("reader.db"); + let db_url = format!("sqlite:{}", db_path.to_str().unwrap()); + // 初始化数据库连接池 + let pool = SqlitePoolOptions::new() .max_connections(5) .min_connections(1) - .connect("sqlite::memory:") - .await + .connect(&db_url) + .await?; + sqlx::query( + " + CREATE TABLE IF NOT EXISTS books ( + id INTEGER, + title TEXT DEFAULT '', + author TEXT DEFAULT '', + cover TEXT DEFAULT '', + file_path TEXT DEFAULT '', + total_chapters INTEGER DEFAULT 0, + total_chars INTEGER DEFAULT 0, + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + last_read_chapter_id INTEGER DEFAULT 0, + last_read_position INTEGER DEFAULT 0, + last_read_time TIMESTAMP, + PRIMARY KEY (id) + ); + + CREATE TABLE IF NOT EXISTS chapters ( + id INTEGER, + book_id INTEGER NOT NULL, + chapter_number INTEGER NOT NULL, + title TEXT NOT NULL DEFAULT '', + content TEXT NOT NULL, + total_chars INTEGER DEFAULT 0, + FOREIGN KEY (book_id) REFERENCES books ON DELETE CASCADE + ); + + CREATE INDEX IF NOT EXISTS idx_chapters_book + ON chapters(book_id, chapter_number); + ", + ) + .execute(&pool) + .await?; + Ok(pool) }