Skip to main content

tdm_server_rust/cache/
auth_snapshot_cache.rs

1//! 鉴权用组员快照短 TTL 缓存
2//!
3//! 每次请求鉴权时查询组员信息的频率极高,
4//! 使用 Redis 缓存减少数据库查询。
5//! TTL 默认 30 秒,可通过 `AUTH_SNAPSHOT_CACHE_SECS` 环境变量调整。
6//!
7//! ## 缓存策略
8//!
9//! - **Key**: 组员 ID (`i32`)
10//! - **Value**: `Member`(组员快照 + 岗位 ID 列表)
11//! - **TTL**: 30 秒(默认),组员信息变更较少但需要及时反映权限变化
12//! - **失效**: 组员更新/删除时调用 [`invalidate_auth_snapshot`] 主动失效
13
14use crate::{
15    app::AppState, entity::member::Member, error::AppError,
16    repository::member_repo::MemberRepository,
17};
18
19/// 鉴权快照缓存标记类型
20pub struct AuthSnapshotCache;
21
22/// 结构化值缓存命名空间
23const VALUE_NS: &str = "auth_snapshot";
24
25/// 创建鉴权快照缓存实例
26///
27/// TTL 由环境变量 `AUTH_SNAPSHOT_CACHE_SECS` 控制,默认 30 秒。
28pub fn new_auth_snapshot_cache() -> AuthSnapshotCache {
29    AuthSnapshotCache
30}
31
32/// 获取鉴权快照
33///
34/// 先从缓存查询,未命中时查数据库并回填缓存。
35///
36/// # 参数
37///
38/// - `state`: 应用状态(持有缓存实例和数据库连接池)
39/// - `member_id`: 组员 ID
40///
41/// # 返回值
42///
43/// 返回组员快照(含岗位 ID 列表)。
44///
45/// # Errors
46///
47/// - `AppError::business("组员不存在喵")` — ID 不存在
48/// - `AppError::Database` — 查询失败
49pub async fn get_auth_snapshot_cached(
50    state: &AppState,
51    member_id: i32,
52) -> Result<Member, AppError> {
53    let key = member_id.to_string();
54    if let Some(cached) = state.redis.get_value(VALUE_NS, &key).await {
55        return Ok(cached);
56    }
57    let repo = MemberRepository::new(state.db.clone());
58    let member = repo.get_auth_snapshot(member_id).await?;
59    state
60        .redis
61        .set_value_ttl(VALUE_NS, &key, &member, cache_ttl_secs())
62        .await;
63    Ok(member)
64}
65
66/// 使指定组员的鉴权快照缓存失效
67///
68/// 组员信息(权限、岗位)变更后调用,确保下次请求获取最新数据。
69pub async fn invalidate_auth_snapshot(state: &AppState, member_id: i32) {
70    state
71        .redis
72        .del_value(VALUE_NS, &member_id.to_string())
73        .await;
74}
75
76/// 读取 TTL 秒数
77fn cache_ttl_secs() -> u64 {
78    std::env::var("AUTH_SNAPSHOT_CACHE_SECS")
79        .ok()
80        .and_then(|v| v.parse().ok())
81        .unwrap_or(30)
82}