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#[derive(Debug, Clone, Default, PartialEq)]
19pub struct WorkflowTaskListQuery {
20 pub tasklist_guid: Option<String>,
22 pub section_guid: Option<String>,
24 pub filter: Option<String>,
26 pub sort: Option<serde_json::Value>,
28 pub user_type: Option<String>,
30 pub page_size: Option<i32>,
32}
33
34impl WorkflowTaskListQuery {
35 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 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 pub fn filter(mut self, filter: impl Into<String>) -> Self {
51 self.filter = Some(filter.into());
52 self
53 }
54
55 pub fn sort(mut self, sort: serde_json::Value) -> Self {
57 self.sort = Some(sort);
58 self
59 }
60
61 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 pub fn page_size(mut self, page_size: i32) -> Self {
69 self.page_size = Some(page_size);
70 self
71 }
72}
73
74#[derive(Debug, Clone, Default, PartialEq)]
78pub struct WorkflowTaskMutation {
79 pub summary: Option<String>,
81 pub description: Option<String>,
83 pub due: Option<String>,
85 pub priority: Option<i32>,
87 pub assignee: Option<String>,
89 pub status: Option<String>,
91}
92
93impl WorkflowTaskMutation {
94 pub fn new() -> Self {
96 Self::default()
97 }
98
99 pub fn summary(mut self, summary: impl Into<String>) -> Self {
101 self.summary = Some(summary.into());
102 self
103 }
104
105 pub fn description(mut self, description: impl Into<String>) -> Self {
107 self.description = Some(description.into());
108 self
109 }
110
111 pub fn due(mut self, due: impl Into<String>) -> Self {
113 self.due = Some(due.into());
114 self
115 }
116
117 pub fn priority(mut self, priority: i32) -> Self {
119 self.priority = Some(priority);
120 self
121 }
122
123 pub fn assignee(mut self, assignee: impl Into<String>) -> Self {
125 self.assignee = Some(assignee.into());
126 self
127 }
128
129 pub fn status(mut self, status: impl Into<String>) -> Self {
132 self.status = Some(status.into());
133 self
134 }
135}
136
137#[derive(Debug, Clone, Default, PartialEq)]
141pub struct ApprovalTaskQuery {
142 pub user_id: String,
144 pub topic: String,
146 pub user_id_type: Option<String>,
148 pub status: Option<String>,
150 pub instance_code: Option<String>,
152 pub page_size: Option<i32>,
154}
155
156impl ApprovalTaskQuery {
157 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 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 pub fn status(mut self, status: impl Into<String>) -> Self {
175 self.status = Some(status.into());
176 self
177 }
178
179 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 pub fn page_size(mut self, page_size: i32) -> Self {
187 self.page_size = Some(page_size);
188 self
189 }
190}
191
192#[derive(Debug, Clone, Default, PartialEq)]
196pub struct ApprovalTaskAction {
197 pub approval_code: String,
199 pub instance_code: String,
201 pub user_id: String,
203 pub task_id: String,
205 pub user_id_type: Option<String>,
207 pub comment: Option<String>,
209 pub form: Option<String>,
211}
212
213impl ApprovalTaskAction {
214 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 pub fn comment(mut self, comment: impl Into<String>) -> Self {
234 self.comment = Some(comment.into());
235 self
236 }
237
238 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 pub fn form(mut self, form: impl Into<String>) -> Self {
246 self.form = Some(form.into());
247 self
248 }
249}
250
251pub type ApprovalTaskItem = approval_task_query::TaskItemV4;
253
254#[derive(Debug, Clone, PartialEq)]
256pub struct ApprovalTaskActionResult {
257 pub success: bool,
259}
260
261#[derive(Clone)]
265#[allow(dead_code)]
266pub struct WorkflowService {
267 config: Arc<Config>,
268}
269
270impl WorkflowService {
271 pub fn new(config: Config) -> Self {
273 Self {
274 config: Arc::new(config),
275 }
276 }
277
278 #[cfg(feature = "v1")]
279 pub fn v1(&self) -> crate::v1::TaskV1 {
281 crate::v1::TaskV1::new(self.config.clone())
282 }
283
284 #[cfg(feature = "v2")]
285 pub fn v2(&self) -> crate::v2::TaskV2 {
287 crate::v2::TaskV2::new(self.config.clone())
288 }
289
290 #[cfg(feature = "v2")]
291 pub fn task(&self) -> crate::v2::task::Task {
293 crate::v2::task::Task::new(self.config.clone())
294 }
295
296 #[cfg(feature = "v2")]
297 pub fn tasklist(&self) -> crate::v2::tasklist::Tasklist {
299 crate::v2::tasklist::Tasklist::new(self.config.clone())
300 }
301
302 #[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 #[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 #[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 #[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 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 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 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 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 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 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}