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

1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5use crate::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
15/// 文档块服务
16pub struct DocumentBlockService {
17    config: Config,
18}
19
20impl DocumentBlockService {
21    pub fn new(config: Config) -> Self {
22        Self { config }
23    }
24
25    /// 创建块
26    ///
27    /// 该接口用于在文档中创建一个新的块。
28    ///
29    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document-block/create>
30    pub async fn create(
31        &self,
32        document_id: impl Into<String>,
33        request: CreateBlockRequest,
34        option: Option<RequestOption>,
35    ) -> SDKResult<BaseResponse<CreateBlockRespData>> {
36        let api_req = ApiRequest {
37            http_method: Method::POST,
38            api_path: format!("/open-apis/docx/v1/documents/{}/blocks", document_id.into()),
39            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
40            body: serde_json::to_vec(&request)?,
41            ..Default::default()
42        };
43
44        let api_resp = Transport::request(api_req, &self.config, option).await?;
45        Ok(api_resp)
46    }
47
48    /// 获取块的内容
49    ///
50    /// 该接口用于获取块的详细内容。
51    ///
52    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document-block/get>
53    pub async fn get(
54        &self,
55        document_id: impl Into<String>,
56        block_id: impl Into<String>,
57        option: Option<RequestOption>,
58    ) -> SDKResult<BaseResponse<GetBlockRespData>> {
59        let api_req = ApiRequest {
60            http_method: Method::GET,
61            api_path: format!(
62                "/open-apis/docx/v1/documents/{}/blocks/{}",
63                document_id.into(),
64                block_id.into()
65            ),
66            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
67            ..Default::default()
68        };
69
70        let api_resp = Transport::request(api_req, &self.config, option).await?;
71        Ok(api_resp)
72    }
73
74    /// 更新块的内容
75    ///
76    /// 该接口用于更新块的内容。
77    ///
78    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document-block/patch>
79    pub async fn patch(
80        &self,
81        document_id: impl Into<String>,
82        block_id: impl Into<String>,
83        request: PatchBlockRequest,
84        option: Option<RequestOption>,
85    ) -> SDKResult<BaseResponse<PatchBlockRespData>> {
86        let api_req = ApiRequest {
87            http_method: Method::PATCH,
88            api_path: format!(
89                "/open-apis/docx/v1/documents/{}/blocks/{}",
90                document_id.into(),
91                block_id.into()
92            ),
93            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
94            body: serde_json::to_vec(&request)?,
95            ..Default::default()
96        };
97
98        let api_resp = Transport::request(api_req, &self.config, option).await?;
99        Ok(api_resp)
100    }
101
102    /// 批量更新块的内容
103    ///
104    /// 该接口用于批量更新多个块的内容。
105    ///
106    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document-block/batch_update>
107    pub async fn batch_update(
108        &self,
109        document_id: impl Into<String>,
110        request: BatchUpdateBlockRequest,
111        option: Option<RequestOption>,
112    ) -> SDKResult<BaseResponse<BatchUpdateBlockRespData>> {
113        let mut api_req = ApiRequest {
114            http_method: Method::PATCH,
115            api_path: format!(
116                "/open-apis/docx/v1/documents/{}/blocks/batch_update",
117                document_id.into()
118            ),
119            ..Default::default()
120        };
121        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
122        api_req.body = serde_json::to_vec(&request)?;
123
124        let api_resp = Transport::request(api_req, &self.config, option).await?;
125        Ok(api_resp)
126    }
127
128    /// 删除块
129    ///
130    /// 该接口用于批量删除块。
131    ///
132    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document-block/batch_delete>
133    pub async fn batch_delete(
134        &self,
135        document_id: impl Into<String>,
136        request: BatchDeleteBlockRequest,
137        option: Option<RequestOption>,
138    ) -> SDKResult<BaseResponse<BatchDeleteBlockRespData>> {
139        let mut api_req = ApiRequest {
140            http_method: Method::DELETE,
141            api_path: format!(
142                "/open-apis/docx/v1/documents/{}/blocks/batch_delete",
143                document_id.into()
144            ),
145            ..Default::default()
146        };
147        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
148        api_req.body = serde_json::to_vec(&request)?;
149
150        let api_resp = Transport::request(api_req, &self.config, option).await?;
151        Ok(api_resp)
152    }
153
154    /// 获取所有子块
155    ///
156    /// 该接口用于获取指定块的所有子块。
157    ///
158    /// <https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document-block/get-2>
159    pub async fn list_children(
160        &self,
161        document_id: impl Into<String>,
162        block_id: impl Into<String>,
163        request: ListChildrenRequest,
164        option: Option<RequestOption>,
165    ) -> SDKResult<BaseResponse<ListChildrenRespData>> {
166        let mut api_req = ApiRequest {
167            http_method: Method::GET,
168            api_path: format!(
169                "/open-apis/docx/v1/documents/{}/blocks/{}/children",
170                document_id.into(),
171                block_id.into()
172            ),
173            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
174            ..Default::default()
175        };
176
177        // 添加查询参数
178        if let Some(page_size) = request.page_size {
179            api_req
180                .query_params
181                .insert("page_size".to_string(), page_size.to_string());
182        }
183        if let Some(page_token) = request.page_token {
184            api_req
185                .query_params
186                .insert("page_token".to_string(), page_token);
187        }
188
189        let api_resp = Transport::request(api_req, &self.config, option).await?;
190        Ok(api_resp)
191    }
192}
193
194// === 数据结构定义 ===
195
196/// 创建块请求参数
197#[derive(Debug, Clone, Serialize, Deserialize, Default)]
198pub struct CreateBlockRequest {
199    /// 父块ID,如果创建在文档根级,传document_id
200    pub parent_id: String,
201    /// 块索引位置
202    pub index: Option<i32>,
203    /// 块数据列表
204    pub blocks: Vec<BlockData>,
205}
206
207impl CreateBlockRequest {
208    pub fn builder() -> CreateBlockRequestBuilder {
209        CreateBlockRequestBuilder::default()
210    }
211
212    pub fn new(parent_id: impl Into<String>, blocks: Vec<BlockData>) -> Self {
213        Self {
214            parent_id: parent_id.into(),
215            index: None,
216            blocks,
217        }
218    }
219
220    pub fn with_index(mut self, index: i32) -> Self {
221        self.index = Some(index);
222        self
223    }
224}
225
226/// 创建块请求构建器
227#[derive(Default)]
228pub struct CreateBlockRequestBuilder {
229    request: CreateBlockRequest,
230    document_id: String,
231}
232
233impl CreateBlockRequestBuilder {
234    pub fn document_id(mut self, document_id: impl Into<String>) -> Self {
235        self.document_id = document_id.into();
236        self
237    }
238
239    pub fn parent_id(mut self, parent_id: impl Into<String>) -> Self {
240        self.request.parent_id = parent_id.into();
241        self
242    }
243
244    pub fn index(mut self, index: i32) -> Self {
245        self.request.index = Some(index);
246        self
247    }
248
249    pub fn blocks(mut self, blocks: Vec<BlockData>) -> Self {
250        self.request.blocks = blocks;
251        self
252    }
253
254    pub fn add_block(mut self, block: BlockData) -> Self {
255        self.request.blocks.push(block);
256        self
257    }
258
259    pub fn build(self) -> (String, CreateBlockRequest) {
260        (self.document_id, self.request)
261    }
262}
263
264/// 块数据
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct BlockData {
267    /// 块类型
268    pub block_type: i32,
269    /// 块内容(根据不同类型有不同结构)
270    pub block: Value,
271}
272
273/// 创建块响应数据
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct CreateBlockRespData {
276    /// 创建的块列表
277    pub blocks: Vec<BlockInfo>,
278    /// 文档版本ID
279    pub document_revision_id: i64,
280}
281
282impl ApiResponseTrait for CreateBlockRespData {
283    fn data_format() -> ResponseFormat {
284        ResponseFormat::Data
285    }
286}
287
288/// 块信息
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct BlockInfo {
291    /// 块ID
292    pub block_id: String,
293    /// 父块ID
294    pub parent_id: String,
295    /// 子块ID列表
296    pub children: Vec<String>,
297    /// 块类型
298    pub block_type: i32,
299    /// 块索引
300    pub index: i32,
301}
302
303/// 获取块响应数据
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct GetBlockRespData {
306    /// 块信息
307    pub block: DetailedBlock,
308}
309
310impl ApiResponseTrait for GetBlockRespData {
311    fn data_format() -> ResponseFormat {
312        ResponseFormat::Data
313    }
314}
315
316/// 详细块信息
317#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct DetailedBlock {
319    /// 块ID
320    pub block_id: String,
321    /// 父块ID
322    pub parent_id: String,
323    /// 子块ID列表
324    pub children: Vec<String>,
325    /// 块类型
326    pub block_type: i32,
327    /// 块索引
328    pub index: i32,
329    /// 块内容
330    pub block: Value,
331}
332
333/// 更新块请求参数
334#[derive(Debug, Clone, Serialize, Deserialize)]
335pub struct PatchBlockRequest {
336    /// 要更新的块内容
337    pub block: Value,
338}
339
340impl PatchBlockRequest {
341    pub fn new(block: Value) -> Self {
342        Self { block }
343    }
344}
345
346/// 更新块响应数据
347#[derive(Debug, Clone, Serialize, Deserialize)]
348pub struct PatchBlockRespData {
349    /// 更新后的块信息
350    pub block: DetailedBlock,
351    /// 文档版本ID
352    pub document_revision_id: i64,
353}
354
355impl ApiResponseTrait for PatchBlockRespData {
356    fn data_format() -> ResponseFormat {
357        ResponseFormat::Data
358    }
359}
360
361/// 批量更新块请求参数
362#[derive(Debug, Clone, Serialize, Deserialize, Default)]
363pub struct BatchUpdateBlockRequest {
364    /// 要更新的块列表
365    pub requests: Vec<UpdateBlockItem>,
366}
367
368/// 更新块项
369#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct UpdateBlockItem {
371    /// 块ID
372    pub block_id: String,
373    /// 要更新的块内容
374    pub block: Value,
375}
376
377impl BatchUpdateBlockRequest {
378    pub fn builder() -> BatchUpdateBlockRequestBuilder {
379        BatchUpdateBlockRequestBuilder::default()
380    }
381
382    pub fn new(requests: Vec<UpdateBlockItem>) -> Self {
383        Self { requests }
384    }
385}
386
387/// 批量更新块请求构建器
388#[derive(Default)]
389pub struct BatchUpdateBlockRequestBuilder {
390    request: BatchUpdateBlockRequest,
391    document_id: String,
392}
393
394impl BatchUpdateBlockRequestBuilder {
395    pub fn document_id(mut self, document_id: impl Into<String>) -> Self {
396        self.document_id = document_id.into();
397        self
398    }
399
400    pub fn requests(mut self, requests: Vec<UpdateBlockItem>) -> Self {
401        self.request.requests = requests;
402        self
403    }
404
405    pub fn add_request(mut self, block_id: impl Into<String>, block: Value) -> Self {
406        self.request.requests.push(UpdateBlockItem {
407            block_id: block_id.into(),
408            block,
409        });
410        self
411    }
412
413    pub fn build(self) -> (String, BatchUpdateBlockRequest) {
414        (self.document_id, self.request)
415    }
416}
417
418/// 批量更新块响应数据
419#[derive(Debug, Clone, Serialize, Deserialize)]
420pub struct BatchUpdateBlockRespData {
421    /// 更新的块列表
422    pub blocks: Vec<DetailedBlock>,
423    /// 文档版本ID
424    pub document_revision_id: i64,
425}
426
427impl ApiResponseTrait for BatchUpdateBlockRespData {
428    fn data_format() -> ResponseFormat {
429        ResponseFormat::Data
430    }
431}
432
433/// 批量删除块请求参数
434#[derive(Debug, Clone, Serialize, Deserialize, Default)]
435pub struct BatchDeleteBlockRequest {
436    /// 要删除的块ID列表
437    pub block_ids: Vec<String>,
438}
439
440impl BatchDeleteBlockRequest {
441    pub fn builder() -> BatchDeleteBlockRequestBuilder {
442        BatchDeleteBlockRequestBuilder::default()
443    }
444
445    pub fn new(block_ids: Vec<String>) -> Self {
446        Self { block_ids }
447    }
448}
449
450/// 批量删除块请求构建器
451#[derive(Default)]
452pub struct BatchDeleteBlockRequestBuilder {
453    request: BatchDeleteBlockRequest,
454    document_id: String,
455}
456
457impl BatchDeleteBlockRequestBuilder {
458    pub fn document_id(mut self, document_id: impl Into<String>) -> Self {
459        self.document_id = document_id.into();
460        self
461    }
462
463    pub fn block_ids(mut self, block_ids: Vec<String>) -> Self {
464        self.request.block_ids = block_ids;
465        self
466    }
467
468    pub fn add_block_id(mut self, block_id: impl Into<String>) -> Self {
469        self.request.block_ids.push(block_id.into());
470        self
471    }
472
473    pub fn build(self) -> (String, BatchDeleteBlockRequest) {
474        (self.document_id, self.request)
475    }
476}
477
478/// 批量删除块响应数据
479#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct BatchDeleteBlockRespData {
481    /// 文档版本ID
482    pub document_revision_id: i64,
483}
484
485impl ApiResponseTrait for BatchDeleteBlockRespData {
486    fn data_format() -> ResponseFormat {
487        ResponseFormat::Data
488    }
489}
490
491/// 获取子块请求参数
492#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct ListChildrenRequest {
494    /// 分页大小
495    pub page_size: Option<i32>,
496    /// 分页标记
497    pub page_token: Option<String>,
498}
499
500impl ListChildrenRequest {
501    pub fn new() -> Self {
502        Self {
503            page_size: None,
504            page_token: None,
505        }
506    }
507
508    pub fn with_page_size(mut self, page_size: i32) -> Self {
509        self.page_size = Some(page_size);
510        self
511    }
512
513    pub fn with_page_token(mut self, page_token: impl Into<String>) -> Self {
514        self.page_token = Some(page_token.into());
515        self
516    }
517}
518
519impl Default for ListChildrenRequest {
520    fn default() -> Self {
521        Self::new()
522    }
523}
524
525/// 获取子块响应数据
526#[derive(Debug, Clone, Serialize, Deserialize)]
527pub struct ListChildrenRespData {
528    /// 子块列表
529    pub items: Vec<DetailedBlock>,
530    /// 是否还有更多数据
531    pub has_more: bool,
532    /// 下一页标记
533    pub page_token: Option<String>,
534}
535
536impl ApiResponseTrait for ListChildrenRespData {
537    fn data_format() -> ResponseFormat {
538        ResponseFormat::Data
539    }
540}
541
542// === Builder execute方法实现 ===
543// 为需要路径参数的Builder提供统一的execute方法
544
545macro_rules! impl_execute_with_path {
546    ($builder:ty, $response:ty, $method:ident) => {
547        impl $builder {
548            /// 执行请求
549            pub async fn execute(
550                self,
551                service: &DocumentBlockService,
552                option: Option<RequestOption>,
553            ) -> SDKResult<$response> {
554                let (document_id, request) = self.build();
555                service.$method(document_id, request, option).await
556            }
557
558            /// 执行请求(带选项)
559            pub async fn execute_with_options(
560                self,
561                service: &DocumentBlockService,
562                option: RequestOption,
563            ) -> SDKResult<$response> {
564                self.execute(service, Some(option)).await
565            }
566        }
567    };
568}
569
570impl_execute_with_path!(
571    CreateBlockRequestBuilder,
572    BaseResponse<CreateBlockRespData>,
573    create
574);
575
576impl_execute_with_path!(
577    BatchUpdateBlockRequestBuilder,
578    BaseResponse<BatchUpdateBlockRespData>,
579    batch_update
580);
581
582impl_execute_with_path!(
583    BatchDeleteBlockRequestBuilder,
584    BaseResponse<BatchDeleteBlockRespData>,
585    batch_delete
586);