Skip to main content

wx_rust_open/api/impl/
wx_open_ma_embedded_service_impl.rs

1//! 半屏小程序管理服务实现。
2//!
3//! 对应 Java `me.chanjar.weixin.open.api.impl.WxOpenMaEmbeddedServiceImpl`
4//! (`@AllArgsConstructor` 持有 `WxMaService`——代 ma 桥接服务)。
5//!
6//! Java 每个方法在 post/get 后额外以 `WxError.fromJson(response)` 校验
7//! `errcode != 0` 抛异常;由于代 ma 执行引擎(SimplePost/GetRequestExecutor)
8//! 对 `errcode != 0` 已抛错,该二次校验在 Java 侧同为防御性死代码,Rust
9//! 不再重复(ADAPTED,见各方法注释)。
10//!
11//! `getOwnList()` 无参重载对应 Java 直接 GET `?num=1000`(默认分页起始
12//! 0、一次拉取最大 1000);`getOwnList(start, num)` 对 null 取默认值
13//! (0/10)、`num > 1000` 截断为 1000,Rust 以 `Option` 入参镜像。
14
15use std::sync::{Arc, Weak};
16
17use async_trait::async_trait;
18
19use wx_rust_common::error::WxErrorException;
20use wx_rust_miniapp::api::WxMaService;
21
22use crate::api::WxOpenMaEmbeddedService;
23use crate::api::WxOpenService;
24use crate::api::r#impl::WxOpenMaService;
25use crate::api::r#impl::base_wx_open_service_impl;
26use crate::bean::WxOpenMaEmbeddedListResult;
27use crate::enums::url_ma_domain::{
28    ma_embedded_add_url, ma_embedded_del_authorize_url, ma_embedded_del_url,
29    ma_embedded_get_list_url, ma_embedded_get_own_list_url, ma_embedded_set_authorize_url,
30};
31
32/// 半屏小程序管理服务实现(对应 Java `WxOpenMaEmbeddedServiceImpl`)。
33pub struct WxOpenMaEmbeddedServiceImpl {
34    wx_open_service: Weak<dyn WxOpenService>,
35    app_id: String,
36}
37
38impl WxOpenMaEmbeddedServiceImpl {
39    /// 构建服务(对应 Java
40    /// `new WxOpenMaEmbeddedServiceImpl(WxMaService)`)。
41    pub fn new(wx_open_service: Arc<dyn WxOpenService>, app_id: String) -> Self {
42        Self {
43            wx_open_service: Arc::downgrade(&wx_open_service),
44            app_id,
45        }
46    }
47
48    /// 授权方 appid(Java 构造入参,代运营目标账号)。
49    pub fn app_id(&self) -> &str {
50        &self.app_id
51    }
52
53    fn svc(&self) -> Result<Arc<dyn WxOpenService>, WxErrorException> {
54        self.wx_open_service
55            .upgrade()
56            .ok_or_else(|| WxErrorException::from_code(-99, "门面服务已被释放"))
57    }
58
59    /// 取代 ma 桥接服务(同
60    /// [`WxOpenMaAuthServiceImpl::ma_service`])。
61    fn ma_service(&self) -> Result<Arc<dyn WxMaService>, WxErrorException> {
62        let svc = self.svc()?;
63        let component = svc.wx_open_component_service().ok_or_else(|| {
64            WxErrorException::from_code(
65                -99,
66                "组件子服务未装配(getWxOpenComponentService 返回 null)",
67            )
68        })?;
69        let any = component
70            .get_wx_ma_service_by_appid(&self.app_id)
71            .ok_or_else(|| WxErrorException::from_code(-99, "getWxMaServiceByAppid 返回 None"))?;
72        let ma = any.downcast::<WxOpenMaService>().map_err(|_| {
73            WxErrorException::from_code(-99, "代 ma 服务 downcast 失败(缓存类型不匹配)")
74        })?;
75        Ok(ma as Arc<dyn WxMaService>)
76    }
77}
78
79#[async_trait]
80impl WxOpenMaEmbeddedService for WxOpenMaEmbeddedServiceImpl {
81    /// 添加半屏小程序(对应 Java `addEmbedded(...)`:POST
82    /// `{"appid": ...}`,`apply_reason` 非空才携带
83    /// `{"apply_reason": ...}`,Java `StringUtils.isNotBlank` 语义)。
84    async fn add_embedded(
85        &self,
86        embedded_app_id: &str,
87        apply_reason: &str,
88    ) -> Result<(), WxErrorException> {
89        let svc = self.svc()?;
90        let config = svc.wx_open_config_storage();
91        let ma = self.ma_service()?;
92        let mut body = serde_json::json!({ "appid": embedded_app_id });
93        if !apply_reason.trim().is_empty() {
94            body["apply_reason"] = serde_json::Value::String(apply_reason.to_string());
95        }
96        ma.post(&ma_embedded_add_url(config.as_ref()), &body.to_string())
97            .await?;
98        Ok(())
99    }
100
101    /// 删除半屏小程序(对应 Java `deleteEmbedded(...)`:POST
102    /// `{"appid": ...}`)。
103    async fn delete_embedded(&self, embedded_app_id: &str) -> Result<(), WxErrorException> {
104        let svc = self.svc()?;
105        let config = svc.wx_open_config_storage();
106        let ma = self.ma_service()?;
107        let body = serde_json::json!({ "appid": embedded_app_id });
108        ma.post(&ma_embedded_del_url(config.as_ref()), &body.to_string())
109            .await?;
110        Ok(())
111    }
112
113    /// 获取半屏小程序调用列表(对应 Java `getEmbeddedList()`:GET
114    /// `null` 参数 → `WxOpenMaEmbeddedListResult`)。
115    async fn get_embedded_list(&self) -> Result<WxOpenMaEmbeddedListResult, WxErrorException> {
116        let svc = self.svc()?;
117        let config = svc.wx_open_config_storage();
118        let ma = self.ma_service()?;
119        let response = ma
120            .get(&ma_embedded_get_list_url(config.as_ref()), "")
121            .await?;
122        // Java Gson 宽松类型转换:数字 errcode 归一化为字符串(bean 的
123        // errcode 字段为 String,镜像 Java 字段类型)
124        let response = base_wx_open_service_impl::normalize_errcode(&response)?;
125        serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
126    }
127
128    /// 取消授权小程序(对应 Java `deleteAuthorizedEmbedded(...)`:POST
129    /// `{"appid": ...}`)。
130    async fn delete_authorized_embedded(
131        &self,
132        embedded_app_id: &str,
133    ) -> Result<(), WxErrorException> {
134        let svc = self.svc()?;
135        let config = svc.wx_open_config_storage();
136        let ma = self.ma_service()?;
137        let body = serde_json::json!({ "appid": embedded_app_id });
138        ma.post(
139            &ma_embedded_del_authorize_url(config.as_ref()),
140            &body.to_string(),
141        )
142        .await?;
143        Ok(())
144    }
145
146    /// 获取半屏小程序授权列表(对应 Java `getOwnList()`:GET
147    /// `?num=1000`)。
148    async fn get_own_list(&self) -> Result<WxOpenMaEmbeddedListResult, WxErrorException> {
149        let svc = self.svc()?;
150        let config = svc.wx_open_config_storage();
151        let ma = self.ma_service()?;
152        let url = format!("{}?num=1000", ma_embedded_get_own_list_url(config.as_ref()));
153        let response = ma.get(&url, "").await?;
154        // Java Gson 宽松类型转换:数字 errcode 归一化为字符串(bean 的
155        // errcode 字段为 String,镜像 Java 字段类型)
156        let response = base_wx_open_service_impl::normalize_errcode(&response)?;
157        serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
158    }
159
160    /// 获取半屏小程序授权列表(对应 Java `getOwnList(Integer start,
161    /// Integer num)`:null 取默认值 0/10、`num > 1000` 截断,GET
162    /// `?start={start}&num={num}`)。
163    async fn get_own_list_with(
164        &self,
165        start: Option<i32>,
166        num: Option<i32>,
167    ) -> Result<WxOpenMaEmbeddedListResult, WxErrorException> {
168        let start = start.unwrap_or(0);
169        let num = num.unwrap_or(10).min(1000);
170        let svc = self.svc()?;
171        let config = svc.wx_open_config_storage();
172        let ma = self.ma_service()?;
173        let url = format!(
174            "{}?start={start}&num={num}",
175            ma_embedded_get_own_list_url(config.as_ref())
176        );
177        let response = ma.get(&url, "").await?;
178        // Java Gson 宽松类型转换:数字 errcode 归一化为字符串(bean 的
179        // errcode 字段为 String,镜像 Java 字段类型)
180        let response = base_wx_open_service_impl::normalize_errcode(&response)?;
181        serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
182    }
183
184    /// 设置授权方式(对应 Java `setAuthorizedEmbedded(Integer flag)`:
185    /// POST `{"flag": ...}`;0 需要管理员验证,1 自动通过,2 自动拒绝)。
186    async fn set_authorized_embedded(&self, flag: i32) -> Result<(), WxErrorException> {
187        let svc = self.svc()?;
188        let config = svc.wx_open_config_storage();
189        let ma = self.ma_service()?;
190        let body = serde_json::json!({ "flag": flag });
191        ma.post(
192            &ma_embedded_set_authorize_url(config.as_ref()),
193            &body.to_string(),
194        )
195        .await?;
196        Ok(())
197    }
198}