[stepName => seconds]] */ private array $dbDurationCache = []; public function __construct(TimeValidationConfig $config) { parent::__construct([]); $this->timeValidationConfig = $config; Logger::debug("TimeValidationStrategy 初始化完成"); } /** * 从数据库获取步骤时长(按流程类型精确匹配) * 优先级:数据库记录 > 构造时配置 > 内置 fallback */ protected function getDurationFromDb(string $stepCode, string $processType): int { if (empty($processType)) { return $this->timeValidationConfig->getDuration($stepCode, $processType); } // 整批加载并缓存,减少查询次数 if (!isset($this->dbDurationCache[$processType])) { $this->dbDurationCache[$processType] = ProcessDurationRepository::new() ->getDurationsByProcessType($processType); } $dbValue = $this->dbDurationCache[$processType][$stepCode] ?? null; if ($dbValue !== null) { return $dbValue; } // fallback:返回配置中的默认值 return $this->timeValidationConfig->getDuration($stepCode); } /** * 执行时间验证 * 验证上一步骤(currentStep)的时长是否达标 */ protected function doExecute(ProcessContext $context, ProcessNodeInterface $node): ProcessContext { // 验证的是上一步骤(currentStep)的时长,而不是当前节点 $stepCode = $context->getCurrentStep(); $processType = $context->getProcessType(); Logger::debug("开始执行时间验证策略,步骤:{$stepCode},流程类型:{$processType}"); $configDuration = $this->timeValidationConfig->getDuration($stepCode, $processType); Logger::debug("步骤:{$stepCode},流程类型:{$processType},配置时长:{$configDuration}s"); if ($configDuration > 0) { // 配置中有明确时长,直接使用 $requiredDuration = $configDuration; } else { Logger::debug("步骤:{$stepCode},流程类型:{$processType},无配置时长,从数据库查询"); // 从数据库按流程类型精确查询 $requiredDuration = $this->getDurationFromDb($stepCode, $processType); if ($requiredDuration > 0) { $context = $context->createModifyBuilder()->setStepDuration($stepCode, $requiredDuration)->build(); } else { // 最后使用上下文已缓存值 $requiredDuration = $context->getStepDuration($stepCode); } } // 无需时间验证,直接放行 if ($requiredDuration <= 0) { return $context; } // 获取上次该步骤的操作时间 $duration = $context->getDuration(); // 没有上次记录(第一次操作),允许通过 if (empty($duration)) { Logger::debug("步骤:{$stepCode},流程类型:{$processType},无上次记录,允许通过"); return $context; } if ($duration < $requiredDuration) { Logger::debug("[{$stepCode}] 时间不足,剩余: {}s", [$requiredDuration - $duration]); if (Config::getInstance()->blockMode){ $voice = VoiceMessage::NOT_ENOUGH_TIME->value; $voice = str_replace('{step}', $stepCode, $voice); $voice = str_replace('{time}', $requiredDuration - $duration, $voice); $context = $context->createModifyBuilder()->setCustomError($voice)->build(); } return $context; } return $context; } /** * 判断策略是否适用 * 只有在 timeValidationConfig 中登记的步骤才参与时间验证 * 验证的是上一步骤(currentStep)的时长 */ public function isApplicable(ProcessContext $context, ProcessNodeInterface $node): bool { if ($context->getCurrentStep() != $context->getPreviousAction()?->process_name) return false; // 检查的是 currentStep(上一步骤)是否有时间配置 if (!$this->timeValidationConfig->hasStep($context->getCurrentStep(), $context->getProcessType())) return false; return true; } /** * 获取策略名称 */ public function getName(): string { return '时间验证策略'; } /** * 手动设置步骤时长(覆盖 fallback,不影响数据库缓存) */ public function setStepDuration(string $stepCode, int $seconds): self { $this->timeValidationConfig->setDuration($stepCode, $seconds); return $this; } }