open-lark 0.14.0

Enterprise-grade Lark/Feishu Open API SDK with comprehensive Chinese documentation and advanced error handling
Documentation
use reqwest::Method;
use serde::{Deserialize, Serialize};

use crate::{
    core::{
        api_req::ApiRequest,
        api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
        config::Config,
        constants::AccessTokenType,
        endpoints::{cloud_docs::*, EndpointBuilder},
        http::Transport,
        req_option::RequestOption,
        SDKResult,
    },
    impl_executable_builder_owned,
};

/// 获取任务结果请求
#[derive(Debug, Serialize, Default)]
pub struct GetTaskRequest {
    #[serde(skip)]
    api_request: ApiRequest,
    /// 任务id
    #[serde(skip)]
    task_id: String,
}

impl GetTaskRequest {
    pub fn builder() -> GetTaskRequestBuilder {
        GetTaskRequestBuilder::default()
    }

    pub fn new(task_id: impl ToString) -> Self {
        Self {
            task_id: task_id.to_string(),
            ..Default::default()
        }
    }
}

#[derive(Default)]
pub struct GetTaskRequestBuilder {
    request: GetTaskRequest,
}

impl GetTaskRequestBuilder {
    /// 任务id
    pub fn task_id(mut self, task_id: impl ToString) -> Self {
        self.request.task_id = task_id.to_string();
        self
    }

    pub fn build(mut self) -> GetTaskRequest {
        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
        self.request
    }
}

impl_executable_builder_owned!(
    GetTaskRequestBuilder,
    crate::service::cloud_docs::wiki::v2::task::TaskService,
    GetTaskRequest,
    GetTaskResponse,
    get
);

/// 任务状态
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TaskStatus {
    /// 进行中
    Processing,
    /// 成功
    Success,
    /// 失败
    Failed,
}

/// 移动结果
#[derive(Debug, Deserialize)]
pub struct MoveResult {
    /// 原始文档token
    pub obj_token: String,
    /// 知识空间中的节点token
    pub node_token: String,
    /// 文档标题
    pub title: Option<String>,
    /// 文档类型
    pub obj_type: Option<String>,
}

/// 任务详细信息
#[derive(Debug, Deserialize)]
pub struct TaskDetail {
    /// 任务id
    pub task_id: String,
    /// 任务状态
    pub status: TaskStatus,
    /// 知识空间id
    pub space_id: Option<String>,
    /// 已处理的文档数量
    pub processed_count: Option<i32>,
    /// 总文档数量
    pub total_count: Option<i32>,
    /// 移动成功的结果列表
    pub move_results: Option<Vec<MoveResult>>,
    /// 错误信息
    pub error_message: Option<String>,
    /// 创建时间(毫秒时间戳)
    pub create_time: Option<String>,
    /// 完成时间(毫秒时间戳)
    pub finish_time: Option<String>,
}

/// 获取任务结果响应
#[derive(Debug, Deserialize)]
pub struct GetTaskResponse {
    /// 任务详细信息
    pub task: TaskDetail,
}

impl ApiResponseTrait for GetTaskResponse {
    fn data_format() -> ResponseFormat {
        ResponseFormat::Data
    }
}

/// 获取任务结果
pub async fn get_task(
    request: GetTaskRequest,
    config: &Config,
    option: Option<RequestOption>,
) -> SDKResult<BaseResponse<GetTaskResponse>> {
    let mut api_req = request.api_request;
    api_req.http_method = Method::GET;
    api_req.api_path =
        EndpointBuilder::replace_param(WIKI_V2_TASK_GET, "task_id", &request.task_id);
    api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];

    let api_resp = Transport::request(api_req, config, option).await?;
    Ok(api_resp)
}

impl TaskStatus {
    /// 是否已完成(成功或失败)
    pub fn is_finished(&self) -> bool {
        matches!(self, TaskStatus::Success | TaskStatus::Failed)
    }

    /// 是否成功
    pub fn is_success(&self) -> bool {
        matches!(self, TaskStatus::Success)
    }

    /// 是否失败
    pub fn is_failed(&self) -> bool {
        matches!(self, TaskStatus::Failed)
    }

    /// 是否进行中
    pub fn is_processing(&self) -> bool {
        matches!(self, TaskStatus::Processing)
    }
}

impl TaskDetail {
    /// 获取进度百分比
    pub fn progress_percentage(&self) -> Option<f32> {
        if let (Some(processed), Some(total)) = (self.processed_count, self.total_count) {
            if total > 0 {
                return Some((processed as f32 / total as f32) * 100.0);
            }
        }
        None
    }

    /// 是否有错误
    pub fn has_error(&self) -> bool {
        self.error_message.is_some()
    }

    /// 获取成功移动的文档数量
    pub fn success_count(&self) -> usize {
        self.move_results
            .as_ref()
            .map_or(0, |results| results.len())
    }
}

#[cfg(test)]
#[allow(unused_variables, unused_unsafe)]
mod tests {
    use super::*;

    #[test]
    fn test_get_task_request_builder() {
        let request = GetTaskRequest::builder().task_id("taskxxxxxx").build();

        assert_eq!(request.task_id, "taskxxxxxx");
    }

    #[test]
    fn test_task_status_methods() {
        assert!(TaskStatus::Success.is_finished());
        assert!(TaskStatus::Failed.is_finished());
        assert!(!TaskStatus::Processing.is_finished());

        assert!(TaskStatus::Success.is_success());
        assert!(!TaskStatus::Failed.is_success());

        assert!(TaskStatus::Failed.is_failed());
        assert!(!TaskStatus::Success.is_failed());

        assert!(TaskStatus::Processing.is_processing());
        assert!(!TaskStatus::Success.is_processing());
    }
}