## 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）。availability 计算精度 SHALL 与 `getTargetStats` 一致，统一使用 `Math.round(value * 100) / 100` 保留两位小数。

#### 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** 计算 availability（upCount / totalChecks * 100）
- **THEN** 结果 SHALL 使用 `Math.round(value * 100) / 100` 四舍五入保留两位小数，与 `getTargetStats` 方法一致

### Requirement: summary 查询使用批量方法
`getSummary` 方法 SHALL 使用 `getLatestChecksMap` 一次性获取所有 target 的最新检查结果，而非对每个 target 逐条查询。

#### Scenario: 统计总览使用批量查询
- **WHEN** 调用 `store.getSummary()`
- **THEN** 系统 SHALL 调用 `getLatestChecksMap()` 一次获取所有最新结果，在内存中遍历统计 up/down 数量，而非循环 N 次调用 `getLatestCheck()`

### Requirement: targets 列表使用批量方法
`handleTargets`（routes/targets.ts 中生成 TargetStatus[] 的逻辑）SHALL 使用 `getLatestChecksMap`、`getAllTargetStats` 和 `getAllRecentSamples` 替代逐目标查询，消除 N+1 查询。

#### Scenario: 目标列表使用批量查询
- **WHEN** 处理 `GET /api/targets` 请求
- **THEN** 系统 SHALL 分别调用 `getLatestChecksMap()`、`getAllTargetStats()`、`getAllRecentSamples(30)` 批量获取数据，在内存中组装 TargetStatus 数组，而非对每个 target 逐条查询数据库

#### Scenario: 目标无采样数据
- **WHEN** 某 target 在 getAllRecentSamples 返回的 Map 中不存在
- **THEN** 该 target 的 recentSamples SHALL 为空数组

### Requirement: 批量查询所有目标的最近采样数据
系统 SHALL 提供 `getAllRecentSamples(limit: number)` 方法，通过单次 SQL 查询获取所有 target 的最近 N 条采样数据，返回 `Map<number, Array<{ timestamp: string; duration_ms: number | null; matched: number }>>` 结构。

#### Scenario: 获取所有目标的最近采样
- **WHEN** 调用 `getAllRecentSamples(30)`
- **THEN** 系统 SHALL 通过单次 SQL 查询获取每个 target 最近 30 条记录，返回按 target_id 索引的 Map

#### Scenario: 目标无历史记录
- **WHEN** 某 target 在 check_results 表中无任何记录
- **THEN** 该 target_id 在返回的 Map 中 SHALL 不存在对应的 key

#### Scenario: 采样数据排序
- **WHEN** 获取采样数据
- **THEN** 每个 target 的记录 SHALL 按 timestamp 降序排列（最新在前）

### 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()`，因需要在事务闭包内持有引用
