174 lines
6.9 KiB
Markdown
174 lines
6.9 KiB
Markdown
|
|
# 实施计划 — 脚本库全局执行进度 + Telegram 通知
|
|||
|
|
|
|||
|
|
**日期**: 2026-06-08
|
|||
|
|
**设计**: [2026-06-08-script-exec-global-progress-telegram-design.md](../specs/2026-06-08-script-exec-global-progress-telegram-design.md)
|
|||
|
|
|
|||
|
|
## 分期概览
|
|||
|
|
|
|||
|
|
| 阶段 | 内容 | 估时 | 依赖 |
|
|||
|
|
|------|------|------|------|
|
|||
|
|
| **P0** | 后端异步 exec + terminal hook | 1d | — |
|
|||
|
|
| **P1** | WS `script_progress` / `script_complete` | 0.5d | P0 |
|
|||
|
|
| **P2** | Telegram `send_telegram_script_complete` | 0.5d | P0 |
|
|||
|
|
| **P3** | 前端全局 SnackbarQueue + Progress + Alert + 声音 | 1d | P1 |
|
|||
|
|
| **P4** | ScriptsPage 接入 / 移除页内面板 | 0.5d | P3 |
|
|||
|
|
| **P5** | 测试、changelog、audit、部署 | 0.5d | P0–P4 |
|
|||
|
|
|
|||
|
|
**合计**:约 3–4 人日(单 Agent 连续实施)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## P0 — 后端:异步执行(关键路径)
|
|||
|
|
|
|||
|
|
### 涉及文件
|
|||
|
|
|
|||
|
|
| 文件 | 变更 |
|
|||
|
|
|------|------|
|
|||
|
|
| [server/application/services/script_service.py](../../server/application/services/script_service.py) | `execute_command` 拆为 `start_execution` + `_run_execution_batches` |
|
|||
|
|
| [server/api/scripts.py](../../server/api/scripts.py) | `/exec`、`/retry` 立即返回 running |
|
|||
|
|
| [server/main.py](../../server/main.py) | 确保 task 异常日志 + 可选 task 注册防 shutdown 丢失 |
|
|||
|
|
|
|||
|
|
### 步骤
|
|||
|
|
|
|||
|
|
1. `execute_command` 创建 DB/Redis 记录后 `asyncio.create_task(_run_execution_batches(...))`。
|
|||
|
|
2. 将现有 batch 循环移入 `_run_execution_batches`,逻辑不变。
|
|||
|
|
3. Task 顶层 `try/except`:异常时 `apply_update(status=failed, terminal=True)`。
|
|||
|
|
4. API 返回 `get_execution_detail(id)`(status=running, progress=0/N)。
|
|||
|
|
5. **注意**:同一 worker 内 task 即可;多 worker 时 Redis live 已是 SSOT(已满足)。
|
|||
|
|
|
|||
|
|
### 测试
|
|||
|
|
|
|||
|
|
- `tests/test_script_execution_async.py`:mock dispatch,断言 POST /exec 立即返回 & 后台终态。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## P1 — WebSocket 进度广播
|
|||
|
|
|
|||
|
|
### 涉及文件
|
|||
|
|
|
|||
|
|
| 文件 | 变更 |
|
|||
|
|
|------|------|
|
|||
|
|
| [server/api/websocket.py](../../server/api/websocket.py) | `broadcast_script_progress` / `broadcast_script_complete` |
|
|||
|
|
| [server/infrastructure/redis/script_execution_store.py](../../server/infrastructure/redis/script_execution_store.py) | `apply_update` 中调用 broadcast(或 script_service 批次回调) |
|
|||
|
|
| [server/application/services/script_job_callback.py](../../server/application/services/script_job_callback.py) | long_task 单台 callback 时也 broadcast |
|
|||
|
|
|
|||
|
|
### 步骤
|
|||
|
|
|
|||
|
|
1. 定义消息 schema(见 design doc)。
|
|||
|
|
2. 每批 `batch_progress` 或每台完成后推送(366 台建议**每完成 1 台或每批**推送,避免 WS 风暴可 batch 每 5 台)。
|
|||
|
|
3. 前端 [useWebSocket.ts](../../frontend/src/composables/useWebSocket.ts) 解析 `script_progress` / `script_complete`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## P2 — Telegram 脚本执行结果
|
|||
|
|
|
|||
|
|
### 涉及文件
|
|||
|
|
|
|||
|
|
| 文件 | 变更 |
|
|||
|
|
|------|------|
|
|||
|
|
| [server/infrastructure/telegram/__init__.py](../../server/infrastructure/telegram/__init__.py) | `send_telegram_script_complete` |
|
|||
|
|
| [server/config.py](../../server/config.py) | `NOTIFY_SCRIPT_COMPLETE` |
|
|||
|
|
| [server/api/settings.py](../../server/api/settings.py) | 设置项读写(若 NOTIFY_* 走 settings 表) |
|
|||
|
|
| terminal hook | 组装 failed 列表(server name + exit_code + stderr 首行) |
|
|||
|
|
|
|||
|
|
### 步骤
|
|||
|
|
|
|||
|
|
1. 复用 `send_telegram_sync_complete` 结构与 `_notify_enabled`。
|
|||
|
|
2. 失败明细:查 server repo 取 name;summary 用 `sanitize_external_message`。
|
|||
|
|
3. 超长分片或截断 + 指向 execution #id。
|
|||
|
|
4. 单测:mock httpx,断言 HTML 含失败台名。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## P3 — 前端全局 UI(Vuetify 4)
|
|||
|
|
|
|||
|
|
### 涉及文件
|
|||
|
|
|
|||
|
|
| 文件 | 变更 |
|
|||
|
|
|------|------|
|
|||
|
|
| [frontend/src/App.vue](../../frontend/src/App.vue) | `VSnackbarQueue` 替换单条 `v-snackbar`;挂载 `ScriptExecProgressHost` |
|
|||
|
|
| **新建** `frontend/src/components/script/ScriptExecProgressHost.vue` | Queue item 模板:ProgressLinear chunks + 文案 |
|
|||
|
|
| **新建** `frontend/src/composables/useScriptExecutionQueue.ts` | track / onProgress / onComplete / playSound |
|
|||
|
|
| **新建** `frontend/src/components/script/ScriptExecCompleteAlert.vue` | VAlert closable + 1s leave transition |
|
|||
|
|
| `frontend/public/notify.mp3` 或 assets | 提示音(短、免版权) |
|
|||
|
|
|
|||
|
|
### UI 行为
|
|||
|
|
|
|||
|
|
1. **进行中**:SnackbarQueue 顶部,`location="top"`,`timeout="-1"`(手动关闭或完成关闭)。
|
|||
|
|
2. **进度条**:`v-progress-linear` `rounded` `height="6"` `:model-value="done/total*100"` `:buffer-value="..."` 制造流动感(见 Vuetify chunks 示例)。
|
|||
|
|
3. **完成**:
|
|||
|
|
- Snackbar queue 项 resolve(Promise API)。
|
|||
|
|
- 显示 `VAlert`(type=success/warning/error),`closable`,点击关闭触发 `v-fade-transition` 1000ms。
|
|||
|
|
- `new Audio(...).play().catch(() => {})`(静默处理自动播放策略拦截)。
|
|||
|
|
4. Alert 可点「查看详情」→ `router.push('#/scripts')` + `openExecDetail(id)`(通过 event bus 或 query)。
|
|||
|
|
|
|||
|
|
### 与现有 `$snackbar` 兼容
|
|||
|
|
|
|||
|
|
- 保留 `window.$snackbar` 给普通 toast;脚本进度走独立 queue,避免互相覆盖。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## P4 — ScriptsPage 接入
|
|||
|
|
|
|||
|
|
### 涉及文件
|
|||
|
|
|
|||
|
|
| 文件 | 变更 |
|
|||
|
|
|------|------|
|
|||
|
|
| [frontend/src/pages/ScriptsPage.vue](../../frontend/src/pages/ScriptsPage.vue) | `submitExec` → `queue.track()`;删除/隐藏页内「批量执行状态」卡片 |
|
|||
|
|
| [frontend/src/pages/SchedulesPage.vue](../../frontend/src/pages/SchedulesPage.vue) | 若 schedule 触发 exec,同样 track(可选 P4+) |
|
|||
|
|
|
|||
|
|
### 步骤
|
|||
|
|
|
|||
|
|
1. `doRun` / `doQuickExec`:POST 成功后 `showRun=false`,调用 `queue.track(id, label, total)`。
|
|||
|
|
2. 移除 `trackedExecs` / `pollTimer` / `activeTrackedExecs`(逻辑迁至 composable)。
|
|||
|
|
3. WS 断线时 composable 降级 3s 轮询直至终态。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## P5 — 验证与交付
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pytest tests/test_script_execution_async.py tests/test_telegram_script.py -q
|
|||
|
|
cd frontend && npx vite build
|
|||
|
|
bash scripts/local_verify.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 手动验收
|
|||
|
|
|
|||
|
|
1. 生产 10 台脚本:顶部进度 + 完成 Alert + 声音。
|
|||
|
|
2. 366 台(或 staging):Telegram 汇总与失败明细。
|
|||
|
|
3. 关闭 Telegram 通知设置后无推送。
|
|||
|
|
|
|||
|
|
### 文档
|
|||
|
|
|
|||
|
|
- `docs/changelog/2026-06-08-script-exec-global-progress-telegram.md`
|
|||
|
|
- `docs/audit/2026-06-08-script-exec-global-progress-telegram.md`
|
|||
|
|
- 更新 [AI-HANDOFF-2026-06-08.md](../../project/AI-HANDOFF-2026-06-08.md)
|
|||
|
|
|
|||
|
|
### 回滚
|
|||
|
|
|
|||
|
|
- 后端:revert commit;进行中的 execution 靠 Redis live 继续,无 schema 迁移。
|
|||
|
|
- 前端:revert + vite build deploy。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 风险与对策
|
|||
|
|
|
|||
|
|
| 风险 | 对策 |
|
|||
|
|
|------|------|
|
|||
|
|
| 366 台 WS 消息过多 | 节流:每批或每 5 台推送一次 |
|
|||
|
|
| 浏览器拦截自动播放 | 首次用户手势后解锁;Alert 仍可见 |
|
|||
|
|
| 异步 exec 重复提交 | 可选:同脚本 running 时 confirm(P4+) |
|
|||
|
|
| Telegram 过长 | 截断 + 链接 execution id |
|
|||
|
|
| 多 Tab 重复声音 | `localStorage` leader election 或仅 focused tab 播放 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 建议实施顺序(给 Agent)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
P0 异步 API → P1 WS → P2 Telegram → P3 全局 UI → P4 ScriptsPage 接入 → P5 验证部署
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**请先确认设计再写代码**(perfect-implementation 纪律)。
|