805 lines
30 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 logging
# from typing import Dict, Any
# from services.execution.task_context import TaskContext
# from utils.logger import get_logger
# from .model.block_name import ScriptBlockName
# from .base import BlockHandler, register_handler
#
#
# # 获取日志记录器
# logger = get_logger("services.execution.handlers.script")
#
#
# # 脚本块处理器
# @register_handler(ScriptBlockName.SET_TASK_VARIABLES)
# class SetTaskVariablesBlockHandler(BlockHandler):
# """设置任务变量块处理器"""
#
# async def execute(
# self,
# block: Dict[str, Any],
# input_params: Dict[str, Any],
# context: TaskContext
# ) -> Dict[str, Any]:
# """执行设置任务变量块"""
# try:
# # 获取函数名和参数
# function_name = input_params.get("functionName")
# function_args = input_params.get("functionArgs", {})
#
# if not function_name:
# result = {
# "success": False,
# "message": "缺少函数名"
# }
# # 记录执行结果
# await self._record_task_log(block, result, context)
# return result
#
# # 参数类型验证和转换
# function_args = self._validate_and_convert_args(function_args)
#
# # 调用脚本执行方法
# logger.info("进入执行脚本功能块")
# exec_result = await self._execute_script(function_name, function_args, context)
# # print(exec_result, "=------------------=-")
# exec_result_output = exec_result.get("output")
# if exec_result_output:
# save_result = exec_result_output.get("result")
# context.set_variable(function_name, save_result)
# # 如果执行成功,将变量保存到任务记录
# # if exec_result.get("success", False):
# # save_result = {function_name: exec_result}
#
# await self._save_variables_to_database(context)
#
#
# # 记录执行结果
# await self._record_task_log(block, exec_result, context)
# return exec_result
# except Exception as e:
# result = {
# "success": False,
# "message": f"设置任务变量失败: {str(e)}"
# }
# # 记录异常
# await self._record_task_log(block, result, context)
# return result
#
# async def _execute_script(
# self,
# function_name: str,
# function_args: Any,
# context: TaskContext
# ) -> Dict[str, Any]:
# """
# 执行指定的脚本函数(通过脚本注册中心)
#
# Args:
# function_name: 要执行的函数名
# function_args: 函数参数
# context: 任务上下文
#
# Returns:
# Dict[str, Any]: 执行结果
# """
# logger.info(f"执行注册函数: {function_name}")
#
# try:
# # 获取脚本引擎服务
# from services.online_script.script_engine_service import get_script_engine
# from services.online_script.script_registry_service import get_global_registry
#
# script_engine = get_script_engine()
# registry = get_global_registry()
#
# # 检查函数是否已注册
# func_info = registry.get_function_info(function_name)
# if not func_info:
# return {
# "success": False,
# "message": f"函数 {function_name} 未在脚本注册中心注册"
# }
#
# # 获取函数所属的脚本服务ID
# script_service_id = func_info.get("script_id")
# if not script_service_id:
# return {
# "success": False,
# "message": f"函数 {function_name} 没有关联的脚本服务"
# }
#
# # 调用脚本引擎执行函数
# result = await script_engine.execute_script_function(
# script_id=script_service_id,
# function_name=function_name,
# function_args=function_args
# )
#
# if result.get("success"):
# result_value = result.get("result")
# logger.info(f"函数 {function_name} 执行结果: {result_value}")
#
# # 设置返回值到上下文 - 与ScriptBp不同这里会更新所有变量
# if isinstance(result_value, dict):
# # 如果返回值是字典,将字典中的每个键值对设置为任务变量
# for key, value in result_value.items():
# context.set_variable(key, value)
#
# result_message = f"脚本函数 {function_name} 执行成功,已更新 {len(result_value)} 个变量"
# else:
# # 如果返回值不是字典将整个返回值设置为scriptResult变量
# context.set_variable("scriptResult", result_value)
# result_message = f"脚本函数 {function_name} 执行成功结果已保存为scriptResult变量"
#
# # 构建成功结果
# return {
# "success": True,
# "message": result_message,
# "output": {
# "result": result_value,
# "functionName": function_name,
# "execution_time_ms": result.get("execution_time_ms", 0)
# }
# }
# else:
# # 执行失败,返回错误信息
# return {
# "success": False,
# "message": result.get("error", f"执行函数 {function_name} 失败")
# }
#
# except Exception as e:
# logger.error(f"执行脚本函数时发生异常: {str(e)}", exc_info=True)
# return {
# "success": False,
# "message": f"执行脚本函数 {function_name} 失败: {str(e)}"
# }
#
# async def _save_variables_to_database(self, context: TaskContext) -> None:
# """
# 将任务变量保存到数据库的任务记录中
#
# Args:
# context: 任务上下文
# """
# try:
# from sqlalchemy import update
# from data.models.taskrecord import VWEDTaskRecord
# from data.session import get_async_session
# import json
#
# # 获取任务记录ID
# task_record_id = context.task_record_id
#
# if not task_record_id:
# logger.error("无法保存变量任务记录ID为空")
# return
#
# # 将任务变量转换为JSON字符串
# variables_json = json.dumps(context.variables, ensure_ascii=False)
# # 更新数据库记录
# async with get_async_session() as session:
# stmt = update(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id).values(
# variables=variables_json
# )
# await session.execute(stmt)
# await session.commit()
#
# logger.info(f"已将任务变量保存到数据库记录 {task_record_id}")
#
# except Exception as e:
# logger.error(f"保存任务变量到数据库失败: {str(e)}")
#
# def _validate_and_convert_args(self, function_args: Any) -> Any:
# """
# 验证并转换函数参数,确保参数是有效的 JSON 格式
#
# Args:
# function_args: 输入的函数参数
#
# Returns:
# Any: 转换后的参数JSON 对象)
#
# Raises:
# ValueError: 当参数无法转换为有效 JSON 时抛出异常
# """
# import json
#
# # 如果已经是字典或列表等 JSON 兼容类型,直接返回
# if isinstance(function_args, (dict, list, int, float, bool)) or function_args is None:
# return function_args
#
# # 如果是字符串,尝试解析为 JSON
# if isinstance(function_args, str):
# try:
# parsed_args = json.loads(function_args)
# logger.info(f"成功解析参数字符串为 JSON: {parsed_args}")
# return parsed_args
# except json.JSONDecodeError as e:
# error_msg = f"函数参数不是有效的 JSON 格式: {str(e)}"
# logger.error(error_msg)
# raise ValueError(error_msg)
#
# # 其他类型不支持,抛出异常
# error_msg = f"不支持的函数参数类型: {type(function_args)},参数必须是有效的 JSON 格式"
# logger.error(error_msg)
# raise ValueError(error_msg)
#
#
# # 脚本块处理器
# @register_handler(ScriptBlockName.SCRIPT)
# class ScriptBlockHandler(BlockHandler):
# """脚本块处理器"""
#
# async def execute(
# self,
# block: Dict[str, Any],
# input_params: Dict[str, Any],
# context: TaskContext
# ) -> Dict[str, Any]:
# """执行脚本块"""
# try:
# # 获取函数名和参数
# function_name = input_params.get("functionName")
# function_args = input_params.get("functionArgs", {})
#
# if not function_name:
# result = {
# "success": False,
# "message": "缺少函数名"
# }
# # 记录执行结果
# await self._record_task_log(block, result, context)
# return result
#
# # 参数类型验证和转换
# function_args = self._validate_and_convert_args(function_args)
#
# # 调用脚本执行方法
# exec_result = await self._execute_script(function_name, function_args, context)
#
# # 记录执行结果
# await self._record_task_log(block, exec_result, context)
# return exec_result
# except Exception as e:
# result = {
# "success": False,
# "message": f"脚本执行失败: {str(e)}"
# }
# # 记录异常
# await self._record_task_log(block, result, context)
# return result
#
# async def _execute_script(
# self,
# function_name: str,
# function_args: Any,
# context: TaskContext
# ) -> Dict[str, Any]:
# """
# 执行指定的脚本函数(通过脚本注册中心)
#
# Args:
# function_name: 要执行的函数名
# function_args: 函数参数
# context: 任务上下文
#
# Returns:
# Dict[str, Any]: 执行结果
# """
# logger.info(f"执行注册函数: {function_name}")
#
# try:
# # 获取脚本引擎服务
# from services.online_script.script_engine_service import get_script_engine
# from services.online_script.script_registry_service import get_global_registry
#
# script_engine = get_script_engine()
# registry = get_global_registry()
#
# # 检查函数是否已注册
# func_info = registry.get_function_info(function_name)
# if not func_info:
# return {
# "success": False,
# "message": f"函数 {function_name} 未在脚本注册中心注册"
# }
#
# # 获取函数所属的脚本服务ID
# script_service_id = func_info.get("script_id")
# if not script_service_id:
# return {
# "success": False,
# "message": f"函数 {function_name} 没有关联的脚本服务"
# }
# # 调用脚本引擎执行函数
# result = await script_engine.execute_script_function(
# script_id=script_service_id,
# function_name=function_name,
# function_args=function_args
# )
#
# if result.get("success"):
# result_value = result.get("result")
# logger.info(f"函数 {function_name} 执行结果: {result_value}")
#
# # 设置返回值到上下文
# context.set_variable("scriptResult", result_value)
#
# # 构建成功结果
# return {
# "success": True,
# "message": f"脚本函数 {function_name} 执行成功",
# "output": {
# "result": result_value,
# "functionName": function_name,
# "execution_time_ms": result.get("execution_time_ms", 0)
# }
# }
# else:
# # 执行失败,返回错误信息
# return {
# "success": False,
# "message": result.get("error", f"执行函数 {function_name} 失败")
# }
#
# except Exception as e:
# logger.error(f"执行脚本函数时发生异常: {str(e)}", exc_info=True)
# return {
# "success": False,
# "message": f"执行脚本函数 {function_name} 失败: {str(e)}"
# }
#
# def _validate_and_convert_args(self, function_args: Any) -> Any:
# """
# 验证并转换函数参数,确保参数是有效的 JSON 格式
#
# Args:
# function_args: 输入的函数参数
#
# Returns:
# Any: 转换后的参数JSON 对象)
#
# Raises:
# ValueError: 当参数无法转换为有效 JSON 时抛出异常
# """
# import json
#
# # 如果已经是字典或列表等 JSON 兼容类型,直接返回
# if isinstance(function_args, (dict, list, int, float, bool)) or function_args is None:
# return function_args
#
# # 如果是字符串,尝试解析为 JSON
# if isinstance(function_args, str):
# try:
# parsed_args = json.loads(function_args)
# logger.info(f"成功解析参数字符串为 JSON: {parsed_args}")
# return parsed_args
# except json.JSONDecodeError as e:
# error_msg = f"函数参数不是有效的 JSON 格式: {str(e)}"
# logger.error(error_msg)
# raise ValueError(error_msg)
#
# # 其他类型不支持,抛出异常
# error_msg = f"不支持的函数参数类型: {type(function_args)},参数必须是有效的 JSON 格式"
# logger.error(error_msg)
# raise ValueError(error_msg)
###========================== 暂时使用旧版
# !/usr/bin/env python
# -*- coding: utf-8 -*-
"""
脚本处理器模块
提供脚本执行和变量设置的处理器
"""
import logging
from typing import Dict, Any
from services.execution.task_context import TaskContext
from utils.logger import get_logger
from .model.block_name import ScriptBlockName
from .base import BlockHandler, register_handler
# 获取日志记录器
logger = get_logger("services.execution.handlers.script")
# 脚本块处理器
@register_handler(ScriptBlockName.SET_TASK_VARIABLES)
class SetTaskVariablesBlockHandler(BlockHandler):
"""设置任务变量块处理器"""
async def execute(
self,
block: Dict[str, Any],
input_params: Dict[str, Any],
context: TaskContext
) -> Dict[str, Any]:
"""执行设置任务变量块"""
try:
# 获取函数名和参数
function_name = input_params.get("functionName")
function_args = input_params.get("functionArgs", {})
if not function_name:
result = {
"success": False,
"message": "缺少函数名"
}
# 记录执行结果
await self._record_task_log(block, result, context)
return result
# 调用脚本执行方法
logger.info("进入执行脚本功能块")
exec_result = await self._execute_script(function_name, function_args, context)
# print(exec_result, "=------------------=-")
exec_result_output = exec_result.get("output")
if exec_result_output:
save_result = exec_result_output.get("result")
context.set_variable(function_name, save_result)
# 如果执行成功,将变量保存到任务记录
# if exec_result.get("success", False):
# save_result = {function_name: exec_result}
await self._save_variables_to_database(context)
# 记录执行结果
await self._record_task_log(block, exec_result, context)
return exec_result
except Exception as e:
result = {
"success": False,
"message": f"设置任务变量失败: {str(e)}"
}
# 记录异常
await self._record_task_log(block, result, context)
return result
async def _execute_script(
self,
function_name: str,
function_args: Any,
context: TaskContext
) -> Dict[str, Any]:
"""
执行指定的脚本函数
Args:
function_name: 要执行的函数名
function_args: 函数参数
context: 任务上下文
Returns:
Dict[str, Any]: 执行结果
"""
# 固定加载scripts/user_save/test1.py文件
script_file = "scripts/user_save/test1.py"
try:
# 动态加载脚本模块
import importlib.util
import sys
import os
# 处理打包后的路径
if getattr(sys, 'frozen', False):
# 在打包后的环境中
base_path = sys._MEIPASS if hasattr(sys, '_MEIPASS') else os.path.dirname(sys.executable)
script_path = os.path.join(base_path, script_file)
else:
# 在开发环境中
script_path = script_file
logger.info(f"正在加载脚本文件: {script_path}")
# 检查文件是否存在
if not os.path.exists(script_path):
return {
"success": False,
"message": f"脚本文件不存在: {script_path}"
}
# 加载模块
spec = importlib.util.spec_from_file_location("user_script", script_path)
if not spec:
return {
"success": False,
"message": f"无法加载脚本规范: {script_path}"
}
module = importlib.util.module_from_spec(spec)
sys.modules["user_script"] = module
spec.loader.exec_module(module)
# 检查函数是否存在
if not hasattr(module, function_name):
return {
"success": False,
"message": f"函数 {function_name} 在脚本 {script_path} 中不存在"
}
# 获取函数对象
func = getattr(module, function_name)
# 准备函数参数(处理不同类型的参数传递方式)
args = []
kwargs = {}
# 如果function_args是字典将其作为关键字参数传递
if isinstance(function_args, dict):
kwargs = function_args
# 如果function_args是列表将其作为位置参数传递
elif isinstance(function_args, list):
args = function_args
# 如果function_args是其他类型将其作为单个位置参数传递
# elif function_args is not None:
# args = [function_args]
elif isinstance(function_args, str):
function_args = eval(function_args)
# 调用函数
logger.info(f"调用函数 {function_name} 参数: args={args}, kwargs={kwargs}")
# 特殊处理:如果函数只接受一个参数且传入的是字典,直接传递字典
# print(function_args, "--------", type(function_args))
# result_value = func(function_args)
import inspect
sig = inspect.signature(func)
params = list(sig.parameters.keys())
if len(params) == 1 and isinstance(function_args, dict) and not args:
# 函数只有一个参数且传入的是字典,直接传递
result_value = func(function_args)
else:
result_value = func(**function_args)
# 检查是否是异步函数并等待结果
if inspect.iscoroutine(result_value):
import asyncio
result_value = await result_value
logger.info(f"函数 {function_name} 执行结果: {result_value}")
# 设置返回值到上下文 - 与ScriptBp不同这里会更新所有变量
if isinstance(result_value, dict):
# 如果返回值是字典,将字典中的每个键值对设置为任务变量
for key, value in result_value.items():
context.set_variable(key, value)
result_message = f"脚本函数 {function_name} 执行成功,已更新 {len(result_value)} 个变量"
else:
# 如果返回值不是字典将整个返回值设置为scriptResult变量
context.set_variable("scriptResult", result_value)
result_message = f"脚本函数 {function_name} 执行成功结果已保存为scriptResult变量"
# 构建成功结果
return {
"success": True,
"message": result_message,
"output": {
"result": result_value,
"functionName": function_name
}
}
except Exception as e:
logger.error(f"执行脚本函数时发生异常: {str(e)}", exc_info=True)
return {
"success": False,
"message": f"执行脚本函数 {function_name} 失败: {str(e)}"
}
async def _save_variables_to_database(self, context: TaskContext) -> None:
"""
将任务变量保存到数据库的任务记录中
Args:
context: 任务上下文
"""
try:
from sqlalchemy import update
from data.models.taskrecord import VWEDTaskRecord
from data.session import get_async_session
import json
# 获取任务记录ID
task_record_id = context.task_record_id
if not task_record_id:
logger.error("无法保存变量任务记录ID为空")
return
# 将任务变量转换为JSON字符串
variables_json = json.dumps(context.variables, ensure_ascii=False)
# 更新数据库记录
async with get_async_session() as session:
stmt = update(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id).values(
variables=variables_json
)
await session.execute(stmt)
await session.commit()
logger.info(f"已将任务变量保存到数据库记录 {task_record_id}")
except Exception as e:
logger.error(f"保存任务变量到数据库失败: {str(e)}")
# 脚本块处理器
@register_handler(ScriptBlockName.SCRIPT)
class ScriptBlockHandler(BlockHandler):
"""脚本块处理器"""
async def execute(
self,
block: Dict[str, Any],
input_params: Dict[str, Any],
context: TaskContext
) -> Dict[str, Any]:
"""执行脚本块"""
try:
# 获取函数名和参数
function_name = input_params.get("functionName")
function_args = input_params.get("functionArgs", {})
if not function_name:
result = {
"success": False,
"message": "缺少函数名"
}
# 记录执行结果
await self._record_task_log(block, result, context)
return result
# 调用脚本执行方法
exec_result = await self._execute_script(function_name, function_args, context)
# 记录执行结果
await self._record_task_log(block, exec_result, context)
return exec_result
except Exception as e:
result = {
"success": False,
"message": f"脚本执行失败: {str(e)}"
}
# 记录异常
await self._record_task_log(block, result, context)
return result
async def _execute_script(
self,
function_name: str,
function_args: Any,
context: TaskContext
) -> Dict[str, Any]:
"""
执行指定的脚本函数
Args:
function_name: 要执行的函数名
function_args: 函数参数
context: 任务上下文
Returns:
Dict[str, Any]: 执行结果
"""
# 固定加载scripts/user_save/test1.py文件
script_file = "scripts/user_save/test1.py"
try:
# 动态加载脚本模块
import importlib.util
import sys
import os
# 处理打包后的路径
if getattr(sys, 'frozen', False):
# 在打包后的环境中
base_path = sys._MEIPASS if hasattr(sys, '_MEIPASS') else os.path.dirname(sys.executable)
script_path = os.path.join(base_path, script_file)
else:
# 在开发环境中
script_path = script_file
logger.info(f"正在加载脚本文件: {script_path}")
# 检查文件是否存在
if not os.path.exists(script_path):
return {
"success": False,
"message": f"脚本文件不存在: {script_path}"
}
# 加载模块
spec = importlib.util.spec_from_file_location("user_script", script_path)
if not spec:
return {
"success": False,
"message": f"无法加载脚本规范: {script_path}"
}
module = importlib.util.module_from_spec(spec)
sys.modules["user_script"] = module
spec.loader.exec_module(module)
# 检查函数是否存在
if not hasattr(module, function_name):
return {
"success": False,
"message": f"函数 {function_name} 在脚本 {script_path} 中不存在"
}
# 获取函数对象
func = getattr(module, function_name)
# 准备函数参数(处理不同类型的参数传递方式)
args = []
kwargs = {}
# 如果function_args是字典将其作为关键字参数传递
if isinstance(function_args, dict):
kwargs = function_args
# 如果function_args是列表将其作为位置参数传递
elif isinstance(function_args, list):
args = function_args
# 如果function_args是其他类型将其作为单个位置参数传递
elif isinstance(function_args, str):
eval_result = eval(function_args)
if isinstance(eval_result, dict):
kwargs = eval_result
elif isinstance(eval_result, list):
args = eval_result
else:
args = [eval_result]
elif function_args is not None:
args = [function_args]
# 调用函数
logger.info(f"调用函数 {function_name} 参数: args={args}, kwargs={kwargs}")
# 特殊处理:如果函数只接受一个参数且传入的是字典,直接传递字典
import inspect
sig = inspect.signature(func)
params = list(sig.parameters.keys())
if len(params) == 1 and isinstance(function_args, dict) and not args:
# 函数只有一个参数且传入的是字典,直接传递
result_value = func(function_args)
else:
# 正常的参数传递
result_value = func(*args, **kwargs)
# 检查是否是异步函数并等待结果
if inspect.iscoroutine(result_value):
import asyncio
result_value = await result_value
logger.info(f"函数 {function_name} 执行结果: {result_value}")
# 设置返回值到上下文
context.set_variable("scriptResult", result_value)
# 构建成功结果
return {
"success": True,
"message": f"脚本函数 {function_name} 执行成功",
"output": {
"result": result_value,
"functionName": function_name
}
}
except Exception as e:
logger.error(f"执行脚本函数时发生异常: {str(e)}", exc_info=True)
return {
"success": False,
"message": f"执行脚本函数 {function_name} 失败: {str(e)}"
}