ai-refactor(flow): 调整抽象流程节点实现和依赖路径
- 修改 AbstractProcessNode 中 ProcessContext 的命名空间引用为 app\flow\context\ProcessContext - 引入 app\flow\vo\CanHandleResult 用于节点处理结果表示 - 更新前置策略执行后对成功状态的判断,改为调用 isSuccess() 方法 - 增加日志记录细节,便于调试策略执行中断时的错误信息 - 优化代码注释,提升代码可读性和维护性
This commit is contained in:
@@ -0,0 +1,623 @@
|
||||
<?php
|
||||
|
||||
namespace app\flow\context;
|
||||
|
||||
use app\config\Config;
|
||||
use app\flow\config\ProcessConfig;
|
||||
use app\flow\vo\EndoscopeInfo;
|
||||
use app\flow\vo\ExecutionResult;
|
||||
use app\flow\vo\MorningWashStatus;
|
||||
use app\flow\vo\OperatorInfo;
|
||||
use app\flow\vo\ProcessStatus;
|
||||
use app\flow\vo\ReaderInfo;
|
||||
use app\flow\vo\ReminderStatus;
|
||||
use app\flow\vo\StorageStatus;
|
||||
use app\flow\vo\VoiceState;
|
||||
use app\flow\enum\DbOperationType;
|
||||
use app\flow\enum\VoiceMessage;
|
||||
use app\model\EctActions;
|
||||
use app\net\PacketContext;
|
||||
use app\repository\EctActionsRepository;
|
||||
use app\repository\EndoscopeRepository;
|
||||
use app\repository\ReaderRepository;
|
||||
use app\repository\UserRepository;
|
||||
use app\utils\Logger;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 流程上下文构造器
|
||||
* 负责从各种数据源加载数据并构建不可变的 ProcessContext 实例
|
||||
*/
|
||||
class ProcessContextBuilder
|
||||
{
|
||||
private Config $config;
|
||||
|
||||
// ==================== 值对象 ====================
|
||||
private EndoscopeInfo $endoscope;
|
||||
private ReaderInfo $reader;
|
||||
private OperatorInfo $operator;
|
||||
private StorageStatus $storage;
|
||||
private MorningWashStatus $morningWash;
|
||||
private VoiceState $voice;
|
||||
private ExecutionResult $result;
|
||||
private ProcessStatus $processStatus;
|
||||
private ReminderStatus $reminder;
|
||||
|
||||
// ==================== 原始数据 ====================
|
||||
private ?PacketContext $packetContext = null;
|
||||
private ?ProcessConfig $engineConfig = null;
|
||||
private array $rawData = [];
|
||||
|
||||
// ==================== 标记 ====================
|
||||
private bool $isOperatorCard = false;
|
||||
private array $stepDurations = [];
|
||||
|
||||
// 晨洗开始时间(用于加载过程)
|
||||
private string $morningStartTime = '00:00:00';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user