use std::sync::{Arc, Weak};
use async_trait::async_trait;
use wx_rust_common::bean::CommonUploadParam;
use wx_rust_common::error::WxErrorException;
use wx_rust_common::util::http::{
MediaDownloadRequestExecutor, MediaUploadRequestExecutor, RequestExecutor,
};
use wx_rust_miniapp::api::WxMaService;
use crate::api::WxOpenMaIcpService;
use crate::api::WxOpenService;
use crate::api::r#impl::WxOpenMaService;
use crate::api::r#impl::base_wx_open_service_impl;
use crate::bean::WxOpenApplyIcpFilingParam;
use crate::bean::WxOpenApplyIcpFilingResult;
use crate::bean::WxOpenIcpCreateIcpVerifyTaskResult;
use crate::bean::WxOpenIcpEntranceInfoResult;
use crate::bean::WxOpenIcpVerifyTaskResult;
use crate::bean::WxOpenOnlineIcpOrderResult;
use crate::bean::WxOpenQueryIcpCertificateTypeResult;
use crate::bean::WxOpenQueryIcpDistrictCodeResult;
use crate::bean::WxOpenQueryIcpNrlxTypesResult;
use crate::bean::WxOpenQueryIcpServiceContentTypesResult;
use crate::bean::WxOpenQueryIcpSubjectTypeResult;
use crate::bean::WxOpenResult;
use crate::bean::WxOpenUploadIcpMediaParam;
use crate::bean::WxOpenUploadIcpMediaResult;
use crate::enums::url_ma_domain::{
ma_icp_apply_filing_url, ma_icp_cancel_apply_filing_url, ma_icp_cancel_filing_url,
ma_icp_create_verify_task_url, ma_icp_get_entrance_info_url, ma_icp_get_media_url,
ma_icp_get_online_order_url, ma_icp_query_certificate_types_url,
ma_icp_query_district_code_url, ma_icp_query_nrlx_types_url,
ma_icp_query_service_content_types_url, ma_icp_query_subject_types_url,
ma_icp_query_verify_task_url, ma_icp_upload_media_url,
};
pub struct WxOpenMaIcpServiceImpl {
wx_open_service: Weak<dyn WxOpenService>,
app_id: String,
}
impl WxOpenMaIcpServiceImpl {
pub fn new(wx_open_service: Arc<dyn WxOpenService>, app_id: String) -> Self {
Self {
wx_open_service: Arc::downgrade(&wx_open_service),
app_id,
}
}
pub fn app_id(&self) -> &str {
&self.app_id
}
fn svc(&self) -> Result<Arc<dyn WxOpenService>, WxErrorException> {
self.wx_open_service
.upgrade()
.ok_or_else(|| WxErrorException::from_code(-99, "门面服务已被释放"))
}
fn ma_service(&self) -> Result<Arc<dyn WxMaService>, WxErrorException> {
let svc = self.svc()?;
let component = svc.wx_open_component_service().ok_or_else(|| {
WxErrorException::from_code(
-99,
"组件子服务未装配(getWxOpenComponentService 返回 null)",
)
})?;
let any = component
.get_wx_ma_service_by_appid(&self.app_id)
.ok_or_else(|| WxErrorException::from_code(-99, "getWxMaServiceByAppid 返回 None"))?;
let ma = any.downcast::<WxOpenMaService>().map_err(|_| {
WxErrorException::from_code(-99, "代 ma 服务 downcast 失败(缓存类型不匹配)")
})?;
Ok(ma as Arc<dyn WxMaService>)
}
}
#[async_trait]
impl WxOpenMaIcpService for WxOpenMaIcpServiceImpl {
async fn query_icp_verify_task(
&self,
task_id: &str,
) -> Result<WxOpenIcpVerifyTaskResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let body = serde_json::json!({ "task_id": task_id });
let response = ma
.post(
&ma_icp_query_verify_task_url(config.as_ref()),
&body.to_string(),
)
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn create_icp_verify_task(
&self,
along_with_auth: bool,
) -> Result<WxOpenIcpCreateIcpVerifyTaskResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let body = serde_json::json!({ "along_with_auth": along_with_auth });
let response = ma
.post(
&ma_icp_create_verify_task_url(config.as_ref()),
&body.to_string(),
)
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn upload_icp_media(
&self,
param: &WxOpenUploadIcpMediaParam,
) -> Result<WxOpenUploadIcpMediaResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let multi = param.to_common_upload_multi_param();
let mut form_fields = std::collections::HashMap::new();
for normal in &multi.normal_params {
form_fields.insert(normal.name.clone(), normal.value.clone());
}
let upload_param = multi.upload_param.ok_or_else(|| {
WxErrorException::from_code(-99, "uploadIcpMedia 缺少 media 文件(Java media 为 null)")
})?;
let executor = MediaUploadRequestExecutor::new(ma.http_client().clone());
let param = CommonUploadParam::with_form_fields(
upload_param.name.clone(),
upload_param.data.clone(),
form_fields,
);
let response = wx_rust_miniapp::api::r#impl::base_wx_ma_service_impl::execute_with_retry(
ma.as_ref(),
&executor,
&ma_icp_upload_media_url(config.as_ref()),
param,
)
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn cancel_apply_icp_filing(&self) -> Result<WxOpenResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let response = ma
.post(&ma_icp_cancel_apply_filing_url(config.as_ref()), "")
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn apply_icp_filing(
&self,
param: &WxOpenApplyIcpFilingParam,
) -> Result<WxOpenApplyIcpFilingResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let body =
serde_json::to_string(param).map_err(|e| WxErrorException::Serde(e.to_string()))?;
let response = ma
.post(&ma_icp_apply_filing_url(config.as_ref()), &body)
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn cancel_icp_filing(&self, cancel_type: i32) -> Result<WxOpenResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let body = serde_json::json!({ "cancel_type": cancel_type });
let response = ma
.post(
&ma_icp_cancel_filing_url(config.as_ref()),
&body.to_string(),
)
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn get_icp_entrance_info(&self) -> Result<WxOpenIcpEntranceInfoResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let response = ma
.get(&ma_icp_get_entrance_info_url(config.as_ref()), "")
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn get_online_icp_order(&self) -> Result<WxOpenOnlineIcpOrderResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let response = ma
.get(&ma_icp_get_online_order_url(config.as_ref()), "")
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn query_icp_service_content_types(
&self,
) -> Result<WxOpenQueryIcpServiceContentTypesResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let response = ma
.get(&ma_icp_query_service_content_types_url(config.as_ref()), "")
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn query_icp_certificate_types(
&self,
) -> Result<WxOpenQueryIcpCertificateTypeResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let response = ma
.get(&ma_icp_query_certificate_types_url(config.as_ref()), "")
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn query_icp_district_code(
&self,
) -> Result<WxOpenQueryIcpDistrictCodeResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let response = ma
.get(&ma_icp_query_district_code_url(config.as_ref()), "")
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn query_icp_nrlx_types(
&self,
) -> Result<WxOpenQueryIcpNrlxTypesResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let response = ma
.get(&ma_icp_query_nrlx_types_url(config.as_ref()), "")
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn query_icp_subject_types(
&self,
) -> Result<WxOpenQueryIcpSubjectTypeResult, WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let response = ma
.get(&ma_icp_query_subject_types_url(config.as_ref()), "")
.await?;
let response = base_wx_open_service_impl::normalize_errcode(&response)?;
serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn get_icp_media(
&self,
media_id: &str,
target_path: &str,
) -> Result<(), WxErrorException> {
let svc = self.svc()?;
let config = svc.wx_open_config_storage();
let ma = self.ma_service()?;
let executor = MediaDownloadRequestExecutor::new(ma.http_client().clone());
let uri = format!(
"{}?media_id={media_id}",
ma_icp_get_media_url(config.as_ref())
);
let downloaded = executor
.execute(&uri, String::new(), wx_rust_common::enums::WxType::MiniApp)
.await?;
std::fs::write(target_path, downloaded)
.map_err(|e| WxErrorException::from_code(-99, format!("写入备案媒体文件失败: {e}")))
}
}