chore: 删除抖音养号任务类型,平台只保留 generic_steps

tasks/douyin/(task_type=douyin_nurture)整体删除,唯一任务类型是 generic_steps。
所有默认值/文档/接口示例同步改成 generic_steps:

- tasks/:删 douyin 包;__init__ 只注册 generic;base.py/generic 注释改为照 generic 抄
- 默认值:core/models.py(列默认 + 旧 JSON 迁移默认)、core/task_manager.py TaskJob、
  web/tasks_api.py 建任务默认、static/admin/tasks.js 新建任务默认
- core/task_manager.py:去掉 douyin 专属的"清理废弃 comment 参数"迁移块,改为**启动时告警**
  仍残留已删类型的任务(只告警不改数据);run_job_now 对已删类型直接返回明确错误,
  不再"报已触发、线程里静默失败"
- 清理残留:douyin_running 状态位(无任何读取方)、core/__init__、core/actions/*、
  core/logger.py 注释里的抖音示例
- 文档:README(特性/目录树/类型表/参数表/示例)、TASK_DEV(目录树/注册说明/模板引用)、
  ARCHITECTURE(注册示例/action 注册表示例)、API.md(task_types 与任务 JSON 示例)、
  AI_TASK_GEN(P1 去掉 douyin 预设)、DEVELOPMENT
- 注:示例里"抖音"作为**App 名**(MCP 列应用、AI 建任务的需求举例)保留,与任务类型无关

自测(全部通过):类型列表只剩 generic_steps;建任务不传类型默认 generic_steps;传
douyin_nurture 被 400 拒;库里塞残留旧类型任务 → 启动日志告警 + 执行返回明确错误 +
不自动删用户数据;前端新建任务下拉 1 项且默认选中、界面建任务成功;监控页卡片两个按钮 +
覆盖设备正常。临时任务/数据验完已清理,任务集合复原。
This commit is contained in:
2026-09-10 21:43:08 +08:00
parent 86c65236a2
commit 98cdc39224
23 changed files with 75 additions and 555 deletions
+18 -15
View File
@@ -1,9 +1,9 @@
"""通用任务管理框架:任务类型注册 + 设备分组 + 任务计划 + 定时调度 + 重试 + 持久化。
设计目标:可扩展,未来加非抖音任务只需注册新的 Task 类。
设计目标:可扩展——新增任务类型只需在 tasks/ 下注册新的 Task 类(见 doc/TASK_DEV.md)。
核心概念:
TaskType — 任务类型(如"抖音养号"),可注册,含默认参数和 worker 工厂
TaskType — 任务类型(如"通用步骤"),可注册,含默认参数和 worker 工厂
DeviceGroup — 设备分组,持久化到 SQLite(core.models.DeviceGroup)
TaskJob — 任务计划(类型+目标+参数+调度+重试),持久化到 SQLite(core.models.TaskJob)
TaskManager — 统管调度器、分组、任务、运行实例、状态
@@ -121,7 +121,7 @@ class DeviceGroup:
class TaskJob:
"""一个任务计划:什么任务、跑哪些设备、什么参数、何时跑、失败重试策略。"""
def __init__(self, job_id=None, name="", task_type="douyin_nurture",
def __init__(self, job_id=None, name="", task_type="generic_steps",
target=None, params=None, schedule=None, retry=None, enabled=True):
self.id = job_id or uuid.uuid4().hex[:8]
self.name = name
@@ -378,24 +378,22 @@ class TaskManager:
for row in GroupRow.query.all():
g = DeviceGroup(row.name, row.get_serials(), row.description or "")
self.groups[g.name] = g
dirty = False # 标记是否有任务参数需要写回
stale = [] # 任务类型已不存在(历史任务类型被删除)的任务
for row in JobRow.query.all():
j = TaskJob(job_id=row.id, name=row.name, task_type=row.task_type,
target=row.get_target(), params=row.get_params(),
schedule=row.get_schedule(), retry=row.get_retry(),
enabled=row.enabled)
# 数据规整:移除已废弃的 action 配置(如抖音的 comment 已删除)
if j.task_type == "douyin_nurture":
actions = j.params.get("actions", {})
if "comment" in actions:
del actions["comment"]
row.set_params(j.params)
dirty = True
_log.info(f"任务 {j.name}({j.id}): 已移除废弃的 comment 配置")
# 启动时告警:库里留着已删除的任务类型(如 douyin_nurture),
# 这类任务跑不起来也不该静默——提示人工删除或改用现有类型。
# 只告警不改数据(不自动删用户的任务)。
if not get_task_class(j.task_type):
stale.append(j)
self.jobs[j.id] = j
if dirty:
db.session.commit()
_log.info("已将规整后的任务参数写回数据库")
if stale:
_log.warning(
"以下任务的任务类型已不存在,无法执行,请在「任务」页删除或改用现有类型:"
+ ";".join(f"{j.name}({j.id}, type={j.task_type})" for j in stale))
_log.info(f"从数据库加载 {len(self.groups)} 个分组, {len(self.jobs)} 个任务")
except Exception as e:
_log.error(f"从数据库加载失败: {e}")
@@ -608,6 +606,11 @@ class TaskManager:
job = self.jobs.get(job_id)
if not job:
return {"ok": False, "error": "任务不存在"}
# 任务类型已不存在(历史类型被删除)→ 明确报错,别只返回"已触发"然后线程里静默失败
if not get_task_class(job.task_type):
return {"ok": False, "error":
f"任务类型 {job.task_type} 已不存在(该类型已被删除),"
f"请删除此任务或改用现有类型"}
# 在独立线程跑,不阻塞调用方
t = threading.Thread(target=self._run_job, args=(job,), daemon=True)
t.start()