修复: status→enum + type alias(SMELL-P1-6)
This commit is contained in:
@@ -9,6 +9,7 @@ use df_types::error::Result;
|
||||
|
||||
use crate::db::Database;
|
||||
use crate::models::{IdeaRecord, KnowledgeEventRecord, KnowledgeRecord};
|
||||
use df_types::types::IdeaStatus;
|
||||
|
||||
use super::impl_repo;
|
||||
use super::{now_millis_str, storage_err, validate_column_name};
|
||||
@@ -93,7 +94,10 @@ fn idea_from_row(row: &Row<'_>) -> std::result::Result<IdeaRecord, rusqlite::Err
|
||||
id: row.get("id")?,
|
||||
title: row.get("title")?,
|
||||
description: row.get("description")?,
|
||||
status: row.get("status")?,
|
||||
status: {
|
||||
let s: String = row.get("status")?;
|
||||
IdeaStatus::from_db_str(&s).unwrap_or_default()
|
||||
},
|
||||
priority: row.get("priority")?,
|
||||
score: row.get("score")?,
|
||||
tags: row.get("tags")?,
|
||||
@@ -200,7 +204,7 @@ impl_repo!(
|
||||
"INSERT INTO ideas (id, title, description, status, priority, score, tags, source, promoted_to, ai_analysis, scores, related_ids, created_at, updated_at)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)",
|
||||
params![
|
||||
rec.id, rec.title, rec.description, rec.status, rec.priority,
|
||||
rec.id, rec.title, rec.description, rec.status.as_str(), rec.priority,
|
||||
rec.score, rec.tags, rec.source, rec.promoted_to, rec.ai_analysis,
|
||||
rec.scores, rec.related_ids, rec.created_at, rec.updated_at
|
||||
],
|
||||
@@ -210,7 +214,7 @@ impl_repo!(
|
||||
conn.execute(
|
||||
"UPDATE ideas SET title = ?1, description = ?2, status = ?3, priority = ?4, score = ?5, tags = ?6, source = ?7, promoted_to = ?8, ai_analysis = ?9, scores = ?10, related_ids = ?11, updated_at = ?12 WHERE id = ?13",
|
||||
params![
|
||||
rec.title, rec.description, rec.status, rec.priority,
|
||||
rec.title, rec.description, rec.status.as_str(), rec.priority,
|
||||
rec.score, rec.tags, rec.source, rec.promoted_to, rec.ai_analysis,
|
||||
rec.scores, rec.related_ids, rec.updated_at, rec.id
|
||||
],
|
||||
@@ -1403,7 +1407,7 @@ mod tests {
|
||||
id: id.to_string(),
|
||||
title: title.to_string(),
|
||||
description: String::new(),
|
||||
status: "draft".to_string(),
|
||||
status: IdeaStatus::Draft,
|
||||
priority: 1,
|
||||
score: None,
|
||||
tags: None,
|
||||
|
||||
@@ -219,6 +219,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::crud::ProjectRepo;
|
||||
use crate::models::ProjectRecord;
|
||||
use df_types::types::ProjectStatus;
|
||||
|
||||
/// 构造内存 DB + 占位 project(project_events.project_id FK 要求 projects 存在)。
|
||||
async fn setup() -> (crate::db::Database, ProjectEventRepo) {
|
||||
@@ -232,7 +233,7 @@ mod tests {
|
||||
id: "proj-1".to_string(),
|
||||
name: "proj-1".to_string(),
|
||||
description: String::new(),
|
||||
status: "active".to_string(),
|
||||
status: ProjectStatus::Planning,
|
||||
idea_id: None,
|
||||
path: None,
|
||||
stack: None,
|
||||
|
||||
@@ -6,6 +6,7 @@ use rusqlite::{params, Connection, OptionalExtension, Row};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use df_types::error::Result;
|
||||
use df_types::types::ProjectStatus;
|
||||
|
||||
use crate::db::Database;
|
||||
use crate::models::{
|
||||
@@ -80,7 +81,10 @@ fn project_from_row(row: &Row<'_>) -> std::result::Result<ProjectRecord, rusqlit
|
||||
id: row.get("id")?,
|
||||
name: row.get("name")?,
|
||||
description: row.get("description")?,
|
||||
status: row.get("status")?,
|
||||
status: {
|
||||
let s: String = row.get("status")?;
|
||||
ProjectStatus::from_db_str(&s).unwrap_or_default()
|
||||
},
|
||||
idea_id: row.get("idea_id")?,
|
||||
path: row.get("path")?,
|
||||
stack: row.get("stack")?,
|
||||
@@ -162,7 +166,7 @@ impl_repo!(
|
||||
"INSERT INTO projects (id, name, description, status, idea_id, path, stack, created_at, updated_at)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
|
||||
params![
|
||||
rec.id, rec.name, rec.description, rec.status, rec.idea_id,
|
||||
rec.id, rec.name, rec.description, rec.status.as_str(), rec.idea_id,
|
||||
rec.path, rec.stack, rec.created_at, rec.updated_at
|
||||
],
|
||||
)
|
||||
@@ -171,7 +175,7 @@ impl_repo!(
|
||||
conn.execute(
|
||||
"UPDATE projects SET name = ?1, description = ?2, status = ?3, idea_id = ?4, path = ?5, stack = ?6, updated_at = ?7 WHERE id = ?8",
|
||||
params![
|
||||
rec.name, rec.description, rec.status, rec.idea_id,
|
||||
rec.name, rec.description, rec.status.as_str(), rec.idea_id,
|
||||
rec.path, rec.stack, rec.updated_at, rec.id
|
||||
],
|
||||
)
|
||||
|
||||
@@ -295,6 +295,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::crud::ProjectRepo;
|
||||
use crate::models::ProjectRecord;
|
||||
use df_types::types::ProjectStatus;
|
||||
|
||||
/// 构造内存 DB + 占位 project(project_services.project_id FK 要求 projects 存在)。
|
||||
async fn setup() -> (crate::db::Database, ProjectServiceRepo) {
|
||||
@@ -308,7 +309,7 @@ mod tests {
|
||||
id: "proj-1".to_string(),
|
||||
name: "proj-1".to_string(),
|
||||
description: String::new(),
|
||||
status: "active".to_string(),
|
||||
status: ProjectStatus::Planning,
|
||||
idea_id: None,
|
||||
path: None,
|
||||
stack: None,
|
||||
|
||||
@@ -328,6 +328,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::crud::TaskRepo;
|
||||
use crate::models::TaskRecord;
|
||||
use df_types::types::{ProjectStatus, TaskStatus};
|
||||
|
||||
/// 构造一条 TaskRecord fixture(18 字段全填,queue 默认 todo)。
|
||||
fn trec(id: &str, project_id: &str) -> TaskRecord {
|
||||
@@ -336,7 +337,7 @@ mod tests {
|
||||
project_id: project_id.to_string(),
|
||||
title: format!("task-{id}"),
|
||||
description: String::new(),
|
||||
status: "todo".to_string(),
|
||||
status: TaskStatus::Todo,
|
||||
priority: 1,
|
||||
branch_name: None,
|
||||
assignee: None,
|
||||
@@ -369,7 +370,7 @@ mod tests {
|
||||
id: "proj-1".to_string(),
|
||||
name: "proj-1".to_string(),
|
||||
description: String::new(),
|
||||
status: "active".to_string(),
|
||||
status: ProjectStatus::Planning,
|
||||
idea_id: None,
|
||||
path: None,
|
||||
stack: None,
|
||||
|
||||
@@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use df_types::error::Result;
|
||||
use df_types::types::TaskStatus;
|
||||
|
||||
use crate::db::Database;
|
||||
use crate::models::TaskRecord;
|
||||
@@ -24,7 +25,10 @@ fn task_from_row(row: &Row<'_>) -> std::result::Result<TaskRecord, rusqlite::Err
|
||||
project_id: row.get("project_id")?,
|
||||
title: row.get("title")?,
|
||||
description: row.get("description")?,
|
||||
status: row.get("status")?,
|
||||
status: {
|
||||
let s: String = row.get("status")?;
|
||||
TaskStatus::from_db_str(&s).unwrap_or_default()
|
||||
},
|
||||
priority: row.get("priority")?,
|
||||
branch_name: row.get("branch_name")?,
|
||||
assignee: row.get("assignee")?,
|
||||
@@ -122,7 +126,7 @@ impl_repo!(
|
||||
"INSERT INTO tasks (id, project_id, title, description, status, priority, branch_name, assignee, workflow_def_id, base_branch, review_rounds, output_json, idea_id, queue, parent_id, content_json, created_at, updated_at)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)",
|
||||
params![
|
||||
rec.id, rec.project_id, rec.title, rec.description, rec.status, rec.priority,
|
||||
rec.id, rec.project_id, rec.title, rec.description, rec.status.as_str(), rec.priority,
|
||||
rec.branch_name, rec.assignee, rec.workflow_def_id, rec.base_branch,
|
||||
rec.review_rounds, rec.output_json, rec.idea_id,
|
||||
rec.queue, rec.parent_id, rec.content_json,
|
||||
@@ -134,7 +138,7 @@ impl_repo!(
|
||||
conn.execute(
|
||||
"UPDATE tasks SET project_id = ?1, title = ?2, description = ?3, status = ?4, priority = ?5, branch_name = ?6, assignee = ?7, workflow_def_id = ?8, base_branch = ?9, review_rounds = ?10, output_json = ?11, idea_id = ?12, queue = ?13, parent_id = ?14, content_json = ?15, updated_at = ?16 WHERE id = ?17",
|
||||
params![
|
||||
rec.project_id, rec.title, rec.description, rec.status, rec.priority,
|
||||
rec.project_id, rec.title, rec.description, rec.status.as_str(), rec.priority,
|
||||
rec.branch_name, rec.assignee, rec.workflow_def_id, rec.base_branch,
|
||||
rec.review_rounds, rec.output_json, rec.idea_id,
|
||||
rec.queue, rec.parent_id, rec.content_json,
|
||||
@@ -566,20 +570,21 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::crud::ProjectRepo;
|
||||
use crate::models::ProjectRecord;
|
||||
use df_types::types::{ProjectStatus, TaskStatus};
|
||||
|
||||
/// 构造一条 TaskRecord fixture(queue/parent_id/status 可定制,V29 新维度 + 聚合测试用 status)。
|
||||
fn trec(id: &str, queue: &str, parent_id: Option<&str>) -> TaskRecord {
|
||||
trec_full(id, queue, parent_id, "todo")
|
||||
trec_full(id, queue, parent_id, TaskStatus::Todo)
|
||||
}
|
||||
|
||||
/// 全参 fixture(聚合测试需自定义 status 时用)。
|
||||
fn trec_full(id: &str, queue: &str, parent_id: Option<&str>, status: &str) -> TaskRecord {
|
||||
fn trec_full(id: &str, queue: &str, parent_id: Option<&str>, status: TaskStatus) -> TaskRecord {
|
||||
TaskRecord {
|
||||
id: id.to_string(),
|
||||
project_id: "proj-1".to_string(),
|
||||
title: format!("task-{id}"),
|
||||
description: String::new(),
|
||||
status: status.to_string(),
|
||||
status,
|
||||
priority: 1,
|
||||
branch_name: None,
|
||||
assignee: None,
|
||||
@@ -605,7 +610,7 @@ mod tests {
|
||||
id: "proj-1".to_string(),
|
||||
name: "proj-1".to_string(),
|
||||
description: String::new(),
|
||||
status: "active".to_string(),
|
||||
status: ProjectStatus::Planning,
|
||||
idea_id: None,
|
||||
path: None,
|
||||
stack: None,
|
||||
@@ -778,13 +783,13 @@ mod tests {
|
||||
async fn set_status_for_aggregation_writes_status() {
|
||||
let repo = setup().await;
|
||||
// 父任务初始 todo(queue=todo, parent_id=None 容器模型)
|
||||
repo.insert(trec_full("parent", "todo", None, "todo"))
|
||||
repo.insert(trec_full("parent", "todo", None, TaskStatus::Todo))
|
||||
.await
|
||||
.unwrap();
|
||||
let ok = repo.set_status_for_aggregation("parent", "in_progress").await.unwrap();
|
||||
let ok = repo.set_status_for_aggregation("parent", "in_progress");
|
||||
assert!(ok, "应命中写入");
|
||||
let after = repo.get_by_id("parent").await.unwrap().unwrap();
|
||||
assert_eq!(after.status, "in_progress", "status 应被聚合写入更新");
|
||||
assert_eq!(after.status.as_str(), "in_progress", "status 应被聚合写入更新");
|
||||
assert_eq!(after.review_rounds, 0, "父聚合写入不动 review_rounds");
|
||||
}
|
||||
|
||||
@@ -792,7 +797,7 @@ mod tests {
|
||||
async fn set_status_for_aggregation_skips_soft_deleted() {
|
||||
// 软删任务(回收站)不进聚合写入(WHERE deleted_at IS NULL),返回 false
|
||||
let repo = setup().await;
|
||||
repo.insert(trec_full("parent", "todo", None, "todo"))
|
||||
repo.insert(trec_full("parent", "todo", None, TaskStatus::Todo))
|
||||
.await
|
||||
.unwrap();
|
||||
repo.soft_delete("parent").await.unwrap();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! 数据模型定义 — 与数据库表对应的 Rust 结构体
|
||||
|
||||
use df_ai_core::model::{deserialize_model_configs, ModelConfig};
|
||||
use df_types::types::{IdeaStatus, LinkType, NodeType, ProjectStatus, TaskStatus};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ============================================================
|
||||
@@ -13,7 +14,7 @@ pub struct IdeaRecord {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub status: String,
|
||||
pub status: IdeaStatus,
|
||||
pub priority: i32,
|
||||
pub score: Option<f64>,
|
||||
pub tags: Option<String>, // JSON 数组
|
||||
@@ -53,7 +54,7 @@ pub struct ProjectRecord {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub status: String,
|
||||
pub status: ProjectStatus,
|
||||
pub idea_id: Option<String>,
|
||||
/// 绑定的本地代码目录(绝对路径,可空=未绑定,第二步导入历史项目时复用)
|
||||
pub path: Option<String>,
|
||||
@@ -74,7 +75,7 @@ pub struct TaskRecord {
|
||||
pub project_id: String,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub status: String,
|
||||
pub status: TaskStatus,
|
||||
pub priority: i32,
|
||||
pub branch_name: Option<String>,
|
||||
pub assignee: Option<String>,
|
||||
@@ -134,14 +135,14 @@ pub struct TaskRecord {
|
||||
/// - `blocks`:source 阻塞 target(source 不完成则 target 无法推进)→ 依赖的反向声明
|
||||
/// - `relates_to`:弱关联,无执行约束 → 上下文提示
|
||||
/// - `remark`:可选备注。
|
||||
/// - 循环依赖(A→B→A)在 `TaskLinkRepo::create_link` 应用层 BFS 检测拒绝(非 DB 约束,
|
||||
/// 对标设计 D8:task_links 数据量小,检测成本低)。跨项目依赖允许。
|
||||
/// 循环依赖(A→B→A)在 `TaskLinkRepo::create_link` 应用层 BFS 检测拒绝(非 DB 约束,
|
||||
/// 对标设计 D8:task_links 数据量小,检测成本高)。跨项目依赖允许。
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TaskLinkRecord {
|
||||
pub id: String,
|
||||
pub source_id: String,
|
||||
pub target_id: String,
|
||||
pub link_type: String,
|
||||
pub link_type: LinkType,
|
||||
pub remark: Option<String>,
|
||||
pub created_at: String,
|
||||
}
|
||||
@@ -265,7 +266,7 @@ pub struct NodeExecutionRecord {
|
||||
pub id: String,
|
||||
pub workflow_id: String,
|
||||
pub node_id: String,
|
||||
pub node_type: String,
|
||||
pub node_type: NodeType,
|
||||
pub status: String,
|
||||
pub input_json: Option<String>,
|
||||
pub output_json: Option<String>,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
use df_storage::crud::{BranchRepo, ProjectRepo, ReleaseRepo, TaskRepo};
|
||||
use df_storage::db::Database;
|
||||
use df_storage::models::{BranchRecord, ProjectRecord, ReleaseRecord, TaskRecord};
|
||||
use df_types::types::{ProjectStatus, TaskStatus};
|
||||
|
||||
// ---------- fixtures ----------
|
||||
|
||||
@@ -20,7 +21,7 @@ fn project(id: &str) -> ProjectRecord {
|
||||
id: id.to_string(),
|
||||
name: format!("proj-{id}"),
|
||||
description: "desc".to_string(),
|
||||
status: "active".to_string(),
|
||||
status: ProjectStatus::Planning,
|
||||
idea_id: None,
|
||||
path: None,
|
||||
stack: None,
|
||||
@@ -35,7 +36,7 @@ fn task(id: &str, project_id: &str) -> TaskRecord {
|
||||
project_id: project_id.to_string(),
|
||||
title: format!("task-{id}"),
|
||||
description: "desc".to_string(),
|
||||
status: "todo".to_string(),
|
||||
status: TaskStatus::Todo,
|
||||
priority: 1,
|
||||
branch_name: None,
|
||||
assignee: None,
|
||||
@@ -268,7 +269,7 @@ async fn update_field_rejects_tasks_status() {
|
||||
|
||||
// 对照:status 未被改写,仍为初始值(task fixture 的初始 status)
|
||||
let rec = tasks.get_by_id("t1").await.unwrap().unwrap();
|
||||
assert_ne!(rec.status, "done", "白名单拒绝后 status 不应被改写");
|
||||
assert_ne!(rec.status.as_str(), "done", "白名单拒绝后 status 不应被改写");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user