open_lark/service/cloud_docs/drive/v1/
folder.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 FolderService {
19    config: Config,
20}
21
22impl FolderService {
23    pub fn new(config: Config) -> Self {
24        Self { config }
25    }
26
27    /// 获取我的空间(root folder)元数据
28    ///
29    /// 该接口用于根据用户的访问凭证获取用户的根目录信息,包括根目录的token等。
30    ///
31    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/get-root-folder-meta>
32    pub async fn get_root_folder_meta(
33        &self,
34        option: Option<RequestOption>,
35    ) -> SDKResult<BaseResponse<GetRootFolderMetaRespData>> {
36        let api_req = ApiRequest {
37            http_method: Method::GET,
38            api_path: "/open-apis/drive/v1/folders/root_folder_meta".to_string(),
39            supported_access_token_types: vec![AccessTokenType::User],
40            ..Default::default()
41        };
42
43        let api_resp = Transport::request(api_req, &self.config, option).await?;
44        Ok(api_resp)
45    }
46
47    /// 获取文件夹中的文件清单
48    ///
49    /// 该接口用于根据文件夹的token获取文件夹中的文件清单。
50    ///
51    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/list>
52    pub async fn list_files(
53        &self,
54        request: ListFilesRequest,
55        option: Option<RequestOption>,
56    ) -> SDKResult<BaseResponse<ListFilesRespData>> {
57        let mut api_req = ApiRequest {
58            http_method: Method::GET,
59            api_path: format!(
60                "/open-apis/drive/v1/folders/{}/children",
61                request.folder_token
62            ),
63            ..Default::default()
64        };
65        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
66
67        // 添加查询参数
68        if let Some(page_token) = request.page_token {
69            api_req
70                .query_params
71                .insert("page_token".to_string(), page_token);
72        }
73        if let Some(page_size) = request.page_size {
74            api_req
75                .query_params
76                .insert("page_size".to_string(), page_size.to_string());
77        }
78        if let Some(order_by) = request.order_by {
79            api_req
80                .query_params
81                .insert("order_by".to_string(), order_by);
82        }
83        if let Some(direction) = request.direction {
84            api_req
85                .query_params
86                .insert("direction".to_string(), direction);
87        }
88
89        let api_resp = Transport::request(api_req, &self.config, option).await?;
90        Ok(api_resp)
91    }
92
93    /// 获取文件夹元数据
94    ///
95    /// 该接口用于根据文件夹的token获取文件夹的详细元数据信息。
96    ///
97    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/get-folder-meta>
98    pub async fn get_folder_meta(
99        &self,
100        request: GetFolderMetaRequest,
101        option: Option<RequestOption>,
102    ) -> SDKResult<BaseResponse<GetFolderMetaRespData>> {
103        let api_req = ApiRequest {
104            http_method: Method::GET,
105            api_path: format!("/open-apis/drive/v1/folders/{}", request.folder_token),
106            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
107            ..Default::default()
108        };
109
110        let api_resp = Transport::request(api_req, &self.config, option).await?;
111        Ok(api_resp)
112    }
113
114    /// 新建文件夹
115    ///
116    /// 该接口用于根据父文件夹的token在其中创建一个新的空文件夹。
117    ///
118    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/create_folder>
119    pub async fn create_folder(
120        &self,
121        request: CreateFolderRequest,
122        option: Option<RequestOption>,
123    ) -> SDKResult<BaseResponse<CreateFolderRespData>> {
124        let api_req = ApiRequest {
125            http_method: Method::POST,
126            api_path: "/open-apis/drive/v1/folders".to_string(),
127            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
128            body: serde_json::to_vec(&request)?,
129            ..Default::default()
130        };
131
132        let api_resp = Transport::request(api_req, &self.config, option).await?;
133        Ok(api_resp)
134    }
135
136    /// 移动或删除文件夹
137    ///
138    /// 该接口用于根据文件夹的token移动或删除文件夹。
139    ///
140    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/move-delete-folder>
141    pub async fn move_or_delete_folder(
142        &self,
143        request: MoveOrDeleteFolderRequest,
144        option: Option<RequestOption>,
145    ) -> SDKResult<BaseResponse<MoveOrDeleteFolderRespData>> {
146        let mut api_req = ApiRequest {
147            http_method: Method::POST,
148            api_path: format!("/open-apis/drive/v1/folders/{}/move", request.folder_token),
149            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
150            ..Default::default()
151        };
152
153        // 构建请求体,只包含需要的字段
154        let body = serde_json::json!({
155            "type": request.operation_type,
156            "parent_token": request.parent_token
157        });
158        api_req.body = serde_json::to_vec(&body)?;
159
160        let api_resp = Transport::request(api_req, &self.config, option).await?;
161        Ok(api_resp)
162    }
163
164    /// 查询异步任务状态
165    ///
166    /// 该接口用于查询异步任务的执行状态,如移动或删除文件夹等操作。
167    ///
168    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/file/async-task/task_check>
169    pub async fn check_async_task(
170        &self,
171        request: CheckAsyncTaskRequest,
172        option: Option<RequestOption>,
173    ) -> SDKResult<BaseResponse<CheckAsyncTaskRespData>> {
174        let api_req = ApiRequest {
175            http_method: Method::GET,
176            api_path: format!("/open-apis/drive/v1/tasks/{}", request.task_id),
177            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
178            ..Default::default()
179        };
180
181        let api_resp = Transport::request(api_req, &self.config, option).await?;
182        Ok(api_resp)
183    }
184}
185
186/// 获取我的空间(root folder)元数据响应数据
187#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct GetRootFolderMetaRespData {
189    /// 用户空间的根目录 token
190    pub token: String,
191    /// 用户 ID
192    pub user_id: String,
193}
194
195impl ApiResponseTrait for GetRootFolderMetaRespData {
196    fn data_format() -> ResponseFormat {
197        ResponseFormat::Data
198    }
199}
200
201/// 获取文件夹中的文件清单请求参数
202#[derive(Debug, Clone, Default, Serialize, Deserialize)]
203pub struct ListFilesRequest {
204    /// 文件夹的token
205    pub folder_token: String,
206    /// 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果
207    pub page_token: Option<String>,
208    /// 分页大小,最大200
209    pub page_size: Option<i32>,
210    /// 排序字段,支持:创建时间(created_time)、修改时间(edited_time)、文件类型(file_type)、大小(size)
211    pub order_by: Option<String>,
212    /// 排序方向,支持:升序(ASC)、降序(DESC)
213    pub direction: Option<String>,
214}
215
216impl ListFilesRequest {
217    pub fn new(folder_token: impl Into<String>) -> Self {
218        Self {
219            folder_token: folder_token.into(),
220            ..Default::default()
221        }
222    }
223
224    pub fn builder() -> ListFilesRequestBuilder {
225        ListFilesRequestBuilder::default()
226    }
227}
228
229/// 获取文件夹中的文件清单请求构建器
230#[derive(Debug, Clone, Default)]
231pub struct ListFilesRequestBuilder {
232    request: ListFilesRequest,
233}
234
235impl ListFilesRequestBuilder {
236    pub fn folder_token(mut self, folder_token: impl Into<String>) -> Self {
237        self.request.folder_token = folder_token.into();
238        self
239    }
240
241    pub fn page_token(mut self, page_token: impl Into<String>) -> Self {
242        self.request.page_token = Some(page_token.into());
243        self
244    }
245
246    pub fn page_size(mut self, page_size: i32) -> Self {
247        self.request.page_size = Some(page_size);
248        self
249    }
250
251    pub fn order_by(mut self, order_by: impl Into<String>) -> Self {
252        self.request.order_by = Some(order_by.into());
253        self
254    }
255
256    pub fn direction(mut self, direction: impl Into<String>) -> Self {
257        self.request.direction = Some(direction.into());
258        self
259    }
260
261    pub fn build(self) -> ListFilesRequest {
262        self.request
263    }
264}
265
266/// 获取文件夹中的文件清单响应数据
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct ListFilesRespData {
269    /// 是否还有更多项
270    pub has_more: bool,
271    /// 分页标记,当 has_more 为 true 时,会返回新的 page_token,否则不返回 page_token
272    pub page_token: Option<String>,
273    /// 文件清单
274    pub files: Vec<DriveFile>,
275}
276
277impl ApiResponseTrait for ListFilesRespData {
278    fn data_format() -> ResponseFormat {
279        ResponseFormat::Data
280    }
281}
282
283/// 驱动文件信息
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct DriveFile {
286    /// 文件的token
287    pub token: String,
288    /// 文件名
289    pub name: String,
290    /// 文件类型
291    #[serde(rename = "type")]
292    pub file_type: String,
293    /// 父文件夹token
294    pub parent_token: Option<String>,
295    /// 文件链接
296    pub url: Option<String>,
297    /// 文件短链接
298    pub short_url: Option<String>,
299    /// 文件大小(字节)
300    pub size: Option<i64>,
301    /// 文件mime类型
302    pub mime_type: Option<String>,
303    /// 创建时间
304    pub created_time: Option<String>,
305    /// 修改时间
306    pub modified_time: Option<String>,
307    /// 拥有者id
308    pub owner_id: Option<String>,
309}
310
311/// 获取文件夹元数据请求参数
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct GetFolderMetaRequest {
314    /// 文件夹的token
315    pub folder_token: String,
316}
317
318impl GetFolderMetaRequest {
319    pub fn new(folder_token: impl Into<String>) -> Self {
320        Self {
321            folder_token: folder_token.into(),
322        }
323    }
324}
325
326/// 获取文件夹元数据响应数据
327#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct GetFolderMetaRespData {
329    /// 文件夹token
330    pub token: String,
331    /// 文件夹ID
332    pub id: String,
333    /// 文件夹名称
334    pub name: String,
335    /// 父文件夹token
336    pub parent_token: Option<String>,
337    /// 拥有者ID
338    pub owner_id: String,
339    /// 创建者ID
340    pub creator_id: Option<String>,
341    /// 创建时间
342    pub create_time: String,
343    /// 修改时间
344    pub edit_time: String,
345    /// 文件夹描述
346    pub description: Option<String>,
347    /// 文件夹链接
348    pub url: String,
349}
350
351impl ApiResponseTrait for GetFolderMetaRespData {
352    fn data_format() -> ResponseFormat {
353        ResponseFormat::Data
354    }
355}
356
357/// 新建文件夹请求参数
358#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct CreateFolderRequest {
360    /// 文件夹名称
361    pub name: String,
362    /// 父文件夹token
363    pub parent_token: String,
364}
365
366impl CreateFolderRequest {
367    pub fn new(name: impl Into<String>, parent_token: impl Into<String>) -> Self {
368        Self {
369            name: name.into(),
370            parent_token: parent_token.into(),
371        }
372    }
373}
374
375/// 新建文件夹响应数据
376#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct CreateFolderRespData {
378    /// 新创建文件夹的token
379    pub token: String,
380    /// 新创建文件夹的链接
381    pub url: String,
382}
383
384impl ApiResponseTrait for CreateFolderRespData {
385    fn data_format() -> ResponseFormat {
386        ResponseFormat::Data
387    }
388}
389
390/// 移动或删除文件夹请求参数
391#[derive(Debug, Clone, Serialize, Deserialize)]
392pub struct MoveOrDeleteFolderRequest {
393    /// 文件夹token
394    pub folder_token: String,
395    /// 操作类型,move: 移动,delete: 删除
396    #[serde(rename = "type")]
397    pub operation_type: String,
398    /// 移动的目标父文件夹token(删除操作时可以为空)
399    pub parent_token: Option<String>,
400}
401
402impl MoveOrDeleteFolderRequest {
403    /// 创建移动文件夹的请求
404    pub fn move_folder(folder_token: impl Into<String>, parent_token: impl Into<String>) -> Self {
405        Self {
406            folder_token: folder_token.into(),
407            operation_type: "move".to_string(),
408            parent_token: Some(parent_token.into()),
409        }
410    }
411
412    /// 创建删除文件夹的请求
413    pub fn delete_folder(folder_token: impl Into<String>) -> Self {
414        Self {
415            folder_token: folder_token.into(),
416            operation_type: "delete".to_string(),
417            parent_token: None,
418        }
419    }
420}
421
422/// 移动或删除文件夹响应数据
423#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct MoveOrDeleteFolderRespData {
425    /// 异步任务ID,可以通过该ID查询任务执行状态
426    pub task_id: Option<String>,
427}
428
429impl ApiResponseTrait for MoveOrDeleteFolderRespData {
430    fn data_format() -> ResponseFormat {
431        ResponseFormat::Data
432    }
433}
434
435/// 查询异步任务状态请求参数
436#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct CheckAsyncTaskRequest {
438    /// 任务ID
439    pub task_id: String,
440}
441
442impl CheckAsyncTaskRequest {
443    pub fn new(task_id: impl Into<String>) -> Self {
444        Self {
445            task_id: task_id.into(),
446        }
447    }
448}
449
450/// 查询异步任务状态响应数据
451#[derive(Debug, Clone, Serialize, Deserialize)]
452pub struct CheckAsyncTaskRespData {
453    /// 任务状态,PENDING: 等待中,SUCCESS: 成功,FAILURE: 失败
454    pub status: String,
455    /// 任务错误信息(如果失败)
456    pub error_msg: Option<String>,
457}
458
459impl ApiResponseTrait for CheckAsyncTaskRespData {
460    fn data_format() -> ResponseFormat {
461        ResponseFormat::Data
462    }
463}
464
465// === 宏实现 ===
466
467impl_executable_builder_owned!(
468    ListFilesRequestBuilder,
469    FolderService,
470    ListFilesRequest,
471    BaseResponse<ListFilesRespData>,
472    list_files
473);