新增: 工程依赖图数据层 + 依赖边渲染 + 小地图

- 后端 module_dependencies 表(V35 迁移)+ ModuleDependencyRepo CRUD
- IPC:add/remove/list_module_dependencies
- 前端 API 封装 + DependencyGraph 接入真实边数据
- 依赖类型颜色区分(library/api/mq/shared/custom)
- 小地图插件(MiniMap)大图概览导航
- 点击节点跳转项目详情
- 任务列表后端真分页 count_by_query 方法
This commit is contained in:
2026-07-01 00:20:51 +08:00
parent 3c2fa91fc6
commit 3758bea0b5
10 changed files with 316 additions and 10 deletions

View File

@@ -19,7 +19,7 @@ use std::process::Command;
use serde::{Deserialize, Serialize};
use tauri::State;
use df_storage::models::ProjectModuleRecord;
use df_storage::models::{ModuleDependencyRecord, ProjectModuleRecord};
use df_types::types::new_id;
use crate::state::AppState;
@@ -1133,3 +1133,68 @@ pub async fn get_commit_detail(
"full_message": full_message,
}))
}
// ============================================================
// IPC 命令 — 工程依赖关系(module_dependencies 表)
// ============================================================
/// 新增工程依赖。返回完整记录。
#[tauri::command]
pub async fn add_module_dependency(
state: State<'_, AppState>,
project_id: String,
from_module_id: String,
to_module_id: String,
dep_type: Option<String>,
label: Option<String>,
) -> Result<ModuleDependencyRecord, String> {
let project_id = project_id.trim().to_string();
let from_module_id = from_module_id.trim().to_string();
let to_module_id = to_module_id.trim().to_string();
if project_id.is_empty() || from_module_id.is_empty() || to_module_id.is_empty() {
return Err("project_id / from_module_id / to_module_id 不能为空".to_string());
}
let dep_type = dep_type.unwrap_or_else(|| "library".to_string());
let record = ModuleDependencyRecord {
id: new_id(),
project_id,
from_module_id,
to_module_id,
dep_type,
label: trim_opt(label),
created_at: now_millis(),
};
state.module_dependencies.insert(record.clone()).await.map_err(err_str)?;
Ok(record)
}
/// 删除工程依赖。
#[tauri::command]
pub async fn remove_module_dependency(
state: State<'_, AppState>,
id: String,
) -> Result<(), String> {
let id = id.trim().to_string();
if id.is_empty() {
return Err("id 不能为空".to_string());
}
state.module_dependencies.delete(&id).await.map_err(err_str)?;
Ok(())
}
/// 列出项目的全部工程依赖。
#[tauri::command]
pub async fn list_module_dependencies(
state: State<'_, AppState>,
project_id: String,
) -> Result<Vec<ModuleDependencyRecord>, String> {
let project_id = project_id.trim().to_string();
if project_id.is_empty() {
return Err("project_id 不能为空".to_string());
}
state
.module_dependencies
.list_by_field("project_id", &project_id)
.await
.map_err(err_str)
}

View File

@@ -334,6 +334,9 @@ pub fn run() {
commands::module::get_module_file_diff,
commands::module::get_module_commits,
commands::module::get_commit_detail,
commands::module::add_module_dependency,
commands::module::remove_module_dependency,
commands::module::list_module_dependencies,
// 灵感
commands::idea::list_ideas,
commands::idea::create_idea,

View File

@@ -39,7 +39,7 @@ use df_storage::crud::{
AiConversationRepo, AiMessageRepo, AiProviderRepo, AiToolExecutionRepo, IdeaEvalRepo, IdeaRepo,
KnowledgeEventsRepo, KnowledgeRepo, NodeExecutionRepo, ProjectEventRepo, ProjectModuleRepo,
ProjectRepo, ProjectServiceRepo, ReleaseRepo, SettingsRepo, TaskLinkRepo, TaskRepo,
WorkflowRepo,
WorkflowRepo, ModuleDependencyRepo,
};
use df_storage::db::Database;
use df_workflow::eventbus::EventBus;
@@ -75,6 +75,8 @@ pub struct AppState {
/// 项目多工程(每个工程独立代码仓库):Monorepo 多仓库 / 微服务 / 前后端分离。
/// 记录工程元数据(路径/Git 地址/技术栈);Git 状态(分支/改动/提交)实时派生不存表。
pub project_modules: ProjectModuleRepo,
/// 工程依赖关系 Repo(V35 module_dependencies,依赖图边数据)。
pub module_dependencies: ModuleDependencyRepo,
/// 发布表 Repo预留:ReleaseRepo 持久化已就位·IPC/逻辑未接入·SW-260618-21 b 保留)
#[allow(dead_code)]
pub releases: ReleaseRepo,
@@ -209,6 +211,7 @@ impl AppState {
project_events: ProjectEventRepo::new(&db),
project_services: ProjectServiceRepo::new(&db),
project_modules: ProjectModuleRepo::new(&db),
module_dependencies: ModuleDependencyRepo::new(&db),
releases: ReleaseRepo::new(&db),
workflows: WorkflowRepo::new(&db),
node_executions: NodeExecutionRepo::new(&db),