# 计划执行：监测统计模块

## 文档信息

- 创建时间：2026-07-22
- 关联需求文档：`Docs/需求文档/服务监测/PRD_需求文档_监测统计模块.md`
- 预计改动文件：4 个
- 预计新增文件：2 个

---

## 执行步骤

### Step 1：后端 — statistics_service 统计聚合服务

**文件**：`skill/code/web/service_monitor/services/statistics_service.py`（新增）

**核心函数**：

```python
# 常量
NORMAL, WARNING, CRITICAL = "正常", "警告", "严重"
DEFAULT_RANGE_DAYS = 30
MAX_REPORTS_LOAD = 200

# 内部辅助
def _parse_date_filter(target_id, start_date, end_date) -> (target_id, start_iso, end_iso)
def _load_reports_in_range(target_id, start_date, end_date, limit=200) -> List[dict]

# 公开 API
def get_overview(target_id, start_date, end_date) -> dict
def get_module_stats(target_id, start_date, end_date) -> dict
def get_item_trend(target_id, item_key, start_date, end_date) -> dict
def get_abnormal_items(target_id, start_date, end_date, top_n=10) -> dict
def get_item_keys_for_target(target_id, start_date, end_date) -> List[dict]
```

**`_load_reports_in_range` 实现策略**：
1. 调用 `report_service.list_reports(target_id=target_id, limit=MAX_REPORTS_LOAD)` 获取摘要列表
2. 按 `started_at` 过滤日期范围
3. 对匹配的摘要调用 `report_service.get_report(id)` 获取完整数据
4. 按 `started_at` 升序排序（时间序列需要）

**`get_overview` 实现**：遍历报告，累积 total/normal/warning/critical 计数，计算异常率，找出最常异常项

**`get_module_stats` 实现**：遍历报告的 modules，按 module_id 累积计数，计算 pass_rate

**`get_item_trend` 实现**：扫描每份报告的所有 items，匹配 item_key，用 `_extract_numeric()` 提取数值，构建时间序列

**`get_abnormal_items` 实现**：按 item_key 统计异常次数，降序取 TOP 10

**`get_item_keys_for_target` 实现**：扫描报告，收集所有出现的 item_key，去重后返回

**依赖**：
- `report_service.list_reports()` / `report_service.get_report()`
- `thresholds._extract_numeric()` 数值提取
- `display_names.DISPLAY_NAMES` 键名映射

---

### Step 2：后端 — services/__init__.py 导入

**文件**：`skill/code/web/service_monitor/services/__init__.py`

**改动**：添加 `from . import statistics_service`

---

### Step 3：后端 — routes 添加页面路由 + API

**文件**：`skill/code/web/service_monitor/routes.py`

**3.1 导入**：添加 `statistics_service` 到 import 行

**3.2 页面路由**：
```python
@bp.route('/service-monitor/statistics')
def page_statistics():
    if 'user' not in session:
        return redirect(url_for('auth.login'))
    user = _current_user()
    targets = target_service.list_targets(role=user.get('role', ''))
    return render_template('service_monitor/statistics.html',
        user=user, targets=targets,
        is_admin=(user.get('role') == 'admin'),
        active_menu='statistics')
```

**3.3 API 端点**（5 个）：
- `GET /api/service-monitor/statistics/overview`
- `GET /api/service-monitor/statistics/modules`
- `GET /api/service-monitor/statistics/item-trend`
- `GET /api/service-monitor/statistics/abnormal-items`
- `GET /api/service-monitor/statistics/item-keys`

---

### Step 4：前端 — base.html 侧边栏菜单

**文件**：`skill/code/web/templates/service_monitor/base.html`

**位置**：在"巡检报告"菜单项之后添加

```html
<a href="/service-monitor/statistics" class="nav-item {{ 'active' if active_menu == 'statistics' }}">
    <span class="nav-icon">📈</span>
    <span>监测统计</span>
</a>
```

---

### Step 5：前端 — statistics.html 统计页面

**文件**：`skill/code/web/templates/service_monitor/statistics.html`（新增）

**页面结构**：

1. **筛选栏**：目标选择器 + 日期范围 + 查询按钮
2. **概览卡片**：4 卡片网格（总报告数、异常率、最常异常项、平均检测项）
3. **模块健康概览**：ECharts 堆叠条形图
4. **检测项趋势分析**：检测项选择器 + 折线图（2/3）+ 饼图（1/3）
5. **高频异常检测项**：ECharts 水平堆叠条形图 TOP 10

**ECharts CDN**：`https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js`

**颜色方案**：
- 正常：`#16a34a`
- 警告：`#d97706`
- 严重：`#dc2626`

**JavaScript 逻辑**：
- 页面加载时初始化 ECharts 实例，设置默认日期范围，首次加载数据
- 查询按钮触发 `fetchAllData()` 并行请求 overview + modules + abnormal-items
- 检测项选择器变化时触发 `fetchItemTrend()` 仅刷新趋势区域
- 高频异常项点击时自动填充检测项选择器并刷新趋势
- 窗口 resize 时调用 `chart.resize()`
- 无数据时显示空状态提示

---

### Step 6：本地测试

**命令**：
```bash
cd skill/code && python -m pytest -v
```

**重点验证**：
- statistics_service 各函数返回正确数据结构
- 日期过滤逻辑正确
- 数值提取对百分比/纯数字/带单位值正确
- 空数据场景不报错
- 已有测试不受影响

---

### Step 7：部署验证

**命令**：
```bash
! cd deploy && SSH_PASSWORD='***' python upload_to_server.py
```

**验证步骤**：
1. 登录后点击左侧"监测统计"菜单
2. 确认页面正常加载，筛选栏和图表区域显示
3. 选择目标服务器，设置日期范围，点击查询
4. 确认概览卡片数据正确
5. 确认模块健康图表正常渲染
6. 选择检测项，确认趋势图和饼图显示
7. 确认高频异常项图表正确

---

## 回滚方案

1. 还原各文件：
   ```bash
   git checkout HEAD~1 -- skill/code/web/service_monitor/routes.py
   git checkout HEAD~1 -- skill/code/web/service_monitor/services/__init__.py
   git checkout HEAD~1 -- skill/code/web/templates/service_monitor/base.html
   ```
2. 删除新增文件：
   ```bash
   rm skill/code/web/service_monitor/services/statistics_service.py
   rm skill/code/web/templates/service_monitor/statistics.html
   ```
3. 重新部署

---

## 验收清单

| 检查项 | 状态 |
|--------|------|
| statistics_service.py 5 个公开函数 | ⬜ |
| services/__init__.py 导入 | ⬜ |
| routes 页面路由 + 5 个 API | ⬜ |
| base.html 侧边栏菜单项 | ⬜ |
| statistics.html 完整页面 | ⬜ |
| ECharts 图表正常渲染 | ⬜ |
| 筛选功能正常 | ⬜ |
| 检测项趋势分析正常 | ⬜ |
| 本地测试通过 | ⬜ |
| 部署验证通过 | ⬜ |

---

## 执行记录

| 步骤 | 状态 | 时间 | 备注 |
|------|------|------|------|
| Step 1: statistics_service | 待执行 | - | - |
| Step 2: __init__.py 导入 | 待执行 | - | - |
| Step 3: routes 路由 | 待执行 | - | - |
| Step 4: base.html 菜单 | 待执行 | - | - |
| Step 5: statistics.html 页面 | 待执行 | - | - |
| Step 6: 本地测试 | 待执行 | - | - |
| Step 7: 部署验证 | 待执行 | - | - |
