open_lark/service/cloud_docs/docx/v1/
document.rs

1use 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
17/// 文档服务
18pub struct DocumentService {
19    config: Config,
20}
21
22impl DocumentService {
23    pub fn new(config: Config) -> Self {
24        Self { config }
25    }
26
27    /// 创建文档
28    ///
29    /// 该接口用于创建一个新的文档。
30    ///
31    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document/create>
32    pub async fn create(
33        &self,
34        request: CreateDocumentRequest,
35        option: Option<RequestOption>,
36    ) -> SDKResult<BaseResponse<CreateDocumentRespData>> {
37        let api_req = ApiRequest {
38            http_method: Method::POST,
39            api_path: "/open-apis/docx/v1/documents".to_string(),
40            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
41            body: serde_json::to_vec(&request)?,
42            ..Default::default()
43        };
44
45        let api_resp = Transport::request(api_req, &self.config, option).await?;
46        Ok(api_resp)
47    }
48
49    /// 获取文档基本信息
50    ///
51    /// 该接口用于获取文档的基本信息。
52    ///
53    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document/get>
54    pub async fn get(
55        &self,
56        document_id: impl Into<String>,
57        option: Option<RequestOption>,
58    ) -> SDKResult<BaseResponse<GetDocumentRespData>> {
59        let api_req = ApiRequest {
60            http_method: Method::GET,
61            api_path: format!("/open-apis/docx/v1/documents/{}", document_id.into()),
62            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
63            ..Default::default()
64        };
65
66        let api_resp = Transport::request(api_req, &self.config, option).await?;
67        Ok(api_resp)
68    }
69
70    /// 获取文档纯文本内容
71    ///
72    /// 该接口用于获取文档的纯文本内容。
73    ///
74    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document/raw_content>
75    pub async fn get_raw_content(
76        &self,
77        document_id: impl Into<String>,
78        option: Option<RequestOption>,
79    ) -> SDKResult<BaseResponse<GetRawContentRespData>> {
80        let mut api_req = ApiRequest {
81            http_method: Method::GET,
82            api_path: format!(
83                "/open-apis/docx/v1/documents/{}/raw_content",
84                document_id.into()
85            ),
86            ..Default::default()
87        };
88        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
89
90        let api_resp = Transport::request(api_req, &self.config, option).await?;
91        Ok(api_resp)
92    }
93
94    /// 获取文档所有块
95    ///
96    /// 该接口用于获取文档的所有块。
97    ///
98    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document/list>
99    pub async fn list_blocks(
100        &self,
101        request: ListDocumentBlocksRequest,
102        option: Option<RequestOption>,
103    ) -> SDKResult<BaseResponse<ListDocumentBlocksRespData>> {
104        let mut api_req = ApiRequest {
105            http_method: Method::GET,
106            api_path: format!(
107                "/open-apis/docx/v1/documents/{}/blocks",
108                request.document_id
109            ),
110            ..Default::default()
111        };
112        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
113
114        // 添加查询参数
115        if let Some(page_size) = request.page_size {
116            api_req
117                .query_params
118                .insert("page_size".to_string(), page_size.to_string());
119        }
120        if let Some(page_token) = request.page_token {
121            api_req
122                .query_params
123                .insert("page_token".to_string(), page_token);
124        }
125
126        let api_resp = Transport::request(api_req, &self.config, option).await?;
127        Ok(api_resp)
128    }
129
130    /// 转换为文档块
131    ///
132    /// 该接口用于将旧版文档转换为新版文档块格式。
133    ///
134    /// <https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/document-docx/docx-v1/document/convert>
135    pub async fn convert_to_docx(
136        &self,
137        document_id: impl Into<String>,
138        option: Option<RequestOption>,
139    ) -> SDKResult<BaseResponse<ConvertToDocxRespData>> {
140        let mut api_req = ApiRequest {
141            http_method: Method::POST,
142            api_path: format!(
143                "/open-apis/docx/v1/documents/{}/convert",
144                document_id.into()
145            ),
146            ..Default::default()
147        };
148        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
149
150        let api_resp = Transport::request(api_req, &self.config, option).await?;
151        Ok(api_resp)
152    }
153}
154
155// === 数据结构定义 ===
156
157/// 创建文档请求参数
158#[derive(Debug, Clone, Serialize, Deserialize, Default)]
159pub struct CreateDocumentRequest {
160    /// 文档标题
161    pub title: String,
162    /// 文档内容,JSON格式的块内容
163    pub content: Option<String>,
164    /// 文件夹token
165    pub folder_token: Option<String>,
166}
167
168impl CreateDocumentRequest {
169    pub fn builder() -> CreateDocumentRequestBuilder {
170        CreateDocumentRequestBuilder::default()
171    }
172
173    pub fn new(title: impl Into<String>) -> Self {
174        Self {
175            title: title.into(),
176            content: None,
177            folder_token: None,
178        }
179    }
180
181    pub fn with_content(mut self, content: impl Into<String>) -> Self {
182        self.content = Some(content.into());
183        self
184    }
185
186    pub fn with_folder_token(mut self, folder_token: impl Into<String>) -> Self {
187        self.folder_token = Some(folder_token.into());
188        self
189    }
190}
191
192/// 创建文档请求构建器
193#[derive(Default)]
194pub struct CreateDocumentRequestBuilder {
195    request: CreateDocumentRequest,
196}
197
198impl CreateDocumentRequestBuilder {
199    pub fn title(mut self, title: impl Into<String>) -> Self {
200        self.request.title = title.into();
201        self
202    }
203
204    pub fn content(mut self, content: impl Into<String>) -> Self {
205        self.request.content = Some(content.into());
206        self
207    }
208
209    pub fn folder_token(mut self, folder_token: impl Into<String>) -> Self {
210        self.request.folder_token = Some(folder_token.into());
211        self
212    }
213
214    pub fn build(self) -> CreateDocumentRequest {
215        self.request
216    }
217}
218
219impl_executable_builder_owned!(
220    CreateDocumentRequestBuilder,
221    DocumentService,
222    CreateDocumentRequest,
223    BaseResponse<CreateDocumentRespData>,
224    create
225);
226
227/// 创建文档响应数据
228#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct CreateDocumentRespData {
230    /// 文档ID
231    pub document_id: String,
232    /// 文档版本ID
233    pub document_revision_id: i64,
234    /// 文档标题
235    pub title: String,
236}
237
238impl ApiResponseTrait for CreateDocumentRespData {
239    fn data_format() -> ResponseFormat {
240        ResponseFormat::Data
241    }
242}
243
244/// 获取文档信息响应数据
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct GetDocumentRespData {
247    /// 文档信息
248    pub document: DocumentInfo,
249}
250
251impl ApiResponseTrait for GetDocumentRespData {
252    fn data_format() -> ResponseFormat {
253        ResponseFormat::Data
254    }
255}
256
257/// 文档信息
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct DocumentInfo {
260    /// 文档ID
261    pub document_id: String,
262    /// 文档版本ID
263    pub document_revision_id: i64,
264    /// 文档标题
265    pub title: String,
266    /// 创建时间
267    pub create_time: String,
268    /// 更新时间
269    pub update_time: String,
270    /// 创建者ID
271    pub creator_id: String,
272    /// 最后编辑者ID
273    pub last_editor_id: String,
274}
275
276/// 获取纯文本内容响应数据
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct GetRawContentRespData {
279    /// 纯文本内容
280    pub content: String,
281}
282
283impl ApiResponseTrait for GetRawContentRespData {
284    fn data_format() -> ResponseFormat {
285        ResponseFormat::Data
286    }
287}
288
289/// 获取文档所有块请求参数
290#[derive(Debug, Clone, Serialize, Deserialize, Default)]
291pub struct ListDocumentBlocksRequest {
292    /// 文档ID
293    pub document_id: String,
294    /// 分页大小
295    pub page_size: Option<i32>,
296    /// 分页标记
297    pub page_token: Option<String>,
298}
299
300impl ListDocumentBlocksRequest {
301    pub fn builder() -> ListDocumentBlocksRequestBuilder {
302        ListDocumentBlocksRequestBuilder::default()
303    }
304
305    pub fn new(document_id: impl Into<String>) -> Self {
306        Self {
307            document_id: document_id.into(),
308            page_size: None,
309            page_token: None,
310        }
311    }
312
313    pub fn with_page_size(mut self, page_size: i32) -> Self {
314        self.page_size = Some(page_size);
315        self
316    }
317
318    pub fn with_page_token(mut self, page_token: impl Into<String>) -> Self {
319        self.page_token = Some(page_token.into());
320        self
321    }
322}
323
324/// 获取文档所有块请求构建器
325#[derive(Default)]
326pub struct ListDocumentBlocksRequestBuilder {
327    request: ListDocumentBlocksRequest,
328}
329
330impl ListDocumentBlocksRequestBuilder {
331    pub fn document_id(mut self, document_id: impl Into<String>) -> Self {
332        self.request.document_id = document_id.into();
333        self
334    }
335
336    pub fn page_size(mut self, page_size: i32) -> Self {
337        self.request.page_size = Some(page_size);
338        self
339    }
340
341    pub fn page_token(mut self, page_token: impl Into<String>) -> Self {
342        self.request.page_token = Some(page_token.into());
343        self
344    }
345
346    pub fn build(self) -> ListDocumentBlocksRequest {
347        self.request
348    }
349}
350
351impl_executable_builder_owned!(
352    ListDocumentBlocksRequestBuilder,
353    DocumentService,
354    ListDocumentBlocksRequest,
355    BaseResponse<ListDocumentBlocksRespData>,
356    list_blocks
357);
358
359/// 获取文档所有块响应数据
360#[derive(Debug, Clone, Serialize, Deserialize)]
361pub struct ListDocumentBlocksRespData {
362    /// 块列表
363    pub items: Vec<Block>,
364    /// 是否还有更多数据
365    pub has_more: bool,
366    /// 下一页标记
367    pub page_token: Option<String>,
368}
369
370impl ApiResponseTrait for ListDocumentBlocksRespData {
371    fn data_format() -> ResponseFormat {
372        ResponseFormat::Data
373    }
374}
375
376/// 块信息
377#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct Block {
379    /// 块ID
380    pub block_id: String,
381    /// 父块ID
382    pub parent_id: String,
383    /// 子块ID列表
384    pub children: Vec<String>,
385    /// 块类型
386    pub block_type: i32,
387    /// 块索引
388    pub index: i32,
389}
390
391/// 转换为文档块响应数据
392#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct ConvertToDocxRespData {
394    /// 新文档ID
395    pub document_id: String,
396    /// 文档版本ID
397    pub document_revision_id: i64,
398}
399
400impl ApiResponseTrait for ConvertToDocxRespData {
401    fn data_format() -> ResponseFormat {
402        ResponseFormat::Data
403    }
404}