1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Debug, Clone, Serialize)]
8pub struct ContactGetUserRequest {
9 pub userid: String,
11 #[serde(skip_serializing_if = "Option::is_none")]
13 pub language: Option<String>,
14}
15
16impl ContactGetUserRequest {
17 #[must_use]
19 pub fn new(userid: impl Into<String>) -> Self {
20 Self {
21 userid: userid.into(),
22 language: None,
23 }
24 }
25
26 #[must_use]
28 pub fn language(mut self, value: impl Into<String>) -> Self {
29 self.language = Some(value.into());
30 self
31 }
32}
33
34#[derive(Debug, Clone, Serialize)]
36pub struct ContactGetUserByMobileRequest {
37 pub mobile: String,
39}
40
41impl ContactGetUserByMobileRequest {
42 #[must_use]
44 pub fn new(mobile: impl Into<String>) -> Self {
45 Self {
46 mobile: mobile.into(),
47 }
48 }
49}
50
51#[derive(Debug, Clone, Serialize)]
53pub struct ContactGetUserByUnionIdRequest {
54 pub unionid: String,
56}
57
58impl ContactGetUserByUnionIdRequest {
59 #[must_use]
61 pub fn new(unionid: impl Into<String>) -> Self {
62 Self {
63 unionid: unionid.into(),
64 }
65 }
66}
67
68#[derive(Debug, Clone, Serialize)]
70pub struct ContactListUsersRequest {
71 pub dept_id: i64,
73 pub cursor: i64,
75 pub size: i64,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub language: Option<String>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub order_field: Option<String>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub contain_access_limit: Option<bool>,
86}
87
88impl ContactListUsersRequest {
89 #[must_use]
91 pub fn new(dept_id: i64, cursor: i64, size: i64) -> Self {
92 Self {
93 dept_id,
94 cursor,
95 size,
96 language: None,
97 order_field: None,
98 contain_access_limit: None,
99 }
100 }
101
102 #[must_use]
104 pub fn language(mut self, value: impl Into<String>) -> Self {
105 self.language = Some(value.into());
106 self
107 }
108
109 #[must_use]
111 pub fn order_field(mut self, value: impl Into<String>) -> Self {
112 self.order_field = Some(value.into());
113 self
114 }
115
116 #[must_use]
118 pub fn contain_access_limit(mut self, value: bool) -> Self {
119 self.contain_access_limit = Some(value);
120 self
121 }
122}
123
124#[derive(Debug, Clone, Serialize)]
126pub struct ContactCreateUserRequest {
127 pub name: String,
129 pub mobile: String,
131 pub dept_id_list: String,
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub userid: Option<String>,
136 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
138 pub extra: BTreeMap<String, Value>,
139}
140
141impl ContactCreateUserRequest {
142 #[must_use]
144 pub fn new(
145 name: impl Into<String>,
146 mobile: impl Into<String>,
147 dept_id_list: impl Into<String>,
148 ) -> Self {
149 Self {
150 name: name.into(),
151 mobile: mobile.into(),
152 dept_id_list: dept_id_list.into(),
153 userid: None,
154 extra: BTreeMap::new(),
155 }
156 }
157
158 #[must_use]
160 pub fn userid(mut self, value: impl Into<String>) -> Self {
161 self.userid = Some(value.into());
162 self
163 }
164
165 #[must_use]
167 pub fn insert_extra(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
168 self.extra.insert(key.into(), value.into());
169 self
170 }
171}
172
173#[derive(Debug, Clone, Serialize)]
175pub struct ContactUpdateUserRequest {
176 pub userid: String,
178 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
180 pub extra: BTreeMap<String, Value>,
181}
182
183impl ContactUpdateUserRequest {
184 #[must_use]
186 pub fn new(userid: impl Into<String>) -> Self {
187 Self {
188 userid: userid.into(),
189 extra: BTreeMap::new(),
190 }
191 }
192
193 #[must_use]
195 pub fn insert_extra(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
196 self.extra.insert(key.into(), value.into());
197 self
198 }
199}
200
201#[derive(Debug, Clone, Serialize)]
203pub struct ContactDeleteUserRequest {
204 pub userid: String,
206}
207
208impl ContactDeleteUserRequest {
209 #[must_use]
211 pub fn new(userid: impl Into<String>) -> Self {
212 Self {
213 userid: userid.into(),
214 }
215 }
216}
217
218#[derive(Debug, Clone, Serialize)]
220pub struct ContactGetDepartmentRequest {
221 pub dept_id: i64,
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub language: Option<String>,
226}
227
228impl ContactGetDepartmentRequest {
229 #[must_use]
231 pub fn new(dept_id: i64) -> Self {
232 Self {
233 dept_id,
234 language: None,
235 }
236 }
237
238 #[must_use]
240 pub fn language(mut self, value: impl Into<String>) -> Self {
241 self.language = Some(value.into());
242 self
243 }
244}
245
246#[derive(Debug, Clone, Serialize)]
248pub struct ContactCreateDepartmentRequest {
249 pub name: String,
251 pub parent_id: i64,
253 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
255 pub extra: BTreeMap<String, Value>,
256}
257
258impl ContactCreateDepartmentRequest {
259 #[must_use]
261 pub fn new(name: impl Into<String>, parent_id: i64) -> Self {
262 Self {
263 name: name.into(),
264 parent_id,
265 extra: BTreeMap::new(),
266 }
267 }
268
269 #[must_use]
271 pub fn insert_extra(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
272 self.extra.insert(key.into(), value.into());
273 self
274 }
275}
276
277#[derive(Debug, Clone, Serialize)]
279pub struct ContactUpdateDepartmentRequest {
280 pub dept_id: i64,
282 #[serde(skip_serializing_if = "Option::is_none")]
284 pub name: Option<String>,
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub parent_id: Option<i64>,
288 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
290 pub extra: BTreeMap<String, Value>,
291}
292
293impl ContactUpdateDepartmentRequest {
294 #[must_use]
296 pub fn new(dept_id: i64) -> Self {
297 Self {
298 dept_id,
299 name: None,
300 parent_id: None,
301 extra: BTreeMap::new(),
302 }
303 }
304
305 #[must_use]
307 pub fn name(mut self, value: impl Into<String>) -> Self {
308 self.name = Some(value.into());
309 self
310 }
311
312 #[must_use]
314 pub fn parent_id(mut self, value: i64) -> Self {
315 self.parent_id = Some(value);
316 self
317 }
318
319 #[must_use]
321 pub fn insert_extra(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
322 self.extra.insert(key.into(), value.into());
323 self
324 }
325}
326
327#[derive(Debug, Clone, Serialize)]
329pub struct ContactDeleteDepartmentRequest {
330 pub dept_id: i64,
332}
333
334impl ContactDeleteDepartmentRequest {
335 #[must_use]
337 pub fn new(dept_id: i64) -> Self {
338 Self { dept_id }
339 }
340}
341
342#[derive(Debug, Clone, Serialize)]
344pub struct ContactListSubDepartmentsRequest {
345 pub dept_id: i64,
347 #[serde(skip_serializing_if = "Option::is_none")]
349 pub language: Option<String>,
350}
351
352#[derive(Debug, Clone, Serialize)]
354pub struct ContactListSubDepartmentIdsRequest {
355 pub dept_id: i64,
357 #[serde(skip_serializing_if = "Option::is_none")]
359 pub language: Option<String>,
360}
361
362impl ContactListSubDepartmentIdsRequest {
363 #[must_use]
365 pub fn new(dept_id: i64) -> Self {
366 Self {
367 dept_id,
368 language: None,
369 }
370 }
371
372 #[must_use]
374 pub fn language(mut self, value: impl Into<String>) -> Self {
375 self.language = Some(value.into());
376 self
377 }
378}
379
380impl ContactListSubDepartmentsRequest {
381 #[must_use]
383 pub fn new(dept_id: i64) -> Self {
384 Self {
385 dept_id,
386 language: None,
387 }
388 }
389
390 #[must_use]
392 pub fn language(mut self, value: impl Into<String>) -> Self {
393 self.language = Some(value.into());
394 self
395 }
396}
397
398#[derive(Debug, Clone, Serialize)]
400pub struct ApprovalFormComponentValue {
401 pub name: String,
403 pub value: String,
405}
406
407impl ApprovalFormComponentValue {
408 #[must_use]
410 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
411 Self {
412 name: name.into(),
413 value: value.into(),
414 }
415 }
416}
417
418#[derive(Debug, Clone, Serialize)]
420pub struct ApprovalCreateProcessInstanceRequest {
421 pub process_code: String,
423 pub originator_user_id: String,
425 pub dept_id: i64,
427 #[serde(skip_serializing_if = "Option::is_none")]
429 pub approvers: Option<String>,
430 #[serde(skip_serializing_if = "Option::is_none")]
432 pub cc_list: Option<String>,
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub cc_position: Option<String>,
436 pub form_component_values: Vec<ApprovalFormComponentValue>,
438}
439
440impl ApprovalCreateProcessInstanceRequest {
441 #[must_use]
443 pub fn new(
444 process_code: impl Into<String>,
445 originator_user_id: impl Into<String>,
446 dept_id: i64,
447 form_component_values: Vec<ApprovalFormComponentValue>,
448 ) -> Self {
449 Self {
450 process_code: process_code.into(),
451 originator_user_id: originator_user_id.into(),
452 dept_id,
453 approvers: None,
454 cc_list: None,
455 cc_position: None,
456 form_component_values,
457 }
458 }
459
460 #[must_use]
462 pub fn approvers(mut self, value: impl Into<String>) -> Self {
463 self.approvers = Some(value.into());
464 self
465 }
466
467 #[must_use]
469 pub fn cc_list(mut self, value: impl Into<String>) -> Self {
470 self.cc_list = Some(value.into());
471 self
472 }
473
474 #[must_use]
476 pub fn cc_position(mut self, value: impl Into<String>) -> Self {
477 self.cc_position = Some(value.into());
478 self
479 }
480}
481
482#[derive(Debug, Clone, Serialize)]
484pub struct ApprovalListProcessInstanceIdsRequest {
485 pub start_time: i64,
487 pub end_time: i64,
489 pub cursor: i64,
491 pub size: i64,
493 #[serde(skip_serializing_if = "Option::is_none")]
495 pub process_code: Option<String>,
496 #[serde(skip_serializing_if = "Option::is_none")]
498 pub userid_list: Option<String>,
499}
500
501#[derive(Debug, Clone, Serialize)]
503pub struct ApprovalTerminateProcessInstanceRequest {
504 pub process_instance_id: String,
506 pub operating_userid: String,
508 #[serde(skip_serializing_if = "Option::is_none")]
510 pub is_system: Option<bool>,
511 #[serde(skip_serializing_if = "Option::is_none")]
513 pub remark: Option<String>,
514}
515
516impl ApprovalTerminateProcessInstanceRequest {
517 #[must_use]
519 pub fn new(
520 process_instance_id: impl Into<String>,
521 operating_userid: impl Into<String>,
522 ) -> Self {
523 Self {
524 process_instance_id: process_instance_id.into(),
525 operating_userid: operating_userid.into(),
526 is_system: None,
527 remark: None,
528 }
529 }
530
531 #[must_use]
533 pub fn is_system(mut self, value: bool) -> Self {
534 self.is_system = Some(value);
535 self
536 }
537
538 #[must_use]
540 pub fn remark(mut self, value: impl Into<String>) -> Self {
541 self.remark = Some(value.into());
542 self
543 }
544}
545
546impl ApprovalListProcessInstanceIdsRequest {
547 #[must_use]
549 pub fn new(start_time: i64, end_time: i64, cursor: i64, size: i64) -> Self {
550 Self {
551 start_time,
552 end_time,
553 cursor,
554 size,
555 process_code: None,
556 userid_list: None,
557 }
558 }
559
560 #[must_use]
562 pub fn process_code(mut self, value: impl Into<String>) -> Self {
563 self.process_code = Some(value.into());
564 self
565 }
566
567 #[must_use]
569 pub fn userid_list(mut self, value: impl Into<String>) -> Self {
570 self.userid_list = Some(value.into());
571 self
572 }
573}
574
575#[derive(Debug, Clone, Deserialize)]
576#[non_exhaustive]
577pub struct ApprovalListProcessInstanceIdsResult {
579 pub list: Vec<String>,
581 pub next_cursor: Option<i64>,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
586#[non_exhaustive]
587pub struct ContactUser {
589 #[serde(default)]
591 pub userid: Option<String>,
592 #[serde(default)]
594 pub unionid: Option<String>,
595 #[serde(default)]
597 pub name: Option<String>,
598 #[serde(default)]
600 pub mobile: Option<String>,
601 #[serde(flatten, default)]
603 pub extra: BTreeMap<String, Value>,
604}
605
606#[derive(Debug, Clone, Serialize, Deserialize)]
607#[non_exhaustive]
608pub struct ContactListUsersResult {
610 #[serde(default)]
612 pub has_more: Option<bool>,
613 #[serde(default)]
615 pub next_cursor: Option<i64>,
616 #[serde(default)]
618 pub list: Vec<ContactUser>,
619 #[serde(flatten, default)]
621 pub extra: BTreeMap<String, Value>,
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize)]
625#[non_exhaustive]
626pub struct ContactCreateUserResult {
628 #[serde(default)]
630 pub userid: Option<String>,
631 #[serde(default)]
633 pub unionid: Option<String>,
634 #[serde(flatten, default)]
636 pub extra: BTreeMap<String, Value>,
637}
638
639#[derive(Debug, Clone, Serialize, Deserialize)]
640#[non_exhaustive]
641pub struct ContactDepartment {
643 #[serde(default, alias = "id")]
645 pub dept_id: Option<i64>,
646 #[serde(default)]
648 pub name: Option<String>,
649 #[serde(default)]
651 pub parent_id: Option<i64>,
652 #[serde(flatten, default)]
654 pub extra: BTreeMap<String, Value>,
655}
656
657#[derive(Debug, Clone, Serialize, Deserialize)]
658#[non_exhaustive]
659pub struct ContactListSubDepartmentsResult {
661 #[serde(default, alias = "dept_list", alias = "department", alias = "list")]
663 pub departments: Vec<ContactDepartment>,
664 #[serde(flatten, default)]
666 pub extra: BTreeMap<String, Value>,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize)]
670#[non_exhaustive]
671pub struct ContactListSubDepartmentIdsResult {
673 #[serde(default, alias = "list", alias = "department_ids")]
675 pub dept_id_list: Vec<i64>,
676 #[serde(flatten, default)]
678 pub extra: BTreeMap<String, Value>,
679}
680
681#[derive(Debug, Clone, Serialize, Deserialize)]
682#[non_exhaustive]
683pub struct ContactCreateDepartmentResult {
685 #[serde(default, alias = "id")]
687 pub dept_id: Option<i64>,
688 #[serde(flatten, default)]
690 pub extra: BTreeMap<String, Value>,
691}
692
693#[derive(Debug, Clone, Serialize, Deserialize)]
694#[non_exhaustive]
695pub struct ApprovalProcessInstance {
697 #[serde(default)]
699 pub process_instance_id: Option<String>,
700 #[serde(flatten, default)]
702 pub extra: BTreeMap<String, Value>,
703}
704
705#[cfg(test)]
706mod tests {
707 use super::{
708 ApprovalProcessInstance, ApprovalTerminateProcessInstanceRequest, ContactListUsersResult,
709 };
710
711 #[test]
712 fn approval_terminate_request_serializes_optional_remark() {
713 let request = ApprovalTerminateProcessInstanceRequest::new("PROC-1", "user-1")
714 .is_system(true)
715 .remark("cancelled by sdk test");
716
717 let value = serde_json::to_value(request).expect("request should serialize");
718 assert_eq!(
719 value.get("remark").and_then(serde_json::Value::as_str),
720 Some("cancelled by sdk test")
721 );
722 }
723
724 #[test]
725 fn contact_list_users_result_parses_known_and_extra_fields() {
726 let raw = r#"{
727 "has_more": true,
728 "next_cursor": 30,
729 "list": [{"userid":"u-1","name":"Alice"}],
730 "unknown_flag": 1
731 }"#;
732 let parsed: ContactListUsersResult =
733 serde_json::from_str(raw).expect("response should deserialize");
734
735 assert_eq!(parsed.has_more, Some(true));
736 assert_eq!(parsed.next_cursor, Some(30));
737 assert_eq!(parsed.list.len(), 1);
738 assert_eq!(parsed.list[0].userid.as_deref(), Some("u-1"));
739 assert_eq!(
740 parsed
741 .extra
742 .get("unknown_flag")
743 .and_then(serde_json::Value::as_i64),
744 Some(1)
745 );
746 }
747
748 #[test]
749 fn approval_process_instance_parses_known_and_extra_fields() {
750 let raw = r#"{"process_instance_id":"PROC-1","biz_id":"BIZ-1"}"#;
751 let parsed: ApprovalProcessInstance =
752 serde_json::from_str(raw).expect("response should deserialize");
753
754 assert_eq!(parsed.process_instance_id.as_deref(), Some("PROC-1"));
755 assert_eq!(
756 parsed
757 .extra
758 .get("biz_id")
759 .and_then(serde_json::Value::as_str),
760 Some("BIZ-1")
761 );
762 }
763}