Skip to main content

openlark_workflow/
service.rs

1#[path = "approval/approval/v4/task/approve.rs"]
2mod approval_task_approve;
3#[path = "approval/approval/v4/task/query.rs"]
4mod approval_task_query;
5#[path = "approval/approval/v4/task/reject.rs"]
6mod approval_task_reject;
7#[path = "approval/approval/v4/task/resubmit.rs"]
8mod approval_task_resubmit;
9
10use openlark_core::{SDKResult, config::Config};
11use std::sync::Arc;
12
13use crate::common::constants::MAX_PAGE_SIZE;
14
15/// 任务列表查询 helper。
16///
17/// 用于封装常见的任务列表过滤条件,并让 helper 统一处理分页。
18#[derive(Debug, Clone, Default, PartialEq)]
19pub struct WorkflowTaskListQuery {
20    /// 任务清单 GUID。
21    pub tasklist_guid: Option<String>,
22    /// 分组 GUID。
23    pub section_guid: Option<String>,
24    /// 过滤条件。
25    pub filter: Option<String>,
26    /// 排序条件。
27    pub sort: Option<serde_json::Value>,
28    /// 用户 ID 类型。
29    pub user_type: Option<String>,
30    /// 分页大小。
31    pub page_size: Option<i32>,
32}
33
34impl WorkflowTaskListQuery {
35    /// 为指定任务清单创建查询条件。
36    pub fn for_tasklist(tasklist_guid: impl Into<String>) -> Self {
37        Self {
38            tasklist_guid: Some(tasklist_guid.into()),
39            ..Self::default()
40        }
41    }
42
43    /// 设置分组 GUID。
44    pub fn section_guid(mut self, section_guid: impl Into<String>) -> Self {
45        self.section_guid = Some(section_guid.into());
46        self
47    }
48
49    /// 设置过滤条件。
50    pub fn filter(mut self, filter: impl Into<String>) -> Self {
51        self.filter = Some(filter.into());
52        self
53    }
54
55    /// 设置排序条件。
56    pub fn sort(mut self, sort: serde_json::Value) -> Self {
57        self.sort = Some(sort);
58        self
59    }
60
61    /// 设置用户 ID 类型。
62    pub fn user_type(mut self, user_type: impl Into<String>) -> Self {
63        self.user_type = Some(user_type.into());
64        self
65    }
66
67    /// 设置分页大小。
68    pub fn page_size(mut self, page_size: i32) -> Self {
69        self.page_size = Some(page_size);
70        self
71    }
72}
73
74/// 任务变更 helper。
75///
76/// 只覆盖高频可变字段,不试图替代完整 typed request。
77#[derive(Debug, Clone, Default, PartialEq)]
78pub struct WorkflowTaskMutation {
79    /// 任务标题。
80    pub summary: Option<String>,
81    /// 任务描述。
82    pub description: Option<String>,
83    /// 截止时间。
84    pub due: Option<String>,
85    /// 优先级。
86    pub priority: Option<i32>,
87    /// 执行者。
88    pub assignee: Option<String>,
89    /// 状态。
90    pub status: Option<String>,
91}
92
93impl WorkflowTaskMutation {
94    /// 创建空的任务变更描述。
95    pub fn new() -> Self {
96        Self::default()
97    }
98
99    /// 设置任务标题。
100    pub fn summary(mut self, summary: impl Into<String>) -> Self {
101        self.summary = Some(summary.into());
102        self
103    }
104
105    /// 设置任务描述。
106    pub fn description(mut self, description: impl Into<String>) -> Self {
107        self.description = Some(description.into());
108        self
109    }
110
111    /// 设置截止时间。
112    pub fn due(mut self, due: impl Into<String>) -> Self {
113        self.due = Some(due.into());
114        self
115    }
116
117    /// 设置优先级。
118    pub fn priority(mut self, priority: i32) -> Self {
119        self.priority = Some(priority);
120        self
121    }
122
123    /// 设置执行者。
124    pub fn assignee(mut self, assignee: impl Into<String>) -> Self {
125        self.assignee = Some(assignee.into());
126        self
127    }
128
129    /// 设置审批状态。
130    /// 设置任务状态。
131    pub fn status(mut self, status: impl Into<String>) -> Self {
132        self.status = Some(status.into());
133        self
134    }
135}
136
137/// 审批任务查询 helper。
138///
139/// 用于封装审批待办的常见筛选条件。
140#[derive(Debug, Clone, Default, PartialEq)]
141pub struct ApprovalTaskQuery {
142    /// 用户 ID。
143    pub user_id: String,
144    /// 审批主题。
145    pub topic: String,
146    /// 用户 ID 类型。
147    pub user_id_type: Option<String>,
148    /// 审批状态。
149    pub status: Option<String>,
150    /// 实例编码。
151    pub instance_code: Option<String>,
152    /// 分页大小。
153    pub page_size: Option<i32>,
154}
155
156impl ApprovalTaskQuery {
157    /// 创建审批任务查询条件。
158    pub fn new(user_id: impl Into<String>, topic: impl Into<String>) -> Self {
159        Self {
160            user_id: user_id.into(),
161            topic: topic.into(),
162            ..Self::default()
163        }
164    }
165
166    /// 设置用户 ID 类型。
167    pub fn user_id_type(mut self, user_id_type: impl Into<String>) -> Self {
168        self.user_id_type = Some(user_id_type.into());
169        self
170    }
171
172    /// 设置审批状态。
173    /// 设置任务状态。
174    pub fn status(mut self, status: impl Into<String>) -> Self {
175        self.status = Some(status.into());
176        self
177    }
178
179    /// 设置实例编码。
180    pub fn instance_code(mut self, instance_code: impl Into<String>) -> Self {
181        self.instance_code = Some(instance_code.into());
182        self
183    }
184
185    /// 设置分页大小。
186    pub fn page_size(mut self, page_size: i32) -> Self {
187        self.page_size = Some(page_size);
188        self
189    }
190}
191
192/// 审批任务操作 helper。
193///
194/// 统一高频审批动作的 `task_id + comment` 组合。
195#[derive(Debug, Clone, Default, PartialEq)]
196pub struct ApprovalTaskAction {
197    /// 审批定义编码。
198    pub approval_code: String,
199    /// 审批实例编码。
200    pub instance_code: String,
201    /// 操作人用户 ID。
202    pub user_id: String,
203    /// 审批任务 ID。
204    pub task_id: String,
205    /// 用户 ID 类型。
206    pub user_id_type: Option<String>,
207    /// 备注。
208    pub comment: Option<String>,
209    /// 表单内容。
210    pub form: Option<String>,
211}
212
213impl ApprovalTaskAction {
214    /// 创建审批任务动作参数。
215    pub fn new(
216        approval_code: impl Into<String>,
217        instance_code: impl Into<String>,
218        user_id: impl Into<String>,
219        task_id: impl Into<String>,
220    ) -> Self {
221        Self {
222            approval_code: approval_code.into(),
223            instance_code: instance_code.into(),
224            user_id: user_id.into(),
225            task_id: task_id.into(),
226            user_id_type: None,
227            comment: None,
228            form: None,
229        }
230    }
231
232    /// 设置备注。
233    pub fn comment(mut self, comment: impl Into<String>) -> Self {
234        self.comment = Some(comment.into());
235        self
236    }
237
238    /// 设置用户 ID 类型。
239    pub fn user_id_type(mut self, user_id_type: impl Into<String>) -> Self {
240        self.user_id_type = Some(user_id_type.into());
241        self
242    }
243
244    /// 设置表单内容。
245    pub fn form(mut self, form: impl Into<String>) -> Self {
246        self.form = Some(form.into());
247        self
248    }
249}
250
251/// 审批任务条目类型别名。
252pub type ApprovalTaskItem = approval_task_query::TaskItemV4;
253
254/// 审批任务动作结果 helper。
255#[derive(Debug, Clone, PartialEq)]
256pub struct ApprovalTaskActionResult {
257    /// 操作是否成功。
258    pub success: bool,
259}
260
261/// WorkflowService:工作流服务的统一入口
262///
263/// 提供对任务、审批、看板 API 的访问能力
264#[derive(Clone)]
265#[allow(dead_code)]
266pub struct WorkflowService {
267    config: Arc<Config>,
268}
269
270impl WorkflowService {
271    /// 创建工作流服务入口。
272    pub fn new(config: Config) -> Self {
273        Self {
274            config: Arc::new(config),
275        }
276    }
277
278    #[cfg(feature = "v1")]
279    /// 返回 v1 任务服务入口。
280    pub fn v1(&self) -> crate::v1::TaskV1 {
281        crate::v1::TaskV1::new(self.config.clone())
282    }
283
284    #[cfg(feature = "v2")]
285    /// 返回 v2 任务服务入口。
286    pub fn v2(&self) -> crate::v2::TaskV2 {
287        crate::v2::TaskV2::new(self.config.clone())
288    }
289
290    #[cfg(feature = "v2")]
291    /// 返回 v2 任务资源入口。
292    pub fn task(&self) -> crate::v2::task::Task {
293        crate::v2::task::Task::new(self.config.clone())
294    }
295
296    #[cfg(feature = "v2")]
297    /// 返回 v2 任务清单资源入口。
298    pub fn tasklist(&self) -> crate::v2::tasklist::Tasklist {
299        crate::v2::tasklist::Tasklist::new(self.config.clone())
300    }
301
302    /// 列取任务并自动处理分页。
303    #[cfg(feature = "v2")]
304    pub async fn list_tasks_all(
305        &self,
306        query: WorkflowTaskListQuery,
307    ) -> SDKResult<Vec<crate::v2::task::models::TaskItem>> {
308        use crate::v2::task::list::ListTasksRequest;
309
310        let mut items = Vec::new();
311        let mut page_token: Option<String> = None;
312
313        loop {
314            let mut request = ListTasksRequest::new(self.config.clone())
315                .page_size(query.page_size.unwrap_or(MAX_PAGE_SIZE));
316
317            if let Some(tasklist_guid) = &query.tasklist_guid {
318                request = request.tasklist_guid(tasklist_guid.clone());
319            }
320            if let Some(section_guid) = &query.section_guid {
321                request = request.section_guid(section_guid.clone());
322            }
323            if let Some(filter) = &query.filter {
324                request = request.filter(filter.clone());
325            }
326            if let Some(sort) = &query.sort {
327                request = request.sort(sort.clone());
328            }
329            if let Some(user_type) = &query.user_type {
330                request = request.user_type(user_type.clone());
331            }
332            if let Some(token) = &page_token {
333                request = request.page_token(token.clone());
334            }
335
336            let response = request.execute().await?;
337            items.extend(response.items);
338
339            if !response.has_more {
340                break;
341            }
342            page_token = response.page_token;
343        }
344
345        Ok(items)
346    }
347
348    /// 使用 helper 风格更新任务高频字段。
349    #[cfg(feature = "v2")]
350    pub async fn mutate_task(
351        &self,
352        task_guid: impl Into<String>,
353        mutation: WorkflowTaskMutation,
354    ) -> SDKResult<crate::v2::task::models::UpdateTaskResponse> {
355        use crate::v2::task::update::UpdateTaskRequest;
356
357        let mut request = UpdateTaskRequest::new(self.config.clone(), task_guid.into());
358        if let Some(summary) = mutation.summary {
359            request = request.summary(summary);
360        }
361        if let Some(description) = mutation.description {
362            request = request.description(description);
363        }
364        if let Some(due) = mutation.due {
365            request = request.due(due);
366        }
367        if let Some(priority) = mutation.priority {
368            request = request.priority(priority);
369        }
370        if let Some(assignee) = mutation.assignee {
371            request = request.assignee(assignee);
372        }
373        if let Some(status) = mutation.status {
374            request = request.status(status);
375        }
376
377        request.execute().await
378    }
379
380    /// 完成任务 helper。
381    #[cfg(feature = "v2")]
382    pub async fn complete_task(
383        &self,
384        task_guid: impl Into<String>,
385    ) -> SDKResult<crate::v2::task::models::CompleteTaskResponse> {
386        use crate::v2::task::complete::CompleteTaskRequest;
387
388        CompleteTaskRequest::new(self.config.clone(), task_guid.into())
389            .execute()
390            .await
391    }
392
393    /// 重新打开任务 helper。
394    #[cfg(feature = "v2")]
395    pub async fn reopen_task(
396        &self,
397        task_guid: impl Into<String>,
398    ) -> SDKResult<crate::v2::task::models::UncompleteTaskResponse> {
399        use crate::v2::task::uncomplete::UncompleteTaskRequest;
400
401        UncompleteTaskRequest::new(self.config.clone(), task_guid.into())
402            .execute()
403            .await
404    }
405
406    /// 查询审批任务,并支持按状态/实例做本地筛选。
407    pub async fn query_approval_tasks(
408        &self,
409        query: ApprovalTaskQuery,
410    ) -> SDKResult<Vec<ApprovalTaskItem>> {
411        let mut items = Vec::new();
412        let mut page_token: Option<String> = None;
413
414        loop {
415            let mut request = approval_task_query::QueryTaskRequestV4::new(self.config.clone())
416                .user_id(query.user_id.clone())
417                .topic(query.topic.clone())
418                .page_size(query.page_size.unwrap_or(MAX_PAGE_SIZE));
419
420            if let Some(user_id_type) = &query.user_id_type {
421                request = request.user_id_type(user_id_type.clone());
422            }
423            if let Some(token) = &page_token {
424                request = request.page_token(token.clone());
425            }
426
427            let response = request.execute().await?;
428            items.extend(response.tasks);
429
430            if !response.has_more.unwrap_or(false) {
431                break;
432            }
433            page_token = response.page_token;
434        }
435
436        if let Some(status) = &query.status {
437            items.retain(|item| item.status == *status);
438        }
439        if let Some(instance_code) = &query.instance_code {
440            items.retain(|item| item.instance_code == *instance_code);
441        }
442
443        Ok(items)
444    }
445
446    /// 同意审批任务 helper。
447    pub async fn approve_task(
448        &self,
449        action: ApprovalTaskAction,
450    ) -> SDKResult<ApprovalTaskActionResult> {
451        let mut request = approval_task_approve::ApproveTaskRequestV4::new(self.config.clone())
452            .approval_code(action.approval_code)
453            .instance_code(action.instance_code)
454            .user_id(action.user_id)
455            .task_id(action.task_id);
456        if let Some(user_id_type) = action.user_id_type {
457            request = request.user_id_type(user_id_type);
458        }
459        if let Some(comment) = action.comment {
460            request = request.comment(comment);
461        }
462        if let Some(form) = action.form {
463            request = request.form(form);
464        }
465        let response = request.execute().await?;
466        let _ = response;
467        Ok(ApprovalTaskActionResult { success: true })
468    }
469
470    /// 拒绝审批任务 helper。
471    pub async fn reject_task(
472        &self,
473        action: ApprovalTaskAction,
474    ) -> SDKResult<ApprovalTaskActionResult> {
475        let mut request = approval_task_reject::RejectTaskRequestV4::new(self.config.clone())
476            .approval_code(action.approval_code)
477            .instance_code(action.instance_code)
478            .user_id(action.user_id)
479            .task_id(action.task_id);
480        if let Some(user_id_type) = action.user_id_type {
481            request = request.user_id_type(user_id_type);
482        }
483        if let Some(comment) = action.comment {
484            request = request.comment(comment);
485        }
486        if let Some(form) = action.form {
487            request = request.form(form);
488        }
489        let response = request.execute().await?;
490        let _ = response;
491        Ok(ApprovalTaskActionResult { success: true })
492    }
493
494    /// 重新提交审批任务 helper。
495    pub async fn resubmit_task(
496        &self,
497        action: ApprovalTaskAction,
498    ) -> SDKResult<ApprovalTaskActionResult> {
499        let mut request = approval_task_resubmit::ResubmitTaskRequestV4::new(self.config.clone())
500            .approval_code(action.approval_code)
501            .instance_code(action.instance_code)
502            .user_id(action.user_id)
503            .task_id(action.task_id);
504        if let Some(user_id_type) = action.user_id_type {
505            request = request.user_id_type(user_id_type);
506        }
507        if let Some(comment) = action.comment {
508            request = request.comment(comment);
509        }
510        if let Some(form) = action.form {
511            request = request.form(form);
512        }
513        let response = request.execute().await?;
514        let _ = response;
515        Ok(ApprovalTaskActionResult { success: true })
516    }
517}
518
519#[cfg(test)]
520#[allow(unused_imports)]
521mod tests {
522    use super::*;
523    use serde_json::json;
524
525    #[test]
526    fn test_serialization_roundtrip() {
527        // 基础序列化测试
528        let json = r#"{"test": "value"}"#;
529        assert!(serde_json::from_str::<serde_json::Value>(json).is_ok());
530    }
531
532    #[test]
533    fn test_deserialization_from_json() {
534        // 基础反序列化测试
535        let json = r#"{"field": "data"}"#;
536        let value: serde_json::Value = serde_json::from_str(json).expect("JSON 反序列化失败");
537        assert_eq!(value["field"], "data");
538    }
539
540    #[test]
541    fn test_task_list_query_builder() {
542        let query = WorkflowTaskListQuery::for_tasklist("tasklist_123")
543            .section_guid("section_456")
544            .filter("status = incomplete")
545            .sort(json!([{"field": "due", "order": "asc"}]))
546            .user_type("open_id")
547            .page_size(50);
548
549        assert_eq!(query.tasklist_guid.as_deref(), Some("tasklist_123"));
550        assert_eq!(query.section_guid.as_deref(), Some("section_456"));
551        assert_eq!(query.filter.as_deref(), Some("status = incomplete"));
552        assert_eq!(query.user_type.as_deref(), Some("open_id"));
553        assert_eq!(query.page_size, Some(50));
554    }
555
556    #[test]
557    fn test_task_mutation_builder() {
558        let mutation = WorkflowTaskMutation::new()
559            .summary("完成项目文档")
560            .description("补齐 workflow helper")
561            .due("2026-09-30T23:59:59Z")
562            .priority(3)
563            .assignee("ou_xxx")
564            .status("in_progress");
565
566        assert_eq!(mutation.summary.as_deref(), Some("完成项目文档"));
567        assert_eq!(
568            mutation.description.as_deref(),
569            Some("补齐 workflow helper")
570        );
571        assert_eq!(mutation.due.as_deref(), Some("2026-09-30T23:59:59Z"));
572        assert_eq!(mutation.priority, Some(3));
573        assert_eq!(mutation.assignee.as_deref(), Some("ou_xxx"));
574        assert_eq!(mutation.status.as_deref(), Some("in_progress"));
575    }
576
577    #[test]
578    fn test_approval_task_query_builder() {
579        let query = ApprovalTaskQuery::new("ou_xxx", "1")
580            .user_id_type("open_id")
581            .status("PENDING")
582            .instance_code("instance_123")
583            .page_size(100);
584
585        assert_eq!(query.user_id, "ou_xxx");
586        assert_eq!(query.topic, "1");
587        assert_eq!(query.user_id_type.as_deref(), Some("open_id"));
588        assert_eq!(query.status.as_deref(), Some("PENDING"));
589        assert_eq!(query.instance_code.as_deref(), Some("instance_123"));
590        assert_eq!(query.page_size, Some(100));
591    }
592
593    #[test]
594    fn test_approval_task_action_builder() {
595        let action =
596            ApprovalTaskAction::new("approval_code", "instance_code", "ou_xxx", "task_123")
597                .user_id_type("open_id")
598                .comment("已确认")
599                .form("[{}]");
600
601        assert_eq!(action.approval_code, "approval_code");
602        assert_eq!(action.instance_code, "instance_code");
603        assert_eq!(action.user_id, "ou_xxx");
604        assert_eq!(action.task_id, "task_123");
605        assert_eq!(action.user_id_type.as_deref(), Some("open_id"));
606        assert_eq!(action.comment.as_deref(), Some("已确认"));
607        assert_eq!(action.form.as_deref(), Some("[{}]"));
608    }
609}