Skip to main content

wx_rust_open/config/impl/
wx_open_default_config_impl.rs

1//! 开放平台(第三方平台)配置存储默认实现。
2//!
3//! 对应 Java `me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage`
4//! (任务命名 `WxOpenDefaultConfigImpl`,与 mp/ma 的 DefaultConfigImpl 对齐):
5//! 内存实现,component 三凭证 + verify_ticket + component_access_token 缓存
6//! (预留 200 秒提前过期,Java `(expiresInSeconds - 200) * 1000L`)、按
7//! appId 分桶的授权方 token/ticket 缓存、按 key 的锁表,线程安全。
8
9use std::collections::HashMap;
10use std::sync::{Arc, Mutex, RwLock};
11use tokio::sync::Mutex as AsyncMutex;
12
13use wx_rust_common::config::TokenEntry;
14
15use crate::config::{API_DEFAULT_HOST_URL, WxOpenConfigStorage, WxOpenHostConfig};
16
17/// 当前时间(UNIX 秒)。
18fn now() -> i64 {
19    std::time::SystemTime::now()
20        .duration_since(std::time::UNIX_EPOCH)
21        .map(|d| d.as_secs() as i64)
22        .unwrap_or(0)
23}
24
25/// 开放平台默认配置存储(内存实现)。
26#[derive(Debug)]
27pub struct WxOpenDefaultConfig {
28    component_app_id: RwLock<Option<String>>,
29    component_app_secret: RwLock<Option<String>>,
30    component_token: RwLock<Option<String>>,
31    component_aes_key: RwLock<Option<String>>,
32    component_verify_ticket: RwLock<Option<String>>,
33    component_access_token: Mutex<Option<TokenEntry>>,
34    component_access_token_lock: Arc<AsyncMutex<()>>,
35    /// 按 key 的锁表(对应 Java `Map<String, Lock> locks`)
36    locks: Mutex<HashMap<String, Arc<AsyncMutex<()>>>>,
37    /// 授权方 refresh_token 缓存(对应 Java `authorizerRefreshTokens`)
38    authorizer_refresh_tokens: Mutex<HashMap<String, TokenEntry>>,
39    /// 授权方 access_token 缓存(对应 Java `authorizerAccessTokens`)
40    authorizer_access_tokens: Mutex<HashMap<String, TokenEntry>>,
41    /// 授权方 jsapi ticket 缓存(对应 Java `jsapiTickets`)
42    jsapi_tickets: Mutex<HashMap<String, TokenEntry>>,
43    /// 授权方卡券 api ticket 缓存(对应 Java `cardApiTickets`)
44    card_api_tickets: Mutex<HashMap<String, TokenEntry>>,
45    http_proxy_host: RwLock<Option<String>>,
46    http_proxy_port: i32,
47    http_proxy_username: RwLock<Option<String>>,
48    http_proxy_password: RwLock<Option<String>>,
49    retry_sleep_millis: i32,
50    max_retry_times: i32,
51    /// 自定义 API 主机地址(对应 Java `apiHostUrl`,用于替换默认
52    /// `https://api.weixin.qq.com`)
53    api_host_url: RwLock<Option<String>>,
54    /// 自定义获取 accessToken 地址(对应 Java `accessTokenUrl`)
55    access_token_url: RwLock<Option<String>>,
56    host_config: RwLock<WxOpenHostConfig>,
57    component_api_signature_rsa_private_key: RwLock<Option<String>>,
58    component_api_signature_aes_key: RwLock<Option<String>>,
59    component_api_signature_rsa_private_key_sn: RwLock<Option<String>>,
60    component_api_signature_aes_key_sn: RwLock<Option<String>>,
61}
62
63impl WxOpenDefaultConfig {
64    /// 构建默认配置(Java 以无参构造 + setter 装配,Rust 提供同名语义的
65    /// 便捷构造;字段默认 None/0/1000/5,与 Java 字段默认值一致)。
66    pub fn new() -> Self {
67        Self {
68            component_app_id: RwLock::new(None),
69            component_app_secret: RwLock::new(None),
70            component_token: RwLock::new(None),
71            component_aes_key: RwLock::new(None),
72            component_verify_ticket: RwLock::new(None),
73            component_access_token: Mutex::new(None),
74            component_access_token_lock: Arc::new(AsyncMutex::new(())),
75            locks: Mutex::new(HashMap::new()),
76            authorizer_refresh_tokens: Mutex::new(HashMap::new()),
77            authorizer_access_tokens: Mutex::new(HashMap::new()),
78            jsapi_tickets: Mutex::new(HashMap::new()),
79            card_api_tickets: Mutex::new(HashMap::new()),
80            http_proxy_host: RwLock::new(None),
81            http_proxy_port: 0,
82            http_proxy_username: RwLock::new(None),
83            http_proxy_password: RwLock::new(None),
84            retry_sleep_millis: 1000,
85            max_retry_times: 5,
86            api_host_url: RwLock::new(None),
87            access_token_url: RwLock::new(None),
88            host_config: RwLock::new(WxOpenHostConfig::new()),
89            component_api_signature_rsa_private_key: RwLock::new(None),
90            component_api_signature_aes_key: RwLock::new(None),
91            component_api_signature_rsa_private_key_sn: RwLock::new(None),
92            component_api_signature_aes_key_sn: RwLock::new(None),
93        }
94    }
95
96    // ---- 便捷 setter(对应 Java Lombok setter;返回 &mut Self 链式调用) ----
97
98    /// 设置第三方平台 appid。
99    pub fn set_component_app_id(&mut self, v: impl Into<String>) -> &mut Self {
100        *self.component_app_id.write().unwrap() = Some(v.into());
101        self
102    }
103
104    /// 设置第三方平台 appsecret。
105    pub fn set_component_app_secret(&mut self, v: impl Into<String>) -> &mut Self {
106        *self.component_app_secret.write().unwrap() = Some(v.into());
107        self
108    }
109
110    /// 设置消息校验 Token。
111    pub fn set_component_token(&mut self, v: impl Into<String>) -> &mut Self {
112        *self.component_token.write().unwrap() = Some(v.into());
113        self
114    }
115
116    /// 设置消息加解密 Key。
117    pub fn set_component_aes_key(&mut self, v: impl Into<String>) -> &mut Self {
118        *self.component_aes_key.write().unwrap() = Some(v.into());
119        self
120    }
121
122    /// 设置推送的 verify ticket。
123    pub fn set_component_verify_ticket(&mut self, v: impl Into<String>) -> &mut Self {
124        *self.component_verify_ticket.write().unwrap() = Some(v.into());
125        self
126    }
127
128    /// 设置 HTTP 代理主机。
129    pub fn set_http_proxy_host(&mut self, v: impl Into<String>) -> &mut Self {
130        *self.http_proxy_host.write().unwrap() = Some(v.into());
131        self
132    }
133
134    /// 设置 HTTP 代理端口。
135    pub fn set_http_proxy_port(&mut self, v: i32) -> &mut Self {
136        self.http_proxy_port = v;
137        self
138    }
139
140    /// 设置 HTTP 代理用户名。
141    pub fn set_http_proxy_username(&mut self, v: impl Into<String>) -> &mut Self {
142        *self.http_proxy_username.write().unwrap() = Some(v.into());
143        self
144    }
145
146    /// 设置 HTTP 代理密码。
147    pub fn set_http_proxy_password(&mut self, v: impl Into<String>) -> &mut Self {
148        *self.http_proxy_password.write().unwrap() = Some(v.into());
149        self
150    }
151
152    /// 设置 HTTP 请求重试间隔(毫秒)。
153    pub fn set_retry_sleep_millis(&mut self, v: i32) -> &mut Self {
154        self.retry_sleep_millis = v;
155        self
156    }
157
158    /// 设置 HTTP 请求最大重试次数。
159    pub fn set_max_retry_times(&mut self, v: i32) -> &mut Self {
160        self.max_retry_times = v;
161        self
162    }
163
164    /// 设置自定义 API 主机地址(对应 Java `setApiHostUrl(String)`)。
165    pub fn set_api_host_url(&mut self, v: impl Into<String>) -> &mut Self {
166        *self.api_host_url.write().unwrap() = Some(v.into());
167        self
168    }
169
170    /// 设置自定义获取 accessToken 地址(对应 Java `setAccessTokenUrl(String)`)。
171    pub fn set_access_token_url(&mut self, v: impl Into<String>) -> &mut Self {
172        *self.access_token_url.write().unwrap() = Some(v.into());
173        self
174    }
175
176    /// 自定义 API 主机地址(对应 Java `getApiHostUrl()`)。
177    pub fn api_host_url(&self) -> Option<String> {
178        self.api_host_url.read().unwrap().clone()
179    }
180
181    /// 自定义获取 accessToken 地址(对应 Java `getAccessTokenUrl()`)。
182    pub fn access_token_url(&self) -> Option<String> {
183        self.access_token_url.read().unwrap().clone()
184    }
185
186    /// 根据配置获取实际应使用的 API 主机地址(对应 Java
187    /// `getEffectiveApiHostUrl()`:自定义 apiHostUrl 优先,否则默认
188    /// `https://api.weixin.qq.com`)。
189    pub fn effective_api_host_url(&self) -> String {
190        if let Some(api_host_url) = self.api_host_url() {
191            if !api_host_url.is_empty() {
192                return api_host_url;
193            }
194        }
195        API_DEFAULT_HOST_URL.to_string()
196    }
197
198    /// 从缓存 map 读取 token 值(过期或缺失返回 None)。
199    fn get_token_string(map: &Mutex<HashMap<String, TokenEntry>>, key: &str) -> Option<String> {
200        let guard = map.lock().unwrap();
201        match guard.get(key) {
202            Some(t) if !t.is_expired(now()) => Some(t.value.clone()),
203            _ => None,
204        }
205    }
206
207    /// 强制将缓存 map 中指定 key 的 token 过期(Java `expireToken`:expiresTime=0)。
208    fn expire_token(map: &Mutex<HashMap<String, TokenEntry>>, key: &str) {
209        let mut guard = map.lock().unwrap();
210        guard.remove(key);
211    }
212
213    /// 线程安全地更新缓存 map 中指定 key 的 token。
214    ///
215    /// Java `updateToken` 语义:expiresInSeconds 为 null 或 -1 时不更新过期
216    /// 时间(refresh_token 永久有效);否则 `expiresTime = now +
217    /// (expiresInSeconds - 200) * 1000`(预留 200 秒提前过期)。
218    fn update_token(
219        map: &Mutex<HashMap<String, TokenEntry>>,
220        key: &str,
221        token: &str,
222        expires_in_seconds: Option<i32>,
223    ) {
224        let mut guard = map.lock().unwrap();
225        let entry = guard.entry(key.to_string()).or_insert_with(|| TokenEntry {
226            value: String::new(),
227            expires_at: None,
228        });
229        entry.value = token.to_string();
230        if let Some(expires_in) = expires_in_seconds {
231            if expires_in != -1 {
232                entry.expires_at = Some(now() + (expires_in - 200).max(0) as i64);
233            }
234        }
235    }
236}
237
238impl WxOpenConfigStorage for WxOpenDefaultConfig {
239    fn component_app_id(&self) -> Option<String> {
240        self.component_app_id.read().unwrap().clone()
241    }
242
243    fn set_component_app_id(&self, component_app_id: &str) {
244        *self.component_app_id.write().unwrap() = Some(component_app_id.to_string());
245    }
246
247    fn component_app_secret(&self) -> Option<String> {
248        self.component_app_secret.read().unwrap().clone()
249    }
250
251    fn set_component_app_secret(&self, component_app_secret: &str) {
252        *self.component_app_secret.write().unwrap() = Some(component_app_secret.to_string());
253    }
254
255    fn component_token(&self) -> Option<String> {
256        self.component_token.read().unwrap().clone()
257    }
258
259    fn set_component_token(&self, component_token: &str) {
260        *self.component_token.write().unwrap() = Some(component_token.to_string());
261    }
262
263    fn component_aes_key(&self) -> Option<String> {
264        self.component_aes_key.read().unwrap().clone()
265    }
266
267    fn set_component_aes_key(&self, component_aes_key: &str) {
268        *self.component_aes_key.write().unwrap() = Some(component_aes_key.to_string());
269    }
270
271    fn component_verify_ticket(&self) -> Option<String> {
272        self.component_verify_ticket.read().unwrap().clone()
273    }
274
275    fn set_component_verify_ticket(&self, component_verify_ticket: &str) {
276        *self.component_verify_ticket.write().unwrap() = Some(component_verify_ticket.to_string());
277    }
278
279    fn component_access_token(&self) -> Option<String> {
280        let guard = self.component_access_token.lock().unwrap();
281        guard.as_ref().map(|t| t.value.clone())
282    }
283
284    fn is_component_access_token_expired(&self) -> bool {
285        let guard = self.component_access_token.lock().unwrap();
286        match guard.as_ref() {
287            Some(t) => t.is_expired(now()),
288            None => true,
289        }
290    }
291
292    fn expire_component_access_token(&self) {
293        let mut guard = self.component_access_token.lock().unwrap();
294        *guard = None;
295    }
296
297    fn update_component_access_token_with_expiry(
298        &self,
299        component_access_token: &str,
300        expires_in_seconds: i32,
301    ) {
302        let mut guard = self.component_access_token.lock().unwrap();
303        *guard = Some(TokenEntry {
304            value: component_access_token.to_string(),
305            // Java `(expiresInSeconds - 200) * 1000L`:预留 200 秒提前过期
306            expires_at: Some(now() + (expires_in_seconds - 200).max(0) as i64),
307        });
308    }
309
310    fn component_access_token_lock(&self) -> Arc<AsyncMutex<()>> {
311        self.component_access_token_lock.clone()
312    }
313
314    fn lock_by_key(&self, key: &str) -> Arc<AsyncMutex<()>> {
315        // Java `locks.computeIfAbsent(key, e -> new ReentrantLock())`
316        let mut guard = self.locks.lock().unwrap();
317        guard
318            .entry(key.to_string())
319            .or_insert_with(|| Arc::new(AsyncMutex::new(())))
320            .clone()
321    }
322
323    fn wx_open_host_config(&self) -> Option<WxOpenHostConfig> {
324        let mut host_config = self.host_config.read().unwrap().clone();
325        // Java `apiHostUrl` 替换语义:自定义 apiHostUrl 优先于默认域名
326        if let Some(api_host_url) = self.api_host_url() {
327            if !api_host_url.is_empty() {
328                host_config.api_host = api_host_url;
329            }
330        }
331        Some(host_config)
332    }
333
334    fn authorizer_refresh_token(&self, app_id: &str) -> Option<String> {
335        Self::get_token_string(&self.authorizer_refresh_tokens, app_id)
336    }
337
338    fn set_authorizer_refresh_token(&self, app_id: &str, authorizer_refresh_token: &str) {
339        Self::update_token(
340            &self.authorizer_refresh_tokens,
341            app_id,
342            authorizer_refresh_token,
343            None,
344        );
345    }
346
347    fn authorizer_access_token(&self, app_id: &str) -> Option<String> {
348        Self::get_token_string(&self.authorizer_access_tokens, app_id)
349    }
350
351    fn is_authorizer_access_token_expired(&self, app_id: &str) -> bool {
352        Self::get_token_string(&self.authorizer_access_tokens, app_id).is_none()
353    }
354
355    fn expire_authorizer_access_token(&self, app_id: &str) {
356        Self::expire_token(&self.authorizer_access_tokens, app_id);
357    }
358
359    fn update_authorizer_access_token_with_expiry(
360        &self,
361        app_id: &str,
362        authorizer_access_token: &str,
363        expires_in_seconds: i32,
364    ) {
365        Self::update_token(
366            &self.authorizer_access_tokens,
367            app_id,
368            authorizer_access_token,
369            Some(expires_in_seconds),
370        );
371    }
372
373    fn jsapi_ticket(&self, app_id: &str) -> Option<String> {
374        Self::get_token_string(&self.jsapi_tickets, app_id)
375    }
376
377    fn is_jsapi_ticket_expired(&self, app_id: &str) -> bool {
378        Self::get_token_string(&self.jsapi_tickets, app_id).is_none()
379    }
380
381    fn expire_jsapi_ticket(&self, app_id: &str) {
382        Self::expire_token(&self.jsapi_tickets, app_id);
383    }
384
385    fn update_jsapi_ticket(&self, app_id: &str, jsapi_ticket: &str, expires_in_seconds: i32) {
386        Self::update_token(
387            &self.jsapi_tickets,
388            app_id,
389            jsapi_ticket,
390            Some(expires_in_seconds),
391        );
392    }
393
394    fn card_api_ticket(&self, app_id: &str) -> Option<String> {
395        Self::get_token_string(&self.card_api_tickets, app_id)
396    }
397
398    fn is_card_api_ticket_expired(&self, app_id: &str) -> bool {
399        Self::get_token_string(&self.card_api_tickets, app_id).is_none()
400    }
401
402    fn expire_card_api_ticket(&self, app_id: &str) {
403        Self::expire_token(&self.card_api_tickets, app_id);
404    }
405
406    fn update_card_api_ticket(&self, app_id: &str, card_api_ticket: &str, expires_in_seconds: i32) {
407        Self::update_token(
408            &self.card_api_tickets,
409            app_id,
410            card_api_ticket,
411            Some(expires_in_seconds),
412        );
413    }
414
415    fn http_proxy_host(&self) -> Option<String> {
416        self.http_proxy_host.read().unwrap().clone()
417    }
418
419    fn http_proxy_port(&self) -> i32 {
420        self.http_proxy_port
421    }
422
423    fn http_proxy_username(&self) -> Option<String> {
424        self.http_proxy_username.read().unwrap().clone()
425    }
426
427    fn http_proxy_password(&self) -> Option<String> {
428        self.http_proxy_password.read().unwrap().clone()
429    }
430
431    fn retry_sleep_millis(&self) -> i32 {
432        self.retry_sleep_millis
433    }
434
435    fn max_retry_times(&self) -> i32 {
436        self.max_retry_times
437    }
438
439    fn component_api_signature_rsa_private_key(&self) -> Option<String> {
440        self.component_api_signature_rsa_private_key
441            .read()
442            .unwrap()
443            .clone()
444    }
445
446    fn set_component_api_signature_rsa_private_key(&self, api_signature_rsa_private_key: &str) {
447        *self
448            .component_api_signature_rsa_private_key
449            .write()
450            .unwrap() = Some(api_signature_rsa_private_key.to_string());
451    }
452
453    fn component_api_signature_aes_key(&self) -> Option<String> {
454        self.component_api_signature_aes_key.read().unwrap().clone()
455    }
456
457    fn set_component_api_signature_aes_key(&self, api_signature_aes_key: &str) {
458        *self.component_api_signature_aes_key.write().unwrap() =
459            Some(api_signature_aes_key.to_string());
460    }
461
462    fn component_api_signature_rsa_private_key_sn(&self) -> Option<String> {
463        self.component_api_signature_rsa_private_key_sn
464            .read()
465            .unwrap()
466            .clone()
467    }
468
469    fn set_component_api_signature_rsa_private_key_sn(
470        &self,
471        api_signature_rsa_private_key_sn: &str,
472    ) {
473        *self
474            .component_api_signature_rsa_private_key_sn
475            .write()
476            .unwrap() = Some(api_signature_rsa_private_key_sn.to_string());
477    }
478
479    fn component_api_signature_aes_key_sn(&self) -> Option<String> {
480        self.component_api_signature_aes_key_sn
481            .read()
482            .unwrap()
483            .clone()
484    }
485
486    fn set_component_api_signature_aes_key_sn(&self, api_signature_aes_key_sn: &str) {
487        *self.component_api_signature_aes_key_sn.write().unwrap() =
488            Some(api_signature_aes_key_sn.to_string());
489    }
490}
491
492impl Default for WxOpenDefaultConfig {
493    fn default() -> Self {
494        Self::new()
495    }
496}