483 lines
14 KiB
PHP
483 lines
14 KiB
PHP
<?php
|
|
|
|
namespace tests\flow;
|
|
|
|
use app\flow\config\ProcessConfig;
|
|
use app\flow\context\bean\EndoscopeInfo;
|
|
use app\flow\context\bean\ExecutionResult;
|
|
use app\flow\context\bean\MorningWashStatus;
|
|
use app\flow\context\bean\OperatorInfo;
|
|
use app\flow\context\bean\ProcessStatus;
|
|
use app\flow\context\bean\ReaderInfo;
|
|
use app\flow\context\bean\ReminderStatus;
|
|
use app\flow\context\bean\StorageStatus;
|
|
use app\flow\context\bean\VoiceState;
|
|
use app\flow\context\ProcessContext;
|
|
use app\model\EctActions;
|
|
|
|
/**
|
|
* 虚拟上下文构建器
|
|
*
|
|
* 用于在测试中创建 ProcessContext,无需数据库依赖。
|
|
* 支持从 JSON 环境配置文件加载测试数据。
|
|
*/
|
|
class VirtualContextBuilder
|
|
{
|
|
private array $environment;
|
|
private ?ProcessConfig $engineConfig = null;
|
|
|
|
// 值对象
|
|
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 bool $isOperatorCard = false;
|
|
private array $stepDurations = [];
|
|
private array $rawData = [];
|
|
|
|
/**
|
|
* 构造函数
|
|
*
|
|
* @param string|null $envPath 环境配置文件路径,null 时使用默认配置
|
|
*/
|
|
public function __construct(?string $envPath = null)
|
|
{
|
|
$this->loadEnvironment($envPath);
|
|
$this->initDefaults();
|
|
}
|
|
|
|
/**
|
|
* 加载环境配置
|
|
*/
|
|
private function loadEnvironment(?string $envPath): void
|
|
{
|
|
$defaultPath = __DIR__ . '/../resources/default_environment.json';
|
|
$path = $envPath ?? $defaultPath;
|
|
|
|
if (!file_exists($path)) {
|
|
throw new \RuntimeException("环境配置文件不存在: {$path}");
|
|
}
|
|
|
|
$content = file_get_contents($path);
|
|
$this->environment = json_decode($content, true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
throw new \RuntimeException("环境配置文件解析失败: " . json_last_error_msg());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化默认值
|
|
*/
|
|
private function initDefaults(): void
|
|
{
|
|
$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();
|
|
|
|
// 从环境配置加载步骤时长
|
|
if (isset($this->environment['stepDurations'])) {
|
|
$this->stepDurations = $this->environment['stepDurations'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建构建器实例(静态工厂)
|
|
*/
|
|
public static function create(?string $envPath = null): self
|
|
{
|
|
return new self($envPath);
|
|
}
|
|
|
|
// ==================== 便捷设置方法 ====================
|
|
|
|
/**
|
|
* 设置内镜(从环境配置名称)
|
|
*/
|
|
public function endoscope(string $name): self
|
|
{
|
|
$data = $this->environment['endoscopes'][$name] ?? null;
|
|
if ($data === null) {
|
|
throw new \InvalidArgumentException("未找到内镜配置: {$name}");
|
|
}
|
|
|
|
$this->endoscope = new EndoscopeInfo(
|
|
id: $data['id'],
|
|
name: $data['name'],
|
|
cardNo: $data['cardNo'],
|
|
type: $data['type']
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置自定义内镜
|
|
*/
|
|
public function customEndoscope(string $id, string $name, string $cardNo, string $type = ''): self
|
|
{
|
|
$this->endoscope = new EndoscopeInfo(
|
|
id: $id,
|
|
name: $name,
|
|
cardNo: $cardNo,
|
|
type: $type
|
|
);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置读卡器(从环境配置名称/类型)
|
|
*/
|
|
public function reader(string $typeOrName): self
|
|
{
|
|
$data = $this->environment['readers'][$typeOrName] ?? null;
|
|
if ($data === null) {
|
|
throw new \InvalidArgumentException("未找到读卡器配置: {$typeOrName}");
|
|
}
|
|
|
|
$this->reader = new ReaderInfo(
|
|
no: $data['no'],
|
|
type: $data['type'],
|
|
id: $data['id']
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置自定义读卡器
|
|
*/
|
|
public function customReader(string $no, string $type, string $id = ''): self
|
|
{
|
|
$this->reader = new ReaderInfo(no: $no, type: $type, id: $id);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置操作员(从环境配置名称)
|
|
*/
|
|
public function operator(string $name): self
|
|
{
|
|
$data = $this->environment['operators'][$name] ?? null;
|
|
if ($data === null) {
|
|
throw new \InvalidArgumentException("未找到操作员配置: {$name}");
|
|
}
|
|
|
|
$this->operator = new OperatorInfo(
|
|
id: $data['id'],
|
|
name: $data['name'],
|
|
rfid: $data['rfid']
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置自定义操作员
|
|
*/
|
|
public function customOperator(string $id, string $name, string $rfid = ''): self
|
|
{
|
|
$this->operator = new OperatorInfo(id: $id, name: $name, rfid: $rfid);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置当前步骤
|
|
*/
|
|
public function currentStep(string $step): self
|
|
{
|
|
// 自动从环境配置加载该步骤的时长(如果未手动设置)
|
|
$duration = $this->processStatus->duration;
|
|
if ($duration === null && isset($this->stepDurations[$step])) {
|
|
$duration = $this->stepDurations[$step];
|
|
}
|
|
|
|
$this->processStatus = new ProcessStatus(
|
|
currentStep: $step,
|
|
processType: $this->processStatus->processType,
|
|
batchNo: $this->processStatus->batchNo,
|
|
actionStartTime: $this->processStatus->actionStartTime,
|
|
duration: $duration,
|
|
previousAction: $this->processStatus->previousAction
|
|
);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置批次号
|
|
*/
|
|
public function batchNo(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 processType(string $type): self
|
|
{
|
|
$this->processStatus = new ProcessStatus(
|
|
currentStep: $this->processStatus->currentStep,
|
|
processType: $type,
|
|
batchNo: $this->processStatus->batchNo,
|
|
actionStartTime: $this->processStatus->actionStartTime,
|
|
duration: $this->processStatus->duration,
|
|
previousAction: $this->processStatus->previousAction
|
|
);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置操作时长(秒)
|
|
*/
|
|
public function setDuration(?int $seconds): self
|
|
{
|
|
$this->processStatus = new ProcessStatus(
|
|
currentStep: $this->processStatus->currentStep,
|
|
processType: $this->processStatus->processType,
|
|
batchNo: $this->processStatus->batchNo,
|
|
actionStartTime: $this->processStatus->actionStartTime,
|
|
duration: $seconds,
|
|
previousAction: $this->processStatus->previousAction
|
|
);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置操作开始时间
|
|
*/
|
|
public function actionStartTime(string $time): self
|
|
{
|
|
$this->processStatus = new ProcessStatus(
|
|
currentStep: $this->processStatus->currentStep,
|
|
processType: $this->processStatus->processType,
|
|
batchNo: $this->processStatus->batchNo,
|
|
actionStartTime: $time,
|
|
duration: $this->processStatus->duration,
|
|
previousAction: $this->processStatus->previousAction
|
|
);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置上一条操作记录(虚拟)
|
|
*/
|
|
public function setPreviousAction(string $processName, string $batchNo = '', ?string $opEndtime = null): self
|
|
{
|
|
$action = new EctActions();
|
|
$action->process_name = $processName;
|
|
$action->op_batchno = $batchNo ?: $this->processStatus->batchNo;
|
|
$action->op_starttime = date('Y-m-d H:i:s', time() - 60);
|
|
$action->op_endtime = $opEndtime;
|
|
$action->action_type = $this->mapActionType($this->processStatus->processType);
|
|
$action->action_type_name = $this->processStatus->processType;
|
|
|
|
$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;
|
|
}
|
|
|
|
/**
|
|
* 映射流程类型到 action_type 数字
|
|
*/
|
|
private function mapActionType(string $processType): int
|
|
{
|
|
return match ($processType) {
|
|
'手工洗' => 1,
|
|
'机洗' => 2,
|
|
'手工洗(加强)' => 3,
|
|
'机洗(加强)' => 4,
|
|
'手工洗(晨洗)' => 5,
|
|
'机洗(晨洗)' => 6,
|
|
'晨洗' => 7,
|
|
default => 0,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 设置晨洗状态 - 需要晨洗
|
|
*/
|
|
public function needMorningWash(int $todayRecords = 0): self
|
|
{
|
|
$startTime = $this->environment['config']['morningWashStartTime'] ?? '06:00:00';
|
|
$this->morningWash = MorningWashStatus::required($startTime, $todayRecords);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置晨洗状态 - 已完成晨洗
|
|
*/
|
|
public function morningWashCompleted(): self
|
|
{
|
|
$startTime = $this->environment['config']['morningWashStartTime'] ?? '06:00:00';
|
|
$this->morningWash = MorningWashStatus::completed($startTime);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置晨洗状态 - 不需要晨洗
|
|
*/
|
|
public function noMorningWash(): self
|
|
{
|
|
$this->morningWash = MorningWashStatus::notRequired();
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置存储状态 - 在库中
|
|
*/
|
|
public function inStorage(?string $inTime = null): self
|
|
{
|
|
$this->storage = StorageStatus::inStorage($inTime ?? date('Y-m-d H:i:s', time() - 3600));
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置存储状态 - 已出库
|
|
*/
|
|
public function outOfStorage(): self
|
|
{
|
|
$this->storage = StorageStatus::outOfStorage();
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置为人员卡
|
|
*/
|
|
public function asOperatorCard(): self
|
|
{
|
|
$this->isOperatorCard = true;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置流程引擎配置
|
|
*/
|
|
public function withEngineConfig(ProcessConfig $config): self
|
|
{
|
|
$this->engineConfig = $config;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 设置步骤时长
|
|
*/
|
|
public function setStepDuration(string $stepCode, int $duration): self
|
|
{
|
|
$this->stepDurations[$stepCode] = $duration;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 新流程开始(清空当前步骤,生成新批次号)
|
|
*/
|
|
public function newProcess(): self
|
|
{
|
|
$machineId = $this->environment['config']['machineId'] ?? '01';
|
|
$batchNo = date('Ymd') . $machineId . str_pad(rand(1, 9999), 4, '0', STR_PAD_LEFT);
|
|
|
|
$this->processStatus = new ProcessStatus(
|
|
currentStep: '',
|
|
processType: '',
|
|
batchNo: $batchNo,
|
|
actionStartTime: date('Y-m-d H:i:s'),
|
|
duration: 0,
|
|
previousAction: null
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 模拟结束状态
|
|
*/
|
|
public function finished(): self
|
|
{
|
|
return $this->currentStep('结束');
|
|
}
|
|
|
|
// ==================== 构建方法 ====================
|
|
|
|
/**
|
|
* 构建 ProcessContext
|
|
*/
|
|
public function build(): 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: null,
|
|
engineConfig: $this->engineConfig,
|
|
rawData: $this->rawData,
|
|
isOperatorCard: $this->isOperatorCard,
|
|
stepDurations: $this->stepDurations,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 获取当前环境配置
|
|
*/
|
|
public function getEnvironment(): array
|
|
{
|
|
return $this->environment;
|
|
}
|
|
|
|
/**
|
|
* 获取读卡器配置列表
|
|
*/
|
|
public function getAvailableReaders(): array
|
|
{
|
|
return array_keys($this->environment['readers'] ?? []);
|
|
}
|
|
|
|
/**
|
|
* 获取内镜配置列表
|
|
*/
|
|
public function getAvailableEndoscopes(): array
|
|
{
|
|
return array_keys($this->environment['endoscopes'] ?? []);
|
|
}
|
|
|
|
/**
|
|
* 获取操作员配置列表
|
|
*/
|
|
public function getAvailableOperators(): array
|
|
{
|
|
return array_keys($this->environment['operators'] ?? []);
|
|
}
|
|
}
|