# 任务日志层级关系优化方案 ## 改造背景 ### 原有问题 1. **日志表缺少层级关系字段**: `vwed_tasklog` 表只有基本字段,没有记录父子关系、迭代索引等信息 2. **层级重构困难**: `get_block_results` 接口需要解析日志中的 JSON `output.iterationResults` 来重构层级,不稳定 3. **循环日志分散**: 循环块的日志("第1次循环")和子块执行日志是分开的,前端难以组织嵌套显示 ### 期望效果 对于循环任务,期望返回的数据结构为: ```json { "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` 执行迁移: ```bash cd /mnt/d/jsw_code/project/VWED_server alembic upgrade head ``` ### 2. 日志记录逻辑改造 #### TaskContext 扩展 在 `services/execution/task_context.py` 中添加: ```python 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` 方法: ```python 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` 的迭代处理逻辑中: ```python 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. **按迭代组织**: ```python 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` 中: ```python @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解析) ``` ## 数据流示例 ### 任务定义 ```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` | ### 查询结果组织 ```json { "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. 添加日志清理策略(历史数据归档)