新增: 工程依赖图数据层 + 依赖边渲染 + 小地图
- 后端 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:
@@ -28,6 +28,7 @@ mod project_service_repo;
|
||||
mod settings;
|
||||
mod task_link_repo;
|
||||
mod task_repo;
|
||||
mod module_dependency_repo;
|
||||
|
||||
pub use conversation_repo::*;
|
||||
pub use idea_eval_repo::*;
|
||||
@@ -40,6 +41,7 @@ pub use project_service_repo::*;
|
||||
pub use settings::*;
|
||||
pub use task_link_repo::*;
|
||||
pub use task_repo::*;
|
||||
pub use module_dependency_repo::*;
|
||||
|
||||
// ============================================================
|
||||
// 辅助宏 — 消除多个 Repo 的重复样板
|
||||
|
||||
95
crates/df-storage/src/crud/module_dependency_repo.rs
Normal file
95
crates/df-storage/src/crud/module_dependency_repo.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
//! 工程依赖关系 — module_dependencies 表 CRUD(V35,工程间依赖边)
|
||||
//!
|
||||
//! 简化版:insert / delete / list_by_field(查 project_id)。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use rusqlite::{params, OptionalExtension, Row};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::db::Database;
|
||||
use crate::models::ModuleDependencyRecord;
|
||||
|
||||
use super::{storage_err};
|
||||
|
||||
fn dep_from_row(row: &Row<'_>) -> std::result::Result<ModuleDependencyRecord, rusqlite::Error> {
|
||||
Ok(ModuleDependencyRecord {
|
||||
id: row.get("id")?,
|
||||
project_id: row.get("project_id")?,
|
||||
from_module_id: row.get("from_module_id")?,
|
||||
to_module_id: row.get("to_module_id")?,
|
||||
dep_type: row.get("dep_type")?,
|
||||
label: row.get("label")?,
|
||||
created_at: row.get("created_at")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub struct ModuleDependencyRepo {
|
||||
conn: Arc<Mutex<rusqlite::Connection>>,
|
||||
}
|
||||
|
||||
impl ModuleDependencyRepo {
|
||||
pub fn new(db: &Database) -> Self {
|
||||
Self { conn: db.conn() }
|
||||
}
|
||||
|
||||
pub async fn insert(&self, record: ModuleDependencyRecord) -> Result<bool, df_types::error::Error> {
|
||||
let conn = self.conn.clone();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let guard = conn.blocking_lock();
|
||||
let r = &record;
|
||||
let affected = guard
|
||||
.execute(
|
||||
"INSERT INTO module_dependencies \
|
||||
(id, project_id, from_module_id, to_module_id, dep_type, label, created_at) \
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
||||
params![
|
||||
r.id, r.project_id, r.from_module_id, r.to_module_id,
|
||||
r.dep_type, r.label, r.created_at,
|
||||
],
|
||||
)
|
||||
.map_err(storage_err)?;
|
||||
Ok(affected > 0)
|
||||
})
|
||||
.await
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: &str) -> Result<bool, df_types::error::Error> {
|
||||
let conn = self.conn.clone();
|
||||
let id = id.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let guard = conn.blocking_lock();
|
||||
let affected = guard
|
||||
.execute("DELETE FROM module_dependencies WHERE id = ?1", params![id])
|
||||
.map_err(storage_err)?;
|
||||
Ok(affected > 0)
|
||||
})
|
||||
.await
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
|
||||
pub async fn list_by_field(&self, field: &str, value: &str) -> Result<Vec<ModuleDependencyRecord>, df_types::error::Error> {
|
||||
let conn = self.conn.clone();
|
||||
let field = field.to_owned();
|
||||
let value = value.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let guard = conn.blocking_lock();
|
||||
let sql = format!(
|
||||
"SELECT id, project_id, from_module_id, to_module_id, dep_type, label, created_at \
|
||||
FROM module_dependencies WHERE {field} = ?1 ORDER BY created_at ASC"
|
||||
);
|
||||
let mut stmt = guard.prepare(&sql).map_err(storage_err)?;
|
||||
let rows = stmt
|
||||
.query_map(params![value], dep_from_row)
|
||||
.map_err(storage_err)?;
|
||||
let mut results = Vec::new();
|
||||
for r in rows {
|
||||
results.push(r.map_err(storage_err)?);
|
||||
}
|
||||
Ok(results)
|
||||
})
|
||||
.await
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ pub fn run(conn: &Connection) -> Result<()> {
|
||||
// 什么数据库、Redis 在哪、有没有 MQ"的基础设施上下文。
|
||||
// V33 = 审批重启恢复:ai_conversations 加 pending_approvals TEXT 列,持久化挂起审批快照,
|
||||
// 重启后从 DB 恢复 pending_approvals 内存态,使待审批不丢。
|
||||
let steps: [(i32, fn(&Connection) -> Result<()>); 34] = [
|
||||
let steps: [(i32, fn(&Connection) -> Result<()>); 35] = [
|
||||
(1, migrate_v1),
|
||||
(2, migrate_v2),
|
||||
(3, migrate_v3),
|
||||
@@ -80,6 +80,7 @@ pub fn run(conn: &Connection) -> Result<()> {
|
||||
(32, migrate_v32),
|
||||
(33, migrate_v33),
|
||||
(34, migrate_v34),
|
||||
(35, migrate_v35),
|
||||
];
|
||||
|
||||
for (version, migrate_fn) in steps {
|
||||
@@ -995,6 +996,40 @@ fn migrate_v34(conn: &Connection) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V35:工程依赖关系—module_dependencies 表(工程间依赖边,用于依赖图)
|
||||
///
|
||||
/// dep_type 枚举值:library(类库) / api(API调用) / mq(消息队列) / shared(共享资源) / custom(自定义)
|
||||
fn migrate_v35(conn: &Connection) -> Result<()> {
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS module_dependencies (
|
||||
id TEXT PRIMARY KEY,
|
||||
project_id TEXT NOT NULL REFERENCES projects(id),
|
||||
from_module_id TEXT NOT NULL REFERENCES project_modules(id),
|
||||
to_module_id TEXT NOT NULL REFERENCES project_modules(id),
|
||||
dep_type TEXT NOT NULL DEFAULT 'library',
|
||||
label TEXT,
|
||||
created_at TEXT NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_module_deps_project ON module_dependencies(project_id)",
|
||||
[],
|
||||
)?;
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_module_deps_from ON module_dependencies(from_module_id)",
|
||||
[],
|
||||
)?;
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_module_deps_to ON module_dependencies(to_module_id)",
|
||||
[],
|
||||
)?;
|
||||
tracing::info!("v35: 建 module_dependencies 表 + 索引(工程依赖图)");
|
||||
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [35])?;
|
||||
tracing::info!("迁移 v35 完成");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V21 建表 SQL — 消息拆分存储 ai_messages 表
|
||||
///
|
||||
/// 与 V9_SQL 中的 ai_messages 镜像(V9 给新库,此 const 给老库 V21 迁移用 IF NOT EXISTS)。
|
||||
|
||||
@@ -483,3 +483,17 @@ pub struct ProjectModuleRecord {
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
/// 工程依赖关系记录(V35 module_dependencies 表)。
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ModuleDependencyRecord {
|
||||
pub id: String,
|
||||
pub project_id: String,
|
||||
pub from_module_id: String,
|
||||
pub to_module_id: String,
|
||||
/// 依赖类型:library / api / mq / shared / custom
|
||||
pub dep_type: String,
|
||||
/// 可选标签(自定义描述)
|
||||
pub label: Option<String>,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user