fix: 3 项问题

1. 审批DB更新精确匹配 — code_hash 从 removed entry 提取
2. delete memo 提取 id — prompt 中数字 → id 参数
3. 清理未使用的 current_user_id 全局变量
This commit is contained in:
2026-06-02 15:44:28 +08:00
parent fe83687495
commit 6708fe33a0
3 changed files with 12 additions and 16 deletions
+1 -9
View File
@@ -208,9 +208,6 @@ async fn cmd_listen(
database.as_ref().map(|db| Arc::new(db.pool().clone())) database.as_ref().map(|db| Arc::new(db.pool().clone()))
)); ));
// 共享的当前用户 ID(用于审批消息)
let current_user_id = Arc::new(tokio::sync::Mutex::new(String::new()));
let conversation = if enable_llm { let conversation = if enable_llm {
let sys_prompt = std::env::var("WEIXIN_LLM_SYSTEM_PROMPT") let sys_prompt = std::env::var("WEIXIN_LLM_SYSTEM_PROMPT")
.unwrap_or_else(|_| DEFAULT_SYSTEM_PROMPT.to_string()); .unwrap_or_else(|_| DEFAULT_SYSTEM_PROMPT.to_string());
@@ -326,14 +323,12 @@ async fn cmd_listen(
}) })
}; };
let executor_user_id = current_user_id.clone();
let executor: ToolExecutor = Arc::new(move |name: &str, args_json: &str| { let executor: ToolExecutor = Arc::new(move |name: &str, args_json: &str| {
let reg = registry_opt.clone(); let reg = registry_opt.clone();
let approval = approval.clone(); let approval = approval.clone();
let send = send_wechat.clone(); let send = send_wechat.clone();
let session = session.clone(); let session = session.clone();
let memory_store = memory_store.clone(); let memory_store = memory_store.clone();
let current_user = executor_user_id.clone();
let n = name.to_string(); let n = name.to_string();
let args = args_json.to_string(); let args = args_json.to_string();
@@ -447,8 +442,6 @@ async fn cmd_listen(
let shutdown = async { tokio::signal::ctrl_c().await.expect("Ctrl+C 失败") }; let shutdown = async { tokio::signal::ctrl_c().await.expect("Ctrl+C 失败") };
let listen_user_id = current_user_id.clone();
tokio::select! { tokio::select! {
_ = shutdown => { _ = shutdown => {
info!("收到退出信号,注销监听器..."); info!("收到退出信号,注销监听器...");
@@ -456,7 +449,7 @@ async fn cmd_listen(
file_state.save_runtime(&client.get_updates_buf().await); file_state.save_runtime(&client.get_updates_buf().await);
info!("已退出"); info!("已退出");
} }
_ = listen_loop(&client, enable_llm, echo, &account_id, database, file_state, conversation, &approval_manager, listen_user_id, memory_store) => {} _ = listen_loop(&client, enable_llm, echo, &account_id, database, file_state, conversation, &approval_manager, memory_store) => {}
} }
} }
@@ -469,7 +462,6 @@ async fn listen_loop(
file_state: &state::StateManager, file_state: &state::StateManager,
conversation: Option<Arc<Conversation>>, conversation: Option<Arc<Conversation>>,
approval_manager: &ApprovalManager, approval_manager: &ApprovalManager,
current_user_id: Arc<tokio::sync::Mutex<String>>,
memory_store: &Arc<MemoryStore>, memory_store: &Arc<MemoryStore>,
) { ) {
let last_user_id = Arc::new(tokio::sync::Mutex::new(String::new())); let last_user_id = Arc::new(tokio::sync::Mutex::new(String::new()));
+9 -6
View File
@@ -89,7 +89,7 @@ impl ApprovalManager {
/// 处理用户回复(由消息循环调用) /// 处理用户回复(由消息循环调用)
/// 匹配成功时返回 Some(技能名),匹配失败返回 None /// 匹配成功时返回 Some(技能名),匹配失败返回 None
pub async fn handle_reply(&self, user_id: &str, reply: &str) -> Option<String> { pub async fn handle_reply(&self, user_id: &str, reply: &str) -> Option<String> {
let (result, name) = { let (result, name, code_hash) = {
let mut map = self.pending.lock().await; let mut map = self.pending.lock().await;
let entries = map.get_mut(user_id)?; let entries = map.get_mut(user_id)?;
if entries.is_empty() { return None; } if entries.is_empty() { return None; }
@@ -99,19 +99,22 @@ impl ApprovalManager {
if reply_trimmed == "0" || reply_trimmed == "取消" { if reply_trimmed == "0" || reply_trimmed == "取消" {
let entry = entries.remove(0); let entry = entries.remove(0);
let hash = entry.code_hash.clone();
let _ = entry.sender.send(ApprovalDecision::Rejected); let _ = entry.sender.send(ApprovalDecision::Rejected);
(ApprovalDecision::Rejected, entry.skill_name.clone()) (ApprovalDecision::Rejected, entry.skill_name.clone(), hash)
} else if let Some(idx) = entries.iter().position(|e| e.code_hash == reply_hash) { } else if let Some(idx) = entries.iter().position(|e| e.code_hash == reply_hash) {
let entry = entries.remove(idx); let entry = entries.remove(idx);
let hash = entry.code_hash.clone();
let _ = entry.sender.send(ApprovalDecision::Approved); let _ = entry.sender.send(ApprovalDecision::Approved);
(ApprovalDecision::Approved, entry.skill_name.clone()) (ApprovalDecision::Approved, entry.skill_name.clone(), hash)
} else if let Some(entry) = entries.first_mut() { } else if let Some(entry) = entries.first_mut() {
entry.attempts_left -= 1; entry.attempts_left -= 1;
if entry.attempts_left <= 0 { if entry.attempts_left <= 0 {
let entry = entries.remove(0); let entry = entries.remove(0);
let n = entry.skill_name.clone(); let n = entry.skill_name.clone();
let hash = entry.code_hash.clone();
let _ = entry.sender.send(ApprovalDecision::Rejected); let _ = entry.sender.send(ApprovalDecision::Rejected);
(ApprovalDecision::Rejected, n) (ApprovalDecision::Rejected, n, hash)
} else { } else {
return None; return None;
} }
@@ -128,10 +131,10 @@ impl ApprovalManager {
}; };
// 按 user_id + 最近 pending 记录更新 // 按 user_id + 最近 pending 记录更新
let _ = sqlx::query( let _ = sqlx::query(
"UPDATE pending_approvals SET status = $1, consumed_at = NOW() WHERE id = (SELECT id FROM pending_approvals WHERE user_id = $2 AND status = 'pending' ORDER BY created_at DESC LIMIT 1)", "UPDATE pending_approvals SET status = $1, consumed_at = NOW() WHERE code_hash = $2 AND status = 'pending'",
) )
.bind(status) .bind(status)
.bind(user_id) .bind(&code_hash)
.execute(pool.as_ref()) .execute(pool.as_ref())
.await; .await;
} }
+2 -1
View File
@@ -61,7 +61,8 @@ async fn handle_memos(args_json: &str) -> SkillResult {
"list" "list"
}; };
let content = params.get("prompt").and_then(|v| v.as_str()).unwrap_or(""); let content = params.get("prompt").and_then(|v| v.as_str()).unwrap_or("");
params = serde_json::json!({"action": action, "content": content}); let memo_id: i32 = content.chars().filter(|c| c.is_ascii_digit()).collect::<String>().parse().unwrap_or(0);
params = serde_json::json!({"action": action, "content": content, "id": memo_id});
} }
let action = params.get("action").and_then(|v| v.as_str()).unwrap_or("list"); let action = params.get("action").and_then(|v| v.as_str()).unwrap_or("list");
let path = ".data/memos.json"; let path = ".data/memos.json";