新增: 三实体列表查询维度补全(status/关键词/排序/分页下沉后端)

任务/项目/灵感三实体新增 list_by_query 动态 WHERE(累积式 where_clauses
+params_vec 收口)+ order_by 白名单防注入 + limit/offset 钳制。命令层
list_{tasks,projects,ideas} 吃 Option<XxxQuery> 双参向后兼容(旧无参/单参
路径等价全量)。前端 Tasks/Ideas status/keyword 筛选下沉后端 query。

F-260621-02
This commit is contained in:
2026-06-22 01:03:37 +08:00
parent 72e57c6232
commit c9b6e28433
18 changed files with 878 additions and 76 deletions

View File

@@ -4,6 +4,7 @@ use serde::Deserialize;
use tauri::State;
use df_types::types::{new_id, TaskStatus};
use df_storage::crud::TaskQuery;
use df_storage::models::TaskRecord;
use crate::state::AppState;
@@ -30,18 +31,41 @@ fn default_priority() -> i32 {
2 // medium — 新任务默认中优先级(非 high),符合常识
}
/// 列出未删除任务(deleted_at IS NULL),可按 project_id 过滤
/// 列出未删除任务(deleted_at IS NULL)。
///
/// 软删对标 projects:默认过滤回收站(语义与 list_projects 一致)。
/// 有 project_id 时走 list_active_by_project(SQL 下推 WHERE deleted_at IS NULL AND
/// project_id = ?,避免旧 list_active 全表扫 + 内存 retain 的 N×M 热点);
/// 无 project_id 时 fallback list_active(列全部未删任务)。
/// 注意:不能用通用 query 宏——它不带 deleted_at 过滤,会把回收站任务也返回。
/// **向后兼容铁律**:`project_id` 与 `query` 两参都可选,旧调用方不传(或只传 project_id)
/// 必须等价改造前的全量行为,零破坏。
///
/// F-260621-02 查询维度补全(机制优先 prompt 说教):
/// - 优先走 `query`(`TaskQuery` 多维动态 WHERE):status(P1 下沉)/ keyword(P2 LIKE)
/// /project_id/priority/assignee/order_by/limit/offset 任意组合。
/// - 旧调用方仍可直接传 `project_id`(单维度),此时走 list_active_by_project
/// (SQL 下推,命中 idx_tasks_project_id),保持等价行为。
/// - query 与 project_id 同时传时:query 优先(其内含 project_id 维度,更全),project_id 忽略。
/// - 均不传时:全量未删任务(等价改造前 list_active 行为)。
///
/// status 值合法性兜底:TaskStatus::is_valid 拦截非法值(拼写错/越界),非法值返回 Err
/// (与 update_task 的 status 校验一致),不静默返回空结果误导调用方。
#[tauri::command]
pub async fn list_tasks(
state: State<'_, AppState>,
project_id: Option<String>,
query: Option<TaskQuery>,
) -> Result<Vec<TaskRecord>, String> {
if let Some(q) = &query {
// status 值兜底校验(非法值早 Err,不进 DB 层)
if let Some(status) = &q.status {
if !TaskStatus::is_valid(status) {
return Err(format!(
"非法 status 值 {:?},合法值: {:?}",
status,
TaskStatus::valid_values()
));
}
}
return state.tasks.list_by_query(q).await.map_err(err_str);
}
// 旧调用方:单维度 project_id(SQL 下推,fallback 等价改造前行为)
let tasks = match project_id {
Some(pid) => state
.tasks