ai-refactor(flow): 调整抽象流程节点实现和依赖路径

- 修改 AbstractProcessNode 中 ProcessContext 的命名空间引用为 app\flow\context\ProcessContext
- 引入 app\flow\vo\CanHandleResult 用于节点处理结果表示
- 更新前置策略执行后对成功状态的判断,改为调用 isSuccess() 方法
- 增加日志记录细节,便于调试策略执行中断时的错误信息
- 优化代码注释,提升代码可读性和维护性
This commit is contained in:
zimoyin
2026-03-11 02:40:45 +08:00
parent d5991813a6
commit 3471deb3f1
42 changed files with 1950 additions and 4418 deletions
+4
View File
@@ -83,6 +83,10 @@ class Config
*/
public bool $blockMode {
get => $this->blockMode;
set(bool $value){
Logger::error("禁止修改热修改阻断模式,这只是用于测试方法");
$this->blockMode = $value;
}
}
/**
+5
View File
@@ -0,0 +1,5 @@
模拟发包
内镜未刷取出就操作
刷两次代表加强洗
+2 -2
View File
@@ -138,7 +138,7 @@ class FlowProcessor
$this->updateLastOperationEndTime($context, $repo);
// 数据库操作
if ($context->needDatabaseOperation()) {
if ($context->isDatabaseOperationNeeded()) {
$this->saveToDatabase($context, $repo);
}
@@ -147,7 +147,7 @@ class FlowProcessor
$this->sendVoice($context);
// WebSocket通知
if ($context->needWebSocketNotify()) {
if ($context->isWebSocketNotifyNeeded()) {
Logger::debug('[FlowProcessor] 发送 WebSocket 通知 endoscope={} step={}', [
$context->getEndoscope()->name,
$context->getCurrentStep(),
+2
View File
@@ -3,6 +3,8 @@
namespace app\flow;
use app\flow\config\ProcessConfig;
use app\flow\context\ProcessContext;
use app\flow\enum\VoiceMessage;
use app\flow\nodes\MorningWashNode;
use app\flow\nodes\ProcessNodeInterface;
use app\flow\strategies\MorningWashStrategy;
+27 -8
View File
@@ -157,10 +157,11 @@ readonly class ProcessContext
return $this->stepDurations;
}
// ==================== 便捷查询方法 ====================
// ==================== 便捷查询方法(带数据来源标注) ====================
/**
* 获取当前步骤
* @return string 当前步骤名称 (来源: ProcessStatus)
*/
public function getCurrentStep(): string
{
@@ -169,6 +170,7 @@ readonly class ProcessContext
/**
* 获取流程类型
* @return string 流程类型 (来源: ProcessStatus)
*/
public function getProcessType(): string
{
@@ -177,6 +179,7 @@ readonly class ProcessContext
/**
* 获取批次号
* @return string 批次号 (来源: ProcessStatus)
*/
public function getBatchNo(): string
{
@@ -185,6 +188,7 @@ readonly class ProcessContext
/**
* 获取操作开始时间
* @return string 操作开始时间 (来源: ProcessStatus)
*/
public function getActionStartTime(): string
{
@@ -193,6 +197,7 @@ readonly class ProcessContext
/**
* 获取操作时长
* @return int|null 操作时长(秒)(来源: ProcessStatus)
*/
public function getDuration(): ?int
{
@@ -201,6 +206,7 @@ readonly class ProcessContext
/**
* 获取上一个操作记录
* @return \app\model\EctActions|null 上一个操作记录 (来源: ProcessStatus)
*/
public function getPreviousAction(): ?\app\model\EctActions
{
@@ -209,6 +215,7 @@ readonly class ProcessContext
/**
* 获取完整语音
* @return string 完整语音内容 (来源: VoiceState)
*/
public function getFullVoice(): string
{
@@ -217,6 +224,7 @@ readonly class ProcessContext
/**
* 流程是否成功
* @return bool 是否成功 (来源: ExecutionResult)
*/
public function isSuccess(): bool
{
@@ -225,22 +233,25 @@ readonly class ProcessContext
/**
* 是否需要操作数据库
* @return bool 是否需要数据库操作 (来源: ExecutionResult)
*/
public function needDatabaseOperation(): bool
public function isDatabaseOperationNeeded(): bool
{
return $this->result->needDatabaseOperation;
}
/**
* 是否需要WebSocket通知
* @return bool 是否需要通知 (来源: ExecutionResult)
*/
public function needWebSocketNotify(): bool
public function isWebSocketNotifyNeeded(): bool
{
return $this->result->needWebSocketNotify;
}
/**
* 获取数据库操作列表
* @return array 数据库操作列表 (来源: ExecutionResult)
*/
public function getDbOperations(): array
{
@@ -249,6 +260,7 @@ readonly class ProcessContext
/**
* 检查是否可以开始新流程
* @return bool 是否可以开始新流程 (来源: ProcessStatus)
*/
public function canStartNewProcess(): bool
{
@@ -257,6 +269,7 @@ readonly class ProcessContext
/**
* 检查是否已完成清洗流程
* @return bool 是否已完成 (来源: ProcessStatus)
*/
public function isWashProcessCompleted(): bool
{
@@ -265,6 +278,7 @@ readonly class ProcessContext
/**
* 是否有操作员
* @return bool 是否有有效操作员 (来源: OperatorInfo)
*/
public function hasOperator(): bool
{
@@ -281,40 +295,45 @@ readonly class ProcessContext
/**
* 是否需要增强洗
* @return bool 是否需要增强洗 (来源: ReminderStatus)
*/
public function needEnhanceWash(): bool
public function isEnhanceWashNeeded(): bool
{
return $this->reminder->needEnhanceWash;
}
/**
* 是否需要测漏提醒
* @return bool 是否需要测漏提醒 (来源: ReminderStatus)
*/
public function needLeakTestRemind(): bool
public function isLeakTestRemindNeeded(): bool
{
return $this->reminder->needLeakTestRemind;
}
/**
* 是否需要存储提醒
* @return bool 是否需要存储提醒 (来源: ReminderStatus)
*/
public function needStorageRemind(): bool
public function isStorageRemindNeeded(): bool
{
return $this->reminder->needStorageRemind;
}
/**
* 是否已测漏
* @return bool 是否已测漏 (来源: ReminderStatus)
*/
public function isLeakTestDone(): bool
public function isLeakTestDoneByReminder(): bool
{
return $this->reminder->leakTestDone;
}
/**
* 获取测漏结果
* @return string 测漏结果 (来源: ReminderStatus)
*/
public function getLeakTestResult(): string
public function getLeakTestResultByReminder(): string
{
return $this->reminder->leakTestResult;
}
+3 -19
View File
@@ -15,6 +15,7 @@ use app\flow\vo\StorageStatus;
use app\flow\vo\VoiceState;
use app\flow\enum\DbOperationType;
use app\flow\enum\VoiceMessage;
use app\flow\vo\BatchNo;
use app\model\EctActions;
use app\net\PacketContext;
use app\repository\EctActionsRepository;
@@ -316,28 +317,11 @@ class ProcessContextBuilder
/**
* 生成批次号
* @return string 批次号值
*/
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;
return BatchNo::generate($this->config->machineId)->value;
}
// ==================== 值对象设置方法 ====================
+1 -1
View File
@@ -201,7 +201,7 @@ abstract class AbstractProcessNode implements ProcessNodeInterface
* 1. 遍历所有已注册的策略
* 2. 筛选出阶段为 'before' 的策略
* 3. 依次执行策略的 execute 方法
* 4. 如果某个策略导致上下文错误(!$context->isSuccess()),立即中断后续策略执行
* 4. 如果某个策略导致上下文错误(!$context->isSuccessByResult()),立即中断后续策略执行
*
* 前置策略的应用:
* - 时间验证:检查步骤执行时间是否符合要求
+2 -2
View File
@@ -38,7 +38,7 @@ class CloseNode extends AbstractProcessNode
*/
public function canHandle(ProcessContext $context): CanHandleResult
{
if (!$context->isSuccess() || $context->needDatabaseOperation() || !empty($context->getVoice()->message)) {
if (!$context->isSuccess() || $context->isDatabaseOperationNeeded() || !empty($context->getVoice()->message)) {
return CanHandleResult::no();
}
return CanHandleResult::yes();
@@ -49,7 +49,7 @@ class CloseNode extends AbstractProcessNode
*/
protected function doHandle(ProcessContext $context): ProcessContext
{
if (!$context->isSuccess() || $context->needDatabaseOperation() || !empty($context->getVoice()->message)) {
if (!$context->isSuccess() || $context->isDatabaseOperationNeeded() || !empty($context->getVoice()->message)) {
return $context;
}
// 无节点命中
@@ -142,11 +142,11 @@ class VoiceGenerationStrategy extends AbstractStrategy
{
$reminds = [];
if ($context->needLeakTestRemind()) {
if ($context->isLeakTestRemindNeeded()) {
$reminds[] = ',清洗开始前,请测漏';
}
if ($context->needStorageRemind()) {
if ($context->isStorageRemindNeeded()) {
$reminds[] = ',未登记取出';
}
+1 -1
View File
@@ -19,7 +19,7 @@ readonly class ProcessStatus
public string $batchNo = '',
/** 操作开始时间 */
public string $actionStartTime = '',
/** 操作时长(秒) */
/** 操作时长(秒) 计算方式为上下文创建的时间减去上个操作的开始时间(此时上个操作的结束时间不存在,需要等待此次刷卡流程结束) */
public ?int $duration = null,
/** 上一个操作记录 */
public ?EctActions $previousAction = null,
+1 -1
View File
@@ -242,7 +242,7 @@ class EctActionsRepository extends BaseRepository
'process_name' => $context->getCurrentStep(),
'process_order' => $process_order + 1,
'op_morning' => $context->getMorningWash()->needMorningWash ? 1 : 0,
'op_enhance' => $context->needEnhanceWash() ? 1 : 0,
'op_enhance' => $context->isEnhanceWashNeeded() ? 1 : 0,
'reader_id' => (int)$context->getReader()->id ?: null,
'reader_no' => $context->getReader()->no ?: null,
'opuser_type' => $opuserType,