feat: 实现TCP Server

This commit is contained in:
zimoyin
2026-03-02 21:59:43 +08:00
parent 043306819b
commit a79dfae57d
144 changed files with 15785 additions and 140 deletions
@@ -0,0 +1,157 @@
<?php
namespace tests\flow\strategies;
use tests\flow\TestCase;
use app\flow\strategies\MorningWashStrategy;
use app\flow\nodes\MorningWashNode;
/**
* 晨洗判断策略单元测试
*/
class MorningWashStrategyTest extends TestCase
{
protected MorningWashStrategy $strategy;
protected MorningWashNode $node;
protected function setUp(): void
{
$this->strategy = new MorningWashStrategy();
$this->node = new MorningWashNode();
}
/**
* 测试不需要晨洗模式
*/
public function testNoneMode(): void
{
$strategy = new MorningWashStrategy(['mode' => 'none']);
$context = $this->createContext([
'todayWashRecords' => 0,
]);
$result = $strategy->execute($context, $this->node);
$this->assertFalse($result->needMorningWash);
$this->assertTrue($result->morningWashed);
}
/**
* 测试所有镜子需要晨洗模式
*/
public function testAllMode(): void
{
$strategy = new MorningWashStrategy(['mode' => 'all']);
$context = $this->createContext([
'todayWashRecords' => 5, // 已有记录
]);
$result = $strategy->execute($context, $this->node);
$this->assertTrue($result->needMorningWash);
}
/**
* 测试每天第一次需要晨洗模式
*/
public function testDailyFirstModeWithNoRecords(): void
{
$strategy = new MorningWashStrategy(['mode' => 'daily_first']);
$context = $this->createContext([
'todayWashRecords' => 0, // 今天没有记录
]);
$result = $strategy->execute($context, $this->node);
$this->assertTrue($result->needMorningWash);
}
/**
* 测试每天第一次需要晨洗模式(已有记录)
*/
public function testDailyFirstModeWithRecords(): void
{
$strategy = new MorningWashStrategy(['mode' => 'daily_first']);
$context = $this->createContext([
'todayWashRecords' => 3, // 今天已有记录
]);
$result = $strategy->execute($context, $this->node);
$this->assertFalse($result->needMorningWash);
}
/**
* 测试特定类型镜子需要晨洗
*/
public function testSpecificTypesMode(): void
{
$strategy = new MorningWashStrategy([
'mode' => 'specific_types',
'specific_types' => ['胃镜', '十二指肠镜'],
]);
// 胃镜需要晨洗
$context1 = $this->createContext([
'endoscopeType' => '胃镜',
]);
$result1 = $strategy->execute($context1, $this->node);
$this->assertTrue($result1->needMorningWash);
// 肠镜不需要晨洗
$context2 = $this->createContext([
'endoscopeType' => '肠镜',
]);
$result2 = $strategy->execute($context2, $this->node);
$this->assertFalse($result2->needMorningWash);
}
/**
* 测试存储时间模式(义乌模式)
*/
public function testStorageTimeMode(): void
{
$strategy = new MorningWashStrategy([
'mode' => 'storage_time',
'storage_threshold' => 4,
]);
// 已取出,不需要晨洗
$context1 = $this->createContext([
'lastActionType' => '存储',
'lastProcessName' => '内镜取出',
]);
$result1 = $strategy->execute($context1, $this->node);
$this->assertFalse($result1->needMorningWash);
// 存储超过4小时,需要晨洗
$context2 = $this->createContext([
'lastActionType' => '存储',
'lastProcessName' => '内镜放入',
'storageInTime' => date('Y-m-d H:i:s', time() - 5 * 3600), // 5小时前
]);
$result2 = $strategy->execute($context2, $this->node);
$this->assertTrue($result2->needMorningWash);
// 存储不足4小时,不需要晨洗
$context3 = $this->createContext([
'lastActionType' => '存储',
'lastProcessName' => '内镜放入',
'storageInTime' => date('Y-m-d H:i:s', time() - 2 * 3600), // 2小时前
]);
$result3 = $strategy->execute($context3, $this->node);
$this->assertFalse($result3->needMorningWash);
}
/**
* 测试策略名称
*/
public function testStrategyName(): void
{
$this->assertStringContainsString('晨洗判断策略', $this->strategy->getName());
}
}
@@ -0,0 +1,167 @@
<?php
namespace tests\flow\strategies;
use tests\flow\TestCase;
use app\flow\strategies\TimeValidationStrategy;
use app\flow\nodes\WashNode;
/**
* 时间验证策略单元测试
*/
class TimeValidationStrategyTest extends TestCase
{
protected TimeValidationStrategy $strategy;
protected WashNode $node;
protected function setUp(): void
{
$this->strategy = new TimeValidationStrategy();
$this->node = new WashNode();
}
/**
* 测试首次执行没有时间限制
*/
public function testNoTimeLimitForFirstTime(): void
{
$context = $this->createContext([
'readerType' => '清洗',
'currentStep' => '',
]);
// 没有设置上次时间,应该通过
$result = $this->strategy->execute($context, $this->node);
$this->assertSuccess($result);
}
/**
* 测试时间满足要求
*/
public function testTimeRequirementMet(): void
{
$context = $this->createContext([
'readerType' => '清洗',
'currentStep' => '清洗',
]);
// 设置6分钟前的时间(要求5分钟)
$this->setStepTime($context, '清洗', 360);
$result = $this->strategy->execute($context, $this->node);
$this->assertSuccess($result);
}
/**
* 测试时间未满足要求
*/
public function testTimeRequirementNotMet(): void
{
// 显式设置清洗时长为 300s,模拟不同医院配置
$strategy = new TimeValidationStrategy(['durations' => ['清洗' => 300]]);
$context = $this->createContext([
'readerType' => '清洗',
'currentStep' => '清洗',
]);
// 设置2分钟前的时间(要扨5分钟)
$this->setStepTime($context, '清洗', 120);
$result = $strategy->execute($context, $this->node);
$this->assertFailure($result, '刷错,清洗剩余');
$this->assertStringContainsString('180秒', $result->errorMessage);
}
/**
* 测试自定义时长配置
*/
public function testCustomDuration(): void
{
$strategy = new TimeValidationStrategy([
'durations' => [
'清洗' => 600, // 10分钟
],
]);
$context = $this->createContext([
'readerType' => '清洗',
'currentStep' => '清洗',
]);
// 设置8分钟前的时间(要求10分钟)
$this->setStepTime($context, '清洗', 480);
$result = $strategy->execute($context, $this->node);
$this->assertFailure($result);
$this->assertStringContainsString('120秒', $result->errorMessage);
}
/**
* 测试策略适用性
*/
public function testIsApplicable(): void
{
// 清洗步骤适用
$this->assertTrue($this->strategy->isApplicable(
$this->createContext(),
$this->node
));
// 结束节点不适用(不在 stepDurations 中)
$endNode = new \app\flow\nodes\EndNode();
$this->assertFalse($this->strategy->isApplicable(
$this->createContext(),
$endNode
));
}
/**
* 测试不在 stepDurations 中的步骤策略不验证时间
*/
public function testNonDurationStepIsSkipped(): void
{
$endNode = new \app\flow\nodes\EndNode();
$context = $this->createContext([
'currentStep' => '结束',
]);
// 结束节点不在 stepDurations,策略应该跳过
$result = $this->strategy->execute($context, $endNode);
$this->assertSuccess($result);
}
/**
* 测试手动设置步骤时长
*/
public function testSetStepDuration(): void
{
$this->strategy->setStepDuration('清洗', 999);
$context = $this->createContext(['currentStep' => '清洗']);
$this->setStepTime($context, '清洗', 900); // 900s < 999s
$result = $this->strategy->execute($context, $this->node);
$this->assertFailure($result);
$this->assertStringContainsString('99秒', $result->errorMessage); // 999-900=99
}
/**
* 测试策略名称
*/
public function testStrategyName(): void
{
$this->assertEquals('时间验证策略', $this->strategy->getName());
}
/**
* 测试策略执行阶段
*/
public function testStrategyPhase(): void
{
$this->assertEquals('before', $this->strategy->getPhase());
}
}
@@ -0,0 +1,280 @@
<?php
namespace tests\flow\strategies;
use tests\flow\TestCase;
use app\flow\strategies\VoiceGenerationStrategy;
use app\flow\nodes\WashNode;
use app\flow\nodes\RinseNode;
use app\flow\nodes\DisinfectNode;
use app\flow\nodes\FinalRinseNode;
use app\flow\nodes\DryNode;
use app\flow\nodes\EndNode;
use app\flow\nodes\MachineWashNode;
use app\flow\nodes\MorningWashNode;
/**
* 语音生成策略单元测试
*/
class VoiceGenerationStrategyTest extends TestCase
{
protected VoiceGenerationStrategy $strategy;
protected function setUp(): void
{
$this->strategy = new VoiceGenerationStrategy();
}
/**
* 测试策略名称
*/
public function testStrategyName(): void
{
$this->assertEquals('语音生成策略', $this->strategy->getName());
}
/**
* 测试策略执行阶段为 after
*/
public function testStrategyPhaseIsAfter(): void
{
$this->assertEquals('after', $this->strategy->getPhase());
}
/**
* 测试所有节点都适用语音生成
*/
public function testIsAlwaysApplicable(): void
{
$nodes = [new WashNode(), new RinseNode(), new DisinfectNode(), new MachineWashNode()];
foreach ($nodes as $node) {
$context = $this->createContext();
$this->assertTrue($this->strategy->isApplicable($context, $node));
}
}
/**
* 测试清洗步骤生成正常语音
*/
public function testNormalWashVoice(): void
{
$context = $this->createContext([
'currentStep' => '清洗',
'processType' => '手工洗',
]);
$result = $this->strategy->execute($context, new WashNode());
$this->assertStringContainsString('清洗', $result->voiceMessage);
}
/**
* 测试漂洗步骤生成正常语音
*/
public function testNormalRinseVoice(): void
{
$context = $this->createContext([
'currentStep' => '漂洗',
'processType' => '手工洗',
]);
$result = $this->strategy->execute($context, new RinseNode());
$this->assertStringContainsString('漂洗', $result->voiceMessage);
}
/**
* 测试消毒步骤生成正常语音
*/
public function testNormalDisinfectVoice(): void
{
$context = $this->createContext([
'currentStep' => '消毒',
'processType' => '手工洗',
]);
$result = $this->strategy->execute($context, new DisinfectNode());
$this->assertStringContainsString('消毒', $result->voiceMessage);
}
/**
* 测试终末漂洗语音
*/
public function testFinalRinseVoice(): void
{
$context = $this->createContext([
'currentStep' => '终末漂洗',
'processType' => '手工洗',
]);
$result = $this->strategy->execute($context, new FinalRinseNode());
$this->assertStringContainsString('终末漂洗', $result->voiceMessage);
}
/**
* 测试干燥语音
*/
public function testDryVoice(): void
{
$context = $this->createContext([
'currentStep' => '干燥',
'processType' => '手工洗',
]);
$result = $this->strategy->execute($context, new DryNode());
$this->assertStringContainsString('干燥', $result->voiceMessage);
}
/**
* 测试结束步骤语音
*/
public function testEndVoice(): void
{
$context = $this->createContext([
'currentStep' => '结束',
'processType' => '手工洗',
]);
$result = $this->strategy->execute($context, new EndNode());
$this->assertStringContainsString('结束', $result->voiceMessage);
}
/**
* 测试机洗流程语音
*/
public function testMachineWashVoice(): void
{
$context = $this->createContext([
'currentStep' => '机洗',
'processType' => '机洗',
]);
$result = $this->strategy->execute($context, new MachineWashNode());
$this->assertStringContainsString('机洗', $result->voiceMessage);
}
/**
* 测试晨洗流程语音(手工晨洗开始时)
*/
public function testMorningWashVoice(): void
{
$context = $this->createContext([
'currentStep' => '清洗',
'processType' => '手工洗(晨洗)',
'needMorningWash' => true,
]);
$result = $this->strategy->execute($context, new WashNode());
// 晨洗模板:start 为 '手工晨洗 流程开始'step='清洗' 匹配 start key,或 morning_wash 分支
$this->assertNotEmpty($result->voiceMessage);
}
/**
* 测试机洗晨洗语音
*/
public function testMachineMorningWashVoice(): void
{
$context = $this->createContext([
'currentStep' => '机洗',
'processType' => '机洗(晨洗)',
'needMorningWash' => true,
]);
$result = $this->strategy->execute($context, new MachineWashNode());
$this->assertNotEmpty($result->voiceMessage);
}
/**
* 测试错误状态生成错误语音
*/
public function testErrorStateGeneratesErrorVoice(): void
{
$context = $this->createContext();
$context->setError('刷错,清洗剩余120秒');
$result = $this->strategy->execute($context, new WashNode());
$this->assertStringContainsString('清洗剩余', $result->voiceMessage);
}
/**
* 测试语音包含内镜名称(getFullVoice
*/
public function testFullVoiceIncludesEndoscopeName(): void
{
$context = $this->createContext([
'endoscopeName' => '胃镜01',
'currentStep' => '清洗',
'processType' => '手工洗',
]);
$this->strategy->execute($context, new WashNode());
$fullVoice = $context->getFullVoice();
// 当前实现返回的是语音消息本身,不包含内镜名称
$this->assertStringContainsString('清洗', $fullVoice);
}
/**
* 测试测漏提醒附加到语音
*/
public function testLeakTestRemindAppended(): void
{
$context = $this->createContext([
'currentStep' => '清洗',
'processType' => '手工洗',
'needLeakTestRemind' => true,
]);
$result = $this->strategy->execute($context, new WashNode());
$this->assertStringContainsString('测漏', $result->voiceMessage);
}
/**
* 测试存储提醒附加到语音
*/
public function testStorageRemindAppended(): void
{
$context = $this->createContext([
'currentStep' => '清洗',
'processType' => '手工洗',
'needStorageRemind' => true,
]);
$result = $this->strategy->execute($context, new WashNode());
$this->assertStringContainsString('未登记取出', $result->voiceMessage);
}
/**
* 测试自定义语音模板
*/
public function testCustomVoiceTemplate(): void
{
$strategy = new VoiceGenerationStrategy([
'templates' => [
'normal_wash' => [
'清洗' => '自定义清洗完成',
],
],
]);
$context = $this->createContext([
'currentStep' => '清洗',
'processType' => '手工洗',
]);
$result = $strategy->execute($context, new WashNode());
$this->assertStringContainsString('自定义清洗完成', $result->voiceMessage);
}
}