新增: 任务灵感归档后端
This commit is contained in:
@@ -165,6 +165,7 @@ mod idea_source_test_helpers {
|
||||
ai_analysis: None,
|
||||
scores: None,
|
||||
related_ids: None,
|
||||
archived: false,
|
||||
created_at: now.clone(),
|
||||
updated_at: now,
|
||||
})
|
||||
|
||||
@@ -86,6 +86,7 @@ pub fn register(registry: &mut AiToolRegistry, db: &Arc<Database>) {
|
||||
source: args["source"].as_str().map(|s| s.to_string()),
|
||||
promoted_to: None, ai_analysis: None, scores: None,
|
||||
related_ids: None,
|
||||
archived: false,
|
||||
created_at: now_millis(), updated_at: now_millis(),
|
||||
};
|
||||
let id = record.id.clone();
|
||||
|
||||
@@ -121,6 +121,7 @@ pub fn register(registry: &mut AiToolRegistry, db: &Arc<Database>) {
|
||||
queue,
|
||||
parent_id,
|
||||
content_json,
|
||||
archived: false,
|
||||
created_at: now_millis(), updated_at: now_millis(),
|
||||
};
|
||||
let id = record.id.clone();
|
||||
|
||||
@@ -96,6 +96,7 @@ pub async fn create_idea(
|
||||
ai_analysis: None,
|
||||
scores: None,
|
||||
related_ids: None,
|
||||
archived: false,
|
||||
created_at: now.clone(),
|
||||
updated_at: now,
|
||||
};
|
||||
@@ -142,13 +143,20 @@ pub async fn update_idea(
|
||||
serde_json::from_str::<serde_json::Value>(&value)
|
||||
.map_err(|e| format!("{field} 不是合法 JSON: {e}"))?;
|
||||
}
|
||||
// update_field_active 过滤软删(deleted_at IS NULL),回收站灵感不可改字段。
|
||||
// update_field_active 过滤软删与归档(deleted_at IS NULL AND archived = 0),
|
||||
// 回收站/归档灵感不可改字段。
|
||||
let updated = state
|
||||
.ideas
|
||||
.update_field_active(&id, &field, &value)
|
||||
.await
|
||||
.map_err(err_str)?;
|
||||
if !updated {
|
||||
// 区分「已归档」与「不存在/已软删」,避免归档灵感被误报为已删除。
|
||||
if let Ok(Some(rec)) = state.ideas.get_by_id(&id).await {
|
||||
if rec.archived {
|
||||
return Err(format!("灵感 ID {id} 已归档,请先取消归档再修改"));
|
||||
}
|
||||
}
|
||||
return Err(format!("灵感 ID {id} 不存在或已删除"));
|
||||
}
|
||||
Ok(true)
|
||||
@@ -166,6 +174,26 @@ pub async fn restore_idea(state: State<'_, AppState>, id: String) -> Result<bool
|
||||
state.ideas.restore(&id).await.map_err(err_str)
|
||||
}
|
||||
|
||||
/// 归档/取消归档灵感(归档功能 P1,V47)。对标 archive_task。
|
||||
///
|
||||
/// 纯元数据标记:归档后灵感退出主视图(list_by_query 恒带 archived = 0),
|
||||
/// 仅在归档视图(list_archived_ideas)可见。软删优先:回收站灵感不可直接归档。
|
||||
/// 设计要点(灵感归档动线):rejected/deferred 状态提供一键归档,清空主视图噪音。
|
||||
#[tauri::command]
|
||||
pub async fn archive_idea(
|
||||
state: State<'_, AppState>,
|
||||
id: String,
|
||||
archived: bool,
|
||||
) -> Result<bool, String> {
|
||||
state.ideas.set_archived(&id, archived).await.map_err(err_str)
|
||||
}
|
||||
|
||||
/// 列出归档灵感(archived = 1 且未删),按更新时间降序。归档视图数据源。
|
||||
#[tauri::command]
|
||||
pub async fn list_archived_ideas(state: State<'_, AppState>) -> Result<Vec<IdeaRecord>, String> {
|
||||
state.ideas.list_archived().await.map_err(err_str)
|
||||
}
|
||||
|
||||
/// 双向同步关联关系:原子地设置主体灵感的关联目标列表,并自动添加/移除反向关联。
|
||||
/// 主体灵感 + 所有受影响的关联目标在同一 SQLite 事务中更新,保证原子性。
|
||||
#[tauri::command]
|
||||
|
||||
@@ -329,6 +329,7 @@ pub async fn create_task(
|
||||
queue,
|
||||
parent_id,
|
||||
content_json,
|
||||
archived: false,
|
||||
created_at: now.clone(),
|
||||
updated_at: now,
|
||||
};
|
||||
@@ -471,6 +472,11 @@ pub async fn update_task(
|
||||
.await
|
||||
.map_err(err_str)?;
|
||||
if !updated {
|
||||
// update_field_active 恒带 archived = 0:0 行时区分「已归档」与「不存在/已软删」,
|
||||
// 避免归档任务被误报为已删除(用户无从知道要先取消归档)。
|
||||
if current.as_ref().is_some_and(|c| c.archived) {
|
||||
return Err(format!("任务 ID {id} 已归档,请先取消归档再修改"));
|
||||
}
|
||||
return Err(format!("任务 ID {id} 不存在或已删除"));
|
||||
}
|
||||
// LW-9改 parent_id 后重算父聚合——旧父(任务已离开,计数减少)与新父(任务挂上)。
|
||||
@@ -588,6 +594,52 @@ pub async fn restore_task(state: State<'_, AppState>, id: String) -> Result<bool
|
||||
Ok(ok)
|
||||
}
|
||||
|
||||
/// 归档/取消归档任务(归档功能 P1,V46)。对标 ai_conversation_archive。
|
||||
///
|
||||
/// 纯元数据标记,与 status/queue 正交:归档后任务退出主视图与状态机(冻结:
|
||||
/// 不可推进/跨池/字段更新,repo 层活跃查询与写入路径恒带 archived = 0 收口),
|
||||
/// 仅在归档视图(list_archived_tasks)可见。软删优先:回收站任务不可直接归档。
|
||||
///
|
||||
/// 埋点 task_archived/task_unarchived(项目事件流,best-effort 不阻断)。
|
||||
#[tauri::command]
|
||||
pub async fn archive_task(
|
||||
state: State<'_, AppState>,
|
||||
id: String,
|
||||
archived: bool,
|
||||
) -> Result<bool, String> {
|
||||
let rec = state.tasks.get_by_id(&id).await.map_err(err_str)?;
|
||||
let ok = state.tasks.set_archived(&id, archived).await.map_err(err_str)?;
|
||||
if ok {
|
||||
if let Some(r) = rec {
|
||||
emit_event(
|
||||
&state,
|
||||
&r.project_id,
|
||||
if archived { "task_archived" } else { "task_unarchived" },
|
||||
Some("task"),
|
||||
Some(&r.id),
|
||||
None,
|
||||
Some(if archived { "archived" } else { "active" }),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
Ok(ok)
|
||||
}
|
||||
|
||||
/// 列出归档任务(archived = 1 且未删),按更新时间降序。归档视图数据源。
|
||||
#[tauri::command]
|
||||
pub async fn list_archived_tasks(
|
||||
state: State<'_, AppState>,
|
||||
project_id: Option<String>,
|
||||
) -> Result<Vec<TaskRecord>, String> {
|
||||
let tasks = state.tasks.list_archived().await.map_err(err_str)?;
|
||||
// 归档量级小(低频操作),内存过滤 project_id 足够;SQL 下推待归档量增长再说
|
||||
Ok(match project_id {
|
||||
Some(pid) => tasks.into_iter().filter(|t| t.project_id == pid).collect(),
|
||||
None => tasks,
|
||||
})
|
||||
}
|
||||
|
||||
/// 推进任务状态(任务推进链 F-260616-02,推进链唯一 status 写入路径)。
|
||||
///
|
||||
/// thin 入口业务逻辑(状态机校验 + 原子 CAS + review_rounds 累加 + 父聚合)
|
||||
|
||||
@@ -373,6 +373,9 @@ pub fn run() {
|
||||
commands::task::update_task,
|
||||
commands::task::delete_task,
|
||||
commands::task::restore_task,
|
||||
// 归档功能 P1(V46/V47):任务归档 + 归档视图
|
||||
commands::task::archive_task,
|
||||
commands::task::list_archived_tasks,
|
||||
commands::task::get_task_by_id,
|
||||
commands::task::advance_task,
|
||||
// 知识图谱 Phase 1(对标设计 §五):任务横向关联 CRUD + 跨池移动 + 父子树
|
||||
@@ -415,6 +418,9 @@ pub fn run() {
|
||||
commands::idea::relate_ideas,
|
||||
commands::idea::delete_idea,
|
||||
commands::idea::restore_idea,
|
||||
// 归档功能 P1(V47):灵感归档 + 归档视图
|
||||
commands::idea::archive_idea,
|
||||
commands::idea::list_archived_ideas,
|
||||
commands::idea::list_deleted_ideas,
|
||||
commands::idea::evaluate_idea,
|
||||
commands::idea::list_idea_evaluations,
|
||||
|
||||
Reference in New Issue
Block a user