wx_rust_open/api/impl/
base_wx_open_service_impl.rs1use wx_rust_common::api::wx_consts::ACCESS_TOKEN_ERROR_CODES;
19use wx_rust_common::enums::WxType;
20use wx_rust_common::error::{WxError, WxErrorException};
21use wx_rust_common::util::http::{RequestExecutor, SimplePostRequestExecutor};
22
23use crate::api::WxOpenService;
24use crate::bean::WxOpenComponentAccessToken;
25use crate::enums::url_core::api_component_token_url;
26
27fn build_uri_with_token(uri: &str, access_token_key: &str, token: &str) -> String {
30 if uri.contains('?') {
31 format!("{uri}&{access_token_key}={token}")
32 } else {
33 format!("{uri}?{access_token_key}={token}")
34 }
35}
36
37pub async fn execute_with_retry<S, T, E>(
43 svc: &S,
44 executor: &dyn RequestExecutor<T, E>,
45 uri: &str,
46 data: E,
47 access_token_key: &str,
48) -> Result<T, WxErrorException>
49where
50 S: WxOpenService + ?Sized,
51 T: Send,
52 E: Send + Clone,
53{
54 let config = svc.wx_open_config_storage();
55 let max_retry_times = config.max_retry_times();
56 let retry_sleep_millis = config.retry_sleep_millis();
57
58 let mut retry_times = 0;
59 loop {
60 match execute_internal(svc, executor, uri, &data, access_token_key, false).await {
61 Ok(result) => return Ok(result),
62 Err(e) => {
63 if e.error_code() == Some(-1) {
65 if retry_times + 1 > max_retry_times {
66 return Err(WxErrorException::from_code(
67 -99,
68 "微信服务端异常,超出重试次数",
69 ));
70 }
71 let sleep_millis = retry_sleep_millis * (1 << retry_times);
72 tokio::time::sleep(std::time::Duration::from_millis(sleep_millis as u64)).await;
73 } else {
74 return Err(e);
75 }
76 }
77 }
78 retry_times += 1;
79 if retry_times > max_retry_times {
80 break;
81 }
82 }
83 Err(WxErrorException::from_code(
84 -99,
85 "微信服务端异常,超出重试次数",
86 ))
87}
88
89pub async fn execute_internal<S, T, E>(
95 svc: &S,
96 executor: &dyn RequestExecutor<T, E>,
97 uri: &str,
98 data: &E,
99 access_token_key: &str,
100 do_not_auto_refresh: bool,
101) -> Result<T, WxErrorException>
102where
103 S: WxOpenService + ?Sized,
104 T: Send,
105 E: Send + Clone,
106{
107 if uri.contains(&format!("{access_token_key}=")) {
108 return Err(WxErrorException::from_code(
109 -99,
110 format!("uri参数中不允许有{access_token_key}: {uri}"),
111 ));
112 }
113
114 let config = svc.wx_open_config_storage();
115 let component_access_token = svc.get_component_access_token(false).await?;
116 let mut component_access_token = component_access_token;
117 let mut uri_with_token = build_uri_with_token(uri, access_token_key, &component_access_token);
118
119 let mut do_not_auto_refresh = do_not_auto_refresh;
123 loop {
124 match executor
125 .execute(&uri_with_token, data.clone(), WxType::Open)
126 .await
127 {
128 Ok(result) => return Ok(result),
129 Err(e) => {
130 if let Some(code) = e.error_code() {
131 if ACCESS_TOKEN_ERROR_CODES.contains(&code) {
132 {
136 let lock = config.component_access_token_lock();
137 let _guard = lock.lock().await;
138 if config.component_access_token().as_deref()
139 == Some(component_access_token.as_str())
140 {
141 config.expire_component_access_token();
142 }
143 }
144 if config.auto_refresh_token() && !do_not_auto_refresh {
145 do_not_auto_refresh = true;
147 component_access_token = svc.get_component_access_token(false).await?;
148 uri_with_token = build_uri_with_token(
149 uri,
150 access_token_key,
151 &component_access_token,
152 );
153 continue;
154 }
155 }
156 if code != 0 {
157 return Err(e);
158 }
159 return Err(e);
163 }
164 return Err(e);
165 }
166 }
167 }
168}
169
170pub async fn get_component_access_token_with_lock<S>(
178 svc: &S,
179 force_refresh: bool,
180) -> Result<String, WxErrorException>
181where
182 S: WxOpenService + ?Sized,
183{
184 let config = svc.wx_open_config_storage();
185 if !force_refresh && !config.is_component_access_token_expired() {
186 return config
187 .component_access_token()
188 .ok_or_else(|| WxErrorException::from_code(-99, "component access token 为空"));
189 }
190
191 let lock = config.component_access_token_lock();
192 let timeout_at = std::time::Instant::now() + std::time::Duration::from_millis(3000);
193 let _guard = loop {
195 if !force_refresh && !config.is_component_access_token_expired() {
196 return config
197 .component_access_token()
198 .ok_or_else(|| WxErrorException::from_code(-99, "component access token 为空"));
199 }
200 match lock.try_lock() {
201 Ok(guard) => break guard,
202 Err(_) => {
203 if std::time::Instant::now() > timeout_at {
204 return Err(WxErrorException::from_code(
205 -99,
206 "获取componentAccessToken超时:获取时间超时",
207 ));
208 }
209 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
210 }
211 }
212 };
213
214 let body = serde_json::json!({
216 "component_appid": config.component_app_id().unwrap_or_default(),
217 "component_appsecret": config.component_app_secret().unwrap_or_default(),
218 "component_verify_ticket": config.component_verify_ticket().unwrap_or_default(),
219 });
220 let executor = SimplePostRequestExecutor::new(svc.http_client().clone());
223 let uri = api_component_token_url(config.as_ref());
224 let response = executor
225 .execute(&uri, body.to_string(), WxType::Open)
226 .await?;
227
228 let component_access_token = extract_component_access_token(&response)?;
229 config.update_component_access_token(&component_access_token);
230 Ok(config
231 .component_access_token()
232 .unwrap_or_else(|| component_access_token.component_access_token().to_string()))
233}
234
235pub fn extract_component_access_token(
240 result_content: &str,
241) -> Result<WxOpenComponentAccessToken, WxErrorException> {
242 let error = WxError::from_json_with_type(result_content, Some(WxType::Open));
243 if error.error_code != 0 {
244 return Err(WxErrorException::from_code(
245 error.error_code,
246 error.error_msg.unwrap_or_default(),
247 ));
248 }
249 WxOpenComponentAccessToken::from_json(result_content)
250 .map_err(|e| WxErrorException::Serde(e.to_string()))
251}
252
253pub fn normalize_errcode(json: &str) -> Result<String, WxErrorException> {
262 let mut value: serde_json::Value =
263 serde_json::from_str(json).map_err(|e| WxErrorException::Serde(e.to_string()))?;
264 if let Some(errcode) = value.get("errcode") {
265 if errcode.is_number() {
266 value["errcode"] = serde_json::Value::String(errcode.to_string());
267 }
268 }
269 serde_json::to_string(&value).map_err(|e| WxErrorException::Serde(e.to_string()))
270}