134 lines
4.3 KiB
Python
134 lines
4.3 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
服务调用节点处理器
|
|
处理外部 HTTP 服务调用
|
|
"""
|
|
import json
|
|
import logging
|
|
import re
|
|
from typing import Dict, Optional, TYPE_CHECKING
|
|
|
|
import httpx
|
|
|
|
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 ServiceHandler(BaseNodeHandler):
|
|
"""
|
|
服务调用节点处理器
|
|
|
|
支持:
|
|
- HTTP 方法: GET/POST/PUT/DELETE/PATCH
|
|
- 请求头配置
|
|
- 参数变量替换
|
|
- 重试机制
|
|
- 失败处理策略
|
|
"""
|
|
|
|
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)
|
|
|
|
url = node_config.get('url', '')
|
|
method = node_config.get('method', 'POST')
|
|
headers = {h['key']: h['value'] for h in node_config.get('headers', []) if h.get('key')}
|
|
params = node_config.get('params', '')
|
|
body = node_config.get('body', '')
|
|
timeout = node_config.get('timeout', 30)
|
|
retry_count = node_config.get('retryCount', 0)
|
|
fail_action = node_config.get('failAction', 'stop')
|
|
result_variable = node_config.get('resultVariable', '')
|
|
|
|
logger.info(f"服务调用节点 - {method} {url}")
|
|
|
|
# 更新实例当前节点
|
|
await self.update_instance_node(context, node)
|
|
|
|
success = False
|
|
response_data = None
|
|
error_message = ''
|
|
|
|
# 尝试调用服务
|
|
async with httpx.AsyncClient() as client:
|
|
for attempt in range(retry_count + 1):
|
|
try:
|
|
# 解析参数和请求体(支持变量替换)
|
|
parsed_params = self._parse_params(params, context.form_data)
|
|
parsed_body = self._parse_params(body, context.form_data)
|
|
|
|
response = await client.request(
|
|
method=method,
|
|
url=url,
|
|
headers=headers,
|
|
params=parsed_params if method == 'GET' else None,
|
|
json=parsed_body if method != 'GET' and parsed_body else None,
|
|
timeout=timeout,
|
|
)
|
|
|
|
response.raise_for_status()
|
|
response_data = response.json() if response.text else {}
|
|
success = True
|
|
break
|
|
|
|
except Exception as e:
|
|
error_message = str(e)
|
|
logger.warning(f"服务调用失败 (尝试 {attempt + 1}/{retry_count + 1}): {e}")
|
|
|
|
# 记录日志
|
|
await self.create_log(
|
|
context, node, 'service_call',
|
|
comment=f"{'成功' if success else '失败'}: {method} {url}",
|
|
extra_data={
|
|
'url': url,
|
|
'method': method,
|
|
'success': success,
|
|
'response': response_data,
|
|
'error': error_message,
|
|
},
|
|
)
|
|
|
|
if success:
|
|
# 存储结果到流程变量
|
|
if result_variable and response_data:
|
|
context.form_data[result_variable] = response_data
|
|
await self.advance_to_next(context, node)
|
|
else:
|
|
# 处理失败
|
|
if fail_action == 'continue':
|
|
logger.warning(f"服务调用失败,继续流程")
|
|
await self.advance_to_next(context, node)
|
|
elif fail_action == 'stop':
|
|
logger.error(f"服务调用失败,终止流程")
|
|
await self.engine._end_instance(context, 'rejected')
|
|
|
|
def _parse_params(self, params_str: str, form_data: Dict) -> Optional[Dict]:
|
|
"""
|
|
解析服务调用参数,支持变量替换
|
|
|
|
变量格式: ${field_name}
|
|
"""
|
|
if not params_str:
|
|
return None
|
|
|
|
# 替换变量
|
|
def replace_var(match):
|
|
var_name = match.group(1)
|
|
return str(form_data.get(var_name, ''))
|
|
|
|
replaced = re.sub(r'\$\{(\w+)\}', replace_var, params_str)
|
|
|
|
try:
|
|
return json.loads(replaced)
|
|
except json.JSONDecodeError:
|
|
return None
|