From d5991813a63c5fc59c2080431026de98fc4cb3f2 Mon Sep 17 00:00:00 2001 From: zimoyin <2556608754@qq.com> Date: Wed, 11 Mar 2026 00:49:02 +0800 Subject: [PATCH] =?UTF-8?q?ai-refactor(flow):=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E6=8A=BD=E8=B1=A1=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E5=92=8C=E4=BE=9D=E8=B5=96=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 AbstractProcessNode 中 ProcessContext 的命名空间引用为 app\flow\context\ProcessContext - 引入 app\flow\vo\CanHandleResult 用于节点处理结果表示 - 更新前置策略执行后对成功状态的判断,改为调用 isSuccess() 方法 - 增加日志记录细节,便于调试策略执行中断时的错误信息 - 优化代码注释,提升代码可读性和维护性 --- app/flow/context/ProcessContextBuilder.php | 623 +++++++++++++++++++++ app/flow/enum/DbOperationType.php | 17 + app/flow/enum/VoiceMessage.php | 65 +++ app/flow/vo/BatchNo.php | 169 ++++++ app/flow/vo/CanHandleResult.php | 54 ++ app/flow/vo/EndoscopeInfo.php | 58 ++ app/flow/vo/ExecutionResult.php | 139 +++++ app/flow/vo/MorningWashStatus.php | 91 +++ app/flow/vo/OperatorInfo.php | 55 ++ app/flow/vo/ProcessStatus.php | 75 +++ app/flow/vo/ReaderInfo.php | 63 +++ app/flow/vo/ReminderStatus.php | 111 ++++ app/flow/vo/StorageStatus.php | 79 +++ app/flow/vo/VoiceState.php | 156 ++++++ 14 files changed, 1755 insertions(+) create mode 100644 app/flow/context/ProcessContextBuilder.php create mode 100644 app/flow/enum/DbOperationType.php create mode 100644 app/flow/enum/VoiceMessage.php create mode 100644 app/flow/vo/BatchNo.php create mode 100644 app/flow/vo/CanHandleResult.php create mode 100644 app/flow/vo/EndoscopeInfo.php create mode 100644 app/flow/vo/ExecutionResult.php create mode 100644 app/flow/vo/MorningWashStatus.php create mode 100644 app/flow/vo/OperatorInfo.php create mode 100644 app/flow/vo/ProcessStatus.php create mode 100644 app/flow/vo/ReaderInfo.php create mode 100644 app/flow/vo/ReminderStatus.php create mode 100644 app/flow/vo/StorageStatus.php create mode 100644 app/flow/vo/VoiceState.php diff --git a/app/flow/context/ProcessContextBuilder.php b/app/flow/context/ProcessContextBuilder.php new file mode 100644 index 0000000..505efe0 --- /dev/null +++ b/app/flow/context/ProcessContextBuilder.php @@ -0,0 +1,623 @@ +config = Config::getInstance(); + // 初始化值对象为空对象 + $this->endoscope = EndoscopeInfo::empty(); + $this->reader = ReaderInfo::empty(); + $this->operator = OperatorInfo::empty(); + $this->storage = StorageStatus::notInStorage(); + $this->morningWash = MorningWashStatus::notRequired(); + $this->voice = VoiceState::empty(); + $this->result = ExecutionResult::success(); + $this->processStatus = ProcessStatus::empty(); + $this->reminder = ReminderStatus::none(); + } + + /** + * 从 PacketContext 创建构造器 + */ + public static function fromPacketContext(PacketContext $packetContext, array $additionalData = []): self + { + $builder = new self(); + $builder->packetContext = $packetContext; + $builder->engineConfig = $additionalData['engineConfig'] ?? null; + + // 获取原始卡号和读卡器编号 + $cardNo = $packetContext->packet->card ?? ''; + $readerNo = $packetContext->packet->reader ?? ''; + $builder->rawData = $packetContext->packet->toArray(); + + // 获取晨洗开始时间 + if ($builder->engineConfig !== null) { + $builder->morningStartTime = $builder->engineConfig->getMorningWashConfig()->morningStartTime; + } + + // 1. 加载内镜/操作员信息 + $builder->loadEndoscopeOrOperatorInfo($cardNo); + + // 2. 加载读卡器信息 + $builder->loadReaderInfo($readerNo); + + // 3. 加载内镜操作记录相关信息(仅当内镜ID存在时执行) + if (!$builder->endoscope->isEmpty()) { + $builder->loadEndoscopeActionInfo(); + $builder->loadStorageStatus(); + } + + // 4. 初始化晨洗状态 + $builder->morningWash = new MorningWashStatus( + needMorningWash: false, // TODO: 由策略计算 + morningWashed: false, + startTime: $builder->morningStartTime, + todayWashRecords: $builder->morningWash->todayWashRecords + ); + + // 5. 合并额外数据 + foreach ($additionalData as $key => $value) { + if (property_exists($builder, $key) && $value !== null) { + $builder->$key = $value; + } + } + + Logger::debug("从 PacketContext 创建 ProcessContextBuilder"); + return $builder; + } + + /** + * 从现有的 ProcessContext 创建构造器 + * 用于基于现有上下文创建修改后的新实例 + */ + public static function from(ProcessContext $context): self + { + $builder = new self(); + + // 复制值对象 + $builder->endoscope = $context->getEndoscope(); + $builder->reader = $context->getReader(); + $builder->operator = $context->getOperator(); + $builder->storage = $context->getStorage(); + $builder->morningWash = $context->getMorningWash(); + $builder->voice = $context->getVoice(); + $builder->result = $context->getResult(); + $builder->processStatus = $context->getProcessStatus(); + $builder->reminder = $context->getReminder(); + + // 复制原始数据 + $builder->packetContext = $context->getPacketContext(); + $builder->engineConfig = $context->getEngineConfig(); + $builder->rawData = $context->getRawData(); + + // 复制标记 + $builder->isOperatorCard = $context->isOperatorCard(); + $builder->stepDurations = $context->getStepDurations(); + + return $builder; + } + + // ==================== 数据加载方法 ==================== + + /** + * 加载内镜或操作员信息 + */ + private function loadEndoscopeOrOperatorInfo(string $cardNo): void + { + if (empty($cardNo)) { + return; + } + + // 优先查询内镜信息 + $endoscope = EndoscopeRepository::new()->findByCardNo($cardNo); + if ($endoscope !== null) { + $this->endoscope = new EndoscopeInfo( + id: (string)$endoscope->endoscope_id, + name: (string)$endoscope->endoscope_name, + cardNo: $cardNo, + type: (string)$endoscope->endoscope_type + ); + return; + } + + // 内镜无记录则查询人员卡信息 + try { + $user = UserRepository::new()->findByRfid($cardNo); + if ($user !== null) { + $this->isOperatorCard = true; + $this->operator = new OperatorInfo( + id: (string)$user->user_id, + name: (string)$user->user_name, + rfid: (string)$user->user_rfid + ); + } + } catch (Exception $e) { + Logger::error("[ProcessContextBuilder] 查询人员卡信息出错: {}", [$e->getMessage()]); + } + } + + /** + * 加载读卡器信息 + */ + private function loadReaderInfo(string $readerNo): void + { + if (empty($readerNo)) { + return; + } + + $readerInfo = ReaderRepository::new()->findReaderInfo($readerNo); + if ($readerInfo !== null) { + $this->reader = new ReaderInfo( + no: $readerNo, + type: $readerInfo['readerType'], + id: $readerInfo['readerId'] + ); + } + } + + /** + * 加载内镜操作记录相关信息 + */ + private function loadEndoscopeActionInfo(): void + { + $actionsRepo = EctActionsRepository::new(); + + // 查询最后一条操作记录 + $lastAction = $actionsRepo->findLastAction($this->endoscope->id, [0, 7, 8]); + if (empty($lastAction)) { + return; + } + $lastStepStartTime = $lastAction->op_starttime; + $duration = time() - strtotime($lastStepStartTime); + $this->handleLastAction($lastAction, $actionsRepo, $duration); + + // 查询今日洗消记录数(晨洗判断) + $todayWashRecords = $actionsRepo->countTodayActions($this->endoscope->id, $this->morningStartTime, [0, 7, 8]); + $this->morningWash = new MorningWashStatus( + needMorningWash: $this->morningWash->needMorningWash, + morningWashed: $this->morningWash->morningWashed, + startTime: $this->morningStartTime, + todayWashRecords: $todayWashRecords + ); + } + + /** + * 加载内镜存储状态 + */ + private function loadStorageStatus(): void + { + $actionsRepo = EctActionsRepository::new(); + + // 查询最后一次存储操作记录 + $lastStorageAction = $actionsRepo->findLastStorageAction($this->endoscope->id); + if ($lastStorageAction !== null) { + $isInStorage = ($lastStorageAction['process_name'] === '内镜放入'); + $inTime = $isInStorage ? $lastStorageAction['op_starttime'] : null; + $this->storage = new StorageStatus( + isInStorage: $isInStorage, + lastAction: $lastStorageAction['process_name'], + inTime: $inTime + ); + } + + // 查询最后一次存储入库时间(义乌模式晨洗判断) + $storageTime = $actionsRepo->findLastStorageTime($this->endoscope->id); + if ($storageTime !== null) { + $this->storage = new StorageStatus( + isInStorage: $this->storage->isInStorage, + lastAction: $this->storage->lastAction, + inTime: $storageTime + ); + } + } + + /** + * 处理最后一条操作记录的核心逻辑 + */ + private function handleLastAction(EctActions $lastAction, EctActionsRepository $actionsRepo, int $duration): void + { + // 设置基础操作信息 + $currentStep = (string)$lastAction->process_name; + $actionStartTime = date('Y-m-d H:i:s'); + $processType = ''; + $batchNo = ''; + + // 处理批次号逻辑 + if ($currentStep === '结束') { + $batchNo = $this->generateBatchNo(); + } else { + $batchNo = (string)$lastAction->op_batchno; + $processType = (string)$lastAction->action_type_name; + } + + $this->processStatus = new ProcessStatus( + currentStep: $currentStep, + processType: $processType, + batchNo: $batchNo, + actionStartTime: $actionStartTime, + duration: $duration, + previousAction: $lastAction + ); + + // 加载批次操作员信息 + if ($currentStep !== '结束' && !empty($batchNo)) { + $this->loadBatchOperatorInfo($actionsRepo, $batchNo); + } + } + + /** + * 加载批次对应的操作员信息 + */ + private function loadBatchOperatorInfo(EctActionsRepository $actionsRepo, string $batchNo): void + { + $operator = $actionsRepo->findOperatorByBatchNo($batchNo); + if ($operator !== null) { + $this->operator = new OperatorInfo( + id: $operator['id'], + name: $operator['name'], + rfid: $operator['rfid'] + ); + } + } + + /** + * 生成批次号 + */ + private function generateBatchNo(): string + { + $existingBatchNo = null; + if (!$this->endoscope->isEmpty()) { + $existingBatchNo = EctActionsRepository::new()->findTodayActiveBatchNo($this->config->machineId); + } + + $datePart = date('Ymd'); + $sequence = 1; + + if (!empty($existingBatchNo)) { + $existingDatePart = substr($existingBatchNo, 0, 8); + $existingSequence = substr($existingBatchNo, 10, 4); + + if ($existingDatePart === $datePart && is_numeric($existingSequence)) { + $sequence = (int)$existingSequence + 1; + } + } + + $sequencePart = str_pad($sequence, 4, '0', STR_PAD_LEFT); + return $datePart . $this->config->machineId . $sequencePart; + } + + // ==================== 值对象设置方法 ==================== + + public function withEndoscope(EndoscopeInfo $endoscope): self + { + $this->endoscope = $endoscope; + return $this; + } + + public function withReader(ReaderInfo $reader): self + { + $this->reader = $reader; + return $this; + } + + public function withOperator(OperatorInfo $operator): self + { + $this->operator = $operator; + return $this; + } + + public function withStorage(StorageStatus $storage): self + { + $this->storage = $storage; + return $this; + } + + public function withMorningWash(MorningWashStatus $morningWash): self + { + $this->morningWash = $morningWash; + return $this; + } + + public function withVoice(VoiceState $voice): self + { + $this->voice = $voice; + return $this; + } + + public function withResult(ExecutionResult $result): self + { + $this->result = $result; + return $this; + } + + public function withProcessStatus(ProcessStatus $processStatus): self + { + $this->processStatus = $processStatus; + return $this; + } + + public function withReminder(ReminderStatus $reminder): self + { + $this->reminder = $reminder; + return $this; + } + + // ==================== 流程状态便捷设置方法 ==================== + + public function withCurrentStep(string $currentStep): self + { + $this->processStatus = new ProcessStatus( + currentStep: $currentStep, + processType: $this->processStatus->processType, + batchNo: $this->processStatus->batchNo, + actionStartTime: $this->processStatus->actionStartTime, + duration: $this->processStatus->duration, + previousAction: $this->processStatus->previousAction + ); + return $this; + } + + public function withProcessType(string $processType): self + { + $this->processStatus = new ProcessStatus( + currentStep: $this->processStatus->currentStep, + processType: $processType, + batchNo: $this->processStatus->batchNo, + actionStartTime: $this->processStatus->actionStartTime, + duration: $this->processStatus->duration, + previousAction: $this->processStatus->previousAction + ); + return $this; + } + + public function withBatchNo(string $batchNo): self + { + $this->processStatus = new ProcessStatus( + currentStep: $this->processStatus->currentStep, + processType: $this->processStatus->processType, + batchNo: $batchNo, + actionStartTime: $this->processStatus->actionStartTime, + duration: $this->processStatus->duration, + previousAction: $this->processStatus->previousAction + ); + return $this; + } + + public function withDuration(?int $duration): self + { + $this->processStatus = new ProcessStatus( + currentStep: $this->processStatus->currentStep, + processType: $this->processStatus->processType, + batchNo: $this->processStatus->batchNo, + actionStartTime: $this->processStatus->actionStartTime, + duration: $duration, + previousAction: $this->processStatus->previousAction + ); + return $this; + } + + public function withPreviousAction(?EctActions $action): self + { + $this->processStatus = new ProcessStatus( + currentStep: $this->processStatus->currentStep, + processType: $this->processStatus->processType, + batchNo: $this->processStatus->batchNo, + actionStartTime: $this->processStatus->actionStartTime, + duration: $this->processStatus->duration, + previousAction: $action + ); + return $this; + } + + // ==================== 便捷设置方法(语义化) ==================== + + /** + * 设置成功状态 + */ + public function success(): self + { + $this->result = new ExecutionResult(success: true); + return $this; + } + + /** + * 设置错误状态和消息 + */ + public function error(VoiceMessage $message): self + { + $this->voice = $this->voice->withError($message); + $this->result = $this->result->fail(); + return $this; + } + + /** + * 设置自定义错误 + */ + public function customError(string $message): self + { + $this->voice = $this->voice->withMessage($message)->withError(VoiceMessage::CUSTOM); + $this->result = $this->result->fail(); + return $this; + } + + /** + * 设置语音消息 + */ + public function voiceMessage(string|VoiceMessage $message): self + { + $this->voice = $this->voice->withMessage($message); + return $this; + } + + /** + * 设置期望下一步 + */ + public function expectedNextStep(VoiceMessage $expected): self + { + $this->voice = $this->voice->withExpectedNextStep($expected); + return $this; + } + + /** + * 添加数据库操作 + */ + public function dbOperation(DbOperationType $operation): self + { + $this->result = $this->result->addDbOperation($operation); + return $this; + } + + /** + * 设置需要数据库操作 + */ + public function needDatabaseOperation(bool $need = true): self + { + $this->result = new ExecutionResult( + success: $this->result->success, + needDatabaseOperation: $need, + dbOperations: $this->result->dbOperations, + needWebSocketNotify: $this->result->needWebSocketNotify, + skipNodeCount: $this->result->skipNodeCount + ); + return $this; + } + + /** + * 设置需要WebSocket通知 + */ + public function needWebSocketNotify(bool $need = true): self + { + $this->result = $this->result->withWebSocketNotify($need); + return $this; + } + + /** + * 设置跳过节点数 + */ + public function skipNodeCount(int $count): self + { + $this->result = $this->result->withSkipNodeCount($count); + return $this; + } + + // ==================== 提醒状态便捷设置方法 ==================== + + public function withNeedEnhanceWash(bool $need): self + { + $this->reminder = $this->reminder->withEnhanceWash($need); + return $this; + } + + public function withNeedLeakTestRemind(bool $need): self + { + $this->reminder = $this->reminder->withLeakTestRemind($need); + return $this; + } + + public function withNeedStorageRemind(bool $need): self + { + $this->reminder = $this->reminder->withStorageRemind($need); + return $this; + } + + public function withLeakTestDone(bool $done, string $result = ''): self + { + $this->reminder = $this->reminder->withLeakTestDone($result); + return $this; + } + + // ==================== 标记设置方法 ==================== + + public function withIsOperatorCard(bool $isOperatorCard): self + { + $this->isOperatorCard = $isOperatorCard; + return $this; + } + + public function withStepDuration(string $stepCode, int $duration): self + { + $this->stepDurations[$stepCode] = $duration; + return $this; + } + + // ==================== 构建方法 ==================== + + /** + * 构建不可变的 ProcessContext 实例 + */ + public function build(): ProcessContext + { + Logger::debug("ProcessContextBuilder 构建 ProcessContext 完成"); + + return new ProcessContext( + endoscope: $this->endoscope, + reader: $this->reader, + operator: $this->operator, + storage: $this->storage, + morningWash: $this->morningWash, + voice: $this->voice, + result: $this->result, + processStatus: $this->processStatus, + reminder: $this->reminder, + packetContext: $this->packetContext, + engineConfig: $this->engineConfig, + rawData: $this->rawData, + isOperatorCard: $this->isOperatorCard, + stepDurations: $this->stepDurations, + ); + } +} diff --git a/app/flow/enum/DbOperationType.php b/app/flow/enum/DbOperationType.php new file mode 100644 index 0000000..0abf24c --- /dev/null +++ b/app/flow/enum/DbOperationType.php @@ -0,0 +1,17 @@ +machineId; + + $existingBatchNo = EctActionsRepository::new()->findTodayActiveBatchNo($machineId); + + $datePart = date('Ymd'); + $sequence = 1; + + if (!empty($existingBatchNo)) { + $existingDatePart = substr($existingBatchNo, 0, 8); + $existingSequence = substr($existingBatchNo, 10, 4); + + if ($existingDatePart === $datePart && is_numeric($existingSequence)) { + $sequence = (int)$existingSequence + 1; + } + } + + $sequencePart = str_pad($sequence, 4, '0', STR_PAD_LEFT); + return new self(value: $datePart . $machineId . $sequencePart); + } + + /** + * 解析批次号结构 + * + * @return array{date: string, machineId: string, sequence: int, dateFormatted: string} + */ + public function parse(): array + { + if (!$this->isValid()) { + return [ + 'date' => '', + 'machineId' => '', + 'sequence' => 0, + 'dateFormatted' => '', + ]; + } + + $datePart = substr($this->value, 0, 8); + $machineId = substr($this->value, 8, 2); + $sequence = (int)substr($this->value, 10, 4); + + return [ + 'date' => $datePart, + 'machineId' => $machineId, + 'sequence' => $sequence, + 'dateFormatted' => substr($datePart, 0, 4) . '-' . substr($datePart, 4, 2) . '-' . substr($datePart, 6, 2), + ]; + } + + /** + * 验证批次号格式是否有效 + */ + public function isValid(): bool + { + // 批次号格式: YYYYMMDD + 机器ID(2位) + 序号(4位) = 14位 + if (strlen($this->value) !== 14) { + return false; + } + + $datePart = substr($this->value, 0, 8); + $sequencePart = substr($this->value, 10, 4); + + // 验证日期部分 + if (!is_numeric($datePart)) { + return false; + } + + // 验证序号部分 + if (!is_numeric($sequencePart)) { + return false; + } + + return true; + } + + /** + * 是否为空批次号 + */ + public function isEmpty(): bool + { + return empty($this->value); + } + + /** + * 获取日期部分 + */ + public function getDate(): string + { + $parsed = $this->parse(); + return $parsed['dateFormatted']; + } + + /** + * 获取机器ID + */ + public function getMachineId(): string + { + $parsed = $this->parse(); + return $parsed['machineId']; + } + + /** + * 获取序号 + */ + public function getSequence(): int + { + $parsed = $this->parse(); + return $parsed['sequence']; + } + + /** + * 是否是今天的批次号 + */ + public function isToday(): bool + { + if (!$this->isValid()) { + return false; + } + $datePart = substr($this->value, 0, 8); + return $datePart === date('Ymd'); + } + + /** + * 转换为字符串 + */ + public function __toString(): string + { + return $this->value; + } +} diff --git a/app/flow/vo/CanHandleResult.php b/app/flow/vo/CanHandleResult.php new file mode 100644 index 0000000..521af31 --- /dev/null +++ b/app/flow/vo/CanHandleResult.php @@ -0,0 +1,54 @@ +canHandle; + } + + /** + * 是否有期望下一步提示 + */ + public function hasExpectedNextStep(): bool + { + return $this->expectedNextStep !== VoiceMessage::NONE; + } +} diff --git a/app/flow/vo/EndoscopeInfo.php b/app/flow/vo/EndoscopeInfo.php new file mode 100644 index 0000000..3edd256 --- /dev/null +++ b/app/flow/vo/EndoscopeInfo.php @@ -0,0 +1,58 @@ +id); + } + + /** + * 判断内镜信息是否有效 + */ + public function isValid(): bool + { + return !empty($this->id) && !empty($this->cardNo); + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'cardNo' => $this->cardNo, + 'type' => $this->type, + ]; + } +} diff --git a/app/flow/vo/ExecutionResult.php b/app/flow/vo/ExecutionResult.php new file mode 100644 index 0000000..647533a --- /dev/null +++ b/app/flow/vo/ExecutionResult.php @@ -0,0 +1,139 @@ +success, + needDatabaseOperation: true, + dbOperations: [...$this->dbOperations, $operation], + needWebSocketNotify: $this->needWebSocketNotify, + skipNodeCount: $this->skipNodeCount + ); + } + + /** + * 设置为失败 + */ + public function fail(): self + { + return new self( + success: false, + needDatabaseOperation: $this->needDatabaseOperation, + dbOperations: $this->dbOperations, + needWebSocketNotify: $this->needWebSocketNotify, + skipNodeCount: $this->skipNodeCount + ); + } + + /** + * 设置需要WebSocket通知 + */ + public function withWebSocketNotify(bool $need = true): self + { + return new self( + success: $this->success, + needDatabaseOperation: $this->needDatabaseOperation, + dbOperations: $this->dbOperations, + needWebSocketNotify: $need, + skipNodeCount: $this->skipNodeCount + ); + } + + /** + * 设置跳过节点数 + */ + public function withSkipNodeCount(int $count): self + { + return new self( + success: $this->success, + needDatabaseOperation: $this->needDatabaseOperation, + dbOperations: $this->dbOperations, + needWebSocketNotify: $this->needWebSocketNotify, + skipNodeCount: $count + ); + } + + /** + * 是否有数据库操作 + */ + public function hasDbOperations(): bool + { + return !empty($this->dbOperations); + } + + /** + * 获取数据库操作类型名称列表 + */ + public function getDbOperationNames(): array + { + return array_map(fn(DbOperationType $op) => $op->name, $this->dbOperations); + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'success' => $this->success, + 'needDatabaseOperation' => $this->needDatabaseOperation, + 'dbOperations' => $this->getDbOperationNames(), + 'needWebSocketNotify' => $this->needWebSocketNotify, + 'skipNodeCount' => $this->skipNodeCount, + ]; + } +} diff --git a/app/flow/vo/MorningWashStatus.php b/app/flow/vo/MorningWashStatus.php new file mode 100644 index 0000000..e7bcf0a --- /dev/null +++ b/app/flow/vo/MorningWashStatus.php @@ -0,0 +1,91 @@ +needMorningWash && !$this->morningWashed; + } + + /** + * 判断晨洗是否已完成 + */ + public function isCompleted(): bool + { + return $this->morningWashed; + } + + /** + * 判断今天是否有洗消记录 + */ + public function hasWashRecordsToday(): bool + { + return $this->todayWashRecords > 0; + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'needMorningWash' => $this->needMorningWash, + 'morningWashed' => $this->morningWashed, + 'startTime' => $this->startTime, + 'todayWashRecords' => $this->todayWashRecords, + ]; + } +} diff --git a/app/flow/vo/OperatorInfo.php b/app/flow/vo/OperatorInfo.php new file mode 100644 index 0000000..fbd8da9 --- /dev/null +++ b/app/flow/vo/OperatorInfo.php @@ -0,0 +1,55 @@ +id); + } + + /** + * 判断操作员信息是否完整有效 + */ + public function isValid(): bool + { + return !empty($this->id) && !empty($this->name) && !empty($this->rfid); + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'rfid' => $this->rfid, + ]; + } +} diff --git a/app/flow/vo/ProcessStatus.php b/app/flow/vo/ProcessStatus.php new file mode 100644 index 0000000..ebbb40c --- /dev/null +++ b/app/flow/vo/ProcessStatus.php @@ -0,0 +1,75 @@ +currentStep, $validSteps); + } + + /** + * 检查是否已完成清洗流程 + */ + public function isWashProcessCompleted(): bool + { + return $this->currentStep === '结束'; + } + + /** + * 是否有上一个操作 + */ + public function hasPreviousAction(): bool + { + return $this->previousAction !== null; + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'currentStep' => $this->currentStep, + 'processType' => $this->processType, + 'batchNo' => $this->batchNo, + 'actionStartTime' => $this->actionStartTime, + 'duration' => $this->duration, + 'previousActionId' => $this->previousAction?->action_id, + ]; + } +} diff --git a/app/flow/vo/ReaderInfo.php b/app/flow/vo/ReaderInfo.php new file mode 100644 index 0000000..db3de4f --- /dev/null +++ b/app/flow/vo/ReaderInfo.php @@ -0,0 +1,63 @@ +no); + } + + /** + * 判断读卡器类型是否匹配 + */ + public function isType(string $type): bool + { + return $this->type === $type; + } + + /** + * 判断读卡器类型是否在给定列表中 + */ + public function isTypeIn(array $types): bool + { + return in_array($this->type, $types, true); + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'no' => $this->no, + 'type' => $this->type, + 'id' => $this->id, + ]; + } +} diff --git a/app/flow/vo/ReminderStatus.php b/app/flow/vo/ReminderStatus.php new file mode 100644 index 0000000..e10dd51 --- /dev/null +++ b/app/flow/vo/ReminderStatus.php @@ -0,0 +1,111 @@ +needLeakTestRemind, + needStorageRemind: $this->needStorageRemind, + leakTestDone: $this->leakTestDone, + leakTestResult: $this->leakTestResult + ); + } + + /** + * 设置需要测漏提醒 + */ + public function withLeakTestRemind(bool $need = true): self + { + return new self( + needEnhanceWash: $this->needEnhanceWash, + needLeakTestRemind: $need, + needStorageRemind: $this->needStorageRemind, + leakTestDone: $this->leakTestDone, + leakTestResult: $this->leakTestResult + ); + } + + /** + * 设置需要存储提醒 + */ + public function withStorageRemind(bool $need = true): self + { + return new self( + needEnhanceWash: $this->needEnhanceWash, + needLeakTestRemind: $this->needLeakTestRemind, + needStorageRemind: $need, + leakTestDone: $this->leakTestDone, + leakTestResult: $this->leakTestResult + ); + } + + /** + * 设置测漏完成 + */ + public function withLeakTestDone(string $result = ''): self + { + return new self( + needEnhanceWash: $this->needEnhanceWash, + needLeakTestRemind: $this->needLeakTestRemind, + needStorageRemind: $this->needStorageRemind, + leakTestDone: true, + leakTestResult: $result + ); + } + + /** + * 是否有任何提醒 + */ + public function hasAnyRemind(): bool + { + return $this->needEnhanceWash + || $this->needLeakTestRemind + || $this->needStorageRemind; + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'needEnhanceWash' => $this->needEnhanceWash, + 'needLeakTestRemind' => $this->needLeakTestRemind, + 'needStorageRemind' => $this->needStorageRemind, + 'leakTestDone' => $this->leakTestDone, + 'leakTestResult' => $this->leakTestResult, + ]; + } +} diff --git a/app/flow/vo/StorageStatus.php b/app/flow/vo/StorageStatus.php new file mode 100644 index 0000000..b0e7850 --- /dev/null +++ b/app/flow/vo/StorageStatus.php @@ -0,0 +1,79 @@ +isInStorage; + } + + /** + * 判断是否已出库 + */ + public function isTakenOut(): bool + { + return !$this->isInStorage && $this->lastAction === '内镜取出'; + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'isInStorage' => $this->isInStorage, + 'lastAction' => $this->lastAction, + 'inTime' => $this->inTime, + ]; + } +} diff --git a/app/flow/vo/VoiceState.php b/app/flow/vo/VoiceState.php new file mode 100644 index 0000000..7236c8b --- /dev/null +++ b/app/flow/vo/VoiceState.php @@ -0,0 +1,156 @@ +value); + } + return new self(message: $message); + } + + /** + * 从错误枚举创建 + */ + public static function fromError(VoiceMessage $error): self + { + return new self(errorMessage: $error); + } + + /** + * 设置语音消息 + */ + public function withMessage(string|VoiceMessage $message): self + { + $msg = $message instanceof VoiceMessage ? $message->value : $message; + return new self( + message: $msg, + errorMessage: $this->errorMessage, + expectedNextStep: $this->expectedNextStep, + templateParams: $this->templateParams + ); + } + + /** + * 设置错误消息 + */ + public function withError(VoiceMessage $error): self + { + return new self( + message: $this->message, + errorMessage: $error, + expectedNextStep: $this->expectedNextStep, + templateParams: $this->templateParams + ); + } + + /** + * 设置期望下一步 + */ + public function withExpectedNextStep(VoiceMessage $expected): self + { + return new self( + message: $this->message, + errorMessage: $this->errorMessage, + expectedNextStep: $expected, + templateParams: $this->templateParams + ); + } + + /** + * 添加语音前缀 + */ + public function prependMessage(string $prefix): self + { + return new self( + message: $prefix . $this->message, + errorMessage: $this->errorMessage, + expectedNextStep: $this->expectedNextStep, + templateParams: $this->templateParams + ); + } + + /** + * 设置模板参数 + */ + public function withTemplateParams(array $params): self + { + return new self( + message: $this->message, + errorMessage: $this->errorMessage, + expectedNextStep: $this->expectedNextStep, + templateParams: $params + ); + } + + /** + * 获取完整语音(优先返回 message,否则返回 errorMessage) + */ + public function getFullVoice(): string + { + if (!empty($this->message)) { + return $this->message; + } + return $this->errorMessage->value; + } + + /** + * 是否有错误 + */ + public function hasError(): bool + { + return $this->errorMessage !== VoiceMessage::NONE; + } + + /** + * 是否有期望下一步提示 + */ + public function hasExpectedNextStep(): bool + { + return $this->expectedNextStep !== VoiceMessage::NONE; + } + + /** + * 转换为数组 + */ + public function toArray(): array + { + return [ + 'message' => $this->message, + 'errorMessage' => $this->errorMessage->name, + 'expectedNextStep' => $this->expectedNextStep->name, + 'templateParams' => $this->templateParams, + ]; + } +}