Skip to content

Commit ef0a1ec

Browse files
committed
fix(review): 校对失败路径任务终态记账(不残留 running 行)
1 parent 086ccbf commit ef0a1ec

1 file changed

Lines changed: 73 additions & 18 deletions

File tree

‎app/src-tauri/src/commands_proofread.rs‎

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -164,20 +164,33 @@ pub async fn proofread_run(
164164
});
165165

166166
let started = std::time::Instant::now();
167+
// 预估成本(成功/失败记账共用——内联不再重复估算)
168+
let est_tokens = crate::ai_cost::estimate_tokens(chars);
169+
let (est_price, _) = crate::ai_cost::price_for_model(&model);
170+
let est_cost = crate::ai_cost::estimate_cost(est_tokens, est_price);
167171
let client = if mock {
168172
None
169173
} else {
170-
let key = crate::commands_ai_providers::resolve_default_provider_key(&st)?;
171-
let providers = st
172-
.ai_providers
173-
.lock()
174-
.map_err(|e| format!("Provider 存储锁中毒: {}", e))?
175-
.clone();
176-
Some(crate::ai_client::AiClient::from_settings_with_store(
177-
&settings,
178-
key,
179-
&providers,
180-
))
174+
match crate::commands_ai_providers::resolve_default_provider_key(&st) {
175+
Ok(key) => {
176+
let providers = match st.ai_providers.lock() {
177+
Ok(g) => g.clone(),
178+
Err(e) => {
179+
record_proofread_failure(&db, task_id, session_id, &model, est_cost, created_at, 0, &format!("Provider 存储锁中毒: {}", e));
180+
return Err(format!("Provider 存储锁中毒: {}", e));
181+
}
182+
};
183+
Some(crate::ai_client::AiClient::from_settings_with_store(
184+
&settings,
185+
key,
186+
&providers,
187+
))
188+
}
189+
Err(e) => {
190+
record_proofread_failure(&db, task_id, session_id, &model, est_cost, created_at, 0, &e);
191+
return Err(e);
192+
}
193+
}
181194
};
182195

183196
let chunks = ai_proofread::chunk_sentences(&expected.iter().map(|s| s.to_string()).collect::<Vec<_>>());
@@ -187,9 +200,14 @@ pub async fn proofread_run(
187200
let user = build_user_prompt(
188201
&chunk.iter().map(|&i| expected[i].to_string()).collect::<Vec<_>>(),
189202
);
190-
let raw = client
191-
.chat_text(&build_system_prompt(), &user)
192-
.map_err(|e| format!("校对请求 {}/{} 失败(原文未改动): {}", ci + 1, chunks.len(), e))?;
203+
let raw = match client.chat_text(&build_system_prompt(), &user) {
204+
Ok(raw) => raw,
205+
Err(e) => {
206+
let msg = format!("校对请求 {}/{} 失败(原文未改动): {}", ci + 1, chunks.len(), e);
207+
record_proofread_failure(&db, task_id, session_id, &model, est_cost, created_at, started.elapsed().as_millis() as i64, &msg);
208+
return Err(msg);
209+
}
210+
};
193211
let batch_expected: Vec<String> =
194212
chunk.iter().map(|&i| expected[i].to_string()).collect();
195213
suggestions.extend(parse_suggestions(&raw, &batch_expected));
@@ -242,12 +260,17 @@ pub async fn proofread_run(
242260
similarity: Some(similarity),
243261
});
244262
}
245-
let draft_count = db.add_refine_drafts(&drafts).map_err(|e| format!("落校对草稿失败: {e}"))?;
263+
let draft_count = match db.add_refine_drafts(&drafts) {
264+
Ok(n) => n,
265+
Err(e) => {
266+
let msg = format!("落校对草稿失败: {}", e);
267+
record_proofread_failure(&db, task_id, session_id, &model, est_cost, created_at, started.elapsed().as_millis() as i64, &msg);
268+
return Err(msg);
269+
}
270+
};
246271

247272
let elapsed = started.elapsed().as_millis() as i64;
248-
let tokens = crate::ai_cost::estimate_tokens(chars);
249-
let (price, _) = crate::ai_cost::price_for_model(&model);
250-
let cost_yuan = crate::ai_cost::estimate_cost(tokens, price);
273+
let cost_yuan = est_cost;
251274
let result_json = serde_json::json!({
252275
"draftCount": draft_count,
253276
"suggestionsReceived": suggestions_received,
@@ -277,6 +300,38 @@ pub async fn proofread_run(
277300
Ok(ProofreadRunView { draft_count, suggestions_received, chars, cost_yuan, model, capped })
278301
}
279302

303+
/// 失败终态记账(网络/解析/落库错误路径不残留 running 行——任务中心/审计可查)。
304+
fn record_proofread_failure(
305+
db: &crate::db::Db,
306+
task_id: u64,
307+
session_id: i64,
308+
model: &str,
309+
cost_yuan: f64,
310+
created_at: i64,
311+
elapsed_ms: i64,
312+
error: &str,
313+
) {
314+
let rec = crate::db_ai_tasks::AiTaskRecord {
315+
task_id,
316+
op_type: "proofread".to_string(),
317+
ref_id: session_id,
318+
state: "failed".to_string(),
319+
target_kind: Some("session".to_string()),
320+
result_json: None,
321+
cost_yuan: Some(cost_yuan),
322+
elapsed_ms: Some(elapsed_ms),
323+
model: Some(model.to_string()),
324+
error: Some(error.to_string()),
325+
slices: None,
326+
created_at,
327+
finished_at: Some(crate::db::unix_seconds()),
328+
adopted: false,
329+
};
330+
if let Err(e) = db.insert_ai_task(&rec) {
331+
eprintln!("[Proofread] 失败任务记账失败: {e}");
332+
}
333+
}
334+
280335
/// 校对草稿列表(origin=proofread;裁决走 second_pass_decide——裁决与来源解耦)。
281336
#[tauri::command]
282337
pub fn proofread_list(

0 commit comments

Comments
 (0)