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