use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentBase {
pub document_id: String,
pub title: String,
pub doc_type: DocumentType,
pub create_time: DateTime<Utc>,
pub modify_time: DateTime<Utc>,
pub creator: UserInfo,
pub modifier: Option<UserInfo>,
pub status: DocumentStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum DocumentType {
Doc,
Sheet,
Slide,
Mindnote,
Flowchart,
Bitable,
Wiki,
Folder,
Other(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum DocumentStatus {
Normal,
Recycle,
Deleted,
Encrypted,
Archived,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UserInfo {
pub user_id: String,
pub name: String,
pub email: Option<String>,
pub avatar_url: Option<String>,
pub department: Option<DepartmentInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DepartmentInfo {
pub department_id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FileInfo {
pub file_id: String,
pub name: String,
pub size: u64,
pub mime_type: String,
pub url: String,
pub download_token: Option<String>,
pub thumbnail_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Permission {
pub permission_type: PermissionType,
pub can_read: bool,
pub can_write: bool,
pub can_delete: bool,
pub can_share: bool,
pub expire_time: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum PermissionType {
Owner,
Admin,
Editor,
Commenter,
Viewer,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ShareInfo {
pub share_url: String,
pub share_token: String,
pub permission_type: PermissionType,
pub need_password: bool,
pub expire_time: Option<DateTime<Utc>>,
pub visit_limit: Option<u32>,
pub visit_count: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VersionInfo {
pub version: i32,
pub version_name: String,
pub create_time: DateTime<Utc>,
pub creator: UserInfo,
pub description: Option<String>,
pub is_current: bool,
pub size: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
pub document: DocumentBase,
pub snippets: Vec<String>,
pub score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DocumentStats {
pub view_count: u64,
pub edit_count: u64,
pub comment_count: u64,
pub share_count: u64,
pub download_count: u64,
pub last_stats_time: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperationRecord {
pub record_id: String,
pub operation_type: OperationType,
pub operation_time: DateTime<Utc>,
pub operator: UserInfo,
pub description: Option<String>,
pub ip_address: Option<String>,
pub device_info: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum OperationType {
Create,
Read,
Update,
Delete,
Share,
Download,
Copy,
Move,
Rename,
PermissionChange,
Other(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ImportExportTask {
pub task_id: String,
pub task_type: TaskType,
pub status: TaskStatus,
pub progress: u8,
pub start_time: DateTime<Utc>,
pub end_time: Option<DateTime<Utc>>,
pub error_message: Option<String>,
pub result_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum TaskType {
Import,
Export,
Convert,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum TaskStatus {
Pending,
Processing,
Completed,
Failed,
Canceled,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CommonResponse<T: PartialEq> {
pub success: bool,
pub data: Option<T>,
pub error: Option<ErrorInfo>,
pub request_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ErrorInfo {
pub code: i32,
pub message: String,
pub details: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PageRequest {
pub page: u32,
pub page_size: u32,
pub sort_field: Option<String>,
pub sort_direction: Option<SortDirection>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum SortDirection {
Asc,
Desc,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PageResponse<T: PartialEq> {
pub items: Vec<T>,
pub total: u64,
pub page: u32,
pub page_size: u32,
pub total_page: u32,
pub has_next: bool,
}
impl Default for PageRequest {
fn default() -> Self {
Self {
page: 1,
page_size: 20,
sort_field: None,
sort_direction: Some(SortDirection::Desc),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_roundtrip<T: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug>(
original: &T,
) {
let json = serde_json::to_string(original).expect("序列化失败");
let deserialized: T = serde_json::from_str(&json).expect("反序列化失败");
assert_eq!(original, &deserialized, "roundtrip 后数据不一致");
}
#[test]
fn test_document_type_serialization() {
test_roundtrip(&DocumentType::Doc);
test_roundtrip(&DocumentType::Sheet);
test_roundtrip(&DocumentType::Other("custom".to_string()));
}
#[test]
fn test_document_status_serialization() {
test_roundtrip(&DocumentStatus::Normal);
test_roundtrip(&DocumentStatus::Archived);
}
#[test]
fn test_permission_type_serialization() {
test_roundtrip(&PermissionType::Owner);
test_roundtrip(&PermissionType::Viewer);
}
#[test]
fn test_operation_type_serialization() {
test_roundtrip(&OperationType::Create);
test_roundtrip(&OperationType::Other("custom_op".to_string()));
}
#[test]
fn test_task_type_serialization() {
test_roundtrip(&TaskType::Import);
test_roundtrip(&TaskType::Export);
}
#[test]
fn test_task_status_serialization() {
test_roundtrip(&TaskStatus::Pending);
test_roundtrip(&TaskStatus::Completed);
}
#[test]
fn test_sort_direction_serialization() {
test_roundtrip(&SortDirection::Asc);
test_roundtrip(&SortDirection::Desc);
}
#[test]
fn test_user_info_serialization() {
let user = UserInfo {
user_id: "user123".to_string(),
name: "测试用户".to_string(),
email: Some("test@example.com".to_string()),
avatar_url: None,
department: Some(DepartmentInfo {
department_id: "dept456".to_string(),
name: "技术部".to_string(),
}),
};
test_roundtrip(&user);
}
#[test]
fn test_department_info_serialization() {
let dept = DepartmentInfo {
department_id: "dept789".to_string(),
name: "产品部".to_string(),
};
test_roundtrip(&dept);
}
#[test]
fn test_file_info_serialization() {
let file = FileInfo {
file_id: "file123".to_string(),
name: "test.pdf".to_string(),
size: 1024000,
mime_type: "application/pdf".to_string(),
url: "https://example.com/file".to_string(),
download_token: Some("token123".to_string()),
thumbnail_url: None,
};
test_roundtrip(&file);
}
#[test]
fn test_permission_serialization() {
let perm = Permission {
permission_type: PermissionType::Editor,
can_read: true,
can_write: true,
can_delete: false,
can_share: true,
expire_time: None,
};
test_roundtrip(&perm);
}
#[test]
fn test_share_info_serialization() {
let share = ShareInfo {
share_url: "https://share.example.com/doc".to_string(),
share_token: "token456".to_string(),
permission_type: PermissionType::Viewer,
need_password: false,
expire_time: None,
visit_limit: Some(100),
visit_count: 10,
};
test_roundtrip(&share);
}
#[test]
fn test_version_info_serialization() {
let version = VersionInfo {
version: 1,
version_name: "v1.0".to_string(),
create_time: Utc::now(),
creator: UserInfo {
user_id: "user001".to_string(),
name: "张三".to_string(),
email: None,
avatar_url: None,
department: None,
},
description: Some("初始版本".to_string()),
is_current: true,
size: 2048,
};
test_roundtrip(&version);
}
#[test]
fn test_document_stats_serialization() {
let stats = DocumentStats {
view_count: 100,
edit_count: 20,
comment_count: 5,
share_count: 10,
download_count: 3,
last_stats_time: Utc::now(),
};
test_roundtrip(&stats);
}
#[test]
fn test_import_export_task_serialization() {
let task = ImportExportTask {
task_id: "task789".to_string(),
task_type: TaskType::Convert,
status: TaskStatus::Processing,
progress: 50,
start_time: Utc::now(),
end_time: None,
error_message: None,
result_url: Some("https://result.example.com".to_string()),
};
test_roundtrip(&task);
}
#[test]
fn test_error_info_serialization() {
let mut details = HashMap::new();
details.insert("field".to_string(), serde_json::json!("value"));
let error = ErrorInfo {
code: 404,
message: "Not Found".to_string(),
details: Some(details),
};
test_roundtrip(&error);
}
#[test]
fn test_page_request_serialization() {
let req = PageRequest {
page: 1,
page_size: 20,
sort_field: Some("create_time".to_string()),
sort_direction: Some(SortDirection::Desc),
};
test_roundtrip(&req);
}
#[test]
fn test_page_request_default() {
let default_req = PageRequest::default();
assert_eq!(default_req.page, 1);
assert_eq!(default_req.page_size, 20);
}
#[test]
fn test_common_response_serialization() {
let response: CommonResponse<String> = CommonResponse {
success: true,
data: Some("test data".to_string()),
error: None,
request_id: "req123".to_string(),
};
test_roundtrip(&response);
}
#[test]
fn test_page_response_serialization() {
let response: PageResponse<String> = PageResponse {
items: vec!["item1".to_string(), "item2".to_string()],
total: 100,
page: 1,
page_size: 20,
total_page: 5,
has_next: true,
};
test_roundtrip(&response);
}
}