重构: 拆tool_registry register_data_tools(strategy·降单函数复杂度)

- register_data_tools 381→10调度 + 5子函数(register_project_tools 8/register_task_tools 6/register_workflow_tools 1/register_idea_tools 2/register_trash_tools 1)
- 基线测试守护(28工具集合稳定, 漏移/rename立即红)
- build_ai_tool_registry调用零变更, HashMap注册顺序无关
strategy: 单函数复杂度降(register_data 381→10, 子函数<136); 总行+36(拆分签名+doc非压缩目标); register_file_tools 713留后续
This commit is contained in:
2026-06-19 12:03:26 +08:00
parent 03f0effcf2
commit 64ece1d3a0

View File

@@ -379,8 +379,27 @@ pub fn build_ai_tool_registry(db: &Arc<Database>) -> AiToolRegistry {
/// ///
/// SMELL-P0-2:抽自原 build_ai_tool_registry 1091 行单函数(数据+文件混合)。 /// SMELL-P0-2:抽自原 build_ai_tool_registry 1091 行单函数(数据+文件混合)。
/// 18 个 register 调用【原样移入】,零行为变更,仅机械搬运。 /// 18 个 register 调用【原样移入】,零行为变更,仅机械搬运。
///
/// SMELL-P0-2 续拆(2026-06-19):18 工具按业务表细分为 5 子函数:
/// - register_project_tools(8):list/update/create/bind/delete/restore/purge/get_count
/// - register_task_tools(6):list/create/update/advance/delete/get_count
/// - register_workflow_tools(1):run_workflow(High,单独,handler 防御兜底)
/// - register_idea_tools(2):list/create
/// - register_trash_tools(1):list_trash
/// AiToolRegistry 底层 HashMap(注册顺序无关),拆分后工具集合与原一致,
/// 行为零变更(基线测试 test_build_ai_tool_registry_baseline_tool_count 守护)。
fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) { fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
// ── 只读 (Low) ── register_project_tools(registry, db);
register_task_tools(registry, db);
register_workflow_tools(registry);
register_idea_tools(registry, db);
register_trash_tools(registry, db);
}
/// 项目类 AI 工具注册(8 个:CRUD + 目录绑定 + 探总量)——持 db:Arc<Database>。
/// 抽自 register_data_tools(SMELL-P0-2 续拆),【原样移入】,零行为变更。
/// 组内顺序保留原相对顺序(list/update/create/bind/delete/restore/purge/get_count)。
fn register_project_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
registry.register( registry.register(
"list_projects", "列出所有项目,支持 offset/limit 分页。返回 items项目列表、total总量、has_more是否有更多页。默认 limit=50", "list_projects", "列出所有项目,支持 offset/limit 分页。返回 items项目列表、total总量、has_more是否有更多页。默认 limit=50",
df_ai::ai_tools::object_schema(vec![("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low, df_ai::ai_tools::object_schema(vec![("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low,
@@ -398,46 +417,6 @@ fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
}) })
})}, })},
); );
registry.register(
"list_tasks", "列出任务,可按 project_id 筛选,支持 offset/limit 分页。返回 items、total、has_more。默认 limit=50",
df_ai::ai_tools::object_schema(vec![("project_id", "string", false), ("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let repo = df_storage::crud::TaskRepo::new(&db);
let tasks = if let Some(pid) = args.get("project_id").and_then(|v| v.as_str()) {
repo.query("project_id", pid).await?
} else {
repo.list_all().await?
};
let total = tasks.len();
let offset = args["offset"].as_u64().unwrap_or(0) as usize;
let limit = args["limit"].as_u64().unwrap_or(MAX_LIST_RESULTS as u64).min(MAX_LIST_RESULTS as u64) as usize;
let page_items: Vec<_> = tasks.into_iter().skip(offset).take(limit).collect();
let has_more = (offset + page_items.len()) < total;
Ok(serde_json::json!({ "items": page_items, "total": total, "has_more": has_more }))
})
})},
);
registry.register(
"list_ideas", "列出所有灵感,支持 offset/limit 分页。返回 items、total、has_more。默认 limit=50",
df_ai::ai_tools::object_schema(vec![("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let repo = df_storage::crud::IdeaRepo::new(&db);
let items = repo.list_all().await?;
let total = items.len();
let offset = args["offset"].as_u64().unwrap_or(0) as usize;
let limit = args["limit"].as_u64().unwrap_or(MAX_LIST_RESULTS as u64).min(MAX_LIST_RESULTS as u64) as usize;
let page_items: Vec<_> = items.into_iter().skip(offset).take(limit).collect();
let has_more = (offset + page_items.len()) < total;
Ok(serde_json::json!({ "items": page_items, "total": total, "has_more": has_more }))
})
})},
);
// ── 创建 (Medium) ──
registry.register( registry.register(
"update_project", "更新项目的指定字段name/status/description/path/stack需要提供项目 ID、字段名和新值。绑定代码目录推荐改用 bind_directory", "update_project", "更新项目的指定字段name/status/description/path/stack需要提供项目 ID、字段名和新值。绑定代码目录推荐改用 bind_directory",
df_ai::ai_tools::object_schema(vec![("id", "string", true), ("field", "string", true), ("value", "string", true)]), df_ai::ai_tools::object_schema(vec![("id", "string", true), ("field", "string", true), ("value", "string", true)]),
@@ -507,6 +486,85 @@ fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
}) })
})}, })},
); );
registry.register(
"delete_project", "删除项目(移入回收站,可恢复。永久删除用 purge_project",
df_ai::ai_tools::object_schema(vec![("id", "string", true)]), RiskLevel::High,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
let repo = df_storage::crud::ProjectRepo::new(&db);
let deleted = repo.soft_delete(id).await?;
Ok(serde_json::json!({ "deleted": deleted, "id": id }))
})
})},
);
registry.register(
"restore_project", "从回收站恢复已删除项目",
df_ai::ai_tools::object_schema(vec![("id", "string", true)]), RiskLevel::High,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
let repo = df_storage::crud::ProjectRepo::new(&db);
let restored = repo.restore(id).await?;
Ok(serde_json::json!({ "restored": restored, "id": id }))
})
})},
);
registry.register(
"purge_project", "永久删除项目及关联数据(不可恢复)",
df_ai::ai_tools::object_schema(vec![("id", "string", true)]), RiskLevel::High,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
let repo = df_storage::crud::ProjectRepo::new(&db);
let purged = repo.purge_with_descendants(id).await?;
Ok(serde_json::json!({ "purged": purged, "id": id }))
})
})},
);
// ── 探总量工具 (Low risk, F-260616-08) ──
registry.register(
"get_project_count", "获取项目总数(未删除项目),用于分页策略判断。返回 { total: usize }",
df_ai::ai_tools::object_schema(vec![]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |_args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let repo = df_storage::crud::ProjectRepo::new(&db);
let items = repo.list_active().await?;
Ok(serde_json::json!({ "total": items.len() }))
})
})},
);
}
/// 任务类 AI 工具注册(6 个:CRUD + 状态机推进 + 探总量)——持 db:Arc<Database>。
/// 抽自 register_data_tools(SMELL-P0-2 续拆),【原样移入】,零行为变更。
/// 组内顺序保留原相对顺序(list/create/update/advance/delete/get_count)。
fn register_task_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
registry.register(
"list_tasks", "列出任务,可按 project_id 筛选,支持 offset/limit 分页。返回 items、total、has_more。默认 limit=50",
df_ai::ai_tools::object_schema(vec![("project_id", "string", false), ("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let repo = df_storage::crud::TaskRepo::new(&db);
let tasks = if let Some(pid) = args.get("project_id").and_then(|v| v.as_str()) {
repo.query("project_id", pid).await?
} else {
repo.list_all().await?
};
let total = tasks.len();
let offset = args["offset"].as_u64().unwrap_or(0) as usize;
let limit = args["limit"].as_u64().unwrap_or(MAX_LIST_RESULTS as u64).min(MAX_LIST_RESULTS as u64) as usize;
let page_items: Vec<_> = tasks.into_iter().skip(offset).take(limit).collect();
let has_more = (offset + page_items.len()) < total;
Ok(serde_json::json!({ "items": page_items, "total": total, "has_more": has_more }))
})
})},
);
registry.register( registry.register(
"create_task", "在指定项目下创建新任务", "create_task", "在指定项目下创建新任务",
df_ai::ai_tools::object_schema(vec![("project_id", "string", true), ("title", "string", true), ("description", "string", false), ("priority", "integer", false)]), df_ai::ai_tools::object_schema(vec![("project_id", "string", true), ("title", "string", true), ("description", "string", false), ("priority", "integer", false)]),
@@ -587,6 +645,43 @@ fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
}) })
})}, })},
); );
// FR-D6:delete_task 对齐 commands::task::delete_task(D-260616-02 软删除)
// tasks 表 V14 加 deleted_at,delete_task 改调 soft_delete 移入回收站(对标 delete_project)
registry.register(
"delete_task", "删除任务(移入回收站,可恢复)",
df_ai::ai_tools::object_schema(vec![("id", "string", true)]), RiskLevel::High,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
let repo = df_storage::crud::TaskRepo::new(&db);
let deleted = repo.soft_delete(id).await?;
Ok(serde_json::json!({ "deleted": deleted, "id": id }))
})
})},
);
// ── 探总量工具 (Low risk, F-260616-08) ──
registry.register(
"get_task_count", "获取任务总数(未删除任务),用于分页策略判断。返回 { total: usize }",
df_ai::ai_tools::object_schema(vec![("project_id", "string", false)]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let repo = df_storage::crud::TaskRepo::new(&db);
let tasks = if let Some(pid) = args.get("project_id").and_then(|v| v.as_str()) {
repo.query("project_id", pid).await?
} else {
repo.list_all().await?
};
Ok(serde_json::json!({ "total": tasks.len() }))
})
})},
);
}
/// 工作流类 AI 工具注册(1 个:run_workflow)——不持 db(handler 防御兜底,详见注释)。
/// 抽自 register_data_tools(SMELL-P0-2 续拆),【原样移入】,零行为变更。
fn register_workflow_tools(registry: &mut AiToolRegistry) {
// F-260616-07 阶段3(子项2):run_workflow AI 工具注册实装。 // F-260616-07 阶段3(子项2):run_workflow AI 工具注册实装。
// 实施路径文档 §三 列为阶段3 必做项(tool_registry.rs 此前无此工具连空壳都没有)。 // 实施路径文档 §三 列为阶段3 必做项(tool_registry.rs 此前无此工具连空壳都没有)。
// 描述明确按任务 target_status 推进对应工作流(含 AiNode 自审 / HumanNode 核对闸门)。 // 描述明确按任务 target_status 推进对应工作流(含 AiNode 自审 / HumanNode 核对闸门)。
@@ -627,18 +722,25 @@ fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
}) })
})}, })},
); );
// FR-D6:delete_task 对齐 commands::task::delete_task(D-260616-02 软删除) }
// tasks 表 V14 加 deleted_at,delete_task 改调 soft_delete 移入回收站(对标 delete_project)
/// 灵感类 AI 工具注册(2 个:list + create)——持 db:Arc<Database>。
/// 抽自 register_data_tools(SMELL-P0-2 续拆),【原样移入】,零行为变更。
fn register_idea_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
registry.register( registry.register(
"delete_task", "删除任务(移入回收站,可恢复)", "list_ideas", "列出所有灵感,支持 offset/limit 分页。返回 items、total、has_more。默认 limit=50",
df_ai::ai_tools::object_schema(vec![("id", "string", true)]), RiskLevel::High, df_ai::ai_tools::object_schema(vec![("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| { { let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone(); let db = db.clone();
Box::pin(async move { Box::pin(async move {
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?; let repo = df_storage::crud::IdeaRepo::new(&db);
let repo = df_storage::crud::TaskRepo::new(&db); let items = repo.list_all().await?;
let deleted = repo.soft_delete(id).await?; let total = items.len();
Ok(serde_json::json!({ "deleted": deleted, "id": id })) let offset = args["offset"].as_u64().unwrap_or(0) as usize;
let limit = args["limit"].as_u64().unwrap_or(MAX_LIST_RESULTS as u64).min(MAX_LIST_RESULTS as u64) as usize;
let page_items: Vec<_> = items.into_iter().skip(offset).take(limit).collect();
let has_more = (offset + page_items.len()) < total;
Ok(serde_json::json!({ "items": page_items, "total": total, "has_more": has_more }))
}) })
})}, })},
); );
@@ -672,47 +774,11 @@ fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
}) })
})}, })},
); );
}
// ── 高风险 (High) ── /// 回收站类 AI 工具注册(1 个:list_trash)——持 db:Arc<Database>。
registry.register( /// 抽自 register_data_tools(SMELL-P0-2 续拆),【原样移入】,零行为变更。
"delete_project", "删除项目(移入回收站,可恢复。永久删除用 purge_project", fn register_trash_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
df_ai::ai_tools::object_schema(vec![("id", "string", true)]), RiskLevel::High,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
let repo = df_storage::crud::ProjectRepo::new(&db);
let deleted = repo.soft_delete(id).await?;
Ok(serde_json::json!({ "deleted": deleted, "id": id }))
})
})},
);
registry.register(
"restore_project", "从回收站恢复已删除项目",
df_ai::ai_tools::object_schema(vec![("id", "string", true)]), RiskLevel::High,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
let repo = df_storage::crud::ProjectRepo::new(&db);
let restored = repo.restore(id).await?;
Ok(serde_json::json!({ "restored": restored, "id": id }))
})
})},
);
registry.register(
"purge_project", "永久删除项目及关联数据(不可恢复)",
df_ai::ai_tools::object_schema(vec![("id", "string", true)]), RiskLevel::High,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let id = args["id"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 id"))?;
let repo = df_storage::crud::ProjectRepo::new(&db);
let purged = repo.purge_with_descendants(id).await?;
Ok(serde_json::json!({ "purged": purged, "id": id }))
})
})},
);
registry.register( registry.register(
"list_trash", "列出回收站已删除项目,支持 offset/limit 分页。返回 items、total、has_more。默认 limit=50", "list_trash", "列出回收站已删除项目,支持 offset/limit 分页。返回 items、total、has_more。默认 limit=50",
df_ai::ai_tools::object_schema(vec![("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low, df_ai::ai_tools::object_schema(vec![("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low,
@@ -730,36 +796,6 @@ fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
}) })
})}, })},
); );
// ── 探总量工具 (Low risk, F-260616-08) ──
registry.register(
"get_project_count", "获取项目总数(未删除项目),用于分页策略判断。返回 { total: usize }",
df_ai::ai_tools::object_schema(vec![]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |_args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let repo = df_storage::crud::ProjectRepo::new(&db);
let items = repo.list_active().await?;
Ok(serde_json::json!({ "total": items.len() }))
})
})},
);
registry.register(
"get_task_count", "获取任务总数(未删除任务),用于分页策略判断。返回 { total: usize }",
df_ai::ai_tools::object_schema(vec![("project_id", "string", false)]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let repo = df_storage::crud::TaskRepo::new(&db);
let tasks = if let Some(pid) = args.get("project_id").and_then(|v| v.as_str()) {
repo.query("project_id", pid).await?
} else {
repo.list_all().await?
};
Ok(serde_json::json!({ "total": tasks.len() }))
})
})},
);
} }
/// 文件系统类 AI 工具注册(命令执行/读/列/写/改/元/追加/删/移/搜)——不持 db,纯文件系统操作。 /// 文件系统类 AI 工具注册(命令执行/读/列/写/改/元/追加/删/移/搜)——不持 db,纯文件系统操作。