VWED_server/docs/task_log_hierarchy_upgrade.md

7.6 KiB

任务日志层级关系优化方案

改造背景

原有问题

  1. 日志表缺少层级关系字段: vwed_tasklog 表只有基本字段,没有记录父子关系、迭代索引等信息
  2. 层级重构困难: get_block_results 接口需要解析日志中的 JSON output.iterationResults 来重构层级,不稳定
  3. 循环日志分散: 循环块的日志("第1次循环")和子块执行日志是分开的,前端难以组织嵌套显示

期望效果

对于循环任务,期望返回的数据结构为:

{
  "children": [
    {
      "iterationIndex": 0,
      "success": true,
      "iterationLogs": [
        {"message": "第1次循环, item=1"}
      ],
      "children": [
        {
          "blockName": "b4",
          "blockType": "PrintBp",
          "logs": [
            {"message": "打印: 0"}
          ]
        }
      ]
    }
  ]
}

改造方案

1. 数据库表结构扩展

新增字段

vwed_tasklog 表添加以下字段:

字段名 类型 说明
parent_log_id VARCHAR(255) 父日志ID,用于建立层级关系
iteration_index INT 迭代索引,记录是第几次循环(从0开始)
block_record_id VARCHAR(255) 关联的块执行记录ID
log_type VARCHAR(50) 日志类型: iteration_start, iteration_end, block_execution, branch_execution

迁移脚本

位置: migrations/versions/004_add_tasklog_hierarchy_fields.py

执行迁移:

cd /mnt/d/jsw_code/project/VWED_server
alembic upgrade head

2. 日志记录逻辑改造

TaskContext 扩展

services/execution/task_context.py 中添加:

self.parent_log_id = None  # 当前父日志ID
self.current_iteration_index = None  # 当前迭代索引

def set_iteration_context(self, parent_log_id: str = None, iteration_index: int = None):
    """设置当前迭代上下文"""
    self.parent_log_id = parent_log_id
    self.current_iteration_index = iteration_index

BlockHandler 日志记录方法

修改 services/execution/handlers/base.py_record_task_log 方法:

async def _record_task_log(
    self,
    block: Dict[str, Any],
    result: Dict[str, Any],
    context: Any,
    log_type: str = "block_execution",
    parent_log_id: str = None,
    iteration_index: int = None
) -> str:
    """记录任务日志,返回日志ID"""
    # ... 记录时包含 parent_log_id, iteration_index, log_type 等字段

IterateListBlockHandler 改造

services/execution/handlers/progress.py 的迭代处理逻辑中:

for index, item in enumerate(array_data):
    # 1. 记录迭代开始日志,作为该次迭代的父日志
    iteration_start_log_id = await self._record_task_log(
        block,
        {"success": True, "message": f"第{index+1}次循环, item={item}"},
        context,
        log_type="iteration_start",
        iteration_index=index
    )

    # 2. 设置迭代上下文,后续子块日志都会关联到这个父日志
    context.set_iteration_context(parent_log_id=iteration_start_log_id, iteration_index=index)

    # 3. 执行循环体(子块日志会自动记录parent_log_id和iteration_index)
    loop_result = await executor.execute_children(block, "default")

    # 4. 清除迭代上下文
    context.clear_iteration_context()

3. 查询接口优化

新版 get_block_results_v2

创建新文件 services/task_record_service_new.py,实现基于层级关系字段的查询逻辑:

核心思路:

  1. 构建映射表:

    • log_map_by_block: 按块名称分组日志
    • log_map_by_parent: 按父日志ID分组子日志
  2. 识别迭代块:

    • 检查 block_type 是否为 ITERATE_LIST, WHILE, REPEAT_NUM
    • 找出所有 log_type="iteration_start" 的日志
  3. 按迭代组织:

    for iter_log in iteration_start_logs:
        iteration_item = {
            "iterationIndex": iter_log.iteration_index,
            "iterationLogs": [iter_log],  # 迭代开始日志
            "children": []  # 该次迭代的子块
        }
    
        # 查找 parent_log_id = iter_log.id 的所有子日志
        child_logs = log_map_by_parent.get(iter_log.id, [])
        # ... 递归构建子块
    
  4. 递归处理嵌套:

    • 如果子块也是迭代块,继续应用相同逻辑
    • 普通块直接递归处理其子块

兼容旧版本

services/task_record_service.py 中:

@staticmethod
async def get_block_results(task_record_id: str, use_new_version: bool = True):
    if use_new_version:
        from services.task_record_service_new import get_block_results_v2
        return await get_block_results_v2(task_record_id)
    # ... 旧版本逻辑(基于JSON解析)

数据流示例

任务定义

{
  "rootBlock": {
    "id": -1,
    "name": "-1",
    "blockType": "RootBp",
    "children": {
      "default": [
        {
          "id": 5,
          "name": "b3",
          "blockType": "IterateListBp",
          "inputParams": {
            "list": {"value": "[1, 2, 3]"}
          },
          "children": {
            "default": [
              {
                "id": 6,
                "name": "b4",
                "blockType": "PrintBp",
                "inputParams": {
                  "message": {"value": "blocks.b3.index"}
                }
              }
            ]
          }
        }
      ]
    }
  }
}

日志记录流程

时间 操作 日志记录
T1 开始第1次循环 log_id=L1, task_block_id=b3, log_type=iteration_start, iteration_index=0, parent_log_id=null
T2 执行打印块 log_id=L2, task_block_id=b4, log_type=block_execution, iteration_index=0, parent_log_id=L1
T3 开始第2次循环 log_id=L3, task_block_id=b3, log_type=iteration_start, iteration_index=1, parent_log_id=null
T4 执行打印块 log_id=L4, task_block_id=b4, log_type=block_execution, iteration_index=1, parent_log_id=L3

查询结果组织

{
  "blockName": "b3",
  "blockType": "IterateListBp",
  "children": [
    {
      "iterationIndex": 0,
      "iterationLogs": [
        {"logId": "L1", "message": "第1次循环, item=1"}
      ],
      "children": [
        {
          "blockName": "b4",
          "logs": [
            {"logId": "L2", "message": "打印: 0", "parent_log_id": "L1"}
          ]
        }
      ]
    },
    {
      "iterationIndex": 1,
      "iterationLogs": [
        {"logId": "L3", "message": "第2次循环, item=2"}
      ],
      "children": [
        {
          "blockName": "b4",
          "logs": [
            {"logId": "L4", "message": "打印: 1", "parent_log_id": "L3"}
          ]
        }
      ]
    }
  ]
}

优势

  1. 数据完整性: 日志记录时就建立层级关系,不依赖运行时的内存状态
  2. 查询性能: 通过索引直接查询层级关系,无需解析JSON
  3. 前端友好: 返回的嵌套结构清晰,循环日志和子块日志自然组织在一起
  4. 可扩展性: log_type 字段便于未来扩展其他类型的日志
  5. 向后兼容: 保留旧版本逻辑,可通过参数切换

测试建议

  1. 功能测试:

    • 执行简单循环任务(1层循环)
    • 执行嵌套循环任务(2-3层循环)
    • 验证返回的层级结构正确
  2. 性能测试:

    • 对比新旧版本的查询性能
    • 测试大量循环(>100次)的场景
  3. 边界测试:

    • 空循环(数组为空)
    • 循环中断(break)
    • 循环失败(子块报错)

后续优化

  1. 为其他循环块(WhileBlockHandler, RepeatNumBlockHandler)应用相同逻辑
  2. 考虑为条件分支(IfBlockHandler)也记录分支信息
  3. 添加日志清理策略(历史数据归档)