#!/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)}" }