feat: 实现TCP Server
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
# 内镜清洗流程管理系统 - 设计思路
|
||||
|
||||
## 一、项目背景
|
||||
|
||||
医院内镜清洗流程复杂多变,不同医院有不同的业务需求:
|
||||
- 有的医院需要晨洗,有的不需要
|
||||
- 有的医院机洗后可以直接结束,有的必须继续手工流程
|
||||
- 有的医院对步骤时间有特殊要求
|
||||
- 流程步骤可能随时调整
|
||||
|
||||
## 二、核心问题
|
||||
|
||||
### 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);
|
||||
```
|
||||
Reference in New Issue
Block a user