From 4f4a44ddb63aeb427a736571dc44c5e988e8795c Mon Sep 17 00:00:00 2001 From: iven Date: Sun, 26 Apr 2026 14:35:19 +0800 Subject: [PATCH] =?UTF-8?q?test(health):=20=E8=A1=A5=E5=85=85=20article/di?= =?UTF-8?q?alysis/lab=5Freport=20=E7=8A=B6=E6=80=81=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=20+=2083=20=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - validate_article_status (枚举白名单) + validate_article_status_transition (draft→pending_review→approved→published, rejected→pending_review, published→draft) - validate_dialysis_status + validate_dialysis_status_transition (draft→completed→reviewed) - validate_lab_report_status_transition (pending→reviewed) - 全部 83 个 validation 测试通过 --- crates/erp-health/src/service/validation.rs | 141 ++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/crates/erp-health/src/service/validation.rs b/crates/erp-health/src/service/validation.rs index eabb6a0..4b5f993 100644 --- a/crates/erp-health/src/service/validation.rs +++ b/crates/erp-health/src/service/validation.rs @@ -131,6 +131,85 @@ pub fn validate_online_status(value: &str) -> HealthResult<()> { Ok(()) } +/// article.status 枚举白名单 +pub fn validate_article_status(value: &str) -> HealthResult<()> { + validate_enum!(value, "article.status", [ + "draft", "pending_review", "approved", "rejected", "published", + ]); + Ok(()) +} + +/// article.status 状态转换 +/// draft → pending_review, rejected → pending_review +/// pending_review → approved / rejected +/// approved → published +/// published → draft (下架) +pub fn validate_article_status_transition(current: &str, new: &str) -> HealthResult<()> { + if current == new { + return Ok(()); + } + let allowed = match current { + "draft" => matches!(new, "pending_review"), + "pending_review" => matches!(new, "approved" | "rejected"), + "approved" => matches!(new, "published"), + "rejected" => matches!(new, "pending_review"), + "published" => matches!(new, "draft"), + _ => false, + }; + if allowed { + Ok(()) + } else { + Err(HealthError::InvalidStatusTransition(format!( + "article.status: 不允许从 '{}' 转换到 '{}'", current, new + ))) + } +} + +/// dialysis_record.status 枚举白名单 +pub fn validate_dialysis_status(value: &str) -> HealthResult<()> { + validate_enum!(value, "dialysis_record.status", ["draft", "completed", "reviewed"]); + Ok(()) +} + +/// dialysis_record.status 状态转换 +/// draft → completed → reviewed +pub fn validate_dialysis_status_transition(current: &str, new: &str) -> HealthResult<()> { + if current == new { + return Ok(()); + } + let allowed = match current { + "draft" => matches!(new, "completed"), + "completed" => matches!(new, "reviewed"), + _ => false, + }; + if allowed { + Ok(()) + } else { + Err(HealthError::InvalidStatusTransition(format!( + "dialysis_record.status: 不允许从 '{}' 转换到 '{}'", current, new + ))) + } +} + +/// lab_report.status 状态转换 +/// pending → reviewed +pub fn validate_lab_report_status_transition(current: &str, new: &str) -> HealthResult<()> { + if current == new { + return Ok(()); + } + let allowed = match current { + "pending" => matches!(new, "reviewed"), + _ => false, + }; + if allowed { + Ok(()) + } else { + Err(HealthError::InvalidStatusTransition(format!( + "lab_report.status: 不允许从 '{}' 转换到 '{}'", current, new + ))) + } +} + /// follow_up_task.status 状态转换(含 overdue 状态) pub fn validate_follow_up_status_transition(current: &str, new: &str) -> HealthResult<()> { if current == new { @@ -276,6 +355,68 @@ mod tests { #[test] fn online_invalid() { assert!(validate_online_status("away").is_err()); } + // --- article_status --- + #[test] + fn article_draft() { assert!(validate_article_status("draft").is_ok()); } + #[test] + fn article_published() { assert!(validate_article_status("published").is_ok()); } + #[test] + fn article_invalid() { assert!(validate_article_status("archived").is_err()); } + + // --- article_status_transition --- + #[test] + fn art_draft_to_pending_review() { assert!(validate_article_status_transition("draft", "pending_review").is_ok()); } + #[test] + fn art_draft_to_published_fails() { assert!(validate_article_status_transition("draft", "published").is_err()); } + #[test] + fn art_pending_review_to_approved() { assert!(validate_article_status_transition("pending_review", "approved").is_ok()); } + #[test] + fn art_pending_review_to_rejected() { assert!(validate_article_status_transition("pending_review", "rejected").is_ok()); } + #[test] + fn art_pending_review_to_draft_fails() { assert!(validate_article_status_transition("pending_review", "draft").is_err()); } + #[test] + fn art_approved_to_published() { assert!(validate_article_status_transition("approved", "published").is_ok()); } + #[test] + fn art_rejected_to_pending_review() { assert!(validate_article_status_transition("rejected", "pending_review").is_ok()); } + #[test] + fn art_published_to_draft() { assert!(validate_article_status_transition("published", "draft").is_ok()); } + #[test] + fn art_published_to_approved_fails() { assert!(validate_article_status_transition("published", "approved").is_err()); } + #[test] + fn art_same_status_ok() { assert!(validate_article_status_transition("draft", "draft").is_ok()); } + + // --- dialysis_status --- + #[test] + fn dialysis_draft() { assert!(validate_dialysis_status("draft").is_ok()); } + #[test] + fn dialysis_reviewed() { assert!(validate_dialysis_status("reviewed").is_ok()); } + #[test] + fn dialysis_invalid() { assert!(validate_dialysis_status("approved").is_err()); } + + // --- dialysis_status_transition --- + #[test] + fn dial_draft_to_completed() { assert!(validate_dialysis_status_transition("draft", "completed").is_ok()); } + #[test] + fn dial_draft_to_reviewed_fails() { assert!(validate_dialysis_status_transition("draft", "reviewed").is_err()); } + #[test] + fn dial_completed_to_reviewed() { assert!(validate_dialysis_status_transition("completed", "reviewed").is_ok()); } + #[test] + fn dial_completed_to_draft_fails() { assert!(validate_dialysis_status_transition("completed", "draft").is_err()); } + #[test] + fn dial_reviewed_to_any_fails() { assert!(validate_dialysis_status_transition("reviewed", "draft").is_err()); } + #[test] + fn dial_same_status_ok() { assert!(validate_dialysis_status_transition("draft", "draft").is_ok()); } + + // --- lab_report_status_transition --- + #[test] + fn lab_pending_to_reviewed() { assert!(validate_lab_report_status_transition("pending", "reviewed").is_ok()); } + #[test] + fn lab_pending_to_draft_fails() { assert!(validate_lab_report_status_transition("pending", "draft").is_err()); } + #[test] + fn lab_reviewed_to_any_fails() { assert!(validate_lab_report_status_transition("reviewed", "pending").is_err()); } + #[test] + fn lab_same_status_ok() { assert!(validate_lab_report_status_transition("pending", "pending").is_ok()); } + // --- follow_up_status_transition --- #[test] fn fu_pending_to_in_progress() { assert!(validate_follow_up_status_transition("pending", "in_progress").is_ok()); }