新增: Reviewer仲裁+命令互斥锁+冲突解决IPC(批次C散项)

- coordinator: arbitrate_conflicts 方法(规则仲裁:成功方优先/双方成功推荐合并/都失败手动)

- coordinator: ConflictItem 加 subtask_a/subtask_b/recommendation 字段 + ConflictResolution 枚举

- command_lock.rs: CommandLockRegistry (dir+cmd首词 Semaphore 串行化,5个测试全绿)

- resolve_conflict IPC: 解决冲突→更新DB→emit AiConflictResolved 事件闭环

- 3个仲裁测试 + 5个互斥锁测试全绿

- 零编译警告
This commit is contained in:
lxy
2026-07-02 00:17:00 +08:00
parent 840949ad05
commit 61d3e9c148
5 changed files with 302 additions and 0 deletions
+42
View File
@@ -140,3 +140,45 @@ pub async fn set_plan_execution(enabled: bool) -> Result<(), String> {
pub async fn get_plan_execution() -> Result<bool, String> {
Ok(df_ai::plan_executor::plan_execution_enabled())
}
/// 解决合并冲突(用户/reviewer 选择解决方案后调用)。
///
/// 更新 ai_conflicts 表的 resolution + resolved_by + resolved_at,
/// 并 emit AiConflictResolved 事件供前端更新徽章。
#[tauri::command]
pub async fn resolve_conflict(
state: State<'_, AppState>,
app_handle: tauri::AppHandle,
conflict_id: String,
plan_id: String,
resolution: String,
resolved_by: String,
conversation_id: Option<String>,
) -> Result<(), String> {
use tauri::Emitter;
let repo = df_storage::crud::ConflictRepo::new(&state.db);
let now = df_types::now_millis().to_string();
let ok = repo.resolve(&conflict_id, &resolution, &resolved_by, &now)
.await
.map_err(err_str)?;
if !ok {
return Err(format!("冲突 {} 不存在或已解决", conflict_id));
}
tracing::info!(
conflict_id = %conflict_id,
resolution = %resolution,
resolved_by = %resolved_by,
"[CONFLICT] 冲突已解决"
);
// emit AiConflictResolved 事件
let ev = crate::commands::ai::AiChatEvent::AiConflictResolved {
conflict_id,
plan_id,
resolution,
resolved_by,
conversation_id,
};
let _ = app_handle.emit("ai-chat-event", ev.clone());
let _ = state.ai_event_bus.publish_event(ev);
Ok(())
}