Skip to main content

wx_sdk/mp/
user.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    error::{CommonError, CommonResponse},
5    wechat::WxApiRequestBuilder,
6    SdkResult,
7};
8
9#[derive(Debug, Serialize, Deserialize)]
10pub struct UserList {
11    pub total: Option<i32>,
12    pub data: OpenidList,
13    pub count: i32,
14    pub next_openid: String,
15}
16
17#[derive(Debug, Serialize, Deserialize)]
18pub struct OpenidList {
19    pub openid: Vec<String>,
20}
21#[derive(Debug, Serialize, Deserialize)]
22pub struct QueryUserInfo {
23    pub openid: String,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub lang: Option<String>,
26}
27#[derive(Debug, Serialize, Deserialize)]
28pub struct UserInfo {
29    pub subscribe: i8,
30    pub openid: String,
31    pub nickname: String,
32    pub sex: Option<i8>,
33    pub city: Option<String>,
34    pub country: Option<String>,
35    pub province: Option<String>,
36    pub language: Option<String>,
37    pub headimgurl: String,
38    pub subscribe_time: i64,
39    pub unionid: Option<String>,
40    pub remark: Option<String>,
41    pub groupid: i32,
42    pub tagid_list: Vec<i32>,
43    pub subscribe_scene: String,
44    pub qr_scene: Option<i32>,
45    pub qr_scene_str: Option<String>,
46}
47
48#[derive(Debug, Serialize, Deserialize)]
49pub struct UserInfoList {
50    pub user_info_list: Vec<UserInfoItem>,
51}
52
53#[derive(Debug, Serialize, Deserialize)]
54#[serde(untagged)]
55pub enum UserInfoItem {
56    Subscribe(UserInfo),
57    Unsubscribe { openid: String },
58}
59
60pub struct UserModule<'a, T: WxApiRequestBuilder>(pub(crate) &'a T);
61
62impl<'a, T: WxApiRequestBuilder> UserModule<'a, T> {
63    pub async fn get(&self, next_openid: Option<String>) -> SdkResult<UserList> {
64        let base_url = "https://api.weixin.qq.com/cgi-bin/user/get";
65        let sdk = self.0;
66        let mut builder = sdk.wx_get(base_url).await?;
67        if let Some(next) = next_openid {
68            builder = builder.query(&[("next_openid", next)])
69        }
70
71        let res: CommonResponse<UserList> = builder.send().await?.json().await?;
72
73        res.into()
74    }
75
76    pub async fn info(&self, openid: &str, lang: &str) -> SdkResult<UserInfo> {
77        let base_url = "https://api.weixin.qq.com/cgi-bin/user/info";
78        let sdk = self.0;
79        let builder = sdk.wx_get(base_url).await?;
80        let builder = builder.query(&[("openid", openid), ("lang", lang)]);
81        let res: CommonResponse<UserInfo> = builder.send().await?.json().await?;
82
83        res.into()
84    }
85
86    pub async fn tag_get(&self, tagid: i32, next_openid: &str) -> SdkResult<UserList> {
87        let base_url = "https://api.weixin.qq.com/cgi-bin/user/tag/get";
88        let sdk = self.0;
89        let res: CommonResponse<UserList> = sdk
90            .wx_post(base_url)
91            .await?
92            .json(&serde_json::json!({ "tagid": tagid, "next_openid": next_openid }))
93            .send()
94            .await?
95            .json()
96            .await?;
97
98        res.into()
99    }
100
101    pub async fn info_updateremark(&self, openid: &str, remark: &str) -> SdkResult<()> {
102        let base_url = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark";
103        let sdk = self.0;
104        let res: CommonError = sdk
105            .wx_post(base_url)
106            .await?
107            .json(&serde_json::json!({ "openid": openid, "remark": remark }))
108            .send()
109            .await?
110            .json()
111            .await?;
112
113        res.into()
114    }
115
116    pub async fn info_batchget(&self, query: &[QueryUserInfo]) -> SdkResult<UserInfoList> {
117        let base_url = "https://api.weixin.qq.com/cgi-bin/user/info/batchget";
118        let sdk = self.0;
119        let res: CommonResponse<UserInfoList> = sdk
120            .wx_post(base_url)
121            .await?
122            .json(query)
123            .send()
124            .await?
125            .json()
126            .await?;
127
128        res.into()
129    }
130}
131#[cfg(test)]
132mod tests {
133    use super::*;
134    #[test]
135    fn test_user_info_item() -> Result<(), &'static str> {
136        let json = r#"
137        [
138            {
139                "subscribe": 1, 
140                "openid": "otvxTs4dckWG7imySrJd6jSi0CWE", 
141                "nickname": "iWithery", 
142                "sex": 1, 
143                "language": "zh_CN", 
144                "city": "揭阳", 
145                "province": "广东", 
146                "country": "中国", 
147                "headimgurl": "http://thirdwx.qlogo.cn/mmopen/xbIQx1GRqdvyqkMMhEaGOX802l1CyqMJNgUzKP8MeAeHFicRDSnZH7FY4XB7p8XHXIf6uJA2SCunTPicGKezDC4saKISzRj3nz/0",
148                "subscribe_time": 1434093047, 
149                "unionid": "oR5GjjgEhCMJFyzaVZdrxZ2zRRF4", 
150                "remark": "", 
151                "groupid": 0,
152                "tagid_list":[128,2],
153                "subscribe_scene": "ADD_SCENE_QR_CODE",
154                "qr_scene": 98765,
155                "qr_scene_str": ""
156            }, {
157                "subscribe": 0, 
158                "openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg"
159            }
160        ]
161        "#;
162        let users: Vec<UserInfoItem> = serde_json::from_str(&json).unwrap();
163        // println!("{:#?}", &users);
164        match &users[0] {
165            UserInfoItem::Subscribe(user) => assert_eq!(user.tagid_list, vec![128, 2]),
166            _ => return Err("match &users[0]"),
167        }
168        match &users[1] {
169            UserInfoItem::Unsubscribe { openid } => {
170                assert_eq!(openid, "otvxTs_JZ6SEiP0imdhpi50fuSZg")
171            }
172            _ => return Err("match &users[1]"),
173        }
174
175        Ok(())
176    }
177}