tdm_server_rust/cache/
stationed_mangas_cache.rs1use crate::{
11 app::AppState,
12 common::result::ResultBody,
13 entity::member::StationedManga,
14 error::AppError,
15 repository::{manga_repo::MangaRepository, member_repo::MemberRepository},
16 utils::fast_json,
17};
18use std::sync::Arc;
19
20pub struct StationedMangasCache;
22
23const VALUE_NS: &str = "stationed_mangas";
25
26fn cache_ttl_secs() -> u64 {
28 std::env::var("STATIONED_MANGAS_CACHE_SECS")
29 .ok()
30 .and_then(|v| v.parse().ok())
31 .unwrap_or(60)
32}
33
34pub fn new_stationed_mangas_cache() -> StationedMangasCache {
36 StationedMangasCache
37}
38
39pub fn stationed_mangas_cache_key(member_id: i32) -> String {
41 format!("stationed_mangas:{member_id}")
42}
43
44pub async fn get_stationed_mangas_json_cached(state: &AppState, key: &str) -> Option<Arc<Vec<u8>>> {
46 state.redis.get_json_bytes(key).await
47}
48
49pub async fn get_stationed_mangas_cached(
51 state: &AppState,
52 member_id: i32,
53) -> Option<Vec<StationedManga>> {
54 state
55 .redis
56 .get_value(VALUE_NS, &member_id.to_string())
57 .await
58}
59
60pub async fn set_stationed_mangas_cached(
62 state: &AppState,
63 member_id: i32,
64 data: Vec<StationedManga>,
65) {
66 state
67 .redis
68 .set_value_ttl(VALUE_NS, &member_id.to_string(), &data, cache_ttl_secs())
69 .await;
70}
71
72pub async fn set_stationed_mangas_json_cached(
74 state: &AppState,
75 key: String,
76 data: Vec<StationedManga>,
77) {
78 let body = ResultBody::success_data(&data);
79 let json = match fast_json::to_vec(&body) {
80 Ok(bytes) => Arc::new(bytes),
81 Err(_) => return,
82 };
83 state
84 .redis
85 .set_json_bytes_ttl(&key, json.as_ref(), cache_ttl_secs())
86 .await;
87}
88
89pub async fn invalidate_stationed_mangas(state: &AppState, member_id: i32) {
91 state
92 .redis
93 .del_value(VALUE_NS, &member_id.to_string())
94 .await;
95 state
96 .redis
97 .del_json(&stationed_mangas_cache_key(member_id))
98 .await;
99}
100
101pub async fn load_stationed_mangas(
103 state: &AppState,
104 member_id: i32,
105) -> Result<Vec<StationedManga>, AppError> {
106 if let Some(cached) = get_stationed_mangas_cached(state, member_id).await {
107 return Ok(cached);
108 }
109 let member_repo = MemberRepository::new(state.db.clone());
110 let manga_repo = MangaRepository::new(state.db.clone());
111 let groups = member_repo.stationed_manga_post_groups(member_id).await?;
112 if groups.is_empty() {
113 set_stationed_mangas_cached(state, member_id, vec![]).await;
114 return Ok(vec![]);
115 }
116 let manga_ids: Vec<i32> = groups.keys().copied().collect();
117 let mangas = manga_repo.get_manga_responses_by_ids(&manga_ids).await?;
118 let mut list = Vec::with_capacity(groups.len());
119 for (manga_id, posts) in groups {
120 if let Some(manga) = mangas.get(&manga_id) {
121 list.push(StationedManga {
122 manga: manga.clone(),
123 posts,
124 });
125 }
126 }
127 list.sort_by(|a, b| b.manga.update_time.cmp(&a.manga.update_time));
128 set_stationed_mangas_cached(state, member_id, list.clone()).await;
129 Ok(list)
130}