diff --git a/crates/df-nodes/src/human_node.rs b/crates/df-nodes/src/human_node.rs index bf44cc9..07d03df 100644 --- a/crates/df-nodes/src/human_node.rs +++ b/crates/df-nodes/src/human_node.rs @@ -1,4 +1,7 @@ //! 人工审批节点 — 阻塞工作流,等待人工确认或输入 +//! +//! 拒绝语义化关键字判定等纯逻辑见 `human_node_helpers`(本文件只保留 +//! HumanNode 的 struct + impl,impl 块约束)。 use async_trait::async_trait; use std::time::Duration; @@ -6,31 +9,9 @@ use tokio::sync::broadcast; use df_workflow::node::{Node, NodeContext, NodeOutput, NodeResult, NodeSchema}; use df_types::events::{SelectType, WorkflowEvent}; -/// F-260616-06 阶段2: 拒绝语义化关键字。 -/// decision 归一化(去空白 + 小写)后命中此集合 → 审批拒绝 → 节点返 Err(触发工作流 failed)。 -/// -/// 识别范围(避免误伤): -/// - 仅当节点 options 非空且该 decision ∈ options 时拒绝 —— 前端从受控选项中选择,语义可信; -/// - options 空(自由文本)时同样按关键字判定 —— 仅当文本明确为拒绝词才拒绝, -/// 其余自由文本一律视为通过(向后兼容,不阻断自由反馈)。 -/// -/// 关键字集合与前端/IPC 的 options 命名约定对齐:「拒绝/驳回/退回」中文 + -/// 「reject/decline/deny/no/block」英文,覆盖默认 options `["同意","拒绝"]`。 -const REJECT_KEYWORDS: &[&str] = &[ - "拒绝", "驳回", "退回", "否决", - "reject", "decline", "declined", "deny", "denied", "no", "block", -]; - -/// 判定单个 decision 是否为拒绝选项(归一化后命中 REJECT_KEYWORDS)。 -fn is_reject_decision(d: &str) -> bool { - let normalized = d.trim().to_lowercase(); - REJECT_KEYWORDS.iter().any(|k| normalized == *k) -} - -/// 判定 picked 集合是否含拒绝选项:任一项命中即视为整单拒绝(多选场景「选了驳回」即驳回)。 -fn contains_reject(picked: &[String]) -> bool { - picked.iter().any(|d| is_reject_decision(d)) -} +// 抽离的纯函数/常量(glob 引入,tests `use super::*` 间接可见) +#[allow(unused_imports)] +use crate::human_node_helpers::{contains_reject, is_reject_decision, REJECT_KEYWORDS}; /// 人工审批节点(阻塞节点) pub struct HumanNode; diff --git a/crates/df-nodes/src/human_node_helpers.rs b/crates/df-nodes/src/human_node_helpers.rs new file mode 100644 index 0000000..23f341e --- /dev/null +++ b/crates/df-nodes/src/human_node_helpers.rs @@ -0,0 +1,32 @@ +//! 人工审批节点辅助函数/类型 — 拒绝语义化关键字判定等纯逻辑 +//! +//! 从 human_node.rs 抽离的纯函数/常量(execute 与关键字判定解耦,便于单测覆盖)。 +//! HumanNode 的 struct + impl 仍保留在 human_node.rs(impl 块约束)。 +//! +//! B-260615-05 / CR-260618-15: 拒绝语义化保留(executor set_failed → 推进链退回)。 + +/// F-260616-06 阶段2: 拒绝语义化关键字。 +/// decision 归一化(去空白 + 小写)后命中此集合 → 审批拒绝 → 节点返 Err(触发工作流 failed)。 +/// +/// 识别范围(避免误伤): +/// - 仅当节点 options 非空且该 decision ∈ options 时拒绝 —— 前端从受控选项中选择,语义可信; +/// - options 空(自由文本)时同样按关键字判定 —— 仅当文本明确为拒绝词才拒绝, +/// 其余自由文本一律视为通过(向后兼容,不阻断自由反馈)。 +/// +/// 关键字集合与前端/IPC 的 options 命名约定对齐:「拒绝/驳回/退回」中文 + +/// 「reject/decline/deny/no/block」英文,覆盖默认 options `["同意","拒绝"]`。 +pub(super) const REJECT_KEYWORDS: &[&str] = &[ + "拒绝", "驳回", "退回", "否决", + "reject", "decline", "declined", "deny", "denied", "no", "block", +]; + +/// 判定单个 decision 是否为拒绝选项(归一化后命中 REJECT_KEYWORDS)。 +pub(super) fn is_reject_decision(d: &str) -> bool { + let normalized = d.trim().to_lowercase(); + REJECT_KEYWORDS.iter().any(|k| normalized == *k) +} + +/// 判定 picked 集合是否含拒绝选项:任一项命中即视为整单拒绝(多选场景「选了驳回」即驳回)。 +pub(super) fn contains_reject(picked: &[String]) -> bool { + picked.iter().any(|d| is_reject_decision(d)) +} diff --git a/crates/df-nodes/src/lib.rs b/crates/df-nodes/src/lib.rs index eef29b1..f4bb638 100644 --- a/crates/df-nodes/src/lib.rs +++ b/crates/df-nodes/src/lib.rs @@ -4,6 +4,7 @@ pub mod ai_node; #[allow(dead_code)] mod ai_node_helpers; pub mod human_node; +mod human_node_helpers; pub mod script_node; pub mod task_advance_node; pub mod task_state_machine;