284 lines
7.6 KiB
Markdown
284 lines
7.6 KiB
Markdown
# 任务日志层级关系优化方案
|
|
|
|
## 改造背景
|
|
|
|
### 原有问题
|
|
|
|
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. 添加日志清理策略(历史数据归档)
|