feat(admin): 时间轴只读权威键 + 自排筛选轴 + browser 面板图标(Y2+Y3+Y4 前端一并落)
- 时间轴:页首「已过保留期」的**唯一**判据改为 retentionBoundaryReached === true(truncated 只出
「未显示」);缺席(老后端)一句话都不说;删掉旧的 truncated+retentionEnabled+翻到底 启发式
- Backthink:筛选轴接上服务端 ?trigger=(客户端筛选保留作兜底)+ 自排闸用量行(三态文案)
- plugin/nav.ts 加 browser 图标
- 真机(隔离实例,46/46):翻页不重不漏、三种「必须 false」的权威键语义、**重启后 round 不重复**
⚠️ 未验:真机上没构造出权威键 true(需真有更早轮次被清扫)——别读成「真机验过 true」
This commit is contained in:
1 parent
e0a6bca501
commit
57d0e8c543
11 files changed
+490
-102
No files matched your search
@@ -3,8 +3,11 @@
|
||||
// 为什么需要它:时间轴的四条硬口径全是**判定**而非渲染 —— ① 顺序与分页游标(`before`
|
||||
// 恒向更早走,与展示顺序无关);② **不臆造关系**(去重只认后端给的回合标识,标识缺席
|
||||
// 也不自己拼一个);③ 状态与文案必须把「无图 / 未收尾 / 完成」分开说;④ 老后端(404)
|
||||
// 与「没有回合」必须分成两件事。本项目没有前端测试运行器,因此这是无依赖的纯 node 检查
|
||||
// (与 `flowHistory.test.ts` / `flowSession.test.ts` 同一手法)。
|
||||
// 与「没有回合」必须分成两件事。⑤(v0.81 追补)**「已过保留期」只认权威键**
|
||||
// `retentionBoundaryReached === true`:`truncated` / `retentionEnabled` / 「翻到底了没」
|
||||
// 的任何组合都**不再是**判据,权威键缺席(老后端)⇒ 按最保守话术(一句也不说)。本项目
|
||||
// 没有前端测试运行器,因此这是无依赖的纯 node 检查(与 `flowHistory.test.ts` /
|
||||
// `flowSession.test.ts` 同一手法)。
|
||||
//
|
||||
// 跑法:
|
||||
//
|
||||
@@ -31,7 +34,6 @@ import {
|
||||
orderRounds,
|
||||
pageHasOlder,
|
||||
pageMayHaveOlder,
|
||||
retentionClaimable,
|
||||
retentionText,
|
||||
roundExpanded,
|
||||
roundGraphs,
|
||||
@@ -250,14 +252,24 @@ const packed = timelinePageOf({
|
||||
rounds: [r1],
|
||||
retentionHours: 168,
|
||||
truncated: true,
|
||||
retentionBoundaryReached: true,
|
||||
});
|
||||
same(packed.rounds.length, 1, "解包保留回合");
|
||||
same(packed.retentionHours, 168, "解包保留期");
|
||||
same(packed.truncated, true, "解包截断标记");
|
||||
same(packed.retentionBoundaryReached, true, "解包**权威键**(v0.81):不能丢、不能反相");
|
||||
const packedEmpty = timelinePageOf(null);
|
||||
same(packedEmpty.rounds.length, 0, "没有响应 ⇒ 空回合");
|
||||
same(packedEmpty.retentionHours, null, "没有响应 ⇒ 保留期未知(不是 0)");
|
||||
same(packedEmpty.truncated, false, "没有响应 ⇒ 不宣称「已过保留期」");
|
||||
same(packedEmpty.truncated, false, "没有响应 ⇒ 不宣称「本次不是全部」");
|
||||
same(packedEmpty.retentionBoundaryReached, null, "没有响应 ⇒ 权威键为**未知**(不冒充 false)");
|
||||
// 老后端(v0.80 出口,没有这个键):必须落成 `null`(不知道),**不许**用别的字段回推。
|
||||
const packedOld = timelinePageOf({ rounds: [r1], retentionHours: 168, truncated: true });
|
||||
same(
|
||||
packedOld.retentionBoundaryReached,
|
||||
null,
|
||||
"老后端没给权威键 ⇒ 解包成 null(不知道),**不**退回启发式"
|
||||
);
|
||||
|
||||
// ── ⑫ 真跑 api 客户端:URL 形状 + 游标**原样**回传(不换算、不拼 id) ───────
|
||||
type Call = { url: string; method: string };
|
||||
@@ -355,18 +367,33 @@ same(
|
||||
);
|
||||
same(timelineCursorOf({ nextBefore: null, rounds: [] }), null, "都没有 ⇒ 没有游标(不瞎凑)");
|
||||
|
||||
// ── ⑭ 保留期能不能用来解释缺失(`retentionClaimable` 的三支) ────────────────
|
||||
ok(retentionClaimable({ retentionHours: 168, retentionEnabled: null }), "有小时数且 >0 ⇒ 可以解释");
|
||||
ok(!retentionClaimable({ retentionHours: 0, retentionEnabled: null }), "≤0 = 配置为不回收 ⇒ 不许说过期");
|
||||
// ── ⑭ 权威键缺席 = 不知道(老后端不得拿别的字段回推) ────────────────────────
|
||||
// v0.81 起「已过保留期」的判据**只有** `retentionBoundaryReached === true`:老后端(v0.80
|
||||
// 出口)没这个键 ⇒ 解包成 `null`,「保留期开着」这件事本身**不足以**支撑那句话。
|
||||
const oldBackendPage = timelinePageOf({
|
||||
rounds: [r1],
|
||||
retentionHours: 168,
|
||||
retentionEnabled: true,
|
||||
truncated: true,
|
||||
});
|
||||
same(oldBackendPage.retentionBoundaryReached, null, "老后端缺权威键 ⇒ 状态里是 null(不知道)");
|
||||
// 喂给判定的入参:与 `timelineHints` 的真实入参同形(`canLoadMore` 由取数层算,这里取最保守的 false)。
|
||||
const oldBackendHints = timelineHints({
|
||||
truncated: oldBackendPage.truncated,
|
||||
canLoadMore: false,
|
||||
retentionBoundaryReached: oldBackendPage.retentionBoundaryReached,
|
||||
source: oldBackendPage.source,
|
||||
});
|
||||
ok(
|
||||
!retentionClaimable({ retentionHours: 168, retentionEnabled: false }),
|
||||
"后端显式说没启用 ⇒ **永远**不许说过期(哪怕小时数字段还带着 168)"
|
||||
!oldBackendHints.expired,
|
||||
"「保留期启用 + truncated」**不构成**过期的判据(v0.81 明令禁止自己拼)"
|
||||
);
|
||||
ok(retentionClaimable({ retentionHours: null, retentionEnabled: true }), "显式说启用了 ⇒ 可以解释");
|
||||
ok(!retentionClaimable({ retentionHours: null, retentionEnabled: null }), "两样都不知道 ⇒ 不说");
|
||||
ok(oldBackendHints.hidden, "缺权威键 + truncated ⇒ 只能挂「更早的轮次未显示」");
|
||||
|
||||
// ── ⑮ 页首三句话的判定(`timelineHints`):互不冒充 ─────────────────────────
|
||||
const hintBase = { truncated: true, canLoadMore: false, retentionHours: 168, retentionEnabled: true, source: null };
|
||||
// 入参只认「后端给的键」:`truncated` / `canLoadMore` / `retentionBoundaryReached` / `source`。
|
||||
// ⚠️ `retentionHours` / `retentionEnabled` **已不再**是入参(它们去掉了判据资格)。
|
||||
const hintBase = { truncated: true, canLoadMore: false, retentionBoundaryReached: null, source: null };
|
||||
/** 三支一起比(对象比较要逐字段,`same` 只比标量)。 */
|
||||
function hintsOf(h: TimelineHints): string {
|
||||
return `${h.hidden ? "hidden" : "-"}/${h.expired ? "expired" : "-"}/${h.liveOnly ? "liveOnly" : "-"}`;
|
||||
@@ -378,19 +405,34 @@ same(
|
||||
"还有更早可拉 ⇒ 只能「未显示」(**绝不**说「已过保留期」)"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints(hintBase)),
|
||||
"-/expired/-",
|
||||
"翻到底仍不是全部 + 保留期启用 ⇒ 才说「已过保留期」"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints({ ...hintBase, retentionHours: 0, retentionEnabled: false })),
|
||||
hintsOf(timelineHints({ ...hintBase, retentionBoundaryReached: false })),
|
||||
"hidden/-/-",
|
||||
"保留期未启用 ⇒ 只能说「未显示」"
|
||||
"后端明确说 false(本次不是全部但没被清扫)⇒ 说「未显示」"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints({ ...hintBase, source: "live" })),
|
||||
hintsOf(timelineHints({ ...hintBase, truncated: false, canLoadMore: false, retentionBoundaryReached: true })),
|
||||
"-/expired/-",
|
||||
"权威键 true ⇒ 才说「已过保留期」(哪怕这一页 truncated=false:已到最早一端)"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints({ ...hintBase, retentionBoundaryReached: true })),
|
||||
"-/expired/-",
|
||||
"权威键 true 时**不再**同时挂「未显示」(一句话,不是两句)"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints({ ...hintBase, source: "live", retentionBoundaryReached: false })),
|
||||
"hidden/-/liveOnly",
|
||||
"本页全来自内存(落盘源未接线)⇒ 「只拿到最近一轮」,且**不**说成过期"
|
||||
"本页全来自内存(落盘源未接线)⇒ 「只拿到最近一轮」"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints({ ...hintBase, source: "live", retentionBoundaryReached: true })),
|
||||
"hidden/-/liveOnly",
|
||||
"本页全来自内存时**压住**过期声明(本页没有落盘对照,无从复核 ⇒ 少说)"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints({ truncated: false, canLoadMore: false, retentionBoundaryReached: null, source: null })),
|
||||
"-/-/-",
|
||||
"一切未知 / 什么都没说 ⇒ 一句话都不说(最保守话术)"
|
||||
);
|
||||
|
||||
console.log(`flow timeline logic test: ${checks} checks passed`);
|
||||
Reference in new issue
Block a user