...
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
// Export models
|
||||
pub mod user;
|
||||
pub mod ticket;
|
||||
|
||||
// Re-export models for easier imports
|
||||
pub use user::User;
|
||||
pub use user::User;
|
||||
pub use ticket::{Ticket, TicketComment, TicketStatus, TicketPriority, TicketFilter};
|
177
actix_mvc_app/src/models/ticket.rs
Normal file
177
actix_mvc_app/src/models/ticket.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Represents the status of a support ticket
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum TicketStatus {
|
||||
/// Ticket has been opened but not yet addressed
|
||||
Open,
|
||||
/// Ticket is currently being worked on
|
||||
InProgress,
|
||||
/// Ticket is waiting for customer response
|
||||
WaitingForCustomer,
|
||||
/// Ticket has been resolved
|
||||
Resolved,
|
||||
/// Ticket has been closed without resolution
|
||||
Closed,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TicketStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TicketStatus::Open => write!(f, "Open"),
|
||||
TicketStatus::InProgress => write!(f, "In Progress"),
|
||||
TicketStatus::WaitingForCustomer => write!(f, "Waiting for Customer"),
|
||||
TicketStatus::Resolved => write!(f, "Resolved"),
|
||||
TicketStatus::Closed => write!(f, "Closed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the priority of a support ticket
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum TicketPriority {
|
||||
/// Low priority ticket
|
||||
Low,
|
||||
/// Medium priority ticket
|
||||
Medium,
|
||||
/// High priority ticket
|
||||
High,
|
||||
/// Critical priority ticket
|
||||
Critical,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TicketPriority {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TicketPriority::Low => write!(f, "Low"),
|
||||
TicketPriority::Medium => write!(f, "Medium"),
|
||||
TicketPriority::High => write!(f, "High"),
|
||||
TicketPriority::Critical => write!(f, "Critical"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a support ticket in the system
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Ticket {
|
||||
/// Unique identifier for the ticket
|
||||
pub id: String,
|
||||
/// User ID of the ticket creator
|
||||
pub user_id: i32,
|
||||
/// Title of the ticket
|
||||
pub title: String,
|
||||
/// Description of the issue
|
||||
pub description: String,
|
||||
/// Current status of the ticket
|
||||
pub status: TicketStatus,
|
||||
/// Priority level of the ticket
|
||||
pub priority: TicketPriority,
|
||||
/// When the ticket was created
|
||||
pub created_at: DateTime<Utc>,
|
||||
/// When the ticket was last updated
|
||||
pub updated_at: DateTime<Utc>,
|
||||
/// User ID of the assigned support agent (if any)
|
||||
pub assigned_to: Option<i32>,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
/// Creates a new ticket
|
||||
pub fn new(user_id: i32, title: String, description: String, priority: TicketPriority) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
user_id,
|
||||
title,
|
||||
description,
|
||||
status: TicketStatus::Open,
|
||||
priority,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
assigned_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the ticket status
|
||||
pub fn update_status(&mut self, status: TicketStatus) {
|
||||
self.status = status;
|
||||
self.updated_at = Utc::now();
|
||||
}
|
||||
|
||||
/// Assigns the ticket to a support agent
|
||||
pub fn assign(&mut self, agent_id: i32) {
|
||||
self.assigned_to = Some(agent_id);
|
||||
self.updated_at = Utc::now();
|
||||
}
|
||||
|
||||
/// Unassigns the ticket from any support agent
|
||||
pub fn unassign(&mut self) {
|
||||
self.assigned_to = None;
|
||||
self.updated_at = Utc::now();
|
||||
}
|
||||
|
||||
/// Updates the ticket priority
|
||||
pub fn update_priority(&mut self, priority: TicketPriority) {
|
||||
self.priority = priority;
|
||||
self.updated_at = Utc::now();
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a comment on a support ticket
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TicketComment {
|
||||
/// Unique identifier for the comment
|
||||
pub id: String,
|
||||
/// ID of the ticket this comment belongs to
|
||||
pub ticket_id: String,
|
||||
/// User ID of the comment author
|
||||
pub user_id: i32,
|
||||
/// Content of the comment
|
||||
pub content: String,
|
||||
/// When the comment was created
|
||||
pub created_at: DateTime<Utc>,
|
||||
/// Whether this comment is from a support agent
|
||||
pub is_support_response: bool,
|
||||
}
|
||||
|
||||
impl TicketComment {
|
||||
/// Creates a new ticket comment
|
||||
pub fn new(ticket_id: String, user_id: i32, content: String, is_support_response: bool) -> Self {
|
||||
Self {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
ticket_id,
|
||||
user_id,
|
||||
content,
|
||||
created_at: Utc::now(),
|
||||
is_support_response,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a filter for searching tickets
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TicketFilter {
|
||||
/// Filter by ticket status
|
||||
pub status: Option<TicketStatus>,
|
||||
/// Filter by ticket priority
|
||||
pub priority: Option<TicketPriority>,
|
||||
/// Filter by assigned agent
|
||||
pub assigned_to: Option<i32>,
|
||||
/// Filter by user who created the ticket
|
||||
pub user_id: Option<i32>,
|
||||
/// Search term for title and description
|
||||
pub search_term: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for TicketFilter {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
status: None,
|
||||
priority: None,
|
||||
assigned_to: None,
|
||||
user_id: None,
|
||||
search_term: None,
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use chrono::{DateTime, Utc};
|
||||
use bcrypt::{hash, verify, DEFAULT_COST};
|
||||
|
||||
/// Represents a user in the system
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -10,6 +11,9 @@ pub struct User {
|
||||
pub name: String,
|
||||
/// User's email address
|
||||
pub email: String,
|
||||
/// User's hashed password
|
||||
#[serde(skip_serializing)]
|
||||
pub password_hash: Option<String>,
|
||||
/// User's role in the system
|
||||
pub role: UserRole,
|
||||
/// When the user was created
|
||||
@@ -34,24 +38,56 @@ impl User {
|
||||
id: None,
|
||||
name,
|
||||
email,
|
||||
password_hash: None,
|
||||
role: UserRole::User,
|
||||
created_at: Some(Utc::now()),
|
||||
updated_at: Some(Utc::now()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new user with a password
|
||||
pub fn new_with_password(name: String, email: String, password: &str) -> Result<Self, bcrypt::BcryptError> {
|
||||
let password_hash = hash(password, DEFAULT_COST)?;
|
||||
|
||||
Ok(Self {
|
||||
id: None,
|
||||
name,
|
||||
email,
|
||||
password_hash: Some(password_hash),
|
||||
role: UserRole::User,
|
||||
created_at: Some(Utc::now()),
|
||||
updated_at: Some(Utc::now()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new admin user
|
||||
pub fn new_admin(name: String, email: String) -> Self {
|
||||
Self {
|
||||
id: None,
|
||||
name,
|
||||
email,
|
||||
password_hash: None,
|
||||
role: UserRole::Admin,
|
||||
created_at: Some(Utc::now()),
|
||||
updated_at: Some(Utc::now()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new admin user with a password
|
||||
pub fn new_admin_with_password(name: String, email: String, password: &str) -> Result<Self, bcrypt::BcryptError> {
|
||||
let password_hash = hash(password, DEFAULT_COST)?;
|
||||
|
||||
Ok(Self {
|
||||
id: None,
|
||||
name,
|
||||
email,
|
||||
password_hash: Some(password_hash),
|
||||
role: UserRole::Admin,
|
||||
created_at: Some(Utc::now()),
|
||||
updated_at: Some(Utc::now()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if the user is an admin
|
||||
pub fn is_admin(&self) -> bool {
|
||||
self.role == UserRole::Admin
|
||||
@@ -69,6 +105,38 @@ impl User {
|
||||
|
||||
self.updated_at = Some(Utc::now());
|
||||
}
|
||||
|
||||
/// Sets or updates the user's password
|
||||
pub fn set_password(&mut self, password: &str) -> Result<(), bcrypt::BcryptError> {
|
||||
let password_hash = hash(password, DEFAULT_COST)?;
|
||||
self.password_hash = Some(password_hash);
|
||||
self.updated_at = Some(Utc::now());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verifies if the provided password matches the stored hash
|
||||
pub fn verify_password(&self, password: &str) -> Result<bool, bcrypt::BcryptError> {
|
||||
match &self.password_hash {
|
||||
Some(hash) => verify(password, hash),
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents user login credentials
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct LoginCredentials {
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
/// Represents user registration data
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct RegistrationData {
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
pub password_confirmation: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Reference in New Issue
Block a user