wecom_rs/client/
mod.rs

1use crate::{
2    client::common::model::Response,
3    error::{new_api_error, Result},
4    utils::http::{do_http, PostParameters},
5};
6use common::model::{Responser, Token};
7use reqwest::Method;
8use serde::{de::DeserializeOwned, ser::Serialize};
9use serde_json::{json, Value};
10use std::env;
11use tracing::debug;
12
13const BASE_URL: &str = "https://qyapi.weixin.qq.com/cgi-bin";
14
15#[derive(Debug)]
16pub struct Client {
17    pub(crate) corp_id: String,
18    pub(crate) corp_secret: String,
19    /// 客户联系专用
20    pub(crate) custom_contact_secret: Option<String>,
21}
22
23impl Client {
24    pub fn new(
25        corp_id: String,
26        corp_secret: String,
27        custom_contact_secret: Option<String>,
28    ) -> Client {
29        Self {
30            corp_id,
31            corp_secret,
32            custom_contact_secret,
33        }
34    }
35
36    pub fn new_from_env() -> Result<Self> {
37        let custom_contact_secret = if let Ok(ccs) = env::var("CUSTOM_CONTACT_SECRET") {
38            Some(ccs)
39        } else {
40            None
41        };
42        Ok(Self {
43            corp_id: env::var("CORP_ID")?,
44            corp_secret: env::var("CORP_SECRET")?,
45            custom_contact_secret,
46        })
47    }
48
49    async fn access_token(&self) -> Result<String> {
50        let query_body = json!({
51            "corpid": self.corp_id,
52            "corpsecret": self.corp_secret,
53        });
54
55        let resp = do_http(
56            Method::GET,
57            &format!("{}/gettoken", BASE_URL),
58            None,
59            Some(query_body),
60            None,
61        )
62        .await?;
63
64        let data = resp.json::<Response<Token>>().await?;
65        if data.err_code != 0 {
66            return Err(new_api_error(data.err_code, data.err_msg));
67        }
68        let token = data.data.unwrap();
69        Ok(token.access_token)
70
71        // 测试验证
72        // let data = resp.text().await?;
73        // println!("==>> access_token request: {data}");
74        // Ok(String::new())
75    }
76
77    async fn custom_contact_access_token(&self) -> Result<String> {
78        let query_body = json!({
79            "corpid": self.corp_id,
80            "corpsecret": self.custom_contact_secret,
81        });
82
83        let resp = do_http(
84            Method::GET,
85            &format!("{}/gettoken", BASE_URL),
86            None,
87            Some(query_body),
88            None,
89        )
90        .await?;
91
92        let data = resp.json::<Response<Token>>().await?;
93        if data.err_code != 0 {
94            return Err(new_api_error(data.err_code, data.err_msg));
95        }
96        let token = data.data.unwrap();
97        Ok(token.access_token)
98    }
99
100    // http 请求
101    async fn request<R: Responser + DeserializeOwned + Serialize + Default>(
102        &self,
103        method: Method,
104        url: &str,
105        body: Option<Value>,
106    ) -> Result<R> {
107        let body = if let Some(data) = body {
108            Some(PostParameters::json(data))
109        } else {
110            None
111        };
112
113        let resp = do_http(method, url, None, None, body)
114            .await?
115            .json::<R>()
116            .await?;
117
118        debug!("\nurl:{url} \nresponse: {}", serde_json::to_string(&resp)?);
119        if resp.error_code() != 0 {
120            return Err(new_api_error(resp.error_code(), resp.error_message()));
121        }
122        Ok(resp)
123    }
124}
125
126/// 通讯录管理
127mod contact;
128pub use contact::*;
129
130/// 客户联系管理
131mod external_contact;
132pub use external_contact::*;
133/// 微信客服
134mod wechat_custom_service;
135pub use wechat_custom_service::*;
136/// 身份验证
137mod auth;
138pub use auth::*;
139/// 应用管理
140mod app;
141pub use app::*;
142/// 消息推送
143mod message_push;
144pub use message_push::*;
145/// 事件定义
146pub mod event;
147// TODO: 其他功能模块接口实现
148
149/// 公共组件
150pub mod common;