新增: 任务灵感归档后端

This commit is contained in:
lxy
2026-10-05 00:35:01 +08:00
parent c8c13d302a
commit f4d31e884a
17 changed files with 354 additions and 48 deletions
+65 -5
View File
@@ -53,7 +53,9 @@ pub fn run(conn: &Connection) -> Result<()> {
// 桌面侧栏/miniapp 列表按 origin 过滤子会话)。
// V45 = 无人值守任务边界(ai_conversations.boundary_json TEXT,TaskBoundary JSON 快照,
// 发起时一次声明存续期生效,随会话同亡)。
let steps: [(i32, fn(&Connection) -> Result<()>); 45] = [
// V46/V47 = 任务/灵感归档(archived INTEGER NOT NULL DEFAULT 0,归档功能 P1,
// 对标 ai_conversations.archived V4;归档与软删正交:回收站=删除缓冲,归档=低频可见保检索)。
let steps: [(i32, fn(&Connection) -> Result<()>); 47] = [
(1, migrate_v1),
(2, migrate_v2),
(3, migrate_v3),
@@ -99,6 +101,10 @@ pub fn run(conn: &Connection) -> Result<()> {
(43, migrate_v43),
(44, migrate_v44),
(45, migrate_v45),
// V46 = 任务归档(tasks.archived 列,归档功能 P1,对标 ai_conversations.archived V4);
// V47 = 灵感归档(ideas.archived 列,同款)。归档与软删正交:回收站=删除缓冲,归档=低频可见保检索。
(46, migrate_v46),
(47, migrate_v47),
];
for (version, migrate_fn) in steps {
@@ -1354,6 +1360,50 @@ fn migrate_v45(conn: &Connection) -> Result<()> {
Ok(())
}
/// V46: 幂等补 tasks.archived 列(任务归档,归档功能 P1)
///
/// 归档语义对标 ai_conversations.archived(V4):纯元数据标记,与 status/queue/deleted_at 正交。
/// 「作废」走 status=cancelled,「完结」走 done/cancelled——归档是另一种动作:
/// 低频完结任务移出主视图但保留检索(回收站=删除缓冲不可见,归档=低频可见保检索)。
/// - `INTEGER NOT NULL DEFAULT 0`:老库行迁移后全为 0(未归档),行为零变化;
/// - 不进 update_field 白名单(settings.rs):归档走专用 set_archived(带埋点 + 布尔语义),
/// 对标 status 收口思路防旁路写;
/// - 活跃查询(list_active/list_by_query/count_by_query/list_active_by_project/get_children/
/// advance_status_atomic/move_task_queue)恒带 `archived = 0` 过滤,归档任务退出主视图与
/// 状态机(冻结:不可推进/不可跨池/不可字段更新,需先取消归档)。
/// - **幂等**:column_exists 守卫(同 V45 模式);**可逆**:`ALTER TABLE tasks DROP COLUMN archived`。
fn migrate_v46(conn: &Connection) -> Result<()> {
if !column_exists(conn, "tasks", "archived") {
conn.execute(
"ALTER TABLE tasks ADD COLUMN archived INTEGER NOT NULL DEFAULT 0",
[],
)?;
tracing::info!("v46: 补建 tasks.archived 列(任务归档)");
}
conn.execute("INSERT OR IGNORE INTO schema_version (version) VALUES (?)", [46])?;
tracing::info!("迁移 v46 完成: tasks 加 archived 列");
Ok(())
}
/// V47: 幂等补 ideas.archived 列(灵感归档,归档功能 P1)
///
/// 对标 V46 tasks.archived:rejected/deferred/已晋升灵感一键归档移出主视图,保留检索。
/// 灵感无状态机收口(status 走 idea_repo 通用路径),但归档同样不进 update_field 白名单,
/// 走专用 set_archived(布尔语义 + 防旁路)。活跃查询恒带 `archived = 0`。
/// - **幂等**:column_exists 守卫;**可逆**:`ALTER TABLE ideas DROP COLUMN archived`。
fn migrate_v47(conn: &Connection) -> Result<()> {
if !column_exists(conn, "ideas", "archived") {
conn.execute(
"ALTER TABLE ideas ADD COLUMN archived INTEGER NOT NULL DEFAULT 0",
[],
)?;
tracing::info!("v47: 补建 ideas.archived 列(灵感归档)");
}
conn.execute("INSERT OR IGNORE INTO schema_version (version) VALUES (?)", [47])?;
tracing::info!("迁移 v47 完成: ideas 加 archived 列");
Ok(())
}
/// V21 建表 SQL — 消息拆分存储 ai_messages 表
///
/// 与 V9_SQL 中的 ai_messages 镜像(V9 给新库,此 const 给老库 V21 迁移用 IF NOT EXISTS)。
@@ -2357,6 +2407,16 @@ mod tests {
column_exists(&conn, "tasks", "module_id"),
"tasks.module_id 列缺失(V41 加)"
);
// tasks.archived(V46 归档功能 P1)
assert!(
column_exists(&conn, "tasks", "archived"),
"tasks.archived 列缺失(V46 加)"
);
// ideas.archived(V47 归档功能 P1)
assert!(
column_exists(&conn, "ideas", "archived"),
"ideas.archived 列缺失(V47 加)"
);
// 3. schema_version 应推进到 45(全量迁移成功落版本号)
let max_version: i64 = conn
@@ -2367,8 +2427,8 @@ mod tests {
)
.expect("查 schema_version 应成功");
assert_eq!(
max_version, 45,
"全量迁移后 schema_version 应为 45(实际 {}),说明某条 migrate_vN 链路断在中间",
max_version, 47,
"全量迁移后 schema_version 应为 47(实际 {}),说明某条 migrate_vN 链路断在中间",
max_version
);
@@ -2445,7 +2505,7 @@ mod tests {
|r| r.get(0),
)
.unwrap();
assert_eq!(max_v, 45, "首轮应推进到 45");
assert_eq!(max_v, 47, "首轮应推进到 47");
// 清空版本表强制全链第二遍(每步 execute 第二次)
conn.execute("DELETE FROM schema_version", []).unwrap();
@@ -2457,7 +2517,7 @@ mod tests {
|r| r.get(0),
)
.unwrap();
assert_eq!(max_v2, 45, "重跑后应重新推进到 45");
assert_eq!(max_v2, 47, "重跑后应重新推进到 47");
}
// ============================================================