refactor(context): 重命名上下文构建器及更新相关方法命名

- 将 ProcessContext 中的 builder() 重命名为 createModifyBuilder() 并添加调用栈日志
- ProcessContextBuilder 中所有 with* 方法统一改为 set* 命名风格
- Flow 各节点及流程处理器中调用 builder() 替换为 createModifyBuilder()
- 虚拟测试环境相关 ContextBuilder 中方法同步改名保持一致
- 优化流程节点中上下文修改代码调用,提升代码规范性
- 添加Config中加载自定义流程配置的占位注释及代码
- 无功能改动,纯代码风格及命名规范调整
This commit is contained in:
zimoyin
2026-03-13 18:51:02 +08:00
parent 8e617d06bc
commit 177c3ae9b2
27 changed files with 176 additions and 163 deletions
File diff suppressed because one or more lines are too long
+2
View File
@@ -116,6 +116,8 @@ class Config
{ {
$this->detectCircularDependency(); $this->detectCircularDependency();
$this->database = new DatabaseConfig(); $this->database = new DatabaseConfig();
// TODO 加载自定义流程配置
// 加载根目录的
$this->customProcess = require __DIR__ . '/custom_process_config.php'; $this->customProcess = require __DIR__ . '/custom_process_config.php';
$this->machineId = self::getStringEnv("MACHINE_ID", "0"); $this->machineId = self::getStringEnv("MACHINE_ID", "0");
if (empty($this->machineId) || $this->machineId == '0') { if (empty($this->machineId) || $this->machineId == '0') {
+3 -1
View File
@@ -8,4 +8,6 @@
动态提示请刷XXX 动态提示请刷XXX
配置文件 required 无法生效 配置文件 required 无法生效
干燥
+1 -2
View File
@@ -64,8 +64,7 @@ class FlowMain
$processConfig = null; $processConfig = null;
if ($useCustomProcess && !empty($processConfigKey)) { if ($useCustomProcess && !empty($processConfigKey)) {
// 从全局配置加载自定义流程 // 从全局配置加载自定义流程
$globalConfig = Config::getInstance(); $customProcess = Config::getInstance()->customProcess;
$customProcess = $globalConfig->customProcess;
$processConfigArray = $customProcess[$processConfigKey] ?? null; $processConfigArray = $customProcess[$processConfigKey] ?? null;
if ($processConfigArray !== null) { if ($processConfigArray !== null) {
+5 -5
View File
@@ -62,7 +62,7 @@ class FlowProcessor
// 读卡器未绑定 // 读卡器未绑定
if (empty($context->getReader()->id)) { if (empty($context->getReader()->id)) {
Logger::error('读卡器未绑定,放弃处理 card={}', [$context->isOperatorCard() ? $context->getOperator()->rfid : $context->getEndoscope()->cardNo]); Logger::error('读卡器未绑定,放弃处理 card={}', [$context->isOperatorCard() ? $context->getOperator()->rfid : $context->getEndoscope()->cardNo]);
$context = $context->builder()->error(VoiceMessage::READER_NOT_BOUND)->build(); $context = $context->createModifyBuilder()->error(VoiceMessage::READER_NOT_BOUND)->build();
$this->sendVoice($context); $this->sendVoice($context);
return $context; return $context;
} }
@@ -76,7 +76,7 @@ class FlowProcessor
$context->getOperator()->name, $context->getOperator()->name,
$context->getOperator()->rfid $context->getOperator()->rfid
); );
$context = $context->builder()->error(VoiceMessage::PLEASE_SWIPE_ENDOSCOPE)->build(); $context = $context->createModifyBuilder()->error(VoiceMessage::PLEASE_SWIPE_ENDOSCOPE)->build();
$this->sendVoice($context); $this->sendVoice($context);
return $context; return $context;
} }
@@ -84,7 +84,7 @@ class FlowProcessor
// 如果内镜未绑定,返回错误 // 如果内镜未绑定,返回错误
if ($context->getEndoscope()->isEmpty()) { if ($context->getEndoscope()->isEmpty()) {
Logger::error('内镜未绑定,放弃处理 card={}', [$context->getEndoscope()->cardNo]); Logger::error('内镜未绑定,放弃处理 card={}', [$context->getEndoscope()->cardNo]);
$context = $context->builder()->error(VoiceMessage::CARD_NOT_BOUND)->build(); $context = $context->createModifyBuilder()->error(VoiceMessage::CARD_NOT_BOUND)->build();
$this->sendVoice($context); $this->sendVoice($context);
return $context; return $context;
} }
@@ -94,7 +94,7 @@ class FlowProcessor
if ($mgr->hasOperator($context->getReader()->id) || $context->hasOperator()) { if ($mgr->hasOperator($context->getReader()->id) || $context->hasOperator()) {
if ($mgr->hasOperator($context->getReader()->id)) { if ($mgr->hasOperator($context->getReader()->id)) {
$op = $mgr->getOperator($context->getReader()->id, $context->getReader()->type); $op = $mgr->getOperator($context->getReader()->id, $context->getReader()->type);
$context = $context->builder()->withOperator(new \app\flow\context\bean\OperatorInfo( $context = $context->createModifyBuilder()->setOperator(new \app\flow\context\bean\OperatorInfo(
id: $op['id'], id: $op['id'],
name: $op['name'], name: $op['name'],
rfid: $op['rfid'] rfid: $op['rfid']
@@ -107,7 +107,7 @@ class FlowProcessor
$context->getEndoscope()->cardNo, $context->getEndoscope()->cardNo,
]); ]);
// 处理这个错误 // 处理这个错误
$context = $context->builder()->error(VoiceMessage::PLEASE_SWIPE_OPERATOR)->build(); $context = $context->createModifyBuilder()->error(VoiceMessage::PLEASE_SWIPE_OPERATOR)->build();
$this->handleResult($context); $this->handleResult($context);
return $context; return $context;
} }
+1 -1
View File
@@ -176,7 +176,7 @@ class ProcessEngine
public function execute(ProcessContext $context): ProcessContext public function execute(ProcessContext $context): ProcessContext
{ {
if ($this->chainHead === null) { if ($this->chainHead === null) {
return $context->builder()->error(VoiceMessage::PROCESS_CHAIN_NOT_INITIALIZED)->build(); return $context->createModifyBuilder()->error(VoiceMessage::PROCESS_CHAIN_NOT_INITIALIZED)->build();
} }
Logger::debug('[ProcessEngine] 开始执行流程链 readerType={} currentStep={} endoscope={}', [ Logger::debug('[ProcessEngine] 开始执行流程链 readerType={} currentStep={} endoscope={}', [
+7 -1
View File
@@ -14,6 +14,7 @@ use app\flow\context\bean\StorageStatus;
use app\flow\context\bean\VoiceState; use app\flow\context\bean\VoiceState;
use app\model\EctActions; use app\model\EctActions;
use app\net\PacketContext; use app\net\PacketContext;
use app\utils\Logger;
/** /**
* 流程上下文类(不可变) * 流程上下文类(不可变)
@@ -81,8 +82,13 @@ readonly class ProcessContext
* 创建一个基于当前上下文的 Builder * 创建一个基于当前上下文的 Builder
* 用于创建修改后的新上下文实例 * 用于创建修改后的新上下文实例
*/ */
public function builder(): ProcessContextBuilder public function createModifyBuilder(): ProcessContextBuilder
{ {
$caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1];
$callerClass = $caller['class'];
$callerMethod = $caller['function'];
$callerLine = $caller['line'];
Logger::debug("上下文请求修改构建器: class={}, method={}, line={}", [$callerClass, $callerMethod, $callerLine]);
return ProcessContextBuilder::from($this); return ProcessContextBuilder::from($this);
} }
+28 -26
View File
@@ -328,55 +328,55 @@ class ProcessContextBuilder
// ==================== 值对象设置方法 ==================== // ==================== 值对象设置方法 ====================
public function withEndoscope(EndoscopeInfo $endoscope): self public function setEndoscope(EndoscopeInfo $endoscope): self
{ {
$this->endoscope = $endoscope; $this->endoscope = $endoscope;
return $this; return $this;
} }
public function withReader(ReaderInfo $reader): self public function setReader(ReaderInfo $reader): self
{ {
$this->reader = $reader; $this->reader = $reader;
return $this; return $this;
} }
public function withOperator(OperatorInfo $operator): self public function setOperator(OperatorInfo $operator): self
{ {
$this->operator = $operator; $this->operator = $operator;
return $this; return $this;
} }
public function withStorage(StorageStatus $storage): self public function setStorage(StorageStatus $storage): self
{ {
$this->storage = $storage; $this->storage = $storage;
return $this; return $this;
} }
public function withMorningWash(MorningWashStatus $morningWash): self public function setMorningWash(MorningWashStatus $morningWash): self
{ {
$this->morningWash = $morningWash; $this->morningWash = $morningWash;
return $this; return $this;
} }
public function withVoice(VoiceState $voice): self public function setVoiceState(VoiceState $voice): self
{ {
$this->voice = $voice; $this->voice = $voice;
return $this; return $this;
} }
public function withResult(ExecutionResult $result): self public function setResult(ExecutionResult $result): self
{ {
$this->result = $result; $this->result = $result;
return $this; return $this;
} }
public function withProcessStatus(ProcessStatus $processStatus): self public function setProcessStatus(ProcessStatus $processStatus): self
{ {
$this->processStatus = $processStatus; $this->processStatus = $processStatus;
return $this; return $this;
} }
public function withReminder(ReminderStatus $reminder): self public function setReminder(ReminderStatus $reminder): self
{ {
$this->reminder = $reminder; $this->reminder = $reminder;
return $this; return $this;
@@ -384,7 +384,7 @@ class ProcessContextBuilder
// ==================== 流程状态便捷设置方法 ==================== // ==================== 流程状态便捷设置方法 ====================
public function withCurrentStep(string $currentStep): self public function setCurrentStep(string $currentStep): self
{ {
$this->processStatus = new ProcessStatus( $this->processStatus = new ProcessStatus(
currentStep: $currentStep, currentStep: $currentStep,
@@ -397,7 +397,7 @@ class ProcessContextBuilder
return $this; return $this;
} }
public function withProcessType(string $processType): self public function setProcessType(string $processType): self
{ {
$this->processStatus = new ProcessStatus( $this->processStatus = new ProcessStatus(
currentStep: $this->processStatus->currentStep, currentStep: $this->processStatus->currentStep,
@@ -410,7 +410,7 @@ class ProcessContextBuilder
return $this; return $this;
} }
public function withBatchNo(string $batchNo): self public function setBatchNo(string $batchNo): self
{ {
$this->processStatus = new ProcessStatus( $this->processStatus = new ProcessStatus(
currentStep: $this->processStatus->currentStep, currentStep: $this->processStatus->currentStep,
@@ -423,8 +423,10 @@ class ProcessContextBuilder
return $this; return $this;
} }
public function withDuration(?int $duration): self public function setDuration(?int $duration = null): self
{ {
if (empty($duration)) $duration = time() - strtotime($this->processStatus->actionStartTime);
$this->processStatus = new ProcessStatus( $this->processStatus = new ProcessStatus(
currentStep: $this->processStatus->currentStep, currentStep: $this->processStatus->currentStep,
processType: $this->processStatus->processType, processType: $this->processStatus->processType,
@@ -436,7 +438,7 @@ class ProcessContextBuilder
return $this; return $this;
} }
public function withPreviousAction(?EctActions $action): self public function setPreviousAction(?EctActions $action): self
{ {
$this->processStatus = new ProcessStatus( $this->processStatus = new ProcessStatus(
currentStep: $this->processStatus->currentStep, currentStep: $this->processStatus->currentStep,
@@ -473,7 +475,7 @@ class ProcessContextBuilder
/** /**
* 设置自定义错误 * 设置自定义错误
*/ */
public function customError(string $message): self public function setCustomError(string $message): self
{ {
$this->voice = $this->voice->withMessage($message)->withError(VoiceMessage::CUSTOM); $this->voice = $this->voice->withMessage($message)->withError(VoiceMessage::CUSTOM);
$this->result = $this->result->fail(); $this->result = $this->result->fail();
@@ -483,7 +485,7 @@ class ProcessContextBuilder
/** /**
* 设置语音消息 * 设置语音消息
*/ */
public function voiceMessage(string|VoiceMessage $message): self public function setVoiceMessage(string|VoiceMessage $message): self
{ {
$this->voice = $this->voice->withMessage($message); $this->voice = $this->voice->withMessage($message);
return $this; return $this;
@@ -492,7 +494,7 @@ class ProcessContextBuilder
/** /**
* 设置期望下一步 * 设置期望下一步
*/ */
public function expectedNextStep(VoiceMessage $expected): self public function setExpectedNextStep(VoiceMessage $expected): self
{ {
$this->voice = $this->voice->withExpectedNextStep($expected); $this->voice = $this->voice->withExpectedNextStep($expected);
return $this; return $this;
@@ -501,7 +503,7 @@ class ProcessContextBuilder
/** /**
* 添加数据库操作 * 添加数据库操作
*/ */
public function dbOperation(DbOperationType $operation): self public function setDbOperation(DbOperationType $operation): self
{ {
$this->result = $this->result->addDbOperation($operation); $this->result = $this->result->addDbOperation($operation);
return $this; return $this;
@@ -510,7 +512,7 @@ class ProcessContextBuilder
/** /**
* 设置需要数据库操作 * 设置需要数据库操作
*/ */
public function needDatabaseOperation(bool $need = true): self public function setNeedDatabaseOperation(bool $need = true): self
{ {
$this->result = new ExecutionResult( $this->result = new ExecutionResult(
success: $this->result->success, success: $this->result->success,
@@ -525,7 +527,7 @@ class ProcessContextBuilder
/** /**
* 设置需要WebSocket通知 * 设置需要WebSocket通知
*/ */
public function needWebSocketNotify(bool $need = true): self public function setNeedWebSocketNotify(bool $need = true): self
{ {
$this->result = $this->result->withWebSocketNotify($need); $this->result = $this->result->withWebSocketNotify($need);
return $this; return $this;
@@ -542,25 +544,25 @@ class ProcessContextBuilder
// ==================== 提醒状态便捷设置方法 ==================== // ==================== 提醒状态便捷设置方法 ====================
public function withNeedEnhanceWash(bool $need): self public function setNeedEnhanceWash(bool $need): self
{ {
$this->reminder = $this->reminder->withEnhanceWash($need); $this->reminder = $this->reminder->withEnhanceWash($need);
return $this; return $this;
} }
public function withNeedLeakTestRemind(bool $need): self public function setNeedLeakTestRemind(bool $need): self
{ {
$this->reminder = $this->reminder->withLeakTestRemind($need); $this->reminder = $this->reminder->withLeakTestRemind($need);
return $this; return $this;
} }
public function withNeedStorageRemind(bool $need): self public function setNeedStorageRemind(bool $need): self
{ {
$this->reminder = $this->reminder->withStorageRemind($need); $this->reminder = $this->reminder->withStorageRemind($need);
return $this; return $this;
} }
public function withLeakTestDone(bool $done, string $result = ''): self public function setLeakTestDone(bool $done, string $result = ''): self
{ {
$this->reminder = $this->reminder->withLeakTestDone($result); $this->reminder = $this->reminder->withLeakTestDone($result);
return $this; return $this;
@@ -568,13 +570,13 @@ class ProcessContextBuilder
// ==================== 标记设置方法 ==================== // ==================== 标记设置方法 ====================
public function withIsOperatorCard(bool $isOperatorCard): self public function setIsOperatorCard(bool $isOperatorCard): self
{ {
$this->isOperatorCard = $isOperatorCard; $this->isOperatorCard = $isOperatorCard;
return $this; return $this;
} }
public function withStepDuration(string $stepCode, int $duration): self public function setStepDuration(string $stepCode, int $duration): self
{ {
$this->stepDurations[$stepCode] = $duration; $this->stepDurations[$stepCode] = $duration;
return $this; return $this;
+3 -3
View File
@@ -83,8 +83,8 @@ abstract class AbstractProcessNode implements ProcessNodeInterface
Logger::debug('[{}-Node] 不能处理当前步骤,跳过', [$this->getCode()]); Logger::debug('[{}-Node] 不能处理当前步骤,跳过', [$this->getCode()]);
// 如果有期望下一步提示,设置到上下文 // 如果有期望下一步提示,设置到上下文
if ($canHandleResult->hasExpectedNextStep()) { if ($canHandleResult->hasExpectedNextStep()) {
$context = $context->builder() $context = $context->createModifyBuilder()
->expectedNextStep($canHandleResult->expectedNextStep) ->setExpectedNextStep($canHandleResult->expectedNextStep)
->build(); ->build();
} }
return $this->passToNext($context); return $this->passToNext($context);
@@ -181,7 +181,7 @@ abstract class AbstractProcessNode implements ProcessNodeInterface
*/ */
protected function stopNext(ProcessContext $context): ProcessContext protected function stopNext(ProcessContext $context): ProcessContext
{ {
return $context->builder() return $context->createModifyBuilder()
->skipNodeCount(count($this->getChildNodes())) ->skipNodeCount(count($this->getChildNodes()))
->build(); ->build();
} }
+2 -2
View File
@@ -61,10 +61,10 @@ class CloseNode extends AbstractProcessNode
// 如果有预期的下一步,则返回错误 // 如果有预期的下一步,则返回错误
if ($context->getVoice()->hasExpectedNextStep()) { if ($context->getVoice()->hasExpectedNextStep()) {
Logger::debug("节点期望: {$context->getVoice()->expectedNextStep->value}"); Logger::debug("节点期望: {$context->getVoice()->expectedNextStep->value}");
return $context->builder()->error($context->getVoice()->expectedNextStep)->build(); return $context->createModifyBuilder()->error($context->getVoice()->expectedNextStep)->build();
} }
// 异常流程 // 异常流程
Logger::error("异常流程,所有节点处理完成,无匹配节点并且无预期的下一步"); Logger::error("异常流程,所有节点处理完成,无匹配节点并且无预期的下一步");
return $context->builder()->error(VoiceMessage::UNKNOWN_ERROR)->build(); return $context->createModifyBuilder()->error(VoiceMessage::UNKNOWN_ERROR)->build();
} }
} }
+5 -5
View File
@@ -59,11 +59,11 @@ class DisinfectNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
return $context->builder() return $context->createModifyBuilder()
->withCurrentStep('消毒') ->setCurrentStep('消毒')
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+5 -5
View File
@@ -64,11 +64,11 @@ class DryNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
return $context->builder() return $context->createModifyBuilder()
->withCurrentStep('干燥') ->setCurrentStep('干燥')
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+1 -1
View File
@@ -50,6 +50,6 @@ class DuplicateCheckNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
return $context->builder()->error(VoiceMessage::DUPLICATE_SWIPING)->build(); return $context->createModifyBuilder()->error(VoiceMessage::DUPLICATE_SWIPING)->build();
} }
} }
+5 -5
View File
@@ -62,11 +62,11 @@ class EndNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
return $context->builder() return $context->createModifyBuilder()
->withCurrentStep('结束') ->setCurrentStep('结束')
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+5 -5
View File
@@ -62,11 +62,11 @@ class FinalRinseNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
return $context->builder() return $context->createModifyBuilder()
->withCurrentStep('终末漂洗') ->setCurrentStep('终末漂洗')
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+7 -7
View File
@@ -67,13 +67,13 @@ class MachineWashNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
return $context->builder() return $context->createModifyBuilder()
->withProcessType('机洗') ->setProcessType('机洗')
->withCurrentStep('机洗') ->setCurrentStep('机洗')
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->dbOperation(DbOperationType::UPDATE) ->setDbOperation(DbOperationType::UPDATE)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+7 -7
View File
@@ -71,13 +71,13 @@ class MorningWashNode extends AbstractProcessNode
// 设置流程类型 // 设置流程类型
$processType = $context->getReader()->type === '机洗' ? '机洗(晨洗)' : '手工洗(晨洗)'; $processType = $context->getReader()->type === '机洗' ? '机洗(晨洗)' : '手工洗(晨洗)';
return $context->builder() return $context->createModifyBuilder()
->withMorningWash($morningWash) ->setMorningWash($morningWash)
->withProcessType($processType) ->setProcessType($processType)
->withCurrentStep(self::getName()) ->setCurrentStep(self::getName())
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+5 -5
View File
@@ -63,11 +63,11 @@ class RinseNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
return $context->builder() return $context->createModifyBuilder()
->withCurrentStep('漂洗') ->setCurrentStep('漂洗')
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+7 -7
View File
@@ -78,13 +78,13 @@ class StorageInNode extends AbstractProcessNode
{ {
Logger::debug('[StorageInNode] 内镜入库成功 endoscope={}', [$context->getEndoscope()->name]); Logger::debug('[StorageInNode] 内镜入库成功 endoscope={}', [$context->getEndoscope()->name]);
return $context->builder() return $context->createModifyBuilder()
->withProcessType('存储') ->setProcessType('存储')
->withCurrentStep(self::getName()) ->setCurrentStep(self::getName())
->withStorage(StorageStatus::inStorage(date('Y-m-d H:i:s'))) ->setStorage(StorageStatus::inStorage(date('Y-m-d H:i:s')))
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+8 -8
View File
@@ -82,25 +82,25 @@ class StorageNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
$builder = $context->builder()->withProcessType('存储'); $builder = $context->createModifyBuilder()->setProcessType('存储');
// 根据当前状态判断执行入库还是出库(canHandle 已经验证过状态) // 根据当前状态判断执行入库还是出库(canHandle 已经验证过状态)
if (!$context->getStorage()->isInStorage) { if (!$context->getStorage()->isInStorage) {
// 入库操作 // 入库操作
Logger::debug('[StorageNode] 内镜入库成功 endoscope={}', [$context->getEndoscope()->name]); Logger::debug('[StorageNode] 内镜入库成功 endoscope={}', [$context->getEndoscope()->name]);
$builder->withCurrentStep('内镜放入') $builder->setCurrentStep('内镜放入')
->withStorage(StorageStatus::inStorage(date('Y-m-d H:i:s'))); ->setStorage(StorageStatus::inStorage(date('Y-m-d H:i:s')));
} else { } else {
// 出库操作 // 出库操作
Logger::debug('[StorageNode] 内镜出库成功 endoscope={}', [$context->getEndoscope()->name]); Logger::debug('[StorageNode] 内镜出库成功 endoscope={}', [$context->getEndoscope()->name]);
$builder->withCurrentStep('内镜取出') $builder->setCurrentStep('内镜取出')
->withStorage(StorageStatus::outOfStorage()); ->setStorage(StorageStatus::outOfStorage());
} }
return $builder return $builder
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+7 -7
View File
@@ -78,13 +78,13 @@ class StorageOutNode extends AbstractProcessNode
{ {
Logger::debug('[StorageOutNode] 内镜出库成功 endoscope={}', [$context->getEndoscope()->name]); Logger::debug('[StorageOutNode] 内镜出库成功 endoscope={}', [$context->getEndoscope()->name]);
return $context->builder() return $context->createModifyBuilder()
->withProcessType('存储') ->setProcessType('存储')
->withCurrentStep(self::getName()) ->setCurrentStep(self::getName())
->withStorage(StorageStatus::outOfStorage()) ->setStorage(StorageStatus::outOfStorage())
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+6 -6
View File
@@ -70,18 +70,18 @@ class WashNode extends AbstractProcessNode
*/ */
protected function doHandle(ProcessContext $context): ProcessContext protected function doHandle(ProcessContext $context): ProcessContext
{ {
$builder = $context->builder(); $builder = $context->createModifyBuilder();
// 设置流程类型 // 设置流程类型
if (empty($context->getProcessType()) || $context->getProcessType() === '晨洗') { if (empty($context->getProcessType()) || $context->getProcessType() === '晨洗') {
$builder->withProcessType('手工洗'); $builder->setProcessType('手工洗');
} }
return $builder return $builder
->withCurrentStep(self::getName()) ->setCurrentStep(self::getName())
->needDatabaseOperation() ->setNeedDatabaseOperation()
->dbOperation(DbOperationType::INSERT) ->setDbOperation(DbOperationType::INSERT)
->needWebSocketNotify() ->setNeedWebSocketNotify()
->build(); ->build();
} }
} }
+1 -1
View File
@@ -41,7 +41,7 @@ class MorningWashStrategy extends AbstractStrategy
todayWashRecords: $context->getMorningWash()->todayWashRecords todayWashRecords: $context->getMorningWash()->todayWashRecords
); );
return $context->builder()->withMorningWash($morningWash)->build(); return $context->createModifyBuilder()->setMorningWash($morningWash)->build();
} }
/** /**
@@ -79,7 +79,7 @@ class TimeValidationStrategy extends AbstractStrategy
// 从数据库按流程类型精确查询 // 从数据库按流程类型精确查询
$requiredDuration = $this->getDurationFromDb($stepCode, $processType); $requiredDuration = $this->getDurationFromDb($stepCode, $processType);
if ($requiredDuration > 0) { if ($requiredDuration > 0) {
$context = $context->builder()->withStepDuration($stepCode, $requiredDuration)->build(); $context = $context->createModifyBuilder()->setStepDuration($stepCode, $requiredDuration)->build();
} else { } else {
// 最后使用上下文已缓存值 // 最后使用上下文已缓存值
$requiredDuration = $context->getStepDuration($stepCode); $requiredDuration = $context->getStepDuration($stepCode);
@@ -106,7 +106,7 @@ class TimeValidationStrategy extends AbstractStrategy
$voice = VoiceMessage::NOT_ENOUGH_TIME->value; $voice = VoiceMessage::NOT_ENOUGH_TIME->value;
$voice = str_replace('{step}', $stepCode, $voice); $voice = str_replace('{step}', $stepCode, $voice);
$voice = str_replace('{time}', $requiredDuration - $duration, $voice); $voice = str_replace('{time}', $requiredDuration - $duration, $voice);
$context = $context->builder()->customError($voice)->build(); $context = $context->createModifyBuilder()->setCustomError($voice)->build();
} }
return $context; return $context;
} }
@@ -36,7 +36,7 @@ class VoiceGenerationStrategy extends AbstractStrategy
"流程中存在自定义语音或存在多次设置语音,语音应该在 VoiceGenerationStrategy 策略中配置,否则不能拦截与自定义配置", "流程中存在自定义语音或存在多次设置语音,语音应该在 VoiceGenerationStrategy 策略中配置,否则不能拦截与自定义配置",
new IllegalUsageException("In the existing process, there is custom voice, which should be configured in the VoiceGenerationStrategy strategy; otherwise, it cannot be intercepted and customized") new IllegalUsageException("In the existing process, there is custom voice, which should be configured in the VoiceGenerationStrategy strategy; otherwise, it cannot be intercepted and customized")
); );
return $context->builder()->voiceMessage($context->getVoice()->message)->build(); return $context->createModifyBuilder()->setVoiceMessage($context->getVoice()->message)->build();
} }
// 如果已经有错误,生成错误语音 // 如果已经有错误,生成错误语音
@@ -56,7 +56,7 @@ class VoiceGenerationStrategy extends AbstractStrategy
} }
Logger::debug("应用语音模板后的内容: {$voice}"); Logger::debug("应用语音模板后的内容: {$voice}");
return $context->builder()->voiceMessage($voice)->build(); return $context->createModifyBuilder()->setVoiceMessage($voice)->build();
} }
/** /**
+2 -2
View File
@@ -245,7 +245,7 @@ class VirtualContextBuilder
/** /**
* 设置操作时长(秒) * 设置操作时长(秒)
*/ */
public function duration(int $seconds): self public function setDuration(int $seconds): self
{ {
$this->processStatus = new ProcessStatus( $this->processStatus = new ProcessStatus(
currentStep: $this->processStatus->currentStep, currentStep: $this->processStatus->currentStep,
@@ -277,7 +277,7 @@ class VirtualContextBuilder
/** /**
* 设置上一条操作记录(虚拟) * 设置上一条操作记录(虚拟)
*/ */
public function previousAction(string $processName, string $batchNo = '', ?string $opEndtime = null): self public function setPreviousAction(string $processName, string $batchNo = '', ?string $opEndtime = null): self
{ {
$action = new EctActions(); $action = new EctActions();
$action->process_name = $processName; $action->process_name = $processName;
+48 -46
View File
@@ -12,7 +12,7 @@ use app\utils\Logger;
/** /**
* 虚拟流程处理器 * 虚拟流程处理器
* *
* 用于测试环境的刷卡流程模拟器。 * 用于测试环境的刷卡流程模拟器。
* 不依赖真实的 FlowProcessor,不连接数据库,不发送语音。 * 不依赖真实的 FlowProcessor,不连接数据库,不发送语音。
* 支持完整的刷卡流程模拟和验证。 * 支持完整的刷卡流程模拟和验证。
@@ -22,19 +22,19 @@ class VirtualityFlowProcessor
private ProcessEngine $engine; private ProcessEngine $engine;
private array $environment; private array $environment;
private string $envPath; private string $envPath;
/** @var array 当前操作员会话缓存 (readerId => operatorInfo) */ /** @var array 当前操作员会话缓存 (readerId => operatorInfo) */
private array $operatorSessions = []; private array $operatorSessions = [];
/** @var ProcessContext[] 执行历史 */ /** @var ProcessContext[] 执行历史 */
private array $history = []; private array $history = [];
/** @var array 当前内镜的流程状态 (endoscopeId => ProcessContext) */ /** @var array 当前内镜的流程状态 (endoscopeId => ProcessContext) */
private array $endoscopeStates = []; private array $endoscopeStates = [];
/** /**
* 构造函数 * 构造函数
* *
* @param ProcessConfig|null $config 流程配置 * @param ProcessConfig|null $config 流程配置
* @param string|null $envPath 环境配置文件路径 * @param string|null $envPath 环境配置文件路径
*/ */
@@ -53,10 +53,10 @@ class VirtualityFlowProcessor
if (!file_exists($this->envPath)) { if (!file_exists($this->envPath)) {
throw new \RuntimeException("环境配置文件不存在: {$this->envPath}"); throw new \RuntimeException("环境配置文件不存在: {$this->envPath}");
} }
$content = file_get_contents($this->envPath); $content = file_get_contents($this->envPath);
$this->environment = json_decode($content, true); $this->environment = json_decode($content, true);
if (json_last_error() !== JSON_ERROR_NONE) { if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException("环境配置文件解析失败: " . json_last_error_msg()); throw new \RuntimeException("环境配置文件解析失败: " . json_last_error_msg());
} }
@@ -100,21 +100,21 @@ class VirtualityFlowProcessor
/** /**
* 模拟刷人员卡 * 模拟刷人员卡
* *
* @param string $operatorName 操作员名称(环境配置中定义) * @param string $operatorName 操作员名称(环境配置中定义)
* @param string $readerType 读卡器类型 * @param string $readerType 读卡器类型
* @return ProcessContext * @return ProcessContext
*/ */
public function swipeOperatorCard(string $operatorName, string $readerType = '清洗'): ProcessContext public function swipeOperatorCard(string $operatorName, string $readerType = '清洗'): ProcessContext
{ {
$operatorData = $this->environment['operators'][$operatorName] $operatorData = $this->environment['operators'][$operatorName]
?? throw new \InvalidArgumentException("未找到操作员: {$operatorName}"); ?? throw new \InvalidArgumentException("未找到操作员: {$operatorName}");
$readerData = $this->environment['readers'][$readerType] $readerData = $this->environment['readers'][$readerType]
?? throw new \InvalidArgumentException("未找到读卡器: {$readerType}"); ?? throw new \InvalidArgumentException("未找到读卡器: {$readerType}");
// 保存操作员会话 // 保存操作员会话
$this->operatorSessions[$readerData['id']] = $operatorData; $this->operatorSessions[$readerData['id']] = $operatorData;
// 构建上下文 // 构建上下文
$context = VirtualContextBuilder::create($this->envPath) $context = VirtualContextBuilder::create($this->envPath)
->operator($operatorName) ->operator($operatorName)
@@ -122,92 +122,94 @@ class VirtualityFlowProcessor
->asOperatorCard() ->asOperatorCard()
->withEngineConfig($this->engine->getConfig()) ->withEngineConfig($this->engine->getConfig())
->build(); ->build();
// 人员卡不走流程链,直接返回 // 人员卡不走流程链,直接返回
$result = $context->builder() $result = $context->createModifyBuilder()
->voiceMessage('请刷内镜卡') ->setVoiceMessage('请刷内镜卡')
->build(); ->build();
$this->history[] = $result; $this->history[] = $result;
return $result; return $result;
} }
/** /**
* 模拟刷内镜卡 * 模拟刷内镜卡
* *
* @param string $endoscopeName 内镜名称(环境配置中定义) * @param string $endoscopeName 内镜名称(环境配置中定义)
* @param string $readerType 读卡器类型 * @param string $readerType 读卡器类型
* @return ProcessContext * @return ProcessContext
*/ */
public function swipeEndoscopeCard(string $endoscopeName, string $readerType): ProcessContext public function swipeEndoscopeCard(string $endoscopeName, string $readerType): ProcessContext
{ {
$readerData = $this->environment['readers'][$readerType] $readerData = $this->environment['readers'][$readerType]
?? throw new \InvalidArgumentException("未找到读卡器: {$readerType}"); ?? throw new \InvalidArgumentException("未找到读卡器: {$readerType}");
$endoscopeData = $this->environment['endoscopes'][$endoscopeName] $endoscopeData = $this->environment['endoscopes'][$endoscopeName]
?? throw new \InvalidArgumentException("未找到内镜: {$endoscopeName}"); ?? throw new \InvalidArgumentException("未找到内镜: {$endoscopeName}");
// 获取内镜当前状态 // 获取内镜当前状态
/** @var ProcessContext $currentState */
$currentState = $this->endoscopeStates[$endoscopeData['id']] ?? null; $currentState = $this->endoscopeStates[$endoscopeData['id']] ?? null;
// 构建上下文 // 构建上下文
$builder = VirtualContextBuilder::create($this->envPath) $builder = VirtualContextBuilder::create($this->envPath)
->endoscope($endoscopeName) ->endoscope($endoscopeName)
->reader($readerType) ->reader($readerType)
->withEngineConfig($this->engine->getConfig()); ->withEngineConfig($this->engine->getConfig());
// 如果有操作员会话,添加操作员信息 // 如果有操作员会话,添加操作员信息
if (isset($this->operatorSessions[$readerData['id']])) { if (isset($this->operatorSessions[$readerData['id']])) {
$opData = $this->operatorSessions[$readerData['id']]; $opData = $this->operatorSessions[$readerData['id']];
$builder->customOperator($opData['id'], $opData['name'], $opData['rfid']); $builder->customOperator($opData['id'], $opData['name'], $opData['rfid']);
} }
// 继承之前的流程状态 // 继承之前的流程状态
if ($currentState !== null) { if ($currentState !== null) {
$builder->currentStep($currentState->getCurrentStep()) $builder->currentStep($currentState->getCurrentStep())
->batchNo($currentState->getBatchNo()) ->batchNo($currentState->getBatchNo())
->processType($currentState->getProcessType()); ->setDuration(time() - strtotime($currentState->getActionStartTime()))
->processType($currentState->getProcessType());
// 设置 previousAction:上一步骤名称就是当前状态的 currentStep // 设置 previousAction:上一步骤名称就是当前状态的 currentStep
if (!empty($currentState->getCurrentStep())) { if (!empty($currentState->getCurrentStep())) {
$builder->previousAction($currentState->getCurrentStep(), $currentState->getBatchNo()); $builder->setPreviousAction($currentState->getCurrentStep(), $currentState->getBatchNo());
} }
} else { } else {
// 新流程 // 新流程
$builder->newProcess(); $builder->newProcess();
} }
$context = $builder->build(); $context = $builder->build();
// 未刷人员卡检查 // 未刷人员卡检查
if (!$context->hasOperator() && !isset($this->operatorSessions[$readerData['id']])) { if (!$context->hasOperator() && !isset($this->operatorSessions[$readerData['id']])) {
$result = $context->builder() $result = $context->createModifyBuilder()
->error(\app\flow\enum\VoiceMessage::PLEASE_SWIPE_OPERATOR) ->error(\app\flow\enum\VoiceMessage::PLEASE_SWIPE_OPERATOR)
->build(); ->build();
$this->history[] = $result; $this->history[] = $result;
return $result; return $result;
} }
// 执行流程 // 执行流程
$result = $this->engine->execute($context); $result = $this->engine->execute($context);
// 确保 previousAction 在结果中正确设置 // 确保 previousAction 在结果中正确设置
// 如果执行成功且有上一步骤信息,确保 previousAction 被保留 // 如果执行成功且有上一步骤信息,确保 previousAction 被保留
if ($result->isSuccess() && $context->getPreviousAction() !== null && $result->getPreviousAction() === null) { if ($result->isSuccess() && $context->getPreviousAction() !== null && $result->getPreviousAction() === null) {
$result = $result->builder() $result = $result->createModifyBuilder()
->withPreviousAction($context->getPreviousAction()) ->setPreviousAction($context->getPreviousAction())
->build(); ->build();
} }
// 保存状态 // 保存状态
$this->endoscopeStates[$endoscopeData['id']] = $result; $this->endoscopeStates[$endoscopeData['id']] = $result;
$this->history[] = $result; $this->history[] = $result;
return $result; return $result;
} }
/** /**
* 快捷方法:完整刷卡(先刷人员卡再刷内镜卡) * 快捷方法:完整刷卡(先刷人员卡再刷内镜卡)
* *
* @param string $operatorName 操作员名称 * @param string $operatorName 操作员名称
* @param string $endoscopeName 内镜名称 * @param string $endoscopeName 内镜名称
* @param string $readerType 读卡器类型 * @param string $readerType 读卡器类型
@@ -220,23 +222,23 @@ class VirtualityFlowProcessor
$result = $this->swipeOperatorCard($operatorName, $readerType); $result = $this->swipeOperatorCard($operatorName, $readerType);
// 当前语音 // 当前语音
Logger::info("当前语音:{$result->getFullVoice()}"); Logger::info("当前语音:{$result->getFullVoice()}");
// 刷内镜卡 // 刷内镜卡
Logger::info("测试刷卡:{$endoscopeName}, {$readerType}"); Logger::info("测试刷卡:{$endoscopeName}, {$readerType}");
$result = $this->swipeEndoscopeCard($endoscopeName, $readerType); $result = $this->swipeEndoscopeCard($endoscopeName, $readerType);
Logger::info("当前流程:{}", [$result->getProcessType()]); Logger::info("当前流程:{}", [$result->getProcessType()]);
Logger::info("当前批次号:{}", [$result->getBatchNo()]); Logger::info("当前批次号:{}", [$result->getBatchNo()]);
Logger::info("当前步骤:{}", [$result->getCurrentStep()]); Logger::info("当前步骤:{}", [$result->getCurrentStep()]);
Logger::info("上一个步骤类型:{}", [$result->getPreviousAction()->action_type_name?? "null"]); Logger::info("上一个步骤类型:{}", [$result->getPreviousAction()->action_type_name ?? "null"]);
Logger::info("上一个步骤:{}", [$result->getPreviousAction()->process_name??"null"]); Logger::info("上一个步骤:{}", [$result->getPreviousAction()->process_name ?? "null"]);
Logger::info("时长:{}", [$result->getDuration()?? "null"]); Logger::info("时长:{}", [$result->getDuration() ?? "null"]);
Logger::info("当前语音:{$result->getFullVoice()}\n"); Logger::info("当前语音:{$result->getFullVoice()}\n");
return $result; return $result;
} }
/** /**
* 执行完整的手工洗流程 * 执行完整的手工洗流程
* *
* @param string $operatorName 操作员名称 * @param string $operatorName 操作员名称
* @param string $endoscopeName 内镜名称 * @param string $endoscopeName 内镜名称
* @return array<string, ProcessContext> 每个步骤的执行结果 * @return array<string, ProcessContext> 每个步骤的执行结果
@@ -245,17 +247,17 @@ class VirtualityFlowProcessor
{ {
$steps = ['清洗', '漂洗', '消毒', '终末漂洗', '干燥']; $steps = ['清洗', '漂洗', '消毒', '终末漂洗', '干燥'];
$results = []; $results = [];
foreach ($steps as $step) { foreach ($steps as $step) {
$results[$step] = $this->swipe($operatorName, $endoscopeName, $step); $results[$step] = $this->swipe($operatorName, $endoscopeName, $step);
} }
return $results; return $results;
} }
/** /**
* 执行完整的机洗流程 * 执行完整的机洗流程
* *
* @param string $operatorName 操作员名称 * @param string $operatorName 操作员名称
* @param string $endoscopeName 内镜名称 * @param string $endoscopeName 内镜名称
* @return ProcessContext * @return ProcessContext