1
0
Files
DiAL/openspec/specs/batch-data-queries/spec.md
lanyuanxiaoyao f7facb7232 refactor: 全面优化后端代码质量与架构
- app.ts 单体路由拆分为 routes/ + helpers + middleware + static 独立模块
- 类型去重:CheckFailure/CheckResult 以 shared/api.ts 为唯一源头,收紧 phase 联合类型
- es-toolkit 替换:isPlainObject/isNil/isEmptyObject/isEqual/isError/Semaphore/groupBy
- Bun 内置 API:Object.fromEntries 替代手写 headersToRecord
- bun:sqlite 规范:prepare() → query() 利用内置缓存,避免 N+1 查询
- 新增 getLatestChecksMap/allGetTargetStats 批量查询方法
- 新增 backend-code-quality/api-route-separation/batch-data-queries 规范
- 补充 openspec/config.yaml 后端开发规范与 DEVELOPMENT.md 后端开发指引
2026-05-12 15:15:36 +08:00

4.1 KiB
Raw Blame History

Purpose

定义 ProbeStore 的批量查询方法getLatestChecksMap、getAllTargetStats以及 getSummary 和 createTargetsResponse 的 N+1 查询优化规范。同时约定单次查询操作使用 db.query() 利用内置缓存。

Requirements

Requirement: 批量查询最新检查结果

系统 SHALL 提供 getLatestChecksMap 方法,通过单次 SQL 查询获取所有 target 的最新一次 check 结果,返回 Map 结构供调用方按 target_id 索引。

Scenario: 获取所有目标的最新检查

  • WHEN 调用 getLatestChecksMap()
  • THEN 系统 SHALL 执行子查询找到每个 target_id 的 MAX(timestamp),再 JOIN 回 check_results 获取完整行,返回 Map<number, StoredCheckResult | null>

Scenario: 目标无历史记录

  • WHEN 某 target 在 check_results 表中无任何记录
  • THEN 该 target_id 在返回的 Map 中 SHALL 不存在对应的 key

Requirement: 批量查询目标统计

系统 SHALL 提供 getAllTargetStats 方法,通过单次 SQL GROUP BY 聚合查询获取所有 target 的拨测统计totalChecks 和 availability

Scenario: 获取所有目标的聚合统计

  • WHEN 调用 getAllTargetStats()
  • THEN 系统 SHALL 执行 SELECT target_id, COUNT(*), SUM(CASE WHEN matched=1 THEN 1 ELSE 0 END) FROM check_results GROUP BY target_id,在内存中计算 availability 并返回 Map<number, { totalChecks, availability }>

Scenario: 目标无历史记录

  • WHEN 某 target 在 check_results 表中无任何记录
  • THEN 该 target_id 在返回的 Map 中 SHALL 不存在对应的 key

Scenario: availability 精度

  • WHEN 计算 availabilityupCount / totalChecks * 100
  • THEN 结果 SHALL 四舍五入保留两位小数

Requirement: summary 查询使用批量方法

getSummary 方法 SHALL 使用 getLatestChecksMap 一次性获取所有 target 的最新检查结果,而非对每个 target 逐条查询。

Scenario: 统计总览使用批量查询

  • WHEN 调用 store.getSummary()
  • THEN 系统 SHALL 调用 getLatestChecksMap() 一次获取所有最新结果,在内存中遍历统计 up/down 数量,而非循环 N 次调用 getLatestCheck()

Requirement: targets 列表使用批量方法

createTargetsResponseapp.ts 中生成 TargetStatus[] 的逻辑SHALL 使用 getLatestChecksMapgetAllTargetStats 替代逐目标查询 latest checkout、stats 和 samples。

Scenario: 目标列表使用批量查询

  • WHEN 处理 GET /api/targets 请求
  • THEN 系统 SHALL 分别调用 getLatestChecksMap()getAllTargetStats() 批量获取数据,在内存中组装 TargetStatus 数组,而非对每个 target 逐条查询数据库

Requirement: prepared statement 使用 query() 缓存

ProbeStore 中不涉及事务内复用的单次读/写操作 SHALL 使用 this.db.query() 而非 this.db.prepare(),利用 bun:sqlite 内置的 statement 缓存机制。

Scenario: insertCheckResult 使用 query

  • WHEN 写入一条检查结果
  • THEN insertCheckResult SHALL 使用 this.db.query("INSERT INTO ...").run(...) 而非 this.db.prepare("INSERT INTO ...").run(...)

Scenario: getHistory 查询使用 query

  • WHEN 查询历史记录(包括 COUNT 和分页查询)
  • THEN getHistory SHALL 使用 this.db.query(...) 而非 this.db.prepare(...)

Scenario: getTargetStats 查询使用 query

  • WHEN 查询单目标统计
  • THEN getTargetStats SHALL 使用 this.db.query(...) 而非 this.db.prepare(...)

Scenario: getTrend 查询使用 query

  • WHEN 查询趋势数据
  • THEN getTrend SHALL 使用 this.db.query(...) 而非 this.db.prepare(...)

Scenario: getRecentSamples 查询使用 query

  • WHEN 查询采样数据
  • THEN getRecentSamples SHALL 使用 this.db.query(...) 而非 this.db.prepare(...)

Scenario: syncTargets 事务保持 prepare例外

  • WHEN 同步 targets 配置(事务内多次复用 insertStmt/updateStmt/deleteStmt
  • THEN syncTargets 方法 SHALL 保持使用 this.db.prepare(),因需要在事务闭包内持有引用