fix(stats): 日/周/月视图请求记录表空白,滚动加载顺序错乱

首页记录表由 paintRecords(st.records) 渲染,而 /api/stats 的 period 分支
手写了一份响应 map,压根没有 records 字段 —— 只有 all 分支走 Snapshot()
才带得上。实测切到日/周/月,记录表恒为空。

两处数据源也不一致:

| 端点 | 数据源 | 排序 |
|---|---|---|
| /api/stats(all)| 内存ring(几百条)| oldest-first |
| /api/stats/records(翻页)| 审计文件(全部)| newest-first |
| /api/stats(period)| 无 | 无 |

前端靠 paintRecords 里一句 .reverse() 把 all 的 oldest-first 转成 newest-first,
翻页端点则本来就是 newest-first。首屏和翻页方向相反,滚动时新旧行会错位
拼接;且 all 分支不带 next_cursor,表永远停在第一屏。

修法:两个分支都改用 AuditPage(与翻页端点同一份代码路径),记录方向统一
由它决定,前端去掉 .reverse()。附带好处:记录深度不再受 maxRecs 限制,
"全部"视图真的能翻到底。

判据三条,变异验证:period 分支返回 nil records → 前两条判红;all 分支不
覆盖 records → "no next_cursor"判红。变异过程中我自己也犯了两次错:第一版
判据硬编码 newest-first,而AuditPage 在单文件(整块读完)与多文件(分块反向
读)下方向不同,改为断言两处方向一致;第三版想测 admin 过滤,但
newTestGateway 的 sk-test 不是 admin,改为测真正的越权边界(非admin 不能
用 ?key= 看别人的记录)。

CDP 实测:四个视图各 100 行、时间从新到旧,滚动加载 100→200 行且新末行
时间更旧(顺序衔接正确)。

Co-Authored-By: ModelRouter <noreply@modelrouter.dev>
This commit is contained in:
JianFeeeee
2026-10-03 09:50:38 +08:00
parent aa003e84d3
commit 6311913812
3 changed files with 201 additions and 3 deletions

View File

@ -2418,8 +2418,14 @@ const pnote = $("#period-note");
function paintRecords(records, keyNames) {
const el = $("#tb-recs");
if (!el) return;
recsState.keyNames = keyNames || {};
const incoming = (records || []).slice().reverse(); // API sends oldest-first
recsState.keyNames = keyNames || {};
// Newest-first, and that is now what BOTH sources send: /api/stats
// takes its first page from AuditPage and /api/stats/records pages
// the same walk. The .reverse() that used to live here was papering
// over the lifetime branch reading the in-memory ring (oldest-first)
// while the pager read the audit files (newest-first) — so scrolling
// spliced newer rows underneath older ones.
const incoming = records || [];
if (!recsState.built) {
if (!incoming.length) {