open_lark/service/cloud_docs/comments/
create.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::{ContentElement, ReplyContent, TextRun};
18
19#[derive(Debug, Serialize, Default, Clone)]
21pub struct CreateCommentRequest {
22 #[serde(skip)]
23 api_request: ApiRequest,
24 #[serde(skip)]
26 file_token: String,
27 #[serde(skip)]
29 file_type: String,
30 content: ReplyContent,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 user_id_type: Option<String>,
35}
36
37impl CreateCommentRequest {
38 pub fn builder() -> CreateCommentRequestBuilder {
39 CreateCommentRequestBuilder::default()
40 }
41
42 pub fn new(file_token: impl ToString, file_type: impl ToString, content: ReplyContent) -> Self {
43 Self {
44 file_token: file_token.to_string(),
45 file_type: file_type.to_string(),
46 content,
47 ..Default::default()
48 }
49 }
50
51 pub fn with_text(
53 file_token: impl ToString,
54 file_type: impl ToString,
55 text: impl ToString,
56 ) -> Self {
57 let content = ReplyContent {
58 elements: vec![ContentElement {
59 element_type: "text_run".to_string(),
60 text_run: Some(TextRun {
61 text: text.to_string(),
62 style: None,
63 }),
64 }],
65 };
66
67 Self::new(file_token, file_type, content)
68 }
69}
70
71#[derive(Default)]
72pub struct CreateCommentRequestBuilder {
73 request: CreateCommentRequest,
74}
75
76impl CreateCommentRequestBuilder {
77 pub fn file_token(mut self, file_token: impl ToString) -> Self {
79 self.request.file_token = file_token.to_string();
80 self
81 }
82
83 pub fn file_type(mut self, file_type: impl ToString) -> Self {
85 self.request.file_type = file_type.to_string();
86 self
87 }
88
89 pub fn with_doc_type(mut self) -> Self {
91 self.request.file_type = "doc".to_string();
92 self
93 }
94
95 pub fn with_docx_type(mut self) -> Self {
97 self.request.file_type = "docx".to_string();
98 self
99 }
100
101 pub fn with_sheet_type(mut self) -> Self {
103 self.request.file_type = "sheet".to_string();
104 self
105 }
106
107 pub fn with_bitable_type(mut self) -> Self {
109 self.request.file_type = "bitable".to_string();
110 self
111 }
112
113 pub fn content(mut self, content: ReplyContent) -> Self {
115 self.request.content = content;
116 self
117 }
118
119 pub fn text(mut self, text: impl ToString) -> Self {
121 let element = ContentElement {
122 element_type: "text_run".to_string(),
123 text_run: Some(TextRun {
124 text: text.to_string(),
125 style: None,
126 }),
127 };
128 self.request.content.elements.push(element);
129 self
130 }
131
132 pub fn styled_text(mut self, text: impl ToString, style: serde_json::Value) -> Self {
134 let element = ContentElement {
135 element_type: "text_run".to_string(),
136 text_run: Some(TextRun {
137 text: text.to_string(),
138 style: Some(style),
139 }),
140 };
141 self.request.content.elements.push(element);
142 self
143 }
144
145 pub fn bold_text(self, text: impl ToString) -> Self {
147 let style = serde_json::json!({
148 "bold": true
149 });
150 self.styled_text(text, style)
151 }
152
153 pub fn italic_text(self, text: impl ToString) -> Self {
155 let style = serde_json::json!({
156 "italic": true
157 });
158 self.styled_text(text, style)
159 }
160
161 pub fn user_id_type(mut self, user_id_type: impl ToString) -> Self {
163 self.request.user_id_type = Some(user_id_type.to_string());
164 self
165 }
166
167 pub fn with_open_id(mut self) -> Self {
169 self.request.user_id_type = Some("open_id".to_string());
170 self
171 }
172
173 pub fn with_user_id(mut self) -> Self {
175 self.request.user_id_type = Some("user_id".to_string());
176 self
177 }
178
179 pub fn with_union_id(mut self) -> Self {
181 self.request.user_id_type = Some("union_id".to_string());
182 self
183 }
184
185 pub fn build(mut self) -> CreateCommentRequest {
186 self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
187 self.request
188 }
189}
190
191#[derive(Debug, Deserialize)]
193pub struct CreatedComment {
194 pub comment_id: String,
196 pub user_id: String,
198 pub create_time: i64,
200 pub update_time: i64,
202 pub is_solved: bool,
204 pub is_whole: Option<bool>,
206}
207
208impl_executable_builder_owned!(
210 CreateCommentRequestBuilder,
211 super::CommentsService,
212 CreateCommentRequest,
213 BaseResponse<CreateCommentResponse>,
214 create
215);
216
217#[derive(Debug, Deserialize)]
219pub struct CreateCommentResponse {
220 pub comment: CreatedComment,
222}
223
224impl ApiResponseTrait for CreateCommentResponse {
225 fn data_format() -> ResponseFormat {
226 ResponseFormat::Data
227 }
228}
229
230pub async fn create_comment(
234 request: CreateCommentRequest,
235 config: &Config,
236 option: Option<RequestOption>,
237) -> SDKResult<BaseResponse<CreateCommentResponse>> {
238 let mut api_req = request.api_request;
239 api_req.http_method = Method::POST;
240 api_req.api_path = format!(
241 "/open-apis/comment/v1/comments?file_type={}&file_token={}",
242 request.file_type, request.file_token
243 );
244
245 if let Some(user_id_type) = request.user_id_type {
247 api_req.api_path = format!("{}&user_id_type={}", api_req.api_path, user_id_type);
248 }
249
250 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
251
252 let api_resp = Transport::request(api_req, config, option).await?;
253 Ok(api_resp)
254}
255
256impl CreatedComment {
257 pub fn is_whole_comment(&self) -> bool {
259 self.is_whole.unwrap_or(false)
260 }
261
262 pub fn created_at_formatted(&self) -> String {
264 format!("创建时间: {}", self.create_time)
265 }
266
267 pub fn is_solved(&self) -> bool {
269 self.is_solved
270 }
271}
272
273pub struct ContentBuilder {
275 elements: Vec<ContentElement>,
276}
277
278impl ContentBuilder {
279 pub fn new() -> Self {
280 Self {
281 elements: Vec::new(),
282 }
283 }
284
285 pub fn add_text(mut self, text: impl ToString) -> Self {
287 self.elements.push(ContentElement {
288 element_type: "text_run".to_string(),
289 text_run: Some(TextRun {
290 text: text.to_string(),
291 style: None,
292 }),
293 });
294 self
295 }
296
297 pub fn add_styled_text(mut self, text: impl ToString, style: serde_json::Value) -> Self {
299 self.elements.push(ContentElement {
300 element_type: "text_run".to_string(),
301 text_run: Some(TextRun {
302 text: text.to_string(),
303 style: Some(style),
304 }),
305 });
306 self
307 }
308
309 pub fn add_bold(self, text: impl ToString) -> Self {
311 let style = serde_json::json!({ "bold": true });
312 self.add_styled_text(text, style)
313 }
314
315 pub fn add_italic(self, text: impl ToString) -> Self {
317 let style = serde_json::json!({ "italic": true });
318 self.add_styled_text(text, style)
319 }
320
321 pub fn add_underline(self, text: impl ToString) -> Self {
323 let style = serde_json::json!({ "underline": true });
324 self.add_styled_text(text, style)
325 }
326
327 pub fn build(self) -> ReplyContent {
329 ReplyContent {
330 elements: self.elements,
331 }
332 }
333}
334
335impl Default for ContentBuilder {
336 fn default() -> Self {
337 Self::new()
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn test_create_comment_request_builder() {
347 let request = CreateCommentRequest::builder()
348 .file_token("doccnxxxxxx")
349 .with_doc_type()
350 .text("这是一条评论")
351 .bold_text("重要内容")
352 .with_open_id()
353 .build();
354
355 assert_eq!(request.file_token, "doccnxxxxxx");
356 assert_eq!(request.file_type, "doc");
357 assert_eq!(request.content.elements.len(), 2);
358 assert_eq!(request.user_id_type, Some("open_id".to_string()));
359 }
360
361 #[test]
362 fn test_create_comment_with_text() {
363 let request = CreateCommentRequest::with_text("doccnxxxxxx", "doc", "简单评论");
364 assert_eq!(request.file_token, "doccnxxxxxx");
365 assert_eq!(request.file_type, "doc");
366 assert_eq!(request.content.elements.len(), 1);
367 }
368
369 #[test]
370 fn test_content_builder() {
371 let content = ContentBuilder::new()
372 .add_text("普通文本 ")
373 .add_bold("粗体文本 ")
374 .add_italic("斜体文本")
375 .build();
376
377 assert_eq!(content.elements.len(), 3);
378 assert_eq!(content.elements[0].element_type, "text_run");
379 }
380}