1use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct DocumentBase {
11 pub document_id: String,
13 pub title: String,
15 pub doc_type: DocumentType,
17 pub create_time: DateTime<Utc>,
19 pub modify_time: DateTime<Utc>,
21 pub creator: UserInfo,
23 pub modifier: Option<UserInfo>,
25 pub status: DocumentStatus,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
31#[serde(rename_all = "snake_case")]
32pub enum DocumentType {
33 Doc,
35 Sheet,
37 Slide,
39 Mindnote,
41 Flowchart,
43 Bitable,
45 Wiki,
47 Folder,
49 Other(String),
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
55#[serde(rename_all = "snake_case")]
56pub enum DocumentStatus {
57 Normal,
59 Recycle,
61 Deleted,
63 Encrypted,
65 Archived,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
71pub struct UserInfo {
72 pub user_id: String,
74 pub name: String,
76 pub email: Option<String>,
78 pub avatar_url: Option<String>,
80 pub department: Option<DepartmentInfo>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86pub struct DepartmentInfo {
87 pub department_id: String,
89 pub name: String,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
95pub struct FileInfo {
96 pub file_id: String,
98 pub name: String,
100 pub size: u64,
102 pub mime_type: String,
104 pub url: String,
106 pub download_token: Option<String>,
108 pub thumbnail_url: Option<String>,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
114pub struct Permission {
115 pub permission_type: PermissionType,
117 pub can_read: bool,
119 pub can_write: bool,
121 pub can_delete: bool,
123 pub can_share: bool,
125 pub expire_time: Option<DateTime<Utc>>,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
131#[serde(rename_all = "snake_case")]
132pub enum PermissionType {
133 Owner,
135 Admin,
137 Editor,
139 Commenter,
141 Viewer,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
147pub struct ShareInfo {
148 pub share_url: String,
150 pub share_token: String,
152 pub permission_type: PermissionType,
154 pub need_password: bool,
156 pub expire_time: Option<DateTime<Utc>>,
158 pub visit_limit: Option<u32>,
160 pub visit_count: u32,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
166pub struct VersionInfo {
167 pub version: i32,
169 pub version_name: String,
171 pub create_time: DateTime<Utc>,
173 pub creator: UserInfo,
175 pub description: Option<String>,
177 pub is_current: bool,
179 pub size: u64,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct SearchResult {
186 pub document: DocumentBase,
188 pub snippets: Vec<String>,
190 pub score: f64,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
196pub struct DocumentStats {
197 pub view_count: u64,
199 pub edit_count: u64,
201 pub comment_count: u64,
203 pub share_count: u64,
205 pub download_count: u64,
207 pub last_stats_time: DateTime<Utc>,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct OperationRecord {
214 pub record_id: String,
216 pub operation_type: OperationType,
218 pub operation_time: DateTime<Utc>,
220 pub operator: UserInfo,
222 pub description: Option<String>,
224 pub ip_address: Option<String>,
226 pub device_info: Option<String>,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
232#[serde(rename_all = "snake_case")]
233pub enum OperationType {
234 Create,
236 Read,
238 Update,
240 Delete,
242 Share,
244 Download,
246 Copy,
248 Move,
250 Rename,
252 PermissionChange,
254 Other(String),
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
260pub struct ImportExportTask {
261 pub task_id: String,
263 pub task_type: TaskType,
265 pub status: TaskStatus,
267 pub progress: u8,
269 pub start_time: DateTime<Utc>,
271 pub end_time: Option<DateTime<Utc>>,
273 pub error_message: Option<String>,
275 pub result_url: Option<String>,
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
281#[serde(rename_all = "snake_case")]
282pub enum TaskType {
283 Import,
285 Export,
287 Convert,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
293#[serde(rename_all = "snake_case")]
294pub enum TaskStatus {
295 Pending,
297 Processing,
299 Completed,
301 Failed,
303 Canceled,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
309pub struct CommonResponse<T: PartialEq> {
310 pub success: bool,
312 pub data: Option<T>,
314 pub error: Option<ErrorInfo>,
316 pub request_id: String,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
322pub struct ErrorInfo {
323 pub code: i32,
325 pub message: String,
327 pub details: Option<HashMap<String, serde_json::Value>>,
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
333pub struct PageRequest {
334 pub page: u32,
336 pub page_size: u32,
338 pub sort_field: Option<String>,
340 pub sort_direction: Option<SortDirection>,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
346#[serde(rename_all = "snake_case")]
347pub enum SortDirection {
348 Asc,
350 Desc,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
356pub struct PageResponse<T: PartialEq> {
357 pub items: Vec<T>,
359 pub total: u64,
361 pub page: u32,
363 pub page_size: u32,
365 pub total_page: u32,
367 pub has_next: bool,
369}
370
371impl Default for PageRequest {
372 fn default() -> Self {
373 Self {
374 page: 1,
375 page_size: 20,
376 sort_field: None,
377 sort_direction: Some(SortDirection::Desc),
378 }
379 }
380}
381
382#[cfg(test)]
386mod tests {
387 use super::*;
388
389 fn test_roundtrip<T: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug>(
390 original: &T,
391 ) {
392 let json = serde_json::to_string(original).expect("序列化失败");
393 let deserialized: T = serde_json::from_str(&json).expect("反序列化失败");
394 assert_eq!(original, &deserialized, "roundtrip 后数据不一致");
395 }
396
397 #[test]
398 fn test_document_type_serialization() {
399 test_roundtrip(&DocumentType::Doc);
400 test_roundtrip(&DocumentType::Sheet);
401 test_roundtrip(&DocumentType::Other("custom".to_string()));
402 }
403
404 #[test]
405 fn test_document_status_serialization() {
406 test_roundtrip(&DocumentStatus::Normal);
407 test_roundtrip(&DocumentStatus::Archived);
408 }
409
410 #[test]
411 fn test_permission_type_serialization() {
412 test_roundtrip(&PermissionType::Owner);
413 test_roundtrip(&PermissionType::Viewer);
414 }
415
416 #[test]
417 fn test_operation_type_serialization() {
418 test_roundtrip(&OperationType::Create);
419 test_roundtrip(&OperationType::Other("custom_op".to_string()));
420 }
421
422 #[test]
423 fn test_task_type_serialization() {
424 test_roundtrip(&TaskType::Import);
425 test_roundtrip(&TaskType::Export);
426 }
427
428 #[test]
429 fn test_task_status_serialization() {
430 test_roundtrip(&TaskStatus::Pending);
431 test_roundtrip(&TaskStatus::Completed);
432 }
433
434 #[test]
435 fn test_sort_direction_serialization() {
436 test_roundtrip(&SortDirection::Asc);
437 test_roundtrip(&SortDirection::Desc);
438 }
439
440 #[test]
441 fn test_user_info_serialization() {
442 let user = UserInfo {
443 user_id: "user123".to_string(),
444 name: "测试用户".to_string(),
445 email: Some("test@example.com".to_string()),
446 avatar_url: None,
447 department: Some(DepartmentInfo {
448 department_id: "dept456".to_string(),
449 name: "技术部".to_string(),
450 }),
451 };
452 test_roundtrip(&user);
453 }
454
455 #[test]
456 fn test_department_info_serialization() {
457 let dept = DepartmentInfo {
458 department_id: "dept789".to_string(),
459 name: "产品部".to_string(),
460 };
461 test_roundtrip(&dept);
462 }
463
464 #[test]
465 fn test_file_info_serialization() {
466 let file = FileInfo {
467 file_id: "file123".to_string(),
468 name: "test.pdf".to_string(),
469 size: 1024000,
470 mime_type: "application/pdf".to_string(),
471 url: "https://example.com/file".to_string(),
472 download_token: Some("token123".to_string()),
473 thumbnail_url: None,
474 };
475 test_roundtrip(&file);
476 }
477
478 #[test]
479 fn test_permission_serialization() {
480 let perm = Permission {
481 permission_type: PermissionType::Editor,
482 can_read: true,
483 can_write: true,
484 can_delete: false,
485 can_share: true,
486 expire_time: None,
487 };
488 test_roundtrip(&perm);
489 }
490
491 #[test]
492 fn test_share_info_serialization() {
493 let share = ShareInfo {
494 share_url: "https://share.example.com/doc".to_string(),
495 share_token: "token456".to_string(),
496 permission_type: PermissionType::Viewer,
497 need_password: false,
498 expire_time: None,
499 visit_limit: Some(100),
500 visit_count: 10,
501 };
502 test_roundtrip(&share);
503 }
504
505 #[test]
506 fn test_version_info_serialization() {
507 let version = VersionInfo {
508 version: 1,
509 version_name: "v1.0".to_string(),
510 create_time: Utc::now(),
511 creator: UserInfo {
512 user_id: "user001".to_string(),
513 name: "张三".to_string(),
514 email: None,
515 avatar_url: None,
516 department: None,
517 },
518 description: Some("初始版本".to_string()),
519 is_current: true,
520 size: 2048,
521 };
522 test_roundtrip(&version);
523 }
524
525 #[test]
526 fn test_document_stats_serialization() {
527 let stats = DocumentStats {
528 view_count: 100,
529 edit_count: 20,
530 comment_count: 5,
531 share_count: 10,
532 download_count: 3,
533 last_stats_time: Utc::now(),
534 };
535 test_roundtrip(&stats);
536 }
537
538 #[test]
539 fn test_import_export_task_serialization() {
540 let task = ImportExportTask {
541 task_id: "task789".to_string(),
542 task_type: TaskType::Convert,
543 status: TaskStatus::Processing,
544 progress: 50,
545 start_time: Utc::now(),
546 end_time: None,
547 error_message: None,
548 result_url: Some("https://result.example.com".to_string()),
549 };
550 test_roundtrip(&task);
551 }
552
553 #[test]
554 fn test_error_info_serialization() {
555 let mut details = HashMap::new();
556 details.insert("field".to_string(), serde_json::json!("value"));
557
558 let error = ErrorInfo {
559 code: 404,
560 message: "Not Found".to_string(),
561 details: Some(details),
562 };
563 test_roundtrip(&error);
564 }
565
566 #[test]
567 fn test_page_request_serialization() {
568 let req = PageRequest {
569 page: 1,
570 page_size: 20,
571 sort_field: Some("create_time".to_string()),
572 sort_direction: Some(SortDirection::Desc),
573 };
574 test_roundtrip(&req);
575 }
576
577 #[test]
578 fn test_page_request_default() {
579 let default_req = PageRequest::default();
580 assert_eq!(default_req.page, 1);
581 assert_eq!(default_req.page_size, 20);
582 }
583
584 #[test]
585 fn test_common_response_serialization() {
586 let response: CommonResponse<String> = CommonResponse {
587 success: true,
588 data: Some("test data".to_string()),
589 error: None,
590 request_id: "req123".to_string(),
591 };
592 test_roundtrip(&response);
593 }
594
595 #[test]
596 fn test_page_response_serialization() {
597 let response: PageResponse<String> = PageResponse {
598 items: vec!["item1".to_string(), "item2".to_string()],
599 total: 100,
600 page: 1,
601 page_size: 20,
602 total_page: 5,
603 has_next: true,
604 };
605 test_roundtrip(&response);
606 }
607}