secra_cache/
plugin.rs

1use async_trait::async_trait;
2use redis::AsyncCommands;
3use serde::Serialize;
4use serde::de::DeserializeOwned;
5/// PluginCache 实现
6use std::sync::Arc;
7use std::time::Duration;
8
9use super::error::CacheError;
10use super::manager::CacheManager;
11use super::traits::Cache;
12
13/// 插件缓存实例
14///
15/// 每个插件拥有一个独立的 PluginCache 实例,所有操作自动添加命名空间前缀
16pub struct PluginCache {
17    /// CacheManager 引用
18    manager: Arc<CacheManager>,
19
20    /// 插件 ID
21    plugin_id: String,
22
23    /// 命名空间前缀
24    namespace: String,
25}
26
27impl PluginCache {
28    /// 创建新的 PluginCache 实例
29    ///
30    /// 为指定的插件创建一个独立的缓存实例,所有操作会自动添加命名空间前缀。
31    /// 命名空间格式为:`{system_name}:plugin:{plugin_id}:`
32    ///
33    /// # Arguments
34    /// * `manager` - CacheManager 的引用,用于管理 Redis 连接和配置
35    /// * `plugin_id` - 插件的唯一标识符,用于构建命名空间前缀
36    ///
37    /// # Returns
38    /// * `Self` - 新创建的 PluginCache 实例
39    ///
40    /// # 命名空间隔离
41    /// 每个插件拥有独立的命名空间,确保不同插件的缓存数据完全隔离,
42    /// 防止 Key 冲突和跨插件访问。
43    ///
44    /// # Example
45    /// ```no_run
46    /// # use secra_cache::*;
47    /// # use std::sync::Arc;
48    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
49    /// # let manager = todo!();
50    /// let plugin_cache = PluginCache::new(manager, "my_plugin".to_string());
51    /// # Ok(())
52    /// # }
53    /// ```
54    pub(crate) fn new(manager: Arc<CacheManager>, plugin_id: String) -> Self {
55        let namespace = format!("{}:plugin:{}:", manager.system_name(), plugin_id);
56        Self {
57            manager,
58            plugin_id,
59            namespace,
60        }
61    }
62
63    /// 构建完整的 Key(添加命名空间前缀)
64    ///
65    /// 将业务 Key 与插件的命名空间前缀组合,生成完整的缓存 Key。
66    /// 在组合之前会先验证业务 Key 的格式。
67    ///
68    /// # Arguments
69    /// * `business_key` - 业务 Key(不包含命名空间前缀)
70    ///
71    /// # Returns
72    /// * `Ok(String)` - 完整的缓存 Key(包含命名空间前缀)
73    /// * `Err(CacheError::InvalidKey)` - 业务 Key 格式验证失败
74    ///
75    /// # 性能优化
76    /// * 使用 `String::with_capacity` 预分配容量,减少内存重新分配
77    /// * 容量计算为命名空间长度 + 业务 Key 长度,避免扩容
78    ///
79    /// # 安全性
80    /// * 在构建 Key 之前会调用 `validate_business_key` 进行格式验证
81    /// * 确保业务 Key 符合安全规范,防止注入攻击
82    ///
83    /// # Example
84    /// 如果命名空间为 `system:plugin:my_plugin:`,业务 Key 为 `user:123`,
85    /// 则返回的完整 Key 为 `system:plugin:my_plugin:user:123`
86    fn build_key(&self, business_key: &str) -> Result<String, CacheError> {
87        // 验证业务 Key 格式
88        self.validate_business_key(business_key)?;
89
90        // 性能优化:预分配容量,减少内存重新分配
91        let mut full_key = String::with_capacity(self.namespace.len() + business_key.len());
92        full_key.push_str(&self.namespace);
93        full_key.push_str(business_key);
94        Ok(full_key)
95    }
96
97    /// 验证业务 Key 格式
98    ///
99    /// 对业务 Key 进行格式验证,确保符合安全规范和性能要求。
100    /// 验证规则包括:非空、长度限制、字符限制、命名空间前缀检查。
101    ///
102    /// # Arguments
103    /// * `key` - 待验证的业务 Key
104    ///
105    /// # Returns
106    /// * `Ok(())` - Key 格式验证通过
107    /// * `Err(CacheError::InvalidKey)` - Key 格式验证失败,包含具体错误信息
108    ///
109    /// # 验证规则
110    /// 1. **非空检查**:Key 不能为空字符串
111    /// 2. **长度限制**:Key 长度不能超过 200 字符(防止过长的 Key 影响性能)
112    /// 3. **字符限制**:只允许 ASCII 字母、数字、下划线(`_`)、连字符(`-`)、冒号(`:`)
113    /// 4. **命名空间前缀检查**:Key 不能包含系统命名空间前缀,防止绕过隔离机制
114    ///
115    /// # 性能优化
116    /// * 使用 `as_bytes().iter()` 进行字节级检查,比 `chars()` 迭代更快
117    /// * 使用 `is_ascii_alphanumeric()` 进行字符类型判断,避免 Unicode 处理开销
118    /// * 提前返回机制,一旦发现不符合规则立即返回错误
119    ///
120    /// # 安全性
121    /// * 防止通过构造特殊 Key 访问其他插件的缓存数据
122    /// * 限制字符集,防止注入攻击和特殊字符导致的 Redis 命令注入
123    ///
124    /// # Errors
125    /// * `CacheError::InvalidKey("Key 不能为空")` - Key 为空字符串
126    /// * `CacheError::InvalidKey("Key 长度不能超过 200 字符")` - Key 长度超限
127    /// * `CacheError::InvalidKey("Key 包含非法字符...")` - Key 包含不允许的字符
128    /// * `CacheError::InvalidKey("Key 不能包含命名空间前缀")` - Key 包含命名空间前缀
129    fn validate_business_key(&self, key: &str) -> Result<(), CacheError> {
130        // 1. 不能为空
131        if key.is_empty() {
132            return Err(CacheError::InvalidKey("Key 不能为空".to_string()));
133        }
134
135        // 2. 长度限制(提前检查,避免后续不必要的操作)
136        if key.len() > 200 {
137            return Err(CacheError::InvalidKey(
138                "Key 长度不能超过 200 字符".to_string(),
139            ));
140        }
141
142        // 3. 字符限制(只允许字母、数字、下划线、连字符、冒号)
143        // 性能优化:使用字节检查,比 chars() 更快
144        if !key.as_bytes().iter().all(|&b| {
145            b.is_ascii_alphanumeric() || b == b'_' || b == b'-' || b == b':'
146        }) {
147            return Err(CacheError::InvalidKey(
148                "Key 包含非法字符,只允许字母、数字、下划线、连字符、冒号".to_string(),
149            ));
150        }
151
152        // 4. 不能包含命名空间分隔符(防止绕过隔离)
153        // 性能优化:避免 format!,直接检查字符串片段
154        let forbidden_prefix = format!("{}:plugin:", self.manager.system_name());
155        if key.contains(&forbidden_prefix) {
156            return Err(CacheError::InvalidKey(
157                "Key 不能包含命名空间前缀".to_string(),
158            ));
159        }
160
161        Ok(())
162    }
163
164    /// 验证权限(确保只能操作自己的 Key)
165    ///
166    /// 检查给定的完整 Key 是否属于当前插件的命名空间,防止跨插件访问。
167    ///
168    /// # Arguments
169    /// * `full_key` - 完整的缓存 Key(包含命名空间前缀)
170    ///
171    /// # Returns
172    /// * `Ok(())` - Key 属于当前插件,权限验证通过
173    /// * `Err(CacheError::PermissionDenied)` - Key 不属于当前插件,权限验证失败
174    ///
175    /// # 安全性
176    /// 此方法用于防止插件通过构造 Key 访问或修改其他插件的缓存数据。
177    fn verify_permission(&self, full_key: &str) -> Result<(), CacheError> {
178        if !full_key.starts_with(&self.namespace) {
179            return Err(CacheError::PermissionDenied(
180                "Key 不属于当前插件".to_string(),
181            ));
182        }
183        Ok(())
184    }
185}
186
187#[async_trait]
188impl Cache for PluginCache {
189    /// 获取缓存值
190    ///
191    /// 根据业务 Key 获取缓存中的值,自动添加命名空间前缀。
192    /// 如果 Key 不存在或已过期,返回 `None`。
193    ///
194    /// # Arguments
195    /// * `key` - 业务 Key(不包含命名空间前缀)
196    ///
197    /// # Type Parameters
198    /// * `T` - 返回值的类型,必须实现 `DeserializeOwned` trait
199    ///
200    /// # Returns
201    /// * `Ok(Some(T))` - 成功获取到缓存值
202    /// * `Ok(None)` - Key 不存在或已过期
203    /// * `Err(CacheError)` - 操作失败(如 Key 格式错误、权限不足、反序列化失败等)
204    ///
205    /// # Errors
206    /// * `CacheError::InvalidKey` - Key 格式验证失败
207    /// * `CacheError::PermissionDenied` - Key 不属于当前插件
208    /// * `CacheError::OperationFailed` - Redis 操作失败
209    /// * `CacheError::DeserializationFailed` - JSON 反序列化失败
210    ///
211    /// # Example
212    /// ```no_run
213    /// # use secra_cache::*;
214    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
215    /// # let cache = todo!();
216    /// let value: Option<String> = cache.get("user:123").await?;
217    /// if let Some(v) = value {
218    ///     println!("缓存值: {}", v);
219    /// }
220    /// # Ok(())
221    /// # }
222    /// ```
223    async fn get<T>(&self, key: &str) -> Result<Option<T>, CacheError>
224    where
225        T: DeserializeOwned,
226    {
227        let full_key = self.build_key(key)?;
228        
229        // 安全优化:验证权限(防止通过构造的 key 访问其他插件的缓存)
230        self.verify_permission(&full_key)?;
231        
232        let mut conn = self.manager.get_connection().await?;
233
234        // 从 Redis 获取值
235        let json_value: Option<String> = conn
236            .get(&full_key)
237            .await
238            .map_err(|e| CacheError::OperationFailed(e.to_string()))?;
239
240        // 反序列化
241        match json_value {
242            Some(json) => {
243                // 性能优化:使用 from_slice 替代 from_str(如果可能)
244                let value: T = serde_json::from_str(&json)
245                    .map_err(|e| CacheError::DeserializationFailed(e.to_string()))?;
246                Ok(Some(value))
247            }
248            None => Ok(None),
249        }
250    }
251
252    /// 设置缓存值
253    ///
254    /// 将值存储到缓存中,自动添加命名空间前缀。
255    /// 如果提供了 TTL,则设置过期时间;否则使用默认 TTL。
256    /// 操作完成后会自动将 Key 添加到插件的索引中。
257    ///
258    /// # Arguments
259    /// * `key` - 业务 Key(不包含命名空间前缀)
260    /// * `value` - 要缓存的值,必须实现 `Serialize` 和 `Sync` trait
261    /// * `ttl` - 可选的过期时间,如果为 `None` 则使用默认 TTL
262    ///
263    /// # Type Parameters
264    /// * `T` - 值的类型,必须实现 `Serialize` 和 `Sync` trait
265    ///
266    /// # Returns
267    /// * `Ok(())` - 成功设置缓存
268    /// * `Err(CacheError)` - 操作失败(如 Key 格式错误、权限不足、序列化失败等)
269    ///
270    /// # Errors
271    /// * `CacheError::InvalidKey` - Key 格式验证失败
272    /// * `CacheError::PermissionDenied` - Key 不属于当前插件
273    /// * `CacheError::OperationFailed` - Redis 操作失败
274    /// * `CacheError::SerializationFailed` - JSON 序列化失败
275    ///
276    /// # 性能说明
277    /// * 索引更新是异步非阻塞的,不会影响主操作的性能
278    /// * 序列化使用 `serde_json::to_string`,会自动优化内存分配
279    ///
280    /// # Example
281    /// ```no_run
282    /// # use secra_cache::*;
283    /// # use std::time::Duration;
284    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
285    /// # let cache = todo!();
286    /// cache.set("user:123", &"John Doe", Some(Duration::from_secs(3600))).await?;
287    /// # Ok(())
288    /// # }
289    /// ```
290    async fn set<T>(&self, key: &str, value: &T, ttl: Option<Duration>) -> Result<(), CacheError>
291    where
292        T: Serialize + Sync,
293    {
294        let full_key = self.build_key(key)?;
295        
296        // 安全优化:验证权限(防止通过构造的 key 设置其他插件的缓存)
297        self.verify_permission(&full_key)?;
298        
299        // 序列化为 JSON 字符串
300        // 性能优化:预分配容量(可选,serde_json 会自动优化)
301        let json_value = serde_json::to_string(value)
302            .map_err(|e| CacheError::SerializationFailed(e.to_string()))?;
303
304        let mut conn = self.manager.get_connection().await?;
305        let ttl_secs = self.manager.resolve_ttl(ttl);
306
307        // 设置到 Redis(使用 SET key value EX ttl)
308        let _: String = redis::cmd("SET")
309            .arg(&full_key)
310            .arg(&json_value)
311            .arg("EX")
312            .arg(ttl_secs)
313            .query_async(&mut conn)
314            .await
315            .map_err(|e| CacheError::OperationFailed(e.to_string()))?;
316
317        // 更新索引(异步非阻塞,不等待完成)
318        self.manager
319            .add_key_to_index(&self.plugin_id, &full_key)
320            .await;
321
322        Ok(())
323    }
324
325    /// 删除缓存值
326    ///
327    /// 从缓存中删除指定的 Key,如果 Key 存在则删除并返回 `true`,
328    /// 如果 Key 不存在则返回 `false`。
329    /// 删除成功后会自动从插件的索引中移除该 Key。
330    ///
331    /// # Arguments
332    /// * `key` - 业务 Key(不包含命名空间前缀)
333    ///
334    /// # Returns
335    /// * `Ok(true)` - Key 存在且已成功删除
336    /// * `Ok(false)` - Key 不存在
337    /// * `Err(CacheError)` - 操作失败(如 Key 格式错误、权限不足等)
338    ///
339    /// # Errors
340    /// * `CacheError::InvalidKey` - Key 格式验证失败
341    /// * `CacheError::PermissionDenied` - Key 不属于当前插件
342    /// * `CacheError::OperationFailed` - Redis 操作失败
343    ///
344    /// # Example
345    /// ```no_run
346    /// # use secra_cache::*;
347    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
348    /// # let cache = todo!();
349    /// let deleted = cache.delete("user:123").await?;
350    /// if deleted {
351    ///     println!("缓存已删除");
352    /// }
353    /// # Ok(())
354    /// # }
355    /// ```
356    async fn delete(&self, key: &str) -> Result<bool, CacheError> {
357        let full_key = self.build_key(key)?;
358
359        // 验证权限
360        self.verify_permission(&full_key)?;
361
362        let mut conn = self.manager.get_connection().await?;
363
364        // 删除 Key
365        let deleted: u64 = conn
366            .del(&full_key)
367            .await
368            .map_err(|e| CacheError::OperationFailed(e.to_string()))?;
369
370        // 从索引中移除
371        if deleted > 0 {
372            self.manager
373                .remove_key_from_index(&self.plugin_id, &full_key)
374                .await;
375        }
376
377        Ok(deleted > 0)
378    }
379
380    /// 检查 Key 是否存在
381    ///
382    /// 检查指定的 Key 是否存在于缓存中,无论是否已过期。
383    ///
384    /// # Arguments
385    /// * `key` - 业务 Key(不包含命名空间前缀)
386    ///
387    /// # Returns
388    /// * `Ok(true)` - Key 存在
389    /// * `Ok(false)` - Key 不存在
390    /// * `Err(CacheError)` - 操作失败(如 Key 格式错误、权限不足等)
391    ///
392    /// # Errors
393    /// * `CacheError::InvalidKey` - Key 格式验证失败
394    /// * `CacheError::PermissionDenied` - Key 不属于当前插件
395    /// * `CacheError::OperationFailed` - Redis 操作失败
396    ///
397    /// # Note
398    /// 此方法只检查 Key 是否存在,不检查是否已过期。
399    /// 如果需要检查 Key 是否存在且未过期,建议使用 `get` 方法。
400    ///
401    /// # Example
402    /// ```no_run
403    /// # use secra_cache::*;
404    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
405    /// # let cache = todo!();
406    /// if cache.exists("user:123").await? {
407    ///     println!("Key 存在");
408    /// }
409    /// # Ok(())
410    /// # }
411    /// ```
412    async fn exists(&self, key: &str) -> Result<bool, CacheError> {
413        let full_key = self.build_key(key)?;
414        
415        // 安全优化:验证权限
416        self.verify_permission(&full_key)?;
417        
418        let mut conn = self.manager.get_connection().await?;
419
420        // 检查 Key 是否存在
421        let exists: bool = conn
422            .exists(&full_key)
423            .await
424            .map_err(|e| CacheError::OperationFailed(e.to_string()))?;
425
426        Ok(exists)
427    }
428
429    /// 设置 Key 的过期时间
430    ///
431    /// 为已存在的 Key 设置新的过期时间。如果 Key 不存在,返回 `false`。
432    ///
433    /// # Arguments
434    /// * `key` - 业务 Key(不包含命名空间前缀)
435    /// * `ttl` - 新的过期时间
436    ///
437    /// # Returns
438    /// * `Ok(true)` - Key 存在且已成功设置过期时间
439    /// * `Ok(false)` - Key 不存在
440    /// * `Err(CacheError)` - 操作失败(如 Key 格式错误、权限不足等)
441    ///
442    /// # Errors
443    /// * `CacheError::InvalidKey` - Key 格式验证失败
444    /// * `CacheError::PermissionDenied` - Key 不属于当前插件
445    /// * `CacheError::OperationFailed` - Redis 操作失败
446    ///
447    /// # Example
448    /// ```no_run
449    /// # use secra_cache::*;
450    /// # use std::time::Duration;
451    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
452    /// # let cache = todo!();
453    /// let success = cache.expire("user:123", Duration::from_secs(7200)).await?;
454    /// if success {
455    ///     println!("过期时间已更新");
456    /// }
457    /// # Ok(())
458    /// # }
459    /// ```
460    async fn expire(&self, key: &str, ttl: Duration) -> Result<bool, CacheError> {
461        let full_key = self.build_key(key)?;
462        
463        // 安全优化:验证权限
464        self.verify_permission(&full_key)?;
465        
466        let mut conn = self.manager.get_connection().await?;
467
468        // 设置过期时间
469        let result: i64 = redis::cmd("EXPIRE")
470            .arg(&full_key)
471            .arg(ttl.as_secs() as usize)
472            .query_async(&mut conn)
473            .await
474            .map_err(|e| CacheError::OperationFailed(e.to_string()))?;
475
476        Ok(result == 1)
477    }
478
479    /// 获取 Key 的剩余过期时间
480    ///
481    /// 返回 Key 的剩余过期时间。如果 Key 不存在、已过期或永不过期,返回 `None`。
482    ///
483    /// # Arguments
484    /// * `key` - 业务 Key(不包含命名空间前缀)
485    ///
486    /// # Returns
487    /// * `Ok(Some(Duration))` - Key 存在且有过期时间,返回剩余时间
488    /// * `Ok(None)` - Key 不存在、已过期或永不过期
489    /// * `Err(CacheError)` - 操作失败(如 Key 格式错误、权限不足等)
490    ///
491    /// # Errors
492    /// * `CacheError::InvalidKey` - Key 格式验证失败
493    /// * `CacheError::PermissionDenied` - Key 不属于当前插件
494    /// * `CacheError::OperationFailed` - Redis 操作失败
495    ///
496    /// # Redis TTL 返回值说明
497    /// * `-2` - Key 不存在,返回 `None`
498    /// * `-1` - Key 存在但永不过期,返回 `None`
499    /// * `> 0` - Key 存在且有过期时间,返回剩余秒数
500    ///
501    /// # Example
502    /// ```no_run
503    /// # use secra_cache::*;
504    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
505    /// # let cache = todo!();
506    /// if let Some(remaining) = cache.ttl("user:123").await? {
507    ///     println!("剩余时间: {:?}", remaining);
508    /// } else {
509    ///     println!("Key 不存在或永不过期");
510    /// }
511    /// # Ok(())
512    /// # }
513    /// ```
514    async fn ttl(&self, key: &str) -> Result<Option<Duration>, CacheError> {
515        let full_key = self.build_key(key)?;
516        
517        // 安全优化:验证权限
518        self.verify_permission(&full_key)?;
519        
520        let mut conn = self.manager.get_connection().await?;
521
522        // 获取剩余过期时间
523        let ttl_secs: isize = redis::cmd("TTL")
524            .arg(&full_key)
525            .query_async(&mut conn)
526            .await
527            .map_err(|e| CacheError::OperationFailed(e.to_string()))?;
528
529        match ttl_secs {
530            -2 => Ok(None), // Key 不存在
531            -1 => Ok(None), // Key 存在但永不过期
532            secs if secs > 0 => Ok(Some(Duration::from_secs(secs as u64))),
533            _ => Ok(None),
534        }
535    }
536
537    /// 清空当前插件的所有缓存
538    ///
539    /// 删除当前插件的所有缓存 Key,包括所有模块的数据。
540    /// 此操作会清空插件的索引并删除所有相关的缓存数据。
541    ///
542    /// # Returns
543    /// * `Ok(u64)` - 成功删除的 Key 数量
544    /// * `Err(CacheError)` - 操作失败
545    ///
546    /// # Errors
547    /// * `CacheError::OperationFailed` - Redis 操作失败
548    ///
549    /// # Warning
550    /// 此操作不可逆,会删除当前插件的所有缓存数据,请谨慎使用。
551    ///
552    /// # Example
553    /// ```no_run
554    /// # use secra_cache::*;
555    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
556    /// # let cache = todo!();
557    /// let deleted_count = cache.clear().await?;
558    /// println!("已删除 {} 个缓存项", deleted_count);
559    /// # Ok(())
560    /// # }
561    /// ```
562    async fn clear(&self) -> Result<u64, CacheError> {
563        // 清理当前插件的所有缓存
564        self.manager.clear_plugin(&self.plugin_id).await
565    }
566
567    /// 清空当前插件的指定模块缓存
568    ///
569    /// 删除当前插件中指定模块的所有缓存 Key。
570    /// 模块名称通常作为 Key 的前缀部分,用于逻辑分组。
571    ///
572    /// # Arguments
573    /// * `module` - 模块名称,用于标识要清理的模块
574    ///
575    /// # Returns
576    /// * `Ok(u64)` - 成功删除的 Key 数量
577    /// * `Err(CacheError)` - 操作失败
578    ///
579    /// # Errors
580    /// * `CacheError::OperationFailed` - Redis 操作失败
581    ///
582    /// # Note
583    /// 模块名称应该与 Key 的命名规范一致,通常作为 Key 的前缀使用。
584    /// 例如:如果 Key 格式为 `module:user:123`,则模块名称为 `module`。
585    ///
586    /// # Example
587    /// ```no_run
588    /// # use secra_cache::*;
589    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
590    /// # let cache = todo!();
591    /// let deleted_count = cache.clear_module("user").await?;
592    /// println!("已删除 {} 个用户模块缓存项", deleted_count);
593    /// # Ok(())
594    /// # }
595    /// ```
596    async fn clear_module(&self, module: &str) -> Result<u64, CacheError> {
597        // 清理当前插件的指定模块缓存
598        self.manager.clear_module(&self.plugin_id, module).await
599    }
600}