# 设计说明 — 脚本库全局执行进度 + Telegram 结果通知

**日期**: 2026-06-08  
**基线**: `main@3f995a7`  
**参考 UI**: [VSnackbarQueue Promise](https://vuetifyjs.com/zh-Hans/components/snackbar-queue/#promise) · [VProgressLinear chunks](https://vuetifyjs.com/zh-Hans/components/progress-linear/#chunks) · [VAlert 可关闭](https://vuetifyjs.com/zh-Hans/components/alerts/#section-7c7b578b)

## 背景与目标

运维在脚本库对 300+ 台执行命令时，需要：

1. **点击执行后立即返回**，不阻塞对话框/页面，可在其他页面继续操作。
2. **全局顶部**展示执行进度（流动状进度条 +「已进行 3 台，剩余 363 台」）。
3. **执行结束**时：浏览器提醒 + 短促提示音；可点击关闭，**1 秒渐隐**。
4. **Telegram** 推送汇总：成功 N / 失败 M；失败项含**服务器名称、退出码、原因摘要**（可多条）。

## 现状差距

| 能力 | 现状 | 缺口 |
|------|------|------|
| 后台执行 | `POST /api/scripts/exec` **同步 await** 全部批次（366 台可阻塞数十秒～数分钟） | 须改为「创建 execution → 立即返回 → 后台 dispatch」 |
| 进度 UI | ScriptsPage 底部「批量执行状态」+ 10s 轮询 | 须全局 Snackbar Queue + 细粒度进度 |
| 实时推送 | Push 有 `/ws/sync`；脚本**无** WS | 须新增 `script_progress` 广播或复用 alerts WS |
| Telegram | 仅有 `send_telegram_sync_complete` | 须新增 `send_telegram_script_complete` + 设置项 |
| 完成提醒 | 终态仅 `snackbar(warning)` | 须 Alert + 声音 + 渐隐动画 |

## 方案对比

### A. 后台执行 + WebSocket 进度（推荐）

```
POST /exec → 202 + execution_id
       ↓
asyncio.create_task(run_batches)
       ↓ 每完成一台/一批
broadcast_script_progress → /ws/alerts 或 /ws/scripts
       ↓ 终态
send_telegram_script_complete + WS terminal event
```

**优点**：进度实时、与 Push 模式一致、Snackbar 可流畅更新。  
**缺点**：需改后端执行模型 + WS 协议。

### B. 仅前端全局轮询

API 仍同步，或改为后台但前端 2s 轮询 `GET /executions/{id}`。

**优点**：改动小。  
**缺点**：366 台时 API 仍可能长时间不返回；轮询粗、流量大；不符合「先返回再执行」。

**选定 A**。

## 接口与数据模型

### API 变更

| 端点 | 变更 |
|------|------|
| `POST /api/scripts/exec` | 创建 execution + `init_live` 后**立即返回** `{ id, status: "running", progress: "0/N" }`；dispatch 在 `asyncio.create_task` |
| `POST /api/scripts/executions/{id}/retry` | 同上异步模式 |
| `GET /api/scripts/executions/{id}` | 不变（轮询兜底） |

**兼容**：长任务（`long_task`）逻辑保持；短任务也走同一异步路径。

### WebSocket 消息（扩展 `/ws/alerts` 或新建 `/ws/scripts`）

推荐 **扩展 `/ws/alerts`**（App 已连接），新增 type：

```json
{
  "type": "script_progress",
  "execution_id": 6,
  "label": "磁盘检查",
  "status": "running",
  "done": 3,
  "success": 3,
  "failed": 0,
  "total": 366,
  "remaining": 363
}
```

终态：

```json
{
  "type": "script_complete",
  "execution_id": 6,
  "status": "partial",
  "success": 363,
  "failed": 3,
  "total": 366,
  "failed_preview": [
    { "server_id": 1, "name": "srv-a", "exit_code": 1, "summary": "Connection timed out" }
  ]
}
```

`done` = 已有终态结果的台数（含失败）；`success` 沿用 `execution_progress_summary` 语义（失败不计入 success）。

### Telegram

新增 `send_telegram_script_complete()`，参照 `send_telegram_sync_complete`：

- 设置项：`NOTIFY_SCRIPT_COMPLETE`（默认 `true`，Settings 可关）
- 正文：脚本名/命令摘要、操作人、成功/失败/总计、耗时
- **失败明细**：每台一行 — `名称 · exit码 · 原因摘要`（HTML escape + sanitize）
- 超过 Telegram 4096 字符：首批 20 条 +「…及其他 N 台，详见 Nexus #/scripts 执行 #id」
- 触发点：`script_execution_store.apply_update(..., terminal=True)` 或 dedicated `on_execution_terminal` hook

### 前端全局 UI

| 组件 | 用途 |
|------|------|
| `VSnackbarQueue` | 全局顶部（`location="top"`），每任务一条 queue item |
| `VProgressLinear` `buffer-value` + `indeterminate` 交替 | 「流动状」进度；`model-value = done/total*100` |
| `VAlert` closable + `@click:close` + CSS transition 1s | 完成提醒条（成功/部分失败/全失败配色） |
| `Audio` / `new Audio('/app/notify.mp3')` | 终态播放一次（用户可浏览器静音） |

**Snackbar 文案示例**（进行中）：

> 磁盘检查 · 已进行 3 台，剩余 363 台

**Promise 模式**：queue 项在 `script_complete` 时 resolve，切换为 Alert 或关闭 snackbar 并弹出 Alert。

**状态管理**：新建 `useScriptExecutionQueue`（Pinia 或 composable singleton），ScriptsPage / SchedulesPage 提交后只调用 `queue.track(executionId, label, total)`，**移除**页面内 `trackedExecs` 面板（或保留「详情」链接）。

## 安全与性能

- WS 消息仅 JWT 已连接管理员可见；不含完整 stderr（Telegram 同样截断）。
- 后台 task 须捕获异常写 execution `failed`，避免 silent hang。
- 366 台 Telegram 分条/截断，防 flood。
- 单 worker：`create_task` 绑定 primary worker 约定（与现有 background tasks 一致）。

## 验收标准

- [ ] 选 366 台点执行 → **1s 内**关闭对话框，顶部出现进度 Snackbar。
- [ ] 切换至 Dashboard/其他页，进度条仍更新。
- [ ] 文案显示「已进行 X 台，剩余 Y 台」，X+Y=总数（进行中）；Y 随完成递减。
- [ ] 全部结束：短促提示音 + 顶部 Alert「成功 363 台，失败 3 台」；点击后 **1s 渐隐**。
- [ ] Telegram 收到汇总 + 3 台失败名称与原因。
- [ ] Settings 关闭 `NOTIFY_SCRIPT_COMPLETE` 时不发 Telegram。
- [ ] 长任务 / retry / 临时命令 行为一致。

## 非目标（本期不做）

- 多 execution 合并为一条 Telegram（每台 execution 一条）。
- 浏览器 Push Notification API（仅站内 Alert + 声音）。
- 修改 `success/total` 进度语义。
