新增: AI Chat多项增强(审批去重/编辑重发/导出/实体引用/会话置顶搜索)+任务推进链df-nodes落地

This commit is contained in:
lxy
2026-06-16 12:41:13 +08:00
parent 212a927eee
commit 7d5cd4c89a
62 changed files with 4576 additions and 248 deletions
+63 -11
View File
@@ -68,18 +68,22 @@ fn validate_transition(from: &str, to: &str) -> Result<(), String> {
// CRUD
// ============================================================
/// 列出知识 — 可按 status 筛选,status=None 时默认排除 archived
/// 列出知识 — 可按 status 筛选,status=None 时默认仅返回 published
///
/// 默认范围说明(F-260616-02 决策 a):
/// - status=None → library tab 的数据源,语义为「已发布知识库」,仅 published。
/// 不再混杂 pending_review(pending_review 归 inbox 收件箱,见 knowledge_list_candidates)。
/// - 显式传 status 时按该 status 过滤(含 archived)。
#[tauri::command]
pub async fn knowledge_list(
state: State<'_, AppState>,
status: Option<String>,
) -> Result<Vec<KnowledgeRecord>, String> {
match status {
// 显式查 archived 时原样返回(含归档项)
Some(s) if s == "archived" => state.knowledge.list_by_status("archived").await.map_err(err_str),
// 显式传 status 时按该 status 过滤(含 archived)
Some(s) => state.knowledge.list_by_status(&s).await.map_err(err_str),
// 默认:列出非 archived 的全部(单查询 status != 'archived')
None => state.knowledge.list_non_archived().await.map_err(err_str),
// 默认:library 仅 published(F-260616-02 决策 a,职责清晰:library=published,inbox=待处理)
None => state.knowledge.list_by_status("published").await.map_err(err_str),
}
}
@@ -208,16 +212,64 @@ pub async fn knowledge_record_reuse(
.map_err(err_str)
}
/// 审核收件箱 — 列出 candidate(按 confidence 语义排序)
/// 收件箱 — 列出待处理条目(candidate + pending_review),按 confidence 语义排序
///
/// 语义(F-260616-02 决策 a):inbox = 「待处理」收件箱,聚合 candidate(待评估)
/// 与 pending_review(待发布审核)两种待处理状态。library 仅 published。
///
/// 实现:list_by_status 单状态查询,这里合并 candidate 与 pending_review 两路结果。
/// 两路各自已按 `confidence DESC, created_at DESC` 排序,有序合并保持同一规则。
#[tauri::command]
pub async fn knowledge_list_candidates(
state: State<'_, AppState>,
) -> Result<Vec<KnowledgeRecord>, String> {
state
.knowledge
.list_by_status("candidate")
.await
.map_err(err_str)
let (candidates, pending) = tokio::try_join!(
state.knowledge.list_by_status("candidate"),
state.knowledge.list_by_status("pending_review"),
)
.map_err(err_str)?;
Ok(merge_by_confidence(candidates, pending))
}
/// 有序合并两列(各自已按 confidence DESC, created_at DESC 排序),结果保持同序。
///
/// confidence 排序权重:high=3, medium=2, low=1, 其他=0;同权重按 created_at DESC
/// (字符串毫秒时间戳字典序 = 时间序)。等价 SQL `ORDER BY CASE confidence ... DESC, created_at DESC`。
fn merge_by_confidence(
mut a: Vec<KnowledgeRecord>,
mut b: Vec<KnowledgeRecord>,
) -> Vec<KnowledgeRecord> {
use std::cmp::Ordering;
fn rank(c: &str) -> i8 {
match c {
"high" => 3,
"medium" => 2,
"low" => 1,
_ => 0,
}
}
let cmp = |x: &KnowledgeRecord, y: &KnowledgeRecord| -> Ordering {
let rx = rank(x.confidence.as_deref().unwrap_or(""));
let ry = rank(y.confidence.as_deref().unwrap_or(""));
ry.cmp(&rx) // confidence DESC
.then_with(|| y.created_at.cmp(&x.created_at)) // created_at DESC
};
a.sort_by(cmp);
b.sort_by(cmp);
let mut out = Vec::with_capacity(a.len() + b.len());
let (mut i, mut j) = (0, 0);
while i < a.len() && j < b.len() {
if cmp(&a[i], &b[j]) != Ordering::Greater {
out.push(a[i].clone());
i += 1;
} else {
out.push(b[j].clone());
j += 1;
}
}
out.extend_from_slice(&a[i..]);
out.extend_from_slice(&b[j..]);
out
}
/// 归档(软删除) — UPDATE status='archived'