Files
tcpserver-flow/配置自定义节点.md
zimoyin 9ddec3dfb9 refactor(flow): 优化流程配置及相关测试
- 在测试代码中统一使用 setPreviousAction 与 setDuration 方法,规范方法调用
- VirtualContextBuilder 允许 setDuration 接收 null,增强灵活性
- VirtualityFlowProcessor 计算步骤持续时间时改进判断逻辑,避免错误持续时间值
- 增加日志记录上一步骤开始时间,方便调试流程状态
- 自定义流程配置文件大幅重构,新增多种晨洗模式配置
- 新增机洗单步骤流程、无晨洗流程、每日首次晨洗、存储超时晨洗等多种流程样例
- 自定义流程支持 override_steps 为 true/false 的不同合并策略说明与示例
- 详细补充配置节点说明,增强配置文档准确性和用户理解
- 自定义语音模板扩展,支持多种语音播报及错误提示文本
- 更新时间验证配置,统一各流程中各步骤时间要求
- 补充“配置自定义节点流程指南”文档,详细说明配置原则、合并策略与使用建议
2026-03-13 18:59:36 +08:00

443 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 配置自定义节点流程指南
## 概述
本项目支持通过配置文件自定义洗消流程,可以灵活控制每个节点的前置条件、启用状态等。
## 配置文件位置
- **文件路径**`./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 中记录清楚