feat(调度任务): 实现调度任务管理功能
新增调度任务模块,支持任务的增删改查、启停及手动执行 - 后端添加 schedule_job 模型、服务、路由及调度器工具类 - 前端新增调度任务管理页面 - 修改 flow 相关接口将 id 类型从 String 改为 i64 - 添加 tokio-cron-scheduler 依赖实现定时任务调度 - 初始化时加载已启用任务并注册到调度器
This commit is contained in:
@ -11,19 +11,19 @@ use crate::db::Db;
|
||||
#[async_trait]
|
||||
pub trait FlowLogHandler: Send + Sync {
|
||||
/// 记录流程开始执行
|
||||
async fn log_start(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, operator: Option<(i64, String)>) -> anyhow::Result<()>;
|
||||
async fn log_start(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, operator: Option<(i64, String)>) -> anyhow::Result<()>;
|
||||
|
||||
/// 记录流程执行失败(仅包含错误信息)
|
||||
async fn log_error(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()>;
|
||||
async fn log_error(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()>;
|
||||
|
||||
/// 记录流程执行失败(包含部分输出与累计日志)
|
||||
async fn log_error_detail(&self, _flow_id: &str, _flow_code: Option<&str>, _input: &Value, _output: &Value, _logs: &[String], error_msg: &str, _operator: Option<(i64, String)>, _started_at: DateTime<FixedOffset>, _duration_ms: i64) -> anyhow::Result<()> {
|
||||
async fn log_error_detail(&self, _flow_id: i64, _flow_code: Option<&str>, _input: &Value, _output: &Value, _logs: &[String], error_msg: &str, _operator: Option<(i64, String)>, _started_at: DateTime<FixedOffset>, _duration_ms: i64) -> anyhow::Result<()> {
|
||||
// 默认实现:退化为仅错误信息
|
||||
self.log_error(_flow_id, _flow_code, _input, error_msg, _operator, _started_at, _duration_ms).await
|
||||
}
|
||||
|
||||
/// 记录流程执行成功
|
||||
async fn log_success(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()>;
|
||||
async fn log_success(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()>;
|
||||
|
||||
/// 推送节点执行事件(仅SSE实现需要)
|
||||
async fn emit_node_event(&self, _node_id: &str, _event_type: &str, _data: &Value) -> anyhow::Result<()> {
|
||||
@ -51,15 +51,15 @@ impl DatabaseLogHandler {
|
||||
|
||||
#[async_trait]
|
||||
impl FlowLogHandler for DatabaseLogHandler {
|
||||
async fn log_start(&self, _flow_id: &str, _flow_code: Option<&str>, _input: &Value, _operator: Option<(i64, String)>) -> anyhow::Result<()> {
|
||||
async fn log_start(&self, _flow_id: i64, _flow_code: Option<&str>, _input: &Value, _operator: Option<(i64, String)>) -> anyhow::Result<()> {
|
||||
// 数据库日志处理器不需要记录开始事件,只在结束时记录
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_error(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
async fn log_error(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
let (user_id, username) = operator.map(|(u, n)| (Some(u), Some(n))).unwrap_or((None, None));
|
||||
flow_run_log_service::create(&self.db, CreateRunLogInput {
|
||||
flow_id: flow_id.to_string(),
|
||||
flow_id,
|
||||
flow_code: flow_code.map(|s| s.to_string()),
|
||||
input: Some(serde_json::to_string(input).unwrap_or_default()),
|
||||
output: None,
|
||||
@ -73,7 +73,7 @@ impl FlowLogHandler for DatabaseLogHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_error_detail(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
async fn log_error_detail(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
let (user_id, username) = operator.map(|(u, n)| (Some(u), Some(n))).unwrap_or((None, None));
|
||||
// 将 error_msg 附加到日志尾部(若最后一条不同),确保日志中有清晰的错误描述且不重复
|
||||
let mut all_logs = logs.to_vec();
|
||||
@ -81,7 +81,7 @@ impl FlowLogHandler for DatabaseLogHandler {
|
||||
all_logs.push(error_msg.to_string());
|
||||
}
|
||||
flow_run_log_service::create(&self.db, CreateRunLogInput {
|
||||
flow_id: flow_id.to_string(),
|
||||
flow_id,
|
||||
flow_code: flow_code.map(|s| s.to_string()),
|
||||
input: Some(serde_json::to_string(input).unwrap_or_default()),
|
||||
output: Some(serde_json::to_string(output).unwrap_or_default()),
|
||||
@ -95,10 +95,10 @@ impl FlowLogHandler for DatabaseLogHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_success(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
async fn log_success(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
let (user_id, username) = operator.map(|(u, n)| (Some(u), Some(n))).unwrap_or((None, None));
|
||||
flow_run_log_service::create(&self.db, CreateRunLogInput {
|
||||
flow_id: flow_id.to_string(),
|
||||
flow_id,
|
||||
flow_code: flow_code.map(|s| s.to_string()),
|
||||
input: Some(serde_json::to_string(input).unwrap_or_default()),
|
||||
output: Some(serde_json::to_string(output).unwrap_or_default()),
|
||||
@ -127,19 +127,19 @@ impl SseLogHandler {
|
||||
|
||||
#[async_trait]
|
||||
impl FlowLogHandler for SseLogHandler {
|
||||
async fn log_start(&self, _flow_id: &str, _flow_code: Option<&str>, _input: &Value, _operator: Option<(i64, String)>) -> anyhow::Result<()> {
|
||||
async fn log_start(&self, _flow_id: i64, _flow_code: Option<&str>, _input: &Value, _operator: Option<(i64, String)>) -> anyhow::Result<()> {
|
||||
// SSE处理器也不需要记录开始事件
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_error(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
async fn log_error(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
// 先推送SSE错误事件(不在此处发送 done,交由调用方统一携带 ctx/logs 发送)
|
||||
crate::middlewares::sse::emit_error(&self.event_tx, error_msg.to_string()).await;
|
||||
|
||||
// 然后记录到数据库(仅错误信息)
|
||||
let (user_id, username) = operator.map(|(u, n)| (Some(u), Some(n))).unwrap_or((None, None));
|
||||
flow_run_log_service::create(&self.db, CreateRunLogInput {
|
||||
flow_id: flow_id.to_string(),
|
||||
flow_id,
|
||||
flow_code: flow_code.map(|s| s.to_string()),
|
||||
input: Some(serde_json::to_string(input).unwrap_or_default()),
|
||||
output: None,
|
||||
@ -153,7 +153,7 @@ impl FlowLogHandler for SseLogHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_error_detail(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
async fn log_error_detail(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], error_msg: &str, operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
// 先推送SSE错误事件(不在此处发送 done,交由调用方统一携带 ctx/logs 发送)
|
||||
crate::middlewares::sse::emit_error(&self.event_tx, error_msg.to_string()).await;
|
||||
|
||||
@ -164,7 +164,7 @@ impl FlowLogHandler for SseLogHandler {
|
||||
all_logs.push(error_msg.to_string());
|
||||
}
|
||||
flow_run_log_service::create(&self.db, CreateRunLogInput {
|
||||
flow_id: flow_id.to_string(),
|
||||
flow_id,
|
||||
flow_code: flow_code.map(|s| s.to_string()),
|
||||
input: Some(serde_json::to_string(input).unwrap_or_default()),
|
||||
output: Some(serde_json::to_string(output).unwrap_or_default()),
|
||||
@ -178,14 +178,14 @@ impl FlowLogHandler for SseLogHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_success(&self, flow_id: &str, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
async fn log_success(&self, flow_id: i64, flow_code: Option<&str>, input: &Value, output: &Value, logs: &[String], operator: Option<(i64, String)>, started_at: DateTime<FixedOffset>, duration_ms: i64) -> anyhow::Result<()> {
|
||||
// 先推送SSE完成事件
|
||||
crate::middlewares::sse::emit_done(&self.event_tx, true, output.clone(), logs.to_vec()).await;
|
||||
|
||||
// 然后记录到数据库
|
||||
let (user_id, username) = operator.map(|(u, n)| (Some(u), Some(n))).unwrap_or((None, None));
|
||||
flow_run_log_service::create(&self.db, CreateRunLogInput {
|
||||
flow_id: flow_id.to_string(),
|
||||
flow_id,
|
||||
flow_code: flow_code.map(|s| s.to_string()),
|
||||
input: Some(serde_json::to_string(input).unwrap_or_default()),
|
||||
output: Some(serde_json::to_string(output).unwrap_or_default()),
|
||||
|
||||
Reference in New Issue
Block a user