# PRD — 通知配置功能

> 版本：1.0 ｜ 日期：2026-07-21 ｜ 作者：czj

---

## 1. 背景与问题

当前服务监测模块生成巡检报告后，**仅保存在服务器本地**，用户需要主动登录系统查看。存在以下问题：

1. **无法及时感知**：巡检完成后（特别是定时巡检），管理员无法第一时间获知结果
2. **异常响应滞后**：出现严重问题时，需要等用户主动查看才能发现
3. **缺少推送渠道**：没有邮件/钉钉/企业微信等通知通道配置

## 2. 需求目标

新增**通知配置**功能，让管理员配置报告发送通知的相关信息：

- 配置邮件通知（SMTP）
- 配置钉钉机器人通知
- 配置企业微信机器人通知
- 设置通知触发条件（报告完成后 / 出现异常时）
- 设置通知接收人

## 3. 功能设计

### 3.1 左侧菜单新增

在"定时任务"和"目标管理"之间增加：

```
📊 监测目标
📋 巡检报告
⏰ 定时任务
🔔 通知配置  ← 新增
⚙️ 目标管理
```

### 3.2 通知配置页面

#### 3.2.1 整体布局

- **通知渠道卡片**：邮件 / 钉钉 / 企业微信（可多选启用）
- **触发条件**：报告完成后通知 / 仅异常时通知
- **接收人配置**：邮箱列表 / 钉钉群 / 企业微信群

#### 3.2.2 邮件通知配置

| 字段 | 控件 | 说明 |
|------|------|------|
| 启用邮件通知 | 开关 | 是/否 |
| SMTP 服务器 | 文本 | 如 `smtp.qq.com` |
| SMTP 端口 | 数字 | 如 `465`（SSL）或 `25` |
| 发件人邮箱 | 文本 | 如 `admin@example.com` |
| 邮箱授权码 | 密码 | SMTP 认证密码 |
| 使用 SSL | 开关 | 推荐 SSL |
| 收件人列表 | 文本域 | 多个邮箱用英文逗号分隔 |
| 邮件主题模板 | 文本 | 支持 `{target}`, `{suite}`, `{status}` 变量 |

**测试按钮**：发送测试邮件验证配置

#### 3.2.3 钉钉机器人配置

| 字段 | 控件 | 说明 |
|------|------|------|
| 启用钉钉通知 | 开关 | 是/否 |
| Webhook 地址 | 文本 | 钉钉群机器人的 webhook URL |
| 签名密钥 | 密码 | 加签机器人的密钥（可选） |
| @人员列表 | 文本 | 手机号，多个用逗号分隔（可选） |

**测试按钮**：发送测试消息验证配置

#### 3.2.4 企业微信机器人配置

| 字段 | 控件 | 说明 |
|------|------|------|
| 启用企业微信通知 | 开关 | 是/否 |
| Webhook 地址 | 文本 | 企业微信群机器人的 webhook URL |

**测试按钮**：发送测试消息验证配置

#### 3.2.5 触发条件

| 字段 | 控件 | 说明 |
|------|------|------|
| 触发时机 | 单选 | 报告完成后立即通知 / 仅异常（警告/严重）时通知 |
| 通知内容 | 多选 | 包含摘要 / 包含异常详情 / 包含报告链接 |

### 3.3 通知时机

1. **手动巡检完成**：不发送通知（用户正在页面等待）
2. **定时巡检完成**：根据配置发送通知
3. **异常时通知**：summary 中有 WARNING 或 CRITICAL 时触发

### 3.4 通知内容模板

#### 邮件模板

```
主题：【巡检报告】{target} - {suite} - {status}

正文：
巡检目标：{target_name}
巡检套件：{suite_name}
完成时间：{finished_at}
汇总：正常 {normal} 项，警告 {warning} 项，严重 {critical} 项

{if has_abnormal}
异常项：
{for item in abnormal_items}
- {module}: {name} = {value}（阈值：{threshold}）
{endfor}
{endif}

报告链接：{report_url}
```

#### 钉钉/企业微信模板

```
【巡检报告】{target_name}
套件：{suite_name}
时间：{finished_at}
结果：正常 {normal} / 警告 {warning} / 严重 {critical}
链接：{report_url}
```

## 4. 数据模型

### notifications.json

```json
{
  "email": {
    "enabled": false,
    "smtp_host": "",
    "smtp_port": 465,
    "smtp_user": "",
    "smtp_password": "",
    "use_ssl": true,
    "recipients": [],
    "subject_template": "【巡检报告】{target} - {status}"
  },
  "dingtalk": {
    "enabled": false,
    "webhook": "",
    "secret": "",
    "at_mobiles": []
  },
  "wecom": {
    "enabled": false,
    "webhook": ""
  },
  "trigger": {
    "on_complete": true,
    "on_abnormal_only": false,
    "include_details": true,
    "include_link": true
  },
  "updated_at": "2026-07-21T10:00:00",
  "updated_by": "admin"
}
```

## 5. 后端实现

### 5.1 新增文件

| 文件 | 用途 |
|------|------|
| `service_monitor/services/notification_service.py` | 通知服务：发送邮件/钉钉/企业微信 |
| `service_monitor/templates/service_monitor/notification.html` | 配置页面 |

### 5.2 notification_service.py 核心函数

```python
def get_config() -> dict          # 获取配置
def save_config(data: dict)       # 保存配置
def test_email() -> dict          # 发送测试邮件
def test_dingtalk() -> dict       # 发送测试钉钉消息
def test_wecom() -> dict          # 发送测试企业微信消息
def send_notification(report: dict, report_url: str)  # 根据配置发送通知
```

### 5.3 调用时机

在 `runner_service.run_inspection_sync()` 完成后调用：

```python
# 定时任务执行完成后
if result.get("success"):
    report = report_service.get_report(result["report_id"])
    notification_service.send_notification(report, report_url)
```

## 6. API 设计

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/service-monitor/notification` | 配置页面 |
| GET | `/api/service-monitor/notification` | 获取配置 |
| PUT | `/api/service-monitor/notification` | 保存配置（管理员） |
| POST | `/api/service-monitor/notification/test-email` | 测试邮件 |
| POST | `/api/service-monitor/notification/test-dingtalk` | 测试钉钉 |
| POST | `/api/service-monitor/notification/test-wecom` | 测试企业微信 |

## 7. 安全考虑

1. **敏感信息加密**：SMTP 密码、钉钉密钥使用 Fernet 加密存储（复用现有 `crypto.py`）
2. **权限控制**：仅管理员可配置和查看敏感信息
3. **接口保护**：测试接口加频率限制（防止滥用）

## 8. 不做的事

- 不支持短信通知（需要付费服务商）
- 不支持 Slack 等国外平台
- 不支持通知历史记录（后续可扩展）
- 不支持多套通知配置（如不同目标发不同人）

## 9. 后续扩展

- 支持按目标配置不同通知渠道
- 支持通知历史记录和重发
- 支持通知频率限制（如 1 小时内同类异常只通知一次）