新增: 知识图谱Phase2事件流(父⑥) — project_events统一事件流+埋点+timeline
父⑥ Phase 2(对标设计 §2.4): - V30 迁移:project_events 追加型审计表(id/project_id/event_type/entity_type/entity_id/from_state/to_state/context_json/source/conversation_id/created_at)+ 双索引(project,time / entity) - ProjectEventRecord + ProjectEventRepo(insert 追加型/get_by_project/get_by_entity/list_recent) - 事件埋点(best-effort,hook/after,失败 warn 不阻断主操作): - task.rs emit_event 辅助 + create_task/advance_task/move_task_queue 埋点 - idea.rs promote_idea 埋点(idea_promoted,project_id=新项目) - project.rs create/delete 埋点 - 注:idea_created 暂不埋点(idea 无 project_id,待 Inbox 项目落地,NOT NULL+FK 约束) - get_project_timeline IPC(events 模块)+ lib.rs 注册 + AI 工具(基线 +N) - source 语义:IPC 标 human,AI 工具路径标 ai 注:workflow 脚本中文变量名 const 致验证 agent 未跑,主控独立 cargo check EXIT 0 + grep 落地齐全核验。
This commit is contained in:
@@ -18,12 +18,49 @@ use df_project::scan::{
|
||||
normalize_path, DiscoveredProject,
|
||||
};
|
||||
use df_storage::crud::ProjectQuery;
|
||||
use df_storage::models::ProjectRecord;
|
||||
use df_storage::models::{ProjectEventRecord, ProjectRecord};
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
use super::{err_str, now_millis};
|
||||
|
||||
// ============================================================
|
||||
// 知识图谱 Phase 2:事件流埋点辅助(best-effort,对标设计 §2.4 hook/after + §10.1)
|
||||
// ============================================================
|
||||
|
||||
/// 追加一条项目事件到 project_events(best-effort)。失败仅 tracing::warn 不阻断主 IPC。
|
||||
///
|
||||
/// `source` 标 `"human"`(IPC 层)。AI 工具路径在 tool_registry 内自行标 `"ai"`。
|
||||
async fn emit_project_event(
|
||||
state: &AppState,
|
||||
project_id: &str,
|
||||
event_type: &str,
|
||||
entity_type: Option<&str>,
|
||||
entity_id: Option<&str>,
|
||||
) {
|
||||
let record = ProjectEventRecord {
|
||||
id: new_id(),
|
||||
project_id: project_id.to_string(),
|
||||
event_type: event_type.to_string(),
|
||||
entity_type: entity_type.map(|s| s.to_string()),
|
||||
entity_id: entity_id.map(|s| s.to_string()),
|
||||
from_state: None,
|
||||
to_state: Some("planning".to_string()),
|
||||
context_json: None,
|
||||
source: Some("human".to_string()),
|
||||
conversation_id: None,
|
||||
created_at: now_millis(),
|
||||
};
|
||||
if let Err(e) = state.project_events.insert(record).await {
|
||||
tracing::warn!(
|
||||
event_type = event_type,
|
||||
project_id = project_id,
|
||||
error = %e,
|
||||
"[事件流] 埋点写入失败(不阻断主操作)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建项目入参
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateProjectInput {
|
||||
@@ -132,6 +169,22 @@ async fn create_with_binding(
|
||||
if record.path.is_some() {
|
||||
state.reload_allowed_dirs().await;
|
||||
}
|
||||
// 知识图谱 Phase 2(对标设计 §2.4 hook/after):项目创建事件。best-effort 不阻断。
|
||||
//
|
||||
// 设计 §2.4 event_type 枚举未列 project_created(枚举非穷尽,Repo 不做白名单校验——
|
||||
// 见 project_event_repo.rs 注释「埋点层是唯一生产者,字符串字面量自带约束」)。此处沿用
|
||||
// task_created/idea_created 命名约定新增 project_created,语义自洽。entity_type=project。
|
||||
//
|
||||
// 注:灵感晋升(create_from_idea)在 promote_idea 单独记 idea_promoted 事件(更精确),
|
||||
// 此处一律记 project_created(项目实体自身视角),两事件互补不冲突。
|
||||
emit_project_event(
|
||||
state,
|
||||
&record.id,
|
||||
"project_created",
|
||||
Some("project"),
|
||||
Some(&record.id),
|
||||
)
|
||||
.await;
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user