dingtalk_rs/client/
mod.rs

1use crate::error::{new_api_error, Result};
2use http::{do_http, PostParameters};
3use reqwest::Method;
4use resp::{Responser, Token};
5use serde::{de::DeserializeOwned, ser::Serialize};
6use serde_json::{json, Value};
7
8const BASE_URL: &str = "https://oapi.dingtalk.com";
9const BASE_URL_NEW_VERSION: &str = "https://api.dingtalk.com";
10
11#[derive(Debug)]
12pub struct Client {
13    app_key: String,
14    app_secret: String,
15}
16
17impl Client {
18    pub fn new(app_key: String, app_secret: String) -> Self {
19        Self {
20            app_key,
21            app_secret,
22        }
23    }
24
25    pub fn new_from_env() -> Result<Self> {
26        use std::env;
27        Ok(Client::new(env::var("APP_KEY")?, env::var("APP_SECRET")?))
28    }
29
30    /// 获取企业内部应用的access_token
31    /// https://open.dingtalk.com/document/orgapp-server/obtain-orgapp-token
32    pub async fn access_token(&self) -> Result<String> {
33        let query_body = json!({
34            "appkey": self.app_key,
35            "appsecret": self.app_secret,
36        });
37
38        let resp = do_http(
39            Method::GET,
40            &format!("{BASE_URL}/gettoken"),
41            None,
42            Some(query_body),
43            None,
44        )
45        .await?;
46
47        let data = resp.json::<Token>().await?;
48
49        Ok(data.access_token)
50    }
51
52    // http 请求
53    async fn request<R: Responser + DeserializeOwned + Serialize + Default>(
54        &self,
55        method: Method,
56        url: &str,
57        body: Option<Value>,
58    ) -> Result<R> {
59        let body = if let Some(data) = body {
60            Some(PostParameters::json(data))
61        } else {
62            None
63        };
64
65        let resp = do_http(method, url, None, None, body)
66            .await?
67            .json::<R>()
68            .await?;
69
70        // println!("{}", serde_json::to_string(&resp)?);
71        if resp.error_code() != 0 {
72            return Err(new_api_error(
73                resp.error_code(),
74                resp.error_message(),
75                resp.request_id(),
76            ));
77        }
78        Ok(resp)
79
80        // 调试使用,验证输出结果
81        // let resp = do_http(method, url, None, None, body).await?.text().await?;
82        // println!("{resp}");
83        // Ok(Response::default().result.unwrap())
84    }
85
86    /// 新版服务端API通用请求
87    async fn request_new_version<R: DeserializeOwned + Default>(
88        &self,
89        method: Method,
90        url: &str,
91        body: Option<Value>,
92    ) -> Result<R> {
93        let mut query = None;
94        let mut data = None;
95
96        match method {
97            Method::GET => query = body,
98            Method::POST => {
99                data = if let Some(data) = body {
100                    Some(PostParameters::json(data))
101                } else {
102                    None
103                };
104            }
105            _ => {}
106        }
107
108        let token = self.access_token().await?;
109
110        let headers = Some(std::collections::HashMap::from([(
111            reqwest::header::HeaderName::from_static("x-acs-dingtalk-access-token"),
112            token,
113        )]));
114
115        let resp = do_http(method, url, headers, query, data)
116            .await?
117            .json::<R>()
118            .await?;
119
120        Ok(resp)
121
122        // 调试使用,验证输出结果
123        // let resp = do_http(method, url, None, None, body).await?.text().await?;
124        // println!("{resp}");
125        // Ok(Response::default().result.unwrap())
126    }
127}
128
129mod http;
130mod resp;
131pub(crate) use resp::{Response, ResponseFlatten};
132
133/// 通讯录管理
134mod contact_manager;
135pub use contact_manager::*;
136
137/// 公共参数
138mod dto;
139pub use dto::*;
140/// 公共模型数据
141mod model;
142pub use model::*;
143/// 事件订阅
144mod event_subscribe;
145pub use event_subscribe::*;
146
147/// 消息通知
148mod message_notify;
149pub use message_notify::*;