Files
tcpserver-flow/app/flow/strategies/VoiceGenerationStrategy.php
T
zimoyin 3471deb3f1 ai-refactor(flow): 调整抽象流程节点实现和依赖路径
- 修改 AbstractProcessNode 中 ProcessContext 的命名空间引用为 app\flow\context\ProcessContext
- 引入 app\flow\vo\CanHandleResult 用于节点处理结果表示
- 更新前置策略执行后对成功状态的判断,改为调用 isSuccess() 方法
- 增加日志记录细节,便于调试策略执行中断时的错误信息
- 优化代码注释,提升代码可读性和维护性
2026-03-11 02:40:45 +08:00

172 lines
5.3 KiB
PHP

<?php
namespace app\flow\strategies;
use app\flow\config\VoiceTemplatesConfig;
use app\flow\exception\IllegalUsageException;
use app\flow\context\ProcessContext;
use app\flow\nodes\ProcessNodeInterface;
use app\utils\Logger;
/**
* 语音生成策略
* 根据配置生成语音播报内容
*/
class VoiceGenerationStrategy extends AbstractStrategy
{
protected string $phase = 'after';
protected VoiceTemplatesConfig $voiceTemplatesConfig;
public function __construct(VoiceTemplatesConfig $config)
{
parent::__construct([]);
$this->voiceTemplatesConfig = $config;
Logger::debug("VoiceGenerationStrategy 初始化完成");
}
/**
* 执行语音生成
*/
protected function doExecute(ProcessContext $context, ProcessNodeInterface $node): ProcessContext
{
// 如果存在流程中自定义语音,就直接输出
if (!empty($context->getVoice()->message)) {
Logger::warn(
"流程中存在自定义语音或存在多次设置语音,语音应该在 VoiceGenerationStrategy 策略中配置,否则不能拦截与自定义配置",
new IllegalUsageException("In the existing process, there is custom voice, which should be configured in the VoiceGenerationStrategy strategy; otherwise, it cannot be intercepted and customized")
);
return $context->builder()->voiceMessage($context->getVoice()->message)->build();
}
// 如果已经有错误,生成错误语音
// 否则生成正常流程语音
$voice = !$context->isSuccess() ? $this->generateErrorVoice($context) : $this->generateNormalVoice($context, $node);
// 应用语音模板
foreach ($context->getVoice()->templateParams as $key => $val) {
$replaceVal = match (true) {
is_array($val) => implode(',', $val),
is_bool($val) => $val ? 'true' : 'false',
is_null($val) => '',
default => (string) $val
};
$voice = str_replace('{' . $key . '}', $replaceVal, $voice);
}
Logger::debug("应用语音模板后的内容: {$voice}");
return $context->builder()->voiceMessage($voice)->build();
}
/**
* 生成正常流程语音
*/
protected function generateNormalVoice(ProcessContext $context, ProcessNodeInterface $node): string
{
$stepCode = $node->getCode();
$stepName = $node->getName();
$processType = $context->getProcessType();
// 根据流程类型选择模板
$templateKey = $this->getTemplateKey($processType, $stepCode, $context);
$templates = $this->voiceTemplatesConfig->getTemplates($templateKey);
if (empty($templates)) {
$templates = $this->voiceTemplatesConfig->normalWash;
}
// 优先使用自定义步骤语音(直接存储在对应模板key中)
$voice = $templates[$stepCode] ?? $stepCode . '完成';
// 添加提醒信息
$remind = $this->getRemindMessage($context);
if ($remind) {
$voice .= $remind;
}
return $voice;
}
/**
* 生成错误语音
*/
protected function generateErrorVoice(ProcessContext $context): string
{
$errorMessage = $context->getVoice()->errorMessage;
$errorTemplates = $this->voiceTemplatesConfig->voiceMessage;
$errorMsg = $errorTemplates[$errorMessage->name] ?? $errorMessage->value;
if (empty($errorMsg)) {
$errorMsg = $context->getVoice()->message;
Logger::debug("错误信息配置未命中,使用自定义语音: {$errorMsg}");
} else {
Logger::debug("错误信息配置命中: {$errorMsg}");
}
if (empty($errorMsg)) {
Logger::error("配置文件与枚举信息中未找匹配到错误信息");
}
return $errorMsg ?: '刷卡错误';
}
/**
* 流程类型到模板键的映射
*/
protected const array PROCESS_TYPE_MAP = [
'机洗' => 'machine_wash',
'机洗(晨洗)' => 'machine_wash',
'机洗(加强)' => 'machine_wash',
'测漏' => 'leak_test',
'存储' => 'storage',
];
/**
* 获取模板键
*/
protected function getTemplateKey(string $processType, string $stepCode, ProcessContext $context): string
{
// 晨洗流程中的清洗/机洗步骤
if ($context->getMorningWash()->needMorningWash) {
return 'morning_wash';
}
// 从映射表中获取,默认返回 normal_wash
return self::PROCESS_TYPE_MAP[$processType] ?? 'normal_wash';
}
/**
* 获取提醒信息
*/
protected function getRemindMessage(ProcessContext $context): string
{
$reminds = [];
if ($context->isLeakTestRemindNeeded()) {
$reminds[] = ',清洗开始前,请测漏';
}
if ($context->isStorageRemindNeeded()) {
$reminds[] = ',未登记取出';
}
return implode('', $reminds);
}
/**
* 判断策略是否适用
*/
public function isApplicable(ProcessContext $context, ProcessNodeInterface $node): bool
{
return true;
}
/**
* 获取策略名称
*/
public function getName(): string
{
return '语音生成策略';
}
}