工具系统重构与定时任务管理
1. 工具抽象体系 - 新增 Tool trait + ToolRegistry 注册表 - 三种工具类型:ApiTool / StdioTool / ShellTool - 全局 REGISTRY (LazyLock) 作为主入口 - BuiltinRegistry 简化为兼容层 2. 定时任务管理工具 - 新增 manage_scheduled_tasks 工具(ApiTool) - 支持 list/add/update/delete/toggle 五种操作 - 通过 LazyLock 延迟初始化数据库连接池 3. 定时任务分流 - 新增 task_type 字段(model / system) - 模型可自由管理 model 任务 - system 任务只读,修改操作被拒绝 4. CLI 支持 - 新增 ias task 子命令(list/add/update/delete/toggle) 5. 清理 - 删除废弃的 ias-auth 独立二进制 - SkillResult 增加 Deserialize derive - 所有调用点更新为 crate::tools::xxx 新入口
This commit is contained in:
@@ -243,3 +243,163 @@ pub async fn load_summaries(pool: &PgPool, user_id: &str, limit: i64) -> Result<
|
||||
.map_err(|e| format!("查询摘要失败: {}", e))?;
|
||||
Ok(rows.into_iter().map(|(t,)| t).collect())
|
||||
}
|
||||
|
||||
// ─── 定时任务 ───
|
||||
|
||||
#[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize)]
|
||||
pub struct ScheduledTask {
|
||||
pub id: uuid::Uuid,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub user_id: Option<String>,
|
||||
pub command: String,
|
||||
pub shell: String,
|
||||
pub cwd: String,
|
||||
pub interval_seconds: i32,
|
||||
pub enabled: bool,
|
||||
pub next_run_at: DateTime<Utc>,
|
||||
pub last_run_at: Option<DateTime<Utc>>,
|
||||
pub last_status: Option<String>,
|
||||
pub task_type: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// 列出所有定时任务
|
||||
pub async fn list_scheduled_tasks(pool: &PgPool) -> Result<Vec<ScheduledTask>, String> {
|
||||
let tasks = sqlx::query_as::<_, ScheduledTask>(
|
||||
r#"
|
||||
SELECT id, name, description, user_id, command, shell, cwd,
|
||||
interval_seconds, enabled, next_run_at, last_run_at,
|
||||
last_status, task_type, created_at, updated_at
|
||||
FROM scheduled_tasks
|
||||
ORDER BY next_run_at ASC
|
||||
"#,
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.map_err(|e| format!("查询定时任务失败: {}", e))?;
|
||||
Ok(tasks)
|
||||
}
|
||||
|
||||
/// 添加定时任务
|
||||
pub async fn add_scheduled_task(
|
||||
pool: &PgPool,
|
||||
name: &str,
|
||||
user_id: &str,
|
||||
command: &str,
|
||||
interval_seconds: i32,
|
||||
description: &str,
|
||||
shell: &str,
|
||||
cwd: &str,
|
||||
task_type: &str,
|
||||
) -> Result<uuid::Uuid, String> {
|
||||
let id = uuid::Uuid::new_v4();
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO scheduled_tasks (id, name, description, user_id, command, shell, cwd, interval_seconds, task_type)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
)
|
||||
.bind(id)
|
||||
.bind(name)
|
||||
.bind(description)
|
||||
.bind(user_id)
|
||||
.bind(command)
|
||||
.bind(shell)
|
||||
.bind(cwd)
|
||||
.bind(interval_seconds)
|
||||
.bind(task_type)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| format!("添加定时任务失败: {}", e))?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// 更新定时任务(只更新非 None 的字段)
|
||||
pub async fn update_scheduled_task(
|
||||
pool: &PgPool,
|
||||
id: &uuid::Uuid,
|
||||
name: Option<&str>,
|
||||
command: Option<&str>,
|
||||
interval_seconds: Option<i32>,
|
||||
description: Option<&str>,
|
||||
shell: Option<&str>,
|
||||
cwd: Option<&str>,
|
||||
) -> Result<bool, String> {
|
||||
let mut sets = Vec::new();
|
||||
let mut idx = 1u32;
|
||||
|
||||
if name.is_some() {
|
||||
sets.push(format!("name = ${}", idx)); idx += 1;
|
||||
}
|
||||
if command.is_some() {
|
||||
sets.push(format!("command = ${}", idx)); idx += 1;
|
||||
}
|
||||
if interval_seconds.is_some() {
|
||||
sets.push(format!("interval_seconds = ${}", idx)); idx += 1;
|
||||
}
|
||||
if description.is_some() {
|
||||
sets.push(format!("description = ${}", idx)); idx += 1;
|
||||
}
|
||||
if shell.is_some() {
|
||||
sets.push(format!("shell = ${}", idx)); idx += 1;
|
||||
}
|
||||
if cwd.is_some() {
|
||||
sets.push(format!("cwd = ${}", idx)); idx += 1;
|
||||
}
|
||||
|
||||
if sets.is_empty() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
sets.push("updated_at = NOW()".to_string());
|
||||
|
||||
let sql = format!(
|
||||
"UPDATE scheduled_tasks SET {} WHERE id = ${}",
|
||||
sets.join(", "),
|
||||
idx
|
||||
);
|
||||
|
||||
let mut query = sqlx::query(&sql).bind(id);
|
||||
|
||||
if let Some(v) = name { query = query.bind(v); }
|
||||
if let Some(v) = command { query = query.bind(v); }
|
||||
if let Some(v) = interval_seconds { query = query.bind(v); }
|
||||
if let Some(v) = description { query = query.bind(v); }
|
||||
if let Some(v) = shell { query = query.bind(v); }
|
||||
if let Some(v) = cwd { query = query.bind(v); }
|
||||
|
||||
let rows = query
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| format!("更新定时任务失败: {}", e))?;
|
||||
|
||||
Ok(rows.rows_affected() > 0)
|
||||
}
|
||||
|
||||
/// 删除定时任务
|
||||
pub async fn delete_scheduled_task(pool: &PgPool, id: &uuid::Uuid) -> Result<bool, String> {
|
||||
let rows = sqlx::query("DELETE FROM scheduled_tasks WHERE id = $1")
|
||||
.bind(id)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| format!("删除定时任务失败: {}", e))?;
|
||||
Ok(rows.rows_affected() > 0)
|
||||
}
|
||||
|
||||
/// 切换定时任务启用状态
|
||||
pub async fn toggle_scheduled_task(pool: &PgPool, id: &uuid::Uuid) -> Result<(bool, bool), String> {
|
||||
let row = sqlx::query_as::<_, (bool,)>(
|
||||
"UPDATE scheduled_tasks SET enabled = NOT enabled, updated_at = NOW() WHERE id = $1 RETURNING enabled",
|
||||
)
|
||||
.bind(id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
.map_err(|e| format!("切换定时任务状态失败: {}", e))?;
|
||||
|
||||
match row {
|
||||
Some((enabled,)) => Ok((true, enabled)),
|
||||
None => Ok((false, false)),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user