use axum::Router; use axum::routing::{get, post}; use std::time::Duration; use uuid::Uuid; use erp_core::error::AppResult; use erp_core::events::EventBus; use erp_core::module::ErpModule; use crate::handler::{definition_handler, instance_handler, task_handler}; /// Workflow module implementing the `ErpModule` trait. /// /// Manages workflow definitions, process instances, tasks, /// and the token-driven execution engine. pub struct WorkflowModule; impl WorkflowModule { pub fn new() -> Self { Self } /// Build protected (authenticated) routes for the workflow module. pub fn protected_routes() -> Router where crate::workflow_state::WorkflowState: axum::extract::FromRef, S: Clone + Send + Sync + 'static, { Router::new() // Definition routes .route( "/workflow/definitions", get(definition_handler::list_definitions) .post(definition_handler::create_definition), ) .route( "/workflow/definitions/{id}", get(definition_handler::get_definition).put(definition_handler::update_definition), ) .route( "/workflow/definitions/{id}/publish", post(definition_handler::publish_definition), ) // Instance routes .route( "/workflow/instances", post(instance_handler::start_instance).get(instance_handler::list_instances), ) .route( "/workflow/instances/{id}", get(instance_handler::get_instance), ) .route( "/workflow/instances/{id}/suspend", post(instance_handler::suspend_instance), ) .route( "/workflow/instances/{id}/resume", post(instance_handler::resume_instance), ) .route( "/workflow/instances/{id}/terminate", post(instance_handler::terminate_instance), ) // Task routes .route( "/workflow/tasks/pending", get(task_handler::list_pending_tasks), ) .route( "/workflow/tasks/completed", get(task_handler::list_completed_tasks), ) .route( "/workflow/tasks/{id}/complete", post(task_handler::complete_task), ) .route( "/workflow/tasks/{id}/delegate", post(task_handler::delegate_task), ) } /// 启动超时检查后台任务。 /// /// 每 60 秒扫描一次 tasks 表,查找 due_date 已过期但仍处于 pending 状态的任务。 /// 发现超时任务时记录 warning 日志,后续迭代将实现自动完成/升级逻辑。 pub fn start_timeout_checker(db: sea_orm::DatabaseConnection) { tokio::spawn(async move { let mut interval = tokio::time::interval(Duration::from_secs(60)); // 首次跳过,等一个完整间隔再执行 interval.tick().await; loop { interval.tick().await; match crate::engine::timeout::TimeoutChecker::find_all_overdue_tasks(&db).await { Ok(overdue) => { if !overdue.is_empty() { tracing::warn!( count = overdue.len(), task_ids = ?overdue, "发现超时未完成的任务 — TODO: 实现自动完成/升级逻辑" ); } } Err(e) => { tracing::warn!(error = %e, "超时检查任务执行失败"); } } } }); } } impl Default for WorkflowModule { fn default() -> Self { Self::new() } } #[async_trait::async_trait] impl ErpModule for WorkflowModule { fn name(&self) -> &str { "workflow" } fn version(&self) -> &str { env!("CARGO_PKG_VERSION") } fn dependencies(&self) -> Vec<&str> { vec!["auth"] } fn register_event_handlers(&self, _bus: &EventBus) {} async fn on_tenant_created( &self, _tenant_id: Uuid, _db: &sea_orm::DatabaseConnection, _event_bus: &EventBus, ) -> AppResult<()> { Ok(()) } async fn on_tenant_deleted( &self, _tenant_id: Uuid, _db: &sea_orm::DatabaseConnection, ) -> AppResult<()> { Ok(()) } fn as_any(&self) -> &dyn std::any::Any { self } }