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:
2026-09-10 21:50:18 +08:00
parent 98cdc39224
commit f4b5316436
4 changed files with 24 additions and 17 deletions
+9 -1
View File
@@ -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
View File
@@ -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` 字段格式:
| 字段 | 说明 | | 字段 | 说明 |
|------|------| |------|------|
+4
View File
@@ -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
View File
@@ -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="操作",