Skip to main content

dingtalk_stream/transport/
token.rs

1//! OAuth2 Access Token 管理(带缓存)
2
3use crate::credential::Credential;
4use crate::error::{Error, Result};
5use crate::transport::http::HttpClient;
6use std::time::{SystemTime, UNIX_EPOCH};
7use tokio::sync::RwLock;
8
9/// Token 缓存
10struct TokenCache {
11    access_token: String,
12    expire_time: i64,
13}
14
15/// Token 管理器
16pub struct TokenManager {
17    credential: Credential,
18    http_client: HttpClient,
19    cache: RwLock<Option<TokenCache>>,
20}
21
22impl TokenManager {
23    /// 创建新的 Token 管理器
24    pub fn new(credential: Credential, http_client: HttpClient) -> Self {
25        Self {
26            credential,
27            http_client,
28            cache: RwLock::new(None),
29        }
30    }
31
32    /// 获取当前时间戳(秒)
33    fn now() -> i64 {
34        SystemTime::now()
35            .duration_since(UNIX_EPOCH)
36            .map(|d| d.as_secs() as i64)
37            .unwrap_or(0)
38    }
39
40    /// 获取 access_token(带缓存)
41    pub async fn get_access_token(&self) -> Result<String> {
42        // 先尝试读缓存
43        {
44            let cache = self.cache.read().await;
45            if let Some(ref c) = *cache {
46                if Self::now() < c.expire_time {
47                    return Ok(c.access_token.clone());
48                }
49            }
50        }
51
52        // 缓存过期或不存在,请求新 token
53        let url = format!(
54            "{}/v1.0/oauth2/accessToken",
55            self.http_client.openapi_endpoint()
56        );
57        let body = serde_json::json!({
58            "appKey": self.credential.client_id,
59            "appSecret": self.credential.client_secret,
60        });
61
62        let result: serde_json::Value = self.http_client.post_json(&url, &body, None).await?;
63
64        let access_token = result
65            .get("accessToken")
66            .and_then(|v| v.as_str())
67            .ok_or_else(|| Error::Auth("accessToken not found in response".to_owned()))?
68            .to_owned();
69
70        let expire_in = result
71            .get("expireIn")
72            .and_then(|v| v.as_i64())
73            .unwrap_or(7200);
74
75        // 提前 5 分钟过期
76        let expire_time = Self::now() + expire_in - 300;
77
78        let mut cache = self.cache.write().await;
79        *cache = Some(TokenCache {
80            access_token: access_token.clone(),
81            expire_time,
82        });
83
84        Ok(access_token)
85    }
86
87    /// 重置 token 缓存
88    pub async fn reset(&self) {
89        let mut cache = self.cache.write().await;
90        *cache = None;
91    }
92}