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:
+2
-2
@@ -1,7 +1,7 @@
|
||||
"""core 包:基础设施层。
|
||||
|
||||
config — 全局配置常量(STF、adb 路径、养号参数)
|
||||
config — 全局配置常量(adb 路径、设备发现等)
|
||||
adb_helper — adb 命令封装(并发安全)
|
||||
device_worker — 设备生命周期 + 养号 worker + 全局状态注册表
|
||||
device_worker — 设备生命周期 + 任务 worker 基类 + 全局状态注册表
|
||||
task_manager — 任务管理框架(分组/计划/调度/重试/持久化)
|
||||
"""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""全局操作框架:所有 app 任务共用的 Action 基类与注册机制。
|
||||
|
||||
为什么放这里:
|
||||
抖音的点赞/评论 xpath 只适用抖音,留在 tasks/douyin/actions/。
|
||||
某个 app 专属的 xpath 只适用那个 app,留在 tasks/<app>/actions/。
|
||||
但"操作"的抽象(BaseAction 接口、概率触发、注册机制)是通用的,
|
||||
放这里让所有 app 任务共享同一套操作开发范式。
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
设计要点:
|
||||
1. BaseAction 定义统一接口:execute(d, params, worker) -> bool
|
||||
2. 每个 app 任务有自己的注册表(create_action_registry),
|
||||
互不污染——抖音的 like 和快手的 like 各注册各的。
|
||||
互不污染——A 应用的 like 和 B 应用的 like 各注册各的。
|
||||
3. 概率触发、状态上报等通用逻辑放这里,子类不重复写。
|
||||
4. ActionContext 封装执行上下文,方便扩展(传 d/worker/进度/计数器)。
|
||||
|
||||
新增 app 专属操作步骤(照着 tasks/douyin/actions/like.py 抄):
|
||||
新增 app 专属操作步骤(在 tasks/<app>/actions/ 下新建,参考该包已有实现):
|
||||
|
||||
1. 在 tasks/<app>/actions/ 下新建 my_action.py
|
||||
2. 写一个 BaseAction 子类,用 @register_action 装饰,实现 execute:
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ def get_logger(name="core"):
|
||||
"""获取指定模块的 logger。
|
||||
|
||||
name: 模块名(core/task/web/action),决定写哪个文件。
|
||||
也可传子模块名如 "task.douyin",会归到 task.log。
|
||||
也可传子模块名如 "task.generic",会归到 task.log。
|
||||
返回配置好的 logging.Logger。
|
||||
"""
|
||||
if name in _LOGGERS:
|
||||
|
||||
+2
-2
@@ -128,7 +128,7 @@ class TaskJob(db.Model):
|
||||
"""
|
||||
id = db.Column(db.String(32), primary_key=True) # uuid 前 8 位
|
||||
name = db.Column(db.String(120), nullable=False)
|
||||
task_type = db.Column(db.String(60), default="douyin_nurture")
|
||||
task_type = db.Column(db.String(60), default="generic_steps")
|
||||
target = db.Column(db.Text, default='{"mode":"all"}') # JSON
|
||||
params = db.Column(db.Text, default="{}") # JSON
|
||||
schedule = db.Column(db.Text, default='{"mode":"once"}') # JSON
|
||||
@@ -384,7 +384,7 @@ def _migrate_old_json():
|
||||
del actions["comment"]
|
||||
_log.info(f"迁移任务 {j.get('name')}: 已剔除废弃的 comment 配置")
|
||||
row = TaskJob(id=j["id"], name=j["name"],
|
||||
task_type=j.get("task_type", "douyin_nurture"),
|
||||
task_type=j.get("task_type", "generic_steps"),
|
||||
enabled=j.get("enabled", True))
|
||||
row.set_target(j.get("target", {"mode": "all"}))
|
||||
row.set_params(params)
|
||||
|
||||
+18
-15
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user