engine; } } /** * 构造函数 * @param ProcessEngine|ProcessConfig $value 流程引擎或流程配置 */ public function __construct(ProcessEngine|ProcessConfig $value) { $config = $value instanceof ProcessConfig ? $value : new ProcessConfig(); $this->engine = $value instanceof ProcessEngine ? $value : ProcessEngine::create($config); } /** * 处理刷卡请求 * * @param PacketContext $packetContext 刷卡数据包上下文 * @param bool $isExecuteHandleResult 是否执行处理结果 * @return ProcessContext 处理结果上下文 */ public function process(PacketContext $packetContext, bool $isExecuteHandleResult = true): ProcessContext { // 从 PacketContext 创建 ProcessContext $context = ProcessContext::fromPacketContext($packetContext,["engineConfig"=>$this->engine->getConfig()]); Logger::debug("[{}] card: {} reader({}): {} {}: {} step: {}", [ $context->isOperatorCard ? '人员卡' : '内镜卡', $context->cardNo, empty($context->readerNo) ? '(未绑定)' : $context->readerNo, $context->readerNo, $context->isOperatorCard ? 'user' : 'endoscope', $context->isOperatorCard ? $context->operatorName : ($context->endoscopeName ?: $context->endoscopeId ?: '(未绑定)'), $context->currentStep ?: '(新流程)', ]); // 读卡器未绑定 if (empty($context->readerId)) { Logger::error('读卡器未绑定,放弃处理 card={}', [$context->cardNo]); $context->setError(VoiceMessage::READER_NOT_BOUND); $this->sendVoice($context); return $context; } // 如果是人员卡,记录操作员信息后直接返回,不走流程链 if ($context->isOperatorCard) { $mgr = OperatorSessionManager::getInstance(); $mgr->setOperator( $context->readerId, $context->operatorId, $context->operatorName, $context->operatorRfid ); $context->setError(VoiceMessage::PLEASE_SWIPE_ENDOSCOPE); $this->sendVoice($context); return $context; } // 如果内镜未绑定,返回错误 if (empty($context->endoscopeId)) { Logger::error('内镜未绑定,放弃处理 card={}', [$context->cardNo]); $context->setError(VoiceMessage::CARD_NOT_BOUND); $this->sendVoice($context); return $context; } // 内镜卡:从 OperatorSessionManager 补充操作员信息 $mgr = OperatorSessionManager::getInstance(); if ($mgr->hasOperator($context->readerId) || $context->hasOperator()) { if ($mgr->hasOperator($context->readerId)) { $op = $mgr->getOperator($context->readerId, $context->readerType); $context->operatorId = $op['id']; $context->operatorName = $op['name']; $context->operatorRfid = $op['rfid']; } } else { // 未刷人员卡 Logger::warn('未刷人员卡 reader={} card={}', [ $context->readerId, $context->cardNo, ]); // 处理这个错误 $context->setError(VoiceMessage::PLEASE_SWIPE_OPERATOR); $this->handleResult($context); return $context; } // 执行流程 $result = $this->engine->execute($context); // 处理结果 if ($isExecuteHandleResult) $this->handleResult($result); // 判断人员是否没有 if (empty($result->operatorId)) { Logger::error('[FlowProcessor] 结果集中人员不存在'); } return $result; } /** * 处理流程执行结果 */ protected function handleResult(ProcessContext $context): void { if ($context->success) { $repo = EctActionsRepository::new(); // 更新上一次的操作结束时间 $this->updateLastOperationEndTime($context, $repo); // 数据库操作 if ($context->needDatabaseOperation) { $this->saveToDatabase($context, $repo); } // 发送语音播报 Logger::debug('[FlowProcessor] 播报语音 voice={}', [$context->getFullVoice()]); $this->sendVoice($context); // WebSocket通知 if ($context->needWebSocketNotify) { Logger::debug('[FlowProcessor] 发送 WebSocket 通知 endoscope={} step={}', [ $context->endoscopeName, $context->currentStep, ]); $this->sendWebSocketNotification($context); } } else { // 执行失败,播报错误信息 Logger::warn('流程失败,播报错误 voice={} error={}', [ $context->getFullVoice(), $context->errorMessage, ]); $this->sendVoice($context); } } /** * 处理旧批次的结束时间更新 * * @param ProcessContext $context 上下文 * @param EctActionsRepository $actionsRepo 操作记录仓储 */ protected function updateLastOperationEndTime(ProcessContext $context, EctActionsRepository $actionsRepo): void { $lastAction = $context->previousAction; $oldActionStartTime = $lastAction->op_starttime; // 仅当旧批次未结束且流程类型不一致时更新 if ($lastAction->op_endtime === null && $lastAction->process_name != $context->readerType) { $oldBatchNo = $lastAction->op_batchno; $oldActionEndTime = date('Y-m-d H:i:s'); $oldDuration = strtotime($oldActionEndTime) - strtotime($oldActionStartTime); $actionsRepo->updateEndTime($oldBatchNo, $oldActionEndTime, $oldDuration); Logger::debug( "[更新] {$oldBatchNo} 批次中 {$lastAction->process_name} 流程的结束与耗时时间 {$oldActionEndTime} {$oldDuration} s" ); } } /** * 保存到数据库 */ protected function saveToDatabase(ProcessContext $context, EctActionsRepository $actionsRepo): void { $opuserType = $this->getOpusesType($context->operatorId); Logger::debug('[FlowProcessor] 写库 op={} step={} batch={}', [ $context->dbOperation, $context->currentStep, $context->batchNo, ]); try { $actionsRepo->saveFromContext($context, $opuserType); } catch (\Exception $e) { Logger::error('[FlowProcessor] 保存数据到数据库失败 context={} error={}', [ json_encode($context, JSON_UNESCAPED_UNICODE), $e->getMessage(), ]); } } /** * 发送语音播报 */ protected function sendVoice(string|ProcessContext $context): void { $voice = $context->getFullVoice(); if (is_string($context)) $voice = $context; Logger::info("语音播报:$voice"); // 语音播报内容已准备就绪,外部服务按需接入: // Windows: System.Speech / Linux: ekho / TTS服务 / 读卡器TCP指令 // 示例: VoiceService::speak($voice); } /** * 发送WebSocket通知 */ protected function sendWebSocketNotification(ProcessContext $context): void { // 通过 Webman Push 发送通知: // $api = new Api(...); // $api->trigger('wash-update', 'message', [ // 'endoscope_id' => $context->endoscopeId, // 'endoscope_name' => $context->endoscopeName, // 'current_step' => $context->currentStep, // 'process_type' => $context->processType, // 'voice' => $context->voiceMessage, // ]); } /** * 获取操作员类型(从 User 表查询 role_id) */ protected function getOpusesType(string $operatorId): int { if (empty($operatorId)) { return 1; // 默认洗消工 } try { $user = UserRepository::new()->getUser((int)$operatorId); return $user->role_id ?? 1; } catch (\Throwable $e) { Logger::debug('[FlowProcessor] 获取操作员类型失败,使用默认值 operatorId={} error={}', [ $operatorId, $e->getMessage(), ]); return 1; // 默认洗消工 } } /** * 更新配置 */ public function updateConfig(ProcessConfig $config): self { $this->engine->updateConfig($config); return $this; } /** * 创建处理器(静态工厂) */ public static function create(?ProcessConfig $config = null): self { return new self($config); } /** * 获取流程配置 */ public function getConfig(): ProcessConfig { return $this->engine->getConfig(); } }