chore: 去掉 generic_steps 的默认步骤 + 重试失败带上真实原因
- tasks/generic/task.py:DEFAULT_PARAMS 只留 max_duration,**不再内置示例步骤**
(步骤只能由前端编辑器产出;此前默认的"打开抖音→循环看视频"只有后端在用,
编辑器新建任务本来就是空的,等于埋了个和界面不一致的隐性默认值)
- 空步骤不再静默空跑:worker 立即抛错「通用步骤任务没有可执行步骤…」,
设备表「最近错误」能看到(此前会走完 0 步当成功)
- core/task_manager.py:重试耗尽的 last_error 现在带上最后一次的真实失败原因,
不再只有"重试N次失败"(用户看不到为什么失败);max_attempts=1 显示"执行失败"
而非费解的"重试1次失败";长度截断 200 字符
- 文档:TASK_DEV §2.10 说明无默认步骤 + 空步骤会报错;API.md 的 task_types 示例
改 default_params={"max_duration":0},POST /api/jobs 补「steps 要一起传」提示
自测(全通过):default_params 无 steps;无步骤任务真跑一次 → 设备错误信息完整含原因;
有步骤任务(wait 1s)真跑一次 → status=done 无错误;临时任务验完已删,任务集合复原
This commit is contained in:
@@ -757,7 +757,15 @@ class TaskManager:
|
|||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
||||||
_log.error(f"{serial} 任务 {job.name} 重试耗尽,放弃")
|
_log.error(f"{serial} 任务 {job.name} 重试耗尽,放弃")
|
||||||
_update_status(serial, status="failed", last_error=f"{job.name} 重试{max_attempts}次失败")
|
# 带上最后一次的真实失败原因——只写"重试N次失败"会让用户看不到为什么失败
|
||||||
|
# (worker 出错时已把原因写进 _WORKERS[serial]["last_error"])
|
||||||
|
with _WORKERS_LOCK:
|
||||||
|
cause = (_WORKERS.get(serial, {}).get("last_error") or "").strip()
|
||||||
|
msg = (f"{job.name} 重试{max_attempts}次仍失败" if max_attempts > 1
|
||||||
|
else f"{job.name} 执行失败")
|
||||||
|
if cause and cause != msg:
|
||||||
|
msg += f":{cause}"
|
||||||
|
_update_status(serial, status="failed", last_error=msg[:200])
|
||||||
finally:
|
finally:
|
||||||
# 清除停止标志:整个重试循环结束(成功/失败/停止)后允许下次任务
|
# 清除停止标志:整个重试循环结束(成功/失败/停止)后允许下次任务
|
||||||
with self._lock:
|
with self._lock:
|
||||||
|
|||||||
+5
-1
@@ -222,7 +222,7 @@
|
|||||||
"task_type": "generic_steps",
|
"task_type": "generic_steps",
|
||||||
"name": "通用步骤",
|
"name": "通用步骤",
|
||||||
"description": "通过步骤编辑器编排...",
|
"description": "通过步骤编辑器编排...",
|
||||||
"default_params": {"steps": [...]}
|
"default_params": {"max_duration": 0}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -305,6 +305,10 @@
|
|||||||
{"ok": true, "msg": "任务已创建", "job": {"id": "abc123", "...": "..."}}
|
{"ok": true, "msg": "任务已创建", "job": {"id": "abc123", "...": "..."}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **`params.steps` 要一起传**:`generic_steps` 的执行内容全在 `params.steps`,后端**不提供默认步骤**、
|
||||||
|
> 也不做参数校验(编辑器是参数正确性的唯一关卡)。不传 steps 的任务能建成功,但执行时会立即报错
|
||||||
|
> 「通用步骤任务没有可执行步骤」,请在「任务」页用步骤编辑器补步骤。
|
||||||
|
|
||||||
`schedule` 字段格式:
|
`schedule` 字段格式:
|
||||||
| 字段 | 说明 |
|
| 字段 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
|
|||||||
@@ -280,6 +280,10 @@ while not self.stopped():
|
|||||||
|
|
||||||
- `max_duration` — 最大运行时长(秒),0=不限时
|
- `max_duration` — 最大运行时长(秒),0=不限时
|
||||||
- `steps` — 步骤数组。每步 `{id, type, label, params}`;`id` 前端生成保证唯一
|
- `steps` — 步骤数组。每步 `{id, type, label, params}`;`id` 前端生成保证唯一
|
||||||
|
|
||||||
|
> **没有默认步骤**:`DEFAULT_PARAMS` 只含 `max_duration`,步骤只能由步骤编辑器产出
|
||||||
|
> (新建任务时编辑器从零开始拖)。若任务的 `params.steps` 为空,worker 会**立即报错**
|
||||||
|
> (设备「最近错误」显示"通用步骤任务没有可执行步骤…"),不会静默空跑。
|
||||||
- **进度上报**:`done` = 累计已执行的**非容器**步骤数(loop/group/if_el 不计入,避免监控噪音)、`total=0`、`unit="操作"`(前端显示"已执行 N 次操作")
|
- **进度上报**:`done` = 累计已执行的**非容器**步骤数(loop/group/if_el 不计入,避免监控噪音)、`total=0`、`unit="操作"`(前端显示"已执行 N 次操作")
|
||||||
- **公共参数**:每步都可有 `probability`(0-100,默认 100,<100 时按百分比概率决定本次是否执行该步)
|
- **公共参数**:每步都可有 `probability`(0-100,默认 100,<100 时按百分比概率决定本次是否执行该步)
|
||||||
- **嵌套深度上限 5**:loop/group 的 `children`、if_el 的 `then/else` 递归嵌套超过 5 层会被跳过并告警
|
- **嵌套深度上限 5**:loop/group 的 `children`、if_el 的 `then/else` 递归嵌套超过 5 层会被跳过并告警
|
||||||
|
|||||||
+6
-15
@@ -104,21 +104,8 @@ STEP_TYPES = [
|
|||||||
|
|
||||||
DEFAULT_PARAMS = {
|
DEFAULT_PARAMS = {
|
||||||
"max_duration": 0, # 最大运行时长(秒),0=不限时
|
"max_duration": 0, # 最大运行时长(秒),0=不限时
|
||||||
"steps": [ # 默认示例:打开抖音→循环(看视频→滑动)
|
# 注意:**没有默认 steps**。步骤由前端步骤编辑器编排产出,新建任务时从零开始拖;
|
||||||
{
|
# 缺 steps 的任务执行时会明确报错(见 run_task),不会静默空跑。
|
||||||
"id": "step_1", "type": "open_app", "label": "打开抖音",
|
|
||||||
"params": {"package": "com.ss.android.ugc.aweme", "wait_home": True, "home_feature": "拍摄"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "step_2", "type": "loop", "label": "循环看视频",
|
|
||||||
"params": {"max_iterations": 20, "children": [
|
|
||||||
{"id": "step_2_1", "type": "wait", "label": "观看视频",
|
|
||||||
"params": {"min": 5.0, "max": 35.0}},
|
|
||||||
{"id": "step_2_2", "type": "swipe", "label": "上滑下一个",
|
|
||||||
"params": {"direction": "up", "duration_min": 0.25, "duration_max": 0.50}},
|
|
||||||
]},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -157,6 +144,10 @@ class GenericStepsWorker(BaseWorker):
|
|||||||
|
|
||||||
def run_task(self, d):
|
def run_task(self, d):
|
||||||
"""按 steps 顺序执行。loop 步骤递归执行其 children。"""
|
"""按 steps 顺序执行。loop 步骤递归执行其 children。"""
|
||||||
|
if not self.steps:
|
||||||
|
# 任务没配步骤(DEFAULT_PARAMS 不含默认步骤,步骤只能由编辑器产出):
|
||||||
|
# 明确报错而不是"跑完 0 步"当成功——设备表/任务页会显示该错误
|
||||||
|
raise ValueError("通用步骤任务没有可执行步骤:请在「任务」页编辑该任务并添加步骤")
|
||||||
self._start_timer()
|
self._start_timer()
|
||||||
self.set_action("开始执行步骤链")
|
self.set_action("开始执行步骤链")
|
||||||
self.set_progress(done=0, total=0, unit="操作",
|
self.set_progress(done=0, total=0, unit="操作",
|
||||||
|
|||||||
Reference in New Issue
Block a user