#!/usr/bin/env python # -*- coding: utf-8 -*- """ VWED.task 模块 - 任务系统集成 """ import json from typing import Dict, Any, Optional, List from utils.logger import get_logger from utils.json_parser import safe_parse_dict, safe_parse_list class VWEDTaskModule: """VWED.task 模块 - 任务系统集成""" def __init__(self, script_id: str): self.script_id = script_id self.logger = get_logger(f"script.{script_id}.task") async def _get_auth_token(self, auto_get_token: bool = True) -> Optional[str]: """获取认证token的通用方法 Args: auto_get_token: 是否自动获取登录token,默认true Returns: Optional[str]: 认证令牌,获取失败返回None """ if not auto_get_token: return None try: from services.sync_service import refresh_token_if_needed token = await refresh_token_if_needed() if token: self.logger.info(f"脚本 {self.script_id} 成功获取到登录token") return token else: self.logger.warning(f"脚本 {self.script_id} 获取登录token失败,继续使用空token") return None except Exception as e: self.logger.warning(f"脚本 {self.script_id} 获取token异常: {str(e)},继续使用空token") return None async def create_task( self, label: str, task_type: int = 1, remark: Optional[str] = None, period: Optional[int] = None, delay: Optional[int] = 3000, release_sites: Optional[bool] = True, tenant_id: str = "default", map_id: Optional[str] = None, auto_get_token: bool = True ) -> Dict[str, Any]: """创建VWED任务 Args: label: 任务名称 task_type: 任务类型,1-普通任务,2-定时任务 remark: 任务备注 period: 周期时间(毫秒),定时任务必填 delay: 延迟时间(毫秒),默认3000 release_sites: 是否释放站点,默认true tenant_id: 租户ID,默认"default" map_id: 相关地图ID auto_get_token: 是否自动获取登录token,默认true Returns: Dict[str, Any]: 创建结果 """ try: from services.task_service import TaskService, TaskNameExistsError from data.session import get_db # 获取登录token token = await self._get_auth_token(auto_get_token) # 获取数据库会话 db = next(get_db()) try: # 调用TaskService.create_task方法 result = TaskService.create_task( db=db, label=label, task_type=task_type, remark=remark, period=period, delay=delay, release_sites=release_sites, token=token, # 使用获取到的token tenant_id=tenant_id, map_id=map_id ) self.logger.info(f"脚本 {self.script_id} 成功创建任务: {label} (ID: {result['id']})") return { "success": True, "message": "任务创建成功", "data": result } except TaskNameExistsError as e: self.logger.warning(f"脚本 {self.script_id} 创建任务失败: {str(e)}") return { "success": False, "message": f"任务名称已存在: {str(e)}", "error_type": "name_exists" } except Exception as e: self.logger.error(f"脚本 {self.script_id} 创建任务失败: {str(e)}") return { "success": False, "message": f"创建任务失败: {str(e)}", "error_type": "create_error" } finally: db.close() except Exception as e: self.logger.error(f"脚本 {self.script_id} 任务创建异常: {str(e)}") return { "success": False, "message": f"任务创建异常: {str(e)}", "error_type": "system_error" } async def execute_task( self, task_id: str, parameters: Optional[List[Dict[str, Any]]] = None, source_system: str = "script", source_device: Optional[str] = None, auto_get_token: bool = True ) -> Dict[str, Any]: """执行VWED任务 Args: task_id: 任务ID parameters: 任务输入参数列表 source_system: 来源系统,默认"script" source_device: 来源设备标识 auto_get_token: 是否自动获取登录token,默认true Returns: Dict[str, Any]: 执行结果 """ try: from services.task_edit_service import TaskEditService from routes.model.task_edit_model import TaskEditRunRequest, TaskInputParam from data.enum.task_record_enum import SourceType # 获取登录token token = await self._get_auth_token(auto_get_token) # 构建运行请求 input_params = [] if parameters: for param in parameters: if isinstance(param, dict) and "name" in param and "defaultValue" in param: input_params.append(TaskInputParam( name=param["name"], defaultValue=param["defaultValue"] )) run_request = TaskEditRunRequest( taskId=task_id, params=input_params, source_type=SourceType.SYSTEM_SCHEDULING, source_system=source_system, source_device=source_device or f"script_{self.script_id}" ) # 调用TaskEditService.run_task方法 result = await TaskEditService.run_task( run_request=run_request, client_ip="127.0.0.1", # 本地脚本调用 client_info=f'{{"script_id": "{self.script_id}", "source": "VWED.task.execute_task"}}', tf_api_token=token # 使用获取到的token ) if result and result.get("success"): self.logger.info(f"脚本 {self.script_id} 成功启动任务: {task_id}") return { "success": True, "message": "任务启动成功", "data": result } else: error_msg = result.get("message", "任务启动失败") if result else "任务启动失败" self.logger.error(f"脚本 {self.script_id} 启动任务失败: {error_msg}") return { "success": False, "message": error_msg, "error_type": "execute_error" } except Exception as e: self.logger.error(f"脚本 {self.script_id} 执行任务异常: {str(e)}") return { "success": False, "message": f"执行任务异常: {str(e)}", "error_type": "system_error" } async def execute_task_with_record_id( self, task_id: str, task_record_id: Optional[str] = None, parameters: Optional[List[Dict[str, Any]]] = None, source_system: str = "script", source_device: Optional[str] = None, auto_get_token: bool = True ) -> Dict[str, Any]: """执行VWED任务,支持指定task_record_id Args: task_id: 任务ID task_record_id: 指定的任务记录ID,为空时自动生成 parameters: 任务输入参数列表 source_system: 来源系统,默认"script" source_device: 来源设备标识 auto_get_token: 是否自动获取登录token,默认true Returns: Dict[str, Any]: 执行结果 """ try: from services.task_edit_service import TaskEditService from routes.model.task_edit_model import TaskEditRunRequest, TaskInputParamNew from data.enum.task_record_enum import SourceType # 获取登录token token = await self._get_auth_token(auto_get_token) # 构建运行请求 input_params = [] if parameters: for param in parameters: if isinstance(param, dict) and "name" in param and "defaultValue" in param: input_params.append(TaskInputParamNew( name=param["name"], type="STRING", # 默认字符串类型 label=param["name"], defaultValue=param["defaultValue"] )) run_request = TaskEditRunRequest( taskId=task_id, params=input_params, source_type=SourceType.SYSTEM_SCHEDULING, source_system=source_system, source_device=source_device or f"script_{self.script_id}", task_record_id=task_record_id # 传递指定的task_record_id ) # 调用TaskEditService.run_task方法 result = await TaskEditService.run_task( run_request=run_request, client_ip="127.0.0.1", # 本地脚本调用 client_info=f'{{"script_id": "{self.script_id}", "source": "VWED.task.execute_task_with_record_id"}}', tf_api_token=token # 使用获取到的token ) if result and result.get("success"): self.logger.info(f"脚本 {self.script_id} 成功启动任务: {task_id} (记录ID: {result.get('taskRecordId', task_record_id)})") return { "success": True, "message": "任务启动成功", "data": result } else: error_msg = result.get("message", "任务启动失败") if result else "任务启动失败" self.logger.error(f"脚本 {self.script_id} 启动任务失败: {error_msg}") return { "success": False, "message": error_msg, "error_type": "execute_error" } except Exception as e: self.logger.error(f"脚本 {self.script_id} 执行任务异常: {str(e)}") return { "success": False, "message": f"执行任务异常: {str(e)}", "error_type": "system_error" } async def get_task_status(self, task_record_id: str) -> Dict[str, Any]: """获取任务执行状态 Args: task_record_id: 任务记录ID(不是任务定义ID) Returns: Dict[str, Any]: 任务状态信息 """ try: from data.session import get_async_session from data.models.taskrecord import VWEDTaskRecord from sqlalchemy import select async with get_async_session() as session: result = await session.execute( select(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id) ) task_record = result.scalar_one_or_none() if not task_record: return { "success": False, "message": "任务记录不存在", "error_type": "not_found" } # 构建状态信息 status_info = { "task_record_id": task_record.id, "task_def_id": task_record.task_def_id, "status": task_record.status, "progress": task_record.progress, "start_time": task_record.start_time.isoformat() if task_record.start_time else None, "end_time": task_record.end_time.isoformat() if task_record.end_time else None, "error_message": task_record.error_message, "variables": task_record.variables } self.logger.info(f"脚本 {self.script_id} 获取任务状态: {task_record_id}") return { "success": True, "message": "获取任务状态成功", "data": status_info } except Exception as e: self.logger.error(f"脚本 {self.script_id} 获取任务状态异常: {str(e)}") return { "success": False, "message": f"获取任务状态异常: {str(e)}", "error_type": "system_error" } async def check_task_param(self, param: str) -> bool: """校验任务参数 校验输入参数中的 task_id 和 task_label 字段对应的数据是否在数据库中存在。 Args: param: JSON字符串或Python字典字符串,包含task_id和task_label字段 Returns: bool: True表示数据存在,False表示数据不存在 """ try: # 使用通用解析方法 data = safe_parse_dict(param, self.script_id) if data is None: return False task_id = data.get("task_id", "") task_label = data.get("task_label", "") # 如果task_id和task_label都为空,返回False if not task_id and not task_label: self.logger.warning(f"脚本 {self.script_id} task_id和task_label都为空") return False from data.session import get_async_session from data.models.taskdef import VWEDTaskDef from sqlalchemy import select, or_ async with get_async_session() as session: # 构建查询条件:task_id或task_label匹配即可 conditions = [] if task_id: conditions.append(VWEDTaskDef.id == task_id) if task_label: conditions.append(VWEDTaskDef.label == task_label) # 查询数据库 query = select(VWEDTaskDef.id).where( or_(*conditions), VWEDTaskDef.is_deleted == False ) result = await session.execute(query) task_def = result.scalar_one_or_none() exists = task_def is not None self.logger.info(f"脚本 {self.script_id} 任务参数校验结果: task_id={task_id}, task_label={task_label}, exists={exists}") return exists except Exception as e: self.logger.error(f"脚本 {self.script_id} 校验任务参数异常: {str(e)}") return False async def create_wind_task(self, param: str) -> str: """异步创建并运行一个任务 本方法是非阻塞方法,将在脚本中创建一个线程,来执行天风任务的实例。 Args: param: JSON字符串,包含任务创建参数 Returns: str: JSON格式的创建结果 """ try: # 使用通用解析方法 data = safe_parse_dict(param, self.script_id) if data is None: return "" task_id = data.get("task_id") task_label = data.get("task_label") task_record_id = data.get("task_record_id") input_params = data.get("input_params", []) # 验证必选参数 if not task_id and not task_label: return json.dumps({"code": -1, "msg": "task_id和task_label至少需要提供一个"}, ensure_ascii=False) # 解析输入参数 parameters = [] if input_params: # 如果input_params是字符串,尝试解析为JSON if isinstance(input_params, str): try: input_params_data = json.loads(input_params) except: input_params_data = [] else: # 如果input_params已经是列表,直接使用 input_params_data = input_params # 处理参数列表,保留所有字段信息 if isinstance(input_params_data, list): # 如果是完整的参数配置列表:[{"name": "param1", "label": "参数1", "type": "STRING", "defaultValue": "", ...}, ...] for param in input_params_data: if isinstance(param, dict) and "name" in param: # 保留完整的参数配置信息 param_config = { "name": param["name"], "defaultValue": str(param.get("defaultValue", "")) } # 如果有其他字段也保留 if "label" in param: param_config["label"] = param["label"] if "type" in param: param_config["type"] = param["type"] if "required" in param: param_config["required"] = param["required"] if "remark" in param: param_config["remark"] = param["remark"] if "checked" in param: param_config["checked"] = param["checked"] parameters.append(param_config) elif isinstance(input_params_data, dict): # 如果是简单的键值对格式:{"param1": "value1", "param2": "value2"} for key, value in input_params_data.items(): parameters.append({ "name": key, "defaultValue": str(value) }) # 使用task_id或task_label查找任务并执行 target_task_id = task_id if task_id else None if not target_task_id and task_label: # 根据task_label查找task_id的逻辑 from data.session import get_async_session from data.models.taskdef import VWEDTaskDef from sqlalchemy import select async with get_async_session() as session: result = await session.execute(select(VWEDTaskDef.id).where( VWEDTaskDef.label == task_label, VWEDTaskDef.is_deleted == False )) task_def = result.scalar_one_or_none() if task_def: target_task_id = task_def if not target_task_id: return json.dumps({"code": -1, "msg": "未找到指定的任务"}, ensure_ascii=False) # 如果提供了task_record_id,检查是否已存在 if task_record_id and await self.is_task_record_id_exist(task_record_id): return json.dumps({"code": -1, "msg": f"任务记录ID {task_record_id} 已存在"}, ensure_ascii=False) # 执行任务 result = await self.execute_task_with_record_id( task_id=target_task_id, task_record_id=task_record_id, parameters=parameters, source_system="script", source_device=f"script_{self.script_id}" ) if result.get("success"): return json.dumps({"code": 200, "msg": "任务创建成功"}, ensure_ascii=False) else: return json.dumps({"code": -1, "msg": result.get("message", "任务创建失败")}, ensure_ascii=False) except Exception as e: self.logger.error(f"脚本 {self.script_id} 创建任务异常: {str(e)}") return json.dumps({"code": -1, "msg": f"创建任务异常: {str(e)}"}, ensure_ascii=False) async def get_task_record_by_id(self, task_record_id: str) -> str: """根据任务实例ID查询任务实例 Args: task_record_id: 任务实例的唯一ID Returns: str: JSON格式的任务实例信息,不存在则返回"null" """ try: from data.session import get_async_session from data.models.taskrecord import VWEDTaskRecord from sqlalchemy import select async with get_async_session() as session: result = await session.execute( select(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id) ) task_record = result.scalar_one_or_none() if not task_record: return "null" # 构建返回数据,按照文档格式 task_info = { "id": task_record.id, "def_id": task_record.def_id, "def_label": task_record.def_label, "status": task_record.status, "created_on": task_record.created_at.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] if task_record.created_at else None, "ended_on": task_record.ended_on.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] if task_record.ended_on else None, "state_description": task_record.state_description or "", "agv_id": task_record.agv_id or "" } return json.dumps(task_info, ensure_ascii=False) except Exception as e: self.logger.error(f"脚本 {self.script_id} 查询任务实例异常: {str(e)}") raise e # def get_task_record_list_by_out_order_no(self, out_order_no: str) -> str: # """根据外部单号查询任务实例列表 # # Args: # out_order_no: 外部单号ID # # Returns: # str: JSON数组格式的任务实例列表 # """ # try: # # 暂时返回空数组,因为当前表结构中没有外部订单号字段 # # 需要在VWEDTaskRecord模型中添加out_order_no字段 # return "[]" # # except Exception as e: # self.logger.error(f"脚本 {self.script_id} 根据外部单号查询任务异常: {str(e)}") # raise e async def get_task_record_by_agv_id(self, agv_id: str) -> str: """根据机器人ID查询任务实例列表 Args: agv_id: 机器人ID Returns: str: JSON数组格式的任务实例列表 """ try: from data.session import get_async_session from data.models.taskrecord import VWEDTaskRecord from sqlalchemy import select async with get_async_session() as session: result = await session.execute( select(VWEDTaskRecord).where(VWEDTaskRecord.agv_id == agv_id) ) task_records = result.scalars().all() if not task_records: return "[]" # 构建返回数据列表 task_list = [] for task_record in task_records: task_info = { "id": task_record.id, "def_id": task_record.def_id, "def_label": task_record.def_label, "status": task_record.status, "created_on": task_record.created_at.strftime("%Y-%m-%d %H:%M:%S") if task_record.created_at else None, "ended_reason": task_record.ended_reason or "运行结束", "ended_on": task_record.ended_on.strftime("%Y-%m-%d %H:%M:%S") if task_record.ended_on else None, "state_description": task_record.state_description or "", } task_list.append(task_info) return json.dumps(task_list, ensure_ascii=False) except Exception as e: self.logger.error(f"脚本 {self.script_id} 根据机器人ID查询任务异常: {str(e)}") raise e async def mark_complete(self, task_id: str, auto_get_token: bool = True) -> Dict[str, Any]: """运行任务封口 Args: task_id: VWED任务系统任务ID auto_get_token: 是否自动获取登录token,默认true Returns: Dict[str, Any]: 封口结果 """ try: # 获取认证token token = await self._get_auth_token(auto_get_token) # 调用 closure_task 接口 from services.sync_service import closure_task result = await closure_task(task_id=task_id, token=token) if result and result.get("success"): self.logger.info(f"脚本 {self.script_id} 成功封口任务: {task_id}") return { "success": True, "message": "任务封口成功", "data": result } else: error_msg = result.get("message", "任务封口失败") if result else "任务封口失败" self.logger.error(f"脚本 {self.script_id} 封口任务失败: {task_id}, 错误: {error_msg}") return { "success": False, "message": error_msg, "error_type": "closure_error" } except Exception as e: self.logger.error(f"脚本 {self.script_id} 标记运单完成异常: {str(e)}") return { "success": False, "message": f"标记运单完成异常: {str(e)}", "error_type": "system_error" } async def is_task_record_id_exist(self, task_record_id: str) -> bool: """校验任务实例ID是否存在 Args: task_record_id: 任务实例id Returns: bool: True表示存在,False表示不存在 """ try: from data.session import get_async_session from data.models.taskrecord import VWEDTaskRecord from sqlalchemy import select async with get_async_session() as session: result = await session.execute( select(VWEDTaskRecord.id).where(VWEDTaskRecord.id == task_record_id) ) task_record = result.scalar_one_or_none() return task_record is not None except Exception as e: self.logger.error(f"脚本 {self.script_id} 校验任务实例ID异常: {str(e)}") raise e async def is_task_label_exist(self, task_name: str) -> bool: """校验任务名称是否存在 Args: task_name: 任务名称 Returns: bool: True表示存在,False表示不存在 """ try: from data.session import get_async_session from data.models.taskdef import VWEDTaskDef from sqlalchemy import select async with get_async_session() as session: result = await session.execute( select(VWEDTaskDef.id).where( VWEDTaskDef.label == task_name, VWEDTaskDef.is_deleted == False ) ) task_def = result.scalar_one_or_none() return task_def is not None except Exception as e: self.logger.error(f"脚本 {self.script_id} 校验任务名称异常: {str(e)}") raise e async def query_task_record(self, param: str) -> str: """根据查询条件获取任务实例 Args: param: JSON字符串,包含查询条件 Returns: str: JSON数组格式的任务实例列表 """ try: data = safe_parse_dict(param, self.script_id) if data is None: return "[]" from data.session import get_async_session from data.models.taskrecord import VWEDTaskRecord from sqlalchemy import select, and_ from datetime import datetime async with get_async_session() as session: # 构建查询条件 conditions = [] # 任务ID条件 if data.get("task_id"): conditions.append(VWEDTaskRecord.def_id == data["task_id"]) # 任务实例ID条件 if data.get("task_record_id"): conditions.append(VWEDTaskRecord.id == data["task_record_id"]) # 状态条件 if data.get("status"): conditions.append(VWEDTaskRecord.status == int(data["status"])) # 机器人ID条件 if data.get("agv_id"): conditions.append(VWEDTaskRecord.agv_id == data["agv_id"]) # 时间范围条件 if data.get("start_date"): start_time = datetime.strptime(data["start_date"], "%Y-%m-%d %H:%M:%S") conditions.append(VWEDTaskRecord.created_at >= start_time) if data.get("end_date"): end_time = datetime.strptime(data["end_date"], "%Y-%m-%d %H:%M:%S") conditions.append(VWEDTaskRecord.created_at <= end_time) # 执行查询 query = select(VWEDTaskRecord) if conditions: query = query.where(and_(*conditions)) result = await session.execute(query) task_records = result.scalars().all() if not task_records: return "[]" # 构建返回数据列表 task_list = [] for task_record in task_records: task_info = { "id": task_record.id, "def_id": task_record.def_id, "def_label": task_record.def_label, "status": task_record.status, "created_on": task_record.created_at.strftime("%Y-%m-%d %H:%M:%S") if task_record.created_at else None, "ended_reason": task_record.ended_reason or "运行结束", "ended_on": task_record.ended_on.strftime("%Y-%m-%d %H:%M:%S") if task_record.ended_on else None, "state_description": task_record.state_description or "", } task_list.append(task_info) return json.dumps(task_list, ensure_ascii=False) except Exception as e: self.logger.error(f"脚本 {self.script_id} 查询任务实例异常: {str(e)}") raise e # def receive_third_order(self, order_param: str) -> bool: # """接收并持久化第三方系统任务请求 # Args: # order_param: JSON字符串,第三方请求参数 # Returns: # bool: True表示成功,False表示失败 # """ # try: # # 这里需要实现第三方订单表的创建和插入逻辑 # # 暂时返回True,表示接收成功 # self.logger.info(f"脚本 {self.script_id} 接收第三方订单: {order_param}") # return True # except Exception as e: # self.logger.error(f"脚本 {self.script_id} 接收第三方订单异常: {str(e)}") # return False def save_task_log(self, log_param: str) -> None: """保存任务运行时日志 新增RDS任务实例的运行日志,该日志将显示在任务实例的监控界面中。 Args: log_param: JSON字符串,包含日志信息 """ try: data = safe_parse_dict(log_param, self.script_id) if data is None: return # 记录到系统日志 level = data.get("level", 1) message = data.get("message", "") task_id = data.get("task_id", "") task_record_id = data.get("task_record_id", "") block_id = data.get("block_id", "") log_msg = f"任务日志 [任务ID:{task_id}] [实例ID:{task_record_id}] [块ID:{block_id}] {message}" if level == 1: # 正常运行 self.logger.info(log_msg) elif level == 2: # 正常停止 self.logger.warning(log_msg) elif level == 3: # 异常中断 self.logger.error(log_msg) else: self.logger.info(log_msg) except Exception as e: self.logger.error(f"脚本 {self.script_id} 保存任务日志异常: {str(e)}") async def sync_create_wind_task(self, task_param: str) -> str: """创建并运行一个任务 本方法是阻塞方法,可以在脚本中创建一个天风任务的实例,并在任务执行结束后返回。 Args: task_param: JSON字符串,包含任务创建参数 Returns: str: "9013"表示任务正常结束,"9014"表示任务异常结束 """ try: # 使用create_wind_task的逻辑创建任务 result_json = await self.create_wind_task(task_param) result = json.loads(result_json) if result.get("code") != 200: return "9014" # 解析参数获取task_record_id用于监控 data = safe_parse_dict(task_param, self.script_id) if data is None: return "9014" task_record_id = data.get("task_record_id") if not task_record_id: self.logger.warning(f"脚本 {self.script_id} 无法获取task_record_id,无法监控任务状态") return "9014" # 等待任务完成 return await self._wait_for_task_completion(task_record_id) except Exception as e: self.logger.error(f"脚本 {self.script_id} 同步创建任务异常: {str(e)}") return "9014" async def _wait_for_task_completion(self, task_record_id: str, check_interval: int = 1, timeout: int = 3600) -> str: """等待任务完成 Args: task_record_id: 任务记录ID check_interval: 检查间隔(秒) timeout: 超时时间(秒) Returns: str: "9013"表示任务正常结束,"9014"表示任务异常结束 """ import asyncio from data.enum.task_record_enum import TaskStatus start_time = asyncio.get_event_loop().time() while True: try: # 检查是否超时 if asyncio.get_event_loop().time() - start_time > timeout: self.logger.warning(f"脚本 {self.script_id} 等待任务完成超时: {task_record_id}") return "9014" # 获取任务状态 status_result = await self.get_task_status(task_record_id) if not status_result.get("success"): self.logger.error(f"脚本 {self.script_id} 获取任务状态失败: {task_record_id}") return "9014" task_data = status_result.get("data", {}) status = task_data.get("status") # 检查任务状态 if status == TaskStatus.COMPLETED: self.logger.info(f"脚本 {self.script_id} 任务正常完成: {task_record_id}") return "9013" elif status in [TaskStatus.FAILED, TaskStatus.CANCELED]: self.logger.warning(f"脚本 {self.script_id} 任务异常结束: {task_record_id}, status: {status}") return "9014" elif status in [TaskStatus.RUNNING, TaskStatus.QUEUED, TaskStatus.PAUSED]: # 任务还在执行中,继续等待 self.logger.debug(f"脚本 {self.script_id} 任务执行中: {task_record_id}, status: {status}") await asyncio.sleep(check_interval) else: # 未知状态 self.logger.warning(f"脚本 {self.script_id} 任务状态未知: {task_record_id}, status: {status}") return "9014" except Exception as e: self.logger.error(f"脚本 {self.script_id} 监控任务状态异常: {task_record_id}, 错误: {str(e)}") return "9014" async def terminate_tasks(self, param: str) -> None: """终止任务 本方法是非阻塞方法,可用于终止多个正在运行的天风任务实例。 Args: param: JSON字符串,包含要终止的任务实例ID列表 """ try: data = safe_parse_list(param, self.script_id) if data is None: return # 获取认证token token = await self._get_auth_token(auto_get_token=True) for task_info in data: task_record_id = task_info.get("task_record_id") if task_record_id: try: # 1. 调用调度器取消本地任务 from services.enhanced_scheduler import scheduler scheduler_result = await scheduler.cancel_task(task_record_id) # 2. 调用天风系统接口设置任务为终止状态 from services.sync_service import set_task_terminated tf_result = await set_task_terminated(task_record_id, token) # 记录结果 if scheduler_result.get("success"): self.logger.info(f"脚本 {self.script_id} 成功在调度器中终止任务: {task_record_id}") else: self.logger.warning(f"脚本 {self.script_id} 调度器中终止任务失败: {task_record_id}") if tf_result and tf_result.get("success"): self.logger.info(f"脚本 {self.script_id} 成功在天风系统中设置任务终止状态: {task_record_id}") else: self.logger.warning(f"脚本 {self.script_id} 天风系统中设置任务终止状态失败: {task_record_id}") # 只要任一操作成功就认为整体成功 if scheduler_result.get("success") or (tf_result and tf_result.get("success")): self.logger.info(f"脚本 {self.script_id} 任务终止操作完成: {task_record_id}") else: self.logger.error(f"脚本 {self.script_id} 任务终止操作全部失败: {task_record_id}") except Exception as e: self.logger.error(f"脚本 {self.script_id} 终止任务异常: {task_record_id}, 错误: {str(e)}") except Exception as e: self.logger.error(f"脚本 {self.script_id} 终止任务异常: {str(e)}") raise e async def get_children_task_record_id(self, record_id: str) -> str: """查询子任务的任务id列表 本方法是非阻塞方法,可根据父任务的id查询所有子任务的id。 Args: record_id: 父任务的record_id Returns: str: 包含子任务id的JSON字符串 """ try: from data.session import get_async_session from data.models.taskrecord import VWEDTaskRecord from sqlalchemy import select async with get_async_session() as session: result = await session.execute( select(VWEDTaskRecord.id).where(VWEDTaskRecord.parent_task_record_id == record_id) ) child_ids = [row[0] for row in result.fetchall()] return json.dumps(child_ids, ensure_ascii=False) except Exception as e: self.logger.error(f"脚本 {self.script_id} 查询子任务ID列表异常: {str(e)}") raise e async def set_task_priority(self, task_record_id: str, priority: int) -> None: """设置任务优先级 本方法是非阻塞方法,可根据设置任务的优先级。 Args: task_record_id: 任务的实例id priority: 任务的优先级 Raises: Exception: 当任务记录ID不存在时抛出异常 """ try: from data.session import get_async_session from data.models.taskrecord import VWEDTaskRecord from sqlalchemy import select, update async with get_async_session() as session: # 首先查询任务记录是否存在 result = await session.execute( select(VWEDTaskRecord.id).where(VWEDTaskRecord.id == task_record_id) ) task_record = result.scalar_one_or_none() if not task_record: error_msg = f"任务记录ID不存在: {task_record_id}" self.logger.error(f"脚本 {self.script_id} {error_msg}") raise Exception(error_msg) # 更新任务优先级 await session.execute( update(VWEDTaskRecord) .where(VWEDTaskRecord.id == task_record_id) .values(priority=priority) ) await session.commit() self.logger.info(f"脚本 {self.script_id} 设置任务优先级成功: {task_record_id} -> {priority}") except Exception as e: self.logger.error(f"脚本 {self.script_id} 设置任务优先级异常: {str(e)}") raise e