feat: 新增书籍导入和列表接口,完善数据库初始化

1.  新增encoding_rs、regex、chardetng依赖用于文本编码处理
2.  重构数据库初始化逻辑,适配tauri应用数据目录
3.  新增ApiResponse统一返回格式和书籍、章节数据模型
4.  重写import_book和book_list命令实现基础导入和列表功能
This commit is contained in:
Yuhang Wu 2026-08-20 11:19:28 +08:00
parent 5c1be1cdb0
commit efae900fcf
6 changed files with 127 additions and 10 deletions

23
src-tauri/Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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(())
}
pub async fn import_book(path: String) -> Result<ApiResponse<()>, 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<ApiResponse<Vec<()>>, String> {
Ok(ApiResponse::success(Vec::new()))
}

View File

@ -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(())

View File

@ -45,3 +45,36 @@ impl<T> ApiResponse<T> {
}
}
}
#[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<i64>,
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<Chapter>,
}

View File

@ -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<SqlitePool, sqlx::Error> {
SqlitePoolOptions::new()
pub async fn init_pool(app: &AppHandle) -> Result<SqlitePool, sqlx::Error> {
// 数据库文件路径
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)
}