2025-05-12 15:43:21 +08:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
任务运行记录服务模块
|
|
|
|
|
提供任务运行记录相关的服务方法
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import json
|
2025-09-30 13:52:36 +08:00
|
|
|
|
from typing import Dict, List, Any
|
|
|
|
|
from sqlalchemy import select
|
2025-05-12 15:43:21 +08:00
|
|
|
|
import datetime
|
|
|
|
|
|
|
|
|
|
from data.models.blockrecord import VWEDBlockRecord
|
|
|
|
|
from data.models.taskrecord import VWEDTaskRecord
|
|
|
|
|
from data.models.tasklog import VWEDTaskLog
|
|
|
|
|
from data.session import get_async_session
|
|
|
|
|
from utils.logger import get_logger
|
|
|
|
|
from data.enum.task_block_record_enum import TaskBlockRecordStatus
|
2025-09-09 10:41:27 +08:00
|
|
|
|
|
2025-05-12 15:43:21 +08:00
|
|
|
|
# 设置日志
|
|
|
|
|
logger = get_logger("service.task_record_service")
|
|
|
|
|
|
|
|
|
|
class TaskRecordService:
|
|
|
|
|
"""
|
|
|
|
|
任务运行记录服务类
|
|
|
|
|
提供与任务运行记录相关的方法
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def get_task_blocks(task_record_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
获取指定任务记录下的所有块运行情况
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
task_record_id: 任务记录ID
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict: 包含块运行情况的字典
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 构建查询语句
|
|
|
|
|
query = select(VWEDBlockRecord).where(
|
|
|
|
|
VWEDBlockRecord.task_record_id == task_record_id
|
|
|
|
|
).order_by(VWEDBlockRecord.started_on)
|
|
|
|
|
|
|
|
|
|
# 执行查询
|
|
|
|
|
result = await session.execute(query)
|
|
|
|
|
blocks = result.scalars().all()
|
|
|
|
|
|
|
|
|
|
if not blocks:
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"未找到任务记录 {task_record_id} 的块运行情况",
|
|
|
|
|
"data": []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 转换为字典列表
|
|
|
|
|
block_list = []
|
|
|
|
|
for block in blocks:
|
|
|
|
|
block_dict = {
|
|
|
|
|
"id": block.id,
|
|
|
|
|
"block_name": block.block_name,
|
|
|
|
|
"block_id": block.block_id,
|
|
|
|
|
"status": block.status,
|
|
|
|
|
"started_on": block.started_on.isoformat() if block.started_on else None,
|
|
|
|
|
"ended_on": block.ended_on.isoformat() if block.ended_on else None,
|
|
|
|
|
"ended_reason": block.ended_reason,
|
|
|
|
|
"block_execute_name": block.block_execute_name,
|
|
|
|
|
"block_input_params_value": json.loads(block.block_input_params_value) if block.block_input_params_value else None,
|
|
|
|
|
"block_out_params_value": json.loads(block.block_out_params_value) if block.block_out_params_value else None,
|
|
|
|
|
"remark": block.remark
|
|
|
|
|
}
|
|
|
|
|
block_list.append(block_dict)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": f"成功获取任务记录 {task_record_id} 的块运行情况",
|
|
|
|
|
"data": block_list
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"获取任务块运行情况失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取任务块运行情况失败: {str(e)}",
|
|
|
|
|
"data": []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def get_block_detail(block_record_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
获取指定块记录的详细信息
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
block_record_id: 块记录ID
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict: 包含块记录详细信息的字典
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 构建查询语句
|
|
|
|
|
query = select(VWEDBlockRecord).where(
|
|
|
|
|
VWEDBlockRecord.id == block_record_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 执行查询
|
|
|
|
|
result = await session.execute(query)
|
|
|
|
|
block = result.scalars().first()
|
|
|
|
|
|
|
|
|
|
if not block:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"未找到ID为 {block_record_id} 的块记录",
|
|
|
|
|
"data": None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 转换为字典
|
|
|
|
|
block_dict = {
|
|
|
|
|
"id": block.id,
|
|
|
|
|
"block_name": block.block_name,
|
|
|
|
|
"block_id": block.block_id,
|
|
|
|
|
"block_config_id": block.block_config_id,
|
|
|
|
|
"block_input_params": json.loads(block.block_input_params) if block.block_input_params else None,
|
|
|
|
|
"block_input_params_value": json.loads(block.block_input_params_value) if block.block_input_params_value else None,
|
|
|
|
|
"block_out_params_value": json.loads(block.block_out_params_value) if block.block_out_params_value else None,
|
|
|
|
|
"block_internal_variables": json.loads(block.block_internal_variables) if block.block_internal_variables else None,
|
|
|
|
|
"block_execute_name": block.block_execute_name,
|
|
|
|
|
"task_id": block.task_id,
|
|
|
|
|
"task_record_id": block.task_record_id,
|
|
|
|
|
"started_on": block.started_on.isoformat() if block.started_on else None,
|
|
|
|
|
"ended_on": block.ended_on.isoformat() if block.ended_on else None,
|
|
|
|
|
"ended_reason": block.ended_reason,
|
|
|
|
|
"status": block.status,
|
|
|
|
|
"ctrl_status": block.ctrl_status,
|
|
|
|
|
"input_params": json.loads(block.input_params) if block.input_params else None,
|
|
|
|
|
"internal_variables": json.loads(block.internal_variables) if block.internal_variables else None,
|
|
|
|
|
"output_params": json.loads(block.output_params) if block.output_params else None,
|
|
|
|
|
"version": block.version,
|
|
|
|
|
"remark": block.remark
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "成功获取块记录详情",
|
|
|
|
|
"data": block_dict
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"获取块记录详情失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取块记录详情失败: {str(e)}",
|
|
|
|
|
"data": None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def stop_task_record(task_record_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
停止指定任务记录下的所有运行任务实例,同时禁用定时任务
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
task_record_id: 任务记录ID
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict: 包含停止结果的响应
|
|
|
|
|
"""
|
|
|
|
|
# 导入增强版调度器
|
|
|
|
|
from services.enhanced_scheduler import scheduler
|
|
|
|
|
from data.enum.task_record_enum import TaskStatus
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 查找所有正在运行的任务记录
|
|
|
|
|
running_tasks_query = await session.execute(
|
|
|
|
|
select(VWEDTaskRecord)
|
|
|
|
|
.where(
|
|
|
|
|
VWEDTaskRecord.id == task_record_id,
|
|
|
|
|
VWEDTaskRecord.status == TaskStatus.RUNNING # 执行中状态码
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
running_tasks = running_tasks_query.scalars().first()
|
|
|
|
|
if not running_tasks:
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "任务记录中没有运行中的任务"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 取消所有运行中的任务
|
|
|
|
|
cancel_result = await scheduler.cancel_task(task_record_id)
|
|
|
|
|
if cancel_result.get("success", False):
|
|
|
|
|
running_tasks.status = TaskStatus.CANCELED
|
|
|
|
|
running_tasks.ended_on = datetime.now()
|
|
|
|
|
running_tasks.ended_reason = "任务终止"
|
|
|
|
|
await session.commit()
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "任务终止成功",
|
|
|
|
|
"data": {
|
|
|
|
|
"task_record_id": task_record_id,
|
|
|
|
|
"status": running_tasks.status,
|
|
|
|
|
"ended_on": running_tasks.ended_on,
|
|
|
|
|
"ended_reason": running_tasks.ended_reason
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "任务终止失败",
|
|
|
|
|
"data": cancel_result
|
|
|
|
|
}
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"任务记录终止失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"任务记录终止失败: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
@staticmethod
|
|
|
|
|
async def set_task_error(task_record_id: str, error_reason: str) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
将指定任务记录及其相关任务块状态设置为错误状态
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
task_record_id: 任务记录ID
|
|
|
|
|
error_reason: 错误原因
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict: 包含设置结果的响应
|
|
|
|
|
"""
|
|
|
|
|
from services.enhanced_scheduler import scheduler
|
|
|
|
|
from data.enum.task_record_enum import TaskStatus
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 查找任务记录
|
|
|
|
|
task_query = await session.execute(
|
|
|
|
|
select(VWEDTaskRecord)
|
|
|
|
|
.where(VWEDTaskRecord.id == task_record_id)
|
|
|
|
|
)
|
|
|
|
|
task_record = task_query.scalars().first()
|
|
|
|
|
|
|
|
|
|
if not task_record:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"未找到任务记录 {task_record_id}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 如果任务正在运行,先取消它
|
|
|
|
|
if task_record.status == TaskStatus.RUNNING:
|
2025-08-13 15:27:04 +08:00
|
|
|
|
cancel_result = await scheduler.set_task_error(task_record_id, error_reason)
|
2025-07-14 10:29:37 +08:00
|
|
|
|
if not cancel_result.get("success", False):
|
|
|
|
|
logger.warning(f"取消任务 {task_record_id} 失败: {cancel_result}")
|
2025-08-13 15:27:04 +08:00
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 设置任务状态为失败
|
|
|
|
|
task_record.status = TaskStatus.FAILED
|
|
|
|
|
task_record.ended_on = datetime.now()
|
|
|
|
|
task_record.ended_reason = error_reason
|
|
|
|
|
|
|
|
|
|
# 查找所有相关的任务块记录
|
|
|
|
|
blocks_query = await session.execute(
|
|
|
|
|
select(VWEDBlockRecord)
|
|
|
|
|
.where(VWEDBlockRecord.task_record_id == task_record_id)
|
|
|
|
|
)
|
|
|
|
|
block_records = blocks_query.scalars().all()
|
|
|
|
|
|
|
|
|
|
# 设置所有任务块状态为失败
|
|
|
|
|
updated_blocks = []
|
|
|
|
|
for block in block_records:
|
|
|
|
|
# 只更新未完成的任务块
|
|
|
|
|
if block.status not in [TaskBlockRecordStatus.SUCCESS, TaskBlockRecordStatus.FAILED]:
|
|
|
|
|
block.status = TaskBlockRecordStatus.FAILED
|
|
|
|
|
block.ended_on = datetime.now()
|
|
|
|
|
block.ended_reason = error_reason
|
|
|
|
|
updated_blocks.append({
|
|
|
|
|
"block_id": block.id,
|
|
|
|
|
"block_name": block.block_name,
|
|
|
|
|
"status": block.status
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# 提交所有更改
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "任务状态设置为错误成功",
|
|
|
|
|
"data": {
|
|
|
|
|
"task_record_id": task_record_id,
|
|
|
|
|
"task_status": task_record.status,
|
|
|
|
|
"error_reason": error_reason,
|
|
|
|
|
"ended_on": task_record.ended_on,
|
|
|
|
|
"updated_blocks_count": len(updated_blocks),
|
|
|
|
|
"updated_blocks": updated_blocks
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"设置任务错误状态失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"设置任务错误状态失败: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-12 15:43:21 +08:00
|
|
|
|
@staticmethod
|
|
|
|
|
async def get_block_results(task_record_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""
|
2025-09-30 13:52:36 +08:00
|
|
|
|
获取指定任务记录的执行结果,按照任务定义的嵌套结构组织
|
2025-05-12 15:43:21 +08:00
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
task_record_id: 任务记录ID
|
|
|
|
|
|
|
|
|
|
Returns:
|
2025-09-30 13:52:36 +08:00
|
|
|
|
Dict: 包含嵌套结构执行结果的响应
|
2025-05-12 15:43:21 +08:00
|
|
|
|
"""
|
2025-09-09 10:41:27 +08:00
|
|
|
|
from services.execution.handlers.model.block_name import BLOCK_NAME_STR
|
2025-05-12 15:43:21 +08:00
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
2025-09-30 13:52:36 +08:00
|
|
|
|
# 获取任务记录信息,包含任务定义详情
|
|
|
|
|
task_query = select(VWEDTaskRecord).where(
|
|
|
|
|
VWEDTaskRecord.id == task_record_id
|
|
|
|
|
)
|
|
|
|
|
task_result = await session.execute(task_query)
|
|
|
|
|
task_record = task_result.scalars().first()
|
2025-05-12 15:43:21 +08:00
|
|
|
|
|
2025-09-30 13:52:36 +08:00
|
|
|
|
if not task_record:
|
2025-05-12 15:43:21 +08:00
|
|
|
|
return {
|
2025-09-30 13:52:36 +08:00
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"未找到任务记录 {task_record_id}",
|
|
|
|
|
"data": None
|
2025-05-12 15:43:21 +08:00
|
|
|
|
}
|
2025-09-30 13:52:36 +08:00
|
|
|
|
|
|
|
|
|
# 获取所有块执行记录
|
|
|
|
|
blocks_query = select(VWEDBlockRecord).where(
|
|
|
|
|
VWEDBlockRecord.task_record_id == task_record_id
|
|
|
|
|
).order_by(VWEDBlockRecord.started_on)
|
|
|
|
|
blocks_result = await session.execute(blocks_query)
|
|
|
|
|
blocks = blocks_result.scalars().all()
|
|
|
|
|
|
|
|
|
|
# 获取所有任务日志
|
|
|
|
|
logs_query = select(VWEDTaskLog).where(
|
|
|
|
|
VWEDTaskLog.task_record_id == task_record_id
|
|
|
|
|
)
|
|
|
|
|
logs_result = await session.execute(logs_query)
|
|
|
|
|
logs = logs_result.scalars().all()
|
|
|
|
|
|
|
|
|
|
# 构建块记录和日志的映射
|
|
|
|
|
block_map = {block.block_name: block for block in blocks}
|
|
|
|
|
log_map = {}
|
|
|
|
|
for log in logs:
|
|
|
|
|
if log.task_block_id not in log_map:
|
|
|
|
|
log_map[log.task_block_id] = []
|
|
|
|
|
log_map[log.task_block_id].append(log)
|
|
|
|
|
|
|
|
|
|
# 解析任务定义详情
|
|
|
|
|
task_def_detail = None
|
|
|
|
|
if task_record.task_def_detail:
|
|
|
|
|
try:
|
|
|
|
|
task_def_detail = json.loads(task_record.task_def_detail)
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
|
logger.warning(f"任务记录 {task_record_id} 的task_def_detail解析失败")
|
|
|
|
|
|
|
|
|
|
if not task_def_detail or 'rootBlock' not in task_def_detail:
|
|
|
|
|
# 如果没有任务定义详情,回退到原始的平铺模式
|
|
|
|
|
return await TaskRecordService._get_block_results_fallback(
|
|
|
|
|
task_record_id, blocks, logs, BLOCK_NAME_STR
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 构建嵌套结构
|
|
|
|
|
nested_results = await TaskRecordService._build_nested_block_results(
|
|
|
|
|
task_def_detail['rootBlock'],
|
|
|
|
|
block_map,
|
|
|
|
|
log_map,
|
|
|
|
|
BLOCK_NAME_STR,
|
|
|
|
|
level=0
|
|
|
|
|
)
|
|
|
|
|
|
2025-05-12 15:43:21 +08:00
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
2025-09-30 13:52:36 +08:00
|
|
|
|
"message": "成功获取任务记录嵌套执行结果",
|
|
|
|
|
"data": nested_results
|
2025-05-12 15:43:21 +08:00
|
|
|
|
}
|
2025-09-30 13:52:36 +08:00
|
|
|
|
|
2025-05-12 15:43:21 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"获取任务记录执行结果失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取任务记录执行结果失败: {str(e)}"
|
|
|
|
|
}
|
2025-09-30 13:52:36 +08:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def _build_nested_block_results(
|
|
|
|
|
block_def: Dict[str, Any],
|
|
|
|
|
block_map: Dict[str, VWEDBlockRecord],
|
|
|
|
|
log_map: Dict[str, List[VWEDTaskLog]],
|
|
|
|
|
block_name_mapping: Dict[str, str],
|
|
|
|
|
level: int = 0
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
递归构建嵌套的块执行结果
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
block_def: 任务块定义
|
|
|
|
|
block_map: 块名称到执行记录的映射
|
|
|
|
|
log_map: 块名称到日志记录的映射
|
|
|
|
|
block_name_mapping: 块类型名称映射
|
|
|
|
|
level: 嵌套层级(用于调试)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict: 嵌套的块执行结果
|
|
|
|
|
"""
|
|
|
|
|
block_id = str(block_def.get('id', ''))
|
|
|
|
|
block_name = block_def.get('name', '')
|
|
|
|
|
block_type = block_def.get('blockType', '')
|
|
|
|
|
|
|
|
|
|
# 获取执行记录
|
|
|
|
|
execution_record = block_map.get(block_name)
|
|
|
|
|
|
|
|
|
|
# 构建基础结果结构
|
|
|
|
|
result = {
|
|
|
|
|
"blockId": block_id,
|
|
|
|
|
"blockName": block_name,
|
|
|
|
|
"blockType": block_type,
|
|
|
|
|
"blockTypeName": block_name_mapping.get(block_type, block_type),
|
|
|
|
|
"level": level,
|
|
|
|
|
"executionStatus": None,
|
|
|
|
|
"executionRecord": None,
|
|
|
|
|
"logs": [],
|
|
|
|
|
"children": []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 如果有执行记录,添加执行信息
|
|
|
|
|
if execution_record:
|
|
|
|
|
execution_info = {
|
|
|
|
|
"recordId": execution_record.id,
|
|
|
|
|
"status": execution_record.status,
|
|
|
|
|
"startedOn": execution_record.started_on.isoformat() if execution_record.started_on else None,
|
|
|
|
|
"endedOn": execution_record.ended_on.isoformat() if execution_record.ended_on else None,
|
|
|
|
|
"endedReason": execution_record.ended_reason,
|
|
|
|
|
"inputParamsValue": json.loads(execution_record.block_input_params_value) if execution_record.block_input_params_value else None,
|
|
|
|
|
"outputParamsValue": json.loads(execution_record.block_out_params_value) if execution_record.block_out_params_value else None,
|
|
|
|
|
"remark": execution_record.remark
|
|
|
|
|
}
|
|
|
|
|
result["executionRecord"] = execution_info
|
|
|
|
|
result["executionStatus"] = execution_record.status
|
|
|
|
|
else:
|
|
|
|
|
# 如果没有执行记录,标记为未执行状态
|
|
|
|
|
from data.enum.task_block_record_enum import TaskBlockRecordStatus
|
|
|
|
|
result["executionStatus"] = TaskBlockRecordStatus.NOT_EXECUTED
|
|
|
|
|
result["executionRecord"] = {
|
|
|
|
|
"recordId": None,
|
|
|
|
|
"status": TaskBlockRecordStatus.NOT_EXECUTED,
|
|
|
|
|
"startedOn": None,
|
|
|
|
|
"endedOn": None,
|
|
|
|
|
"endedReason": "任务块尚未执行",
|
|
|
|
|
"inputParamsValue": None,
|
|
|
|
|
"outputParamsValue": None,
|
|
|
|
|
"remark": "任务块尚未执行"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 如果有日志记录,添加日志信息
|
|
|
|
|
if block_name in log_map:
|
|
|
|
|
logs = log_map[block_name]
|
|
|
|
|
for log in logs:
|
|
|
|
|
try:
|
|
|
|
|
log_messages = json.loads(log.message) if log.message else {}
|
|
|
|
|
log_info = {
|
|
|
|
|
"logId": log.id,
|
|
|
|
|
"level": log.level,
|
|
|
|
|
"createdAt": log.created_at.isoformat() if log.created_at else None,
|
|
|
|
|
"message": log_messages.get("message", ""),
|
|
|
|
|
"output": log_messages.get("output", "")
|
|
|
|
|
}
|
|
|
|
|
result["logs"].append(log_info)
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
|
# 如果日志不是JSON格式,直接使用原始消息
|
|
|
|
|
log_info = {
|
|
|
|
|
"logId": log.id,
|
|
|
|
|
"level": log.level,
|
|
|
|
|
"createdAt": log.created_at.isoformat() if log.created_at else None,
|
|
|
|
|
"message": log.message or "",
|
|
|
|
|
"output": ""
|
|
|
|
|
}
|
|
|
|
|
result["logs"].append(log_info)
|
|
|
|
|
|
|
|
|
|
# 递归处理子块
|
|
|
|
|
children_def = block_def.get('children', {})
|
|
|
|
|
if children_def:
|
|
|
|
|
for children_blocks in children_def.values():
|
|
|
|
|
if isinstance(children_blocks, list):
|
|
|
|
|
for child_block in children_blocks:
|
|
|
|
|
child_result = await TaskRecordService._build_nested_block_results(
|
|
|
|
|
child_block, block_map, log_map, block_name_mapping, level + 1
|
|
|
|
|
)
|
|
|
|
|
result["children"].append(child_result)
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def _get_block_results_fallback(
|
|
|
|
|
task_record_id: str,
|
|
|
|
|
blocks: List[VWEDBlockRecord],
|
|
|
|
|
logs: List[VWEDTaskLog],
|
|
|
|
|
block_name_mapping: Dict[str, str]
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
回退方案:当无法解析任务定义时,使用原始的平铺模式
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
task_record_id: 任务记录ID
|
|
|
|
|
blocks: 所有块执行记录
|
|
|
|
|
logs: 所有日志记录
|
|
|
|
|
block_name_mapping: 块类型名称映射
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict: 平铺格式的执行结果
|
|
|
|
|
"""
|
|
|
|
|
block_results = []
|
|
|
|
|
processed_block_names = set()
|
|
|
|
|
log_map = {}
|
|
|
|
|
|
|
|
|
|
# 构建日志映射
|
|
|
|
|
for log in logs:
|
|
|
|
|
if log.task_block_id not in log_map:
|
|
|
|
|
log_map[log.task_block_id] = []
|
|
|
|
|
log_map[log.task_block_id].append(log)
|
|
|
|
|
|
|
|
|
|
for block in sorted(blocks, key=lambda x: x.ended_on or x.created_at, reverse=True):
|
|
|
|
|
if block.block_name in processed_block_names:
|
|
|
|
|
continue
|
|
|
|
|
processed_block_names.add(block.block_name)
|
|
|
|
|
|
|
|
|
|
if block.status != TaskBlockRecordStatus.SUCCESS:
|
|
|
|
|
if block.block_name == "-1":
|
|
|
|
|
context = f"[{block.block_execute_name}@{block_name_mapping.get(block.block_execute_name, '其他')}] {block.task_record_id}"
|
|
|
|
|
else:
|
|
|
|
|
context = f"[{block.block_execute_name}@{block_name_mapping.get(block.block_execute_name, '其他')}] {block.ended_reason}"
|
|
|
|
|
block_results.append({
|
|
|
|
|
"created_at": block.created_at,
|
|
|
|
|
"context": context,
|
|
|
|
|
"status": block.status,
|
|
|
|
|
"blockName": block.block_name,
|
|
|
|
|
"blockType": block.block_execute_name
|
|
|
|
|
})
|
|
|
|
|
else:
|
|
|
|
|
# 处理成功的块
|
|
|
|
|
if block.block_name == "-1":
|
|
|
|
|
context = f"[{block.block_execute_name}@{block_name_mapping.get(block.block_execute_name, '其他')}] {block.task_record_id}"
|
|
|
|
|
block_results.append({
|
|
|
|
|
"created_at": block.created_at,
|
|
|
|
|
"context": context,
|
|
|
|
|
"status": block.status,
|
|
|
|
|
"blockName": block.block_name,
|
|
|
|
|
"blockType": block.block_execute_name
|
|
|
|
|
})
|
|
|
|
|
else:
|
|
|
|
|
# 查找对应的日志
|
|
|
|
|
block_logs = log_map.get(block.block_name, [])
|
|
|
|
|
if block_logs:
|
|
|
|
|
for task_log in block_logs:
|
|
|
|
|
try:
|
|
|
|
|
messages = json.loads(task_log.message) if task_log.message else {}
|
|
|
|
|
message = messages.get("message", "")
|
|
|
|
|
output = messages.get("output", "")
|
|
|
|
|
|
|
|
|
|
context_parts = [f"[{block.block_execute_name}@{block_name_mapping.get(block.block_execute_name, '其他')}]"]
|
|
|
|
|
if message:
|
|
|
|
|
context_parts.append(message)
|
|
|
|
|
if output:
|
|
|
|
|
if isinstance(output, dict) and output.get("message"):
|
|
|
|
|
context_parts.append(f"@{output.get('message')}")
|
|
|
|
|
else:
|
|
|
|
|
context_parts.append(f"@{str(output)}")
|
|
|
|
|
|
|
|
|
|
block_results.append({
|
|
|
|
|
"created_at": task_log.created_at,
|
|
|
|
|
"context": " ".join(context_parts),
|
|
|
|
|
"status": block.status,
|
|
|
|
|
"blockName": block.block_name,
|
|
|
|
|
"blockType": block.block_execute_name
|
|
|
|
|
})
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
|
context = f"[{block.block_execute_name}@{block_name_mapping.get(block.block_execute_name, '其他')}] {task_log.message}"
|
|
|
|
|
block_results.append({
|
|
|
|
|
"created_at": task_log.created_at,
|
|
|
|
|
"context": context,
|
|
|
|
|
"status": block.status,
|
|
|
|
|
"blockName": block.block_name,
|
|
|
|
|
"blockType": block.block_execute_name
|
|
|
|
|
})
|
|
|
|
|
else:
|
|
|
|
|
# 没有日志时使用块的结束原因
|
|
|
|
|
context = f"[{block.block_execute_name}@{block_name_mapping.get(block.block_execute_name, '其他')}] {block.ended_reason or '执行完成'}"
|
|
|
|
|
block_results.append({
|
|
|
|
|
"created_at": block.created_at,
|
|
|
|
|
"context": context,
|
|
|
|
|
"status": block.status,
|
|
|
|
|
"blockName": block.block_name,
|
|
|
|
|
"blockType": block.block_execute_name
|
|
|
|
|
})
|
|
|
|
|
logger.warning(f"任务记录 {task_record_id} 的块 {block.block_name} 没有日志")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "成功获取任务记录执行结果(平铺模式)",
|
|
|
|
|
"data": sorted(block_results, key=lambda x: x["created_at"], reverse=True)
|
|
|
|
|
}
|
2025-05-12 15:43:21 +08:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def get_task_record_detail(task_record_id: str) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
获取指定任务记录的详细信息
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
task_record_id: 任务记录ID
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict: 包含任务记录详细信息的字典
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 构建查询语句
|
|
|
|
|
query = select(VWEDTaskRecord).where(
|
|
|
|
|
VWEDTaskRecord.id == task_record_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 执行查询
|
|
|
|
|
result = await session.execute(query)
|
|
|
|
|
task_record = result.scalars().first()
|
|
|
|
|
|
|
|
|
|
if not task_record:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"未找到ID为 {task_record_id} 的任务记录",
|
|
|
|
|
"data": None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 计算执行时长(如果任务已结束)
|
|
|
|
|
execution_time = None
|
|
|
|
|
if task_record.ended_on and task_record.first_executor_time:
|
|
|
|
|
time_diff = task_record.ended_on - task_record.first_executor_time
|
|
|
|
|
execution_time = int(time_diff.total_seconds() * 1000) # 转换为毫秒
|
|
|
|
|
elif task_record.executor_time:
|
|
|
|
|
execution_time = task_record.executor_time
|
|
|
|
|
|
|
|
|
|
# 转换为字典
|
|
|
|
|
task_dict = {
|
|
|
|
|
"id": task_record.id,
|
|
|
|
|
"task_id": task_record.def_id,
|
|
|
|
|
"task_name": task_record.def_label,
|
|
|
|
|
"task_version": task_record.def_version,
|
|
|
|
|
"status": task_record.status,
|
|
|
|
|
"input_params": json.loads(task_record.input_params) if task_record.input_params else None,
|
|
|
|
|
"started_on": task_record.first_executor_time.isoformat() if task_record.first_executor_time else None,
|
|
|
|
|
"ended_on": task_record.ended_on.isoformat() if task_record.ended_on else None,
|
|
|
|
|
"ended_reason": task_record.ended_reason,
|
|
|
|
|
"execution_time": execution_time,
|
|
|
|
|
"created_at": task_record.created_at.isoformat() if task_record.created_at else None,
|
|
|
|
|
"updated_at": task_record.updated_at.isoformat() if task_record.updated_at else None,
|
|
|
|
|
"agv_id": task_record.agv_id,
|
|
|
|
|
"parent_task_record_id": task_record.parent_task_record_id,
|
|
|
|
|
"root_task_record_id": task_record.root_task_record_id,
|
|
|
|
|
"state_description": task_record.state_description,
|
|
|
|
|
"if_have_child_task": bool(task_record.if_have_child_task) if task_record.if_have_child_task is not None else None,
|
|
|
|
|
"periodic_task": task_record.periodic_task,
|
|
|
|
|
"priority": task_record.priority,
|
|
|
|
|
"work_stations": task_record.work_stations,
|
|
|
|
|
"work_types": task_record.work_types,
|
|
|
|
|
"variables": json.loads(task_record.variables) if task_record.variables else None,
|
|
|
|
|
"source_type": task_record.source_type,
|
|
|
|
|
"source_system": task_record.source_system,
|
|
|
|
|
"source_user": task_record.source_user,
|
|
|
|
|
"source_device": task_record.source_device,
|
|
|
|
|
"source_ip": task_record.source_ip,
|
|
|
|
|
"source_time": task_record.source_time.isoformat() if task_record.source_time else None,
|
|
|
|
|
"source_client_info": task_record.source_client_info,
|
|
|
|
|
"source_remarks": task_record.source_remarks
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "成功获取任务记录详情",
|
|
|
|
|
"data": task_dict
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"获取任务记录详情失败: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"获取任务记录详情失败: {str(e)}",
|
|
|
|
|
"data": None
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-20 16:50:45 +08:00
|
|
|
|
@staticmethod
|
|
|
|
|
async def retry_failed_task(task_record_id: str, client_ip: str = None, client_info: str = None, tf_api_token: str = None) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
重新执行失败的任务
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
task_record_id: 失败的任务记录ID
|
|
|
|
|
client_ip: 客户端IP地址
|
|
|
|
|
client_info: 客户端信息
|
|
|
|
|
tf_api_token: 系统任务令牌
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict: 包含重发结果的响应
|
|
|
|
|
"""
|
|
|
|
|
from data.enum.task_record_enum import TaskStatus
|
|
|
|
|
try:
|
|
|
|
|
async with get_async_session() as session:
|
|
|
|
|
# 查找失败的任务记录
|
|
|
|
|
task_query = await session.execute(
|
|
|
|
|
select(VWEDTaskRecord)
|
|
|
|
|
.where(VWEDTaskRecord.id == task_record_id)
|
|
|
|
|
)
|
|
|
|
|
failed_task = task_query.scalars().first()
|
|
|
|
|
|
|
|
|
|
if not failed_task:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"未找到任务记录 {task_record_id}",
|
|
|
|
|
"code": 404
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查任务状态是否为失败状态
|
|
|
|
|
if failed_task.status not in [TaskStatus.FAILED, TaskStatus.CANCELED]:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"任务状态为 {failed_task.status},只能重发失败/终止的任务",
|
|
|
|
|
"code": 400
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查是否有任务定义
|
|
|
|
|
if not failed_task.def_id:
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": "任务记录缺少任务定义ID,无法重发",
|
|
|
|
|
"code": 400
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 构建重发请求参数
|
2025-09-30 13:52:36 +08:00
|
|
|
|
from routes.model.task_edit_model import TaskEditRunRequest
|
2025-09-20 16:50:45 +08:00
|
|
|
|
|
|
|
|
|
# 解析原任务的输入参数
|
2025-09-25 10:52:52 +08:00
|
|
|
|
if failed_task.input_params:
|
|
|
|
|
input_params = json.loads(failed_task.input_params)
|
|
|
|
|
else:
|
|
|
|
|
input_params = None
|
2025-09-20 16:50:45 +08:00
|
|
|
|
# 构建运行请求
|
|
|
|
|
run_request = TaskEditRunRequest(
|
|
|
|
|
taskId=failed_task.def_id,
|
2025-09-25 10:52:52 +08:00
|
|
|
|
params=input_params if input_params else [],
|
2025-09-20 16:50:45 +08:00
|
|
|
|
source_type=failed_task.source_type,
|
|
|
|
|
source_system=failed_task.source_system or "VWED-RETRY",
|
|
|
|
|
source_device=failed_task.source_device or "retry-system",
|
|
|
|
|
source_user=failed_task.source_user,
|
|
|
|
|
source_remarks=f"重发失败任务: {task_record_id}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 调用任务编辑服务的运行方法
|
|
|
|
|
from services.task_edit_service import TaskEditService
|
|
|
|
|
|
|
|
|
|
result = await TaskEditService.run_task(
|
|
|
|
|
run_request,
|
|
|
|
|
client_ip=client_ip,
|
|
|
|
|
client_info=client_info,
|
|
|
|
|
tf_api_token=tf_api_token
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not result or not result.get("success", False):
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"重发任务失败: {result.get('message', '未知错误') if result else '任务启动失败'}",
|
|
|
|
|
"code": 500
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 记录重发关系
|
|
|
|
|
logger.info(f"任务 {task_record_id} 重发成功,新任务记录ID: {result.get('data', {}).get('taskRecordId')}")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "任务重发成功",
|
|
|
|
|
"data": {
|
|
|
|
|
"original_task_record_id": task_record_id,
|
|
|
|
|
"new_task_record_id": result.get("data", {}).get("taskRecordId"),
|
|
|
|
|
"new_task_id": result.get("data", {}).get("taskId"),
|
|
|
|
|
"retry_time": datetime.datetime.now().isoformat(),
|
|
|
|
|
"original_task_name": failed_task.def_label,
|
|
|
|
|
"original_input_params": json.loads(failed_task.input_params) if failed_task.input_params else {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"重发失败任务异常: {str(e)}")
|
|
|
|
|
return {
|
|
|
|
|
"success": False,
|
|
|
|
|
"message": f"重发任务失败: {str(e)}",
|
|
|
|
|
"code": 500
|
|
|
|
|
}
|
|
|
|
|
|