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 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}