use async_trait::async_trait;
use chrono;
use serde_json;
use std::sync::Arc;
use crate::core::engine::generate_id;
use crate::core::{
TaskState, TaskStatus, WorkflowDefinition, WorkflowResult, WorkflowState, WorkflowStatus,
};
use crate::db::WorkflowDatabase;
pub struct LeaveRequestWorkflow {
database: Arc<dyn WorkflowDatabase>,
}
impl LeaveRequestWorkflow {
pub fn new(database: Arc<dyn WorkflowDatabase>) -> Self {
Self { database }
}
fn task_name(locale: &str, stage: &str) -> &'static str {
match (locale, stage) {
("zh-CN", "manager") => "直属经理审批",
("zh-CN", "hr") => "HR 备案",
("zh-TW", "manager") => "直屬主管審批",
("zh-TW", "hr") => "HR 備案",
(_, "manager") => "Manager Approval",
(_, "hr") => "HR Approval",
_ => "Task",
}
}
}
#[async_trait]
impl WorkflowDefinition for LeaveRequestWorkflow {
fn name(&self) -> &str {
"leave_request"
}
async fn start(&self, data: serde_json::Value) -> WorkflowResult<WorkflowState> {
let now = chrono::Utc::now();
let workflow_id = generate_id();
let locale = data.get("locale").and_then(|v| v.as_str()).unwrap_or("en");
let workflow = WorkflowState {
id: workflow_id.clone(),
name: self.name().to_string(),
status: WorkflowStatus::Running,
data: data.clone(),
created_at: now,
updated_at: now,
};
self.database.create_workflow(&workflow).await?;
let manager_task = TaskState {
id: generate_id(),
workflow_id: workflow_id.clone(),
name: Self::task_name(locale, "manager").to_string(),
status: TaskStatus::Pending,
assignee: data
.get("manager_id")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
data: data.clone(),
created_at: now,
updated_at: now,
completed_at: None,
};
self.database.create_task(&manager_task).await?;
Ok(workflow)
}
async fn execute_task(
&self,
task_id: &str,
data: serde_json::Value,
) -> WorkflowResult<TaskState> {
let mut task = self.database.get_task(task_id).await?;
let locale = data
.get("locale")
.and_then(|v| v.as_str())
.or_else(|| task.data.get("locale").and_then(|v| v.as_str()))
.unwrap_or("en");
if data
.get("approved")
.and_then(|v| v.as_bool())
.unwrap_or(false)
{
task.status = TaskStatus::Completed;
task.completed_at = Some(chrono::Utc::now());
if task.name.contains("Manager")
|| task.name.contains("经理")
|| task.name.contains("主管")
{
let hr_task = TaskState {
id: generate_id(),
workflow_id: task.workflow_id.clone(),
name: Self::task_name(locale, "hr").to_string(),
status: TaskStatus::Pending,
assignee: data
.get("hr_id")
.and_then(|v| v.as_str())
.map(|s| s.to_string())
.or_else(|| Some("hr@example.com".to_string())),
data: data.clone(),
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
completed_at: None,
};
self.database.create_task(&hr_task).await?;
} else {
let mut workflow = self.database.get_workflow(&task.workflow_id).await?;
workflow.status = WorkflowStatus::Completed;
workflow.updated_at = chrono::Utc::now();
self.database.update_workflow(&workflow).await?;
}
} else {
task.status = TaskStatus::Cancelled;
task.completed_at = Some(chrono::Utc::now());
let mut workflow = self.database.get_workflow(&task.workflow_id).await?;
workflow.status = WorkflowStatus::Cancelled;
workflow.updated_at = chrono::Utc::now();
self.database.update_workflow(&workflow).await?;
}
task.data = data;
task.updated_at = chrono::Utc::now();
self.database.update_task(&task).await?;
Ok(task)
}
async fn get_tasks(&self, workflow_id: &str) -> WorkflowResult<Vec<TaskState>> {
self.database.get_tasks_by_workflow(workflow_id).await
}
}