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