1use std::any::Any;
48use std::collections::HashMap;
49use std::sync::{Arc, Mutex, Weak};
50use std::time::Duration;
51
52use async_trait::async_trait;
53use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
54
55use wx_rust_common::api::wx_consts::app_id_type;
56use wx_rust_common::bean::oauth2::WxOAuth2AccessToken;
57use wx_rust_common::bean::result::WxMinishopImageUploadResult;
58use wx_rust_common::enums::WxType;
59use wx_rust_common::error::WxErrorException;
60use wx_rust_common::util::http::{
61 RequestExecutor, SimpleGetRequestExecutor, SimplePostRequestExecutor,
62};
63use wx_rust_miniapp::api::WxMaService;
64use wx_rust_mp::api::WxMpService;
65
66use crate::api::r#impl::base_wx_open_service_impl;
67use crate::api::r#impl::{WxOpenMaService, WxOpenMinishopServiceImpl, WxOpenMpService};
68use crate::api::{WxOpenComponentService, WxOpenService};
69use crate::bean::message::WxOpenXmlMessage;
70use crate::bean::{
71 GetShareCloudBaseEnvResponse, GetTcbEnvListResponse, LimitDiscountGoods, LimitDiscountSku,
72 MinishopBrand, MinishopBrandList, MinishopBusiLicense, MinishopCategories, MinishopCategory,
73 MinishopDeliveryTemplate, MinishopDeliveryTemplateResult, MinishopIdcardInfo, MinishopNameInfo,
74 MinishopOrganizationCodeInfo, MinishopReturnInfo, MinishopShopCat, MinishopShopCatList,
75 MinishopSuperAdministratorInfo, ShareCloudBaseEnvRequest, ShareCloudBaseEnvResponse,
76 ValuationType, WxMinishopAddGoodsSpuResult, WxMinishopCoupon, WxMinishopCouponStock,
77 WxMinishopSku, WxMinishopSpu, WxOpenAuthorizerAccessToken, WxOpenAuthorizerInfoResult,
78 WxOpenAuthorizerListResult, WxOpenAuthorizerOptionResult, WxOpenCreateResult, WxOpenGetResult,
79 WxOpenHaveResult, WxOpenMaApplyOrderPathInfo, WxOpenMaCodeTemplate,
80 WxOpenMaDomainConfirmFileResult, WxOpenMaDomainResult, WxOpenMaWebDomainResult,
81 WxOpenQueryAuthResult, WxOpenRegisterBetaWeappResult, WxOpenRegisterPersonalWeappResult,
82 WxOpenResult,
83};
84use crate::config::WxOpenConfigStorage;
85use crate::constant::wx_open_constants::ACCESS_TOKEN_KEY_COMPONENT;
86use crate::enums::url_core::{
87 api_authorizer_token_url, api_create_preauthcode_url, api_start_push_ticket_url,
88 component_login_page_url, component_mobile_login_page_url,
89};
90
91mod urls {
97 use crate::config::{API_DEFAULT_HOST_URL, WxOpenConfigStorage};
98
99 pub(super) fn api(config: &dyn WxOpenConfigStorage, path: &str) -> String {
102 let host = config
103 .wx_open_host_config()
104 .map(|h| h.api_host)
105 .unwrap_or_else(|| API_DEFAULT_HOST_URL.to_string());
106 format!("{host}{path}")
107 }
108
109 pub(super) fn query_auth(config: &dyn WxOpenConfigStorage) -> String {
110 api(config, "/cgi-bin/component/api_query_auth")
111 }
112 pub(super) fn get_authorizer_info(config: &dyn WxOpenConfigStorage) -> String {
113 api(config, "/cgi-bin/component/api_get_authorizer_info")
114 }
115 pub(super) fn get_authorizer_option(config: &dyn WxOpenConfigStorage) -> String {
116 api(config, "/cgi-bin/component/get_authorizer_option")
117 }
118 pub(super) fn set_authorizer_option(config: &dyn WxOpenConfigStorage) -> String {
119 api(config, "/cgi-bin/component/set_authorizer_option")
120 }
121 pub(super) fn get_authorizer_list(config: &dyn WxOpenConfigStorage) -> String {
122 api(config, "/cgi-bin/component/api_get_authorizer_list")
123 }
124 pub(super) fn oauth2_access_token(
127 config: &dyn WxOpenConfigStorage,
128 app_id: &str,
129 code: &str,
130 ) -> String {
131 format!(
132 "{}/sns/oauth2/component/access_token?appid={}&code={}&grant_type=authorization_code&component_appid={}",
133 host(config),
134 app_id,
135 code,
136 component_app_id(config),
137 )
138 }
139 pub(super) fn oauth2_refresh_token(
141 config: &dyn WxOpenConfigStorage,
142 app_id: &str,
143 refresh_token: &str,
144 ) -> String {
145 format!(
146 "{}/sns/oauth2/component/refresh_token?appid={}&grant_type=refresh_token&refresh_token={}&component_appid={}",
147 host(config),
148 app_id,
149 refresh_token,
150 component_app_id(config),
151 )
152 }
153 pub(super) fn miniapp_jscode2_session(
155 config: &dyn WxOpenConfigStorage,
156 app_id: &str,
157 js_code: &str,
158 ) -> String {
159 format!(
160 "{}/sns/component/jscode2session?appid={}&js_code={}&grant_type=authorization_code&component_appid={}",
161 host(config),
162 app_id,
163 js_code,
164 component_app_id(config),
165 )
166 }
167
168 pub(super) fn have_open(config: &dyn WxOpenConfigStorage) -> String {
169 api(config, "/cgi-bin/open/have")
170 }
171 pub(super) fn create_open(config: &dyn WxOpenConfigStorage) -> String {
173 api(config, "/cgi-bin/open/create")
174 }
175 pub(super) fn bind_open(config: &dyn WxOpenConfigStorage) -> String {
177 api(config, "/cgi-bin/open/bind")
178 }
179 pub(super) fn unbind_open(config: &dyn WxOpenConfigStorage) -> String {
181 api(config, "/cgi-bin/open/unbind")
182 }
183 pub(super) fn get_open(config: &dyn WxOpenConfigStorage) -> String {
185 api(config, "/cgi-bin/open/get")
186 }
187 pub(super) fn get_template_draft_list(config: &dyn WxOpenConfigStorage) -> String {
188 api(config, "/wxa/gettemplatedraftlist")
189 }
190 pub(super) fn get_template_list(config: &dyn WxOpenConfigStorage) -> String {
191 api(config, "/wxa/gettemplatelist")
192 }
193 pub(super) fn add_to_template(config: &dyn WxOpenConfigStorage) -> String {
194 api(config, "/wxa/addtotemplate")
195 }
196 pub(super) fn delete_template(config: &dyn WxOpenConfigStorage) -> String {
197 api(config, "/wxa/deletetemplate")
198 }
199 pub(super) fn fast_register_weapp(config: &dyn WxOpenConfigStorage) -> String {
200 api(config, "/cgi-bin/component/fastregisterweapp?action=create")
201 }
202 pub(super) fn fast_register_weapp_search(config: &dyn WxOpenConfigStorage) -> String {
203 api(config, "/cgi-bin/component/fastregisterweapp?action=search")
204 }
205 pub(super) fn fast_register_personal_weapp(config: &dyn WxOpenConfigStorage) -> String {
206 api(
207 config,
208 "/wxa/component/fastregisterpersonalweapp?action=create",
209 )
210 }
211 pub(super) fn fast_register_personal_weapp_search(config: &dyn WxOpenConfigStorage) -> String {
212 api(
213 config,
214 "/wxa/component/fastregisterpersonalweapp?action=query",
215 )
216 }
217 pub(super) fn fast_register_beta_weapp(config: &dyn WxOpenConfigStorage) -> String {
218 api(config, "/wxa/component/fastregisterbetaweapp")
219 }
220 pub(super) fn register_shop(config: &dyn WxOpenConfigStorage) -> String {
221 api(config, "/product/register/register_shop")
222 }
223 pub(super) fn check_shop_audit_status(config: &dyn WxOpenConfigStorage) -> String {
224 api(config, "/product/register/check_audit_status")
225 }
226 pub(super) fn submit_merchant_info(config: &dyn WxOpenConfigStorage) -> String {
227 api(config, "/product/register/submit_merchantinfo")
228 }
229 pub(super) fn submit_basic_info(config: &dyn WxOpenConfigStorage) -> String {
230 api(config, "/product/register/submit_basicinfo")
231 }
232 pub(super) fn upload_image(config: &dyn WxOpenConfigStorage) -> String {
235 api(config, "/product/img/upload")
236 }
237 pub(super) fn minishop_category_get(config: &dyn WxOpenConfigStorage) -> String {
238 api(config, "/product/category/get")
239 }
240 pub(super) fn minishop_brand_get(config: &dyn WxOpenConfigStorage) -> String {
241 api(config, "/product/brand/get")
242 }
243 pub(super) fn minishop_delivery_template_get(config: &dyn WxOpenConfigStorage) -> String {
244 api(config, "/product/delivery/get_freight_template")
245 }
246 pub(super) fn minishop_shopcategory_get(config: &dyn WxOpenConfigStorage) -> String {
247 api(config, "/product/store/get_shopcat")
248 }
249 pub(super) fn minishop_create_coupon(config: &dyn WxOpenConfigStorage) -> String {
250 api(config, "/product/coupon/create")
251 }
252 pub(super) fn minishop_get_coupon_list(config: &dyn WxOpenConfigStorage) -> String {
253 api(config, "/product/coupon/get_list")
254 }
255 pub(super) fn minishop_push_coupon(config: &dyn WxOpenConfigStorage) -> String {
256 api(config, "/product/coupon/push")
257 }
258 pub(super) fn minishop_update_coupon(config: &dyn WxOpenConfigStorage) -> String {
259 api(config, "/product/coupon/update")
260 }
261 pub(super) fn minishop_update_coupon_status(config: &dyn WxOpenConfigStorage) -> String {
262 api(config, "/product/coupon/update_status")
263 }
264 pub(super) fn minishop_get_delivery_company(config: &dyn WxOpenConfigStorage) -> String {
265 api(config, "/product/delivery/get_company_list")
266 }
267 pub(super) fn minishop_add_spu(config: &dyn WxOpenConfigStorage) -> String {
268 api(config, "/product/spu/add")
269 }
270 pub(super) fn minishop_del_spu(config: &dyn WxOpenConfigStorage) -> String {
271 api(config, "/product/spu/del")
272 }
273 pub(super) fn minishop_update_spu(config: &dyn WxOpenConfigStorage) -> String {
274 api(config, "/product/spu/update")
275 }
276 pub(super) fn minishop_listing_spu(config: &dyn WxOpenConfigStorage) -> String {
277 api(config, "/product/spu/listing")
278 }
279 pub(super) fn minishop_delisting_spu(config: &dyn WxOpenConfigStorage) -> String {
280 api(config, "/product/spu/delisting")
281 }
282 pub(super) fn minishop_add_sku(config: &dyn WxOpenConfigStorage) -> String {
283 api(config, "/product/sku/add")
284 }
285 pub(super) fn minishop_batch_add_sku(config: &dyn WxOpenConfigStorage) -> String {
286 api(config, "/product/sku/batch_add")
287 }
288 pub(super) fn minishop_del_sku(config: &dyn WxOpenConfigStorage) -> String {
289 api(config, "/product/sku/del")
290 }
291 pub(super) fn minishop_update_sku(config: &dyn WxOpenConfigStorage) -> String {
292 api(config, "/product/sku/update")
293 }
294 pub(super) fn minishop_update_sku_price(config: &dyn WxOpenConfigStorage) -> String {
295 api(config, "/product/sku/update_price")
296 }
297 pub(super) fn minishop_update_sku_stock(config: &dyn WxOpenConfigStorage) -> String {
298 api(config, "/product/stock/update")
299 }
300 pub(super) fn minishop_add_limit_discount(config: &dyn WxOpenConfigStorage) -> String {
303 api(config, "/product/limiteddiscount/add/")
304 }
305 pub(super) fn minishop_get_limit_discount(config: &dyn WxOpenConfigStorage) -> String {
306 api(config, "/product/limiteddiscount/get_list/")
307 }
308 pub(super) fn minishop_update_limit_discount_status(
309 config: &dyn WxOpenConfigStorage,
310 ) -> String {
311 api(config, "/product/limiteddiscount/update_status/")
312 }
313 pub(super) fn batch_get_env_id(config: &dyn WxOpenConfigStorage) -> String {
314 api(config, "/componenttcb/batchgetenvid")
315 }
316 pub(super) fn describe_envs(config: &dyn WxOpenConfigStorage) -> String {
317 api(config, "/componenttcb/describeenvs")
318 }
319 pub(super) fn modify_env(config: &dyn WxOpenConfigStorage) -> String {
320 api(config, "/tcb/modifyenv")
321 }
322 pub(super) fn batch_share_env(config: &dyn WxOpenConfigStorage) -> String {
323 api(config, "/componenttcb/batchshareenv")
324 }
325 pub(super) fn clear_quota(config: &dyn WxOpenConfigStorage) -> String {
326 api(config, "/cgi-bin/component/clear_quota/v2")
327 }
328 pub(super) fn modify_wxa_server_domain(config: &dyn WxOpenConfigStorage) -> String {
329 api(config, "/cgi-bin/component/modify_wxa_server_domain")
330 }
331 pub(super) fn get_domain_confirm_file(config: &dyn WxOpenConfigStorage) -> String {
332 api(config, "/cgi-bin/component/get_domain_confirmfile")
333 }
334 pub(super) fn modify_wxa_jump_domain(config: &dyn WxOpenConfigStorage) -> String {
335 api(config, "/cgi-bin/component/modify_wxa_jump_domain")
336 }
337 pub(super) fn apply_set_order_path_info(config: &dyn WxOpenConfigStorage) -> String {
338 api(config, "/wxa/security/applysetorderpathinfo")
339 }
340
341 fn host(config: &dyn WxOpenConfigStorage) -> String {
342 config
343 .wx_open_host_config()
344 .map(|h| h.api_host)
345 .unwrap_or_else(|| API_DEFAULT_HOST_URL.to_string())
346 }
347
348 fn component_app_id(config: &dyn WxOpenConfigStorage) -> String {
349 config.component_app_id().unwrap_or_default()
350 }
351}
352
353mod minishop_json {
358 use serde_json::{Value, json};
359
360 use crate::bean::{
361 LimitDiscountGoods, LimitDiscountSku, MinishopAddressInfo, MinishopBusiLicense,
362 MinishopIdcardInfo, MinishopNameInfo, MinishopOrganizationCodeInfo, MinishopPicFile,
363 MinishopReturnInfo, MinishopShopCat, MinishopSuperAdministratorInfo, WxMinishopCoupon,
364 WxMinishopCouponDiscountCondition, WxMinishopCouponDiscountInfo, WxMinishopCouponExtInfo,
365 WxMinishopCouponPromoteInfo, WxMinishopCouponReceiveInfo, WxMinishopCouponValidInfo,
366 WxMinishopGoodsSkuAttr, WxMinishopSku, WxMinishopSpu,
367 };
368
369 pub(super) fn pic_file(p: &MinishopPicFile) -> Value {
371 json!({ "media_id": p.media_id, "pay_media_id": p.pay_media_id })
372 }
373
374 pub(super) fn busi_license(b: &MinishopBusiLicense) -> Value {
376 let mut v = json!({
377 "license_type": b.license_type,
378 "pic_file": pic_file(&b.pic_file),
379 "registration_num": b.registration_num,
380 "merchant_name": b.merchant_name,
381 "legal_representative": b.legal_representative,
382 "start_date": b.start_date,
383 "end_date": b.end_date,
384 });
385 if !b.registered_addrs.is_empty() {
387 v["registered_addrs"] = json!(b.registered_addrs);
388 }
389 v
390 }
391
392 pub(super) fn organization_code_info(o: &MinishopOrganizationCodeInfo) -> Value {
394 json!({
395 "pic_file": pic_file(&o.pic_file),
396 "organization_code": o.organization_code,
397 "start_date": o.start_date,
398 "end_date": o.end_date,
399 })
400 }
401
402 pub(super) fn idcard_info(i: &MinishopIdcardInfo) -> Value {
404 json!({
405 "portrait_pic_file": pic_file(&i.portrait_pic_file),
406 "nation_pic_file": pic_file(&i.nation_pic_file),
407 "id_card_name": i.id_card_name,
408 "id_card_number": i.id_card_number,
409 "start_date": i.start_date,
410 "end_date": i.end_date,
411 })
412 }
413
414 pub(super) fn super_administrator_info(s: &MinishopSuperAdministratorInfo) -> Value {
416 json!({
417 "type": s.r#type,
418 "name": s.name,
419 "id_card_number": s.id_card_number,
420 "phone": s.phone,
421 "mail": s.mail,
422 })
423 }
424
425 pub(super) fn name_info(n: &MinishopNameInfo) -> Value {
427 json!({
428 "nickname": n.nick_name,
429 "abbr": n.abbr,
430 "introduction": n.introduction,
431 })
432 }
433
434 pub(super) fn address_info(a: &MinishopAddressInfo) -> Value {
436 json!({
437 "user_name": a.user_name,
438 "postal_code": a.postal_code,
439 "province_name": a.province,
440 "city_name": a.city_name,
441 "county_name": a.county_name,
442 "detail_info": a.detail_info,
443 "national_code": a.national_code,
444 "tel_number": a.tel_number,
445 })
446 }
447
448 pub(super) fn return_info(r: &MinishopReturnInfo) -> Value {
450 json!({
451 "address_info": address_info(&r.address_info),
452 "mail": r.email,
453 "company_address": address_info(&r.company_address),
454 })
455 }
456
457 pub(super) fn sku_attr(a: &WxMinishopGoodsSkuAttr) -> Value {
459 json!({ "attr_key": a.attr_key, "attr_value": a.attr_value })
460 }
461
462 pub(super) fn shop_cat(c: &MinishopShopCat) -> Value {
464 json!({ "cat_id": c.shop_cat_id, "level": c.cat_level })
465 }
466
467 pub(super) fn sku(s: &WxMinishopSku) -> Value {
469 let attrs: Vec<Value> = s.sku_attrs.iter().map(sku_attr).collect();
470 json!({
471 "product_id": s.product_id,
472 "out_product_id": s.out_product_id,
473 "out_sku_id": s.out_sku_id,
474 "thumb_img": s.thumb_img,
475 "sale_price": s.sale_price,
476 "market_price": s.market_price,
477 "stock_num": s.stock_num,
478 "sku_code": s.sku_code,
479 "barcode": s.bar_code,
480 "sku_attrs": attrs,
481 })
482 }
483
484 pub(super) fn spu(spu: &WxMinishopSpu) -> Value {
486 let cats: Vec<Value> = spu.shop_cats.iter().map(shop_cat).collect();
487 let attrs: Vec<Value> = spu.attrs.iter().map(sku_attr).collect();
488 let skus: Vec<Value> = spu.skus.iter().map(sku).collect();
489 json!({
490 "out_product_id": spu.out_product_id,
491 "title": spu.title,
492 "sub_title": spu.sub_title,
493 "head_img": spu.head_imgs,
494 "desc_info": { "imgs": spu.desc_info_imgs },
495 "brand_id": spu.brand_id,
496 "cats": cats,
497 "attrs": attrs,
498 "model": spu.model,
499 "express_info": { "template_id": spu.express_template_id },
500 "skus": skus,
501 })
502 }
503
504 pub(super) fn coupon_discount_condition(c: &WxMinishopCouponDiscountCondition) -> Value {
506 json!({
507 "product_cnt": c.product_cnt,
508 "product_ids": c.product_ids,
509 "product_price": c.product_price,
510 })
511 }
512
513 pub(super) fn coupon_discount_info(d: &WxMinishopCouponDiscountInfo) -> Value {
515 json!({
516 "discount_condition": coupon_discount_condition(&d.discount_condition),
517 "discount_fee": d.discount_fee,
518 "discount_num": d.discount_num,
519 })
520 }
521
522 pub(super) fn coupon_ext_info(e: &WxMinishopCouponExtInfo) -> Value {
524 json!({
525 "jump_product_id": e.jump_product_id,
526 "notes": e.notes,
527 "valid_time": e.valid_time,
528 "invalid_time": e.invalid_time,
529 })
530 }
531
532 pub(super) fn coupon_promote_info(p: &WxMinishopCouponPromoteInfo) -> Value {
534 json!({
535 "customize_channel": p.customize_channel,
536 "promote_type": p.promotion_type,
537 })
538 }
539
540 pub(super) fn coupon_receive_info(r: &WxMinishopCouponReceiveInfo) -> Value {
542 json!({
543 "start_time": r.start_time,
544 "end_time": r.end_time,
545 "limit_num_one_person": r.limit_num_one_person,
546 "total_num": r.total_num,
547 })
548 }
549
550 pub(super) fn coupon_valid_info(v: &WxMinishopCouponValidInfo) -> Value {
552 json!({
553 "start_time": v.start_time,
554 "end_time": v.end_time,
555 "valid_day_num": v.valid_day_num,
556 "valid_type": v.valid_type,
557 })
558 }
559
560 pub(super) fn coupon(c: &WxMinishopCoupon) -> Value {
562 let mut v = json!({
563 "type": c.r#type,
564 "name": c.name,
565 });
566 if c.coupon_id >= 0 {
568 v["coupon_id"] = json!(c.coupon_id);
569 }
570 if c.status >= 0 {
571 v["status"] = json!(c.status);
572 }
573 v["discount_info"] = coupon_discount_info(&c.discount_info);
574 v["ext_info"] = coupon_ext_info(&c.ext_info);
575 v["promote_info"] = coupon_promote_info(&c.promote_info);
576 v["receive_info"] = coupon_receive_info(&c.receive_info);
577 v["valid_info"] = coupon_valid_info(&c.valid_info);
578 v
579 }
580
581 pub(super) fn limit_discount_sku(s: &LimitDiscountSku) -> Value {
585 let sale_price_cents = s
586 .sale_price
587 .parse::<f64>()
588 .map(|p| (p * 100.0).round() as i64)
589 .unwrap_or(0);
590 json!({
591 "sku_id": s.sku_id,
592 "sale_price": sale_price_cents,
593 "sale_stock": s.sale_stock,
594 })
595 }
596
597 pub(super) fn limit_discount_goods(g: &LimitDiscountGoods) -> Value {
599 let mut v = json!({
600 "product_id": g.product_id,
601 "start_time": g.start_time,
602 "end_time": g.end_time,
603 });
604 if g.task_id != 0 {
605 v["task_id"] = json!(g.task_id);
606 }
607 if g.status != 0 {
608 v["status"] = json!(g.status);
609 }
610 let skus: Vec<Value> = g
611 .limit_discount_sku_list
612 .iter()
613 .map(limit_discount_sku)
614 .collect();
615 v["limited_discount_sku_list"] = json!(skus);
616 v
617 }
618}
619
620pub struct WxOpenComponentServiceImpl {
622 wx_open_service: Weak<dyn WxOpenService>,
623 mp_services: Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>,
628 ma_services: Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>,
630 fast_ma_services: Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>,
634 minishop_services: Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>,
637}
638
639impl WxOpenComponentServiceImpl {
640 pub fn new(wx_open_service: Weak<dyn WxOpenService>) -> Self {
645 Self {
646 wx_open_service,
647 mp_services: Mutex::new(HashMap::new()),
648 ma_services: Mutex::new(HashMap::new()),
649 fast_ma_services: Mutex::new(HashMap::new()),
650 minishop_services: Mutex::new(HashMap::new()),
651 }
652 }
653
654 fn svc(&self) -> Result<Arc<dyn WxOpenService>, WxErrorException> {
656 self.wx_open_service
657 .upgrade()
658 .ok_or_else(|| WxErrorException::from_code(-99, "门面服务已被释放"))
659 }
660
661 async fn bare_post(&self, uri: &str, post_data: &str) -> Result<String, WxErrorException> {
664 let svc = self.svc()?;
665 let executor = SimplePostRequestExecutor::new(svc.http_client().clone());
666 executor
667 .execute(uri, post_data.to_string(), WxType::Open)
668 .await
669 }
670
671 async fn open_account_service_post(
683 &self,
684 app_id: &str,
685 app_id_type: &str,
686 request_url: &str,
687 param: &serde_json::Value,
688 ) -> Result<String, WxErrorException> {
689 match app_id_type {
690 app_id_type::MP_TYPE => {
691 let any = self.get_wx_mp_service_by_appid(app_id).ok_or_else(|| {
692 WxErrorException::from_code(-99, "getWxMpServiceByAppid 返回 None")
693 })?;
694 let mp = any.downcast::<WxOpenMpService>().map_err(|_| {
695 WxErrorException::from_code(-99, "代 mp 服务 downcast 失败(缓存类型不匹配)")
696 })?;
697 let mp: Arc<dyn WxMpService> = mp;
698 mp.post(request_url, ¶m.to_string()).await
699 }
700 app_id_type::MINI_TYPE => {
701 let any = self.get_wx_ma_service_by_appid(app_id).ok_or_else(|| {
702 WxErrorException::from_code(-99, "getWxMaServiceByAppid 返回 None")
703 })?;
704 let ma = any.downcast::<WxOpenMaService>().map_err(|_| {
705 WxErrorException::from_code(-99, "代 ma 服务 downcast 失败(缓存类型不匹配)")
706 })?;
707 let ma: Arc<dyn WxMaService> = ma;
708 ma.post(request_url, ¶m.to_string()).await
709 }
710 _ => Err(WxErrorException::from_code(-99, "appIdType类型异常")),
711 }
712 }
713
714 async fn create_pre_auth_url(
723 &self,
724 redirect_uri: &str,
725 auth_type: Option<&str>,
726 biz_appid: Option<&str>,
727 is_mobile: bool,
728 ) -> Result<String, WxErrorException> {
729 let svc = self.svc()?;
730 let config = svc.wx_open_config_storage();
731 let component_app_id = config.component_app_id().unwrap_or_default().to_string();
732 let pre_auth_code = self.get_pre_auth_code().await?;
733
734 let encoded_redirect = utf8_percent_encode(redirect_uri, NON_ALPHANUMERIC).to_string();
736 let mut pre_auth_url_str = if is_mobile {
737 component_mobile_login_page_url(&component_app_id, &pre_auth_code, &encoded_redirect)
738 } else {
739 component_login_page_url(&component_app_id, &pre_auth_code, &encoded_redirect)
740 };
741 if let Some(auth_type) = auth_type {
743 if !auth_type.is_empty() {
744 pre_auth_url_str =
745 pre_auth_url_str.replace("&auth_type=xxx", &format!("&auth_type={auth_type}"));
746 } else {
747 pre_auth_url_str = pre_auth_url_str.replace("&auth_type=xxx", "");
748 }
749 } else {
750 pre_auth_url_str = pre_auth_url_str.replace("&auth_type=xxx", "");
751 }
752 if let Some(biz_appid) = biz_appid {
753 if !biz_appid.is_empty() {
754 pre_auth_url_str =
755 pre_auth_url_str.replace("&biz_appid=xxx", &format!("&biz_appid={biz_appid}"));
756 } else {
757 pre_auth_url_str = pre_auth_url_str.replace("&biz_appid=xxx", "");
758 }
759 } else {
760 pre_auth_url_str = pre_auth_url_str.replace("&biz_appid=xxx", "");
761 }
762 Ok(pre_auth_url_str)
763 }
764
765 fn json_i64(v: &serde_json::Value, key: &str) -> i64 {
767 v.get(key).and_then(|x| x.as_i64()).unwrap_or(0)
768 }
769
770 fn json_str(v: &serde_json::Value, key: &str) -> String {
771 v.get(key)
772 .and_then(|x| x.as_str())
773 .unwrap_or_default()
774 .to_string()
775 }
776
777 fn normalize_errcode(json: &str) -> Result<String, WxErrorException> {
785 let mut value: serde_json::Value =
786 serde_json::from_str(json).map_err(|e| WxErrorException::Serde(e.to_string()))?;
787 if let Some(errcode) = value.get("errcode") {
788 if errcode.is_number() {
789 value["errcode"] = serde_json::Value::String(errcode.to_string());
790 }
791 }
792 serde_json::to_string(&value).map_err(|e| WxErrorException::Serde(e.to_string()))
793 }
794}
795
796#[async_trait]
797impl WxOpenComponentService for WxOpenComponentServiceImpl {
798 fn wx_open_service(&self) -> Option<Arc<dyn WxOpenService>> {
799 self.wx_open_service.upgrade()
800 }
801
802 fn get_wx_mp_service_by_appid(&self, app_id: &str) -> Option<Arc<dyn Any + Send + Sync>> {
812 if let Some(svc) = self.mp_services.lock().unwrap().get(app_id) {
814 return Some(svc.clone());
815 }
816 let mut map = self.mp_services.lock().unwrap();
818 if let Some(svc) = map.get(app_id) {
819 return Some(svc.clone());
820 }
821 let open_svc = self.svc().ok()?;
822 let svc: Arc<dyn Any + Send + Sync> =
826 WxOpenMpService::new_arc(open_svc, app_id.to_string());
827 map.insert(app_id.to_string(), svc.clone());
828 Some(svc)
829 }
830
831 fn get_wx_ma_service_by_appid(&self, app_id: &str) -> Option<Arc<dyn Any + Send + Sync>> {
838 if let Some(svc) = self.ma_services.lock().unwrap().get(app_id) {
839 return Some(svc.clone());
840 }
841 let mut map = self.ma_services.lock().unwrap();
842 if let Some(svc) = map.get(app_id) {
843 return Some(svc.clone());
844 }
845 let open_svc = self.svc().ok()?;
846 let svc: Arc<dyn Any + Send + Sync> =
847 Arc::new(WxOpenMaService::new(open_svc, app_id.to_string()));
848 map.insert(app_id.to_string(), svc.clone());
849 Some(svc)
850 }
851
852 fn get_wx_fast_ma_service_by_appid(&self, app_id: &str) -> Option<Arc<dyn Any + Send + Sync>> {
862 if let Some(svc) = self.fast_ma_services.lock().unwrap().get(app_id) {
863 return Some(svc.clone());
864 }
865 let mut map = self.fast_ma_services.lock().unwrap();
866 if let Some(svc) = map.get(app_id) {
867 return Some(svc.clone());
868 }
869 let open_svc = self.svc().ok()?;
870 let svc: Arc<dyn Any + Send + Sync> =
871 Arc::new(WxOpenMaService::new(open_svc, app_id.to_string()));
872 map.insert(app_id.to_string(), svc.clone());
873 Some(svc)
874 }
875
876 fn get_wx_minishop_service_by_appid(&self, app_id: &str) -> Option<Arc<dyn Any + Send + Sync>> {
888 if let Some(svc) = self.minishop_services.lock().unwrap().get(app_id) {
889 return Some(svc.clone());
890 }
891 let mut map = self.minishop_services.lock().unwrap();
892 if let Some(svc) = map.get(app_id) {
893 return Some(svc.clone());
894 }
895 let open_svc = self.svc().ok()?;
896 let svc: Arc<dyn Any + Send + Sync> =
897 Arc::new(WxOpenMinishopServiceImpl::new(open_svc, app_id.to_string()));
898 map.insert(app_id.to_string(), svc.clone());
899 Some(svc)
900 }
901
902 async fn start_push_ticket(&self) -> Result<(), WxErrorException> {
903 let svc = self.svc()?;
904 let config = svc.wx_open_config_storage();
905 let body = serde_json::json!({
907 "component_appid": config.component_app_id().unwrap_or_default(),
908 "component_secret": config.component_app_secret().unwrap_or_default(),
909 });
910 self.post(
911 &api_start_push_ticket_url(config.as_ref()),
912 &body.to_string(),
913 )
914 .await?;
915 Ok(())
916 }
917
918 async fn get_component_access_token(
919 &self,
920 force_refresh: bool,
921 ) -> Result<String, WxErrorException> {
922 let svc = self.svc()?;
923 base_wx_open_service_impl::get_component_access_token_with_lock(svc.as_ref(), force_refresh)
924 .await
925 }
926
927 async fn post(&self, uri: &str, post_data: &str) -> Result<String, WxErrorException> {
928 let svc = self.svc()?;
929 let executor = SimplePostRequestExecutor::new(svc.http_client().clone());
930 base_wx_open_service_impl::execute_with_retry(
931 svc.as_ref(),
932 &executor,
933 uri,
934 post_data.to_string(),
935 ACCESS_TOKEN_KEY_COMPONENT,
936 )
937 .await
938 }
939
940 async fn post_with_key(
941 &self,
942 uri: &str,
943 post_data: &str,
944 access_token_key: &str,
945 ) -> Result<String, WxErrorException> {
946 let svc = self.svc()?;
947 let executor = SimplePostRequestExecutor::new(svc.http_client().clone());
948 base_wx_open_service_impl::execute_with_retry(
949 svc.as_ref(),
950 &executor,
951 uri,
952 post_data.to_string(),
953 access_token_key,
954 )
955 .await
956 }
957
958 async fn post_with_token(
959 &self,
960 uri: &str,
961 post_data: &str,
962 access_token_key: &str,
963 access_token: &str,
964 ) -> Result<String, WxErrorException> {
965 let uri_with_token = if uri.contains('?') {
969 format!("{uri}&{access_token_key}={access_token}")
970 } else {
971 format!("{uri}?{access_token_key}={access_token}")
972 };
973 self.bare_post(&uri_with_token, post_data).await
974 }
975
976 async fn get(&self, uri: &str) -> Result<String, WxErrorException> {
977 let svc = self.svc()?;
978 let executor = SimpleGetRequestExecutor::new(svc.http_client().clone());
979 base_wx_open_service_impl::execute_with_retry(
980 svc.as_ref(),
981 &executor,
982 uri,
983 String::new(),
984 ACCESS_TOKEN_KEY_COMPONENT,
985 )
986 .await
987 }
988
989 async fn get_with_key(
990 &self,
991 uri: &str,
992 access_token_key: &str,
993 ) -> Result<String, WxErrorException> {
994 let svc = self.svc()?;
995 let executor = SimpleGetRequestExecutor::new(svc.http_client().clone());
996 base_wx_open_service_impl::execute_with_retry(
997 svc.as_ref(),
998 &executor,
999 uri,
1000 String::new(),
1001 access_token_key,
1002 )
1003 .await
1004 }
1005
1006 async fn get_pre_auth_code(&self) -> Result<String, WxErrorException> {
1007 let svc = self.svc()?;
1008 let config = svc.wx_open_config_storage();
1009 let body = serde_json::json!({
1011 "component_appid": config.component_app_id().unwrap_or_default(),
1012 });
1013 let response = self
1014 .post(
1015 &api_create_preauthcode_url(config.as_ref()),
1016 &body.to_string(),
1017 )
1018 .await?;
1019 let json: serde_json::Value =
1020 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1021 json.get("pre_auth_code")
1022 .and_then(|v| v.as_str())
1023 .map(str::to_string)
1024 .ok_or_else(|| WxErrorException::from_code(-99, "pre_auth_code 字段缺失"))
1025 }
1026
1027 async fn get_pre_auth_url(&self, redirect_uri: &str) -> Result<String, WxErrorException> {
1028 self.create_pre_auth_url(redirect_uri, None, None, false)
1029 .await
1030 }
1031
1032 async fn get_pre_auth_url_with(
1033 &self,
1034 redirect_uri: &str,
1035 auth_type: Option<&str>,
1036 biz_appid: Option<&str>,
1037 ) -> Result<String, WxErrorException> {
1038 self.create_pre_auth_url(redirect_uri, auth_type, biz_appid, false)
1039 .await
1040 }
1041
1042 async fn get_mobile_pre_auth_url(
1043 &self,
1044 redirect_uri: &str,
1045 ) -> Result<String, WxErrorException> {
1046 self.create_pre_auth_url(redirect_uri, None, None, true)
1047 .await
1048 }
1049
1050 async fn get_mobile_pre_auth_url_with(
1051 &self,
1052 redirect_uri: &str,
1053 auth_type: Option<&str>,
1054 biz_appid: Option<&str>,
1055 ) -> Result<String, WxErrorException> {
1056 self.create_pre_auth_url(redirect_uri, auth_type, biz_appid, true)
1057 .await
1058 }
1059
1060 async fn route(&self, message: &WxOpenXmlMessage) -> Result<String, WxErrorException> {
1061 let info_type = message.info_type.as_deref().unwrap_or_default();
1063 if info_type.eq_ignore_ascii_case("component_verify_ticket") {
1065 let config = self.svc()?.wx_open_config_storage();
1066 if let Some(ticket) = &message.component_verify_ticket {
1067 config.set_component_verify_ticket(ticket);
1068 }
1069 return Ok("success".to_string());
1070 }
1071 if info_type.eq_ignore_ascii_case("authorized")
1073 || info_type.eq_ignore_ascii_case("updateauthorized")
1074 {
1075 let code = message.authorization_code.as_deref().unwrap_or_default();
1076 let query_auth = self.get_query_auth(code).await?;
1077 if query_auth
1078 .authorization_info
1079 .as_ref()
1080 .and_then(|i| i.authorizer_appid.as_ref())
1081 .is_none()
1082 {
1083 return Err(WxErrorException::from_code(-99, "getQueryAuth"));
1085 }
1086 return Ok("success".to_string());
1087 }
1088 if info_type.eq_ignore_ascii_case("notify_third_fasteregister") && message.status == Some(0)
1090 {
1091 let code = message.auth_code.as_deref().unwrap_or_default();
1092 let query_auth = self.get_query_auth(code).await?;
1093 if query_auth
1094 .authorization_info
1095 .as_ref()
1096 .and_then(|i| i.authorizer_appid.as_ref())
1097 .is_none()
1098 {
1099 return Err(WxErrorException::from_code(-99, "getQueryAuth"));
1100 }
1101 return Ok("success".to_string());
1102 }
1103 Ok(String::new())
1104 }
1105
1106 async fn get_query_auth(
1107 &self,
1108 authorization_code: &str,
1109 ) -> Result<WxOpenQueryAuthResult, WxErrorException> {
1110 let svc = self.svc()?;
1111 let config = svc.wx_open_config_storage();
1112 let body = serde_json::json!({
1113 "component_appid": config.component_app_id().unwrap_or_default(),
1114 "authorization_code": authorization_code,
1115 });
1116 let response = self
1117 .post(&urls::query_auth(config.as_ref()), &body.to_string())
1118 .await?;
1119 let query_auth: WxOpenQueryAuthResult =
1120 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1121 if let Some(info) = &query_auth.authorization_info {
1124 if let (Some(appid), Some(token)) =
1125 (&info.authorizer_appid, &info.authorizer_access_token)
1126 {
1127 config.update_authorizer_access_token_with_expiry(
1128 appid,
1129 token,
1130 info.expires_in.unwrap_or(0),
1131 );
1132 }
1133 if let (Some(appid), Some(refresh)) =
1134 (&info.authorizer_appid, &info.authorizer_refresh_token)
1135 {
1136 config.update_authorizer_refresh_token(appid, refresh);
1137 }
1138 }
1139 Ok(query_auth)
1140 }
1141
1142 async fn get_authorizer_info(
1143 &self,
1144 authorizer_appid: &str,
1145 ) -> Result<WxOpenAuthorizerInfoResult, WxErrorException> {
1146 let svc = self.svc()?;
1147 let config = svc.wx_open_config_storage();
1148 let body = serde_json::json!({
1149 "component_appid": config.component_app_id().unwrap_or_default(),
1150 "authorizer_appid": authorizer_appid,
1151 });
1152 let response = self
1153 .post(
1154 &urls::get_authorizer_info(config.as_ref()),
1155 &body.to_string(),
1156 )
1157 .await?;
1158 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1159 }
1160
1161 async fn get_authorizer_list(
1162 &self,
1163 begin: i32,
1164 len: i32,
1165 ) -> Result<WxOpenAuthorizerListResult, WxErrorException> {
1166 let begin = begin.max(0);
1168 let len = if len == 0 { 10 } else { len };
1169 let svc = self.svc()?;
1170 let config = svc.wx_open_config_storage();
1171 let body = serde_json::json!({
1172 "component_appid": config.component_app_id().unwrap_or_default(),
1173 "offset": begin,
1174 "count": len,
1175 });
1176 let response = self
1177 .post(
1178 &urls::get_authorizer_list(config.as_ref()),
1179 &body.to_string(),
1180 )
1181 .await?;
1182 let ret: WxOpenAuthorizerListResult =
1183 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1184 if let Some(list) = &ret.list {
1186 for data in list {
1187 if let (Some(appid), Some(refresh)) =
1188 (data.get("authorizer_appid"), data.get("refresh_token"))
1189 {
1190 config.update_authorizer_refresh_token(appid, refresh);
1191 }
1192 }
1193 }
1194 Ok(ret)
1195 }
1196
1197 async fn get_authorizer_option(
1198 &self,
1199 authorizer_appid: &str,
1200 option_name: &str,
1201 ) -> Result<WxOpenAuthorizerOptionResult, WxErrorException> {
1202 let authorizer_access_token = self
1205 .get_authorizer_access_token(authorizer_appid, false)
1206 .await?;
1207 let svc = self.svc()?;
1208 let config = svc.wx_open_config_storage();
1209 let body = serde_json::json!({
1210 "component_appid": config.component_app_id().unwrap_or_default(),
1211 "authorizer_appid": authorizer_appid,
1212 "option_name": option_name,
1213 });
1214 let response = self
1215 .post_with_token(
1216 &urls::get_authorizer_option(config.as_ref()),
1217 &body.to_string(),
1218 "access_token",
1219 &authorizer_access_token,
1220 )
1221 .await?;
1222 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1223 }
1224
1225 async fn set_authorizer_option(
1226 &self,
1227 authorizer_appid: &str,
1228 option_name: &str,
1229 option_value: &str,
1230 ) -> Result<(), WxErrorException> {
1231 let authorizer_access_token = self
1232 .get_authorizer_access_token(authorizer_appid, false)
1233 .await?;
1234 let svc = self.svc()?;
1235 let config = svc.wx_open_config_storage();
1236 let body = serde_json::json!({
1237 "component_appid": config.component_app_id().unwrap_or_default(),
1238 "authorizer_appid": authorizer_appid,
1239 "option_name": option_name,
1240 "option_value": option_value,
1241 });
1242 self.post_with_token(
1243 &urls::set_authorizer_option(config.as_ref()),
1244 &body.to_string(),
1245 "access_token",
1246 &authorizer_access_token,
1247 )
1248 .await?;
1249 Ok(())
1250 }
1251
1252 async fn get_authorizer_access_token(
1253 &self,
1254 app_id: &str,
1255 force_refresh: bool,
1256 ) -> Result<String, WxErrorException> {
1257 let svc = self.svc()?;
1258 let config = svc.wx_open_config_storage();
1259 if !force_refresh && !config.is_authorizer_access_token_expired(app_id) {
1260 return config
1261 .authorizer_access_token(app_id)
1262 .ok_or_else(|| WxErrorException::from_code(-99, "authorizer access token 为空"));
1263 }
1264 let lock = config.lock_by_key(app_id);
1267 let _guard = loop {
1268 if !force_refresh && !config.is_authorizer_access_token_expired(app_id) {
1269 return config.authorizer_access_token(app_id).ok_or_else(|| {
1270 WxErrorException::from_code(-99, "authorizer access token 为空")
1271 });
1272 }
1273 match lock.try_lock() {
1274 Ok(guard) => break guard,
1275 Err(_) => tokio::time::sleep(Duration::from_millis(100)).await,
1276 }
1277 };
1278
1279 let body = serde_json::json!({
1280 "component_appid": config.component_app_id().unwrap_or_default(),
1281 "authorizer_appid": app_id,
1282 "authorizer_refresh_token": config.authorizer_refresh_token(app_id).unwrap_or_default(),
1283 });
1284 let response = self
1285 .post(
1286 &api_authorizer_token_url(config.as_ref()),
1287 &body.to_string(),
1288 )
1289 .await?;
1290 let token: WxOpenAuthorizerAccessToken =
1291 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1292 config.update_authorizer_access_token(app_id, &token);
1293 config.update_authorizer_refresh_token(app_id, token.authorizer_refresh_token());
1294 config
1295 .authorizer_access_token(app_id)
1296 .ok_or_else(|| WxErrorException::from_code(-99, "authorizer access token 为空"))
1297 }
1298
1299 async fn oauth2_get_access_token(
1300 &self,
1301 app_id: &str,
1302 code: &str,
1303 ) -> Result<WxOAuth2AccessToken, WxErrorException> {
1304 let svc = self.svc()?;
1305 let config = svc.wx_open_config_storage();
1306 let url = urls::oauth2_access_token(config.as_ref(), app_id, code);
1307 let response = self.get(&url).await?;
1308 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1309 }
1310
1311 async fn oauth2_refresh_access_token(
1312 &self,
1313 app_id: &str,
1314 refresh_token: &str,
1315 ) -> Result<WxOAuth2AccessToken, WxErrorException> {
1316 let svc = self.svc()?;
1317 let config = svc.wx_open_config_storage();
1318 let url = urls::oauth2_refresh_token(config.as_ref(), app_id, refresh_token);
1319 let response = self.get(&url).await?;
1320 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1321 }
1322
1323 async fn miniapp_jscode2_session(
1324 &self,
1325 app_id: &str,
1326 js_code: &str,
1327 ) -> Result<serde_json::Value, WxErrorException> {
1328 let svc = self.svc()?;
1329 let config = svc.wx_open_config_storage();
1330 let url = urls::miniapp_jscode2_session(config.as_ref(), app_id, js_code);
1331 let response = self.get(&url).await?;
1332 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1333 }
1334
1335 async fn get_template_draft_list(
1336 &self,
1337 ) -> Result<Option<Vec<WxOpenMaCodeTemplate>>, WxErrorException> {
1338 let svc = self.svc()?;
1339 let config = svc.wx_open_config_storage();
1340 let response = self
1342 .get_with_key(
1343 &urls::get_template_draft_list(config.as_ref()),
1344 "access_token",
1345 )
1346 .await?;
1347 let json: serde_json::Value =
1348 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1349 if let Some(list) = json.get("draft_list") {
1350 serde_json::from_value(list.clone())
1351 .map(Some)
1352 .map_err(|e| WxErrorException::Serde(e.to_string()))
1353 } else {
1354 Ok(None)
1356 }
1357 }
1358
1359 async fn get_template_list(
1360 &self,
1361 ) -> Result<Option<Vec<WxOpenMaCodeTemplate>>, WxErrorException> {
1362 self.get_template_list_with_type(None).await
1363 }
1364
1365 async fn get_template_list_with_type(
1366 &self,
1367 template_type: Option<i32>,
1368 ) -> Result<Option<Vec<WxOpenMaCodeTemplate>>, WxErrorException> {
1369 let svc = self.svc()?;
1370 let config = svc.wx_open_config_storage();
1371 let url = match template_type {
1374 Some(t) => format!(
1375 "{}?template_type={t}",
1376 urls::get_template_list(config.as_ref())
1377 ),
1378 None => urls::get_template_list(config.as_ref()),
1379 };
1380 let response = self.get_with_key(&url, "access_token").await?;
1381 let json: serde_json::Value =
1382 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1383 if let Some(list) = json.get("template_list") {
1384 serde_json::from_value(list.clone())
1385 .map(Some)
1386 .map_err(|e| WxErrorException::Serde(e.to_string()))
1387 } else {
1388 Ok(None)
1389 }
1390 }
1391
1392 async fn add_to_template(&self, draft_id: i64) -> Result<(), WxErrorException> {
1393 let svc = self.svc()?;
1394 let config = svc.wx_open_config_storage();
1395 let body = serde_json::json!({ "draft_id": draft_id });
1396 self.post_with_key(
1397 &urls::add_to_template(config.as_ref()),
1398 &body.to_string(),
1399 "access_token",
1400 )
1401 .await?;
1402 Ok(())
1403 }
1404
1405 async fn add_to_template_with_type(
1406 &self,
1407 draft_id: i64,
1408 template_type: i32,
1409 ) -> Result<(), WxErrorException> {
1410 let svc = self.svc()?;
1411 let config = svc.wx_open_config_storage();
1412 let body = serde_json::json!({ "draft_id": draft_id, "template_type": template_type });
1413 self.post_with_key(
1414 &urls::add_to_template(config.as_ref()),
1415 &body.to_string(),
1416 "access_token",
1417 )
1418 .await?;
1419 Ok(())
1420 }
1421
1422 async fn delete_template(&self, template_id: i64) -> Result<(), WxErrorException> {
1423 let svc = self.svc()?;
1424 let config = svc.wx_open_config_storage();
1425 let body = serde_json::json!({ "template_id": template_id });
1426 self.post_with_key(
1427 &urls::delete_template(config.as_ref()),
1428 &body.to_string(),
1429 "access_token",
1430 )
1431 .await?;
1432 Ok(())
1433 }
1434
1435 async fn create_open_account(
1436 &self,
1437 app_id: &str,
1438 app_id_type: &str,
1439 ) -> Result<WxOpenCreateResult, WxErrorException> {
1440 let svc = self.svc()?;
1443 let config = svc.wx_open_config_storage();
1444 let param = serde_json::json!({ "appid": app_id });
1445 let json = self
1446 .open_account_service_post(
1447 app_id,
1448 app_id_type,
1449 &urls::create_open(config.as_ref()),
1450 ¶m,
1451 )
1452 .await?;
1453 let json = Self::normalize_errcode(&json)?;
1456 WxOpenCreateResult::from_json(&json).map_err(WxErrorException::Serde)
1457 }
1458
1459 async fn bind_open_account(
1460 &self,
1461 app_id: &str,
1462 app_id_type: &str,
1463 open_appid: &str,
1464 ) -> Result<bool, WxErrorException> {
1465 let svc = self.svc()?;
1467 let config = svc.wx_open_config_storage();
1468 let param = serde_json::json!({ "appid": app_id, "open_appid": open_appid });
1469 let json = self
1470 .open_account_service_post(
1471 app_id,
1472 app_id_type,
1473 &urls::bind_open(config.as_ref()),
1474 ¶m,
1475 )
1476 .await?;
1477 let json = Self::normalize_errcode(&json)?;
1481 Ok(WxOpenResult::from_json(&json)
1482 .map_err(WxErrorException::Serde)?
1483 .errcode
1484 .eq_ignore_ascii_case("0"))
1485 }
1486
1487 async fn unbind_open_account(
1488 &self,
1489 app_id: &str,
1490 app_id_type: &str,
1491 open_appid: &str,
1492 ) -> Result<bool, WxErrorException> {
1493 let svc = self.svc()?;
1494 let config = svc.wx_open_config_storage();
1495 let param = serde_json::json!({ "appid": app_id, "open_appid": open_appid });
1496 let json = self
1497 .open_account_service_post(
1498 app_id,
1499 app_id_type,
1500 &urls::unbind_open(config.as_ref()),
1501 ¶m,
1502 )
1503 .await?;
1504 let json = Self::normalize_errcode(&json)?;
1505 Ok(WxOpenResult::from_json(&json)
1506 .map_err(WxErrorException::Serde)?
1507 .errcode
1508 .eq_ignore_ascii_case("0"))
1509 }
1510
1511 async fn get_open_account(
1512 &self,
1513 app_id: &str,
1514 app_id_type: &str,
1515 ) -> Result<WxOpenGetResult, WxErrorException> {
1516 let svc = self.svc()?;
1517 let config = svc.wx_open_config_storage();
1518 let param = serde_json::json!({ "appid": app_id });
1519 let json = self
1520 .open_account_service_post(
1521 app_id,
1522 app_id_type,
1523 &urls::get_open(config.as_ref()),
1524 ¶m,
1525 )
1526 .await?;
1527 let json = Self::normalize_errcode(&json)?;
1530 WxOpenGetResult::from_json(&json).map_err(WxErrorException::Serde)
1531 }
1532
1533 async fn have_open(&self) -> Result<WxOpenHaveResult, WxErrorException> {
1534 let svc = self.svc()?;
1535 let config = svc.wx_open_config_storage();
1536 let response = self
1538 .get_with_key(&urls::have_open(config.as_ref()), "access_token")
1539 .await?;
1540 WxOpenHaveResult::from_json(&response).map_err(WxErrorException::Serde)
1541 }
1542
1543 async fn fast_register_weapp(
1544 &self,
1545 name: &str,
1546 code: &str,
1547 code_type: &str,
1548 legal_persona_wechat: &str,
1549 legal_persona_name: &str,
1550 component_phone: &str,
1551 ) -> Result<WxOpenResult, WxErrorException> {
1552 let svc = self.svc()?;
1553 let config = svc.wx_open_config_storage();
1554 let body = serde_json::json!({
1555 "name": name,
1556 "code": code,
1557 "code_type": code_type,
1558 "legal_persona_wechat": legal_persona_wechat,
1559 "legal_persona_name": legal_persona_name,
1560 "component_phone": component_phone,
1561 });
1562 let response = self
1564 .post_with_key(
1565 &urls::fast_register_weapp(config.as_ref()),
1566 &body.to_string(),
1567 "component_access_token",
1568 )
1569 .await?;
1570 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1571 }
1572
1573 async fn fast_register_weapp_search(
1574 &self,
1575 name: &str,
1576 legal_persona_wechat: &str,
1577 legal_persona_name: &str,
1578 ) -> Result<WxOpenResult, WxErrorException> {
1579 let svc = self.svc()?;
1580 let config = svc.wx_open_config_storage();
1581 let body = serde_json::json!({
1582 "name": name,
1583 "legal_persona_wechat": legal_persona_wechat,
1584 "legal_persona_name": legal_persona_name,
1585 });
1586 let response = self
1587 .post_with_key(
1588 &urls::fast_register_weapp_search(config.as_ref()),
1589 &body.to_string(),
1590 "component_access_token",
1591 )
1592 .await?;
1593 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1594 }
1595
1596 async fn fast_register_personal_weapp(
1597 &self,
1598 idname: &str,
1599 wxuser: &str,
1600 component_phone: &str,
1601 ) -> Result<WxOpenRegisterPersonalWeappResult, WxErrorException> {
1602 let svc = self.svc()?;
1603 let config = svc.wx_open_config_storage();
1604 let body = serde_json::json!({
1605 "idname": idname,
1606 "wxuser": wxuser,
1607 "component_phone": component_phone,
1608 });
1609 let response = self
1610 .post_with_key(
1611 &urls::fast_register_personal_weapp(config.as_ref()),
1612 &body.to_string(),
1613 "component_access_token",
1614 )
1615 .await?;
1616 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1617 }
1618
1619 async fn fast_register_personal_weapp_search(
1620 &self,
1621 taskid: &str,
1622 ) -> Result<WxOpenRegisterPersonalWeappResult, WxErrorException> {
1623 let svc = self.svc()?;
1624 let config = svc.wx_open_config_storage();
1625 let body = serde_json::json!({ "taskid": taskid });
1626 let response = self
1627 .post_with_key(
1628 &urls::fast_register_personal_weapp_search(config.as_ref()),
1629 &body.to_string(),
1630 "component_access_token",
1631 )
1632 .await?;
1633 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1634 }
1635
1636 async fn fast_register_beta_weapp(
1637 &self,
1638 name: &str,
1639 openid: &str,
1640 ) -> Result<WxOpenRegisterBetaWeappResult, WxErrorException> {
1641 let svc = self.svc()?;
1642 let config = svc.wx_open_config_storage();
1643 let body = serde_json::json!({ "name": name, "openid": openid });
1644 let response = self
1646 .post_with_key(
1647 &urls::fast_register_beta_weapp(config.as_ref()),
1648 &body.to_string(),
1649 "access_token",
1650 )
1651 .await?;
1652 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1653 }
1654
1655 async fn register_shop(
1656 &self,
1657 wx_name: &str,
1658 id_card_name: &str,
1659 id_card_number: &str,
1660 channel_id: Option<&str>,
1661 api_openstore_type: Option<i32>,
1662 auth_page_url: Option<&str>,
1663 ) -> Result<WxOpenResult, WxErrorException> {
1664 let svc = self.svc()?;
1665 let config = svc.wx_open_config_storage();
1666 let mut body = serde_json::json!({
1667 "wx_name": wx_name,
1668 "id_card_name": id_card_name,
1669 "id_card_number": id_card_number,
1670 "api_openstore_type": api_openstore_type,
1671 });
1672 if let Some(channel_id) = channel_id {
1673 if !channel_id.is_empty() {
1674 body["channel_id"] = serde_json::json!(channel_id);
1675 }
1676 }
1677 if let Some(auth_page_url) = auth_page_url {
1678 if !auth_page_url.is_empty() {
1679 body["auth_page_url"] = serde_json::json!(auth_page_url);
1680 }
1681 }
1682 let response = self
1683 .post_with_key(
1684 &urls::register_shop(config.as_ref()),
1685 &body.to_string(),
1686 "component_access_token",
1687 )
1688 .await?;
1689 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1690 }
1691
1692 async fn check_audit_status(&self, wx_name: &str) -> Result<String, WxErrorException> {
1693 let svc = self.svc()?;
1694 let config = svc.wx_open_config_storage();
1695 let url = format!(
1698 "{}?access_token={}",
1699 urls::check_shop_audit_status(config.as_ref()),
1700 self.get_component_access_token(false).await?
1701 );
1702 let body = serde_json::json!({ "wx_name": wx_name });
1703 self.post(&url, &body.to_string()).await
1704 }
1705
1706 async fn check_audit_status_with_appid(
1707 &self,
1708 app_id: &str,
1709 wx_name: &str,
1710 ) -> Result<String, WxErrorException> {
1711 let svc = self.svc()?;
1712 let config = svc.wx_open_config_storage();
1713 let url = format!(
1714 "{}?access_token={}",
1715 urls::check_shop_audit_status(config.as_ref()),
1716 self.get_authorizer_access_token(app_id, false).await?
1717 );
1718 let body = serde_json::json!({ "wx_name": wx_name });
1719 self.post(&url, &body.to_string()).await
1720 }
1721
1722 async fn submit_merchant_info(
1723 &self,
1724 app_id: &str,
1725 subject_type: &str,
1726 busi_license: &MinishopBusiLicense,
1727 organization_code_info: Option<&MinishopOrganizationCodeInfo>,
1728 idcard_info: Option<&MinishopIdcardInfo>,
1729 super_administrator_info: Option<&MinishopSuperAdministratorInfo>,
1730 merchant_shortname: Option<&str>,
1731 ) -> Result<WxOpenResult, WxErrorException> {
1732 let svc = self.svc()?;
1733 let config = svc.wx_open_config_storage();
1734 let mut body = serde_json::json!({
1735 "app_id": app_id,
1736 "subject_type": subject_type,
1737 "busi_license": minishop_json::busi_license(busi_license),
1738 });
1739 if let Some(o) = organization_code_info {
1740 body["organization_code_info"] = minishop_json::organization_code_info(o);
1741 }
1742 if let Some(i) = idcard_info {
1743 body["id_card_info"] = minishop_json::idcard_info(i);
1744 }
1745 if let Some(s) = super_administrator_info {
1746 body["super_administrator_info"] = minishop_json::super_administrator_info(s);
1747 }
1748 if let Some(name) = merchant_shortname {
1749 if !name.is_empty() {
1750 body["merchant_shortname"] = serde_json::json!(name);
1751 }
1752 }
1753 let url = format!(
1756 "{}?access_token={}",
1757 urls::submit_merchant_info(config.as_ref()),
1758 self.get_authorizer_access_token(app_id, false).await?
1759 );
1760 let response = self.bare_post(&url, &body.to_string()).await?;
1761 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1762 }
1763
1764 async fn submit_basic_info(
1765 &self,
1766 app_id: &str,
1767 name_info: &MinishopNameInfo,
1768 return_info: &MinishopReturnInfo,
1769 ) -> Result<WxOpenResult, WxErrorException> {
1770 let svc = self.svc()?;
1771 let config = svc.wx_open_config_storage();
1772 let body = serde_json::json!({
1773 "appid": app_id,
1774 "name_info": minishop_json::name_info(name_info),
1775 "return_info": minishop_json::return_info(return_info),
1776 });
1777 let url = format!(
1778 "{}?access_token={}",
1779 urls::submit_basic_info(config.as_ref()),
1780 self.get_authorizer_access_token(app_id, false).await?
1781 );
1782 let response = self.bare_post(&url, &body.to_string()).await?;
1783 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
1784 }
1785
1786 async fn upload_minishop_image_pic_file(
1787 &self,
1788 app_id: &str,
1789 height: i32,
1790 width: i32,
1791 file_path: &str,
1792 ) -> Result<WxMinishopImageUploadResult, WxErrorException> {
1793 let svc = self.svc()?;
1794 let config = svc.wx_open_config_storage();
1795 let url = format!(
1799 "{}?access_token={}&height={}&width={}",
1800 urls::upload_image(config.as_ref()),
1801 self.get_authorizer_access_token(app_id, false).await?,
1802 height,
1803 width,
1804 );
1805 svc.upload_minishop_media_file(&url, file_path).await
1806 }
1807
1808 async fn get_minishop_categories(
1809 &self,
1810 app_id: &str,
1811 f_cat_id: i32,
1812 ) -> Result<MinishopCategories, WxErrorException> {
1813 let svc = self.svc()?;
1814 let config = svc.wx_open_config_storage();
1815 let body = serde_json::json!({ "f_cat_id": f_cat_id });
1816 let url = format!(
1817 "{}?access_token={}",
1818 urls::minishop_category_get(config.as_ref()),
1819 self.get_authorizer_access_token(app_id, false).await?
1820 );
1821 let response = self.bare_post(&url, &body.to_string()).await?;
1822 let resp: serde_json::Value =
1823 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1824 let mut categories = MinishopCategories::default();
1825 categories.errcode = Self::json_i64(&resp, "errcode") as i32;
1826 if categories.errcode == 0 {
1827 if let Some(list) = resp.get("cat_list").and_then(|v| v.as_array()) {
1829 for item in list {
1830 let mut c = MinishopCategory::default();
1831 c.cat_id = Self::json_i64(item, "cat_id") as i32;
1832 c.f_cat_id = Self::json_i64(item, "f_cat_id") as i32;
1833 c.name = Self::json_str(item, "name");
1834 categories.cat_list.push(c);
1835 }
1836 }
1837 } else {
1838 categories.errmsg = Self::json_str(&resp, "errmsg");
1839 }
1840 Ok(categories)
1841 }
1842
1843 async fn get_minishop_brands(
1844 &self,
1845 app_id: &str,
1846 ) -> Result<MinishopBrandList, WxErrorException> {
1847 let svc = self.svc()?;
1848 let config = svc.wx_open_config_storage();
1849 let url = format!(
1850 "{}?access_token={}",
1851 urls::minishop_brand_get(config.as_ref()),
1852 self.get_authorizer_access_token(app_id, false).await?
1853 );
1854 let response = self.bare_post(&url, "{}").await?;
1855 let resp: serde_json::Value =
1856 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1857 let mut brand_list = MinishopBrandList::default();
1858 brand_list.errcode = Self::json_i64(&resp, "errcode") as i32;
1859 if brand_list.errcode == 0 {
1860 if let Some(list) = resp.get("brands").and_then(|v| v.as_array()) {
1861 for item in list {
1862 let mut brand = MinishopBrand::default();
1863 brand.first_cat_id = Self::json_i64(item, "first_cat_id") as i32;
1864 brand.second_cat_id = Self::json_i64(item, "second_cat_id") as i32;
1865 brand.third_cat_id = Self::json_i64(item, "third_cat_id") as i32;
1866 if let Some(info) = item.get("brand_info") {
1867 brand.brand_info.brand_id = Self::json_i64(info, "brand_id") as i32;
1868 brand.brand_info.brand_name = Self::json_str(info, "brand_name");
1869 }
1870 brand_list.brands.push(brand);
1871 }
1872 }
1873 } else {
1874 brand_list.errmsg = Self::json_str(&resp, "errmsg");
1875 }
1876 Ok(brand_list)
1877 }
1878
1879 async fn get_minishop_delivery_template(
1880 &self,
1881 app_id: &str,
1882 ) -> Result<MinishopDeliveryTemplateResult, WxErrorException> {
1883 let svc = self.svc()?;
1884 let config = svc.wx_open_config_storage();
1885 let url = format!(
1886 "{}?access_token={}",
1887 urls::minishop_delivery_template_get(config.as_ref()),
1888 self.get_authorizer_access_token(app_id, false).await?
1889 );
1890 let response = self.bare_post(&url, "{}").await?;
1891 let resp: serde_json::Value =
1892 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1893 let mut result = MinishopDeliveryTemplateResult::default();
1894 result.err_code = Self::json_i64(&resp, "errcode") as i32;
1895 if result.err_code == 0 {
1896 if let Some(list) = resp.get("template_list").and_then(|v| v.as_array()) {
1897 for item in list {
1898 let mut template = MinishopDeliveryTemplate::default();
1899 template.template_id = Self::json_i64(item, "template_id") as i32;
1900 template.name = Self::json_str(item, "name");
1901 template.valuation_type = if Self::json_i64(item, "valuation_type") == 1 {
1903 ValuationType::Weight
1904 } else {
1905 ValuationType::Package
1906 };
1907 result.template_list.push(template);
1908 }
1909 }
1910 } else {
1911 result.err_msg = Self::json_str(&resp, "errmsg");
1912 }
1913 Ok(result)
1914 }
1915
1916 async fn get_minishop_cat_list(
1917 &self,
1918 app_id: &str,
1919 ) -> Result<MinishopShopCatList, WxErrorException> {
1920 let svc = self.svc()?;
1921 let config = svc.wx_open_config_storage();
1922 let url = format!(
1923 "{}?access_token={}",
1924 urls::minishop_shopcategory_get(config.as_ref()),
1925 self.get_authorizer_access_token(app_id, false).await?
1926 );
1927 let response = self.bare_post(&url, "{}").await?;
1928 let resp: serde_json::Value =
1929 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1930 let mut shop_cat_list = MinishopShopCatList::default();
1931 shop_cat_list.errcode = Self::json_i64(&resp, "errcode") as i32;
1932 if shop_cat_list.errcode == 0 {
1933 if let Some(list) = resp.get("shopcat_list").and_then(|v| v.as_array()) {
1934 for item in list {
1935 let mut cat = MinishopShopCat::default();
1936 cat.shop_cat_id = Self::json_i64(item, "shopcat_id") as i32;
1937 cat.shop_cat_name = Self::json_str(item, "shopcat_name");
1938 cat.f_shop_cat_id = Self::json_i64(item, "f_shopcat_id") as i32;
1939 cat.cat_level = Self::json_i64(item, "cat_level") as i32;
1940 shop_cat_list.shop_cat_list.push(cat);
1941 }
1942 }
1943 } else {
1944 shop_cat_list.errmsg = Self::json_str(&resp, "errmsg");
1945 }
1946 Ok(shop_cat_list)
1947 }
1948
1949 async fn get_minishop_delivery_company(
1950 &self,
1951 app_id: &str,
1952 ) -> Result<WxMinishopAddGoodsSpuResult, WxErrorException> {
1953 let svc = self.svc()?;
1954 let config = svc.wx_open_config_storage();
1955 let url = format!(
1956 "{}?access_token={}",
1957 urls::minishop_get_delivery_company(config.as_ref()),
1958 self.get_authorizer_access_token(app_id, false).await?
1959 );
1960 let response = self.bare_post(&url, "{}").await?;
1961 let resp: serde_json::Value =
1962 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1963 let mut result = WxMinishopAddGoodsSpuResult::default();
1964 result.errcode = Self::json_i64(&resp, "errcode") as i32;
1965 if result.errcode == 0 {
1966 result.data = resp.get("company_list").cloned().unwrap_or_default();
1968 } else {
1969 result.errmsg = Self::json_str(&resp, "errmsg");
1970 }
1971 Ok(result)
1972 }
1973
1974 async fn minishop_create_coupon(
1975 &self,
1976 app_id: &str,
1977 coupon_info: &WxMinishopCoupon,
1978 ) -> Result<i32, WxErrorException> {
1979 let svc = self.svc()?;
1980 let config = svc.wx_open_config_storage();
1981 let url = format!(
1983 "{}?access_token={}",
1984 urls::minishop_create_coupon(config.as_ref()),
1985 self.get_authorizer_access_token(app_id, true).await?
1986 );
1987 let response = self
1988 .bare_post(&url, &minishop_json::coupon(coupon_info).to_string())
1989 .await?;
1990 let resp: serde_json::Value =
1991 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
1992 let coupon_id = if Self::json_i64(&resp, "errcode") == 0 {
1994 resp.get("data")
1995 .map(|d| Self::json_i64(d, "coupon_id") as i32)
1996 .unwrap_or(-1)
1997 } else {
1998 -1
1999 };
2000 Ok(coupon_id)
2001 }
2002
2003 async fn minishop_get_coupon_list(
2004 &self,
2005 app_id: &str,
2006 _start_create_time: &str,
2007 _end_create_time: &str,
2008 _status: i32,
2009 _page: i32,
2010 _page_size: i32,
2011 ) -> Result<Option<WxMinishopCouponStock>, WxErrorException> {
2012 let svc = self.svc()?;
2014 let config = svc.wx_open_config_storage();
2015 let _url = format!(
2016 "{}?access_token={}",
2017 urls::minishop_get_coupon_list(config.as_ref()),
2018 self.get_authorizer_access_token(app_id, true).await?
2019 );
2020 Ok(None)
2021 }
2022
2023 async fn minishop_push_coupon_to_user(
2024 &self,
2025 app_id: &str,
2026 open_id: &str,
2027 coupon_id: i32,
2028 ) -> Result<WxOpenResult, WxErrorException> {
2029 let svc = self.svc()?;
2030 let config = svc.wx_open_config_storage();
2031 let url = format!(
2032 "{}?access_token={}",
2033 urls::minishop_push_coupon(config.as_ref()),
2034 self.get_authorizer_access_token(app_id, true).await?
2035 );
2036 let body = serde_json::json!({ "openid": open_id, "coupon_id": coupon_id });
2037 let response = self.bare_post(&url, &body.to_string()).await?;
2038 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2039 }
2040
2041 async fn minishop_update_coupon(
2042 &self,
2043 app_id: &str,
2044 coupon_info: &WxMinishopCoupon,
2045 ) -> Result<i32, WxErrorException> {
2046 let svc = self.svc()?;
2047 let config = svc.wx_open_config_storage();
2048 let url = format!(
2049 "{}?access_token={}",
2050 urls::minishop_update_coupon(config.as_ref()),
2051 self.get_authorizer_access_token(app_id, true).await?
2052 );
2053 let response = self
2054 .bare_post(&url, &minishop_json::coupon(coupon_info).to_string())
2055 .await?;
2056 let resp: serde_json::Value =
2057 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
2058 let coupon_id = if Self::json_i64(&resp, "errcode") == 0 {
2059 resp.get("data")
2060 .map(|d| Self::json_i64(d, "coupon_id") as i32)
2061 .unwrap_or(-1)
2062 } else {
2063 -1
2064 };
2065 Ok(coupon_id)
2066 }
2067
2068 async fn minishop_update_coupon_status(
2069 &self,
2070 app_id: &str,
2071 coupon_id: i32,
2072 status: i32,
2073 ) -> Result<WxOpenResult, WxErrorException> {
2074 let svc = self.svc()?;
2075 let config = svc.wx_open_config_storage();
2076 let url = format!(
2077 "{}?access_token={}",
2078 urls::minishop_update_coupon_status(config.as_ref()),
2079 self.get_authorizer_access_token(app_id, true).await?
2080 );
2081 let body = serde_json::json!({ "coupon_id": coupon_id, "status": status });
2082 let response = self.bare_post(&url, &body.to_string()).await?;
2083 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2084 }
2085
2086 async fn minishop_goods_add_spu(
2087 &self,
2088 app_id: &str,
2089 spu: &WxMinishopSpu,
2090 ) -> Result<WxMinishopAddGoodsSpuResult, WxErrorException> {
2091 self.minishop_spu_common(app_id, urls::minishop_add_spu, spu, "create_time")
2092 .await
2093 }
2094
2095 async fn minishop_goods_del_spu(
2096 &self,
2097 app_id: &str,
2098 product_id: i64,
2099 out_product_id: i64,
2100 ) -> Result<WxOpenResult, WxErrorException> {
2101 let svc = self.svc()?;
2102 let config = svc.wx_open_config_storage();
2103 let url = format!(
2104 "{}?access_token={}",
2105 urls::minishop_del_spu(config.as_ref()),
2106 self.get_authorizer_access_token(app_id, true).await?
2107 );
2108 let body = serde_json::json!({ "product_id": product_id, "out_product_id": out_product_id.to_string() });
2110 let response = self.bare_post(&url, &body.to_string()).await?;
2111 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2112 }
2113
2114 async fn minishop_goods_update_spu(
2115 &self,
2116 app_id: &str,
2117 spu: &WxMinishopSpu,
2118 ) -> Result<WxMinishopAddGoodsSpuResult, WxErrorException> {
2119 self.minishop_spu_common(app_id, urls::minishop_update_spu, spu, "update_time")
2120 .await
2121 }
2122
2123 async fn minishop_goods_listing_spu(
2124 &self,
2125 app_id: &str,
2126 product_id: i64,
2127 out_product_id: i64,
2128 ) -> Result<WxOpenResult, WxErrorException> {
2129 let svc = self.svc()?;
2130 let config = svc.wx_open_config_storage();
2131 let url = format!(
2132 "{}?access_token={}",
2133 urls::minishop_listing_spu(config.as_ref()),
2134 self.get_authorizer_access_token(app_id, true).await?
2135 );
2136 let body = serde_json::json!({ "product_id": product_id, "out_product_id": out_product_id.to_string() });
2137 let response = self.bare_post(&url, &body.to_string()).await?;
2138 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2139 }
2140
2141 async fn minishop_goods_delisting_spu(
2142 &self,
2143 app_id: &str,
2144 product_id: i64,
2145 out_product_id: i64,
2146 ) -> Result<WxOpenResult, WxErrorException> {
2147 let svc = self.svc()?;
2148 let config = svc.wx_open_config_storage();
2149 let url = format!(
2150 "{}?access_token={}",
2151 urls::minishop_delisting_spu(config.as_ref()),
2152 self.get_authorizer_access_token(app_id, true).await?
2153 );
2154 let body = serde_json::json!({ "product_id": product_id, "out_product_id": out_product_id.to_string() });
2155 let response = self.bare_post(&url, &body.to_string()).await?;
2156 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2157 }
2158
2159 async fn minishop_goods_add_sku(
2160 &self,
2161 app_id: &str,
2162 sku: &WxMinishopSku,
2163 ) -> Result<WxMinishopAddGoodsSpuResult, WxErrorException> {
2164 self.minishop_sku_add_common(app_id, urls::minishop_add_sku, sku)
2166 .await
2167 }
2168
2169 async fn minishop_goods_batch_add_sku(
2170 &self,
2171 app_id: &str,
2172 sku_list: &[WxMinishopSku],
2173 ) -> Result<WxOpenResult, WxErrorException> {
2174 let svc = self.svc()?;
2175 let config = svc.wx_open_config_storage();
2176 let url = format!(
2177 "{}?access_token={}",
2178 urls::minishop_batch_add_sku(config.as_ref()),
2179 self.get_authorizer_access_token(app_id, true).await?
2180 );
2181 let skus: Vec<serde_json::Value> = sku_list.iter().map(minishop_json::sku).collect();
2182 let body = serde_json::json!({ "skus": skus });
2183 let response = self.bare_post(&url, &body.to_string()).await?;
2184 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2185 }
2186
2187 async fn minishop_goods_del_sku(
2188 &self,
2189 app_id: &str,
2190 product_id: i64,
2191 out_product_id: i64,
2192 out_sku_id: &str,
2193 sku_id: i64,
2194 ) -> Result<WxOpenResult, WxErrorException> {
2195 let svc = self.svc()?;
2196 let config = svc.wx_open_config_storage();
2197 let url = format!(
2198 "{}?access_token={}",
2199 urls::minishop_del_sku(config.as_ref()),
2200 self.get_authorizer_access_token(app_id, true).await?
2201 );
2202 let body = serde_json::json!({
2203 "product_id": product_id,
2204 "out_product_id": out_product_id,
2205 "sku_id": sku_id,
2206 "out_sku_id": out_sku_id,
2207 });
2208 let response = self.bare_post(&url, &body.to_string()).await?;
2209 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2210 }
2211
2212 async fn minishop_goods_update_sku(
2213 &self,
2214 app_id: &str,
2215 sku: &WxMinishopSku,
2216 ) -> Result<WxOpenResult, WxErrorException> {
2217 let svc = self.svc()?;
2218 let config = svc.wx_open_config_storage();
2219 let url = format!(
2220 "{}?access_token={}",
2221 urls::minishop_update_sku(config.as_ref()),
2222 self.get_authorizer_access_token(app_id, true).await?
2223 );
2224 let response = self
2225 .bare_post(&url, &minishop_json::sku(sku).to_string())
2226 .await?;
2227 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2228 }
2229
2230 async fn minishop_goods_update_sku_price(
2231 &self,
2232 app_id: &str,
2233 product_id: i64,
2234 out_product_id: i64,
2235 out_sku_id: &str,
2236 sku_id: i64,
2237 _sale_price: i64,
2238 _market_price: i64,
2239 ) -> Result<WxOpenResult, WxErrorException> {
2240 let svc = self.svc()?;
2241 let config = svc.wx_open_config_storage();
2242 let url = format!(
2243 "{}?access_token={}",
2244 urls::minishop_update_sku_price(config.as_ref()),
2245 self.get_authorizer_access_token(app_id, true).await?
2246 );
2247 let body = serde_json::json!({
2250 "product_id": product_id,
2251 "out_product_id": out_product_id,
2252 "sku_id": sku_id,
2253 "out_sku_id": out_sku_id,
2254 "sale_price": out_sku_id,
2255 "market_price": out_sku_id,
2256 });
2257 let response = self.bare_post(&url, &body.to_string()).await?;
2258 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2259 }
2260
2261 async fn minishop_goods_update_sku_stock(
2262 &self,
2263 app_id: &str,
2264 product_id: i64,
2265 out_product_id: i64,
2266 out_sku_id: &str,
2267 sku_id: i64,
2268 r#type: i32,
2269 stock_num: i32,
2270 ) -> Result<WxOpenResult, WxErrorException> {
2271 let svc = self.svc()?;
2272 let config = svc.wx_open_config_storage();
2273 let url = format!(
2274 "{}?access_token={}",
2275 urls::minishop_update_sku_stock(config.as_ref()),
2276 self.get_authorizer_access_token(app_id, true).await?
2277 );
2278 let body = serde_json::json!({
2279 "product_id": product_id,
2280 "out_product_id": out_product_id,
2281 "sku_id": sku_id,
2282 "out_sku_id": out_sku_id,
2283 "type": r#type,
2284 "stock_num": stock_num,
2285 });
2286 let response = self.bare_post(&url, &body.to_string()).await?;
2287 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2288 }
2289
2290 async fn minishop_common_post(
2291 &self,
2292 _app_id: &str,
2293 _url: &str,
2294 _request_param: &str,
2295 ) -> Result<Option<String>, WxErrorException> {
2296 Ok(None)
2298 }
2299
2300 async fn add_limit_discount_goods(
2301 &self,
2302 app_id: &str,
2303 limit_discount_goods: &LimitDiscountGoods,
2304 ) -> Result<i32, WxErrorException> {
2305 let svc = self.svc()?;
2306 let config = svc.wx_open_config_storage();
2307 let url = format!(
2310 "{}access_token={}",
2311 urls::minishop_add_limit_discount(config.as_ref()),
2312 self.get_authorizer_access_token(app_id, false).await?
2313 );
2314 let response = self
2315 .bare_post(
2316 &url,
2317 &minishop_json::limit_discount_goods(limit_discount_goods).to_string(),
2318 )
2319 .await?;
2320 let resp: serde_json::Value =
2321 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
2322 let task_id = if Self::json_i64(&resp, "errcode") == 0 {
2324 Self::json_i64(&resp, "task_id") as i32
2325 } else {
2326 0
2327 };
2328 Ok(task_id)
2329 }
2330
2331 async fn get_limit_discount_list(
2332 &self,
2333 app_id: &str,
2334 status: Option<i32>,
2335 ) -> Result<Vec<LimitDiscountGoods>, WxErrorException> {
2336 let svc = self.svc()?;
2337 let config = svc.wx_open_config_storage();
2338 let url = format!(
2339 "{}access_token={}",
2340 urls::minishop_get_limit_discount(config.as_ref()),
2341 self.get_authorizer_access_token(app_id, false).await?
2342 );
2343 let mut body = serde_json::json!({});
2344 if let Some(status) = status {
2345 body["status"] = serde_json::json!(status);
2346 }
2347 let response = self.bare_post(&url, &body.to_string()).await?;
2348 let resp: serde_json::Value =
2349 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
2350 let mut list = Vec::new();
2351 if Self::json_i64(&resp, "errcode") == 0 {
2352 if let Some(goods_array) = resp.get("limited_discount_list").and_then(|v| v.as_array())
2353 {
2354 for (i, goods) in goods_array.iter().enumerate() {
2355 let mut goods_bean = LimitDiscountGoods::default();
2356 goods_bean.task_id = Self::json_i64(goods, "task_id");
2357 goods_bean.status = Self::json_i64(goods, "status") as i32;
2358 goods_bean.start_time = Self::json_i64(goods, "start_time")
2361 .saturating_mul(1000)
2362 .to_string();
2363 goods_bean.end_time = Self::json_i64(goods, "end_time")
2364 .saturating_mul(1000)
2365 .to_string();
2366 if let Some(sku_array) = goods
2367 .get("limited_discount_sku_list")
2368 .and_then(|v| v.as_array())
2369 {
2370 for (j, _) in sku_array.iter().enumerate() {
2371 let sku_json = sku_array.get(i).unwrap_or(&serde_json::Value::Null);
2374 let mut sku = LimitDiscountSku::default();
2375 sku.sku_id = Self::json_i64(sku_json, "sku_id");
2376 let price = sku_json
2379 .get("sale_price")
2380 .and_then(|v| v.as_f64())
2381 .map(|p| (p / 100.0).to_string())
2382 .unwrap_or_default();
2383 sku.sale_price = price;
2384 sku.sale_stock = Self::json_i64(sku_json, "sale_stock") as i32;
2385 let _ = j;
2386 goods_bean.limit_discount_sku_list.push(sku);
2387 }
2388 }
2389 list.push(goods_bean);
2390 }
2391 }
2392 }
2393 Ok(list)
2394 }
2395
2396 async fn update_limit_discount_status(
2397 &self,
2398 app_id: &str,
2399 task_id: i64,
2400 status: i32,
2401 ) -> Result<WxOpenResult, WxErrorException> {
2402 let svc = self.svc()?;
2403 let config = svc.wx_open_config_storage();
2404 let url = format!(
2405 "{}access_token={}",
2406 urls::minishop_update_limit_discount_status(config.as_ref()),
2407 self.get_authorizer_access_token(app_id, false).await?
2408 );
2409 let body = serde_json::json!({ "task_id": task_id, "status": status });
2410 let response = self.bare_post(&url, &body.to_string()).await?;
2411 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2412 }
2413
2414 async fn get_share_cloud_base_env(
2415 &self,
2416 appids: &[String],
2417 ) -> Result<GetShareCloudBaseEnvResponse, WxErrorException> {
2418 let svc = self.svc()?;
2419 let config = svc.wx_open_config_storage();
2420 let body = serde_json::json!({ "appids": appids });
2421 let response = self
2422 .post(&urls::batch_get_env_id(config.as_ref()), &body.to_string())
2423 .await?;
2424 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2425 }
2426
2427 async fn get_tcb_env_list(&self) -> Result<GetTcbEnvListResponse, WxErrorException> {
2428 let svc = self.svc()?;
2429 let config = svc.wx_open_config_storage();
2430 let response = self
2431 .post(&urls::describe_envs(config.as_ref()), "{}")
2432 .await?;
2433 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2434 }
2435
2436 async fn change_tcb_env(&self, env: &str) -> Result<WxOpenResult, WxErrorException> {
2437 let svc = self.svc()?;
2438 let config = svc.wx_open_config_storage();
2439 let body = serde_json::json!({ "env": env });
2440 let response = self
2441 .post(&urls::modify_env(config.as_ref()), &body.to_string())
2442 .await?;
2443 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2444 }
2445
2446 async fn share_cloud_base_env(
2447 &self,
2448 request: &ShareCloudBaseEnvRequest,
2449 ) -> Result<ShareCloudBaseEnvResponse, WxErrorException> {
2450 let svc = self.svc()?;
2451 let config = svc.wx_open_config_storage();
2452 let body =
2453 serde_json::to_string(request).map_err(|e| WxErrorException::Serde(e.to_string()))?;
2454 let response = self
2455 .post(&urls::batch_share_env(config.as_ref()), &body)
2456 .await?;
2457 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2458 }
2459
2460 async fn clear_quota_v2(&self, appid: &str) -> Result<WxOpenResult, WxErrorException> {
2461 let svc = self.svc()?;
2462 let config = svc.wx_open_config_storage();
2463 let body = serde_json::json!({
2465 "appid": appid,
2466 "component_appid": config.component_app_id().unwrap_or_default(),
2467 "appsecret": config.component_app_secret().unwrap_or_default(),
2468 });
2469 let response = self
2470 .bare_post(&urls::clear_quota(config.as_ref()), &body.to_string())
2471 .await?;
2472 WxOpenResult::from_json(&response).map_err(WxErrorException::Serde)
2473 }
2474
2475 async fn apply_set_order_path_info(
2476 &self,
2477 info: &WxOpenMaApplyOrderPathInfo,
2478 ) -> Result<WxOpenResult, WxErrorException> {
2479 let svc = self.svc()?;
2480 let config = svc.wx_open_config_storage();
2481 let body =
2483 serde_json::to_string(info).map_err(|e| WxErrorException::Serde(e.to_string()))?;
2484 let response = self
2485 .post(&urls::apply_set_order_path_info(config.as_ref()), &body)
2486 .await?;
2487 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2488 }
2489
2490 async fn modify_wxa_server_domain(
2491 &self,
2492 action: &str,
2493 request_domains: &[String],
2494 ws_request_domains: &[String],
2495 upload_domains: &[String],
2496 download_domains: &[String],
2497 udp_domains: &[String],
2498 tcp_domains: &[String],
2499 ) -> Result<WxOpenMaDomainResult, WxErrorException> {
2500 let svc = self.svc()?;
2501 let config = svc.wx_open_config_storage();
2502 let mut body = serde_json::json!({ "action": action });
2503 if action != "get" {
2505 body["requestdomain"] = serde_json::json!(request_domains);
2506 body["wsrequestdomain"] = serde_json::json!(ws_request_domains);
2507 body["uploaddomain"] = serde_json::json!(upload_domains);
2508 body["downloaddomain"] = serde_json::json!(download_domains);
2509 body["udpdomain"] = serde_json::json!(udp_domains);
2510 body["tcpdomain"] = serde_json::json!(tcp_domains);
2511 }
2512 let response = self
2513 .post(
2514 &urls::modify_wxa_server_domain(config.as_ref()),
2515 &body.to_string(),
2516 )
2517 .await?;
2518 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2519 }
2520
2521 async fn get_domain_confirm_file(
2522 &self,
2523 ) -> Result<WxOpenMaDomainConfirmFileResult, WxErrorException> {
2524 let svc = self.svc()?;
2525 let config = svc.wx_open_config_storage();
2526 let response = self
2527 .post(&urls::get_domain_confirm_file(config.as_ref()), "{}")
2528 .await?;
2529 WxOpenMaDomainConfirmFileResult::from_json(&response).map_err(WxErrorException::Serde)
2530 }
2531
2532 async fn modify_wxa_jump_domain(
2533 &self,
2534 action: &str,
2535 domain_list: &[String],
2536 ) -> Result<String, WxErrorException> {
2537 let svc = self.svc()?;
2538 let config = svc.wx_open_config_storage();
2539 let mut body = serde_json::json!({ "action": action });
2540 if action != "get" {
2541 body["webviewdomain"] = serde_json::json!(domain_list);
2542 }
2543 self.post(
2544 &urls::modify_wxa_jump_domain(config.as_ref()),
2545 &body.to_string(),
2546 )
2547 .await
2548 }
2549
2550 async fn modify_wxa_jump_domain_info(
2551 &self,
2552 action: &str,
2553 domain_list: &[String],
2554 ) -> Result<WxOpenMaWebDomainResult, WxErrorException> {
2555 let response = self.modify_wxa_jump_domain(action, domain_list).await?;
2556 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))
2557 }
2558}
2559
2560impl WxOpenComponentServiceImpl {
2563 async fn minishop_spu_common<F>(
2564 &self,
2565 app_id: &str,
2566 url_fn: F,
2567 spu: &WxMinishopSpu,
2568 time_key: &str,
2569 ) -> Result<WxMinishopAddGoodsSpuResult, WxErrorException>
2570 where
2571 F: FnOnce(&dyn WxOpenConfigStorage) -> String,
2572 {
2573 let svc = self.svc()?;
2574 let config = svc.wx_open_config_storage();
2575 let url = format!(
2576 "{}?access_token={}",
2577 url_fn(config.as_ref()),
2578 self.get_authorizer_access_token(app_id, true).await?
2579 );
2580 let response = self
2581 .bare_post(&url, &minishop_json::spu(spu).to_string())
2582 .await?;
2583 let resp: serde_json::Value =
2584 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
2585 let mut result = WxMinishopAddGoodsSpuResult::default();
2586 result.errcode = Self::json_i64(&resp, "errcode") as i32;
2587 if result.errcode == 0 {
2588 let data = resp.get("data").cloned().unwrap_or_default();
2589 let mut data_obj = serde_json::Map::new();
2591 if let Some(pid) = data.get("product_id") {
2592 data_obj.insert("product_id".to_string(), pid.clone());
2593 }
2594 if let Some(oid) = data.get("out_product_id") {
2595 data_obj.insert("out_product_id".to_string(), oid.clone());
2596 }
2597 if let Some(t) = data.get(time_key) {
2598 data_obj.insert(time_key.to_string(), t.clone());
2599 }
2600 result.data = serde_json::Value::Object(data_obj);
2601 } else {
2602 result.errmsg = Self::json_str(&resp, "errmsg");
2603 }
2604 Ok(result)
2605 }
2606
2607 async fn minishop_sku_add_common<F>(
2610 &self,
2611 app_id: &str,
2612 url_fn: F,
2613 sku: &WxMinishopSku,
2614 ) -> Result<WxMinishopAddGoodsSpuResult, WxErrorException>
2615 where
2616 F: FnOnce(&dyn WxOpenConfigStorage) -> String,
2617 {
2618 let svc = self.svc()?;
2619 let config = svc.wx_open_config_storage();
2620 let url = format!(
2621 "{}?access_token={}",
2622 url_fn(config.as_ref()),
2623 self.get_authorizer_access_token(app_id, true).await?
2624 );
2625 let response = self
2626 .bare_post(&url, &minishop_json::sku(sku).to_string())
2627 .await?;
2628 let resp: serde_json::Value =
2629 serde_json::from_str(&response).map_err(|e| WxErrorException::Serde(e.to_string()))?;
2630 let mut result = WxMinishopAddGoodsSpuResult::default();
2631 result.errcode = Self::json_i64(&resp, "errcode") as i32;
2632 if result.errcode == 0 {
2633 let data = resp.get("data").cloned().unwrap_or_default();
2634 let mut data_obj = serde_json::Map::new();
2635 if let Some(sid) = data.get("sku_id") {
2636 data_obj.insert("sku_id".to_string(), sid.clone());
2637 }
2638 if let Some(t) = data.get("create_time") {
2639 data_obj.insert("create_time".to_string(), t.clone());
2640 }
2641 result.data = serde_json::Value::Object(data_obj);
2642 } else {
2643 result.errmsg = Self::json_str(&resp, "errmsg");
2644 }
2645 Ok(result)
2646 }
2647}