hostbasket/actix_mvc_app/src/models/flow.rs

388 lines
12 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
/// Status of a flow
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum FlowStatus {
/// Flow is in progress
InProgress,
/// Flow is completed
Completed,
/// Flow is stuck at a step
Stuck,
/// Flow is cancelled
Cancelled,
}
impl std::fmt::Display for FlowStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FlowStatus::InProgress => write!(f, "In Progress"),
FlowStatus::Completed => write!(f, "Completed"),
FlowStatus::Stuck => write!(f, "Stuck"),
FlowStatus::Cancelled => write!(f, "Cancelled"),
}
}
}
/// Type of flow
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum FlowType {
/// Company registration flow
CompanyRegistration,
/// User onboarding flow
UserOnboarding,
/// Service activation flow
ServiceActivation,
/// Payment processing flow
PaymentProcessing,
/// Asset tokenization flow
AssetTokenization,
/// Certification flow
Certification,
/// License application flow
LicenseApplication,
}
impl std::fmt::Display for FlowType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FlowType::CompanyRegistration => write!(f, "Company Registration"),
FlowType::UserOnboarding => write!(f, "User Onboarding"),
FlowType::ServiceActivation => write!(f, "Service Activation"),
FlowType::PaymentProcessing => write!(f, "Payment Processing"),
FlowType::AssetTokenization => write!(f, "Asset Tokenization"),
FlowType::Certification => write!(f, "Certification"),
FlowType::LicenseApplication => write!(f, "License Application"),
}
}
}
/// Filter for flows
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FlowFilter {
/// All flows
All,
/// Only in progress flows
InProgress,
/// Only completed flows
Completed,
/// Only stuck flows
Stuck,
/// Only cancelled flows
Cancelled,
/// Flows of a specific type
ByType(FlowType),
}
impl std::fmt::Display for FlowFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FlowFilter::All => write!(f, "All"),
FlowFilter::InProgress => write!(f, "In Progress"),
FlowFilter::Completed => write!(f, "Completed"),
FlowFilter::Stuck => write!(f, "Stuck"),
FlowFilter::Cancelled => write!(f, "Cancelled"),
FlowFilter::ByType(flow_type) => write!(f, "Type: {}", flow_type),
}
}
}
/// A step in a flow
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowStep {
/// Step ID
pub id: String,
/// Step name
pub name: String,
/// Step description
pub description: String,
/// Step status
pub status: StepStatus,
/// Step order in the flow
pub order: u32,
/// Step started at
pub started_at: Option<DateTime<Utc>>,
/// Step completed at
pub completed_at: Option<DateTime<Utc>>,
/// Step logs
pub logs: Vec<FlowLog>,
}
impl FlowStep {
/// Creates a new flow step
pub fn new(name: String, description: String, order: u32) -> Self {
Self {
id: Uuid::new_v4().to_string(),
name,
description,
status: StepStatus::Pending,
order,
started_at: None,
completed_at: None,
logs: Vec::new(),
}
}
/// Starts the step
pub fn start(&mut self) {
self.status = StepStatus::InProgress;
self.started_at = Some(Utc::now());
self.add_log("Step started".to_string());
}
/// Completes the step
pub fn complete(&mut self) {
self.status = StepStatus::Completed;
self.completed_at = Some(Utc::now());
self.add_log("Step completed".to_string());
}
/// Marks the step as stuck
pub fn mark_stuck(&mut self, reason: String) {
self.status = StepStatus::Stuck;
self.add_log(format!("Step stuck: {}", reason));
}
/// Adds a log entry to the step
pub fn add_log(&mut self, message: String) {
self.logs.push(FlowLog::new(message));
}
}
/// Status of a step in a flow
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum StepStatus {
/// Step is pending
Pending,
/// Step is in progress
InProgress,
/// Step is completed
Completed,
/// Step is stuck
Stuck,
/// Step is skipped
Skipped,
}
impl std::fmt::Display for StepStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StepStatus::Pending => write!(f, "Pending"),
StepStatus::InProgress => write!(f, "In Progress"),
StepStatus::Completed => write!(f, "Completed"),
StepStatus::Stuck => write!(f, "Stuck"),
StepStatus::Skipped => write!(f, "Skipped"),
}
}
}
/// A log entry in a flow step
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowLog {
/// Log ID
pub id: String,
/// Log message
pub message: String,
/// Log timestamp
pub timestamp: DateTime<Utc>,
}
impl FlowLog {
/// Creates a new flow log
pub fn new(message: String) -> Self {
Self {
id: Uuid::new_v4().to_string(),
message,
timestamp: Utc::now(),
}
}
}
/// A flow with multiple steps
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Flow {
/// Flow ID
pub id: String,
/// Flow name
pub name: String,
/// Flow description
pub description: String,
/// Flow type
pub flow_type: FlowType,
/// Flow status
pub status: FlowStatus,
/// Flow owner ID
pub owner_id: String,
/// Flow owner name
pub owner_name: String,
/// Flow created at
pub created_at: DateTime<Utc>,
/// Flow updated at
pub updated_at: DateTime<Utc>,
/// Flow completed at
pub completed_at: Option<DateTime<Utc>>,
/// Flow steps
pub steps: Vec<FlowStep>,
/// Progress percentage
pub progress_percentage: u8,
/// Current step
pub current_step: Option<FlowStep>,
}
impl Flow {
/// Creates a new flow
pub fn new(name: &str, description: &str, flow_type: FlowType, owner_id: &str, owner_name: &str) -> Self {
let id = Uuid::new_v4().to_string();
let now = Utc::now();
let steps = vec![
FlowStep::new("Initialization".to_string(), "Setting up the flow".to_string(), 1),
FlowStep::new("Processing".to_string(), "Processing the flow data".to_string(), 2),
FlowStep::new("Finalization".to_string(), "Completing the flow".to_string(), 3),
];
// Set the first step as in progress
let mut flow = Self {
id,
name: name.to_string(),
description: description.to_string(),
flow_type,
status: FlowStatus::InProgress,
owner_id: owner_id.to_string(),
owner_name: owner_name.to_string(),
steps,
created_at: now,
updated_at: now,
completed_at: None,
progress_percentage: 0,
current_step: None,
};
// Calculate progress and set current step
flow.update_progress();
flow
}
fn update_progress(&mut self) {
// Calculate progress percentage
let total_steps = self.steps.len();
if total_steps == 0 {
self.progress_percentage = 100;
return;
}
let completed_steps = self.steps.iter().filter(|s| s.status == StepStatus::Completed).count();
self.progress_percentage = ((completed_steps as f32 / total_steps as f32) * 100.0) as u8;
// Find current step
self.current_step = self.steps.iter()
.find(|s| s.status == StepStatus::InProgress)
.cloned();
// Update flow status based on steps
if self.progress_percentage == 100 {
self.status = FlowStatus::Completed;
} else if self.steps.iter().any(|s| s.status == StepStatus::Stuck) {
self.status = FlowStatus::Stuck;
} else {
self.status = FlowStatus::InProgress;
}
}
pub fn advance_step(&mut self) -> Result<(), String> {
let current_index = self.steps.iter().position(|s| s.status == StepStatus::InProgress);
if let Some(index) = current_index {
// Mark current step as completed
self.steps[index].status = StepStatus::Completed;
self.steps[index].completed_at = Some(Utc::now());
// If there's a next step, mark it as in progress
if index + 1 < self.steps.len() {
self.steps[index + 1].status = StepStatus::InProgress;
self.steps[index + 1].started_at = Some(Utc::now());
}
self.updated_at = Utc::now();
self.update_progress();
Ok(())
} else {
Err("No step in progress to advance".to_string())
}
}
pub fn mark_step_stuck(&mut self, reason: &str) -> Result<(), String> {
let current_index = self.steps.iter().position(|s| s.status == StepStatus::InProgress);
if let Some(index) = current_index {
// Mark current step as stuck
self.steps[index].status = StepStatus::Stuck;
// Add a log entry for the stuck reason
self.steps[index].add_log(reason.to_string());
self.updated_at = Utc::now();
self.update_progress();
Ok(())
} else {
Err("No step in progress to mark as stuck".to_string())
}
}
pub fn add_log_to_step(&mut self, step_id: &str, message: &str) -> Result<(), String> {
if let Some(step) = self.steps.iter_mut().find(|s| s.id == step_id) {
step.add_log(message.to_string());
self.updated_at = Utc::now();
Ok(())
} else {
Err(format!("Step with ID {} not found", step_id))
}
}
}
/// Flow statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowStatistics {
/// Total number of flows
pub total_flows: usize,
/// Number of in progress flows
pub in_progress_flows: usize,
/// Number of completed flows
pub completed_flows: usize,
/// Number of stuck flows
pub stuck_flows: usize,
/// Number of cancelled flows
pub cancelled_flows: usize,
}
impl FlowStatistics {
/// Creates new flow statistics
pub fn new(flows: &[Flow]) -> Self {
let total_flows = flows.len();
let in_progress_flows = flows.iter()
.filter(|flow| flow.status == FlowStatus::InProgress)
.count();
let completed_flows = flows.iter()
.filter(|flow| flow.status == FlowStatus::Completed)
.count();
let stuck_flows = flows.iter()
.filter(|flow| flow.status == FlowStatus::Stuck)
.count();
let cancelled_flows = flows.iter()
.filter(|flow| flow.status == FlowStatus::Cancelled)
.count();
Self {
total_flows,
in_progress_flows,
completed_flows,
stuck_flows,
cancelled_flows,
}
}
}