Files
ai-agent-admin/backend-fastapi/online_dev/workflow/engine/handlers/delay_handler.py
T

144 lines
4.3 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
延时节点处理器
处理流程延时等待
"""
import logging
from datetime import datetime, timedelta
from typing import Dict, TYPE_CHECKING
from online_dev.workflow.engine.handlers.base_handler import BaseNodeHandler
if TYPE_CHECKING:
from online_dev.workflow.engine.base import ExecutionContext
logger = logging.getLogger(__name__)
class DelayNodeHandler(BaseNodeHandler):
"""
延时节点处理器
支持延时单位:
- minute: 分钟
- hour: 小时
- day: 天
- workday: 工作日(跳过周六日)
"""
async def execute(self, context: 'ExecutionContext', node: Dict) -> None:
"""
执行延时节点:计算到期时间,注册定时任务
"""
node_id = self.get_node_id(node)
node_name = self.get_node_name(node)
node_config = self.get_node_config(node)
duration = node_config.get('duration', 1)
unit = node_config.get('unit', 'hour')
unit_names = {
'minute': '分钟',
'hour': '小时',
'day': '',
'workday': '工作日',
}
unit_name = unit_names.get(unit, unit)
logger.info(f"延时节点 - 等待 {duration} {unit_name}")
# 更新实例当前节点
await self.update_instance_node(context, node)
# 计算到期时间
delay_until = self._calculate_delay_until(duration, unit)
# 更新实例延时状态
context.instance.delay_node_id = node_id
context.instance.delay_until = delay_until
context.db.add(context.instance)
await context.db.flush()
# 记录延时开始日志
await self.create_log(
context, node, 'delay_start',
comment=f'开始延时等待 {duration} {unit_name},预计 {delay_until.strftime("%Y-%m-%d %H:%M:%S")} 恢复',
extra_data={
'duration': duration,
'unit': unit,
'delay_until': delay_until.isoformat(),
},
operator_id='',
)
# 注册定时任务
from online_dev.workflow.engine.delay_callback import _register_delay_job
instance_id = str(context.instance.id)
success = await _register_delay_job(instance_id, node_id, delay_until)
if not success:
# 调度器不可用时,直接跳过延时继续推进(降级处理)
logger.warning(f"调度器不可用,延时节点直接跳过: instance={instance_id}, node={node_id}")
# 清除延时状态
context.instance.delay_node_id = ""
context.instance.delay_until = None
context.db.add(context.instance)
await context.db.flush()
await self.create_log(
context, node, 'delay_skip',
comment='调度器不可用,延时跳过',
operator_id='',
)
await self.advance_to_next(context, node)
@staticmethod
def _calculate_delay_until(duration: int, unit: str) -> datetime:
"""
计算延时到期时间
Args:
duration: 延时时长
unit: 延时单位 (minute/hour/day/workday)
Returns:
到期时间
"""
now = datetime.now()
if unit == 'minute':
return now + timedelta(minutes=duration)
elif unit == 'hour':
return now + timedelta(hours=duration)
elif unit == 'day':
return now + timedelta(days=duration)
elif unit == 'workday':
return DelayNodeHandler._add_workdays(now, duration)
else:
# 默认按小时处理
return now + timedelta(hours=duration)
@staticmethod
def _add_workdays(start: datetime, days: int) -> datetime:
"""
添加工作日(跳过周六日)
Args:
start: 起始时间
days: 工作日天数
Returns:
目标时间(保持原始时分秒)
"""
current = start
added = 0
while added < days:
current += timedelta(days=1)
# weekday(): 0=周一, 5=周六, 6=周日
if current.weekday() < 5:
added += 1
return current