open_lark/service/cloud_docs/wiki/v2/task/
get.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/// 获取任务结果请求
18#[derive(Debug, Serialize, Default)]
19pub struct GetTaskRequest {
20    #[serde(skip)]
21    api_request: ApiRequest,
22    /// 任务id
23    #[serde(skip)]
24    task_id: String,
25}
26
27impl GetTaskRequest {
28    pub fn builder() -> GetTaskRequestBuilder {
29        GetTaskRequestBuilder::default()
30    }
31
32    pub fn new(task_id: impl ToString) -> Self {
33        Self {
34            task_id: task_id.to_string(),
35            ..Default::default()
36        }
37    }
38}
39
40#[derive(Default)]
41pub struct GetTaskRequestBuilder {
42    request: GetTaskRequest,
43}
44
45impl GetTaskRequestBuilder {
46    /// 任务id
47    pub fn task_id(mut self, task_id: impl ToString) -> Self {
48        self.request.task_id = task_id.to_string();
49        self
50    }
51
52    pub fn build(mut self) -> GetTaskRequest {
53        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
54        self.request
55    }
56}
57
58impl_executable_builder_owned!(
59    GetTaskRequestBuilder,
60    crate::service::cloud_docs::wiki::v2::task::TaskService,
61    GetTaskRequest,
62    GetTaskResponse,
63    get
64);
65
66/// 任务状态
67#[derive(Debug, Deserialize)]
68#[serde(rename_all = "snake_case")]
69pub enum TaskStatus {
70    /// 进行中
71    Processing,
72    /// 成功
73    Success,
74    /// 失败
75    Failed,
76}
77
78/// 移动结果
79#[derive(Debug, Deserialize)]
80pub struct MoveResult {
81    /// 原始文档token
82    pub obj_token: String,
83    /// 知识空间中的节点token
84    pub node_token: String,
85    /// 文档标题
86    pub title: Option<String>,
87    /// 文档类型
88    pub obj_type: Option<String>,
89}
90
91/// 任务详细信息
92#[derive(Debug, Deserialize)]
93pub struct TaskDetail {
94    /// 任务id
95    pub task_id: String,
96    /// 任务状态
97    pub status: TaskStatus,
98    /// 知识空间id
99    pub space_id: Option<String>,
100    /// 已处理的文档数量
101    pub processed_count: Option<i32>,
102    /// 总文档数量
103    pub total_count: Option<i32>,
104    /// 移动成功的结果列表
105    pub move_results: Option<Vec<MoveResult>>,
106    /// 错误信息
107    pub error_message: Option<String>,
108    /// 创建时间(毫秒时间戳)
109    pub create_time: Option<String>,
110    /// 完成时间(毫秒时间戳)
111    pub finish_time: Option<String>,
112}
113
114/// 获取任务结果响应
115#[derive(Debug, Deserialize)]
116pub struct GetTaskResponse {
117    /// 任务详细信息
118    pub task: TaskDetail,
119}
120
121impl ApiResponseTrait for GetTaskResponse {
122    fn data_format() -> ResponseFormat {
123        ResponseFormat::Data
124    }
125}
126
127/// 获取任务结果
128pub async fn get_task(
129    request: GetTaskRequest,
130    config: &Config,
131    option: Option<RequestOption>,
132) -> SDKResult<BaseResponse<GetTaskResponse>> {
133    let mut api_req = request.api_request;
134    api_req.http_method = Method::GET;
135    api_req.api_path = format!("/open-apis/wiki/v2/tasks/{}", request.task_id);
136    api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
137
138    let api_resp = Transport::request(api_req, config, option).await?;
139    Ok(api_resp)
140}
141
142impl TaskStatus {
143    /// 是否已完成(成功或失败)
144    pub fn is_finished(&self) -> bool {
145        matches!(self, TaskStatus::Success | TaskStatus::Failed)
146    }
147
148    /// 是否成功
149    pub fn is_success(&self) -> bool {
150        matches!(self, TaskStatus::Success)
151    }
152
153    /// 是否失败
154    pub fn is_failed(&self) -> bool {
155        matches!(self, TaskStatus::Failed)
156    }
157
158    /// 是否进行中
159    pub fn is_processing(&self) -> bool {
160        matches!(self, TaskStatus::Processing)
161    }
162}
163
164impl TaskDetail {
165    /// 获取进度百分比
166    pub fn progress_percentage(&self) -> Option<f32> {
167        if let (Some(processed), Some(total)) = (self.processed_count, self.total_count) {
168            if total > 0 {
169                return Some((processed as f32 / total as f32) * 100.0);
170            }
171        }
172        None
173    }
174
175    /// 是否有错误
176    pub fn has_error(&self) -> bool {
177        self.error_message.is_some()
178    }
179
180    /// 获取成功移动的文档数量
181    pub fn success_count(&self) -> usize {
182        self.move_results
183            .as_ref()
184            .map_or(0, |results| results.len())
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn test_get_task_request_builder() {
194        let request = GetTaskRequest::builder().task_id("taskxxxxxx").build();
195
196        assert_eq!(request.task_id, "taskxxxxxx");
197    }
198
199    #[test]
200    fn test_task_status_methods() {
201        assert!(TaskStatus::Success.is_finished());
202        assert!(TaskStatus::Failed.is_finished());
203        assert!(!TaskStatus::Processing.is_finished());
204
205        assert!(TaskStatus::Success.is_success());
206        assert!(!TaskStatus::Failed.is_success());
207
208        assert!(TaskStatus::Failed.is_failed());
209        assert!(!TaskStatus::Success.is_failed());
210
211        assert!(TaskStatus::Processing.is_processing());
212        assert!(!TaskStatus::Success.is_processing());
213    }
214}