Files
zn-ai/docs/TaskList-Implementation-Plan.md
DEV_DSW 210e8eb363 feat: add task management and progress reporting
- Implemented task and subtask structures with progress tracking.
- Added reporting functionality to log progress at various stages in hotel room status management scripts.
- Created a task store to manage tasks and their states, including persistence to local storage.
- Updated UI components to display task lists and handle task actions (retry, remove).
- Removed deprecated TaskCard and TaskList components, replacing them with a new structure for better maintainability.
- Enhanced script execution service to emit progress events for UI updates.
2026-04-16 16:59:49 +08:00

15 KiB
Raw Blame History

任务列表TaskList实时显示脚本执行进度 — 实现思路与开发计划

基于 zn-ai/electron/scripts 中美团、抖音、飞猪等脚本,在渲染层任务列表组件中实时展示脚本执行过程,并支持任务状态流转与操作。


一、现状梳理

模块 当前状态 关键文件
脚本执行 通过 utilityProcess.fork 串行执行,阻塞式返回结果,无实时推送 electron/service/execute-script-service/index.ts
任务列表 UI 使用 @constant/task 静态假数据 src/pages/home/components/TaskList.vueTaskCard.vue
脚本触发入口 TaskOperationDialog.vue 中调用 window.api.executeScript(options) src/pages/home/components/TaskOperationDialog.vue
状态管理 已有 store/script.ts 管理脚本元数据,缺少任务Task生命周期管理 src/store/script.ts
IPC 通信 只有 Request/Response 模式invoke/handle无主进程主动推送 electron/preload/index.ts

二、核心设计思路

2.1 数据模型Task任务+ SubTask子任务

一次"操作房型"的执行产生 1 个 Task,该 Task 包含 N 个 SubTask(对应美团、飞猪、抖音酒店、抖音温泉等脚本)。

// 子任务状态
type SubTaskStatus = 'pending' | 'running' | 'success' | 'failed';

interface SubTask {
  id: string;           // 子任务唯一 ID
  taskId: string;       // 所属主任务 ID
  scriptId: string;     // 脚本标识,如 mt_trace.js
  name: string;         // 渠道名称,如"美团房态追踪"
  status: SubTaskStatus;
  progress: number;     // 0-100
  message: string;      // 当前执行步骤描述
  stdoutTail: string;   // 最新输出
  stderrTail: string;   // 最新错误
  error?: string;       // 失败原因
  startedAt: string;
  completedAt?: string;
}

// 主任务状态
type TaskStatus = 'pending' | 'running' | 'success' | 'partial_failed' | 'failed';

interface Task {
  id: string;
  title: string;        // 如"关闭渠道房型 - 大床房"
  operation: 'open' | 'close';
  roomType: string;
  dateRange: [string, string];
  status: TaskStatus;
  subTasks: SubTask[];
  roomList: any[];      // 保留原始房型列表,用于重试时重新传参
  createdAt: string;
  updatedAt: string;
}

2.2 实时进度推送方案

utilityProcess.forkstdout 可以被主进程监听。采用 "特殊前缀 JSON" + IPC 主动推送" 的方案:

  1. 脚本侧:在执行关键步骤时输出进度日志
    console.log('__ZN_PROGRESS__' + JSON.stringify({ step: '正在登录美团后台', percent: 30 }));
    
  2. 主进程侧execute-script-service 解析 stdout匹配 __ZN_PROGRESS__ 前缀emit 内部事件
  3. IPC 推送runTaskOperationService.ts 订阅内部事件,通过 BrowserWindow.webContents.send('task:progress', payload) 推送到渲染进程

如果暂时不修改脚本文件,也可以先实现"开始/完成"两个节点的推送,中间进度用 stdout 文本更新。

2.3 状态流转与操作权限

Tab 包含状态 操作按钮
待处理 pendingrunning 查看(展开进度详情)
已处理 successfailedpartial_failed 成功 → 移除;失败 / 部分失败 → 重试失败项

排队机制:一个 Task 包含的 N 个 SubTask对应各渠道脚本在主进程中串行排队执行,避免并发启动多个浏览器实例导致资源冲突。

重试粒度:仅针对该 Task 下 status === 'failed' 的 SubTask 进行重试,成功的 SubTask 保持结果不变。


三、分阶段开发计划

Phase 1类型定义与 IPC 扩展

目标:打通主进程 -> 渲染进程的推送通道。

  1. src/lib/task-types.ts(新建)

    • 定义 TaskSubTaskTaskProgressPayload 等接口。
  2. src/lib/constants.ts(修改)

    • 新增 IPC 事件:
      TASK_PROGRESS = 'task:progress',
      TASK_STARTED = 'task:started',
      TASK_COMPLETED = 'task:completed',
      
  3. electron/preload/index.ts(修改)

    • 暴露监听接口:
      onTaskProgress: (cb) => ipcRenderer.on(IPC_EVENTS.TASK_PROGRESS, cb),
      onTaskStarted: (cb) => ipcRenderer.on(IPC_EVENTS.TASK_STARTED, cb),
      onTaskCompleted: (cb) => ipcRenderer.on(IPC_EVENTS.TASK_COMPLETED, cb),
      
  4. global.d.ts(修改)

    • 更新 WindowApi 类型声明。

Phase 2主进程进度推送改造

目标:让脚本执行过程可被渲染层实时感知。

  1. electron/service/execute-script-service/index.ts(修改)

    • 继承 EventEmitter,新增事件:
      • progress:解析到 __ZN_PROGRESS__ 前缀时触发
      • stdout:有新的标准输出时触发
      • stderr:有新的错误输出时触发
    • executeScript 方法签名扩展,增加 taskId / subTaskId 参数用于上下文绑定。
  2. electron/process/runTaskOperationService.ts(修改)

    • EXECUTE_SCRIPT handler 改造:
      • 接收 options.taskId,生成 subTaskId 映射表
      • 执行每个脚本前,发送 TASK_STARTED IPC
      • 订阅 executeScriptServiceInstanceprogress/stdout/stderr 事件,组装 payload 后通过 webContents.send(IPC_EVENTS.TASK_PROGRESS, ...) 推送
      • 脚本退出后发送 TASK_COMPLETED IPC

Phase 3渲染层任务状态管理

目标:集中管理任务生命周期。

  1. src/store/task.ts(新建)

    export const useTaskStore = defineStore('task', () => {
      const tasks = ref<Task[]>([]);
    
      // 创建任务(在 TaskOperationDialog 确认时调用)
      const createTask = (options: ExecuteScriptOptions): Task => { ... };
    
      // 更新子任务进度(监听 TASK_PROGRESS 时调用)
      const updateSubTaskProgress = (taskId, subTaskId, payload) => { ... };
    
      // 完成子任务
      const completeSubTask = (taskId, subTaskId, result) => { ... };
    
     // 重试该任务下所有失败的子任务
     const retryFailedSubTasks = async (taskId) => { ... };
    
      // 移除已处理任务
     const removeTask = (taskId) => { ... };
    
      // Computed
      const pendingTasks = computed(() => ...);   // pending + running
      const completedTasks = computed(() => ...);  // success + failed
    
      return { ... };
    });
    
  2. src/App.vuesrc/pages/home/index.vue(修改)

    • 应用挂载时注册 IPC 监听:
      onMounted(() => {
        window.api.onTaskProgress((_, payload) => taskStore.updateSubTaskProgress(...));
        window.api.onTaskStarted((_, payload) => ...);
        window.api.onTaskCompleted((_, payload) => taskStore.completeSubTask(...));
      });
      

Phase 4UI 组件改造

目标:将假数据替换为真实任务数据,支持 tab 切换与操作。

  1. src/pages/home/components/TaskCard.vue(修改)

    • props 改为接收 TaskSubTask 对象
    • 根据 status 渲染不同状态标签(warning 运行中 / error 失败 / success 成功)
    • 按钮逻辑:
      • running → "查看"(可展开显示 stdoutTail
      • failed / partial_failed → "重试失败项"emit retry-failed
      • success → "移除"emit remove
    • 显示进度条(el-progress 或自定义 div
  2. src/pages/home/components/TaskList.vue(修改)

    • useTaskStore() 读取任务列表
    • "待处理" tab 显示 pendingTasks"已处理" tab 显示 completedTasks
    • 动态计算 total 数量
    • 顶部日期时间实时动态化("今天"/"昨天"/具体日期 + HH:mm:ss
    • 处理 retry-failed(调用 taskStore.retryFailedSubTasks(taskId)/ remove 事件

Phase 5调用点接入

目标:让用户在对话框确认后,任务立刻出现在列表中并开始推送进度。

  1. src/pages/home/components/TaskOperationDialog.vue(修改)

    • confirm 方法中,在调用 window.api.executeScript(options) 之前:
      const task = taskStore.createTask(options);
      options.taskId = task.id;
      window.api.executeScript(options);
      
  2. src/pages/scripts/index.vue(可选)

    • 脚本管理页的"测试运行"也可接入任务列表(通过 SCRIPT_RUN IPC保持体验一致性。

Phase 6脚本输出执行步骤可选但推荐

目标:让子任务进度条真正动起来。

  1. electron/scripts/mt_trace.jsfg_trace.jsdy_hotel_trace.jsdy_hot_spring_trace.js(修改)
    • 在关键步骤插入进度输出:
      function reportProgress(step, percent) {
        console.log('__ZN_PROGRESS__' + JSON.stringify({ step, percent }));
      }
      
      // 示例
      reportProgress('连接本地浏览器', 10);
      reportProgress('定位目标页面', 30);
      reportProgress('操作房态数据', 60);
      reportProgress('保存并校验', 90);
      

四、关键数据流

首次执行

用户点击确认
    ↓
TaskOperationDialog 调用 taskStore.createTask() → 生成 taskId
    ↓
window.api.executeScript({ taskId, roomType, startTime, endTime, operation })
    ↓
主进程 EXECUTE_SCRIPT handler
    ├─ 发送 IPC task:started
    ├─ for 循环串行执行每个脚本utilityProcess.fork
    │     ├─ 脚本 stdout → 解析 __ZN_PROGRESS__ → 发送 IPC task:progress
    │     └─ 脚本 exit → 发送 IPC task:completed
    ↓
渲染层 Store 接收 IPC → 更新 tasks 数组
    ↓
List.vue / Card.vue 响应式更新 UI

重试失败项

用户点击"重试失败项"
    ↓
List.vue 调用 taskStore.retryFailedSubTasks(taskId)
    ├─ 将该 Task 下所有 failed 的 SubTask 重置为 pending
    ├─ Task 状态回退为 pending / running
    └─ 重新调用 window.api.executeScript({ taskId, roomType, startTime, endTime, operation, roomList })
        ↓
主进程只执行 status === pending 的 SubTask串行排队
    ↓
执行完成后更新 Task 状态,移回"已处理" Tab

注意:重试时需要 roomList 参数。createTask 会将 options.roomList 存入 Task.roomList 并随任务列表一起持久化到 electron-store,确保刷新后重试仍能拿到完整参数。


五、文件变更清单汇总

新建文件 说明
src/lib/task-types.ts Task / SubTask 类型定义
src/store/task.ts 任务状态管理 Pinia Store
修改文件 说明
src/lib/constants.ts 新增 TASK_PROGRESS 等 IPC 常量
electron/preload/index.ts 暴露 onTaskProgress 等监听 API
global.d.ts 更新 WindowApi 类型
electron/service/execute-script-service/index.ts 解析进度并 emit 事件
electron/process/runTaskOperationService.ts 绑定 taskId 并推送 IPC
src/pages/home/components/TaskList.vue 接入真实数据、tab 过滤、日期时间动态化
src/pages/home/components/TaskCard.vue 动态状态、进度、操作按钮
src/pages/home/components/TaskOperationDialog.vue 执行前创建 Task
electron/scripts/*.js 插入 __ZN_PROGRESS__ 输出(可选)

六、调整说明2026-04-16

针对实现边界补充以下确认:

  1. 排队机制:一个 Task 内的多个 SubTask各渠道脚本在主进程中串行排队执行,不会并发启动多个浏览器实例。
  2. 重试粒度"重试"操作仅针对该 Task 下 status === 'failed' 的 SubTask成功的 SubTask 保持原结果不变。
  3. Store 方法src/store/task.ts 中增加 retryFailedSubTasks(taskId) 方法,用于批量重置并重新触发失败子任务。
  4. 视觉 UI 延用当前TaskList.vueTaskCard.vue 保持现有样式和布局,仅将数据来源从 @constant/task 静态假数据替换为 useTaskStore,并在现有样式框架内绑定状态、进度与操作按钮。
  5. 日期时间动态化TaskList.vue 顶部原有的静态日期("今天")和时间("02:32:05")改为响应式实时显示:
    • 左侧日期标签根据当前日期自动判断显示 "今天""昨天" 或具体日期(如 04/16)。
    • 右侧时间通过 setInterval 每秒更新,格式为 HH:mm:ss
    • 组件卸载时自动清理定时器。
  6. 数据持久化使用 electron-store
    • 渲染层 Store useTaskStore 通过 IPC (GET_CONFIG / SET_CONFIG) 读写任务列表,避免主进程直接暴露 Store 实例到渲染层。
    • 主进程在 config-serviceDEFAULT_CONFIG 中新增 CONFIG_KEYS.TASK_LIST,默认值为空数组 []
    • useTaskStore 初始化时异步加载已持久化的任务列表;每次 createTaskcompleteSubTaskremoveTask 等变更操作后,通过 window.api.invoke(IPC_EVENTS.SET_CONFIG, CONFIG_KEYS.TASK_LIST, tasks.value) 同步到 electron-store
    • 注意:持久化数据为任务列表(含 SubTask 状态),实时进度更新(task:progress)频繁变化时可仅更新内存状态,待 task:completed 后再统一持久化,以减少写盘次数。

七、Sub-agent 开发分工

共启动 4 个 sub-agent 按流水线并行开发。

Sub-agent 负责阶段 关键文件 依赖
SA-1 主进程 Phase 1 + Phase 2 src/lib/task-types.tssrc/lib/constants.tselectron/preload/index.tsglobal.d.tselectron/service/execute-script-service/index.tselectron/process/runTaskOperationService.ts
SA-2 状态管理 Phase 3 src/store/task.ts(新建)、src/App.vue(挂载监听) 需 SA-1 的类型与 IPC 契约
SA-3 前端 UI Phase 4 + Phase 5 src/pages/home/components/TaskList.vueTaskCard.vueTaskOperationDialog.vue 需 SA-2 的 Store API可按本计划接口契约先行开发
SA-4 脚本进度 Phase 6 electron/scripts/mt_trace.jsfg_trace.jsdy_hotel_trace.jsdy_hot_spring_trace.js

各 Sub-agent 验收标准

SA-1

  • executeScriptService 继承 EventEmitter,能解析 __ZN_PROGRESS__ 前缀并触发 progress/stdout/stderr 事件。
  • runTaskOperationServiceEXECUTE_SCRIPT handler 接收 options.taskId,为每个脚本生成 subTaskId,串行执行并推送 task:started / task:progress / task:completed IPC 事件。

SA-2

  • useTaskStore 通过 electron-store 持久化任务列表IPC 读写)。
  • 提供 createTaskupdateSubTaskProgresscompleteSubTaskretryFailedSubTasksremoveTask
  • pendingTasks / completedTasks computed 正确过滤。

SA-3

  • TaskList.vue / TaskCard.vue 接入 useTaskStoreTab 数量和任务数量动态计算。
  • running 状态显示进度条和最新日志展开;failed / partial_failed 显示"重试失败项"success 显示"移除"。
  • TaskOperationDialog.vue 确认时先 taskStore.createTask(options),再 window.api.executeScript(options)

SA-4

  • 每个脚本在关键步骤输出 __ZN_PROGRESS__{"step":"...","percent":N}
  • 不影响原有脚本逻辑。