use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use uuid::Uuid; use validator::Validate; // --- Auth DTOs --- #[derive(Debug, Deserialize, Validate, ToSchema)] pub struct LoginReq { #[validate(length(min = 1, message = "用户名不能为空"))] pub username: String, #[validate(length(min = 1, message = "密码不能为空"))] pub password: String, } #[derive(Debug, Serialize, ToSchema)] pub struct LoginResp { pub access_token: String, pub refresh_token: String, pub expires_in: u64, pub user: UserResp, } #[derive(Debug, Deserialize, ToSchema)] pub struct RefreshReq { pub refresh_token: String, } // --- User DTOs --- #[derive(Debug, Serialize, ToSchema)] pub struct UserResp { pub id: Uuid, pub username: String, pub email: Option, pub phone: Option, pub display_name: Option, pub avatar_url: Option, pub status: String, pub roles: Vec, pub version: i32, } #[derive(Debug, Deserialize, Validate, ToSchema)] pub struct CreateUserReq { #[validate(length(min = 1, max = 50))] pub username: String, #[validate(length(min = 6, max = 128))] pub password: String, #[validate(email)] pub email: Option, pub phone: Option, pub display_name: Option, } #[derive(Debug, Deserialize, ToSchema)] pub struct UpdateUserReq { pub email: Option, pub phone: Option, pub display_name: Option, pub status: Option, pub version: i32, } // --- Role DTOs --- #[derive(Debug, Serialize, ToSchema)] pub struct RoleResp { pub id: Uuid, pub name: String, pub code: String, pub description: Option, pub is_system: bool, pub version: i32, } #[derive(Debug, Deserialize, Validate, ToSchema)] pub struct CreateRoleReq { #[validate(length(min = 1, max = 50))] pub name: String, #[validate(length(min = 1, max = 50))] pub code: String, pub description: Option, } #[derive(Debug, Deserialize, ToSchema)] pub struct UpdateRoleReq { pub name: Option, pub description: Option, pub version: i32, } #[derive(Debug, Deserialize, ToSchema)] pub struct AssignRolesReq { pub role_ids: Vec, } // --- Permission DTOs --- #[derive(Debug, Serialize, ToSchema)] pub struct PermissionResp { pub id: Uuid, pub code: String, pub name: String, pub resource: String, pub action: String, pub description: Option, } #[derive(Debug, Deserialize, ToSchema)] pub struct AssignPermissionsReq { pub permission_ids: Vec, } // --- Organization DTOs --- #[derive(Debug, Serialize, ToSchema)] pub struct OrganizationResp { pub id: Uuid, pub name: String, pub code: Option, pub parent_id: Option, pub path: Option, pub level: i32, pub sort_order: i32, pub children: Vec, pub version: i32, } #[derive(Debug, Deserialize, Validate, ToSchema)] pub struct CreateOrganizationReq { #[validate(length(min = 1))] pub name: String, pub code: Option, pub parent_id: Option, pub sort_order: Option, } #[derive(Debug, Deserialize, ToSchema)] pub struct UpdateOrganizationReq { pub name: Option, pub code: Option, pub sort_order: Option, pub version: i32, } // --- Department DTOs --- #[derive(Debug, Serialize, ToSchema)] pub struct DepartmentResp { pub id: Uuid, pub org_id: Uuid, pub name: String, pub code: Option, pub parent_id: Option, pub manager_id: Option, pub path: Option, pub sort_order: i32, pub children: Vec, pub version: i32, } #[derive(Debug, Deserialize, Validate, ToSchema)] pub struct CreateDepartmentReq { #[validate(length(min = 1))] pub name: String, pub code: Option, pub parent_id: Option, pub manager_id: Option, pub sort_order: Option, } #[derive(Debug, Deserialize, ToSchema)] pub struct UpdateDepartmentReq { pub name: Option, pub code: Option, pub manager_id: Option, pub sort_order: Option, pub version: i32, } // --- Position DTOs --- #[derive(Debug, Serialize, ToSchema)] pub struct PositionResp { pub id: Uuid, pub dept_id: Uuid, pub name: String, pub code: Option, pub level: i32, pub sort_order: i32, pub version: i32, } #[derive(Debug, Deserialize, Validate, ToSchema)] pub struct CreatePositionReq { #[validate(length(min = 1))] pub name: String, pub code: Option, pub level: Option, pub sort_order: Option, } #[derive(Debug, Deserialize, ToSchema)] pub struct UpdatePositionReq { pub name: Option, pub code: Option, pub level: Option, pub sort_order: Option, pub version: i32, } #[cfg(test)] mod tests { use super::*; use validator::Validate; #[test] fn login_req_valid() { let req = LoginReq { username: "admin".to_string(), password: "password123".to_string(), }; assert!(req.validate().is_ok()); } #[test] fn login_req_empty_username_fails() { let req = LoginReq { username: "".to_string(), password: "password123".to_string(), }; let result = req.validate(); assert!(result.is_err()); } #[test] fn login_req_empty_password_fails() { let req = LoginReq { username: "admin".to_string(), password: "".to_string(), }; assert!(req.validate().is_err()); } #[test] fn create_user_req_valid() { let req = CreateUserReq { username: "alice".to_string(), password: "secret123".to_string(), email: Some("alice@example.com".to_string()), phone: None, display_name: Some("Alice".to_string()), }; assert!(req.validate().is_ok()); } #[test] fn create_user_req_short_password_fails() { let req = CreateUserReq { username: "bob".to_string(), password: "12345".to_string(), // min 6 email: None, phone: None, display_name: None, }; assert!(req.validate().is_err()); } #[test] fn create_user_req_empty_username_fails() { let req = CreateUserReq { username: "".to_string(), password: "secret123".to_string(), email: None, phone: None, display_name: None, }; assert!(req.validate().is_err()); } #[test] fn create_user_req_invalid_email_fails() { let req = CreateUserReq { username: "charlie".to_string(), password: "secret123".to_string(), email: Some("not-an-email".to_string()), phone: None, display_name: None, }; assert!(req.validate().is_err()); } #[test] fn create_user_req_long_username_fails() { let req = CreateUserReq { username: "a".repeat(51), // max 50 password: "secret123".to_string(), email: None, phone: None, display_name: None, }; assert!(req.validate().is_err()); } #[test] fn create_role_req_valid() { let req = CreateRoleReq { name: "管理员".to_string(), code: "admin".to_string(), description: Some("系统管理员".to_string()), }; assert!(req.validate().is_ok()); } #[test] fn create_role_req_empty_name_fails() { let req = CreateRoleReq { name: "".to_string(), code: "admin".to_string(), description: None, }; assert!(req.validate().is_err()); } #[test] fn create_role_req_empty_code_fails() { let req = CreateRoleReq { name: "管理员".to_string(), code: "".to_string(), description: None, }; assert!(req.validate().is_err()); } #[test] fn create_org_req_valid() { let req = CreateOrganizationReq { name: "总部".to_string(), code: Some("HQ".to_string()), parent_id: None, sort_order: Some(0), }; assert!(req.validate().is_ok()); } #[test] fn create_org_req_empty_name_fails() { let req = CreateOrganizationReq { name: "".to_string(), code: None, parent_id: None, sort_order: None, }; assert!(req.validate().is_err()); } #[test] fn create_dept_req_valid() { let req = CreateDepartmentReq { name: "技术部".to_string(), code: Some("TECH".to_string()), parent_id: None, manager_id: None, sort_order: Some(1), }; assert!(req.validate().is_ok()); } #[test] fn create_position_req_valid() { let req = CreatePositionReq { name: "高级工程师".to_string(), code: Some("SENIOR".to_string()), level: Some(3), sort_order: None, }; assert!(req.validate().is_ok()); } #[test] fn create_position_req_empty_name_fails() { let req = CreatePositionReq { name: "".to_string(), code: None, level: None, sort_order: None, }; assert!(req.validate().is_err()); } }