Skip to main content

tdm_server_rust/cache/
task_tracking_cache.rs

1//! 稿件监控列表短 TTL 缓存
2//!
3//! 缓存任务看板的待做稿件列表和组员任务计数。
4//! 数据变化频繁(每次接稿/交稿都触发刷新),TTL 默认 20 秒。
5//!
6//! ## 两个缓存
7//!
8//! | 缓存 | Key | 内容 |
9//! |------|-----|------|
10//! | [`EpisodeTasksCache`] | `"episode_tasks"` | 各岗位待做稿件分桶列表 |
11//! | [`MemberTaskCountsCache`] | `"member_task_counts"` | 每人各岗位任务计数 |
12//!
13//! ## 失效策略
14//!
15//! 接稿/交稿后调用 [`invalidate_task_tracking`] 同时失效两个缓存。
16
17use crate::{
18    app::AppState,
19    entity::episode::{MemberTaskCount, TaskTrackingResponse},
20};
21use std::sync::Arc;
22use std::time::Duration;
23
24/// 待做稿件缓存键
25const EPISODE_TASKS_KEY: &str = "episode_tasks";
26/// 组员任务计数缓存键
27const MEMBER_TASK_COUNTS_KEY: &str = "member_task_counts";
28
29/// 待做稿件响应缓存类型
30pub type EpisodeTasksCache = moka::future::Cache<String, Arc<TaskTrackingResponse>>;
31/// 组员任务计数缓存类型
32pub type MemberTaskCountsCache = moka::future::Cache<String, Arc<Vec<MemberTaskCount>>>;
33
34/// 创建待做稿件缓存实例
35///
36/// TTL 由环境变量 `TASK_TRACKING_CACHE_SECS` 控制,默认 20 秒。
37pub fn new_episode_tasks_cache() -> EpisodeTasksCache {
38    let ttl_secs = std::env::var("TASK_TRACKING_CACHE_SECS")
39        .ok()
40        .and_then(|v| v.parse().ok())
41        .unwrap_or(20);
42    moka::future::Cache::builder()
43        .time_to_live(Duration::from_secs(ttl_secs))
44        .build()
45}
46
47/// 创建组员任务计数缓存实例
48pub fn new_member_task_counts_cache() -> MemberTaskCountsCache {
49    let ttl_secs = std::env::var("TASK_TRACKING_CACHE_SECS")
50        .ok()
51        .and_then(|v| v.parse().ok())
52        .unwrap_or(20);
53    moka::future::Cache::builder()
54        .time_to_live(Duration::from_secs(ttl_secs))
55        .build()
56}
57
58/// 读取缓存的待做稿件,未命中返回 `None`
59///
60/// # 返回值
61///
62/// - `Some(TaskTrackingResponse)` — 缓存命中
63/// - `None` — 缓存未命中或已过期,需重新查询
64pub async fn get_episode_tasks_cached(state: &AppState) -> Option<TaskTrackingResponse> {
65    state
66        .episode_tasks_cache
67        .get(EPISODE_TASKS_KEY)
68        .await
69        .map(|v| (*v).clone())
70}
71
72/// 写入待做稿件缓存
73pub async fn set_episode_tasks_cached(state: &AppState, data: TaskTrackingResponse) {
74    state
75        .episode_tasks_cache
76        .insert(EPISODE_TASKS_KEY.to_string(), Arc::new(data))
77        .await;
78}
79
80/// 读取缓存的组员任务计数,未命中返回 `None`
81pub async fn get_member_task_counts_cached(state: &AppState) -> Option<Vec<MemberTaskCount>> {
82    state
83        .member_task_counts_cache
84        .get(MEMBER_TASK_COUNTS_KEY)
85        .await
86        .map(|v| (*v).clone())
87}
88
89/// 写入组员任务计数缓存
90pub async fn set_member_task_counts_cached(state: &AppState, data: Vec<MemberTaskCount>) {
91    state
92        .member_task_counts_cache
93        .insert(MEMBER_TASK_COUNTS_KEY.to_string(), Arc::new(data))
94        .await;
95}
96
97/// 使稿件监控两个缓存同时失效
98///
99/// 接稿/交稿后调用,确保下次查询获取最新数据。
100pub async fn invalidate_task_tracking(state: &AppState) {
101    state.episode_tasks_cache.invalidate(EPISODE_TASKS_KEY).await;
102    state
103        .member_task_counts_cache
104        .invalidate(MEMBER_TASK_COUNTS_KEY)
105        .await;
106}