修复: 审批不超时+重启恢复 + 其他小改

- APPROVAL_TIMEOUT_MS=Infinity(已决策:审批不做超时)
- V33 迁移: ai_conversations 加 pending_approvals 列
- save_conversation 持久化 pending_approvals
- ai_conversation_switch 从 DB 恢复 pending_approvals
- switchConversation 加 try/catch(对话不存在→建新)
- @项目 关联添加取消按钮(×)
- TopBar 底部无标题时图标右对齐
- TaskDetail.vue 删除重复 case
This commit is contained in:
2026-06-28 13:09:55 +08:00
parent c4b02b5370
commit ad1821bc14
11 changed files with 140 additions and 19 deletions

View File

@@ -85,6 +85,7 @@ fn ai_conversation_from_row(row: &Row<'_>) -> std::result::Result<AiConversation
prompt_tokens: row.get("prompt_tokens")?,
completion_tokens: row.get("completion_tokens")?,
pinned_goals: row.get("pinned_goals")?,
pending_approvals: row.get("pending_approvals")?,
created_at: row.get("created_at")?,
updated_at: row.get("updated_at")?,
})
@@ -160,24 +161,24 @@ impl_repo!(
from_row => |row| ai_conversation_from_row(row),
insert => |conn, rec| {
conn.execute(
"INSERT INTO ai_conversations (id, title, messages, provider_id, model, models, archived, pinned, prompt_tokens, completion_tokens, pinned_goals, created_at, updated_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)",
"INSERT INTO ai_conversations (id, title, messages, provider_id, model, models, archived, pinned, prompt_tokens, completion_tokens, pinned_goals, pending_approvals, created_at, updated_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)",
params![
rec.id, rec.title, rec.messages, rec.provider_id, rec.model, rec.models, rec.archived,
if rec.pinned { 1i32 } else { 0i32 },
rec.prompt_tokens, rec.completion_tokens,
rec.pinned_goals, rec.created_at, rec.updated_at
rec.pinned_goals, rec.pending_approvals, rec.created_at, rec.updated_at
],
)
},
update => |conn, rec| {
conn.execute(
"UPDATE ai_conversations SET title = ?1, messages = ?2, provider_id = ?3, model = ?4, models = ?5, archived = ?6, pinned = ?7, prompt_tokens = ?8, completion_tokens = ?9, pinned_goals = ?10, updated_at = ?11 WHERE id = ?12",
"UPDATE ai_conversations SET title = ?1, messages = ?2, provider_id = ?3, model = ?4, models = ?5, archived = ?6, pinned = ?7, prompt_tokens = ?8, completion_tokens = ?9, pinned_goals = ?10, pending_approvals = ?11, updated_at = ?12 WHERE id = ?13",
params![
rec.title, rec.messages, rec.provider_id, rec.model, rec.models, rec.archived,
if rec.pinned { 1i32 } else { 0i32 },
rec.prompt_tokens, rec.completion_tokens,
rec.pinned_goals, rec.updated_at, rec.id
rec.pinned_goals, rec.pending_approvals, rec.updated_at, rec.id
],
)
}

View File

@@ -43,7 +43,9 @@ pub fn run(conn: &Connection) -> Result<()> {
// V31 = 知识图谱 Phase 3 基础设施数据层(对标设计 §2.3):project_services 表,
// 项目基础设施配置(数据库/缓存/MQ/API 等),为 AI 执行任务时提供"这项目用了
// 什么数据库、Redis 在哪、有没有 MQ"的基础设施上下文。
let steps: [(i32, fn(&Connection) -> Result<()>); 32] = [
// V33 = 审批重启恢复:ai_conversations 加 pending_approvals TEXT 列,持久化挂起审批快照,
// 重启后从 DB 恢复 pending_approvals 内存态,使待审批不丢。
let steps: [(i32, fn(&Connection) -> Result<()>); 33] = [
(1, migrate_v1),
(2, migrate_v2),
(3, migrate_v3),
@@ -76,6 +78,7 @@ pub fn run(conn: &Connection) -> Result<()> {
(30, migrate_v30),
(31, migrate_v31),
(32, migrate_v32),
(33, migrate_v33),
];
for (version, migrate_fn) in steps {
@@ -917,6 +920,28 @@ fn migrate_v32(conn: &Connection) -> Result<()> {
Ok(())
}
fn migrate_v33(conn: &Connection) -> Result<()> {
// 用 PRAGMA 探测列存在性,缺失才 ALTER,对新库/老库/坏库均安全(同 v4 模式)
let has_col: bool = conn
.query_row(
"SELECT COUNT(*) > 0 FROM pragma_table_info('ai_conversations') WHERE name = 'pending_approvals'",
[],
|row| row.get(0),
)
.unwrap_or(false);
if !has_col {
conn.execute_batch(
"ALTER TABLE ai_conversations ADD COLUMN pending_approvals TEXT DEFAULT '{}';"
)?;
tracing::info!("v33: ai_conversations 加 pending_approvals 列(审批重启恢复)");
} else {
tracing::info!("v33: pending_approvals 列已存在,跳过");
}
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [33])?;
tracing::info!("迁移 v33 完成");
Ok(())
}
/// V21 建表 SQL — 消息拆分存储 ai_messages 表
///
/// 与 V9_SQL 中的 ai_messages 镜像(V9 给新库,此 const 给老库 V21 迁移用 IF NOT EXISTS)。

View File

@@ -338,6 +338,7 @@ pub struct AiConversationRecord {
pub prompt_tokens: Option<i64>, // 输入 token 累计(流式 usage 落库)
pub completion_tokens: Option<i64>, // 输出 token 累计(流式 usage 落库)
pub pinned_goals: Option<String>, // 对话目标钉扎持久化(JSON 字符串数组,默认'[]')
pub pending_approvals: Option<String>, // 挂起审批快照(JSON 对象,以 tool_call_id 为键,默认'{}')
pub created_at: String,
pub updated_at: String,
}