新增: Phase2 阶段收尾(Sprint 1-20)
重构:删 5 零引用 crate(df-evolve/plugin/stages/task/traceability)+ 清死模块、ai.rs 拆 11 子 module、ai.ts 拆 6 composable、i18n 拆目录 功能:知识库全栈(df-project/scan + CRUD + 时间线 + 前端)、Settings 拆分、appSettings KV 迁移、模型池、LLM 并发 Semaphore 修复:审批持久化根治、ConditionEngine 默认拒绝、NodeRegistry unimplemented 清除、promote 补偿删除、工具结果截断 50KB、路径校验防 symlink 逃逸 文档:B-03 人工审批设计、决策记录三分档、规格契约自检、经验记录、todo 看板、PROGRESS 更新 详见 PROGRESS.md。src-tauri/儿童每日打卡应用/ 与本项目无关,已排除。
This commit is contained in:
123
src-tauri/src/commands/knowledge_timeline.rs
Normal file
123
src-tauri/src/commands/knowledge_timeline.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
//! 知识生命线记录器 — 统一入口,各业务流程只调一行
|
||||
//!
|
||||
//! 设计目标:
|
||||
//! - 事件记录逻辑不散落在各业务流程内部(提取/注入/状态变更),集中于此模块
|
||||
//! - fire-and-forget 友好:便捷方法内部吞掉错误(只 warn),调用方无需处理 Result
|
||||
//! - 易扩展:新事件类型只加一个便捷方法 + 一行 context 构造,不改主流程
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use df_core::types::new_id;
|
||||
use df_storage::crud::KnowledgeEventsRepo;
|
||||
use df_storage::db::Database;
|
||||
use df_storage::models::KnowledgeEventRecord;
|
||||
|
||||
use super::now_millis;
|
||||
|
||||
/// 事件类型常量(生命线节点;归档复用 status_changed 的 to=archived,不单列)
|
||||
pub const EVENT_CREATED: &str = "created";
|
||||
pub const EVENT_EXTRACTED: &str = "extracted";
|
||||
pub const EVENT_STATUS_CHANGED: &str = "status_changed";
|
||||
pub const EVENT_REFERENCED: &str = "referenced";
|
||||
|
||||
/// 知识生命线记录器 — 持有 DB 句柄,提供便捷事件写入方法
|
||||
///
|
||||
/// 用法:`KnowledgeTimeline::new(&state.db).record_xxx(...).await`
|
||||
///
|
||||
/// 异步策略(按路径热度,非 bug,刻意不一致):
|
||||
/// - 热路径(对话注入 build_knowledge_context / 提炼 extract):整块已在 spawn 任务内,
|
||||
/// 此处直接 await = 对用户 fire-and-forget,不阻塞 AI 对话
|
||||
/// - 低频命令(状态变更 / 创建):事件写入 ms 级,直接 await 比 detached spawn 更简单
|
||||
/// 便捷方法内部 `fire()` 已吞错误(warn 不阻断),调用方无需处理 Result。
|
||||
pub struct KnowledgeTimeline {
|
||||
db: Arc<Database>,
|
||||
}
|
||||
|
||||
impl KnowledgeTimeline {
|
||||
pub fn new(db: &Arc<Database>) -> Self {
|
||||
Self { db: db.clone() }
|
||||
}
|
||||
|
||||
/// 通用写入(底层入口,便捷方法均经此)
|
||||
pub async fn record(
|
||||
&self,
|
||||
knowledge_id: &str,
|
||||
event_type: &str,
|
||||
source_ref: Option<String>,
|
||||
context_json: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
let record = KnowledgeEventRecord {
|
||||
id: new_id(),
|
||||
knowledge_id: knowledge_id.to_string(),
|
||||
event_type: event_type.to_string(),
|
||||
source_ref,
|
||||
context_json,
|
||||
timestamp: now_millis(),
|
||||
};
|
||||
KnowledgeEventsRepo::new(&self.db)
|
||||
.insert(record)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 手动录入产生(context: method)
|
||||
pub async fn record_created(&self, knowledge_id: &str, method: &str) {
|
||||
let ctx = serde_json::json!({ "method": method }).to_string();
|
||||
self.fire(knowledge_id, EVENT_CREATED, Some("manual".into()), Some(ctx)).await;
|
||||
}
|
||||
|
||||
/// AI 对话提炼产生(context: conv_id / conv_title / reasoning)
|
||||
pub async fn record_extracted(
|
||||
&self,
|
||||
knowledge_id: &str,
|
||||
conv_id: &str,
|
||||
conv_title: &str,
|
||||
reasoning: &str,
|
||||
) {
|
||||
let ctx = serde_json::json!({
|
||||
"conv_id": conv_id,
|
||||
"conv_title": conv_title,
|
||||
"reasoning": reasoning,
|
||||
})
|
||||
.to_string();
|
||||
self.fire(
|
||||
knowledge_id,
|
||||
EVENT_EXTRACTED,
|
||||
Some(format!("conv:{}", conv_id)),
|
||||
Some(ctx),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// 被检索命中/注入(context: conv_id / query)
|
||||
pub async fn record_referenced(&self, knowledge_id: &str, conv_id: &str, query: &str) {
|
||||
let ctx = serde_json::json!({ "conv_id": conv_id, "query": query }).to_string();
|
||||
self.fire(
|
||||
knowledge_id,
|
||||
EVENT_REFERENCED,
|
||||
Some(format!("conv:{}", conv_id)),
|
||||
Some(ctx),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// 状态变更(context: from / to;to=published=审核通过,to=archived=归档)
|
||||
pub async fn record_status_change(&self, knowledge_id: &str, from: &str, to: &str) {
|
||||
let ctx = serde_json::json!({ "from": from, "to": to }).to_string();
|
||||
self.fire(knowledge_id, EVENT_STATUS_CHANGED, None, Some(ctx)).await;
|
||||
}
|
||||
|
||||
/// 内部:写入并吞错误(fire-and-forget,失败只 warn 不阻断主流程)
|
||||
async fn fire(
|
||||
&self,
|
||||
knowledge_id: &str,
|
||||
event_type: &str,
|
||||
source_ref: Option<String>,
|
||||
context_json: Option<String>,
|
||||
) {
|
||||
if let Err(e) = self.record(knowledge_id, event_type, source_ref, context_json).await {
|
||||
tracing::warn!("知识生命线事件写入失败(非阻断) [{}]: {}", event_type, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user