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