laoyang
57a0ddd5ad
日程页加载慢:O(n²) 改 O(n) + 全量范围收窄(A+B)
## 问题
用户:「首页点悬浮按钮进日程,加载得好几秒」。
实测数据:`calendar_events` 2026 条 + 328 条 rrule,按范围展开后 **28628 条**。
## 根因:渲染路径上有 O(n²)
| 函数 | 每次成本 | 调用次数 |
|---|---|---|
| `lunarOf(iso)` | 遍历 28628 条 **×2 遍**(先找节日、再找农历) | 月视图每格 + 周视图每天 + 列表每天 |
| `visibleEvents()` | **新建数组** + 遍历 28628 条 | `eventsOf()` 每次调用,列表按天调 |
月视图 42 格 × 2 遍 × 28628 ≈ **240 万次比较**,全部同步跑在 build 路径上。
## A:索引化(客户端)
- 新增 `ensureIndex()`:**一次遍历**同时建 `lunarIdx`(iso→农历文字)
和 `dayIdx`(iso→当天条目,已排序)
- `lunarOf()` 改为 O(1) 查 Map
- `eventsOf()` 改为 O(1) 查桶,返回副本(防止调用方 sort/push 改到索引)
- 缓存失效:`evtVer` 计数,`allEvents` / `books` 变化时 `++`;
`idxVer` 记录索引对应版本,不一致才重建
⚠️ **语义必须一致**:旧 `lunarOf` 是「先找 traditional/solarterm/holiday,
找不到才用 kind=lunar」。建索引时用两个 Map 分级收集、最后按"节日优先"合并,
否则会出现「初一」盖掉「中秋」。
保留了这些特例:
- `calId === 0` 的衍生条目不受"本可见性"控制
- `books` 为空时全部通过
- 桶内按 `byTime` 排序
## B:范围收窄 + 服务端上限
### 客户端
`CAL_FULL_FROM/TO` 写死的 `1970-01-01 ~ 2100-12-31`(47846 天)
改为 `calFullRange()` 动态算「今年 ±5 年」(`CAL_SPAN_YEARS = 5`)。
理由:
- 服务端要为**每一天**生成农历条目 ⇒ 130 年范围要传上万条(约 6MB)
- 实际使用只看眼前几年
- 动态计算而非写死年份,避免随时间推移失效
- 缓存键仍带 from/to ⇒ 跨年后范围平移、旧缓存自然失效
### 服务端 `app/Holiday.php`(三处都修)
| 函数 | 原问题 | 修法 |
|---|---|---|
| `lunarRange()` | `$i < 400` ⇒ 只算前 400 天,**今天的农历空** | 去上限,改年数上限 30 年 + 夹 Lunar 支持范围 |
| `traditionalRange()` | 同样 400 天 ⇒ **今年春节/中秋全丢** | 同上 |
| `solarTermRange()` | 只取首末两年 ⇒ 130 年仅 2 年节气 | 改逐年遍历 + 年数上限 |
`MAX_LUNAR_YEARS = 30` 是防御性的:客户端只要 ±5 年,
留余量足够;同时防止有人手拼 1900~2100 把服务器拖死(算 7 万天农历需数十秒)。
## 验证
### 服务端实测(部署后)
```
今天 2026-11-02 → 「廿四」 ← 修复前为空
本月农历覆盖 30/30 天
今年 11 个传统节日、24 个节气 ← 齐全
2021~2031 农历 4017 条(应 4017) ← 修复前只有 400 条
2021~2031 节气 264 条(应 264) ← 修复前只有 48 条
1970~2100 农历 10958 条,0.47 秒(按 30 年上限截断,符合设计)
```
> ⚠️ 中途发现改动**没上传到服务器**就跑了验证,一度得出"已修好"的错误结论。
> 比对 md5 才定位到。现在 `_tooling/check_cal_perf.js` 里有这项检查(7.1)。
### 静态校验
`_tooling/check_cal_perf.js`:**36 项全过**,含
- 渲染路径无全表遍历
- 索引语义与原实现一致(8 项)
- 缓存失效正确
- 范围收窄且动态
- 服务端三处上限已修
- 服务器文件 md5 与本地一致
### 编译
`build_hap debug` → `BUILD SUCCESSFUL`
### 未验证
**真机加载耗时未测**(设备当时未连接)。理论提速约 84 倍
(240 万次比较 → 28628 次遍历 + 42 次查表),但实际感知需真机确认。
## 顺带记录
服务端 `Holiday.php` 需要**单独上传**(它不在客户端仓库里)。
以后改服务端文件,验证前必须先比对服务器 md5,否则会拿旧代码验出假结果。
2026-11-02 08:51:28 +08:00
..
2026-11-02 08:51:28 +08:00
2026-10-11 22:07:48 +08:00
2026-10-11 22:07:48 +08:00
2026-10-11 22:07:48 +08:00
2026-10-11 22:07:48 +08:00