Skip to main content

tdm_server_rust/
app.rs

1//! 应用全局状态 (Application State)
2
3use crate::{
4    cache::{
5        AuthSnapshotCache, EpisodeTasksCache, MemberAllCache, MemberTaskCountsCache,
6        MangaTranNameCache, RssFileLock,
7    },
8    config::AppConfig,
9    dev::error_log,
10    dev::paths::resolve_error_log_path,
11    runner,
12    service::rss_service,
13    utils::error_log as file_error_log,
14};
15use sqlx::MySqlPool;
16use std::sync::Arc;
17use std::time::Instant;
18
19/// 共享应用状态,注入到各 handler
20#[derive(Clone)]
21pub struct AppState {
22    /// 数据库连接池
23    pub db: MySqlPool,
24    /// 运行时配置
25    pub config: Arc<AppConfig>,
26    /// 出站 HTTP 客户端
27    pub http_client: reqwest::Client,
28    /// 漫画译名列表缓存
29    pub manga_tran_name_cache: MangaTranNameCache,
30    /// 漫画原名列表缓存
31    pub manga_ori_name_cache: MangaTranNameCache,
32    /// 全量组员列表缓存
33    pub member_all_cache: MemberAllCache,
34    /// 鉴权用组员快照缓存
35    pub auth_snapshot_cache: AuthSnapshotCache,
36    /// 待做稿件列表缓存
37    pub episode_tasks_cache: EpisodeTasksCache,
38    /// 组员任务计数缓存
39    pub member_task_counts_cache: MemberTaskCountsCache,
40    /// RSS 文件 per-file 互斥锁
41    pub rss_file_lock: Arc<RssFileLock>,
42    /// 进程启动时刻
43    pub started_at: Instant,
44}
45
46impl AppState {
47    /// 根据配置初始化数据库连接池
48    pub async fn new(config: AppConfig) -> anyhow::Result<Self> {
49        let db = sqlx::mysql::MySqlPoolOptions::new()
50            .max_connections(config.database.max_connections)
51            .connect(&config.database.url)
52            .await?;
53        runner::run_all(&db).await?;
54        rss_service::ensure_rss_dir(&config).await?;
55        let http_client = reqwest::Client::builder()
56            .pool_max_idle_per_host(8)
57            .build()
58            .map_err(|e| anyhow::anyhow!("构建 HTTP 客户端失败: {e}"))?;
59
60        if matches!(config.profile.as_str(), "dev" | "dev-h2") {
61            error_log::init_pool(db.clone());
62        }
63
64        file_error_log::init(resolve_error_log_path(&config.dev_console));
65
66        Ok(Self {
67            db,
68            config: Arc::new(config),
69            http_client,
70            manga_tran_name_cache: crate::cache::new_manga_tran_name_cache(),
71            manga_ori_name_cache: crate::cache::new_manga_tran_name_cache(),
72            member_all_cache: crate::cache::new_member_all_cache(),
73            auth_snapshot_cache: crate::cache::new_auth_snapshot_cache(),
74            episode_tasks_cache: crate::cache::new_episode_tasks_cache(),
75            member_task_counts_cache: crate::cache::new_member_task_counts_cache(),
76            rss_file_lock: Arc::new(RssFileLock::new()),
77            started_at: Instant::now(),
78        })
79    }
80}