1
0

feat: 重构 Dashboard 为卡片式分组布局

表格布局替换为按分组展示的卡片式布局,新增 group 字段配置和 TargetBoard/TargetCard 等组件。模态框详情页支持时间范围筛选和分页,SummaryCards 减为 3 个。API 端点变更:trend/history 改用 from/to 参数,history 支持分页。recentSampleCount 硬编码为 30。
This commit is contained in:
2026-05-11 08:54:21 +08:00
parent b8810f1182
commit 548b44d28e
44 changed files with 1676 additions and 557 deletions

View File

@@ -0,0 +1,63 @@
## Purpose
定义 Dashboard 的卡片式分组布局:按分组展示目标卡片、响应式网格、卡片内容结构和交互行为。
## Requirements
### Requirement: 分组卡片布局
Dashboard SHALL 按分组展示所有拨测目标,每个分组包含带统计的分组标题和固定宽度的卡片网格。
#### Scenario: 按分组展示目标
- **WHEN** 用户打开 Dashboard 页面
- **THEN** 页面 SHALL 按分组展示目标卡片,"默认分组" 排在最上面,其余分组按 YAML 配置顺序排列
#### Scenario: 分组标题带统计
- **WHEN** 页面渲染某个分组
- **THEN** 分组标题 SHALL 显示分组名称、该分组内目标总数、正常数和异常数,格式为 `分组名 (N个, X UP / Y DOWN)`
#### Scenario: "default" 分组显示名称
- **WHEN** 分组名称为 "default"
- **THEN** 分组标题 SHALL 显示 "默认分组"
### Requirement: 响应式卡片网格
Dashboard SHALL 使用固定宽度的卡片配合响应式网格布局。
#### Scenario: 卡片固定宽度
- **WHEN** 页面渲染卡片
- **THEN** 每个卡片 SHALL 固定宽度 280px
#### Scenario: 响应式列数
- **WHEN** 视口宽度变化
- **THEN** 卡片网格 SHALL 自动调整列数,使用 CSS Grid auto-fill 适配可用空间
### Requirement: 目标卡片内容
每个目标卡片 SHALL 展示目标名称、当前状态、类型标签、状态条和迷你耗时趋势线。
#### Scenario: 卡片第一行内容
- **WHEN** 卡片渲染
- **THEN** 卡片第一行 SHALL 展示状态指示圆点UP 绿色 / DOWN 红色、目标名称和类型标签HTTP / Command
#### Scenario: 卡片状态指示圆点
- **WHEN** 目标最近一次拨测 success=true 且 matched=true
- **THEN** 卡片状态圆点 SHALL 显示为绿色
- **WHEN** 目标最近一次拨测 success=false 或 matched=false
- **THEN** 卡片状态圆点 SHALL 显示为红色
#### Scenario: 卡片状态条可视化
- **WHEN** 卡片渲染且 recentSamples 数据可用
- **THEN** 卡片 SHALL 展示一条 30 方块的状态条每个采样点为一个色块UP 显示绿色(#1fbf75DOWN 显示红色(#e5484d),无数据显示灰色(#e2e8f0
#### Scenario: 卡片迷你耗时趋势线
- **WHEN** 卡片渲染且 recentSamples 中有 durationMs 数据
- **THEN** 卡片 SHALL 展示基于 recharts 的迷你折线图80x32px展示最近 30 次检查的耗时趋势
### Requirement: 卡片交互
卡片 SHALL 支持 hover 效果和点击打开模态框。
#### Scenario: 卡片 hover 效果
- **WHEN** 鼠标悬停在卡片上
- **THEN** 卡片 SHALL 显示上浮效果(阴影加深)
#### Scenario: 卡片点击打开详情
- **WHEN** 用户点击某个目标卡片
- **THEN** 系统 SHALL 打开该目标的详情模态框

View File

@@ -1,48 +1,82 @@
## Purpose
定义拨测系统的 REST API 端点:总览统计、目标列表含状态、历史记录和趋势聚合。
定义拨测系统的 REST API 端点:总览统计、目标列表含分组和结构化采样数据、带时间范围和分页的历史记录、按时间范围的趋势聚合。
## Requirements
### Requirement: 总览统计 API
系统 SHALL 提供 `GET /api/summary` 端点,返回所有目标的总体统计信息。
系统 SHALL 提供 `GET /api/summary` 端点,返回所有目标的总体统计信息(不含平均耗时)
#### Scenario: 获取总览统计
- **WHEN** 客户端请求 `GET /api/summary`
- **THEN** 系统 SHALL 返回 JSON 包含 total总目标数、up正常数、down异常数avgDurationMs所有目标平均耗时lastCheckTime最近一次检查时间
- **THEN** 系统 SHALL 返回 JSON 包含 total总目标数、up正常数、down异常数、lastCheckTime最近一次检查时间
### Requirement: 目标列表 API
系统 SHALL 提供 `GET /api/targets` 端点,返回所有 typed target 及其最新状态和统计摘要
系统 SHALL 提供 `GET /api/targets` 端点,返回所有 typed target 及其最新状态、分组信息和结构化采样数据
#### Scenario: 获取目标列表
- **WHEN** 客户端请求 `GET /api/targets`
- **THEN** 系统 SHALL 返回 JSON 数组每个元素包含目标基本信息id、name、type、target、interval、最近一次检查结果timestamp、success、matched、durationMs、statusDetail、failure统计摘要totalChecks、availability、avgDurationMs、p99DurationMs
- **THEN** 系统 SHALL 返回 JSON 数组每个元素包含目标基本信息id、name、group、type、target、interval、最近一次检查结果timestamp、success、matched、durationMs、statusDetail、failure统计摘要totalChecks、availability)和结构化采样数据 recentSamples代替原 sparkline
#### Scenario: 目标无历史记录
- **WHEN** 某目标尚未执行过任何拨测
- **THEN** 其 latestCheck 为 nullstats 中 totalChecks 为 0
- **THEN** 其 latestCheck 为 nullrecentSamples 为空数组
### Requirement: 历史记录 API
系统 SHALL 提供 `GET /api/targets/:id/history` 端点,返回指定目标的最近 N 条拨测记录。
系统 SHALL 提供 `GET /api/targets/:id/history` 端点,支持时间范围筛选和分页返回指定目标的拨测记录。
#### Scenario: 获取最近历史记录
- **WHEN** 客户端请求 `GET /api/targets/1/history?limit=20`
- **THEN** 系统 SHALL 返回最多 20 条检查记录,按时间倒序排列,且每条包含 success、matched、durationMs、statusDetail 和 failure
#### Scenario: 获取指定时间范围内的历史记录
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO&page=1&pageSize=20`
- **THEN** 系统 SHALL 返回带分页信息的历史记录,包含 items、total、page、pageSize按时间倒序排列
#### Scenario: 使用默认 limit
- **WHEN** 客户端请求 `GET /api/targets/1/history`(未指定 limit
- **THEN** 系统 SHALL 默认返回最近 20 条记录
#### Scenario: 使用默认分页参数
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO`(未指定 page 或 pageSize
- **THEN** 系统 SHALL 使用默认 page=1, pageSize=20
### Requirement: 趋势聚合 API
系统 SHALL 提供 `GET /api/targets/:id/trend` 端点,返回指定目标按小时聚合的趋势数据。
#### Scenario: from 或 to 参数缺失
- **WHEN** 客户端请求 `GET /api/targets/1/history` 未提供 from 或 to 参数
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
#### Scenario: 获取 24 小时趋势
- **WHEN** 客户端请求 `GET /api/targets/1/trend?hours=24`
- **THEN** 系统 SHALL 返回按小时分组的聚合数据,每个数据点包含 hour、avgDurationMs、availability、totalChecks
### Requirement: 趋势 API 支持时间范围
系统 SHALL 提供 `GET /api/targets/:id/trend` 端点,支持 `from``to` 查询参数指定时间范围。
#### Scenario: 使用默认时间范围
- **WHEN** 客户端请求 `GET /api/targets/1/trend`(未指定 hours
- **THEN** 系统 SHALL 默认返回最近 24 小时的趋势数据
#### 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`booleansuccess && matched
#### Scenario: recentSamples 数量
- **WHEN** 客户端请求 `GET /api/targets`
- **THEN** 每个目标的 recentSamples SHALL 最多包含 30 个元素,按时间倒序排列
#### Scenario: 目标无历史记录
- **WHEN** 某目标尚未执行过任何拨测
- **THEN** 其 recentSamples SHALL 为空数组
### Requirement: 新增共享类型
系统 SHALL 在 `src/shared/api.ts` 中定义 `RecentSample``HistoryResponse` 类型。
#### Scenario: RecentSample 类型
- **WHEN** 前后端共享 `RecentSample` 类型
- **THEN** 该类型 SHALL 包含 `timestamp: string``durationMs: number | null``up: boolean` 字段
#### Scenario: HistoryResponse 类型
- **WHEN** 前后端共享 `HistoryResponse` 类型
- **THEN** 该类型 SHALL 包含 `items: CheckResult[]``total: number``page: number``pageSize: number` 字段
### Requirement: 保留健康检查端点
系统 SHALL 保留 `GET /health` 端点,不受拨测功能影响。
@@ -58,8 +92,20 @@
- **WHEN** 客户端请求 `GET /api/targets/999/history`
- **THEN** 系统 SHALL 返回 404 状态码和错误信息
#### Scenario: 无效的 limit 参数
- **WHEN** 客户端请求 `GET /api/targets/1/history?limit=abc`
#### Scenario: 无效的 from/to 参数
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=invalid`
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
#### Scenario: 无效的分页参数
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO&page=abc`
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
#### Scenario: from 或 to 参数缺失
- **WHEN** 客户端请求 `GET /api/targets/1/trend` 未提供 from 或 to 参数
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
#### Scenario: 无效的目标 ID
- **WHEN** 客户端请求 `GET /api/targets/abc/history`
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
### Requirement: 失败信息 API 契约

View File

@@ -5,15 +5,15 @@
## Requirements
### Requirement: YAML 配置文件格式
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、runtime 配置、checker 默认值和 typed target 列表。target MUST 使用 `type` 字段声明 checker 类型HTTP 领域字段 MUST 放在 `http` 分组command 领域字段 MUST 放在 `command` 分组。
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、runtime 配置、checker 默认值和 typed target 列表(含可选 group 字段)。target MUST 使用 `type` 字段声明 checker 类型HTTP 领域字段 MUST 放在 `http` 分组command 领域字段 MUST 放在 `command` 分组。
#### Scenario: 完整配置文件解析
- **WHEN** 系统启动并读取包含 server、runtime、defaults、targets 的 YAML 配置文件
- **WHEN** 系统启动并读取包含 server、runtime、defaults、targets(含 group 字段)的 YAML 配置文件
- **THEN** 系统 SHALL 正确解析所有字段并用于初始化服务、调度引擎和对应 checker runner
#### Scenario: 最简 HTTP 配置文件解析
- **WHEN** 系统读取只包含一个 `type: http` target 和 `http.url` 的 YAML 配置文件(省略 server、runtime、defaults 和 expect
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段host=127.0.0.1, port=3000, dir=./data, interval=30s, timeout=10s, runtime.maxConcurrentChecks=20, http.method=GET, http.maxBodyBytes=100MB
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段host=127.0.0.1, port=3000, dir=./data, interval=30s, timeout=10s, runtime.maxConcurrentChecks=20, http.method=GET, http.maxBodyBytes=100MB, group="default"
#### Scenario: 最简 command 配置文件解析
- **WHEN** 系统读取只包含一个 `type: command` target 和 `command.exec` 的 YAML 配置文件
@@ -61,6 +61,10 @@
- **WHEN** YAML 中存在两个 name 相同的 target
- **THEN** 系统 SHALL 以错误退出,提示重复的 name
#### Scenario: group 字段类型校验
- **WHEN** YAML 中某个 target 的 `group` 字段不是字符串
- **THEN** 系统 SHALL 以错误退出并提示 group 字段类型错误
#### Scenario: interval 格式非法
- **WHEN** interval 或 timeout 值不是有效的时长格式(如 `30s``5m``500ms`
- **THEN** 系统 SHALL 以错误退出并提示格式错误

View File

@@ -1,79 +1,126 @@
## Purpose
定义拨测系统的 React 前端 Dashboard统计卡片、目标列表表格、可展开详情面板和趋势可视化。
定义拨测系统的 React 前端 Dashboard统计卡片、按分组卡片式布局、状态条和迷你趋势线可视化、目标详情模态框和时间范围筛选
## Requirements
### Requirement: 总览统计卡片
Dashboard SHALL 在页面顶部展示总览统计卡片,包含总目标数、正常数异常数平均耗时。
Dashboard SHALL 在页面顶部展示总览统计卡片,包含总目标数、正常数异常数(移除平均耗时
#### Scenario: 展示统计卡片
- **WHEN** 用户打开 Dashboard 页面
- **THEN** 页面顶部 SHALL 显示 4 个统计卡片:全部目标数、正常目标数、异常目标数、所有目标平均耗时
- **THEN** 页面顶部 SHALL 显示 3 个统计卡片:全部目标数、正常目标数、异常目标数
#### Scenario: 统计数据自动刷新
- **WHEN** 页面处于打开状态
- **THEN** 统计卡片 SHALL 每 5-10 秒自动刷新数据
### Requirement: 目标列表表格
Dashboard SHALL 展示所有 checker target 的列表表格,包含名称、类型、目标摘要、当前状态、最新耗时、最近失败原因和迷你趋势线
### Requirement: 卡片式分组布局
Dashboard SHALL 使用按分组展示的卡片式布局替代表格布局,每个分组包含带统计的分组标题和响应式卡片网格
#### Scenario: 展示目标列表
#### Scenario: 按分组渲染卡片
- **WHEN** 用户打开 Dashboard 页面
- **THEN** 页面 SHALL 显示表格每行包含目标名称、类型、目标摘要、状态指示圆点UP / DOWN、最新耗时值、最近失败原因摘要、迷你 Sparkline 趋势线
- **THEN** 页面 SHALL 按 group 字段将目标分组展示,每组一个区域,"默认分组" 排在最上面
#### Scenario: 状态指示圆点
#### Scenario: 无分组时的展示
- **WHEN** 所有目标均属于 "default" 分组
- **THEN** 页面 SHALL 显示一个 "默认分组" 区域,卡片正常展示
### Requirement: 分组标题展示
Dashboard SHALL 在每个分组区域上方显示带统计信息的分组标题。
#### Scenario: 显示分组统计
- **WHEN** 渲染分组区域
- **THEN** 分组标题 SHALL 显示格式为 `分组名 (N个, X UP / Y DOWN)` 的统计信息
#### Scenario: default 分组标题
- **WHEN** 分组名为 "default"
- **THEN** 标题 SHALL 显示 "默认分组"
### Requirement: 响应式卡片网格
Dashboard SHALL 使用固定宽度的卡片配合响应式网格布局。
#### Scenario: 卡片固定宽度
- **WHEN** 页面渲染卡片
- **THEN** 每个卡片 SHALL 固定宽度 280px
#### Scenario: 响应式列数
- **WHEN** 视口宽度变化
- **THEN** 卡片网格 SHALL 自动调整列数,使用 CSS Grid auto-fill 适配可用空间
### Requirement: 目标卡片内容
每个目标卡片 SHALL 展示目标名称、当前状态、类型标签、状态条和迷你耗时趋势线。
#### Scenario: 卡片第一行内容
- **WHEN** 卡片渲染
- **THEN** 卡片第一行 SHALL 展示状态指示圆点UP 绿色 / DOWN 红色、目标名称和类型标签HTTP / Command
#### Scenario: 卡片状态指示圆点
- **WHEN** 目标最近一次拨测 success=true 且 matched=true
- **THEN** 状态圆点 SHALL 显示为绿色UP
- **THEN** 卡片状态圆点 SHALL 显示为绿色
- **WHEN** 目标最近一次拨测 success=false 或 matched=false
- **THEN** 状态圆点 SHALL 显示为红色DOWN
- **THEN** 卡片状态圆点 SHALL 显示为红色
### Requirement: 可展开的目标详情面板
Dashboard SHALL 支持在目标列表中展开某行,显示该目标的详细状态、统计摘要、趋势图和最近历史记录。
#### Scenario: 卡片状态条可视化
- **WHEN** 卡片渲染且 recentSamples 数据可用
- **THEN** 卡片 SHALL 展示一条状态条每个采样点为一个色块UP 显示绿色(#1fbf75DOWN 显示红色(#e5484d),无数据显示灰色(#e2e8f0
#### Scenario: 展开目标详情
- **WHEN** 用户点击目标列表中的某一行
- **THEN** 该行下方 SHALL 展开详情面板包含可用率百分比、平均耗时、P99 耗时、24 小时耗时趋势折线图、最近 5-10 检查记录列表、领域状态详情和失败信息
#### Scenario: 卡片迷你耗时趋势线
- **WHEN** 卡片渲染且 recentSamples 中有 durationMs 数据
- **THEN** 卡片 SHALL 展示基于 recharts 的迷你折线图80x32px展示最近 30 检查的耗时趋势
#### Scenario: 收起目标详情
- **WHEN** 用户再次点击已展开的目标行
- **THEN** 详情面板 SHALL 收起
### Requirement: 卡片交互
卡片 SHALL 支持 hover 效果和点击打开模态框。
#### Scenario: 趋势图按需加载
- **WHEN** 用户展开某个目标的详情面板
- **THEN** 系统 SHALL 此时请求该目标的趋势数据,而非页面加载时预加载所有目标的趋势数据
#### Scenario: 卡片 hover 效果
- **WHEN** 鼠标悬停在卡片上
- **THEN** 卡片 SHALL 显示上浮效果(阴影加深)
### Requirement: 历史记录展示
Dashboard SHALL 在目标详情面板中展示最近的检查记录,包含时间、领域状态详情、耗时、成功/失败标记和失败信息。
#### Scenario: 卡片点击打开详情
- **WHEN** 用户点击某个目标卡片
- **THEN** 系统 SHALL 打开该目标的详情模态框
#### Scenario: 展示历史记录
- **WHEN** 用户展开目标详情面板
- **THEN** 面板 SHALL 显示最近检查记录列表每条包含时间戳、statusDetail如 HTTP 200 或 exitCode=1、耗时毫秒数、UP/DOWN 标记和 failure.message如存在
### Requirement: 目标详情模态框
Dashboard SHALL 提供模态框展示目标详情,包含时间范围筛选、多维统计图和分页检查记录列表。
### Requirement: 趋势图可视化
Dashboard SHALL 使用 recharts 库渲染趋势图,包括目标列表中的迷你 Sparkline 和详情面板中的完整折线图。
#### Scenario: 打开模态框
- **WHEN** 用户点击某个目标卡片
- **THEN** 系统 SHALL 弹出模态框,占据视口 80% 宽度,展示该目标的详情
#### Scenario: 表格行内迷你趋势线
- **WHEN** 目标列表表格渲染
- **THEN** 每行 SHALL 包含一个基于 recharts 的迷你折线图,展示最近的耗时趋势
#### Scenario: 模态框默认时间范围
- **WHEN** 模态框打开
- **THEN** 筛选器 SHALL 默认选中"最近 24 小时"
#### Scenario: 详情面板完整趋势图
- **WHEN** 用户展开目标详情面板
- **THEN** 面板 SHALL 展示基于 recharts 的完整折线图X 轴为时间小时Y 轴为平均耗时,并标注可用率
#### Scenario: 模态框布局
- **WHEN** 模态框打开
- **THEN** 模态框 SHALL 占据视口 80% 宽度,图表区在上方展示统计图,检查记录列表在下方展示
### Requirement: checker 类型展示
Dashboard SHALL 在列表和详情中明确展示 target 的 checker 类型。
#### Scenario: 快捷时间范围按钮
- **WHEN** 模态框渲染
- **THEN** 筛选栏 SHALL 显示快捷按钮1h、6h、24h、7d当前选中的按钮高亮显示
#### Scenario: 展示 HTTP 类型
- **WHEN** 目标 type 为 `http`
- **THEN** Dashboard SHALL 在类型列显示 HTTP并将目标摘要显示为 URL
#### Scenario: 点击快捷按钮
- **WHEN** 用户点击快捷按钮(如 "24h"
- **THEN** 筛选器 SHALL 自动设置对应的起止时间,日期选择器显示对应的时间范围,该按钮高亮
#### Scenario: 展示 command 类型
- **WHEN** 目标 type 为 `command`
- **THEN** Dashboard SHALL 在类型列显示 Command并将目标摘要显示为命令摘要
#### Scenario: 自定义日期时间选择
- **WHEN** 用户通过日期时间选择器修改起止时间(分钟精度)
- **THEN** 快捷按钮 SHALL 取消高亮,表示当前为自定义时间范围
#### Scenario: 关闭模态框
- **WHEN** 用户点击模态框关闭按钮或模态框外部区域
- **THEN** 模态框 SHALL 关闭
#### Scenario: 统计图表
- **WHEN** 模态框加载完成
- **THEN** 图表区 SHALL 展示可用率趋势折线图、耗时趋势折线图和状态分布环形图
#### Scenario: 检查记录分页
- **WHEN** 检查记录超过一页
- **THEN** 检查记录列表底部 SHALL 展示分页器
### Requirement: 页面加载与错误状态
Dashboard SHALL 正确处理加载状态和 API 错误。
Dashboard SHALL 正确处理加载状态和 API 错误,适配卡片式布局
#### Scenario: 首次加载
- **WHEN** 页面首次加载且数据尚未返回
@@ -82,3 +129,7 @@ Dashboard SHALL 正确处理加载状态和 API 错误。
#### Scenario: API 请求失败
- **WHEN** 前端轮询 API 请求失败
- **THEN** 页面 SHALL 显示错误提示,并在下一次轮询周期自动重试
#### Scenario: 模态框内部加载状态
- **WHEN** 模态框内趋势数据或历史记录正在加载
- **THEN** 对应图表或列表区域 SHALL 显示加载指示

View File

@@ -1,15 +1,15 @@
## Purpose
定义基于 SQLite 的拨测数据持久化存储targets 同步、check_results 追加写入、索引与聚合查询。
定义基于 SQLite 的拨测数据持久化存储targets 同步(含分组信息)、check_results 追加写入、结构化采样数据查询、时间范围和分页查询、索引与聚合查询。
## Requirements
### Requirement: SQLite 数据库初始化
系统 SHALL 使用 Bun 内置 `bun:sqlite` 模块在配置的数据目录下创建 SQLite 数据库文件,并以 WAL 模式运行。数据库 schema MUST 支持 typed checker target 和结构化检查结果。
系统 SHALL 使用 Bun 内置 `bun:sqlite` 模块在配置的数据目录下创建 SQLite 数据库文件,并以 WAL 模式运行。数据库 schema MUST 支持 typed checker target 和结构化检查结果targets 表 MUST 包含 `grp` 列存储分组信息
#### Scenario: 首次启动创建数据库
- **WHEN** 指定的数据目录下不存在数据库文件
- **THEN** 系统 SHALL 创建数据库文件并初始化包含 type、target、config、duration_ms、status_detail、failure 等字段的 targets 和 check_results 表
- **THEN** 系统 SHALL 创建数据库文件并初始化包含 type、target、config、grp、duration_ms、status_detail、failure 等字段的 targets 和 check_results 表
#### Scenario: 数据目录不存在
- **WHEN** 配置的数据目录路径不存在
@@ -20,15 +20,15 @@
- **THEN** 系统 SHALL 直接打开数据库,不重新建表
### Requirement: targets 表同步
系统 SHALL 在启动时将 YAML 配置中的目标列表同步到 SQLite targets 表,并持久化 target 类型、展示摘要、领域配置、调度配置expect 配置。
系统 SHALL 在启动时将 YAML 配置中的目标列表同步到 SQLite targets 表,并持久化 target 类型、展示摘要、领域配置、调度配置expect 配置和分组信息
#### Scenario: 首次同步目标
- **WHEN** 数据库为空且 YAML 中定义了 N 个 typed target
- **THEN** 系统 SHALL 将所有目标插入 targets 表,包含 name、type、target、config、interval_ms、timeout_msexpect
- **THEN** 系统 SHALL 将所有目标插入 targets 表,包含 name、type、target、config、interval_ms、timeout_msexpect 和 grp
#### Scenario: 配置变更后重新同步
- **WHEN** YAML 配置发生变更(新增、删除或修改目标)后重启
- **THEN** 系统 SHALL 根据 name 字段匹配:新增的插入、删除的移除、修改的更新
- **THEN** 系统 SHALL 根据 name 字段匹配:新增的插入、删除的移除、修改的更新(含 grp 字段)
### Requirement: check_results 表追加写入
系统 SHALL 将每次检查结果追加写入 check_results 表,不更新或删除已有记录。
@@ -48,6 +48,42 @@
- **WHEN** 查询指定 target_id 的最近 N 条记录
- **THEN** 系统 SHALL 使用索引快速定位,无需全表扫描
### Requirement: 目标列表按分组排序
系统 SHALL 保证 targets 查询结果按分组排序返回。
#### Scenario: 分组排序查询
- **WHEN** 查询所有 targets
- **THEN** 结果 SHALL 将 "default" 分组目标排在首位,其余分组按分组名称和目标插入顺序排列
### Requirement: 结构化采样数据查询
系统 SHALL 提供 `getRecentSamples` 方法替代 `getSparkline`,返回包含状态信息的结构化采样数据。
#### Scenario: 获取最近采样数据
- **WHEN** 调用 `getRecentSamples(targetId, 30)`
- **THEN** 系统 SHALL 返回最多 30 条记录,每条包含 timestamp、duration_ms、success、matched
#### Scenario: 采样数据排序
- **WHEN** 获取采样数据
- **THEN** 记录 SHALL 按 timestamp 降序排列(最新在前)
### Requirement: 趋势数据时间范围查询
系统 SHALL 支持按任意时间范围查询趋势聚合数据,替代固定 hours 参数。
#### Scenario: 按时间范围查询趋势
- **WHEN** 查询指定 target 在 from 到 to 时间范围内的趋势数据
- **THEN** 系统 SHALL 返回按小时分组的聚合数据,包括每小时的 avgDurationMs、availability 和 totalChecks
### Requirement: 历史记录时间范围和分页查询
系统 SHALL 支持按时间范围筛选并分页查询历史记录。
#### Scenario: 按时间范围筛选历史记录
- **WHEN** 查询指定 target 在 from 到 to 时间范围内的历史记录
- **THEN** 系统 SHALL 返回该时间范围内的记录,按 timestamp 降序排列
#### Scenario: 分页查询历史记录
- **WHEN** 查询指定 page 和 pageSize 的历史记录
- **THEN** 系统 SHALL 返回对应页的数据和总记录数
### Requirement: 聚合查询支持
数据存储 SHALL 支持按时间段聚合查询用于计算可用率、平均耗时、P99 耗时等统计指标。

View File

@@ -0,0 +1,76 @@
## Purpose
定义目标详情模态框:时间范围筛选(快捷按钮 + 日期选择器)、多维统计图(可用率趋势、耗时趋势、状态分布环形图)和分页检查结果列表。
## Requirements
### Requirement: 目标详情模态框
Dashboard SHALL 在用户点击目标卡片后弹出模态框,展示该目标的详细统计图表和检查结果列表。
#### Scenario: 打开模态框
- **WHEN** 用户点击某个目标卡片
- **THEN** 系统 SHALL 弹出模态框,占据视口 80% 宽度,展示该目标的详情
#### Scenario: 模态框默认时间范围
- **WHEN** 模态框打开
- **THEN** 筛选器 SHALL 默认选中"最近 24 小时"
#### Scenario: 关闭模态框
- **WHEN** 用户点击模态框关闭按钮或模态框外部区域
- **THEN** 模态框 SHALL 关闭
### Requirement: 时间范围筛选
模态框 SHALL 支持通过快捷按钮和自定义日期时间选择器筛选数据的时间范围。
#### Scenario: 快捷时间范围按钮
- **WHEN** 模态框渲染
- **THEN** 筛选栏 SHALL 显示快捷按钮1h、6h、24h、7d当前选中的按钮高亮显示
#### Scenario: 点击快捷按钮
- **WHEN** 用户点击快捷按钮(如 "24h"
- **THEN** 筛选器 SHALL 自动设置对应的起止时间,日期选择器显示对应的时间范围,该按钮高亮
#### Scenario: 自定义日期时间选择
- **WHEN** 用户通过日期时间选择器修改起止时间(分钟精度)
- **THEN** 快捷按钮 SHALL 取消高亮,表示当前为自定义时间范围
#### Scenario: 筛选触发数据刷新
- **WHEN** 时间范围发生变化(快捷按钮或自定义选择)
- **THEN** 系统 SHALL 重新请求该时间范围内的趋势数据和历史记录
### Requirement: 统计图表展示
模态框图表区 SHALL 展示可用率趋势折线图、耗时趋势折线图和状态分布环形图。
#### Scenario: 可用率趋势折线图
- **WHEN** 模态框加载完成且趋势数据可用
- **THEN** 图表区 SHALL 展示可用率随时间变化的折线图Y 轴为可用率百分比
#### Scenario: 耗时趋势折线图
- **WHEN** 模态框加载完成且趋势数据可用
- **THEN** 图表区 SHALL 展示耗时随时间变化的折线图Y 轴为耗时毫秒数
#### Scenario: 状态分布环形图
- **WHEN** 模态框加载完成
- **THEN** 图表区 SHALL 展示环形图Donut Chart外圈显示 UP/DOWN 比例(绿色/红色),中间显示可用率百分比数字
### Requirement: 检查结果列表
模态框检查记录列表 SHALL 展示当前筛选时间范围内的检查结果列表,支持分页浏览。
#### Scenario: 展示检查结果
- **WHEN** 模态框加载完成且历史记录可用
- **THEN** 检查记录列表 SHALL 展示检查结果每条包含时间戳、UP/DOWN 状态标记、耗时毫秒数、statusDetail 和 failure 信息
#### Scenario: 分页导航
- **WHEN** 检查结果总数超过一页
- **THEN** 列表底部 SHALL 展示分页器,用户可点击切换页码
#### Scenario: 翻页刷新
- **WHEN** 用户点击分页器切换页码
- **THEN** 系统 SHALL 请求对应页码的历史记录数据,列表更新
### Requirement: 模态框布局
模态框 SHALL 采用自上而下布局,上方展示统计图表,下方展示检查记录列表。
#### Scenario: 自上而下渲染
- **WHEN** 模态框渲染
- **THEN** 内容区域 SHALL 分为上下两部分,上方展示统计图表,下方展示检查结果列表和分页器

View File

@@ -0,0 +1,45 @@
## Purpose
定义 target 分组能力YAML 配置中的 group 字段、后端存储、API 传递和前端分组排序。
## Requirements
### Requirement: target 分组配置
系统 SHALL 支持在每个 target 上配置可选的 `group` 字段,用于将目标归类到不同分组。未指定 `group` 的 target SHALL 归入 `"default"` 分组。
#### Scenario: 配置分组名称
- **WHEN** YAML 配置中某个 target 指定 `group: "搜索引擎"`
- **THEN** 系统 SHALL 将该 target 归类到 "搜索引擎" 分组
#### Scenario: 不配置分组
- **WHEN** YAML 配置中某个 target 未指定 `group` 字段
- **THEN** 系统 SHALL 将该 target 归类到 "default" 分组
#### Scenario: group 字段类型校验
- **WHEN** YAML 配置中某个 target 的 `group` 字段不是字符串
- **THEN** 系统 SHALL 以错误退出并提示 group 字段类型错误
### Requirement: 分组排序
系统 SHALL 保证 "default" 分组始终排在最前面,其余分组按配置文件中首次出现的顺序排列。
#### Scenario: default 分组排最前
- **WHEN** 配置中存在多个分组(包括 "default" 和自定义分组)
- **THEN** API 返回的目标列表中 "default" 分组的目标 SHALL 排在其他分组之前
#### Scenario: 自定义分组按出现顺序
- **WHEN** 配置中 "搜索引擎" 分组在 "后端服务" 分组之前首次出现
- **THEN** API 返回中 "搜索引擎" 分组 SHALL 排在 "后端服务" 分组之前
### Requirement: 分组信息 API 传递
系统 SHALL 在 API 响应中返回每个 target 的分组信息。
#### Scenario: targets 列表包含分组
- **WHEN** 客户端请求 `GET /api/targets`
- **THEN** 响应中每个目标 SHALL 包含 `group` 字段,值为该目标所属的分组名称
### Requirement: 分组存储
系统 SHALL 在数据库 targets 表中持久化每个 target 的分组信息。
#### Scenario: 持久化分组信息
- **WHEN** 系统同步 targets 到数据库
- **THEN** 每个 target 的 `grp` 列 SHALL 存储其分组名称,未配置分组的存储 `"default"`