Files
Nexus/docs/design/plans/2026-06-08-script-exec-global-progress-telegram.md
T

174 lines
6.9 KiB
Markdown
Raw Normal View History

# 实施计划 — 脚本库全局执行进度 + 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 | P0P4 |
**合计**:约 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 取 namesummary 用 `sanitize_external_message`
3. 超长分片或截断 + 指向 execution #id
4. 单测:mock httpx,断言 HTML 含失败台名。
---
## P3 — 前端全局 UIVuetify 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 项 resolvePromise 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 时 confirmP4+ |
| Telegram 过长 | 截断 + 链接 execution id |
| 多 Tab 重复声音 | `localStorage` leader election 或仅 focused tab 播放 |
---
## 建议实施顺序(给 Agent
```
P0 异步 API → P1 WS → P2 Telegram → P3 全局 UI → P4 ScriptsPage 接入 → P5 验证部署
```
**请先确认设计再写代码**perfect-implementation 纪律)。