锁定首批 5 份核心架构文档: dashboard-system-rebuild/refund-cashflow-views-rebuild/staff-assignment-history/metric-alerting-notification-design/时间字段统计规范修订 → 移入 docs/arch/ 并重命名 A-20260902-00X, 标 LOCKED(v1.0), 打 arch/A-20260902-00X tag。更新 _pending/README 移除已锁定项(剩余 15 份待审)。
385 lines
19 KiB
Markdown
385 lines
19 KiB
Markdown
# 运营指标阈值化与预警通知机制 设计方案
|
||
|
||
**状态**:LOCKED(v1.0)
|
||
**锁定日期**:20260902
|
||
**锁定脚本**:scripts/lock-arch-docs.sh
|
||
|
||
|
||
> 版本: v0.3 · 2026-08-21
|
||
> 状态: M1~M3 已实施(最小闭环), M4 通道架构已就绪(站内信已接, 群机器人/邮件可配置接入), M5 扩展待定
|
||
> 范围: 指标注册表 + 多级阈值(优秀/达标/预警/告警) + 预警引擎 + 通知通道抽象
|
||
|
||
## 0. 实施状态(2026-08-21)
|
||
|
||
| 里程碑 | 状态 | 说明 |
|
||
|---|---|---|
|
||
| M1 指标与阈值基础 | ✅ 已实施 | 5 个指标注册(recovery_rate/refund_count/ticket_backlog/external_complaint_7d/internal_complaint_daily) + 四级阈值(metric_thresholds 留痕) |
|
||
| M2 预警引擎 | ✅ 已实施 | scan_alerts 注册表驱动: 四级分级/区间幂等/升级/恢复自愈/数据健康检查; 定时每小时自动扫描 |
|
||
| M3 站内通知 | ✅ 已实施 | 告警触发/恢复 → 站内信(notify) + 投递记录(alert_deliveries) |
|
||
| M4 推送通道 | 🟡 架构已就绪 | notification_channels 表 + Channel 抽象(站内信已接; webhook 群机器人/邮件通道类已实现, 配置即用); 前端通道管理+测试按钮 |
|
||
| M5 扩展联动 | ⏳ 待定 | 指标扩充/聚合/与 Incident/Measure 联动 |
|
||
|
||
**实现与设计差异(§8.1)**: 告警记录复用现有 `alerts` 表(扩展 metric_code/metric_value/threshold_value/period_start/period_end/data_ok/recovered_at 字段), 未新建 alert_events 表。
|
||
|
||
**新增后端文件**: `app/models/metric.py` / `app/models/notification_channel.py` / `app/services/metric_registry_service.py` / `app/services/notification_channel.py` / `app/api/v1/metrics.py` / `app/api/v1/channels.py`; 迁移 `n9a8b7c6d5e4f_add_metric_alerting.py`(含指标/阈值/站内信种子数据)。
|
||
|
||
**前端**: `web/src/views/management/Alerts.vue` 升级为四 Tab(指标水位/预警规则/预警记录/通知通道); 新增 `web/src/api/metric.js`、`web/src/api/channel.js`。
|
||
|
||
---
|
||
|
||
## 1. 背景与目标
|
||
|
||
当前系统的看板(运营看板/今日看板/投诉分析)已实时计算大量运营指标(退款挽回率、三源投诉、积压工单、首响时长等),但**指标的"好坏"完全靠人肉看数字判断**——没有"低于多少要盯、低于多少要报警"的系统化机制。
|
||
|
||
目标:
|
||
|
||
1. **指标化监测**: 运营指标有明确的"正常/预警/告警"水位线,系统按周期自动评估
|
||
2. **阈值参数化**: 优秀/达标/预警/告警四级阈值全部由系统参数配置(可改、可留痕),不硬编码
|
||
3. **预警自动生成**: 指标越线自动生成预警记录,走完整生命周期(待确认→已确认→已恢复)
|
||
4. **通知多渠道**: 预警触发后站内信通知(已有),并预留企业微信/钉钉/飞书群机器人、邮件等推送通道(可扩充)
|
||
|
||
核心原则:**预警监测的指标口径必须与看板同源同口径**,先有指标注册表,再有阈值与规则,避免"看板一个数、预警另一个数"。
|
||
|
||
---
|
||
|
||
## 2. 现状盘点
|
||
|
||
### 2.1 已有基础(可复用)
|
||
|
||
| 模块 | 位置 | 现状 |
|
||
|---|---|---|
|
||
| 预警规则/记录 | `app/models/alert.py` AlertRule/Alert | 已有: name/type/condition(JSON)/threshold/enabled; 记录含 level/status(pending/confirmed/ignored) |
|
||
| 预警扫描 | `app/api/v1/alerts.py` scan_alerts | 4 种硬编码规则(退款激增/工单积压/事件激增/投诉激增), 单一 threshold, level 由 threshold×2 推断, **当日幂等** |
|
||
| 定时调度 | `app/services/bms_scheduler.py` | 已有每小时预警扫描钩子(当日幂等), 可复用 |
|
||
| 站内信 | `app/services/notifier.py` notify() | 统一入口, 支持按用户/角色, Notification 表, 前端有角标 |
|
||
| 前端预警中心 | `web/src/views/management/Alerts.vue` | 规则 CRUD + 预警记录 + 手动扫描 + 确认/忽略 |
|
||
| 指标计算 | `app/services/analytics.py` | 各看板指标已实时计算(如 refund recovery), 口径已沉淀 |
|
||
|
||
### 2.2 缺口
|
||
|
||
1. **阈值单一数值** → 无法表达"达标(绿)/预警(黄)/告警(红)"三级水位
|
||
2. **规则类型硬编码** → 加一个新指标预警必须改 scan_alerts 代码, 不可配置
|
||
3. **只支持"≥阈值"的激增类判断** → 无法表达"挽回率 < 60%"这类"越低越差"指标
|
||
4. **指标无注册表** → 口径散落, 规则无法引用统一指标定义
|
||
5. **预警只入库不通知** → 不调 notify(), 无任何推送通道
|
||
6. **无告警生命周期闭环** → 无"恢复/解除"概念, 无效告警会堆积
|
||
7. **无数据健康保护** → SCRM 同步失败导致指标=0 会误报"恶化告警"
|
||
|
||
---
|
||
|
||
## 3. 总体架构
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────┐
|
||
│ 指标注册表 (metric_registry) │
|
||
│ 每个指标: code / 口径函数 / 周期 / 方向 / 依赖数据源 │
|
||
└───────────────┬──────────────────────────────────────┘
|
||
│ 引用
|
||
┌───────────────▼──────────────────────────────────────┐
|
||
│ 阈值参数 (metric_thresholds) │
|
||
│ metric_code + 优秀线/达标线/预警线/告警线 + 生效区间 │
|
||
└───────────────┬──────────────────────────────────────┘
|
||
│ 计算
|
||
┌───────────────▼──────────────────────────────────────┐
|
||
│ 预警引擎 (alert_engine) │
|
||
│ 周期扫描 → 数据健康检查 → 指标求值 → 与阈值比较 │
|
||
│ → 幂等/升级/静默 → 生成告警记录 │
|
||
└───────────────┬──────────────────────────────────────┘
|
||
│ 触发
|
||
┌───────────────▼──────────────────────────────────────┐
|
||
│ 通知分发 (notification_channel) │
|
||
│ 站内信(先) → 企业微信/钉钉/飞书群机器人 → 邮件(后) │
|
||
└──────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
分层职责:
|
||
|
||
- **指标注册表**: 定义"算什么"(口径), 是唯一事实源, 看板与预警共用
|
||
- **阈值参数**: 定义"多好算好"(水位线), 可配置可留痕
|
||
- **预警引擎**: 定义"何时喊"(触发), 与指标/阈值解耦, 新增指标无需改引擎
|
||
- **通知通道**: 定义"喊给谁、怎么喊", 通道可插拔
|
||
|
||
---
|
||
|
||
## 4. 指标注册表(核心)
|
||
|
||
### 4.1 设计要点
|
||
|
||
- 每个指标注册一条元数据:**计算函数** + **周期** + **方向** + **依赖数据源**
|
||
- 指标值由统一求值器调用注册的计算函数得到, **与看板共用同一函数**(同源同口径)
|
||
- 注册方式: 代码注册(函数式) + 数据库元数据(阈值引用), 避免重复实现
|
||
|
||
### 4.2 首批指标清单(试点范围)
|
||
|
||
| code | 指标名 | 方向 | 周期 | 依赖数据源 | 口径来源 |
|
||
|---|---|---|---|---|---|
|
||
| recovery_rate | 退款挽回率 | 越高越好 | 日 | SCRM cs_record | analytics 退款挽回计算 |
|
||
| refund_count | 当日退款笔数 | 越低越好 | 日 | refund | 看板 refund kpi |
|
||
| ticket_backlog | 待处理投诉工单积压 | 越低越好 | 日 | service_tickets | 投诉 backlog.total |
|
||
| external_complaint_7d | 近7天外部投诉量 | 越低越好 | 日 | external_complaints | 投诉 supplementary |
|
||
| internal_complaint_daily | 当日内部投诉量 | 越低越好 | 日 | service_tickets | 投诉 internal.total |
|
||
| first_response_sec | 首次响应时长(均值) | 越低越好 | 日 | scrm_session | SCRM 绩效 |
|
||
| transfer_rate | 会话转接率 | 双向 | 日 | scrm_session_transfer | SCRM 绩效 |
|
||
| sync_health | 数据同步成功率 | 越高越好 | 日 | scrm_sync_log/bms_sync_log | 同步日志 |
|
||
|
||
> 指标清单按需扩充: 值班在线率、工作效率值、四路退款占比、套餐充值量等。
|
||
> 重要:**先有指标, 后有阈值**; 未注册的指标不参与预警。
|
||
|
||
---
|
||
|
||
## 5. 三级阈值与规则模型
|
||
|
||
### 5.1 阈值语义(四级: 优秀/达标/预警/告警)
|
||
|
||
| 级别 | 含义 | 运营动作 | 颜色 |
|
||
|---|---|---|---|
|
||
| 优秀(excellent) | 指标超出预期, 明显优于正常水位 | 通报表扬, 提炼可复制的经验 | 蓝 |
|
||
| 达标(green) | 指标处于正常区间 | 无需处理 | 绿 |
|
||
| 预警(yellow) | 指标偏离正常,需关注 | 盯一盯,排查原因 | 黄 |
|
||
| 告警(red) | 指标严重偏离,需干预 | 立即处理,升级 | 红 |
|
||
|
||
- **越高越好型**(如挽回率): 阈值 = 下限线。`value ≥ 优秀线` → 优秀; `value < 预警线` → 预警; `value < 告警线` → 告警; 其余 → 达标
|
||
- **越低越好型**(如退款笔数/积压): 阈值 = 上限线。`value ≤ 优秀线` → 优秀; `value > 预警线` → 预警; `value > 告警线` → 告警; 其余 → 达标
|
||
- **优秀线可选**: 不配置优秀线则退化为"达标/预警/告警"三级, 不产生"优秀"状态
|
||
- **仅 预警/告警 触发告警记录与通知**; 优秀/达标仅展示水位状态(优秀可另行配置"通报表扬"通知, 默认不推)
|
||
- 方向由指标注册表声明, 阈值表只存数值, 引擎按方向解释
|
||
|
||
### 5.2 规则 = 指标 × 阈值 × 调度
|
||
|
||
规则配置项:
|
||
|
||
```
|
||
metric_code 引用指标注册表
|
||
warning_threshold 预警线
|
||
critical_threshold 告警线
|
||
period 日/周/月
|
||
enabled 是否启用
|
||
channels 通知通道列表(默认站内信)
|
||
recipient_roles 通知角色(admin/supervisor)或指定用户
|
||
silence_window 静默时段(如 00:00-08:00 不推送,可选)
|
||
```
|
||
|
||
> 兼容现有 AlertRule: 规则仍存 alert_rules 表, 字段扩展(见 §8), 现有 4 类硬编码规则迁移为注册表指标+阈值。
|
||
|
||
---
|
||
|
||
## 6. 预警引擎
|
||
|
||
### 6.1 扫描流程
|
||
|
||
```
|
||
1. 数据健康检查(防假告警)
|
||
- 指标依赖的数据源今日同步是否成功(sync_health)
|
||
- 数据量是否合理(如 SCRM 会话量低于前3日均值 20% → 视为数据异常)
|
||
- 数据异常 → 跳过该指标, 生成"数据异常"提示(不产生告警)
|
||
2. 指标求值(调用注册表计算函数, 与看板同口径)
|
||
3. 与多级阈值比较 → 确定级别(优秀/达标/预警/告警; 无越线时若之前有告警则生成"已恢复"记录)
|
||
4. 幂等去重: 同规则同周期同级别最多一条 pending
|
||
5. 升级: 连续 N 个周期处于预警且未处理 → 升级为告警
|
||
6. 生成 Alert 记录 + 通知分发(§7)
|
||
```
|
||
|
||
### 6.2 关键规则
|
||
|
||
| 规则 | 说明 |
|
||
|---|---|
|
||
| 当日幂等 | 同规则同周期同级别不重复生成(复用现有机制) |
|
||
| 连续恶化升级 | 预警持续 N 天未处理且指标继续恶化 → 自动升级告警 |
|
||
| 恢复自愈 | 指标回到达标线 → 自动生成/更新"已恢复"状态并通知 |
|
||
| 静默窗口 | 配置的时段内不推送(仅入库), 避免夜间打扰 |
|
||
| 数据保护 | 数据不完整/异常时不告警, 单独标记(防假告警, 见 6.3) |
|
||
| 批量聚合 | 同批多条同类预警合并为一条摘要(可选, 后期) |
|
||
|
||
### 6.3 数据健康保护(重要)
|
||
|
||
- 预警求值前先跑 `sync_health` 检查: 今日 SCRM/BMS 是否同步成功
|
||
- 指标值出现"断崖式"变化(如为 0 或骤降 80%)时, 先判定为数据异常而非业务恶化
|
||
- 数据异常与业务告警分开展示, 避免"假告警"干扰决策
|
||
|
||
---
|
||
|
||
## 7. 通知通道抽象
|
||
|
||
### 7.1 通道接口
|
||
|
||
```python
|
||
class NotificationChannel(Protocol):
|
||
def send(self, ctx: AlertContext, recipient: list) -> ChannelResult: ...
|
||
```
|
||
|
||
- `站内信(InAppChannel)` — 复用现有 `notifier.notify()`, **首发实现**
|
||
- `企业微信群机器人 / 钉钉 / 飞书群机器人(WebhookChannel)` — 二期
|
||
- `邮件(EmailChannel)` — 三期
|
||
|
||
### 7.2 配置模型
|
||
|
||
- 通道注册表: `notification_channels` 表, 每个通道有 type + config(webhook URL/密钥, 加密存储)
|
||
- 规则级配置: 每条规则选择通知通道 + 接收对象(角色/指定用户)
|
||
- 测试能力: 每个通道提供"发送测试"按钮(前端), 验证配置可用
|
||
- 发送失败降级: 群机器人失败 → 自动降级为站内信, 并记发送日志
|
||
|
||
### 7.3 通知去重
|
||
|
||
- 同一告警不重复推送: 以 Alert.id 为幂等键, 推送状态记录在 alert_deliveries 表
|
||
- 恢复通知与触发通知各推送一次
|
||
|
||
---
|
||
|
||
## 8. 数据模型(表结构草案)
|
||
|
||
### 8.1 新建
|
||
|
||
```sql
|
||
-- 指标注册表(元数据; 计算函数在代码层注册)
|
||
CREATE TABLE metric_registry (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
code VARCHAR(64) UNIQUE NOT NULL, -- 指标code, 与代码注册对应
|
||
name_cn VARCHAR(128) NOT NULL,
|
||
direction VARCHAR(8) NOT NULL, -- higher_better / lower_better
|
||
period VARCHAR(8) NOT NULL DEFAULT 'day', -- day/week/month
|
||
data_sources JSONB DEFAULT '[]', -- 依赖数据源, 供健康检查
|
||
description TEXT,
|
||
created_at TIMESTAMPTZ DEFAULT now()
|
||
);
|
||
|
||
-- 阈值参数(四级水位: 优秀/达标/预警/告警, 可留痕)
|
||
CREATE TABLE metric_thresholds (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
metric_code VARCHAR(64) REFERENCES metric_registry(code),
|
||
excellent_value DOUBLE PRECISION, -- 优秀线(可选, 不配则无优秀状态)
|
||
warning_value DOUBLE PRECISION NOT NULL, -- 预警线
|
||
critical_value DOUBLE PRECISION NOT NULL, -- 告警线
|
||
unit VARCHAR(16), -- % / 笔 / 秒 ...
|
||
valid_from TIMESTAMPTZ, -- 生效区间(支持历史调参留痕)
|
||
valid_to TIMESTAMPTZ,
|
||
updated_by INTEGER REFERENCES users(id),
|
||
updated_at TIMESTAMPTZ DEFAULT now()
|
||
);
|
||
|
||
-- 告警记录(扩展现有 alert_alerts 或新建 alert_events)
|
||
CREATE TABLE alert_events (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
metric_code VARCHAR(64),
|
||
rule_id BIGINT, -- 关联 alert_rules
|
||
level VARCHAR(16), -- warning / critical / recovered
|
||
metric_value DOUBLE PRECISION,
|
||
threshold_value DOUBLE PRECISION,
|
||
period_start TIMESTAMPTZ,
|
||
period_end TIMESTAMPTZ,
|
||
status VARCHAR(16) DEFAULT 'pending', -- pending/confirmed/ignored/recovered
|
||
data_ok BOOLEAN DEFAULT TRUE, -- 数据健康标记(防假告警)
|
||
detail TEXT,
|
||
created_at TIMESTAMPTZ DEFAULT now()
|
||
);
|
||
|
||
-- 通知通道配置
|
||
CREATE TABLE notification_channels (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
code VARCHAR(32) UNIQUE NOT NULL, -- inapp / wecom_robot / dingtalk_robot / feishu_robot / email
|
||
name_cn VARCHAR(64),
|
||
channel_type VARCHAR(16),
|
||
config JSONB, -- webhook等, 敏感字段加密
|
||
enabled BOOLEAN DEFAULT TRUE,
|
||
created_at TIMESTAMPTZ DEFAULT now()
|
||
);
|
||
|
||
-- 通知投递记录(幂等)
|
||
CREATE TABLE alert_deliveries (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
alert_id BIGINT,
|
||
channel VARCHAR(32),
|
||
status VARCHAR(16), -- success / failed / degraded
|
||
detail TEXT,
|
||
created_at TIMESTAMPTZ DEFAULT now()
|
||
);
|
||
|
||
-- 指标历史值(回溯"为什么触发/持续多久")
|
||
CREATE TABLE metric_daily_snapshots (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
metric_code VARCHAR(64),
|
||
value DOUBLE PRECISION,
|
||
period_date DATE,
|
||
level VARCHAR(16), -- excellent/green/warning/critical
|
||
UNIQUE (metric_code, period_date)
|
||
);
|
||
```
|
||
|
||
### 8.2 复用与兼容
|
||
|
||
- `alert_rules` 扩展字段: metric_code / warning_value / critical_value / channels / recipient_roles / silence_window(JSONB), 现有 4 类规则迁移
|
||
- `Alert` 记录增加 metric_code/level 对齐(或迁移到 alert_events)
|
||
- 前端 Alerts.vue 扩展: 规则管理升级为"指标+阈值"配置, 预警记录增加级别/恢复状态
|
||
|
||
---
|
||
|
||
## 9. API 与前端规划
|
||
|
||
### 9.1 API(后端)
|
||
|
||
```
|
||
GET /api/v1/metrics 指标注册表列表
|
||
GET /api/v1/metrics/{code}/current 指标当前值+当前级别(前端展示水位)
|
||
PUT /api/v1/metrics/{code}/thresholds 配置/修改三级阈值(留痕)
|
||
POST /api/v1/alerts/scan 手动触发扫描(已有, 扩展)
|
||
POST /api/v1/alerts/{id}/recover 手动标记恢复
|
||
GET /api/v1/alerts/events 告警事件列表(含数据异常)
|
||
GET /api/v1/channels 通道列表
|
||
POST /api/v1/channels/{code}/test 发送测试通知
|
||
```
|
||
|
||
### 9.2 前端(Alerts.vue 升级)
|
||
|
||
1. **指标水位视图**: 指标列表 + 当前值 + 优秀/达标/预警/告警状态徽标 + 趋势
|
||
2. **阈值配置**: 多级阈值可视化编辑, 修改留痕
|
||
3. **告警事件流**: 级别/状态/恢复时间/数据异常标记
|
||
4. **通道管理**: 启停 + 测试发送
|
||
5. **通知中心**: 已有站内信角标复用; 预警类通知可点击跳转预警详情
|
||
|
||
---
|
||
|
||
## 10. 与现有模块联动
|
||
|
||
| 联动 | 说明 |
|
||
|---|---|
|
||
| 影响事件(Incident) | 高严重度告警(告警级)可一键/自动生成影响事件, 今日看板横幅已支持 |
|
||
| 管理措施(Measure) | 告警可转"管理措施"或"待办", 形成"发现→措施→回看"闭环 |
|
||
| 数据同步(sync) | 预警依赖数据同步; 同步失败本身也是监测指标(sync_health) |
|
||
| 工作台 | 待处理告警进入工作台待办, 管理端可见 |
|
||
|
||
---
|
||
|
||
## 11. 实施里程碑
|
||
|
||
| 阶段 | 内容 | 交付物 |
|
||
|---|---|---|
|
||
| M1 指标与阈值基础 | metric_registry + metric_thresholds 表; 指标求值器(复用 analytics 函数); 首批 3~5 个指标注册(含退款挽回率) | 指标列表 API + 当前值/级别 |
|
||
| M2 预警引擎 | 扩展 scan_alerts 为注册表驱动; 三级阈值比较; 当日幂等 + 恢复自愈; 数据健康检查; 定时扫描接入 | 告警事件 + 恢复通知 |
|
||
| M3 站内通知 | 告警生成/恢复 → notify() 站内信; 前端 Alerts.vue 升级(水位视图+阈值配置+事件流) | 站内信闭环 |
|
||
| M4 推送通道 | notification_channels 表 + 通道抽象 + 企业微信/钉钉/飞书群机器人 + 邮件; 测试发送; 失败降级 | 多通道推送 |
|
||
| M5 扩展 | 指标清单扩充; 连续升级/聚合; 与 Incident/Measure 联动 | 运营指标全覆盖 |
|
||
|
||
> M1~M3 是"最小闭环"(用户先只做站内信, 推送通道架构先抽象、后续接入)。
|
||
|
||
---
|
||
|
||
## 12. 风险与权衡
|
||
|
||
| 风险 | 应对 |
|
||
|---|---|
|
||
| 指标口径与看板不一致 | 指标注册表强制复用看板计算函数, 不另写 |
|
||
| 假告警(数据未同步) | 数据健康检查前置, 数据异常与业务告警分离 |
|
||
| 告警风暴/打扰 | 当日幂等 + 静默窗口 + 恢复自愈 + (后期)聚合 |
|
||
| 硬编码规则迁移成本 | 现有 4 类规则映射到注册表指标, 阈值表迁移, 保留 condition 字段兼容 |
|
||
| 阈值配置失误 | 阈值修改留痕(valid_from/valid_to + updated_by), 可回看可回滚 |
|
||
| 推送通道失败 | 发送失败降级站内信, 投递记录可查 |
|
||
| 性能 | 指标求值缓存(复用 Redis), 日终批量计算 + 快照表 |
|
||
|
||
---
|
||
|
||
## 待评审问题
|
||
|
||
1. 首批指标清单(§4.2)是否合理? 是否有遗漏的高优先级指标?
|
||
2. ~~三级阈值是否够用~~ → **已确认改为四级(优秀/达标/预警/告警)**; 是否再引入"红线"级(合规/安全等硬性底线)待定, 运营指标暂不引入
|
||
3. 告警是否需要在运营看板/今日看板顶部展示(类似影响事件横幅)?
|
||
4. M1~M3 最小闭环是否按此顺序推进?
|