重构: 后端编排拆解与DRY债收敛及命名纠偏

阶段1拆解: stream 系外移 stream_relay.rs,mod.rs 3048→2607,loop_body 502→83
技能收尾: regenerate/edit 回溯补技能,新增 read_skill 工具,项目级技能扫描
DRY: emit 双写 46 处收敛 event_bus 正典,KV 引号归一收口,LOCK-SLOW 33 处单行化
警示骨架 4→1 与压缩兜底 2→1,噪音目录档位收敛,truncate 同名陷阱消除
工具元数据落 ToolMeta 八维,7 张 match 表改查 registry,parity 测试锁等价
命名: is_noise_entry/teardown_round/probe_missed_terminal_event 等 6 项纠偏
This commit is contained in:
lxy
2026-10-01 19:35:19 +08:00
parent 3861e13b32
commit 75c5e23523
40 changed files with 2988 additions and 1964 deletions
+77 -1
View File
@@ -31,7 +31,7 @@ use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::Duration;
use std::time::{Duration, Instant};
use anyhow::Result;
use tokio::sync::{Mutex, RwLock};
@@ -52,6 +52,82 @@ use crate::commands::ai::augmentation::ResolverRegistry;
use crate::commands::ai::AiSession;
use df_types::types::ExecutionId;
// ============================================================
// Settings KV 引号归一读取(DRY #2 收敛,2026-10-01)
//
// 背景:前端 appSettings.set 以 JSON.stringify 落库(字符串值带引号),但 KV 里也并存
// 历史裸值,读侧此前散布三种归一策略(trim_matches / serde_json::from_str / bool 比对),
// 已漂移出行为差异(from_str 版能正确处理转义,trim 版不能)。
// 正典 = lib.rs 原 relay_url 的 from_str 版,收敛为本 trait 两个方法,新代码禁止手抄归一。
// ============================================================
/// SettingsRepo 的引号归一读扩展(KV 值契约模糊:JSON 序列化带引号或裸值并存)。
///
/// 只加只读归一方法,不碰 SettingsRepo 本身的 get/set 语义。
pub trait SettingsNorm {
/// 读字符串值并归一去引号。
///
/// 策略(正典,原 lib.rs relay_url 写法):先按 JSON 字符串解析(正确处理 `\` 转义),
/// 失败(裸值)再 trim 首尾 `"` 兜底。key 缺失或读库失败返 None(调用方决定默认值)。
async fn get_string_norm(&self, key: &str) -> Option<String>;
/// 读 bool 值并归一比对。
///
/// 归一后 == "true" 或 == "1" 为 true,其余(含 key 缺失/读库失败/空值)取 `default`。
/// 默认值由调用方显式传参,禁止在方法内隐藏默认(各开关默认不同:bypass=false / trace=true)。
async fn get_bool_norm(&self, key: &str, default: bool) -> bool;
}
impl SettingsNorm for SettingsRepo {
async fn get_string_norm(&self, key: &str) -> Option<String> {
let raw = self.get(key).await.ok().flatten()?;
Some(
serde_json::from_str::<String>(&raw)
.unwrap_or_else(|_| raw.trim_matches('"').to_string()),
)
}
async fn get_bool_norm(&self, key: &str, default: bool) -> bool {
match self.get(key).await {
Ok(Some(raw)) => {
let v = serde_json::from_str::<String>(&raw)
.unwrap_or_else(|_| raw.trim_matches('"').to_string());
v == "true" || v == "1"
}
_ => default,
}
}
}
// ============================================================
// 慢锁诊断打点(DRY #12 收敛,2026-10-01)
//
// 背景:排查 AiCompleted 延迟/卡死连环期间,各持锁段手抄了
// `let t = Instant::now(); ... let held = t.elapsed(); if held > 30ms { eprintln!(...) }`
// 5 行样板(29 处,变量名编码行号如 __hold610,阈值 30ms 逐处复制),收敛为本函数。
// ============================================================
/// 慢锁打点阈值:持锁段(含 lock 等待)超过 30ms 打 [LOCK-SLOW] 日志。
pub(crate) const LOCK_SLOW_THRESHOLD: Duration = Duration::from_millis(30);
/// 慢锁单行打点:持锁段耗时(自 `t` 起,含 lock 等待)超 [`LOCK_SLOW_THRESHOLD`] 时打 stderr 日志。
///
/// 用法(替代手抄 5 行样板):
/// ```ignore
/// let t = std::time::Instant::now();
/// let mut session = state.ai_session.lock().await;
/// ... // 持锁段
/// drop(session);
/// log_slow_lock(t, "ai_approve:610");
/// ```
/// `site` 传「函数名:行号」字符串,保留原变量名编码行号的定位信息。
pub(crate) fn log_slow_lock(t: Instant, site: &str) {
let held = t.elapsed();
if held > LOCK_SLOW_THRESHOLD {
eprintln!("[LOCK-SLOW] {} 持锁 {:?} (含 lock 等待)", site, held);
}
}
/// 应用全局状态 — 通过 `app.manage()` 注入,command 中以 `State<'_, AppState>` 取用
pub struct AppState {
/// 数据库句柄(Arc 包装,便于在异步任务中重建 Repo)