VWED_server/services/execution/block_executor.py
2025-05-12 15:43:21 +08:00

1302 lines
54 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
块执行器模块
提供任务块的执行能力
"""
import json
import uuid
from datetime import datetime
from typing import Dict, List, Any, Optional
from sqlalchemy import select, insert, text
from sqlalchemy.ext.asyncio import AsyncSession
import re
from data.models.blockrecord import VWEDBlockRecord
from data.models.taskdef import VWEDTaskDef
from data.models.taskrecord import VWEDTaskRecord
from data.session import get_async_session
from services.execution.task_context import TaskContext
from services.execution.handlers.base import get_block_handler
from utils.logger import get_logger
from data.enum.task_block_record_enum import TaskBlockRecordStatus
from data.enum.task_input_param_enum import TaskInputParamType, TaskInputParamVariables
# 获取日志记录器
logger = get_logger("services.execution.block_executor")
class BlockExecutor:
"""
块执行器类
负责执行任务中的各类块,并记录执行状态
"""
def __init__(self, task_context: TaskContext):
"""
初始化块执行器
Args:
task_context: 任务上下文
"""
self.task_context = task_context
self.is_canceled = False
def cancel(self) -> None:
"""
取消块执行
"""
self.is_canceled = True
self.task_context.mark_canceled()
async def execute_block(self, block: Dict[str, Any]) -> Dict[str, Any]:
"""
执行任务块
Args:
block: 任务块定义
Returns:
Dict[str, Any]: 执行结果
"""
# 检查是否已取消
if self.is_canceled or self.task_context.is_task_canceled():
return {
"success": False,
"message": "任务已被取消",
"block_id": block.get("id", "unknown")
}
if block is None:
return {
"success": False,
"message": "核心执行逻辑中的逻辑块不能为空!"
}
block_id = block.get("id", "unknown")
block_type = block.get("blockType", "unknown")
block_name = block.get("name", f"block-{block_id}")
logger.info(f"开始执行任务块: {block_name}, 类型: {block_type}, ID: {block_id}")
try:
# 设置当前块到上下文中
self.task_context.set_current_block(block_id, block_name)
# 创建块记录
block_record_id = await self._create_block_record(block)
# 记录执行路径
self.task_context.add_execution_path(block_id)
# 解析输入参数
input_params = await self._parse_input_params(block.get("inputParams", {}))
# 获取块处理器
handler = get_block_handler(block_type)
if not handler:
error_msg = f"不支持的块类型: {block_type}"
logger.error(error_msg)
await self._update_block_record(block_record_id, TaskBlockRecordStatus.FAILED, error_msg)
return {"success": False, "message": error_msg, "block_id": block_id}
# 执行块
try:
# 更新块状态为执行中
await self._update_block_record(block_record_id, TaskBlockRecordStatus.RUNNING, "执行中")
# 执行块处理器
result = await handler.execute(block, input_params, self.task_context)
# 检查结果
if result.get("success", False):
# 更新块状态为成功
await self._update_block_record(
block_record_id,
TaskBlockRecordStatus.SUCCESS,
"执行成功",
result.get("output", {})
)
return result
else:
# 更新块状态为失败
error_msg = result.get("message", "执行失败")
await self._update_block_record(block_record_id, TaskBlockRecordStatus.FAILED, error_msg)
# 设置错误信息
self.task_context.set_error(error_msg, block_id)
return result
except Exception as e:
# 处理执行异常
error_msg = f"块执行异常: {str(e)}"
logger.error(f"{block_id} ({block_type}) 执行异常: {str(e)}")
# 更新块状态为异常
await self._update_block_record(block_record_id, TaskBlockRecordStatus.FAILED, error_msg)
# 设置错误信息
self.task_context.set_error(error_msg, block_id)
return {
"success": False,
"message": error_msg,
"block_id": block_id,
"error": str(e)
}
except Exception as e:
logger.error(f"{block_id} ({block_type}) 处理失败: {str(e)}")
# 设置错误信息
self.task_context.set_error(f"块处理失败: {str(e)}", block_id)
return {
"success": False,
"message": f"块处理失败: {str(e)}",
"block_id": block_id,
"error": str(e)
}
async def execute_children(self, block: Dict[str, Any], branch: str = "default") -> Dict[str, Any]:
"""
执行子块
Args:
block: 父块定义
branch: 分支名称
Returns:
Dict[str, Any]: 执行结果
"""
try:
block_id = block.get('id', 'unknown')
logger.info(f"开始执行 execute_children 方法 - 块ID: {block_id}, 分支: {branch}")
# 获取指定分支下的子块
children = block.get("children", {}).get(branch, [])
if not children:
logger.info(f"{block_id}{branch} 分支下没有子块")
return {
"success": True,
"message": f"没有子块需要执行: {branch}",
"output": {
"executed": False,
"branch": branch
}
}
logger.info(f"开始执行块 {block_id}{branch} 分支下的 {len(children)} 个子块")
# 检查父块输出,判断是否需要跳过执行
parent_output = self.task_context.get_block_output(block_id)
# 检查是否有executed标志若为False则不执行任何子块
# 这主要用于条件块等流程控制,当条件不满足时避免执行子块
if parent_output and isinstance(parent_output, dict) and parent_output.get("executed") is False:
logger.info(f"{block_id} 的输出包含executed=False标志跳过执行子块")
# 虽然不执行子块,但仍然为每个子块创建记录并标记为"未执行"状态
results = []
for i, child_block in enumerate(children):
child_id = child_block.get('id', 'unknown')
child_type = child_block.get('blockType', 'unknown')
child_name = child_block.get('name', f"block-{child_id}")
logger.info(f"为未执行的子块创建记录 [{i+1}/{len(children)}] - 名称: {child_name}, ID: {child_id}, 类型: {child_type}")
# 创建子块记录
block_record_id = await self._create_block_record(child_block)
# 更新块记录状态为"未执行"
await self._update_block_record(
block_record_id,
TaskBlockRecordStatus.NOT_EXECUTED, # 使用1006表示"未执行"状态
"条件不满足,块未执行",
)
# 添加到结果集
results.append({
"child_id": child_id,
"success": True, # 标记为成功,因为这不是执行失败
"output": {"executed": False, "reason": "parent_condition_not_met"}
})
return {
"success": True,
"message": f"根据父块标志跳过子块执行: {branch}",
"output": {
"executed": False,
"branch": branch,
"results": results
}
}
# 获取跳转目标块ID
# 收集所有子块执行结果
results = []
print("children", children, "=============================================")
# 依次执行子块
for i, child_block in enumerate(children):
# 检查是否有中断或返回信号
if self.task_context.get_variable("__RETURN__", False):
logger.info(f"检测到返回信号,提前结束块 {block_id}{branch} 分支执行,已处理 {i}/{len(children)} 个子块")
break
child_id = child_block.get('id', 'unknown')
child_type = child_block.get('blockType', 'unknown')
child_name = child_block.get('name', f"b{child_id}")
skip_to_block_name = self.task_context.get_skip_to()
start_execution = skip_to_block_name is None # 如果没有设置跳转目标,就从头开始执行
# 检查是否是跳转目标块
if skip_to_block_name and child_name == skip_to_block_name:
logger.info(f"找到跳转目标块: {child_name}, ID: {child_id}")
start_execution = True
# 清除跳转标记,避免影响后续执行
self.task_context.clear_skip_to()
# 如果当前还未开始执行(处于跳过状态),为块创建"未执行"记录
if not start_execution:
logger.info(f"跳过执行子块 [{i+1}/{len(children)}] - 名称: {child_name}, ID: {child_id}, 类型: {child_type}")
# 创建子块记录但标记为未执行
block_record_id = await self._create_block_record(child_block)
# 设置为跳过状态
await self._update_block_record(
block_record_id,
TaskBlockRecordStatus.NOT_EXECUTED, # 未执行状态
"由于跳转设置,此块被跳过",
)
# 添加到结果集
results.append({
"child_id": child_id,
"success": True,
"output": {"executed": False, "reason": "skipped_by_jump"}
})
continue
logger.info(f"执行子块 [{i+1}/{len(children)}] - 名称: {child_name}, ID: {child_id}, 类型: {child_type}")
# 创建子块记录
block_record_id = await self._create_block_record(child_block)
logger.debug(f"为子块 {child_id} 创建记录, 记录ID: {block_record_id}")
try:
# 更新块记录状态为执行中
await self._update_block_record(block_record_id, TaskBlockRecordStatus.RUNNING, "执行中")
# 解析子块的输入参数
raw_params = child_block.get("inputParams", {})
# 解析参数
parsed_params = await self._parse_input_params(raw_params)
# 获取处理器
handler = get_block_handler(child_type)
if not handler:
error_msg = f"未找到块类型 {child_type} 的处理器"
logger.error(error_msg)
await self._update_block_record(
block_record_id,
TaskBlockRecordStatus.FAILED, # 失败
error_msg,
)
return {
"success": False,
"message": error_msg,
"block_id": child_id
}
# 执行块
logger.info(f"开始执行子块 {child_id} 的处理逻辑")
result = await handler.execute(child_block, parsed_params, self.task_context)
print("result", result, "=============================================")
# 检查是否执行成功
if not result.get("success", False):
if result.get("is_canceled", False):
logger.warning(f"子块 {child_id} 执行失败: {result.get('message', '未知错误')}")
# 更新块记录状态为取消
await self._update_block_record(
block_record_id,
TaskBlockRecordStatus.CANCELED, # 取消
result.get("message", "执行失败"),
)
return {
"success": False,
"message": f"子块 {child_id} 执行失败: {result.get('message', '未知错误')}",
"block_id": child_id,
"is_canceled": True,
"output": result.get("output", {})
}
else:
logger.error(f"子块 {child_id} 执行失败: {result.get('message', '未知错误')}")
# 更新块记录状态为失败
await self._update_block_record(
block_record_id,
TaskBlockRecordStatus.FAILED, # 失败
result.get("message", "执行失败"),
)
return {
"success": False,
"message": f"子块 {child_id} 执行失败: {result.get('message', '未知错误')}",
"block_id": child_id,
"output": result.get("output", {})
}
# 更新块记录状态为成功
await self._update_block_record(
block_record_id,
TaskBlockRecordStatus.SUCCESS, # 成功
"执行成功",
result.get("output", {})
)
# 添加到结果集
results.append({
"child_id": child_id,
"success": True,
"output": result.get("output", {})
})
except Exception as e:
# 处理执行过程中的异常
error_msg = f"子块 {child_id} 执行异常: {str(e)}"
logger.error(error_msg)
# 更新块记录状态为失败
await self._update_block_record(
block_record_id,
TaskBlockRecordStatus.FAILED, # 失败
error_msg,
)
# 设置错误信息到上下文
self.task_context.set_error(error_msg, child_id)
return {
"success": False,
"message": error_msg,
"block_id": child_id,
"error": str(e)
}
# 所有子块执行完成
print("results", results, "=============================================")
return {
"success": True,
"message": f"分支 {branch} 的所有子块执行完成",
"output": {
"executed": True,
"branch": branch,
"results": results
}
}
except Exception as e:
# 处理execute_children方法自身的异常
error_msg = f"execute_children 方法异常: {str(e)}"
logger.error(error_msg)
# 设置错误信息到上下文
self.task_context.set_error(error_msg, block.get('id', 'unknown'))
return {
"success": False,
"message": error_msg,
"error": str(e)
}
async def _create_block_record(self, block: Dict[str, Any]) -> str:
"""
创建块记录
Args:
block: 任务块定义
Returns:
str: 块记录ID
"""
try:
block_id = block.get("id", "unknown")
block_type = block.get("blockType", "unknown")
block_name = block.get("name", f"block-{block_id}")
# 创建块记录
record_id = str(uuid.uuid4())
# 预处理输入参数处理不能直接JSON序列化的类型
input_params = self._process_json_data(block.get("inputParams", {}))
# 提取输入参数的具体值
input_params_value = {}
if input_params:
try:
# 对于每个参数提取其value值
for key, value_info in input_params.items():
if isinstance(value_info, dict) and "value" in value_info:
input_params_value[key] = value_info["value"]
else:
input_params_value[key] = value_info
except Exception as e:
logger.warning(f"提取输入参数值失败: {str(e)}")
input_params_value = input_params
async with get_async_session() as session:
stmt = insert(VWEDBlockRecord).values(
id=record_id,
task_record_id=self.task_context.task_record_id,
task_id=self.task_context.task_def_id,
block_id=str(block_id),
block_name=block_name,
block_execute_name=block_type,
block_config_id=block.get("configId", ""),
status=TaskBlockRecordStatus.RUNNING, # 开始执行
input_params=json.dumps(input_params, ensure_ascii=False),
block_input_params=json.dumps(block.get("inputParams", {}), ensure_ascii=False),
block_input_params_value=json.dumps(input_params_value, ensure_ascii=False),
block_internal_variables=json.dumps(self._process_json_data(self.task_context.variables), ensure_ascii=False),
internal_variables=json.dumps(self._process_json_data(self.task_context.variables), ensure_ascii=False),
started_on=datetime.now()
)
await session.execute(stmt)
await session.commit()
return record_id
except Exception as e:
logger.error(f"创建块记录失败: {str(e)}")
raise
async def _update_block_record(
self,
block_record_id: str,
status: int,
message: str = None,
output: Dict[str, Any] = None,
) -> None:
"""
更新块记录
Args:
block_record_id: 块记录ID
status: 状态码
message: 状态消息
output: 输出结果
"""
try:
async with get_async_session() as session:
# 查询块记录
result = await session.execute(
select(VWEDBlockRecord).where(VWEDBlockRecord.id == block_record_id)
)
block_record: VWEDBlockRecord = result.scalars().first()
if not block_record:
logger.error(f"块记录不存在: {block_record_id}")
return
# 更新状态
block_record.status = status
# 更新消息
if message:
block_record.ended_reason = message
block_record.remark = message
# 获取块ID和名称
block_id = block_record.block_id
block_name = block_record.block_name
# 直接获取任务上下文中来自此块的变量
block_variables = self.task_context.get_block_variables(block_id=block_id)
logger.info(f"{block_name} (ID: {block_id}) 设置的变量: {list(block_variables.keys())}")
# 更新内部变量(当前上下文中的所有变量)
block_output = self.task_context.get_block_output(block_name)
# 处理输出并存储
# if block_output:
processed_output = self._process_json_data(block_output)
# 构建输出结构 {"blocks": {"块名称": 输出内容}}
full_output = {TaskInputParamVariables.BLOCKS: {block_name: processed_output}}
output_full_json = json.dumps(full_output, ensure_ascii=False)
output_value_json = json.dumps(processed_output, ensure_ascii=False)
block_record.output_params = output_full_json
block_record.block_out_params_value = output_value_json
block_record.internal_variables = self.task_context.variables.__str__()
logger.info(f"为块 {block_name} 存储输出: {list(processed_output.keys()) if isinstance(processed_output, dict) else type(processed_output)}")
# 更新结束时间(如果状态为完成或失败)
if status in [TaskBlockRecordStatus.SUCCESS, TaskBlockRecordStatus.FAILED]:
block_record.ended_on = datetime.now()
await session.commit()
except Exception as e:
logger.error(f"更新块记录失败: {str(e)}")
raise
def _process_json_data(self, data):
"""
处理数据以确保能够被JSON序列化
Args:
data: 需要处理的数据
Returns:
处理后的数据
"""
if data is None:
return None
if isinstance(data, dict):
# 处理字典
return {k: self._process_json_data(v) for k, v in data.items()}
elif isinstance(data, list):
# 处理列表
return [self._process_json_data(item) for item in data]
elif isinstance(data, datetime):
# 处理日期时间对象
return data.strftime("%Y-%m-%d %H:%M:%S")
elif isinstance(data, (int, float, str, bool)) or data is None:
# 这些类型可以直接序列化
return data
else:
# 其他类型转换为字符串
return str(data)
async def _parse_input_params(self, params: Dict[str, Any]) -> Dict[str, Any]:
"""
解析输入参数
处理表达式、变量引用等
Args:
params: 原始参数
Returns:
Dict[str, Any]: 解析后的参数
"""
def extract_items(input_str):
"""将形如"[taskInputs.a,taskInputs.b]"的字符串转换为["taskInputs.a", "taskInputs.b"]"""
# 使用正则表达式匹配中括号内的内容,然后分割
match = re.search(r'\[(.*)\]', input_str)
if match:
# 获取括号内的内容并按逗号分割
items = []
for item in match.group(1).split(','):
try:
json.loads(item)
json_item = eval(item)
except Exception as e:
json_item = item.strip()
items.append(json_item)
return items
return input_str
if not params:
return {}
parsed_params = {}
for key, value_info in params.items():
# 处理不同类型的参数
param_type = value_info.get("type", TaskInputParamType.SIMPLE)
param_value = value_info.get("value")
if param_type == TaskInputParamType.SIMPLE:
# 简单值,直接使用
parsed_params[key] = param_value
elif param_type == TaskInputParamType.EXPRESSION:
# 表达式,需要从上下文中获取值
param_value = extract_items(param_value)
if isinstance(param_value, list):
parsed_params[key] = [await self._parse_expression(key, c) for c in param_value]
else:
express_result = await self._parse_expression(key, param_value)
parsed_params[key] = express_result
elif param_type == TaskInputParamType.JSON:
# JSON值需要解析
# 先同步解析JSON结构
parsed_value = self._parse_json_value(param_value)
# 再处理其中可能包含的表达式标记
parsed_params[key] = await self._process_json_expressions(parsed_value)
else:
# 默认处理
parsed_params[key] = param_value
return parsed_params
async def _process_json_expressions(self, data: Any) -> Any:
"""
处理JSON中的表达式标记
Args:
data: JSON数据
Returns:
Any: 处理后的数据
"""
# 处理列表
if isinstance(data, list):
return await self._process_nested_json(data)
# # 处理字典
# elif isinstance(data, dict):
# result = {}
# for key, value in data.items():
# result[key] = await self._process_json_expressions(value)
# return result
# 其他类型直接返回
return data
async def _parse_expression(self, key: str, expression_value: Any) -> Any:
"""
解析表达式类型的参数值
Args:
key: 参数键名
expression_value: 表达式值
Returns:
Any: 解析后的值
"""
if not isinstance(expression_value, str):
return expression_value
# 检查是否包含加号,表示需要合并多个对象
if "+" in expression_value and "." in expression_value:
return await self._parse_combined_expression(expression_value)
# 检查是否是数据库表引用的表达式 (如: vwed_taskdef.id)
elif expression_value.startswith(TaskInputParamVariables.VWED_) and "." in expression_value:
return await self._parse_db_reference(expression_value)
# 检查是否是块输出引用 (如: blocks.blockName.fieldName)
elif expression_value.startswith(TaskInputParamVariables.BLOCKS):
return self._parse_block_reference(expression_value)
# 检查是否是任务输入参数引用 (如: taskInputs.paramName)
elif expression_value.startswith(TaskInputParamVariables.TASK_INPUTS):
return await self._parse_task_inputs_reference(expression_value)
# 检查是否是字符串工具函数调用 (如: StringUtils.isBlank("你好"))
elif expression_value.startswith(TaskInputParamVariables.STRING_UTILS):
return await self._parse_string_utils_expression(expression_value)
else:
# 非数据库表引用,尝试从上下文变量中获取
return expression_value
def _split_expression_by_plus(self, expression: str) -> List[str]:
"""
按照加号分割表达式,但忽略引号内的加号
Args:
expression: 需要分割的表达式
Returns:
List[str]: 分割后的表达式部分列表
"""
parts = []
current_part = ""
in_quotes = False
quote_char = None
num_error = 0
num_left_quote = 0
for index, char in enumerate(expression):
# 处理引号
if char in ['(', ")"]:
num_error += 1
if num_error == 1 and char in ['', ""]:
raise Exception(f"表达式 {expression} 格式不规范")
if char in ['"', "'"]:
if not in_quotes: # 开始引号
in_quotes = True
quote_char = char
elif char == quote_char: # 结束引号
in_quotes = False
quote_char = None
num_left_quote += 1
current_part += char
if num_left_quote == 1 and char == "":
raise Exception(f"表达式 {expression} 格式不规范")
elif char == '+' and not in_quotes: # 只处理引号外的加号作为分隔符
if current_part.strip():
parts.append(current_part.strip())
current_part = ""
else:
current_part += char
# 添加最后一部分
if current_part.strip():
parts.append(current_part.strip())
return parts
async def _parse_combined_expression(self, expression: str) -> Any:
"""
解析组合表达式,如 "a + b + c"
Args:
expression: 需要解析的表达式
Returns:
Any: 解析结果
"""
# 分割表达式,获取各部分,但需要处理引号内的加号
parts = self._split_expression_by_plus(expression)
result_values = []
# 处理每个部分
for part in parts:
part_value = None
# 检查是否是数据库表引用
if part.startswith(TaskInputParamVariables.VWED_) and "." in part:
part_value = await self._parse_db_reference(part)
# 检查是否是块输出引用b_reference(part)
# 检查是否是块输出引用
elif part.startswith(TaskInputParamVariables.BLOCKS):
part_value = self._parse_block_reference(part)
# 检查是否是任务输入参数引用
elif part.startswith(TaskInputParamVariables.TASK_INPUTS):
part_value = await self._parse_task_inputs_reference(part)
# 检查是否是字符串工具函数调用
elif part.startswith(TaskInputParamVariables.STRING_UTILS):
part_value = await self._parse_string_utils_expression(part)
else:
# 从上下文变量中获取
part_value = self.task_context.get_variable(part)
# 添加到结果列表
if part_value is not None:
result_values.append(part_value)
# 合并结果
if result_values:
# 检查结果类型并合并
if all(isinstance(v, (int, float)) for v in result_values):
# 数字类型,执行加法运算
result = sum(result_values)
logger.info(f"表达式 {expression} 计算结果(数字相加): {result}")
return result
else:
# 包含非数字类型,转为字符串并连接
result = "".join(str(v) for v in result_values)
logger.info(f"表达式 {expression} 计算结果(字符串拼接): {result}")
return result
else:
# 没有有效结果
logger.warning(f"表达式 {expression} 计算结果为空")
return None
def _parse_block_reference(self, reference: str) -> Any:
"""
解析块引用表达式,如 blocks.blockName.fieldName
Args:
reference: 块引用表达式
Returns:
Any: 引用的值
"""
_, block_name, field_name = reference.split(".")
block_output = self.task_context.get_block_output(block_name)
if block_output is None:
raise Exception(f"{reference} 获取块引用失败 表达式: {reference} 有误")
try:
return block_output[field_name]
except Exception as e:
raise Exception(f"{field_name} 变量不存在!")
async def _parse_db_reference(self, reference: str) -> Any:
"""
解析数据库引用表达式,如 vwed_taskdef.id
Args:
reference: 数据库引用表达式
Returns:
Any: 查询结果
"""
try:
# 提取表名和字段名
table_name, field_name = reference.split(".", 1)
# 从上下文中获取当前任务的相关ID
task_def_id = self.task_context.task_def_id
task_record_id = self.task_context.task_record_id
# 构建SQL查询
async with get_async_session() as session:
# 根据表名确定查询方式
if table_name == VWEDTaskRecord.__tablename__:
return await self._query_task_record(session, field_name, task_record_id)
elif table_name == VWEDTaskDef.__tablename__:
return await self._query_task_def(session, field_name, task_def_id)
else:
return await self._query_other_table(session, table_name, field_name, task_def_id, task_record_id)
except Exception as e:
# 查询失败时记录错误并返回None
logger.error(f"查询表达式 {reference} 失败: {str(e)}")
raise Exception(f"{field_name} 查询相关字段不存在")
async def _query_task_record(self, session: AsyncSession, field_name: str, task_record_id: str) -> Any:
"""
查询任务记录表
Args:
session: 数据库会话
field_name: 字段名
task_record_id: 任务记录ID
Returns:
Any: 查询结果
"""
# 解析字段名,检查是否包含查询条件
field_parts = field_name.split(":")
actual_field = field_parts[0].strip()
# 构建查询条件
conditions = []
query_params = {}
# 默认使用任务记录ID
conditions.append("id = :task_record_id")
query_params["task_record_id"] = task_record_id
# 如果有额外的条件(格式为 field_name:condition=value
if len(field_parts) > 1:
for condition_part in field_parts[1:]:
if "=" in condition_part:
cond_name, cond_value = condition_part.split("=", 1)
cond_name = cond_name.strip()
cond_value = cond_value.strip()
# 添加条件
param_name = f"param_{len(query_params)}"
conditions.append(f"{cond_name} = :{param_name}")
query_params[param_name] = cond_value
# 构建完整SQL
query = f"SELECT {actual_field} FROM {VWEDTaskRecord.__tablename__} WHERE {' AND '.join(conditions)}"
# 执行查询
result = await session.execute(text(query), query_params)
# 获取查询结果
row = result.first()
if row and len(row) > 0:
# 获取字段值
db_value = str(row[0])
logger.info(f"从数据库 {VWEDTaskRecord.__tablename__}.{actual_field} 获取值: {db_value}")
return db_value
else:
# 如果没有找到记录,记录警告
logger.warning(f"在表 {VWEDTaskRecord.__tablename__} 中未找到符合条件的字段 {actual_field}")
return None
async def _query_task_def(self, session: AsyncSession, field_name: str, task_def_id: str) -> Any:
"""
查询任务定义表
Args:
session: 数据库会话
field_name: 字段名
task_def_id: 任务定义ID
Returns:
Any: 查询结果
"""
# 解析字段名,检查是否包含查询条件
field_parts = field_name.split(":")
actual_field = field_parts[0].strip()
# 构建查询条件
conditions = []
params = {}
# 默认使用任务定义ID
conditions.append("id = :def_id")
params["def_id"] = task_def_id
# 如果有额外的条件
if len(field_parts) > 1:
for condition_part in field_parts[1:]:
if "=" in condition_part:
cond_name, cond_value = condition_part.split("=", 1)
cond_name = cond_name.strip()
cond_value = cond_value.strip()
# 添加条件
param_name = f"param_{len(params)}"
conditions.append(f"{cond_name} = :{param_name}")
params[param_name] = cond_value
# 构建完整SQL
query = f"SELECT {actual_field} FROM {VWEDTaskDef.__tablename__} WHERE {' AND '.join(conditions)}"
# 执行查询
result = await session.execute(text(query), params)
row = result.first()
if row and len(row) > 0:
db_value = row[0]
logger.info(f"从数据库 {VWEDTaskDef.__tablename__}.{actual_field} 获取值: {db_value}")
return db_value
else:
logger.warning(f"在表 {VWEDTaskDef.__tablename__} 中未找到符合条件的字段 {actual_field}")
return None
async def _query_other_table(self, session: AsyncSession, table_name: str, field_name: str,
task_def_id: str, task_record_id: str) -> Any:
"""
查询其他表
Args:
session: 数据库会话
table_name: 表名
field_name: 字段名
task_def_id: 任务定义ID
task_record_id: 任务记录ID
Returns:
Any: 查询结果
"""
# 解析字段名,检查是否包含查询条件
field_parts = field_name.split(":")
actual_field = field_parts[0].strip()
# 构建查询条件
conditions = []
params = {}
# 如果有额外的条件(格式为 field_name:condition=value
if len(field_parts) > 1:
for condition_part in field_parts[1:]:
if "=" in condition_part:
cond_name, cond_value = condition_part.split("=", 1)
cond_name = cond_name.strip()
cond_value = cond_value.strip()
# 添加条件
param_name = f"param_{len(params)}"
conditions.append(f"{cond_name} = :{param_name}")
params[param_name] = cond_value
# 默认关联条件
if task_record_id:
conditions.append("task_record_id = :record_id")
params["record_id"] = task_record_id
if task_def_id:
conditions.append("task_id = :def_id")
params["def_id"] = task_def_id
# 如果没有有效条件,尝试使用其他默认条件
if not conditions:
conditions.append("1=1 LIMIT 1") # 简单的兜底条件
# 完成查询语句 - 使用AND连接条件更符合预期
query = f"SELECT {actual_field} FROM {table_name} WHERE {' AND '.join(conditions)}"
# 执行查询
result = await session.execute(text(query), params)
row = result.first()
if row and len(row) > 0:
db_value = row[0]
logger.info(f"从数据库 {table_name}.{actual_field} 获取值: {db_value}")
return db_value
else:
logger.warning(f"在表 {table_name} 中未找到符合条件的字段 {actual_field}")
return None
def _parse_json_value(self, json_value: Any) -> Any:
"""
解析JSON类型的值支持嵌套结构和表达式类型
Args:
json_value: JSON值
Returns:
Any: 解析后的值
"""
if json_value is None:
return None
# 如果是字符串类型尝试解析成JSON
if isinstance(json_value, str):
try:
parsed_value = json.loads(json_value)
# 改为同步处理方法,避免返回协程对象
return parsed_value
except json.JSONDecodeError:
print(f"解析JSON失败: {json_value}")
return json_value
else:
# 已经是Python对象处理嵌套结构xx
return json_value
async def _process_nested_json(self, data: Any) -> Any:
"""
异步处理嵌套的JSON数据解析其中的表达式类型
Args:
data: 需要处理的数据
Returns:
Any: 处理后的数据
"""
# 处理列表
if isinstance(data, list):
result = []
for item in data:
# 递归处理列表中的每个元素
processed_item = await self._process_nested_json(item)
result.append(processed_item)
return result
# 处理字典
elif isinstance(data, dict):
# 检查是否是表达式类型的对象
if "type" in data and data.get("type") == TaskInputParamType.EXPRESSION:
# 解析表达式类型的值
expr_value = data.get("value0")
key_name = data.get("key0", "unknown_key") # 获取键名,用于日志
logger.info(f"在JSON中发现表达式类型值键: {key_name}, 表达式: {expr_value}")
try:
# 解析表达式获取实际值
return await self._parse_expression(key_name, expr_value)
except Exception as e:
logger.error(f"解析表达式 {expr_value} 失败: {str(e)}")
return expr_value # 解析失败时返回原始表达式
# 普通字典,递归处理每个键值对
result = {}
for key, value in data.items():
processed_value = await self._process_nested_json(value)
result[key] = processed_value
return result
# 对于字符串,检查是否是标记的表达式
elif isinstance(data, str) and data.startswith("__EXPRESSION__") and data.endswith("__"):
# 提取表达式值
expr_value = data[14:-2] # 去掉前缀"__EXPRESSION__"和后缀"__"
try:
# 解析表达式获取实际值
return await self._parse_expression("extracted_expr", expr_value)
except Exception as e:
logger.error(f"解析提取的表达式 {expr_value} 失败: {str(e)}")
return expr_value
# 其他基本类型直接返回
return data
# def _get_possible_output_keys(self, block_name: str) -> List[str]:
# """
# 根据块名称获取可能的输出键
#
# Args:
# block_name: 块名称
#
# Returns:
# List[str]: 可能的输出键列表
# """
# # 块类型与可能的输出变量名映射
# output_keys_map = {
# "CreateUuidBp": ["createUuid", "uuid"],
# "CurrentTimeStampBp": ["currentTimeStamp"],
# "TimestampBp": ["timestamp"],
# "StringToJsonObjectBp": ["jsonObject"],
# "StringToJsonArrayBp": ["jsonArray"],
# "JdbcQueryBp": ["jdbcQueryResult"],
# "HttpGetBp": ["httpResponse", "response"],
# "HttpPostBp": ["httpResponse", "response"],
# "ScriptBp": ["scriptResult"],
# # 添加更多块类型和对应的输出变量名
# }
#
# # 从块名中提取类型
# for block_type, keys in output_keys_map.items():
# if block_type in block_name:
# return keys
#
# # 如果没有找到匹配的块类型,返回空列表
# return []
#
async def _parse_task_inputs_reference(self, reference: str) -> Any:
"""
解析任务输入参数引用表达式,如 taskInputs.paramName
Args:
reference: 任务输入参数引用表达式
Returns:
Any: 引用的值
"""
try:
# 分割表达式,获取参数名
parts = reference.split(".")
if len(parts) != 2:
logger.error(f"任务输入参数引用格式不正确: {reference},应为 'taskInputs.paramName'")
raise Exception(f"{reference} 任务输入参数引用格式不正确")
param_name = parts[1]
# 从数据库中查询任务记录的输入参数
task_record_id = self.task_context.task_record_id
if not task_record_id:
logger.error("无法获取任务记录ID")
raise Exception(f"{reference} 无法获取任务记录ID")
async with get_async_session() as session:
# 查询当前任务的blockrecord
from sqlalchemy import select
# 首先尝试查找根块的记录
query = select(VWEDTaskRecord).where(
VWEDTaskRecord.id == task_record_id,
)
result = await session.execute(query)
task_record = result.scalars().first()
# 获取input_params字段
input_params_json = task_record.input_params
if not input_params_json:
logger.error(f"任务记录 {task_record_id} 的块记录没有输入参数")
raise Exception(f"{reference} 无法获取任务记录ID")
try:
# 解析JSON字符串为Python对象
input_params = json.loads(input_params_json)
# 检查输入参数的格式
if not isinstance(input_params, list):
logger.error(f"任务输入参数格式不正确,应为列表: {input_params}")
raise Exception(f"{reference} 任务输入参数格式不正确,应为列表")
# 查找参数名匹配的参数项
for param in input_params:
if isinstance(param, dict) and param.get("name") == param_name:
# 获取参数值
param_value = param.get("defaultValue")
param_type = param.get("type")
from routes.model.task_edit_model import InputParamType
# 根据参数类型进行转换
if param_type == InputParamType.INTEGER:
try:
return int(param_value)
except (ValueError, TypeError):
return param_value
elif param_type == InputParamType.FLOAT:
try:
return float(param_value)
except (ValueError, TypeError):
return param_value
elif param_type == InputParamType.BOOLEAN:
if param_value.lower() in ["true", "1", "yes", "y"]:
return True
elif param_value.lower() in ["false", "0", "no", "n"]:
return False
else:
return param_value
else:
# 其他类型,直接返回值
return param_value
# 如果没有找到匹配的参数名
logger.warning(f"在任务输入参数中找不到参数: {param_name}")
return None
except json.JSONDecodeError as e:
logger.error(f"解析input_params JSON失败: {str(e)}")
return None
except Exception as e:
logger.error(f"获取任务输入参数 {reference} 失败: {str(e)}")
raise Exception(f"{reference} 获取任务输入参数失败")
async def _parse_string_utils_expression(self, expression_value: str) -> Any:
"""
解析StringUtils表达式调用内置字符串函数
Args:
expression_value: StringUtils表达式形如 StringUtils.isBlank("你好")
Returns:
Any: 函数执行结果
Raises:
ValueError: 如果表达式格式不正确或函数不存在
Exception: 函数执行过程中的任何错误
"""
try:
# 导入内置函数模块
from utils.built_in_functions import call_function
# 验证表达式格式
if not expression_value.startswith(TaskInputParamVariables.STRING_UTILS + "."):
raise ValueError(f"无效的StringUtils表达式: {expression_value}")
# 提取函数名和参数部分
function_pattern = rf"{TaskInputParamVariables.STRING_UTILS}\.(\w+)\((.*)\)$"
match = re.match(function_pattern, expression_value)
if not match:
raise ValueError(f"StringUtils表达式格式错误: {expression_value}")
function_name, params_str = match.groups()
# 构建函数ID (匹配BUILT_IN_FUNCTIONS中的id格式)
function_id = f"{function_name}()"
print(f"params_str: {params_str}=======================================")
# 解析参数列表
params = self._parse_params_string(params_str)
print(f"params: {params}=======================================")
# 处理每个参数,解析可能的嵌套表达式
processed_params = []
for param in params:
# 检查参数是否为字符串字面量(被引号包围)
if (param.startswith('"') and param.endswith('"')) or (param.startswith("'") and param.endswith("'")):
processed_params.append(param[1:-1]) # 去掉引号
else:
# 递归解析可能的嵌套表达式
processed_params.append(await self._parse_expression("", param))
print(f"processed_params: {processed_params}=======================================")
# 调用内置函数并返回结果
return await call_function(function_id, *processed_params)
except Exception as e:
logger.error(f"执行StringUtils表达式失败: {expression_value}, 错误: {str(e)}")
raise Exception(f"{expression_value} 执行StringUtils表达式失败 错误: {str(e)}")
def _parse_params_string(self, params_str: str) -> List[str]:
"""
解析函数参数字符串,处理嵌套括号和引号的情况
Args:
params_str: 参数字符串,例如: '"hello"''blocks.b1.uuid, "world"'
Returns:
List[str]: 解析后的参数列表
"""
if not params_str.strip():
return []
# params = []
params = params_str.split(",")
# current_param = ""
# bracket_depth = 0
# quote_char = None
# for index, char in enumerate(params_str):
# if char == ',' and bracket_depth == 0 and quote_char is None:
# # 遇到顶层逗号,完成当前参数
# params.append(current_param.strip())
# current_param = ""
# if index == 0 and char in ['“', "”"]:
# raise Exception(f"表达式 {params_str} 格式不规范")
# elif char in ['"', "'"]:
# if quote_char is None:
# quote_char = char
# elif char == quote_char:
# quote_char = None
# # 处理括号
# if quote_char is None: # 只在非引号内计算括号深度
# if char == '(':
# bracket_depth += 1
# elif char == ')':
# bracket_depth -= 1
# current_param += char
# # 添加最后一个参数
# if current_param.strip():
# params.append(current_param.strip())
return params