Skip to main content

wx_rust_open/api/
wx_open_service.rs

1//! 开放平台(第三方平台)服务门面。
2//!
3//! 对应 Java `me.chanjar.weixin.open.api.WxOpenService`(7 个方法)+
4//! `WxOpenServiceAbstractImpl`(执行引擎)在门面上暴露的方法,并叠加
5//! open 模块特有的组件能力入口(对应 Java `WxOpenComponentService` 的
6//! component 预授权码 / component_access_token / 三方 token 刷新 / POST
7//! 回调消息解密,见任务约定)。
8//!
9//! Java 继承链(`WxOpenServiceImpl` → `WxOpenServiceHttpComponentsImpl` →
10//! `WxOpenServiceAbstractImpl`)在 Rust 以 trait 默认实现 + 组合表达
11//! (与 mp/ma 模块同一设计原则)。执行引擎(指数退避重试 + 自动刷新 +
12//! component_access_token 注入)在 `crate::api::impl::base_wx_open_service_impl`
13//! 中以泛型自由函数承载(trait 无法携带泛型方法,破坏 dyn 兼容)。
14//!
15//! 说明:
16//! - Java `get(String, String)`/`post(String, String)` 为裸请求(
17//!   `WxOpenServiceAbstractImpl.execute` 直调执行器);Rust 侧与 mp/ma
18//!   执行引擎对齐,门面 get/post 自动注入 `component_access_token`
19//!   (ADAPTED,token 注入在 Java 中位于组件服务层)。
20//! - 组件子服务 getter 默认返回 `None`,`WxOpenServiceImpl` 覆写为装配后的
21//!   实例。
22
23use std::sync::Arc;
24
25use async_trait::async_trait;
26
27use wx_rust_common::bean::result::WxMinishopImageUploadResult;
28use wx_rust_common::error::WxErrorException;
29use wx_rust_common::util::http::{SimpleGetRequestExecutor, SimplePostRequestExecutor};
30
31use crate::api::WxOpenComponentService;
32use crate::bean::message::WxOpenXmlMessage;
33use crate::config::WxOpenConfigStorage;
34use crate::constant::wx_open_constants::ACCESS_TOKEN_KEY_COMPONENT;
35
36/// 开放平台(第三方平台)服务门面。
37#[async_trait]
38pub trait WxOpenService: Send + Sync {
39    // ---- 配置与子服务(对应 Java WxOpenService) ----
40
41    /// 组件子服务(对应 Java `getWxOpenComponentService()`)。
42    ///
43    /// 默认返回 `None`,`WxOpenServiceImpl` 覆写为装配后的实例。
44    fn wx_open_component_service(&self) -> Option<Arc<dyn WxOpenComponentService>> {
45        None
46    }
47
48    /// 配置存储(对应 Java `getWxOpenConfigStorage()`)。
49    fn wx_open_config_storage(&self) -> Arc<dyn WxOpenConfigStorage>;
50
51    /// 设置配置存储(对应 Java `setWxOpenConfigStorage(WxOpenConfigStorage)`)。
52    fn set_wx_open_config_storage(&self, wx_open_config_storage: Arc<dyn WxOpenConfigStorage>);
53
54    /// HTTP 客户端(对应 Java `RequestHttp.getRequestHttpClient()`)。
55    fn http_client(&self) -> &reqwest::Client;
56
57    // ---- 核心能力(对应 Java WxOpenService get/post + 执行引擎) ----
58
59    /// GET 请求(对应 Java `get(String, String)`)。
60    ///
61    /// 当本 Service 没有实现某个 API 时可用,针对所有微信 API 中的 GET
62    /// 请求;执行引擎自动附加 `component_access_token`。
63    async fn get(&self, url: &str, query_param: &str) -> Result<String, WxErrorException> {
64        let executor = SimpleGetRequestExecutor::new(self.http_client().clone());
65        crate::api::r#impl::base_wx_open_service_impl::execute_with_retry(
66            self,
67            &executor,
68            url,
69            query_param.to_string(),
70            ACCESS_TOKEN_KEY_COMPONENT,
71        )
72        .await
73    }
74
75    /// POST 请求(对应 Java `post(String, String)`)。
76    ///
77    /// 当本 Service 没有实现某个 API 时可用,针对所有微信 API 中的 POST
78    /// 请求;执行引擎自动附加 `component_access_token`。
79    async fn post(&self, url: &str, post_data: &str) -> Result<String, WxErrorException> {
80        let executor = SimplePostRequestExecutor::new(self.http_client().clone());
81        crate::api::r#impl::base_wx_open_service_impl::execute_with_retry(
82            self,
83            &executor,
84            url,
85            post_data.to_string(),
86            ACCESS_TOKEN_KEY_COMPONENT,
87        )
88        .await
89    }
90
91    /// 上传图片到小程序/开放平台素材库(对应 Java
92    /// `uploadMinishopMediaFile(String url, File file)`)。
93    ///
94    /// ADAPTED:Java 以 `File` 入参,Rust 以文件路径字符串表达;需要
95    /// `MinishopUploadRequestExecutor`(multipart 上传 + JSON 结果解析),
96    /// Wave 1 实现,当前返回未实现错误。
97    async fn upload_minishop_media_file(
98        &self,
99        _url: &str,
100        _file_path: &str,
101    ) -> Result<WxMinishopImageUploadResult, WxErrorException> {
102        Err(WxErrorException::from_code(
103            -99,
104            "upload_minishop_media_file 未实现(Wave 1:MinishopUploadRequestExecutor)",
105        ))
106    }
107
108    // ---- 开放平台特殊能力入口(对应 Java WxOpenComponentService 核心方法,
109    // 任务约定在门面暴露;默认委托组件子服务,未装配时返回错误) ----
110
111    /// 获取 component_access_token(对应 Java
112    /// `WxOpenComponentService.getComponentAccessToken(boolean)`,
113    /// 开放平台接口调用凭据)。
114    async fn get_component_access_token(
115        &self,
116        force_refresh: bool,
117    ) -> Result<String, WxErrorException> {
118        match self.wx_open_component_service() {
119            Some(svc) => svc.get_component_access_token(force_refresh).await,
120            None => Err(WxErrorException::from_code(
121                -99,
122                "组件子服务未装配(getWxOpenComponentService 返回 null)",
123            )),
124        }
125    }
126
127    /// 获取预授权码(对应 Java `WxOpenComponentServiceImpl.createPreAuthUrl`
128    /// 内部 POST `API_CREATE_PREAUTHCODE_URL` 的 `pre_auth_code` 字段)。
129    async fn get_pre_auth_code(&self) -> Result<String, WxErrorException> {
130        match self.wx_open_component_service() {
131            Some(svc) => svc.get_pre_auth_code().await,
132            None => Err(WxErrorException::from_code(
133                -99,
134                "组件子服务未装配(getWxOpenComponentService 返回 null)",
135            )),
136        }
137    }
138
139    /// 获取网页授权预授权链接(对应 Java `WxOpenComponentService.getPreAuthUrl(String)`)。
140    async fn get_pre_auth_url(&self, redirect_uri: &str) -> Result<String, WxErrorException> {
141        match self.wx_open_component_service() {
142            Some(svc) => svc.get_pre_auth_url(redirect_uri).await,
143            None => Err(WxErrorException::from_code(
144                -99,
145                "组件子服务未装配(getWxOpenComponentService 返回 null)",
146            )),
147        }
148    }
149
150    /// 获取网页授权预授权链接(对应 Java
151    /// `WxOpenComponentService.getPreAuthUrl(String, String, String)`)。
152    async fn get_pre_auth_url_with(
153        &self,
154        redirect_uri: &str,
155        auth_type: Option<&str>,
156        biz_appid: Option<&str>,
157    ) -> Result<String, WxErrorException> {
158        match self.wx_open_component_service() {
159            Some(svc) => {
160                svc.get_pre_auth_url_with(redirect_uri, auth_type, biz_appid)
161                    .await
162            }
163            None => Err(WxErrorException::from_code(
164                -99,
165                "组件子服务未装配(getWxOpenComponentService 返回 null)",
166            )),
167        }
168    }
169
170    /// 获取移动端预授权链接(对应 Java
171    /// `WxOpenComponentService.getMobilePreAuthUrl(String)`)。
172    async fn get_mobile_pre_auth_url(
173        &self,
174        redirect_uri: &str,
175    ) -> Result<String, WxErrorException> {
176        match self.wx_open_component_service() {
177            Some(svc) => svc.get_mobile_pre_auth_url(redirect_uri).await,
178            None => Err(WxErrorException::from_code(
179                -99,
180                "组件子服务未装配(getWxOpenComponentService 返回 null)",
181            )),
182        }
183    }
184
185    /// 获取移动端预授权链接(对应 Java
186    /// `WxOpenComponentService.getMobilePreAuthUrl(String, String, String)`)。
187    async fn get_mobile_pre_auth_url_with(
188        &self,
189        redirect_uri: &str,
190        auth_type: Option<&str>,
191        biz_appid: Option<&str>,
192    ) -> Result<String, WxErrorException> {
193        match self.wx_open_component_service() {
194            Some(svc) => {
195                svc.get_mobile_pre_auth_url_with(redirect_uri, auth_type, biz_appid)
196                    .await
197            }
198            None => Err(WxErrorException::from_code(
199                -99,
200                "组件子服务未装配(getWxOpenComponentService 返回 null)",
201            )),
202        }
203    }
204
205    /// 获取(刷新)授权方 access_token(对应 Java
206    /// `WxOpenComponentService.getAuthorizerAccessToken(String, boolean)`,
207    /// 三方 token 刷新:component_access_token + authorizer refresh_token
208    /// 换新 token)。
209    async fn get_authorizer_access_token(
210        &self,
211        app_id: &str,
212        force_refresh: bool,
213    ) -> Result<String, WxErrorException> {
214        match self.wx_open_component_service() {
215            Some(svc) => svc.get_authorizer_access_token(app_id, force_refresh).await,
216            None => Err(WxErrorException::from_code(
217                -99,
218                "组件子服务未装配(getWxOpenComponentService 返回 null)",
219            )),
220        }
221    }
222
223    /// 路由第三方平台推送的加密回调消息(对应 Java
224    /// `WxOpenComponentService.route(WxOpenXmlMessage)`,POST 回调消息
225    /// 解密 + 分发)。
226    ///
227    /// 入参为解密后的 [`WxOpenXmlMessage`](解密经
228    /// `WxOpenXmlMessage::from_encrypted_xml` 完成)。
229    async fn route(&self, message: &WxOpenXmlMessage) -> Result<String, WxErrorException> {
230        match self.wx_open_component_service() {
231            Some(svc) => svc.route(message).await,
232            None => Err(WxErrorException::from_code(
233                -99,
234                "组件子服务未装配(getWxOpenComponentService 返回 null)",
235            )),
236        }
237    }
238}