chimes_utils/wechat/
token.rs

1use crate::ChimesClient;
2use crate::{current_timestamp_secs, json_decode, WechatResult as Result, API_DOMAIN};
3use std::{collections::HashMap, sync::Mutex};
4
5use super::errors::WechatError;
6// use crate::{json_decode, WechatError};
7
8const WECHAT_CONFIG_KEY_TOKIO: &str = "wechat_config_cache_tokio";
9// 默认加载静态全局
10lazy_static! {
11    pub static ref WECHAT_CONFIG_CACHES_ATT: Mutex<HashMap<String, TokioToken>> =
12        Mutex::new(HashMap::new());
13}
14
15/// Access Token对象
16#[derive(Debug, Clone, Default)]
17pub struct TokioAccessToken {
18    // 应用类型
19    pub app_type: String,
20    // 应用id
21    pub app_id: String,
22    // 密钥
23    pub secret: String,
24}
25
26/// Token信息
27#[derive(Debug, Clone, Default)]
28pub struct TokioToken {
29    // 访问token
30    pub access_token: String,
31    // access_token获取时间
32    pub create_time: u64,
33    // 有效期
34    pub expires: i64,
35}
36
37impl TokioAccessToken {
38    /// 创建一个短信配置实例
39    pub fn new(app_type: &str, app_id: &str, secret: &str) -> TokioAccessToken {
40        TokioAccessToken {
41            app_type: app_type.to_owned(),
42            app_id: app_id.to_owned(),
43            secret: secret.to_owned(),
44        }
45    }
46
47    /// 获取微信授权的accessToken
48    pub async fn get_access_token(&self, grant_type: &str) -> Result<TokioToken> {
49        // 组装请求地址
50        let url = format!(
51            "{domain}/cgi-bin/token?grant_type={grant_type}&appid={app_id}&secret={secret}",
52            domain = API_DOMAIN,
53            grant_type = if grant_type.is_empty() {
54                "client_credential"
55            } else {
56                grant_type
57            },
58            app_id = self.app_id,
59            secret = self.secret
60        );
61
62        // 调用远程接口
63        match ChimesClient::new().get(&url).await {
64            Ok(res) => {
65                match json_decode(&res) {
66                    Ok(data) => {
67                        let token = match data["access_token"].as_str() {
68                            Some(s) => s.to_owned(),
69                            None => return Err(WechatError::msg("access token error")),
70                        };
71
72                        // 将Token返出去
73                        return Ok(TokioToken {
74                            access_token: token,
75                            create_time: current_timestamp_secs(),
76                            expires: 7200,
77                        });
78                    }
79                    Err(err) => {
80                        return Err(err);
81                    }
82                }
83            }
84            Err(err) => log::error!("error{:?}", err),
85        }
86
87        Err(WechatError::msg("access token is invalid"))
88    }
89
90    /// 把字符串对象写入缓存中,并指定有有效期单位秒
91    pub async fn set(&mut self, val: TokioToken) {
92        let key = WECHAT_CONFIG_KEY_TOKIO;
93
94        let rkey = format!("{}-{}", key, self.app_id.clone());
95
96        let mut cache = WECHAT_CONFIG_CACHES_ATT.lock().unwrap();
97
98        cache.insert(rkey.to_owned(), val);
99        // log!("setted config");
100    }
101
102    pub fn clear() {
103        let mut cache = WECHAT_CONFIG_CACHES_ATT.lock().unwrap();
104        cache.clear();
105    }
106
107    /// 获取cache中的缓存数据
108    pub async fn get(&mut self) -> Option<TokioToken> {
109        let key = WECHAT_CONFIG_KEY_TOKIO;
110        let appid = self.app_id.clone();
111        let rkey = format!("{}-{}", key, appid.clone());
112        let cache = WECHAT_CONFIG_CACHES_ATT.lock().unwrap().clone();
113
114        if let Some(mcx) = cache.get(rkey.as_str()) {
115            let now = current_timestamp_secs();
116            if now < mcx.create_time + mcx.expires as u64 - 1000u64 {
117                return Some(mcx.clone());
118            }
119        }
120
121        drop(cache);
122
123        match self.get_access_token(self.app_type.clone().as_str()).await {
124            Ok(access) => {
125                self.set(access.clone()).await;
126                return Some(access.clone());
127            }
128            Err(err) => {
129                log::info!("get access token error{:?}", err);
130            }
131        }
132        None
133    }
134}