新增: 三实体列表查询维度补全(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:
@@ -8,7 +8,7 @@ use tauri::State;
|
||||
use df_ai::provider::LlmProvider;
|
||||
use df_types::types::{new_id, Priority};
|
||||
use df_ideas::capture::Idea;
|
||||
use df_storage::crud::is_unique_constraint_err;
|
||||
use df_storage::crud::{is_unique_constraint_err, IdeaQuery};
|
||||
use df_storage::models::{IdeaEvaluationRecord, IdeaRecord, ProjectRecord};
|
||||
|
||||
use crate::state::AppState;
|
||||
@@ -32,16 +32,29 @@ fn default_priority() -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
/// 列出灵感,可选按 status 过滤(指定状态走 query 走白名单索引列,否则全量)
|
||||
/// 列出灵感。
|
||||
///
|
||||
/// **双路径向后兼容**(F-260621-02):
|
||||
/// - 旧调用方仅传 `status`(`ideaApi.list(status)`)→ 转 IdeaQuery 仅带 status,走
|
||||
/// `list_by_query`(白名单 status 列 WHERE),与原 `query("status", s)` 等价。
|
||||
/// - 新调用方传 `query`(`ideaApi.list(query)`)→ 多条件(status/keyword/order_by/limit/offset)。
|
||||
/// - 两者都不传 → 等价全量(`list_by_query` 空 query 走默认 created_at DESC,与 list_all 等价)。
|
||||
///
|
||||
/// `query` 优先于 `status`(二者同传时以 query 为准,避免重复过滤语义冲突)。
|
||||
#[tauri::command]
|
||||
pub async fn list_ideas(
|
||||
state: State<'_, AppState>,
|
||||
status: Option<String>,
|
||||
query: Option<IdeaQuery>,
|
||||
) -> Result<Vec<IdeaRecord>, String> {
|
||||
match status {
|
||||
Some(s) => state.ideas.query("status", &s).await.map_err(err_str),
|
||||
None => state.ideas.list_all().await.map_err(err_str),
|
||||
}
|
||||
let q = match query {
|
||||
Some(q) => q,
|
||||
None => IdeaQuery {
|
||||
status,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
state.ideas.list_by_query(&q).await.map_err(err_str)
|
||||
}
|
||||
|
||||
/// 列出指定灵感的评估历史(version DESC,最新版本在前)。
|
||||
|
||||
@@ -17,6 +17,7 @@ use df_project::scan::{
|
||||
collect_sample, detect_stack, discover_projects, extract_description,
|
||||
normalize_path, DiscoveredProject,
|
||||
};
|
||||
use df_storage::crud::ProjectQuery;
|
||||
use df_storage::models::ProjectRecord;
|
||||
|
||||
use crate::state::AppState;
|
||||
@@ -38,10 +39,27 @@ pub struct CreateProjectInput {
|
||||
pub stack: Option<String>,
|
||||
}
|
||||
|
||||
/// 列出未删除项目(过滤回收站)
|
||||
/// 列出未删除项目(过滤回收站)。
|
||||
///
|
||||
/// F-260621-02:吃可选 query(关键词/排序/分页),向后兼容——不传(或 None)走 list_active
|
||||
/// 全量(deleted_at IS NULL + created_at DESC),零破坏;传 query 走 list_by_query 动态 WHERE。
|
||||
#[tauri::command]
|
||||
pub async fn list_projects(state: State<'_, AppState>) -> Result<Vec<ProjectRecord>, String> {
|
||||
state.projects.list_active().await.map_err(err_str)
|
||||
pub async fn list_projects(
|
||||
state: State<'_, AppState>,
|
||||
query: Option<ProjectQuery>,
|
||||
) -> Result<Vec<ProjectRecord>, String> {
|
||||
match query {
|
||||
// None 或全空 query(trim 后 keyword 空 + 无 order_by/limit/offset)→ 走 list_active,
|
||||
// 与历史行为完全等价(列表上 list_by_query 空也等价,但保留 list_active 分支明示向后兼容契约)。
|
||||
Some(q) if q.keyword.as_deref().map(str::trim).is_some_and(|k| !k.is_empty())
|
||||
|| q.order_by.is_some()
|
||||
|| q.limit.is_some()
|
||||
|| q.offset.is_some() =>
|
||||
{
|
||||
state.projects.list_by_query(q).await.map_err(err_str)
|
||||
}
|
||||
_ => state.projects.list_active().await.map_err(err_str),
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建项目,返回完整记录
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user