use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageResponse<T> {
pub items: Vec<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_more: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UserType {
Employee,
Visitor,
Contractor,
Temporary,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UserStatus {
Active,
Disabled,
Expired,
Pending,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AcsUser {
pub user_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub employee_id: Option<String>,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_type: Option<UserType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<UserStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub department: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub phone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_face_image: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RuleStatus {
Active,
Disabled,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuleExternal {
pub rule_id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<RuleStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub device_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisitorStatus {
Active,
Expired,
CheckedOut,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Visitor {
pub visitor_id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub phone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub company: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub purpose: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub host_user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub host_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<VisitorStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DeviceType {
AccessControl,
FaceRecognition,
Turnstile,
ElevatorController,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DeviceStatus {
Online,
Offline,
Error,
Maintenance,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Device {
pub device_id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub device_type: Option<DeviceType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<DeviceStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ip_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mac_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AccessType {
Entry,
Exit,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AccessMethod {
FaceRecognition,
Card,
Password,
Fingerprint,
Manual,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AccessResult {
Success,
Failed,
Denied,
Timeout,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccessRecord {
pub record_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_name: Option<String>,
pub device_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub device_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub access_type: Option<AccessType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub access_method: Option<AccessMethod>,
pub result: AccessResult,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_face_image: Option<bool>,
pub access_time: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FaceImage {
pub image_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_size: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uploaded_at: Option<i64>,
}
#[cfg(test)]
#[allow(unused_variables, unused_unsafe)]
mod tests {
use super::*;
#[test]
fn test_page_response_serialization() {
let page_response = PageResponse {
items: vec!["item1".to_string(), "item2".to_string()],
page_token: Some("next_page".to_string()),
has_more: Some(true),
};
let serialized = serde_json::to_string(&page_response).unwrap();
let deserialized: PageResponse<String> = serde_json::from_str(&serialized).unwrap();
assert_eq!(page_response.items.len(), deserialized.items.len());
assert_eq!(page_response.page_token, deserialized.page_token);
assert_eq!(page_response.has_more, deserialized.has_more);
}
#[test]
fn test_user_type_serialization() {
let types = vec![
UserType::Employee,
UserType::Visitor,
UserType::Contractor,
UserType::Temporary,
];
for user_type in types {
let serialized = serde_json::to_string(&user_type).unwrap();
let deserialized: UserType = serde_json::from_str(&serialized).unwrap();
match (user_type, deserialized) {
(UserType::Employee, UserType::Employee) => {}
(UserType::Visitor, UserType::Visitor) => {}
(UserType::Contractor, UserType::Contractor) => {}
(UserType::Temporary, UserType::Temporary) => {}
_ => panic!("Serialization/deserialization failed"),
}
}
}
#[test]
fn test_user_status_serialization() {
let statuses = vec![
UserStatus::Active,
UserStatus::Disabled,
UserStatus::Expired,
UserStatus::Pending,
];
for status in statuses {
let serialized = serde_json::to_string(&status).unwrap();
let deserialized: UserStatus = serde_json::from_str(&serialized).unwrap();
match (status, deserialized) {
(UserStatus::Active, UserStatus::Active) => {}
(UserStatus::Disabled, UserStatus::Disabled) => {}
(UserStatus::Expired, UserStatus::Expired) => {}
(UserStatus::Pending, UserStatus::Pending) => {}
_ => panic!("Serialization/deserialization failed"),
}
}
}
#[test]
fn test_acs_user_serialization() {
let user = AcsUser {
user_id: "user_123".to_string(),
employee_id: Some("emp_456".to_string()),
name: "张三".to_string(),
user_type: Some(UserType::Employee),
status: Some(UserStatus::Active),
department: Some("技术部".to_string()),
phone: Some("13800138000".to_string()),
email: Some("zhangsan@example.com".to_string()),
has_face_image: Some(true),
created_at: Some(1640995200),
updated_at: Some(1672531200),
};
let serialized = serde_json::to_string(&user).unwrap();
let deserialized: AcsUser = serde_json::from_str(&serialized).unwrap();
assert_eq!(user.user_id, deserialized.user_id);
assert_eq!(user.employee_id, deserialized.employee_id);
assert_eq!(user.name, deserialized.name);
assert_eq!(user.email, deserialized.email);
assert_eq!(user.phone, deserialized.phone);
assert_eq!(user.department, deserialized.department);
}
#[test]
fn test_rule_external_serialization() {
let rule = RuleExternal {
rule_id: "rule_123".to_string(),
name: "技术部门禁".to_string(),
description: Some("技术部门员工门禁权限".to_string()),
status: Some(RuleStatus::Active),
device_ids: Some(vec!["device1".to_string(), "device2".to_string()]),
user_ids: Some(vec!["user1".to_string(), "user2".to_string()]),
start_time: Some(1640995200),
end_time: Some(1672531200),
created_at: Some(1640995200),
updated_at: Some(1672531200),
};
let serialized = serde_json::to_string(&rule).unwrap();
let deserialized: RuleExternal = serde_json::from_str(&serialized).unwrap();
assert_eq!(rule.rule_id, deserialized.rule_id);
assert_eq!(rule.name, deserialized.name);
assert_eq!(rule.description, deserialized.description);
assert_eq!(rule.device_ids, deserialized.device_ids);
assert_eq!(rule.user_ids, deserialized.user_ids);
}
#[test]
fn test_visitor_serialization() {
let visitor = Visitor {
visitor_id: "visitor_123".to_string(),
name: "李四".to_string(),
phone: Some("13900139000".to_string()),
company: Some("访客公司".to_string()),
purpose: Some("业务洽谈".to_string()),
host_user_id: Some("host_456".to_string()),
host_name: Some("王五".to_string()),
status: Some(VisitorStatus::Active),
start_time: Some(1640995200),
end_time: Some(1672531200),
created_at: Some(1640995200),
};
let serialized = serde_json::to_string(&visitor).unwrap();
let deserialized: Visitor = serde_json::from_str(&serialized).unwrap();
assert_eq!(visitor.visitor_id, deserialized.visitor_id);
assert_eq!(visitor.name, deserialized.name);
assert_eq!(visitor.phone, deserialized.phone);
assert_eq!(visitor.company, deserialized.company);
assert_eq!(visitor.purpose, deserialized.purpose);
assert_eq!(visitor.host_user_id, deserialized.host_user_id);
}
#[test]
fn test_device_serialization() {
let device = Device {
device_id: "device_123".to_string(),
name: "前台门禁".to_string(),
device_type: Some(DeviceType::FaceRecognition),
status: Some(DeviceStatus::Online),
location: Some("大厅前台".to_string()),
description: Some("前台入口门禁设备".to_string()),
ip_address: Some("192.168.1.100".to_string()),
mac_address: Some("00:11:22:33:44:55".to_string()),
created_at: Some(1640995200),
updated_at: Some(1672531200),
};
let serialized = serde_json::to_string(&device).unwrap();
let deserialized: Device = serde_json::from_str(&serialized).unwrap();
assert_eq!(device.device_id, deserialized.device_id);
assert_eq!(device.name, deserialized.name);
assert_eq!(device.location, deserialized.location);
assert_eq!(device.description, deserialized.description);
assert_eq!(device.ip_address, deserialized.ip_address);
assert_eq!(device.mac_address, deserialized.mac_address);
}
#[test]
fn test_access_record_serialization() {
let record = AccessRecord {
record_id: "record_123".to_string(),
user_id: Some("user_456".to_string()),
user_name: Some("张三".to_string()),
device_id: "device_789".to_string(),
device_name: Some("前台门禁".to_string()),
access_type: Some(AccessType::Entry),
access_method: Some(AccessMethod::FaceRecognition),
result: AccessResult::Success,
has_face_image: Some(true),
access_time: 1672531200,
created_at: Some(1672531200),
};
let serialized = serde_json::to_string(&record).unwrap();
let deserialized: AccessRecord = serde_json::from_str(&serialized).unwrap();
assert_eq!(record.record_id, deserialized.record_id);
assert_eq!(record.user_id, deserialized.user_id);
assert_eq!(record.user_name, deserialized.user_name);
assert_eq!(record.device_id, deserialized.device_id);
assert_eq!(record.device_name, deserialized.device_name);
assert_eq!(record.access_time, deserialized.access_time);
}
#[test]
fn test_face_image_serialization() {
let face_image = FaceImage {
image_id: "img_123".to_string(),
image_content: Some("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==".to_string()),
image_format: Some("PNG".to_string()),
file_size: Some(68),
uploaded_at: Some(1672531200),
};
let serialized = serde_json::to_string(&face_image).unwrap();
let deserialized: FaceImage = serde_json::from_str(&serialized).unwrap();
assert_eq!(face_image.image_id, deserialized.image_id);
assert_eq!(face_image.image_content, deserialized.image_content);
assert_eq!(face_image.image_format, deserialized.image_format);
assert_eq!(face_image.file_size, deserialized.file_size);
assert_eq!(face_image.uploaded_at, deserialized.uploaded_at);
}
#[test]
fn test_enum_serialization_formats() {
assert_eq!(
serde_json::to_string(&AccessType::Entry).unwrap(),
"\"entry\""
);
assert_eq!(
serde_json::to_string(&AccessType::Exit).unwrap(),
"\"exit\""
);
assert_eq!(
serde_json::to_string(&AccessMethod::FaceRecognition).unwrap(),
"\"face_recognition\""
);
assert_eq!(
serde_json::to_string(&AccessMethod::Card).unwrap(),
"\"card\""
);
assert_eq!(
serde_json::to_string(&AccessResult::Success).unwrap(),
"\"success\""
);
assert_eq!(
serde_json::to_string(&AccessResult::Failed).unwrap(),
"\"failed\""
);
}
#[test]
fn test_models_with_none_values() {
let user = AcsUser {
user_id: "user_123".to_string(),
employee_id: None,
name: "Required Name".to_string(),
email: None,
phone: None,
department: None,
user_type: None,
status: None,
has_face_image: None,
created_at: None,
updated_at: None,
};
let serialized = serde_json::to_string(&user).unwrap();
let deserialized: AcsUser = serde_json::from_str(&serialized).unwrap();
assert_eq!(user.user_id, deserialized.user_id);
assert!(deserialized.employee_id.is_none());
assert_eq!(user.name, deserialized.name);
assert!(deserialized.email.is_none());
}
#[test]
fn test_debug_trait_for_models() {
let user = AcsUser {
user_id: "test".to_string(),
employee_id: None,
name: "Test User".to_string(),
email: None,
phone: None,
department: None,
user_type: None,
status: None,
has_face_image: None,
created_at: None,
updated_at: None,
};
let debug_string = format!("{:?}", user);
assert!(debug_string.contains("AcsUser"));
assert!(debug_string.contains("test"));
}
#[test]
fn test_clone_trait_for_models() {
let original_user = AcsUser {
user_id: "test".to_string(),
employee_id: Some("emp123".to_string()),
name: "Test User".to_string(),
email: None,
phone: None,
department: None,
user_type: None,
status: None,
has_face_image: None,
created_at: None,
updated_at: None,
};
let cloned_user = original_user.clone();
assert_eq!(original_user.user_id, cloned_user.user_id);
assert_eq!(original_user.employee_id, cloned_user.employee_id);
assert_eq!(original_user.name, cloned_user.name);
}
}