308 lines
9.5 KiB
Markdown
308 lines
9.5 KiB
Markdown
# 内镜清洗流程管理系统 - 设计思路
|
||
|
||
## 一、项目背景
|
||
|
||
医院内镜清洗流程复杂多变,不同医院有不同的业务需求:
|
||
- 有的医院需要晨洗,有的不需要
|
||
- 有的医院机洗后可以直接结束,有的必须继续手工流程
|
||
- 有的医院对步骤时间有特殊要求
|
||
- 流程步骤可能随时调整
|
||
|
||
## 二、核心问题
|
||
|
||
### 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);
|
||
```
|