# 配置自定义节点流程指南 ## 概述 本项目支持通过配置文件自定义洗消流程,可以灵活控制每个节点的前置条件、启用状态等。 ## 配置文件位置 - **文件路径**:`./custom_process_config.php` - **配置键选择**:通过环境变量 `FLOW_PROCESS_CONFIG_KEY` 指定使用的配置 - **配置键**:通过环境变量 `FLOW_USE_CUSTOM_PROCESS` 来开启允许使用自定义配置 ### override_steps 参数详解 ⭐ **作用**:控制自定义步骤与默认步骤的合并策略。 #### 1. override_steps = true(完全覆盖模式) **行为**: - ✅ 完全使用配置文件中的 `steps` 列表 - ✅ **不会**创建配置中不存在的节点 - ✅ 如果配置为空数组,则使用默认步骤列表 - ✅ 适合:定义全新的流程、大幅简化流程 **示例**:机洗单步骤流程 ```php 'machine_wash' => [ 'override_steps' => true, // 完全覆盖 'steps' => [ // 只定义需要的节点,其他节点不会创建 ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], ], ] // 结果:流程中只有这 4 个节点,清洗/漂洗/消毒等节点不会被创建 ``` **注意事项**: - ⚠️ 必须显式列出所有需要的节点 - ⚠️ 漏掉的节点将**不存在**于流程中(不是禁用,是不存在) - ⚠️ 如果某个节点没配置但后续逻辑需要,会导致错误 #### 2. override_steps = false(增量合并模式) **行为**: - ✅ 使用默认步骤列表作为基础 - ✅ 用自定义步骤**覆盖**相同 code 的默认步骤 - ✅ 自定义步骤中不存在的节点**保持默认配置** - ✅ 适合:微调特定节点的属性(如 enabled、required) **示例**:仅调整晨洗节点 ```php 'custom_morning' => [ 'override_steps' => false, // 增量合并 'steps' => [ // 只配置晨洗节点,其他节点使用默认值 ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true, 'required' => []], ], ] // 结果: // - 晨洗节点:使用自定义配置(enabled=true, required=[]) // - 清洗/漂洗/消毒等其他节点:使用默认配置 ``` **合并逻辑**(伪代码): ```php // 1. 将默认步骤转为「code => StepConfig」的关联数组 $defaultStepsMap = []; foreach (self::defaultSteps() as $defaultStep) { $defaultStepsMap[$defaultStep->code] = $defaultStep; } // 2. 遍历自定义步骤,覆盖默认步骤中相同 code 的元素 foreach ($customSteps as $customStep) { $defaultStepsMap[$customStep->code] = $customStep; // 覆盖 } // 3. 转回索引数组 $this->steps = array_values($defaultStepsMap); ``` **注意事项**: - ⚠️ 只能修改已存在于默认列表中的节点 - ⚠️ 无法添加默认列表中不存在的新节点(会被忽略) - ⚠️ 适合小范围调整,不适合大规模改动 #### 3. 对比总结 | 特性 | override_steps = true | override_steps = false | |------|----------------------|------------------------| | 基础列表 | 自定义 steps | 默认 steps | | 未配置的节点 | 不存在 | 使用默认配置 | | 相同 code 的节点 | 以自定义为准 | 以自定义为准 | | 适用场景 | 全新流程、大幅简化 | 微调特定节点 | | 风险 | 可能遗漏必需节点 | 无法添加新节点 | #### 4. 使用建议 **使用 override_steps = true 的场景**: - ✅ 机洗单步骤流程(只需 4 个节点) - ✅ 无干燥流程(去掉干燥和终末漂洗) - ✅ 特殊医院定制的简化流程 **使用 override_steps = false 的场景**: - ✅ 仅调整晨洗规则 - ✅ 仅修改某个节点的 enabled 状态 - ✅ 仅添加 required 约束 - ✅ 临时测试某个节点的配置变更 **默认推荐**: - 📌 大多数情况使用 `true`,明确列出所有需要的节点 - 📌 只在确实只需要微调时使用 `false` --- ## 配置结构说明 ### 完整配置示例 ```php 'custom_flow' => [ 'name' => '自定义流程名称', 'description' => '流程描述', 'override_steps' => true, // 是否覆盖默认步骤 // 晨洗配置 'morning_wash' => [ 'mode' => 'none', // none | daily_first | all | storage_time | specific_types 'storage_threshold' => 4, // 存储时间阈值(小时)- storage_time 模式使用 'morning_start_time' => '00:00:00', // 每日开始时间 - daily_first 模式使用 'specific_types' => ['胃镜', '十二指肠镜'], // 特定类型 - specific_types 模式使用 ], // 步骤配置(核心) 'steps' => [ [ 'code' => '清洗', // 节点标识码(必须与 ProcessNode 的 getCode() 一致) 'class' => 'WashNode', // 节点类名(不含命名空间) 'enabled' => true, // 是否启用该节点 'required' => ['结束', '晨洗'], // 可选:指定前置节点列表 ], // ... 其他节点 ], // 时间验证配置(秒) 'time_validation' => [ 'durations' => [ '晨洗' => 60, // 晨洗至少 60 秒 '清洗' => 120, // 清洗至少 120 秒 '漂洗' => 60, // 漂洗至少 60 秒 '消毒' => 300, // 消毒至少 300 秒 '终末漂洗' => 120, // 终末漂洗至少 120 秒 '干燥' => 30, // 干燥至少 30 秒 '机洗' => 360, // 机洗至少 360 秒 ], 'enabled' => true, // 是否启用时间验证 ], // 语音模板配置 'voice_templates' => [ // 正常洗消流程语音 'normal_wash' => [ '晨洗' => '晨洗开始,请进行每日首次清洗准备', '清洗' => '第一步清洗开始,请认真清洗内镜外表面和管道', '漂洗' => '第二步漂洗开始,用流动水冲洗残留酶液', '消毒' => '第三步消毒开始,请确保消毒剂浓度和时间', '终末漂洗' => '第四步终末漂洗开始,用无菌水彻底冲洗', '干燥' => '最后一步干燥开始,用压力气枪吹干管道', '结束' => '清洗流程全部完成,请妥善保管内镜', '机洗' => '机洗开始,请将内镜放入清洗机', ], // 晨洗流程语音 'morning_wash' => [ '晨洗' => '晨洗模式启动,今日首次清洗需额外注意', '清洗' => '晨洗后的第一次清洗,请按标准流程操作', ], // 错误提示语音 'error_messages' => [ 'NOT_ENOUGH_TIME' => '{step}时间不足,还需等待{time}秒', 'PLEASE_SWIPE_OPERATOR' => '请先刷人员卡', 'PLEASE_SWIPE_ENDOSCOPE' => '请刷内镜卡', 'PLEASE_SWIPE_READER' => '请刷读卡器', ], // 重复刷卡提示 'duplicate_check' => [ 'operator' => '您已刷过人员卡,无需重复刷卡', 'endoscope' => '您已刷过内镜卡,无需重复刷卡', 'reader' => '您已刷过读卡器,无需重复刷卡', ], ], ], ``` ## 核心参数详解 ### 1. steps[].code(节点标识码) **作用**:唯一标识一个节点,必须与对应的 `ProcessNode` 子类的 `getCode()` 方法返回值一致。 **可用节点码**: - `晨洗` - MorningWashNode - `重复刷卡` - DuplicateCheckNode - `清洗` - WashNode - `漂洗` - RinseNode - `消毒` - DisinfectNode - `终末漂洗` - FinalRinseNode - `干燥` - DryNode - `结束` - EndNode - `机洗` - MachineWashNode - `虚拟清洗机` - VirtualWashMachineNode - `存储` - StorageNode - `内镜放入` - StorageInNode - `内镜取出` - StorageOutNode - `Close` - CloseNode ### 2. steps[].class(节点类名) **作用**:指定处理该节点的类名(不含命名空间)。 **命名空间**:所有节点类都在 `app\flow\nodes\` 命名空间下。 **示例**: ```php 'class' => 'WashNode' // 对应 \app\flow\nodes\WashNode ``` ### 3. steps[].enabled(启用状态) **作用**:控制节点是否启用。 **值**: - `true` - 启用该节点 - `false` - 禁用/跳过该节点 **示例**: ```php // 机洗单步骤流程中,跳手工洗节点 ['code' => '清洗', 'class' => 'WashNode', 'enabled' => false], ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ``` ### 4. steps[].required(前置节点要求)⭐ **作用**:指定当前节点的前一个节点可以是哪些。用于实现特殊流程控制。 **类型**:字符串数组(允许的前置节点 code 列表) **工作原理**: - 当流程执行到某个节点时,会检查上一个节点的 code 是否在 `required` 列表中 - 如果不在列表中,流程会拒绝执行并提示错误 - 如果不设置 `required`,则使用默认的配置 **使用场景**: #### 场景 1:强制流程顺序 ```php 'steps' => [ ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true, 'required' => ['结束', '晨洗']], ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true, 'required' => ['清洗']], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true, 'required' => ['漂洗']], ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true, 'required' => ['消毒']], ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true, 'required' => ['终末漂洗']], ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥']], ] ``` 这样确保必须按顺序执行:清洗 → 漂洗 → 消毒 → 终末漂洗 → 干燥 → 结束 #### 场景 2:机洗介入 ```php 'steps' => [ ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], // 机洗可以在手工洗的任意阶段介入 ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true, 'required' => ['清洗', '漂洗', '消毒']], ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true, 'required' => ['机洗']], ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true, 'required' => ['终末漂洗']], ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥', '机洗']], ] ``` #### 场景 3:多路径汇聚 ```php 'steps' => [ ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], // 结束可以从干燥或机洗过来 ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥', '机洗']], ] ``` **注意事项**: 1. ✅ `required` 中的节点码必须在 `steps` 中存在 2. ✅ 循环依赖可能会导致死锁(如 A 要求 B,B 要求 A) 3. ✅ 如果节点设置了 `required`,则只能从指定的前驱节点过来 ## 常用流程配置示例 ### 1. 标准完整流程(不设置 required) ```php 'standard' => [ 'name' => '标准完整流程', 'override_steps' => true, 'morning_wash' => ['mode' => 'daily_first'], 'steps' => [ ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => true], ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true], ], ] ``` ### 2. 机洗单步骤流程(设置 required) ```php 'machine_wash' => [ 'name' => '机洗单步骤流程', 'override_steps' => true, 'morning_wash' => ['mode' => 'none'], 'steps' => [ ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true, 'required' => ['结束']], ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true, 'required' => ['清洗']], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true, 'required' => ['漂洗']], ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true, 'required' => ['清洗', '漂洗', '消毒']], ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true, 'required' => ['机洗']], ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true, 'required' => ['终末漂洗']], ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥']], ], ] ``` ### 3. 无干燥流程 ```php 'no_dry' => [ 'name' => '无干燥流程', 'override_steps' => true, 'morning_wash' => ['mode' => 'none'], 'steps' => [ ['code' => '晨洗', 'class' => 'MorningWashNode', 'enabled' => false], ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => false], ['code' => '干燥', 'class' => 'DryNode', 'enabled' => false], ['code' => '结束', 'class' => 'EndNode', 'enabled' => true], ], ] ``` ## 调试技巧 ### 1. 查看当前使用的配置 ```bash # 在 .env 文件中查看 FLOW_PROCESS_CONFIG_KEY=standard ``` ### 2. 日志输出 流程引擎会在日志中输出加载的配置信息: ``` ProcessConfig 初始化中... 加载 [steps] 步骤配置,来源:自定义配置 ``` ### 3. 测试配置有效性 运行单元测试验证配置: ```bash phpunit tests/flow/node_flow/ ``` ## 常见问题 ### Q1: 配置后流程不生效? **检查清单**: 1. ✅ 确认 `.env` 中的 `FLOW_PROCESS_CONFIG_KEY` 正确 2. ✅ 确认 `steps[].code` 与节点类的 `getCode()` 一致 3. ✅ 确认 `steps[].class` 类名存在且正确 4. ✅ 清除缓存(如果有) ### Q2: required 设置后流程卡住? **可能原因**: - 循环依赖:A 要求 B,B 要求 A - 前置节点不存在于 steps 中 - 前置节点被禁用(enabled=false) **解决方法**: 1. 检查 `required` 数组中的节点码是否拼写正确 2. 确保前置节点在同一个配置的 `steps` 中且启用 3. 暂时移除 `required` 测试基础流程 ### Q3: 如何同时支持手工洗和机洗? **方案**:在 `steps` 中同时启用两种节点,通过 `required` 控制介入时机: ```php 'steps' => [ ['code' => '清洗', 'class' => 'WashNode', 'enabled' => true], ['code' => '漂洗', 'class' => 'RinseNode', 'enabled' => true], // 机洗可以在任意阶段介入 ['code' => '机洗', 'class' => 'MachineWashNode', 'enabled' => true, 'required' => ['清洗', '漂洗', '消毒', '终末漂洗']], ['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true], ['code' => '终末漂洗', 'class' => 'FinalRinseNode', 'enabled' => true], // 结束可以从干燥或机洗过来 ['code' => '结束', 'class' => 'EndNode', 'enabled' => true, 'required' => ['干燥', '机洗']], ['code' => '干燥', 'class' => 'DryNode', 'enabled' => true], ] ``` ## 相关文件 - **配置文件**:`app/config/custom_process_config.php` - **配置加载类**:`app/flow/config/ProcessConfig.php` - **步骤配置类**:`app/flow/config/StepConfig.php` - **步骤集合类**:`app/flow/config/StepsConfig.php` - **流程引擎**:`app/flow/ProcessEngine.php` - **节点基类**:`app/flow/nodes/ProcessNodeInterface.php` - **抽象节点**:`app/flow/nodes/AbstractProcessNode.php` ## 开发建议 1. ✅ **标准流程保持简单**:`standard` 配置不设置 `required`,保持灵活性 2. ✅ **特殊流程才用 required**:只在需要严格控制顺序时使用 3. ✅ **充分测试**:修改配置后运行完整的流程测试 4. ✅ **注释清晰**:为每个自定义配置添加详细的用途说明 5. ✅ **版本管理**:配置变更要在 Git 中记录清楚