open_lark/service/cloud_docs/drive/v1/
media.rs

1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    core::{
6        api_req::ApiRequest,
7        api_resp::{ApiResponseTrait, BaseResponse, BinaryResponse, 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 MediaService {
19    config: Config,
20}
21
22impl MediaService {
23    pub fn new(config: Config) -> Self {
24        Self { config }
25    }
26
27    /// 上传素材
28    ///
29    /// 该接口用于上传素材文件。
30    ///
31    /// <https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/upload_all>
32    pub async fn upload_all(
33        &self,
34        request: UploadMediaRequest,
35        option: Option<RequestOption>,
36    ) -> SDKResult<BaseResponse<UploadMediaRespData>> {
37        let mut api_req = request.api_req;
38        api_req.http_method = Method::POST;
39        api_req.api_path = "/open-apis/drive/v1/medias/upload_all".to_string();
40        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
41
42        let api_resp = Transport::request(api_req, &self.config, option).await?;
43        Ok(api_resp)
44    }
45
46    /// 分片上传素材-预上传
47    ///
48    /// 该接口用于分片上传的预上传步骤。
49    ///
50    /// <https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/upload_prepare>
51    pub async fn upload_prepare(
52        &self,
53        request: UploadPrepareRequest,
54        option: Option<RequestOption>,
55    ) -> SDKResult<BaseResponse<UploadPrepareRespData>> {
56        let api_req = ApiRequest {
57            http_method: Method::POST,
58            api_path: "/open-apis/drive/v1/medias/upload_prepare".to_string(),
59            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
60            body: serde_json::to_vec(&request)?,
61            ..Default::default()
62        };
63
64        let api_resp = Transport::request(api_req, &self.config, option).await?;
65        Ok(api_resp)
66    }
67
68    /// 分片上传素材-上传分片
69    ///
70    /// 该接口用于上传文件分片。
71    ///
72    /// <https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/upload_part>
73    pub async fn upload_part(
74        &self,
75        request: UploadPartRequest,
76        option: Option<RequestOption>,
77    ) -> SDKResult<BaseResponse<UploadPartRespData>> {
78        let mut api_req = request.api_req;
79        api_req.http_method = Method::POST;
80        api_req.api_path = "/open-apis/drive/v1/medias/upload_part".to_string();
81        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
82
83        let api_resp = Transport::request(api_req, &self.config, option).await?;
84        Ok(api_resp)
85    }
86
87    /// 分片上传素材-完成上传
88    ///
89    /// 该接口用于完成分片上传。
90    ///
91    /// <https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/upload_finish>
92    pub async fn upload_finish(
93        &self,
94        request: UploadFinishRequest,
95        option: Option<RequestOption>,
96    ) -> SDKResult<BaseResponse<UploadFinishRespData>> {
97        let api_req = ApiRequest {
98            http_method: Method::POST,
99            api_path: "/open-apis/drive/v1/medias/upload_finish".to_string(),
100            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
101            body: serde_json::to_vec(&request)?,
102            ..Default::default()
103        };
104
105        let api_resp = Transport::request(api_req, &self.config, option).await?;
106        Ok(api_resp)
107    }
108
109    /// 下载素材
110    ///
111    /// 该接口用于下载素材文件。
112    ///
113    /// <https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/download>
114    pub async fn download(
115        &self,
116        request: DownloadMediaRequest,
117        option: Option<RequestOption>,
118    ) -> SDKResult<BaseResponse<BinaryResponse>> {
119        let api_req = ApiRequest {
120            http_method: Method::GET,
121            api_path: format!("/open-apis/drive/v1/medias/{}/download", request.file_token),
122            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
123            ..Default::default()
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/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/batch_get_tmp_download_url>
135    pub async fn batch_get_tmp_download_url(
136        &self,
137        request: BatchGetTmpDownloadUrlRequest,
138        option: Option<RequestOption>,
139    ) -> SDKResult<BaseResponse<BatchGetTmpDownloadUrlRespData>> {
140        let mut api_req = ApiRequest {
141            http_method: Method::GET,
142            api_path: "/open-apis/drive/v1/medias/batch_get_tmp_download_url".to_string(),
143            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
144            ..Default::default()
145        };
146
147        // 添加查询参数
148        let file_tokens = request.file_tokens.join(",");
149        api_req
150            .query_params
151            .insert("file_tokens".to_string(), file_tokens);
152
153        let api_resp = Transport::request(api_req, &self.config, option).await?;
154        Ok(api_resp)
155    }
156}
157
158// === 数据结构定义 ===
159
160/// 上传素材请求参数
161#[derive(Debug, Clone, Default, Serialize, Deserialize)]
162pub struct UploadMediaRequest {
163    /// 请求体
164    #[serde(skip)]
165    pub api_req: ApiRequest,
166    /// 素材名称
167    file_name: String,
168    /// 父文件夹token
169    parent_token: String,
170    /// 文件大小
171    size: i32,
172    /// 校验和(可选)
173    checksum: Option<String>,
174}
175
176impl UploadMediaRequest {
177    pub fn builder() -> UploadMediaRequestBuilder {
178        UploadMediaRequestBuilder::default()
179    }
180}
181
182/// 上传素材请求构建器
183#[derive(Default)]
184pub struct UploadMediaRequestBuilder {
185    request: UploadMediaRequest,
186}
187
188impl UploadMediaRequestBuilder {
189    pub fn file_name(mut self, file_name: impl ToString) -> Self {
190        self.request.file_name = file_name.to_string();
191        self
192    }
193
194    pub fn parent_token(mut self, parent_token: impl ToString) -> Self {
195        self.request.parent_token = parent_token.to_string();
196        self
197    }
198
199    pub fn size(mut self, size: i32) -> Self {
200        self.request.size = size;
201        self
202    }
203
204    pub fn checksum(mut self, checksum: impl ToString) -> Self {
205        self.request.checksum = Some(checksum.to_string());
206        self
207    }
208
209    pub fn file(mut self, file: Vec<u8>) -> Self {
210        self.request.api_req.file = file;
211        self
212    }
213
214    pub fn build(mut self) -> UploadMediaRequest {
215        self.request.api_req.body = serde_json::to_vec(&self.request).unwrap();
216        self.request
217    }
218}
219
220/// 上传素材响应数据
221#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct UploadMediaRespData {
223    /// 素材token
224    pub file_token: String,
225}
226
227impl ApiResponseTrait for UploadMediaRespData {
228    fn data_format() -> ResponseFormat {
229        ResponseFormat::Data
230    }
231}
232
233/// 分片上传预上传请求参数
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct UploadPrepareRequest {
236    /// 文件名称
237    pub file_name: String,
238    /// 父文件夹token
239    pub parent_token: String,
240    /// 文件大小
241    pub size: i64,
242    /// 分片大小(可选)
243    pub block_size: Option<i32>,
244    /// 文件校验和(可选)
245    pub checksum: Option<String>,
246}
247
248impl UploadPrepareRequest {
249    pub fn new(file_name: impl Into<String>, parent_token: impl Into<String>, size: i64) -> Self {
250        Self {
251            file_name: file_name.into(),
252            parent_token: parent_token.into(),
253            size,
254            block_size: None,
255            checksum: None,
256        }
257    }
258}
259
260/// 分片上传预上传响应数据
261#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct UploadPrepareRespData {
263    /// 上传事务ID
264    pub upload_id: String,
265    /// 分片大小
266    pub block_size: i32,
267    /// 分片数量
268    pub block_num: i32,
269}
270
271impl ApiResponseTrait for UploadPrepareRespData {
272    fn data_format() -> ResponseFormat {
273        ResponseFormat::Data
274    }
275}
276
277/// 上传分片请求参数
278#[derive(Debug, Clone, Default, Serialize, Deserialize)]
279pub struct UploadPartRequest {
280    /// 请求体
281    #[serde(skip)]
282    pub api_req: ApiRequest,
283    /// 上传事务ID
284    upload_id: String,
285    /// 分片序号
286    seq: i32,
287    /// 分片大小
288    size: i32,
289    /// 分片校验和(可选)
290    checksum: Option<String>,
291}
292
293impl UploadPartRequest {
294    pub fn builder() -> UploadPartRequestBuilder {
295        UploadPartRequestBuilder::default()
296    }
297}
298
299/// 上传分片请求构建器
300#[derive(Default)]
301pub struct UploadPartRequestBuilder {
302    request: UploadPartRequest,
303}
304
305impl UploadPartRequestBuilder {
306    pub fn upload_id(mut self, upload_id: impl ToString) -> Self {
307        self.request.upload_id = upload_id.to_string();
308        self
309    }
310
311    pub fn seq(mut self, seq: i32) -> Self {
312        self.request.seq = seq;
313        self
314    }
315
316    pub fn size(mut self, size: i32) -> Self {
317        self.request.size = size;
318        self
319    }
320
321    pub fn checksum(mut self, checksum: impl ToString) -> Self {
322        self.request.checksum = Some(checksum.to_string());
323        self
324    }
325
326    pub fn file_chunk(mut self, chunk: Vec<u8>) -> Self {
327        self.request.api_req.file = chunk;
328        self
329    }
330
331    pub fn build(mut self) -> UploadPartRequest {
332        self.request.api_req.body = serde_json::to_vec(&self.request).unwrap();
333        self.request
334    }
335}
336
337/// 上传分片响应数据
338#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct UploadPartRespData {
340    /// 分片ETag
341    pub etag: String,
342}
343
344impl ApiResponseTrait for UploadPartRespData {
345    fn data_format() -> ResponseFormat {
346        ResponseFormat::Data
347    }
348}
349
350/// 完成上传请求参数
351#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct UploadFinishRequest {
353    /// 上传事务ID
354    pub upload_id: String,
355    /// 分片信息列表
356    pub block_infos: Vec<BlockInfo>,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct BlockInfo {
361    /// 分片ETag
362    pub etag: String,
363    /// 分片序号
364    pub seq: i32,
365}
366
367impl UploadFinishRequest {
368    pub fn new(upload_id: impl Into<String>, block_infos: Vec<BlockInfo>) -> Self {
369        Self {
370            upload_id: upload_id.into(),
371            block_infos,
372        }
373    }
374}
375
376/// 完成上传响应数据
377#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct UploadFinishRespData {
379    /// 素材token
380    pub file_token: String,
381}
382
383impl ApiResponseTrait for UploadFinishRespData {
384    fn data_format() -> ResponseFormat {
385        ResponseFormat::Data
386    }
387}
388
389/// 下载素材请求参数
390#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct DownloadMediaRequest {
392    /// 素材token
393    pub file_token: String,
394}
395
396impl DownloadMediaRequest {
397    pub fn new(file_token: impl Into<String>) -> Self {
398        Self {
399            file_token: file_token.into(),
400        }
401    }
402}
403
404/// 批量获取临时下载链接请求参数
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct BatchGetTmpDownloadUrlRequest {
407    /// 素材token列表
408    pub file_tokens: Vec<String>,
409}
410
411impl BatchGetTmpDownloadUrlRequest {
412    pub fn new(file_tokens: Vec<String>) -> Self {
413        Self { file_tokens }
414    }
415}
416
417/// 批量获取临时下载链接响应数据
418#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct BatchGetTmpDownloadUrlRespData {
420    /// 临时下载链接信息
421    pub tmp_download_urls: Vec<TmpDownloadUrl>,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct TmpDownloadUrl {
426    /// 素材token
427    pub file_token: String,
428    /// 临时下载链接
429    pub tmp_download_url: String,
430}
431
432impl ApiResponseTrait for BatchGetTmpDownloadUrlRespData {
433    fn data_format() -> ResponseFormat {
434        ResponseFormat::Data
435    }
436}
437
438// === 宏实现 ===
439
440impl_executable_builder_owned!(
441    UploadMediaRequestBuilder,
442    MediaService,
443    UploadMediaRequest,
444    BaseResponse<UploadMediaRespData>,
445    upload_all
446);
447
448impl_executable_builder_owned!(
449    UploadPartRequestBuilder,
450    MediaService,
451    UploadPartRequest,
452    BaseResponse<UploadPartRespData>,
453    upload_part
454);