Files
tcpserver-flow/app/flow/docs/01-设计思路.md
T
2026-03-08 22:58:56 +08:00

308 lines
9.5 KiB
Markdown
Raw 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.
# 内镜清洗流程管理系统 - 设计思路
## 一、项目背景
医院内镜清洗流程复杂多变,不同医院有不同的业务需求:
- 有的医院需要晨洗,有的不需要
- 有的医院机洗后可以直接结束,有的必须继续手工流程
- 有的医院对步骤时间有特殊要求
- 流程步骤可能随时调整
## 二、核心问题
### 2.1 旧系统的问题
1. **硬编码流程**:流程步骤写死在代码中,修改困难
2. **状态黑盒**:使用数组存储状态,不知道在哪里被修改
3. **无法扩展**:新增流程类型需要修改大量代码
4. **分布式问题**:多台机器部署时,批次号无法保持一致
### 2.2 需要支持的灵活场景
1. 医院A:不需要晨洗
2. 医院B:只有部分镜子需要晨洗
3. 医院C:机洗后不允许刷终末漂洗,只能刷干燥或结束
4. 医院D:只需要清洗和结束两个步骤
5. 医院E:自定义每个步骤的语音播报
## 三、设计方案
### 3.1 架构模式
#### 责任链模式 (Chain of Responsibility)
```
Request → Node1 → Node2 → Node3 → ... → Response
↓ ↓ ↓
处理或传递 处理或传递 处理或传递
```
**优点**
- 每个节点只关心自己能否处理
- 节点可以动态添加/删除/禁用
- 流程顺序可配置
#### 策略模式 (Strategy)
```
Context → StrategyA
→ StrategyB
→ StrategyC
```
**优点**
- 晨洗判断逻辑可替换
- 时间验证规则可配置
- 语音生成模板可定制
### 3.2 核心组件
#### ProcessContext(流程上下文)
```php
class ProcessContext
{
// 明确字段,避免黑盒
public string $endoscopeId = '';
public string $currentStep = '';
public string $batchNo = '';
public array $stepLastTimes = [];
// ...
}
```
**设计原则**
- 所有状态都是明确的属性
- 不使用 `getExtra/setExtra` 黑盒方法
- 支持从 PacketContext 初始化
#### ProcessNode(流程节点)
```php
interface ProcessNodeInterface
{
public function canHandle(ProcessContext $context): bool;
public function handle(ProcessContext $context): ProcessContext;
public function setNext(ProcessNodeInterface $next): ProcessNodeInterface;
}
```
**节点列表**
- MorningWashNode:晨洗节点
- WashNode:清洗节点
- RinseNode:漂洗节点
- DisinfectNode:消毒节点
- FinalRinseNode:终末漂洗节点
- DryNode:干燥节点
- EndNode:结束节点
- MachineWashNode:机洗节点
#### ProcessStrategy(流程策略)
```php
interface ProcessStrategyInterface
{
public function execute(ProcessContext $context, ProcessNodeInterface $node): ProcessContext;
}
```
**策略列表**
- MorningWashStrategy:晨洗判断(5种模式)
- TimeValidationStrategy:时间验证
- VoiceGenerationStrategy:语音生成
### 3.3 分布式批次号一致性
**问题**:机器A处理清洗,机器B处理消毒,如何保证批次号相同?
**解决方案**
```php
public function getOrCreateBatchNo(bool $forceNew = false): string
{
// 1. 首先查询数据库获取该内镜未完成的批次号
$existingBatchNo = Database::query(
"SELECT op_batchno FROM ect_actions
WHERE endoscope_id = ?
AND process_name != '结束'
AND created_at >= CURDATE()
ORDER BY action_id DESC LIMIT 1"
);
// 2. 如果存在,使用已有的;如果不存在,生成新的
if ($existingBatchNo) {
return $existingBatchNo;
}
return $this->generateBatchNo();
}
```
**批次号格式**`YYYYMMDD + HHMMSS + 内镜ID(6位) + 随机数(4位)`
- 示例:`202603031230450000011234`
- 长度固定:24位
- 包含时间戳,便于排序
## 四、流程执行流程
### 4.1 整体流程图
```
┌─────────────────┐
│ 接收刷卡数据 │
│ PacketContext │
└────────┬────────┘
┌─────────────────┐
│ 查询数据库 │
│ - 内镜信息 │
│ - 历史记录 │
│ - 当前批次号 │
└────────┬────────┘
┌─────────────────┐
│ 创建ProcessContext│
└────────┬────────┘
┌─────────────────┐
│ 执行责任链 │
│ - 晨洗判断 │
│ - 时间验证 │
│ - 节点处理 │
│ - 语音生成 │
└────────┬────────┘
┌─────────────────┐
│ 处理结果 │
│ - 插入数据库 │
│ - 语音播报 │
│ - WebSocket通知 │
└─────────────────┘
```
### 4.2 策略执行详解
责任链中的每个节点都可以配置多个策略,策略在节点处理前后执行:
```
┌─────────────────────────────────────────┐
│ 节点处理流程 │
├─────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 前置策略执行 │ → │ MorningWash │ │
│ │ │ │ Strategy │ │
│ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 前置策略执行 │ → │ TimeValidat │ │
│ │ │ │ ionStrategy │ │
│ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ 节点核心逻辑处理 │ │
│ │ (canHandle + handle) │ │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 后置策略执行 │ → │ VoiceGenerat│ │
│ │ │ │ ionStrategy │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────┘
```
**策略执行顺序**
1. **前置策略**(按配置顺序执行)
- `MorningWashStrategy`:判断是否需要晨洗
- `TimeValidationStrategy`:验证步骤时间间隔
2. **节点核心逻辑**
- 调用 `canHandle()` 判断当前节点是否能处理
- 调用 `handle()` 执行业务逻辑
3. **后置策略**
- `VoiceGenerationStrategy`:生成语音播报内容
**代码示例**
```php
// 在 AbstractProcessNode 中执行策略
final public function process(ProcessContext $context): ProcessContext
{
// 1. 执行前置策略
foreach ($this->preStrategies as $strategy) {
$context = $strategy->execute($context, $this);
}
// 2. 检查是否能处理
if (!$this->canHandle($context)) {
// 传递给下一个节点
if ($this->next !== null) {
return $this->next->process($context);
}
return $context;
}
// 3. 执行节点核心逻辑
$context = $this->handle($context);
// 4. 执行后置策略
foreach ($this->postStrategies as $strategy) {
$context = $strategy->execute($context, $this);
}
return $context;
}
```
**策略配置示例**
```php
// 在 ProcessConfig 中配置策略
'nodes' => [
'wash' => [
'class' => WashNode::class,
'pre_strategies' => [
MorningWashStrategy::class, // 晨洗判断
TimeValidationStrategy::class, // 时间验证
],
'post_strategies' => [
VoiceGenerationStrategy::class, // 语音生成
],
],
],
```
## 五、配置化设计
### 5.1 流程配置
```php
$config = [
'steps' => [
['code' => '清洗', 'class' => 'WashNode', 'enabled' => true],
['code' => '消毒', 'class' => 'DisinfectNode', 'enabled' => true],
['code' => '结束', 'class' => 'EndNode', 'enabled' => true],
],
'morning_wash' => [
'mode' => 'none', // 不需要晨洗
],
];
```
### 5.2 晨洗模式
- `none`:不需要晨洗
- `all`:所有镜子都需要
- `storage_time`:根据存储时间判断(义乌模式)
- `daily_first`:每天第一次(忠县模式)
- `specific_types`:特定类型镜子需要
### 5.3 医院特殊配置
```php
// FinalRinseNode:机洗后不允许刷终末漂洗
$node->setAllowAfterMachineWash(false);
// DryNode:机洗后必须直接刷干燥
$node->setRequireDirectAfterMachineWash(true);
```