feat: 前端指标体系增强 — Dashboard/Metrics API、2×4 统计区、趋势图面积+异常标记、连续状态列
- 新增 GET /api/dashboard 合并原 summary+targets 首屏接口 - 新增 GET /api/targets/:id/metrics 合并原 stats+trend 概览接口 - 后端指标纯函数:可用率、百分位、故障段分析、连续状态、UTC 小时分桶 - ProbeStore 窗口取数方法替代全量历史查询 - SummaryCards 扩展为 4 卡片(新增异常事件数)+ 数据新鲜度展示 - 表格新增「连续」列(Tag 渲染 capped 状态) - OverviewTab 重构为 2×4 Statistic 多维度布局 - TrendChart 改为延迟范围面积图 + 红色异常标记点 - 删除旧路由(summary/targets/trend)和 computeTrendStats - 同步 delta specs 到主 specs 并归档变更
This commit is contained in:
@@ -1,29 +1,88 @@
|
||||
## Purpose
|
||||
|
||||
定义拨测系统的 REST API 端点:总览统计、目标列表含分组和结构化采样数据、带时间范围和分页的历史记录、按时间范围的趋势聚合。
|
||||
定义拨测系统的 REST API 端点:Dashboard 聚合 API、单目标指标 API、带时间范围和分页的历史记录、共享类型定义和 API 错误处理。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 总览统计 API
|
||||
系统 SHALL 提供 `GET /api/summary` 端点,返回所有目标的总体统计信息(不含平均耗时)。
|
||||
### Requirement: Dashboard 聚合 API
|
||||
系统 SHALL 提供 `GET /api/dashboard` 端点,返回 Dashboard 首屏所需的总览统计和目标列表数据。
|
||||
|
||||
#### Scenario: 获取总览统计
|
||||
- **WHEN** 客户端请求 `GET /api/summary`
|
||||
- **THEN** 系统 SHALL 返回 JSON 包含 total(总目标数)、up(正常数)、down(异常数)、lastCheckTime(最近一次检查时间)
|
||||
#### Scenario: 获取 Dashboard 数据
|
||||
- **WHEN** 客户端请求 `GET /api/dashboard?window=24h&recentLimit=30`
|
||||
- **THEN** 系统 SHALL 返回 JSON 包含 summary 和 targets 字段
|
||||
|
||||
### Requirement: 目标列表 API
|
||||
系统 SHALL 提供 `GET /api/targets` 端点,返回所有 typed target 及其最新状态、分组信息和结构化采样数据。
|
||||
#### Scenario: summary 字段
|
||||
- **WHEN** Dashboard 响应包含 summary
|
||||
- **THEN** summary SHALL 包含 total(总目标数)、up(当前正常目标数)、down(当前异常目标数)、lastCheckTime(最近一次检查时间)、incidents(指定窗口内异常事件数)、window(from/to/label)字段
|
||||
|
||||
#### Scenario: 获取目标列表
|
||||
- **WHEN** 客户端请求 `GET /api/targets`
|
||||
- **THEN** 系统 SHALL 返回 JSON 数组,每个元素包含目标基本信息(id、name、group、type、target、interval)、最近一次检查结果(timestamp、matched、durationMs、statusDetail、failure)、统计摘要(totalChecks、availability)和结构化采样数据 recentSamples(代替原 sparkline)
|
||||
#### Scenario: targets 字段
|
||||
- **WHEN** Dashboard 响应包含 targets
|
||||
- **THEN** targets 数组中每个元素 SHALL 包含目标基本信息(id、name、group、type、target、interval)、latestCheck、stats、currentStreak 和 recentSamples 字段
|
||||
|
||||
#### Scenario: window 参数缺失
|
||||
- **WHEN** 客户端请求 `GET /api/dashboard` 未提供 window 参数
|
||||
- **THEN** 系统 SHALL 默认使用 window=`24h`
|
||||
|
||||
#### Scenario: window 参数语义
|
||||
- **WHEN** 系统处理 Dashboard 请求
|
||||
- **THEN** 系统 SHALL 以服务端当前时间作为 window.to,以 window 参数换算 window.from,并在响应中回显 window.from、window.to、window.label
|
||||
|
||||
#### Scenario: window 参数有效值
|
||||
- **WHEN** 客户端请求 Dashboard 端点并指定 window 参数
|
||||
- **THEN** 系统 SHALL 接受 `24h` 作为有效值;其他值 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
#### Scenario: 不支持的 window 参数
|
||||
- **WHEN** 客户端请求 `GET /api/dashboard?window=abc`
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
#### Scenario: recentLimit 参数缺失
|
||||
- **WHEN** 客户端请求 `GET /api/dashboard?window=24h` 未提供 recentLimit 参数
|
||||
- **THEN** 系统 SHALL 默认使用 recentLimit=30
|
||||
|
||||
#### Scenario: 不支持的 recentLimit 参数
|
||||
- **WHEN** 客户端请求 `GET /api/dashboard?recentLimit=0` 或超过系统上限的 recentLimit
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
#### Scenario: 目标无历史记录
|
||||
- **WHEN** 某目标尚未执行过任何拨测
|
||||
- **THEN** 其 latestCheck 为 null,recentSamples 为空数组
|
||||
- **THEN** 其 latestCheck 为 null,recentSamples 为空数组,stats.totalChecks 为 0,stats.availability 为 0,currentStreak 为 null
|
||||
|
||||
### Requirement: Dashboard 指标字段
|
||||
Dashboard API SHALL 返回基于时间窗口计算的目标统计和连续状态字段。
|
||||
|
||||
#### Scenario: 目标 stats 字段
|
||||
- **WHEN** Dashboard 响应包含目标 stats
|
||||
- **THEN** stats SHALL 包含 totalChecks、upChecks、downChecks、availability 字段,且这些字段 SHALL 基于请求 window 对应的时间范围计算
|
||||
|
||||
#### Scenario: 目标 currentStreak 字段
|
||||
- **WHEN** Dashboard 响应包含目标 currentStreak
|
||||
- **THEN** currentStreak SHALL 为 `{ up: boolean, count: number, capped?: boolean }` 或 null
|
||||
|
||||
#### Scenario: currentStreak 达到 recentLimit
|
||||
- **WHEN** 连续状态次数达到 recentLimit 上限
|
||||
- **THEN** currentStreak.capped SHALL 为 true
|
||||
|
||||
#### Scenario: recentSamples 字段
|
||||
- **WHEN** Dashboard 响应包含 recentSamples
|
||||
- **THEN** 每个 recentSamples 元素 SHALL 包含 timestamp、durationMs、up 字段,其中 up 为 boolean 且等于 matched
|
||||
|
||||
### Requirement: 单目标指标 API
|
||||
系统 SHALL 提供 `GET /api/targets/:id/metrics` 端点,返回 Drawer 概览所需的单目标统计和趋势数据。端点的详细计算规则(P95/P99、MTTR、故障分析、趋势分桶等)定义在 `target-metrics-api` 能力中。
|
||||
|
||||
#### Scenario: 指定时间范围查询指标
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/metrics?from=ISO&to=ISO&bucket=1h`
|
||||
- **THEN** 系统 SHALL 返回 targetId、window、stats、trend 字段
|
||||
|
||||
#### Scenario: from 或 to 参数缺失
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/metrics` 未提供 from 或 to 参数
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
#### Scenario: bucket 参数缺失
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/metrics?from=ISO&to=ISO` 未提供 bucket 参数
|
||||
- **THEN** 系统 SHALL 默认使用 bucket=`1h`
|
||||
|
||||
### Requirement: 历史记录 API
|
||||
系统 SHALL 提供 `GET /api/targets/:id/history` 端点,支持时间范围筛选和分页返回指定目标的拨测记录。
|
||||
系统 SHALL 保留 `GET /api/targets/:id/history` 端点,支持时间范围筛选和分页返回指定目标的拨测记录。
|
||||
|
||||
#### Scenario: 获取指定时间范围内的历史记录
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO&page=1&pageSize=20`
|
||||
@@ -37,38 +96,24 @@
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history` 未提供 from 或 to 参数
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
### Requirement: 趋势 API 支持时间范围
|
||||
系统 SHALL 提供 `GET /api/targets/:id/trend` 端点,支持 `from` 和 `to` 查询参数指定时间范围。
|
||||
|
||||
#### Scenario: 指定时间范围查询趋势
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/trend?from=ISO&to=ISO`
|
||||
- **THEN** 系统 SHALL 返回指定时间范围内按小时分组的聚合数据
|
||||
|
||||
#### Scenario: from 或 to 参数缺失
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/trend` 未提供 from 或 to 参数
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
### Requirement: 目标列表返回分组和采样数据
|
||||
`GET /api/targets` SHALL 返回每个目标的分组信息和结构化采样数据,替代原有 sparkline。
|
||||
|
||||
#### Scenario: 返回分组信息
|
||||
- **WHEN** 客户端请求 `GET /api/targets`
|
||||
- **THEN** 响应中每个目标 SHALL 包含 `group` 字段,值为该目标所属的分组名称
|
||||
|
||||
#### Scenario: 返回 recentSamples
|
||||
- **WHEN** 客户端请求 `GET /api/targets`
|
||||
- **THEN** 响应中每个目标 SHALL 包含 `recentSamples` 数组,每个元素包含 `timestamp`(ISO 8601)、`durationMs`(number | null)、`up`(boolean,matched === true)
|
||||
|
||||
#### Scenario: recentSamples 数量
|
||||
- **WHEN** 客户端请求 `GET /api/targets`
|
||||
- **THEN** 每个目标的 recentSamples SHALL 最多包含 30 个元素,按时间倒序排列
|
||||
|
||||
#### Scenario: 目标无历史记录
|
||||
- **WHEN** 某目标尚未执行过任何拨测
|
||||
- **THEN** 其 recentSamples SHALL 为空数组
|
||||
|
||||
### Requirement: 新增共享类型
|
||||
系统 SHALL 在 `src/shared/api.ts` 中定义 `CheckResult`、`RecentSample` 和 `HistoryResponse` 类型。
|
||||
系统 SHALL 在 `src/shared/api.ts` 中定义 Dashboard 和 Metrics 相关共享类型。
|
||||
|
||||
#### Scenario: DashboardResponse 类型
|
||||
- **WHEN** 前后端共享 `DashboardResponse` 类型
|
||||
- **THEN** 该类型 SHALL 包含 summary 和 targets 字段
|
||||
|
||||
#### Scenario: TargetStatus 类型
|
||||
- **WHEN** 前后端共享 `TargetStatus` 类型
|
||||
- **THEN** 该类型 SHALL 包含 stats(totalChecks、upChecks、downChecks、availability)、currentStreak 和 recentSamples 字段
|
||||
|
||||
#### Scenario: TargetMetricsResponse 类型
|
||||
- **WHEN** 前后端共享 `TargetMetricsResponse` 类型
|
||||
- **THEN** 该类型 SHALL 包含 targetId、window、stats 和 trend 字段
|
||||
|
||||
#### Scenario: TrendPoint 类型
|
||||
- **WHEN** 前后端共享 `TrendPoint` 类型
|
||||
- **THEN** 该类型 SHALL 包含 bucketStart、avgDurationMs、minDurationMs、maxDurationMs、availability、totalChecks、upChecks、downChecks 字段
|
||||
|
||||
#### Scenario: CheckResult 类型
|
||||
- **WHEN** 前后端共享 `CheckResult` 类型
|
||||
@@ -93,13 +138,17 @@
|
||||
系统 SHALL 对不存在的目标 ID、无效参数和超出范围的分页参数返回适当的 HTTP 错误响应。
|
||||
|
||||
#### Scenario: 查询不存在的目标
|
||||
- **WHEN** 客户端请求 `GET /api/targets/999/history`
|
||||
- **WHEN** 客户端请求 `GET /api/targets/999/metrics?from=ISO&to=ISO`
|
||||
- **THEN** 系统 SHALL 返回 404 状态码和错误信息
|
||||
|
||||
#### Scenario: 无效的 from/to 参数
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=invalid`
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/metrics?from=invalid&to=ISO`
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
#### Scenario: from 晚于 to
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/metrics?from=<较晚时间>&to=<较早时间>`
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息,提示 from 必须早于 to
|
||||
|
||||
#### Scenario: 无效的分页参数
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO&page=abc`
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
@@ -112,12 +161,8 @@
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO&pageSize=200`
|
||||
- **THEN** 系统 SHALL 正常返回数据
|
||||
|
||||
#### Scenario: from 或 to 参数缺失
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/trend` 未提供 from 或 to 参数
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
#### Scenario: 无效的目标 ID
|
||||
- **WHEN** 客户端请求 `GET /api/targets/abc/history`
|
||||
- **WHEN** 客户端请求 `GET /api/targets/abc/metrics?from=ISO&to=ISO`
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
### Requirement: 失败信息 API 契约
|
||||
|
||||
Reference in New Issue
Block a user