refactor: 移除 success 字段,简化为 matched 单层判定模型
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-10
|
||||
122
openspec/changes/archive/2026-05-11-card-ui-refactor/design.md
Normal file
122
openspec/changes/archive/2026-05-11-card-ui-refactor/design.md
Normal file
@@ -0,0 +1,122 @@
|
||||
## Context
|
||||
|
||||
当前 Dashboard 使用 `TargetTable` + `TargetRow` + `TargetDetail` 的表格布局,所有目标扁平排列在同一个表格中,点击行展开内联详情面板。前端组件结构为:
|
||||
|
||||
```
|
||||
App → SummaryCards(4) + TargetTable → TargetRow → TargetDetail(内联)
|
||||
```
|
||||
|
||||
后端 API 提供 `GET /api/targets` 返回含 `sparkline: number[]` 的目标列表,`GET /api/targets/:id/trend?hours=24` 返回趋势数据,`GET /api/targets/:id/history?limit=20` 返回历史记录。全局汇总含 `avgDurationMs`。
|
||||
|
||||
本次重构涉及全栈变更:YAML 配置格式、后端数据存储、API 接口、前端组件和样式。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 引入 target 分组概念,按组展示卡片,default 组排最前
|
||||
- 卡片内同时展示状态条(UP/DOWN 可视化)和迷你 sparkline(耗时趋势)
|
||||
- 模态框提供丰富的详情查看体验:多维统计图 + 带分页的检查结果列表
|
||||
- 模态框支持自定义时间范围筛选(分钟精度)和快捷时间范围按钮
|
||||
- 移除无实际意义的全局平均耗时统计
|
||||
- 统一卡片迷你可视化的采样数量为全局可配置项 `recentSampleCount`
|
||||
- 不引入新依赖,复用现有 recharts 库
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 分组折叠/展开功能
|
||||
- 分组排序自定义(固定为 default 最前,其余按 YAML 出现顺序)
|
||||
- per-target 的 sparkline 数量自定义(统一使用全局配置)
|
||||
- 模态框内的状态筛选(仅支持时间范围筛选)
|
||||
- 卡片内显示可用率数字
|
||||
- 按耗时阈值筛选
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: group 配置采用扁平字段(方案 A)
|
||||
|
||||
**选择**: 在每个 target 上加 `group?: string` 可选字段。
|
||||
|
||||
**替代方案**: 嵌套结构(`targets: [{ group: "x", items: [...] }]`)。
|
||||
|
||||
**理由**: 扁平字段是增量变更,完全向后兼容,不破坏现有 targets 数组格式。嵌套结构会改变整个配置文件的顶层结构,影响面大且无额外收益。
|
||||
|
||||
### D2: sparkline 替换为 recentSamples 结构化数据
|
||||
|
||||
**选择**: 将 `sparkline: number[]` 替换为 `recentSamples: RecentSample[]`,每个 sample 包含 `timestamp`、`durationMs`、`up`。
|
||||
|
||||
**替代方案**: 新增独立的 status-bar API。
|
||||
|
||||
**理由**: 合并为一个接口减少请求数,前端一次数据同时满足状态条和 sparkline 两种可视化。`timestamp` 的包含使得 hover tooltip 有意义。
|
||||
|
||||
### D3: recentSampleCount 固定为 30
|
||||
|
||||
**选择**: StatusBar 和 MiniSparkline 的采样数量硬编码为 30。
|
||||
|
||||
**理由**: 30 是合理的默认值,覆盖最近 30 次检查,无需暴露配置项增加复杂度。
|
||||
|
||||
### D4: 模态框时间筛选同时支持快捷按钮和自定义日期选择器
|
||||
|
||||
**选择**: 快捷按钮(1h/6h/24h/7d)与分钟精度日期选择器并存,联动设计——点击快捷按钮自动填入日期,手动修改日期则快捷按钮取消高亮。
|
||||
|
||||
**理由**: 快捷按钮覆盖绝大多数场景,日期选择器提供精确控制能力。分钟精度对于拨测监控场景足够精确。
|
||||
|
||||
### D5: trend API 改用 from/to 时间范围参数
|
||||
|
||||
**选择**: `GET /api/targets/:id/trend?from=ISO&to=ISO` 替代 `?hours=24`。
|
||||
|
||||
**理由**: 模态框支持自定义时间范围,hours 参数无法表达任意时间范围。from/to 是更通用的设计。
|
||||
|
||||
### D6: history API 新增分页支持
|
||||
|
||||
**选择**: `GET /api/targets/:id/history?from=ISO&to=ISO&page=1&pageSize=20`,返回 `{ items, total, page, pageSize }`。
|
||||
|
||||
**理由**: 自定义时间范围可能导致大量数据(如选择 7 天范围),分页避免一次性传输过多数据。
|
||||
|
||||
### D7: SummaryCards 从 4 个减为 3 个
|
||||
|
||||
**选择**: 移除"平均耗时"卡片,保留"全部/正常/异常"。
|
||||
|
||||
**理由**: 引入分组后,不同分组目标的平均耗时混合计算没有实际参考价值。具体目标的耗时信息在模态框中查看。
|
||||
|
||||
### D8: targets 表使用 grp 列名
|
||||
|
||||
**选择**: 数据库列名使用 `grp` 而非 `group`。
|
||||
|
||||
**理由**: `group` 是 SQL 关键字,使用 `grp` 避免转义问题。API 层和前端仍使用 `group` 作为字段名。
|
||||
|
||||
### D9: 卡片固定宽度 280px + CSS Grid auto-fill 响应式
|
||||
|
||||
**选择**: `grid-template-columns: repeat(auto-fill, 280px)` 实现响应式布局。
|
||||
|
||||
**替代方案**: 百分比宽度或 flex-wrap。
|
||||
|
||||
**理由**: 固定宽度保证卡片内容一致性,auto-fill 自动适应视口宽度变化,从 1 列到多列无缝适配。
|
||||
|
||||
### D10: 分组排序由后端 SQL 保证
|
||||
|
||||
**选择**: `ORDER BY CASE WHEN grp='default' THEN 0 ELSE 1 END, grp, id`。
|
||||
|
||||
**理由**: 后端排序后前端只需顺序遍历渲染,无需额外排序逻辑。分组名按 YAML 首次出现顺序(即 id 顺序)自然排序。
|
||||
|
||||
### D11: 环形图(Donut Chart)展示状态分布
|
||||
|
||||
**选择**: 模态框统计图使用 recharts 的 PieChart + 内部标签实现环形图,中间显示可用率百分比。
|
||||
|
||||
**替代方案**: 纯饼图。
|
||||
|
||||
**理由**: 环形图中间可展示关键数字(可用率 %),信息密度更高。
|
||||
|
||||
### D12: 状态条使用连续色块
|
||||
|
||||
**选择**: 方块数量固定 30 个,每个 6px 宽 2px 间距,UP 绿色 `#1fbf75`,DOWN 红色 `#e5484d`,无数据灰色 `#e2e8f0`。
|
||||
|
||||
**理由**: 类似 GitHub contribution graph 的可视化方式,直观展示最近检查状态。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [卡片信息密度] 卡片宽度仅 280px,同时放状态条和 sparkline 可能显得拥挤 → 状态条和 sparkline 各占一行,垂直堆叠,控制高度在合理范围
|
||||
- [API BREAKING 变更] sparkline → recentSamples、trend hours → from/to、history limit → page/pageSize 均为不兼容变更 → 项目未上线无需向前兼容,一次性完成
|
||||
- [targets 表 schema 变更] 新增 grp 列需要数据库 migration → SQLite ALTER TABLE ADD COLUMN 是安全操作,新列有默认值不影响已有数据
|
||||
- [模态框复杂度] 时间选择器 + 分页 + 多图表实现复杂度较高 → 拆分为独立子组件,每个组件职责单一
|
||||
- [recentSampleCount 默认值] 固定为 30,无法通过配置调整 → 合理值,30 覆盖足够长的最近检查周期
|
||||
@@ -0,0 +1,38 @@
|
||||
## Why
|
||||
|
||||
当前 Dashboard 使用表格列表展示所有拨测目标,缺乏分组组织能力,无法直观反映目标的归属关系和批量状态。表格内联展开的详情面板信息密度低、交互不流畅。需要重构为卡片式布局,引入分组概念,并通过模态框提供更丰富的详情查看体验。同时移除全局平均耗时统计(跨分组平均耗时无实际意义),并优化 API 以支持时间范围筛选和分页。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**: 将前端从表格布局重构为按分组展示的卡片式布局,每个卡片固定宽度,响应式排列
|
||||
- **BREAKING**: target 配置新增可选 `group` 字段,未指定时默认为 `"default"`,default 分组排最前
|
||||
|
||||
- **BREAKING**: 点击卡片弹出模态框替代内联展开详情,模态框左侧展示多维统计图(可用率趋势、耗时趋势、状态分布环形图),右侧展示带分页的检查结果列表
|
||||
- **BREAKING**: 模态框支持时间范围筛选,包含快捷按钮(1h/6h/24h/7d)和自定义日期时间选择器(分钟精度)
|
||||
- **BREAKING**: API 接口变更:sparkline 替换为 recentSamples(包含状态信息);trend/history 支持 `from/to` 时间范围参数;history 支持分页
|
||||
- **BREAKING**: 移除 SummaryResponse.avgDurationMs 及相关计算逻辑,SummaryCards 从 4 个变为 3 个(全部/正常/异常)
|
||||
- **BREAKING**: 移除 TargetStats.avgDurationMs 和 TargetStats.p99DurationMs,这些统计仅在模态框详情中按需展示
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `target-grouping`: target 分组能力,包括 YAML 配置的 group 字段、后端存储与 API 返回、前端按分组展示(带统计的分组标题)
|
||||
- `card-dashboard`: 卡片式 Dashboard 布局,包括分组卡片网格、卡片内状态条和迷你 sparkline 双可视化、卡片点击交互
|
||||
- `target-detail-modal`: 目标详情模态框,包括时间范围筛选器(快捷按钮 + 分钟精度日期选择器)、左侧多维统计图(可用率趋势折线、耗时趋势折线、状态分布环形图)、右侧分页检查结果列表
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `probe-config`: 新增 `targets[].group` 可选字段
|
||||
- `probe-api`: API 端点变更——summary 移除 avgDurationMs;targets 返回 group 和 recentSamples 替代 sparkline;trend 改用 from/to 参数替代 hours;history 改用 from/to + page/pageSize 并返回带分页信息的结构
|
||||
- `probe-data-store`: targets 表新增 grp 列存储分组信息;新增 getRecentSamples 方法替代 getSparkline;trend/history 查询改用时间范围参数;history 查询支持分页;移除 avgDurationMs 相关聚合
|
||||
- `probe-dashboard`: 全面重构前端组件,从表格布局改为卡片式分组布局;SummaryCards 减为 3 个;TargetTable/TargetRow/TargetDetail 替换为 TargetBoard/TargetCard/TargetDetailModal 等
|
||||
|
||||
## Impact
|
||||
|
||||
- **配置文件**: `probes.example.yaml` 需更新示例,新增 group 字段示例
|
||||
- **后端**: `types.ts`、`config-loader.ts`、`store.ts`、`app.ts` 需修改;targets 表需 schema migration
|
||||
- **共享类型**: `src/shared/api.ts` 需修改(新增 RecentSample、HistoryResponse 类型,移除/修改部分字段)
|
||||
- **前端**: 组件全面重构,新增 StatusBar、GroupHeader、StatusDonut、TimeRangePicker、Pagination 等组件;CSS 样式全面重写
|
||||
- **测试**: 后端测试需覆盖新 API 参数和返回结构,前端测试需更新
|
||||
- **依赖**: 不引入新依赖,全部使用现有 recharts 库
|
||||
@@ -0,0 +1,70 @@
|
||||
## ADDED 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 展示一条状态条,每个采样点为一个色块:UP 显示绿色(#1fbf75),DOWN 显示红色(#e5484d),无数据显示灰色(#e2e8f0)
|
||||
|
||||
#### Scenario: 卡片迷你耗时趋势线
|
||||
- **WHEN** 卡片渲染且 recentSamples 中有 durationMs 数据
|
||||
- **THEN** 卡片 SHALL 展示基于 recharts 的迷你折线图,展示最近 N 次检查的耗时趋势
|
||||
|
||||
### Requirement: 卡片交互
|
||||
卡片 SHALL 支持 hover 效果和点击打开模态框。
|
||||
|
||||
#### Scenario: 卡片 hover 效果
|
||||
- **WHEN** 鼠标悬停在卡片上
|
||||
- **THEN** 卡片 SHALL 显示上浮效果(阴影加深)
|
||||
|
||||
#### Scenario: 卡片点击打开详情
|
||||
- **WHEN** 用户点击某个目标卡片
|
||||
- **THEN** 系统 SHALL 打开该目标的详情模态框
|
||||
|
||||
### Requirement: SummaryCards 变更
|
||||
Dashboard 顶部 SHALL 展示 3 个统计卡片:全部目标数、正常数、异常数。
|
||||
|
||||
#### Scenario: 展示 3 个统计卡片
|
||||
- **WHEN** 用户打开 Dashboard 页面
|
||||
- **THEN** 页面顶部 SHALL 显示 3 个统计卡片:全部目标数、正常目标数、异常目标数
|
||||
|
||||
#### Scenario: 统计数据自动刷新
|
||||
- **WHEN** 页面处于打开状态
|
||||
- **THEN** 统计卡片 SHALL 每 5-10 秒自动刷新数据
|
||||
@@ -0,0 +1,96 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### 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,success && matched)
|
||||
|
||||
#### Scenario: recentSamples 数量
|
||||
- **WHEN** 客户端请求 `GET /api/targets`
|
||||
- **THEN** 每个目标的 recentSamples SHALL 最多包含 30 个元素,按时间倒序排列
|
||||
|
||||
#### Scenario: 目标无历史记录
|
||||
- **WHEN** 某目标尚未执行过任何拨测
|
||||
- **THEN** 其 recentSamples SHALL 为空数组
|
||||
|
||||
### Requirement: 趋势 API 支持时间范围
|
||||
`GET /api/targets/:id/trend` SHALL 支持 `from` 和 `to` 查询参数指定时间范围。
|
||||
|
||||
#### Scenario: 指定时间范围查询趋势
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/trend?from=2026-05-03T00:00:00Z&to=2026-05-10T00:00:00Z`
|
||||
- **THEN** 系统 SHALL 返回指定时间范围内按小时分组的聚合数据
|
||||
|
||||
#### Scenario: from 或 to 参数缺失
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/trend` 未提供 from 或 to 参数
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
#### Scenario: 无效的时间参数
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/trend?from=invalid`
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
### Requirement: 历史记录 API 支持时间范围和分页
|
||||
`GET /api/targets/:id/history` SHALL 支持 `from`、`to` 时间范围参数和 `page`、`pageSize` 分页参数,返回带分页信息的结构。
|
||||
|
||||
#### Scenario: 指定时间范围和分页
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO&page=1&pageSize=20`
|
||||
- **THEN** 系统 SHALL 返回 JSON 包含 `items`(检查结果数组)、`total`(满足条件的总记录数)、`page`(当前页码)、`pageSize`(每页大小)
|
||||
|
||||
#### Scenario: 使用默认分页参数
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO` 未指定 page 或 pageSize
|
||||
- **THEN** 系统 SHALL 使用默认 page=1, pageSize=20
|
||||
|
||||
#### Scenario: from 或 to 参数缺失
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history` 未提供 from 或 to 参数
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
#### Scenario: 无效的分页参数
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO&page=abc`
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
|
||||
### 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` 字段
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 总览统计 API
|
||||
系统 SHALL 提供 `GET /api/summary` 端点,返回所有目标的总体统计信息(不含平均耗时)。
|
||||
|
||||
#### Scenario: 获取总览统计
|
||||
- **WHEN** 客户端请求 `GET /api/summary`
|
||||
- **THEN** 系统 SHALL 返回 JSON 包含 total(总目标数)、up(正常数)、down(异常数)、lastCheckTime(最近一次检查时间)
|
||||
|
||||
### Requirement: 目标列表 API
|
||||
系统 SHALL 提供 `GET /api/targets` 端点,返回所有 typed target 及其最新状态、分组信息和结构化采样数据。
|
||||
|
||||
#### Scenario: 获取目标列表
|
||||
- **WHEN** 客户端请求 `GET /api/targets`
|
||||
- **THEN** 系统 SHALL 返回 JSON 数组,每个元素包含目标基本信息(id、name、group、type、target、interval)、最近一次检查结果(timestamp、success、matched、durationMs、statusDetail、failure)和结构化采样数据 recentSamples(代替原 sparkline)
|
||||
|
||||
#### Scenario: 目标无历史记录
|
||||
- **WHEN** 某目标尚未执行过任何拨测
|
||||
- **THEN** 其 latestCheck 为 null,recentSamples 为空数组
|
||||
|
||||
### Requirement: 历史记录 API
|
||||
系统 SHALL 提供 `GET /api/targets/:id/history` 端点,支持时间范围筛选和分页返回指定目标的拨测记录。
|
||||
|
||||
#### Scenario: 获取指定时间范围内的历史记录
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO&page=1&pageSize=20`
|
||||
- **THEN** 系统 SHALL 返回带分页信息的历史记录,包含 items、total、page、pageSize,按时间倒序排列
|
||||
|
||||
#### Scenario: 使用默认分页参数
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?from=ISO&to=ISO`(未指定 page 或 pageSize)
|
||||
- **THEN** 系统 SHALL 使用默认 page=1, pageSize=20
|
||||
@@ -0,0 +1,29 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: target 分组字段
|
||||
系统 SHALL 支持在每个 target 上配置可选的 `group` 字段。
|
||||
|
||||
#### Scenario: 配置分组名称
|
||||
- **WHEN** YAML 配置中某个 target 指定 `group: "搜索引擎"`
|
||||
- **THEN** 系统 SHALL 将该 group 值解析并传递给后续模块
|
||||
|
||||
#### Scenario: group 字段可选
|
||||
- **WHEN** YAML 配置中某个 target 未指定 `group` 字段
|
||||
- **THEN** 系统 SHALL 使用默认值 "default"
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: YAML 配置文件格式
|
||||
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、runtime 配置、checker 默认值和 typed target 列表(含可选 group 字段)。target MUST 使用 `type` 字段声明 checker 类型,HTTP 领域字段 MUST 放在 `http` 分组,command 领域字段 MUST 放在 `command` 分组。
|
||||
|
||||
#### Scenario: 完整配置文件解析
|
||||
- **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, group="default")
|
||||
|
||||
#### Scenario: per-target 配置覆盖全局默认值
|
||||
- **WHEN** 某个 target 指定 interval、timeout 或对应领域分组中的默认字段
|
||||
- **THEN** 该 target SHALL 使用其自身的值,不受 defaults 中对应字段影响
|
||||
@@ -0,0 +1,113 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 卡片式分组布局
|
||||
Dashboard SHALL 使用按分组展示的卡片式布局替代表格布局,每个分组包含带统计的分组标题和响应式卡片网格。
|
||||
|
||||
#### Scenario: 按分组渲染卡片
|
||||
- **WHEN** 用户打开 Dashboard 页面
|
||||
- **THEN** 页面 SHALL 按 group 字段将目标分组展示,每组一个区域,"默认分组" 排在最上面
|
||||
|
||||
#### 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 弹出目标详情模态框,展示该目标的统计图表和检查记录
|
||||
|
||||
### Requirement: 卡片状态条可视化
|
||||
Dashboard SHALL 在卡片中展示最近 N 次检查的状态条,每个采样点用色块表示 UP/DOWN 状态。
|
||||
|
||||
#### Scenario: 渲染状态条
|
||||
- **WHEN** 卡片的 recentSamples 数据可用
|
||||
- **THEN** 卡片 SHALL 展示一条由色块组成的状态条,UP 为绿色,DOWN 为红色,无数据为灰色
|
||||
|
||||
### Requirement: 卡片迷你耗时趋势线
|
||||
Dashboard SHALL 在卡片中展示基于 recentSamples 的迷你耗时折线图。
|
||||
|
||||
#### Scenario: 渲染迷你趋势线
|
||||
- **WHEN** 卡片的 recentSamples 中有 durationMs 数据
|
||||
- **THEN** 卡片 SHALL 展示基于 recharts 的迷你折线图,展示最近采样的耗时趋势
|
||||
|
||||
### Requirement: 目标详情模态框
|
||||
Dashboard SHALL 提供模态框展示目标详情,包含时间范围筛选、多维统计图和分页检查记录列表。
|
||||
|
||||
#### Scenario: 模态框布局
|
||||
- **WHEN** 模态框打开
|
||||
- **THEN** 模态框 SHALL 占据视口 80% 宽度,图表区在上方展示统计图,检查记录列表在下方展示
|
||||
|
||||
#### Scenario: 时间范围筛选
|
||||
- **WHEN** 模态框打开
|
||||
- **THEN** 筛选栏 SHALL 包含快捷按钮(1h/6h/24h/7d)和分钟精度的自定义日期时间选择器
|
||||
|
||||
#### Scenario: 统计图表
|
||||
- **WHEN** 模态框加载完成
|
||||
- **THEN** 左侧 SHALL 展示可用率趋势折线图、耗时趋势折线图和状态分布环形图
|
||||
|
||||
#### Scenario: 检查记录分页
|
||||
- **WHEN** 检查记录超过一页
|
||||
- **THEN** 右侧列表底部 SHALL 展示分页器
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 总览统计卡片
|
||||
Dashboard SHALL 在页面顶部展示总览统计卡片,包含总目标数、正常数和异常数(移除平均耗时)。
|
||||
|
||||
#### Scenario: 展示统计卡片
|
||||
- **WHEN** 用户打开 Dashboard 页面
|
||||
- **THEN** 页面顶部 SHALL 显示 3 个统计卡片:全部目标数、正常目标数、异常目标数
|
||||
|
||||
#### Scenario: 统计数据自动刷新
|
||||
- **WHEN** 页面处于打开状态
|
||||
- **THEN** 统计卡片 SHALL 每 5-10 秒自动刷新数据
|
||||
|
||||
### Requirement: 页面加载与错误状态
|
||||
Dashboard SHALL 正确处理加载状态和 API 错误,适配卡片式布局。
|
||||
|
||||
#### Scenario: 首次加载
|
||||
- **WHEN** 页面首次加载且数据尚未返回
|
||||
- **THEN** 页面 SHALL 显示加载状态指示
|
||||
|
||||
#### Scenario: API 请求失败
|
||||
- **WHEN** 前端轮询 API 请求失败
|
||||
- **THEN** 页面 SHALL 显示错误提示,并在下一次轮询周期自动重试
|
||||
|
||||
#### Scenario: 模态框内部加载状态
|
||||
- **WHEN** 模态框内趋势数据或历史记录正在加载
|
||||
- **THEN** 对应图表或列表区域 SHALL 显示加载指示
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: 目标列表表格
|
||||
**Reason**: 替换为卡片式分组布局
|
||||
**Migration**: 使用 TargetBoard + TargetGroup + CardGrid + TargetCard 替代 TargetTable + TargetRow
|
||||
|
||||
### Requirement: 可展开的目标详情面板
|
||||
**Reason**: 替换为目标详情模态框
|
||||
**Migration**: 使用 TargetDetailModal 替代内联展开的 TargetDetail
|
||||
|
||||
### Requirement: 历史记录展示
|
||||
**Reason**: 合并到目标详情模态框的需求中
|
||||
**Migration**: 历史记录在模态框右侧展示,支持时间范围筛选和分页
|
||||
|
||||
### Requirement: 趋势图可视化
|
||||
**Reason**: 合并到目标详情模态框的需求中,卡片内的迷你图独立定义
|
||||
**Migration**: 模态框内的趋势图在 target-detail-modal spec 中定义,卡片内的迷你图在 card-dashboard spec 中定义
|
||||
|
||||
### Requirement: checker 类型展示
|
||||
**Reason**: 功能保留但合并到卡片内容需求中,无需独立 requirement
|
||||
**Migration**: 类型标签在卡片的行1中展示
|
||||
@@ -0,0 +1,67 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: targets 表分组列
|
||||
系统 SHALL 在 targets 表中新增 `grp` 列存储分组信息。
|
||||
|
||||
#### Scenario: 新增 grp 列
|
||||
- **WHEN** 数据库初始化
|
||||
- **THEN** targets 表 SHALL 包含 `grp TEXT NOT NULL DEFAULT 'default'` 列
|
||||
|
||||
#### Scenario: 同步分组信息
|
||||
- **WHEN** 系统同步 targets 到数据库
|
||||
- **THEN** 每个 target 的 grp 列 SHALL 存储其 group 配置值,未配置的存储 '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 保证 targets 查询结果按分组排序返回。
|
||||
|
||||
#### Scenario: 分组排序查询
|
||||
- **WHEN** 查询所有 targets
|
||||
- **THEN** 结果 SHALL 将 "default" 分组目标排在首位,其余分组按分组名称和目标插入顺序排列
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: targets 表同步
|
||||
系统 SHALL 在启动时将 YAML 配置中的目标列表同步到 SQLite targets 表,并持久化 target 类型、展示摘要、领域配置、调度配置、expect 配置和分组信息。
|
||||
|
||||
#### Scenario: 首次同步目标
|
||||
- **WHEN** 数据库为空且 YAML 中定义了 N 个 typed target
|
||||
- **THEN** 系统 SHALL 将所有目标插入 targets 表,包含 name、type、target、config、interval_ms、timeout_ms、expect 和 grp
|
||||
|
||||
#### Scenario: 配置变更后重新同步
|
||||
- **WHEN** YAML 配置发生变更(新增、删除或修改目标)后重启
|
||||
- **THEN** 系统 SHALL 根据 name 字段匹配:新增的插入、删除的移除、修改的更新(含 grp 字段)
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: sparkline 查询
|
||||
**Reason**: 替换为结构化 getRecentSamples 方法
|
||||
**Migration**: 使用 getRecentSamples 替代 getSparkline,新方法返回更丰富的结构化数据
|
||||
@@ -0,0 +1,72 @@
|
||||
## ADDED 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 分为上下两部分,上方展示统计图表,下方展示检查结果列表和分页器
|
||||
@@ -0,0 +1,41 @@
|
||||
## ADDED 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"`
|
||||
@@ -0,0 +1,64 @@
|
||||
## 1. 后端:配置与类型
|
||||
|
||||
- [x] 1.1 types.ts: BaseTargetConfig 新增 group?: string, ResolvedTarget 新增 group: string
|
||||
- [x] 1.2 config-loader.ts: 解析 group 字段(默认 "default")
|
||||
- [x] 1.3 shared/api.ts: 新增 RecentSample 接口和 HistoryResponse 接口,移除 SummaryResponse.avgDurationMs,移除 TargetStats.avgDurationMs 和 TargetStats.p99DurationMs,TargetStatus 中 sparkline 替换为 recentSamples: RecentSample[],新增 group: string
|
||||
- [x] 1.4 编写 config-loader 的 group 解析校验测试
|
||||
|
||||
## 2. 后端:数据存储
|
||||
|
||||
- [x] 2.1 store.ts: targets 表新增 grp 列(ALTER TABLE 或重建建表语句),syncTargets 写入 grp 值
|
||||
- [x] 2.2 store.ts: 新增 getRecentSamples(targetId, limit) 方法替代 getSparkline,返回包含 timestamp/duration_ms/success/matched 的结构化数据
|
||||
- [x] 2.3 store.ts: getTrend 改用 from/to 时间范围参数替代 hours
|
||||
- [x] 2.4 store.ts: getHistory 改用 from/to 时间范围 + page/pageSize 分页参数,返回 { items, total, page, pageSize }
|
||||
- [x] 2.5 store.ts: getTargets 排序改为 ORDER BY CASE WHEN grp='default' THEN 0 ELSE 1 END, grp, id
|
||||
- [x] 2.6 store.ts: getSummary 移除 avgDurationMs 计算逻辑
|
||||
- [x] 2.7 store.ts: 移除 getSparkline 方法
|
||||
- [x] 2.8 编写 store 的新增/变更方法的完整测试
|
||||
|
||||
## 3. 后端:API 路由
|
||||
|
||||
- [x] 3.1 app.ts: createSummaryResponse 移除 avgDurationMs 字段
|
||||
- [x] 3.2 app.ts: createTargetsResponse 返回 group 和 recentSamples 替代 sparkline,移除 stats 中的 avgDurationMs 和 p99DurationMs
|
||||
- [x] 3.3 app.ts: handleTrend 改用 from/to 查询参数(替代 hours),校验参数格式
|
||||
- [x] 3.4 app.ts: handleHistory 改用 from/to + page/pageSize 参数,返回 HistoryResponse 结构(含 total)
|
||||
- [x] 3.5 app.ts: 移除 mapCheckResult 中已不需要的字段映射
|
||||
- [x] 3.6 编写 API 路由的测试,覆盖 from/to 参数校验、分页参数校验、recentSamples 返回结构
|
||||
|
||||
## 4. 前端:组件重构
|
||||
|
||||
- [x] 4.1 新增 StatusBar 组件:渲染 recentSampleCount 个色块(UP 绿/DOWN 红/无数据灰)
|
||||
- [x] 4.2 改造 SparklineChart 为 MiniSparkline:接收 RecentSample[] 数据,提取 durationMs 绘制迷你折线图
|
||||
- [x] 4.3 新增 GroupHeader 组件:显示分组名称和统计信息(分组名 (N个, X UP / Y DOWN)),default 显示"默认分组"
|
||||
- [x] 4.4 新增 TargetCard 组件:固定 280px 宽,行1 为 StatusDot + 名称 + 类型标签,行2 为 StatusBar + MiniSparkline,hover 上浮效果,点击触发回调
|
||||
- [x] 4.5 新增 CardGrid 组件:CSS Grid auto-fill 280px 响应式布局,接收 targets 数组渲染 TargetCard
|
||||
- [x] 4.6 新增 TargetGroup 组件:组合 GroupHeader + CardGrid,接收分组名和该组 targets
|
||||
- [x] 4.7 新增 TargetBoard 组件:接收 targets 数组,前端按 group 分组,顺序渲染 TargetGroup
|
||||
- [x] 4.8 新增 StatusDonut 组件:基于 recharts PieChart 实现环形图,中间显示可用率百分比,外圈 UP/DOWN 比例
|
||||
- [x] 4.9 新增 TimeRangePicker 组件:快捷按钮(1h/6h/24h/7d)+ 分钟精度日期选择器,联动逻辑
|
||||
- [x] 4.10 新增 Pagination 组件:显示页码按钮,支持翻页回调
|
||||
- [x] 4.11 新增 TargetDetailModal 组件:模态框布局(80% 视口宽),筛选栏 + 左侧图表区(40%)+ 右侧列表区(60%),组合 TrendChart/StatusDonut/Pagination
|
||||
- [x] 4.12 改造 TrendChart:适配 from/to 参数的时间范围,替代 hours
|
||||
- [x] 4.13 改造 app.tsx:SummaryCards 从 4 卡片改为 3 卡片,TargetTable 替换为 TargetBoard,模态框状态管理
|
||||
- [x] 4.14 移除 TargetTable、TargetRow、旧版 TargetDetail 组件
|
||||
|
||||
## 5. 前端:Hooks 与数据层
|
||||
|
||||
- [x] 5.1 新增 useTargetDetail hook:管理模态框状态,封装 trend + history 的并行请求逻辑
|
||||
- [x] 5.2 改造 useTrend hook:改用 from/to 参数请求 trend API
|
||||
- [x] 5.3 新增 useHistory hook:使用 from/to + page/pageSize 请求 history API,返回 HistoryResponse 结构
|
||||
|
||||
## 6. 前端:样式
|
||||
|
||||
- [x] 6.1 重写 styles.css:移除表格相关样式,新增卡片样式(280px 固定宽、圆角、阴影)、分组样式、模态框样式(backdrop + 居中 + 左右分栏)、StatusBar 样式(色块)、TimeRangePicker 样式、Pagination 样式、响应式媒体查询
|
||||
- [x] 6.2 SummaryCards grid 改为 repeat(3, 1fr)
|
||||
|
||||
## 7. 文档与配置示例
|
||||
|
||||
- [x] 7.1 更新 probes.example.yaml:新增 group 字段示例
|
||||
- [x] 7.2 更新 README.md:配置说明新增 group,API 端点变更说明,项目结构更新组件列表
|
||||
|
||||
## 8. 质量保障
|
||||
|
||||
- [x] 8.1 执行 bun run check(typecheck + lint + format:check + 单元测试),修复所有问题
|
||||
- [x] 8.2 执行 bun run verify(check + build + smoke test),确保构建产物正常运行
|
||||
Reference in New Issue
Block a user