388 lines
12 KiB
Rust
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,
|
|
}
|
|
}
|
|
}
|