# PRD 需求文档：报告对比功能

> 模块：服务监测（service_monitor）
> 优先级：P1
> 创建日期：2026-07-23
> 状态：待开发

---

## 1. 背景与目标

### 1.1 背景

服务监测模块已实现巡检报告生成、查看、导出等功能。运维人员需要对比两次巡检结果，快速定位变化项（如新增异常、恢复正常的检测项），目前需要人工打开两份报告逐项对照，效率低下。

### 1.2 目标

实现报告对比功能：
- 支持选择两份历史报告进行对比
- 差异项分类展示（新增异常、恢复正常、值变化、新增/消失检测项）
- 支持导出对比结果

---

## 2. 功能需求

### 2.1 入口与触发

**入口位置**：报告详情页（`report.html`）

**触发方式**：
1. 报告详情页顶部操作栏新增"对比历史报告"按钮
2. 点击后弹出报告选择器，显示当前目标的历史报告列表（按时间倒序）
3. 选择目标报告后，跳转到对比结果页

### 2.2 报告选择器

**交互设计**：
- 弹窗标题："选择对比报告"
- 显示当前目标的历史报告列表（最多显示最近 20 份）
- 列表项格式：`报告ID（YYYY-MM-DD HH:mm）- 状态摘要`
- 支持搜索/筛选（按日期范围）
- 默认选中最近一份报告

**数据来源**：
- API：`GET /api/service-monitor/reports?target_id={id}&limit=20`
- 返回报告列表（id、created_at、summary）

### 2.3 对比结果展示

**页面布局**：复用 `report.html` 框架，调整为对比视图

**差异分类**：

| 类型 | 定义 | 样式 |
|------|------|------|
| 新增异常 | 报告A正常 → 报告B异常 | 红色高亮 + ⚠️ 标记 |
| 恢复正常 | 报告A异常 → 报告B正常 | 绿色高亮 + ✅ 标记 |
| 值变化 | 数值型检测项，变化幅度超过阈值 | 黄色高亮 + 📊 标记 |
| 新增检测项 | 报告A无 → 报告B有 | 蓝色 + ➕ 标记 |
| 消失检测项 | 报告A有 → 报告B无 | 灰色 + ➖ 标记 |

**值变化阈值**：
- 默认变化率 > 20% 视为"值变化"
- 可配置：`VALUE_CHANGE_THRESHOLD = 0.2`

**展示层级**：
1. 顶部汇总卡片：各类差异数量统计
2. 按模块分组展示差异项
3. 每个检测项显示：KEY、名称、报告A值、报告B值、变化说明

### 2.4 导出对比报告

**导出格式**：
- Markdown（默认）
- HTML（带样式）

**导出内容**：
- 对比报告头：两份报告 ID、时间、目标名称
- 差异汇总统计
- 按类型的差异明细列表

---

## 3. API 设计

### 3.1 对比接口

```
GET /api/service-monitor/reports/{report_id_a}/compare/{report_id_b}
```

**请求参数**：
- `report_id_a`：基准报告 ID（路径参数）
- `report_id_b`：对比报告 ID（路径参数）

**响应结构**：

```json
{
  "success": true,
  "data": {
    "report_a": {
      "id": "20260723_020607_63127b",
      "target_name": "5.44测试服务器",
      "created_at": "2026-07-23 02:06:07",
      "total_items": 521,
      "normal_count": 500,
      "warning_count": 8,
      "critical_count": 13
    },
    "report_b": {
      "id": "20260722_140523_a1b2c3",
      "target_name": "5.44测试服务器",
      "created_at": "2026-07-22 14:05:23",
      "total_items": 518,
      "normal_count": 510,
      "warning_count": 5,
      "critical_count": 3
    },
    "diff_summary": {
      "new_abnormal": 5,
      "recovered": 8,
      "value_changed": 12,
      "new_items": 3,
      "removed_items": 0
    },
    "diff_details": {
      "new_abnormal": [
        {
          "key": "CPU_USAGE",
          "name": "CPU使用率",
          "module": "01_system_basic",
          "value_a": "45%",
          "value_b": "78%",
          "status_a": "normal",
          "status_b": "warning"
        }
      ],
      "recovered": [...],
      "value_changed": [...],
      "new_items": [...],
      "removed_items": [...]
    }
  }
}
```

### 3.2 导出接口

```
GET /api/service-monitor/reports/{report_id_a}/compare/{report_id_b}/export?format=markdown|html
```

**响应**：文件下载

---

## 4. 前端设计

### 4.1 报告详情页修改

**位置**：`templates/service_monitor/report.html`

**新增元素**：
- 顶部操作栏新增"对比历史报告"按钮（仅在登录状态下显示，访客模式隐藏）
- 弹窗组件：报告选择器

### 4.2 对比结果页

**新增页面**：`templates/service_monitor/compare.html`

**页面结构**：
```
┌─────────────────────────────────────────────────────────────┐
│ 对比报告                                        [导出] [返回] │
├─────────────────────────────────────────────────────────────┤
│ 报告A: 20260723_02... | 报告B: 20260722_14...               │
├─────────────────────────────────────────────────────────────┤
│ 差异汇总卡片                                                 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐        │
│ │新增异常 5│ │恢复正常 8│ │值变化 12 │ │新增项 3  │        │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘        │
├─────────────────────────────────────────────────────────────┤
│ 差异明细（按模块分组，可折叠）                               │
│ ▼ 01_system_basic (5项差异)                                 │
│   ⚠️ CPU_USAGE   报告A: 45% (正常) → 报告B: 78% (警告)      │
│   ✅ MEM_USAGE  报告A: 85% (警告) → 报告B: 62% (正常)       │
│   📊 DISK_USAGE 报告A: 45% → 报告B: 52% (变化+7%)           │
│   ...                                                        │
└─────────────────────────────────────────────────────────────┘
```

---

## 5. 技术方案

### 5.1 后端实现

**新增文件**：
- `skill/code/web/service_monitor/services/compare_service.py`：对比逻辑

**修改文件**：
- `skill/code/web/service_monitor/routes.py`：新增对比 API 路由

**核心函数**：

```python
# compare_service.py

def compare_reports(report_a_id: str, report_b_id: str) -> dict:
    """
    对比两份报告，返回差异结果
    
    Args:
        report_a_id: 基准报告ID
        report_b_id: 对比报告ID
    
    Returns:
        {
            "report_a": {...},
            "report_b": {...},
            "diff_summary": {...},
            "diff_details": {...}
        }
    """
    pass

def _classify_diff(item_a: dict, item_b: dict) -> str:
    """
    分类单个检测项的差异类型
    
    Returns:
        "new_abnormal" | "recovered" | "value_changed" | "same"
    """
    pass

def _calculate_value_change(value_a: str, value_b: str) -> float:
    """
    计算数值变化率
    
    Returns:
        变化率（0.0-1.0），非数值返回 -1
    """
    pass
```

### 5.2 前端实现

**修改文件**：
- `templates/service_monitor/report.html`：新增对比按钮 + 弹窗

**新增文件**：
- `templates/service_monitor/compare.html`：对比结果页

---

## 6. 验收标准

### 6.1 功能验收

- [ ] 报告详情页显示"对比历史报告"按钮
- [ ] 点击按钮弹出报告选择器
- [ ] 报告选择器显示当前目标历史报告列表
- [ ] 选择报告后正确跳转到对比页
- [ ] 对比页正确显示差异汇总
- [ ] 差异项按类型正确分类（新增异常/恢复正常/值变化/新增项/消失项）
- [ ] 值变化计算正确（变化率 > 20%）
- [ ] 支持导出 Markdown 格式
- [ ] 支持导出 HTML 格式

### 6.2 边界情况

- [ ] 两份报告无差异时显示友好提示
- [ ] 报告不存在时返回 404 错误
- [ ] 对比不同目标的报告时返回 400 错误
- [ ] 访客模式下隐藏对比按钮

### 6.3 测试覆盖

- [ ] 单元测试：`test_compare_service.py`（新增）
- [ ] 覆盖率：compare_service.py > 80%

---

## 7. 时间规划

| 阶段 | 任务 | 预估时间 |
|------|------|---------|
| 阶段一 | 后端对比逻辑 + API | 2h |
| 阶段二 | 前端报告选择器 | 1h |
| 阶段三 | 前端对比结果页 | 2h |
| 阶段四 | 导出功能 | 1h |
| 阶段五 | 单元测试 | 1h |
| 阶段六 | 集成测试 + 部署 | 1h |
| **合计** | | **8h** |

---

## 8. 相关文档

- `HANDOFF_服务监测.md`：模块交接文档
- `PRD_需求文档_服务监测模块.md`：模块总体需求
