open_lark/service/cloud_docs/comments/
list_replies.rs1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 core::{
6 api_req::ApiRequest,
7 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
8 config::Config,
9 constants::AccessTokenType,
10 http::Transport,
11 req_option::RequestOption,
12 SDKResult,
13 },
14 impl_executable_builder_owned,
15};
16
17use super::list::Reply;
18
19#[derive(Debug, Serialize, Default, Clone)]
21pub struct ListRepliesRequest {
22 #[serde(skip)]
23 api_request: ApiRequest,
24 #[serde(skip)]
26 file_token: String,
27 #[serde(skip)]
29 file_type: String,
30 #[serde(skip)]
32 comment_id: String,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 page_size: Option<i32>,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 page_token: Option<String>,
39 #[serde(skip_serializing_if = "Option::is_none")]
41 user_id_type: Option<String>,
42}
43
44impl ListRepliesRequest {
45 pub fn builder() -> ListRepliesRequestBuilder {
46 ListRepliesRequestBuilder::default()
47 }
48
49 pub fn new(
50 file_token: impl ToString,
51 file_type: impl ToString,
52 comment_id: impl ToString,
53 ) -> Self {
54 Self {
55 file_token: file_token.to_string(),
56 file_type: file_type.to_string(),
57 comment_id: comment_id.to_string(),
58 ..Default::default()
59 }
60 }
61}
62
63#[derive(Default)]
64pub struct ListRepliesRequestBuilder {
65 request: ListRepliesRequest,
66}
67
68impl ListRepliesRequestBuilder {
69 pub fn file_token(mut self, file_token: impl ToString) -> Self {
71 self.request.file_token = file_token.to_string();
72 self
73 }
74
75 pub fn file_type(mut self, file_type: impl ToString) -> Self {
77 self.request.file_type = file_type.to_string();
78 self
79 }
80
81 pub fn with_doc_type(mut self) -> Self {
83 self.request.file_type = "doc".to_string();
84 self
85 }
86
87 pub fn with_docx_type(mut self) -> Self {
89 self.request.file_type = "docx".to_string();
90 self
91 }
92
93 pub fn with_sheet_type(mut self) -> Self {
95 self.request.file_type = "sheet".to_string();
96 self
97 }
98
99 pub fn with_bitable_type(mut self) -> Self {
101 self.request.file_type = "bitable".to_string();
102 self
103 }
104
105 pub fn comment_id(mut self, comment_id: impl ToString) -> Self {
107 self.request.comment_id = comment_id.to_string();
108 self
109 }
110
111 pub fn page_size(mut self, page_size: i32) -> Self {
113 self.request.page_size = Some(page_size);
114 self
115 }
116
117 pub fn page_token(mut self, page_token: impl ToString) -> Self {
119 self.request.page_token = Some(page_token.to_string());
120 self
121 }
122
123 pub fn user_id_type(mut self, user_id_type: impl ToString) -> Self {
125 self.request.user_id_type = Some(user_id_type.to_string());
126 self
127 }
128
129 pub fn with_open_id(mut self) -> Self {
131 self.request.user_id_type = Some("open_id".to_string());
132 self
133 }
134
135 pub fn with_user_id(mut self) -> Self {
137 self.request.user_id_type = Some("user_id".to_string());
138 self
139 }
140
141 pub fn with_union_id(mut self) -> Self {
143 self.request.user_id_type = Some("union_id".to_string());
144 self
145 }
146
147 pub fn build(mut self) -> ListRepliesRequest {
148 self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
149 self.request
150 }
151}
152
153impl_executable_builder_owned!(
155 ListRepliesRequestBuilder,
156 super::CommentsService,
157 ListRepliesRequest,
158 BaseResponse<ListRepliesResponse>,
159 list_replies
160);
161
162#[derive(Debug, Deserialize)]
164pub struct ListRepliesResponse {
165 pub items: Vec<Reply>,
167 pub has_more: bool,
169 pub page_token: Option<String>,
171}
172
173impl ApiResponseTrait for ListRepliesResponse {
174 fn data_format() -> ResponseFormat {
175 ResponseFormat::Data
176 }
177}
178
179pub async fn list_replies(
181 request: ListRepliesRequest,
182 config: &Config,
183 option: Option<RequestOption>,
184) -> SDKResult<BaseResponse<ListRepliesResponse>> {
185 let mut api_req = request.api_request;
186 api_req.http_method = Method::GET;
187 api_req.api_path = format!(
188 "/open-apis/comment/v1/comments/{}/replies?file_type={}&file_token={}",
189 request.comment_id, request.file_type, request.file_token
190 );
191
192 let mut query_params = Vec::new();
194 if let Some(page_size) = request.page_size {
195 query_params.push(format!("page_size={page_size}"));
196 }
197 if let Some(page_token) = request.page_token {
198 query_params.push(format!("page_token={page_token}"));
199 }
200 if let Some(user_id_type) = request.user_id_type {
201 query_params.push(format!("user_id_type={user_id_type}"));
202 }
203
204 if !query_params.is_empty() {
205 api_req.api_path = format!("{}&{}", api_req.api_path, query_params.join("&"));
206 }
207
208 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
209
210 let api_resp = Transport::request(api_req, config, option).await?;
211 Ok(api_resp)
212}
213
214impl ListRepliesResponse {
215 pub fn count(&self) -> usize {
217 self.items.len()
218 }
219
220 pub fn is_empty(&self) -> bool {
222 self.items.is_empty()
223 }
224
225 pub fn replies_by_user(&self, user_id: &str) -> Vec<&Reply> {
227 self.items
228 .iter()
229 .filter(|reply| reply.user_id == user_id)
230 .collect()
231 }
232
233 pub fn latest_reply(&self) -> Option<&Reply> {
235 self.items.iter().max_by_key(|reply| reply.create_time)
236 }
237
238 pub fn earliest_reply(&self) -> Option<&Reply> {
240 self.items.iter().min_by_key(|reply| reply.create_time)
241 }
242
243 pub fn sorted_by_time(&self) -> Vec<&Reply> {
245 let mut replies: Vec<&Reply> = self.items.iter().collect();
246 replies.sort_by_key(|reply| reply.create_time);
247 replies
248 }
249
250 pub fn sorted_by_time_desc(&self) -> Vec<&Reply> {
252 let mut replies: Vec<&Reply> = self.items.iter().collect();
253 replies.sort_by_key(|reply| std::cmp::Reverse(reply.create_time));
254 replies
255 }
256
257 pub fn get_all_text_content(&self) -> Vec<String> {
259 self.items
260 .iter()
261 .map(|reply| reply.get_text_content())
262 .collect()
263 }
264
265 pub fn summary(&self) -> String {
267 format!(
268 "回复总数: {}, 是否有更多: {}, 最新回复时间: {}",
269 self.count(),
270 self.has_more,
271 self.latest_reply()
272 .map(|r| r.create_time.to_string())
273 .unwrap_or_else(|| "无".to_string())
274 )
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn test_list_replies_request_builder() {
284 let request = ListRepliesRequest::builder()
285 .file_token("doccnxxxxxx")
286 .with_doc_type()
287 .comment_id("comment123")
288 .page_size(20)
289 .with_open_id()
290 .build();
291
292 assert_eq!(request.file_token, "doccnxxxxxx");
293 assert_eq!(request.file_type, "doc");
294 assert_eq!(request.comment_id, "comment123");
295 assert_eq!(request.page_size, Some(20));
296 assert_eq!(request.user_id_type, Some("open_id".to_string()));
297 }
298
299 #[test]
300 fn test_list_replies_new() {
301 let request = ListRepliesRequest::new("doccnxxxxxx", "doc", "comment123");
302 assert_eq!(request.file_token, "doccnxxxxxx");
303 assert_eq!(request.file_type, "doc");
304 assert_eq!(request.comment_id, "comment123");
305 }
306}