use std::any::Any;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
use async_trait::async_trait;
use wx_rust_common::config::TicketType;
use wx_rust_common::error::WxErrorException;
use wx_rust_common::util::http::{SimpleGetRequestExecutor, SimplePostRequestExecutor};
use wx_rust_miniapp::api::WxMaService;
use wx_rust_miniapp::bean::WxMaJscode2SessionResult;
use wx_rust_miniapp::config::WxMaConfig;
use wx_rust_miniapp::config::WxMaHostConfig;
use crate::api::WxOpenService;
use crate::api::r#impl::{
WxOpenMaAuthAndIcpServiceImpl, WxOpenMaAuthServiceImpl, WxOpenMaBasicServiceImpl,
WxOpenMaEmbeddedServiceImpl, WxOpenMaIcpServiceImpl, WxOpenMaPrivacyServiceImpl,
WxOpenMaShoppingOrdersServiceImpl,
};
use crate::config::WxOpenConfigStorage;
pub fn downcast_ma_service(any: Arc<dyn Any + Send + Sync>) -> Option<Arc<dyn WxMaService>> {
any.downcast::<WxOpenMaService>()
.ok()
.map(|svc| svc as Arc<dyn WxMaService>)
}
pub struct WxOpenMaService {
wx_open_service: Weak<dyn WxOpenService>,
app_id: String,
config: Arc<OpenMaConfigBridge>,
http_client: reqwest::Client,
basic_service: Arc<WxOpenMaBasicServiceImpl>,
auth_service: Arc<WxOpenMaAuthServiceImpl>,
icp_service: Arc<WxOpenMaIcpServiceImpl>,
privacy_service: Arc<WxOpenMaPrivacyServiceImpl>,
shopping_orders_service: Arc<WxOpenMaShoppingOrdersServiceImpl>,
embedded_service: Arc<WxOpenMaEmbeddedServiceImpl>,
auth_and_icp_service: Arc<WxOpenMaAuthAndIcpServiceImpl>,
}
impl WxOpenMaService {
pub fn new(wx_open_service: Arc<dyn WxOpenService>, app_id: String) -> Self {
let config = wx_open_service.wx_open_config_storage();
let http_client = wx_open_service.http_client().clone();
let strong = wx_open_service.clone();
let wx_open_service = Arc::downgrade(&wx_open_service);
let bridge_config = Arc::new(OpenMaConfigBridge::new(config, &app_id));
let basic_service = Arc::new(WxOpenMaBasicServiceImpl::new(
strong.clone(),
app_id.clone(),
));
let auth_service = Arc::new(WxOpenMaAuthServiceImpl::new(strong.clone(), app_id.clone()));
let icp_service = Arc::new(WxOpenMaIcpServiceImpl::new(strong.clone(), app_id.clone()));
let privacy_service = Arc::new(WxOpenMaPrivacyServiceImpl::new(
strong.clone(),
app_id.clone(),
));
let shopping_orders_service = Arc::new(WxOpenMaShoppingOrdersServiceImpl::new(
strong.clone(),
app_id.clone(),
));
let embedded_service = Arc::new(WxOpenMaEmbeddedServiceImpl::new(
strong.clone(),
app_id.clone(),
));
let auth_and_icp_service =
Arc::new(WxOpenMaAuthAndIcpServiceImpl::new(strong, app_id.clone()));
Self {
wx_open_service,
app_id,
config: bridge_config,
http_client,
basic_service,
auth_service,
icp_service,
privacy_service,
shopping_orders_service,
embedded_service,
auth_and_icp_service,
}
}
pub fn app_id(&self) -> &str {
&self.app_id
}
pub fn get_basic_service(&self) -> Arc<WxOpenMaBasicServiceImpl> {
self.basic_service.clone()
}
pub fn get_auth_service(&self) -> Arc<WxOpenMaAuthServiceImpl> {
self.auth_service.clone()
}
pub fn get_icp_service(&self) -> Arc<WxOpenMaIcpServiceImpl> {
self.icp_service.clone()
}
pub fn get_privacy_service(&self) -> Arc<WxOpenMaPrivacyServiceImpl> {
self.privacy_service.clone()
}
pub fn get_shopping_orders_service(&self) -> Arc<WxOpenMaShoppingOrdersServiceImpl> {
self.shopping_orders_service.clone()
}
pub fn get_embedded_service(&self) -> Arc<WxOpenMaEmbeddedServiceImpl> {
self.embedded_service.clone()
}
pub fn get_auth_and_icp_service(&self) -> Arc<WxOpenMaAuthAndIcpServiceImpl> {
self.auth_and_icp_service.clone()
}
fn svc(&self) -> Result<Arc<dyn WxOpenService>, WxErrorException> {
self.wx_open_service
.upgrade()
.ok_or_else(|| WxErrorException::from_code(-99, "门面服务已被释放"))
}
}
#[async_trait]
impl WxMaService for WxOpenMaService {
fn wx_ma_config(&self) -> Arc<dyn WxMaConfig> {
self.config.clone()
}
fn http_client(&self) -> &reqwest::Client {
&self.http_client
}
async fn get_access_token_with_force(
&self,
force_refresh: bool,
) -> Result<String, WxErrorException> {
let svc = self.svc()?;
let component = svc.wx_open_component_service().ok_or_else(|| {
WxErrorException::from_code(
-99,
"组件子服务未装配(getWxOpenComponentService 返回 null)",
)
})?;
component
.get_authorizer_access_token(&self.app_id, force_refresh)
.await
}
async fn js_code_to_session(
&self,
js_code: &str,
) -> Result<WxMaJscode2SessionResult, WxErrorException> {
let svc = self.svc()?;
let component = svc.wx_open_component_service().ok_or_else(|| {
WxErrorException::from_code(
-99,
"组件子服务未装配(getWxOpenComponentService 返回 null)",
)
})?;
let value = component
.miniapp_jscode2_session(&self.app_id, js_code)
.await?;
serde_json::from_value(value).map_err(|e| WxErrorException::Serde(e.to_string()))
}
async fn get(&self, url: &str, query_param: &str) -> Result<String, WxErrorException> {
let executor = SimpleGetRequestExecutor::new(self.http_client().clone());
wx_rust_miniapp::api::r#impl::base_wx_ma_service_impl::execute_with_retry(
self,
&executor,
url,
query_param.to_string(),
)
.await
}
async fn post(&self, url: &str, post_data: &str) -> Result<String, WxErrorException> {
let executor = SimplePostRequestExecutor::new(self.http_client().clone());
wx_rust_miniapp::api::r#impl::base_wx_ma_service_impl::execute_with_retry(
self,
&executor,
url,
post_data.to_string(),
)
.await
}
}
struct OpenMaConfigBridge {
open_config: Arc<dyn WxOpenConfigStorage>,
app_id: String,
use_stable_access_token: AtomicBool,
component_token: Option<String>,
component_aes_key: Option<String>,
http_proxy_host: Option<String>,
http_proxy_port: Option<u16>,
host_config: Mutex<WxMaHostConfig>,
}
impl OpenMaConfigBridge {
fn new(open_config: Arc<dyn WxOpenConfigStorage>, app_id: &str) -> Self {
let mut host_config = WxMaHostConfig::new();
if let Some(h) = open_config.wx_open_host_config() {
if !h.api_host.is_empty() {
host_config.api_host = h.api_host;
}
if !h.mp_host.is_empty() {
host_config.mp_host = h.mp_host;
}
if !h.open_host.is_empty() {
host_config.open_host = h.open_host;
}
}
Self {
component_token: open_config.component_token(),
component_aes_key: open_config.component_aes_key(),
http_proxy_host: open_config.http_proxy_host(),
http_proxy_port: match open_config.http_proxy_port() {
p if p > 0 => Some(p as u16),
_ => None,
},
host_config: Mutex::new(host_config),
open_config,
app_id: app_id.to_string(),
use_stable_access_token: AtomicBool::new(false),
}
}
}
impl wx_rust_common::config::WxConfigStorage for OpenMaConfigBridge {
fn app_id(&self) -> &str {
&self.app_id
}
fn secret(&self) -> &str {
""
}
fn access_token(&self) -> Option<String> {
self.open_config.authorizer_access_token(&self.app_id)
}
fn is_access_token_expired(&self) -> bool {
self.open_config
.is_authorizer_access_token_expired(&self.app_id)
}
fn expire_access_token(&self) {
self.open_config
.expire_authorizer_access_token(&self.app_id);
}
fn update_access_token(&self, access_token: &str, expires_in_seconds: i32) {
self.open_config.update_authorizer_access_token_with_expiry(
&self.app_id,
access_token,
expires_in_seconds,
);
}
fn access_token_lock(&self) -> Arc<tokio::sync::Mutex<()>> {
self.open_config
.lock_by_key(&format!("{}:accessTokenLock", self.app_id))
}
fn is_stable_access_token(&self) -> bool {
self.use_stable_access_token.load(Ordering::Relaxed)
}
fn auto_refresh_token(&self) -> bool {
self.open_config.auto_refresh_token()
}
fn ticket(&self, ticket_type: TicketType) -> Option<String> {
match ticket_type {
TicketType::Jsapi => self.open_config.jsapi_ticket(&self.app_id),
TicketType::WxCard => self.open_config.card_api_ticket(&self.app_id),
TicketType::Sdk => None,
}
}
fn is_ticket_expired(&self, ticket_type: TicketType) -> bool {
match ticket_type {
TicketType::Jsapi => self.open_config.is_jsapi_ticket_expired(&self.app_id),
TicketType::WxCard => self.open_config.is_card_api_ticket_expired(&self.app_id),
TicketType::Sdk => false,
}
}
fn update_ticket(&self, ticket_type: TicketType, ticket: &str, expires_in_seconds: i32) {
match ticket_type {
TicketType::Jsapi => {
self.open_config
.update_jsapi_ticket(&self.app_id, ticket, expires_in_seconds)
}
TicketType::WxCard => {
self.open_config
.update_card_api_ticket(&self.app_id, ticket, expires_in_seconds)
}
TicketType::Sdk => {}
}
}
fn ticket_lock(&self, ticket_type: TicketType) -> Arc<tokio::sync::Mutex<()>> {
match ticket_type {
TicketType::Jsapi => self
.open_config
.lock_by_key(&format!("{}:jsapiTicketLock", self.app_id)),
TicketType::WxCard => self
.open_config
.lock_by_key(&format!("{}:cardApiTicketLock", self.app_id)),
TicketType::Sdk => Arc::new(tokio::sync::Mutex::new(())),
}
}
fn expire_ticket(&self, ticket_type: TicketType) {
match ticket_type {
TicketType::Jsapi => self.open_config.expire_jsapi_ticket(&self.app_id),
TicketType::WxCard => self.open_config.expire_card_api_ticket(&self.app_id),
TicketType::Sdk => {}
}
}
fn http_proxy_host(&self) -> Option<&str> {
self.http_proxy_host.as_deref()
}
fn http_proxy_port(&self) -> Option<u16> {
self.http_proxy_port
}
fn tmp_dir(&self) -> Option<&str> {
None
}
}
impl WxMaConfig for OpenMaConfigBridge {
fn use_stable_access_token(&self, use_stable_access_token: bool) {
self.use_stable_access_token
.store(use_stable_access_token, Ordering::Relaxed);
}
fn token(&self) -> Option<&str> {
self.component_token.as_deref()
}
fn aes_key(&self) -> Option<&str> {
self.component_aes_key.as_deref()
}
fn original_id(&self) -> Option<&str> {
None
}
fn cloud_env(&self) -> Option<&str> {
None
}
fn msg_data_format(&self) -> Option<&str> {
None
}
fn retry_sleep_millis(&self) -> i32 {
self.open_config.retry_sleep_millis()
}
fn max_retry_times(&self) -> i32 {
self.open_config.max_retry_times()
}
fn host_config(&self) -> WxMaHostConfig {
self.host_config.lock().unwrap().clone()
}
fn set_host_config(&self, host_config: WxMaHostConfig) {
*self.host_config.lock().unwrap() = host_config;
}
fn api_host_url(&self) -> Option<String> {
self.open_config.wx_open_host_config().map(|h| h.api_host)
}
fn set_api_host_url(&self, _api_host_url: &str) {
}
fn access_token_url(&self) -> Option<String> {
None
}
fn set_access_token_url(&self, _access_token_url: &str) {}
fn api_signature_rsa_private_key(&self) -> Option<String> {
self.open_config.component_api_signature_rsa_private_key()
}
fn api_signature_aes_key(&self) -> Option<String> {
self.open_config.component_api_signature_aes_key()
}
fn api_signature_aes_key_sn(&self) -> Option<String> {
self.open_config.component_api_signature_aes_key_sn()
}
fn api_signature_rsa_private_key_sn(&self) -> Option<String> {
self.open_config
.component_api_signature_rsa_private_key_sn()
}
fn wechat_mp_appid(&self) -> Option<String> {
self.open_config.component_app_id()
}
}