修复: TD-260621-05审批状态双轨统一(executed→completed+V27迁移+测试)

This commit is contained in:
2026-06-24 17:48:42 +08:00
parent ebba9735ed
commit ba811ead96
2 changed files with 114 additions and 11 deletions

View File

@@ -29,7 +29,10 @@ pub fn run(conn: &Connection) -> Result<()> {
// V24 = ideas.related_ids 列(灵感间关联关系持久化打底);
// V25 = idea_evaluations (idea_id, version) 唯一约束(评估版本并发重复兜底);
// V26 = F-260621-02 任务索引缺口补全(priority/assignee,对齐 idx_tasks_status 同类索引)。
let steps: [(i32, fn(&Connection) -> Result<()>); 26] = [
// V27 = TD-260621-05 审批状态统一(ai_tool_executions.status executed→completed,
// 对齐 DTO 契约 audit/mod.rs:53 只列 completed + 前端 i18n auditLog.status 无 executed 键 +
// 治 AuditLog executed 记录显示错位蓝pending标签+raw"executed")。
let steps: [(i32, fn(&Connection) -> Result<()>); 27] = [
(1, migrate_v1),
(2, migrate_v2),
(3, migrate_v3),
@@ -56,6 +59,7 @@ pub fn run(conn: &Connection) -> Result<()> {
(24, migrate_v24),
(25, migrate_v25),
(26, migrate_v26),
(27, migrate_v27),
];
for (version, migrate_fn) in steps {
@@ -657,6 +661,24 @@ fn migrate_v26(conn: &Connection) -> Result<()> {
Ok(())
}
/// V27: 审批状态统一 executed→completed(TD-260621-05)
///
/// chat.rs ai_approve/ai_authorize_dir 审批通过后工具执行成功历史写 "executed",audit 内联执行
/// (低风险无审批)写 "completed",双轨并存致:① DTO 文档(audit/mod.rs:53)只列 completed 契约失配;
/// ② 前端 AuditLog statusClass/i18n auditLog.status 无 executed 键 → executed 记录显示蓝pending
/// 标签 + raw"executed"文案矛盾(用户见"待审批 executed");③ 未来 WHERE status='completed'
/// 统计会漏 executed 路径。统一为 completed(find_cached SW-16 透传已兼容双值,无破坏)。
/// 数据迁移:存量 executed→completed,新库/老库均跑(UPDATE 0 行也安全)。
fn migrate_v27(conn: &Connection) -> Result<()> {
conn.execute(
"UPDATE ai_tool_executions SET status = 'completed' WHERE status = 'executed'",
[],
)?;
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [27])?;
tracing::info!("迁移 v27 完成(审批状态 executed→completed 统一)");
Ok(())
}
/// V21 建表 SQL — 消息拆分存储 ai_messages 表
///
/// 与 V9_SQL 中的 ai_messages 镜像(V9 给新库,此 const 给老库 V21 迁移用 IF NOT EXISTS)。
@@ -1261,4 +1283,85 @@ mod tests {
.unwrap();
assert_eq!(v, 20);
}
/// V27:审批状态统一 executed→completed(TD-260621-05)
/// 存量 executed 记录转 completed,rejected/completed/failed 不变(只动 executed)
#[test]
fn v27_unifies_executed_to_completed() {
let conn = Connection::open_in_memory().expect("open in-memory db");
conn.execute_batch(
"CREATE TABLE schema_version (version INTEGER PRIMARY KEY);
CREATE TABLE ai_tool_executions (
id TEXT PRIMARY KEY,
tool_call_id TEXT,
tool_name TEXT,
status TEXT NOT NULL,
requested_at TEXT,
executed_at TEXT
);",
)
.expect("create ai_tool_executions");
// 混合状态:2 executed(待转)+ completed/rejected/failed(应不变)
conn.execute_batch(
"INSERT INTO ai_tool_executions (id, tool_call_id, tool_name, status) VALUES
('e1', 'tc1', 'write_file', 'executed'),
('e2', 'tc2', 'read_file', 'executed'),
('c1', 'tc3', 'list_directory', 'completed'),
('r1', 'tc4', 'write_file', 'rejected'),
('f1', 'tc5', 'run_command', 'failed');",
)
.unwrap();
migrate_v27(&conn).expect("v27 应成功统一状态");
// executed 全部转 completed
let executed_left: i64 = conn
.query_row("SELECT COUNT(*) FROM ai_tool_executions WHERE status = 'executed'", [], |r| r.get(0))
.unwrap();
assert_eq!(executed_left, 0, "executed 应全部转为 completed");
let completed: i64 = conn
.query_row("SELECT COUNT(*) FROM ai_tool_executions WHERE status = 'completed'", [], |r| r.get(0))
.unwrap();
assert_eq!(completed, 3, "原 2 executed + 1 completed = 3 completed");
// 其他状态不受影响
let rejected: i64 = conn
.query_row("SELECT COUNT(*) FROM ai_tool_executions WHERE status = 'rejected'", [], |r| r.get(0))
.unwrap();
assert_eq!(rejected, 1, "rejected 不变");
let failed: i64 = conn
.query_row("SELECT COUNT(*) FROM ai_tool_executions WHERE status = 'failed'", [], |r| r.get(0))
.unwrap();
assert_eq!(failed, 1, "failed 不变");
let v: i64 = conn
.query_row("SELECT MAX(version) FROM schema_version", [], |r| r.get(0))
.unwrap();
assert_eq!(v, 27, "应写入版本号 27");
}
/// V27 幂等:无 executed 时再跑不崩(UPDATE 0 行),版本号照写
#[test]
fn v27_idempotent_no_executed() {
let conn = Connection::open_in_memory().expect("open in-memory db");
conn.execute_batch(
"CREATE TABLE schema_version (version INTEGER PRIMARY KEY);
CREATE TABLE ai_tool_executions (id TEXT PRIMARY KEY, status TEXT NOT NULL);",
)
.expect("create tables");
conn.execute(
"INSERT INTO ai_tool_executions (id, status) VALUES ('c1', 'completed')",
[],
)
.unwrap();
migrate_v27(&conn).expect("v27 无 executed 时应幂等成功(UPDATE 0 行)");
let v: i64 = conn
.query_row("SELECT MAX(version) FROM schema_version", [], |r| r.get(0))
.unwrap();
assert_eq!(v, 27);
}
}