tdm_server_rust/cache/
list_page_json_cache.rs1use crate::{
10 app::AppState,
11 cache::two_tier,
12 common::{page_bean::PageBean, result::ResultBody},
13 utils::fast_json,
14};
15use std::sync::Arc;
16
17pub struct ListPageJsonCache;
19
20fn cache_ttl_secs() -> u64 {
22 std::env::var("LIST_PAGE_CACHE_SECS")
23 .ok()
24 .and_then(|v| v.parse().ok())
25 .unwrap_or(60)
26}
27
28pub fn new_list_page_json_cache() -> ListPageJsonCache {
30 ListPageJsonCache
31}
32
33pub fn manga_page_cache_key(
35 _state: &AppState,
36 page: i32,
37 page_size: i32,
38 manga_tran_name: Option<&str>,
39 manga_ori_name: Option<&str>,
40 category: Option<i16>,
41 manga_status: Option<i16>,
42 author_name: Option<&str>,
43 magazine_name: Option<&str>,
44 sort_prop: &str,
45 sort_order: &str,
46) -> String {
47 format!(
48 "manga_page:{page}:{page_size}:{}:{}:{}:{}:{}:{}:{sort_prop}:{sort_order}",
49 manga_tran_name.unwrap_or(""),
50 manga_ori_name.unwrap_or(""),
51 category.map(|v| v.to_string()).unwrap_or_default(),
52 manga_status.map(|v| v.to_string()).unwrap_or_default(),
53 author_name.unwrap_or(""),
54 magazine_name.unwrap_or(""),
55 )
56}
57
58pub fn magazine_manga_cache_key(
60 _state: &AppState,
61 magazine_id: i32,
62 page: i32,
63 page_size: i32,
64) -> String {
65 format!("magazine_manga:{magazine_id}:{page}:{page_size}")
66}
67
68pub fn manga_episode_page_cache_key(
70 _state: &AppState,
71 manga_id: i32,
72 page: i32,
73 page_size: i32,
74 episode_type: &str,
75) -> String {
76 manga_episode_page_cache_key_value(manga_id, page, page_size, episode_type)
77}
78
79fn manga_episode_page_cache_key_value(
80 manga_id: i32,
81 page: i32,
82 page_size: i32,
83 episode_type: &str,
84) -> String {
85 format!("manga_episodes:{manga_id}:{episode_type}:{page}:{page_size}")
86}
87
88pub fn uploaded_submit_cache_key(
90 _state: &AppState,
91 page: i32,
92 page_size: i32,
93 manga_tran_name: Option<&str>,
94 username: Option<&str>,
95) -> String {
96 format!(
97 "uploaded_submit:{page}:{page_size}:{}:{}",
98 manga_tran_name.unwrap_or(""),
99 username.unwrap_or(""),
100 )
101}
102
103pub async fn get_list_page_json_cached(state: &AppState, key: &str) -> Option<Arc<Vec<u8>>> {
105 two_tier::get_json(&state.redis, key).await
106}
107
108pub async fn set_list_page_json_cached<T: serde::Serialize>(
110 state: &AppState,
111 key: String,
112 page: PageBean<T>,
113) {
114 let body = ResultBody::success_data(&page);
115 let json = match fast_json::to_vec(&body) {
116 Ok(bytes) => Arc::new(bytes),
117 Err(_) => return,
118 };
119 two_tier::set_json(&state.redis, &key, json.as_ref(), cache_ttl_secs()).await;
120}
121
122pub fn manga_episode_simple_cache_key(_state: &AppState, manga_id: i32) -> String {
124 format!("manga_episodes_simple:{manga_id}")
125}
126
127pub fn member_page_cache_key(
129 _state: &AppState,
130 page: i32,
131 page_size: i32,
132 username: Option<&str>,
133 post: Option<i16>,
134 intern: Option<i16>,
135 email: Option<&str>,
136) -> String {
137 format!(
138 "member_page:{page}:{page_size}:{}:{}:{}:{}",
139 username.unwrap_or(""),
140 post.map(|v| v.to_string()).unwrap_or_default(),
141 intern.map(|v| v.to_string()).unwrap_or_default(),
142 email.unwrap_or(""),
143 )
144}
145
146pub async fn set_result_json_cached<T: serde::Serialize>(state: &AppState, key: String, data: T) {
148 let body = ResultBody::success_data(&data);
149 let json = match fast_json::to_vec(&body) {
150 Ok(bytes) => Arc::new(bytes),
151 Err(_) => return,
152 };
153 two_tier::set_json(&state.redis, &key, json.as_ref(), cache_ttl_secs()).await;
154}
155
156pub async fn invalidate_manga_episode_caches(state: &AppState, manga_id: i32) {
158 state
159 .redis
160 .del_json_pattern(&format!("manga_episodes:{manga_id}:*"))
161 .await;
162 state
163 .redis
164 .del_json(&format!("manga_episodes_simple:{manga_id}"))
165 .await;
166}
167
168pub async fn invalidate_uploaded_submit_caches(state: &AppState) {
170 state.redis.del_json_pattern("uploaded_submit:*").await;
171}
172
173pub async fn invalidate_manga_page_caches(state: &AppState) {
175 state.redis.del_json_pattern("manga_page:*").await;
176}
177
178pub async fn invalidate_magazine_manga_caches(state: &AppState) {
180 state.redis.del_json_pattern("magazine_manga:*").await;
181}
182
183pub async fn invalidate_member_page_caches(state: &AppState) {
185 state.redis.del_json_pattern("member_page:*").await;
186}
187
188#[cfg(test)]
189mod episode_cache_key_tests {
190 use super::manga_episode_page_cache_key_value;
191
192 #[test]
193 fn episode_type_separates_page_cache_keys() {
194 let main = manga_episode_page_cache_key_value(7, 1, 10, "MAIN");
195 let extra = manga_episode_page_cache_key_value(7, 1, 10, "EXTRA");
196 assert_ne!(main, extra);
197 }
198}