fix(admin): 时间轴分页与保留期文案按契约收紧(V2)
- 类型补上后端真在返回的 source/nextBefore/hasMore/retentionEnabled(此前类型里没有) - 新增纯函数 pageHasOlder(hasMore 权威,老后端退满页启发式)/timelineCursorOf/retentionClaimable/ timelineHints;取数体抽成可执行导出函数 ⇒ effect 本体首次被真跑(真 fetch + 真状态机 + 真 SSR) - 🔴 修一处成对契约缺陷:后端 truncated = hasMore || !storeWired,而契约/设计要求 truncated ⇒ 「已过保留期」⇒ 旧前端会对主人说假话。现改为:只有「已翻到底 + 保留期启用 + 非 live-only」 才说「已过保留期」,其余说「更早的轮次未显示」 - 3 个变异自检(去掉 canLoadMore 短路/恒可翻页/忽略 hasMore) ⚠️ 未验(如实):鉴权后的真机 HTTP 往返拿不到 owner 凭据;前端产物未部署(面板仍是旧文案)
This commit is contained in:
1 parent
ed5005307a
commit
745365c7df
8 files changed
+1047
-99
No files matched your search
@@ -29,7 +29,9 @@ import {
|
||||
mergeTimelineRounds,
|
||||
normalizeTimelineRounds,
|
||||
orderRounds,
|
||||
pageHasOlder,
|
||||
pageMayHaveOlder,
|
||||
retentionClaimable,
|
||||
retentionText,
|
||||
roundExpanded,
|
||||
roundGraphs,
|
||||
@@ -40,9 +42,12 @@ import {
|
||||
roundStatusTag,
|
||||
shortTurnId,
|
||||
timelineCursor,
|
||||
timelineCursorOf,
|
||||
timelineHints,
|
||||
timelinePageOf,
|
||||
toggleRoundExpansion,
|
||||
} from "./flowTimeline";
|
||||
import type { TimelineHints } from "./flowTimeline";
|
||||
|
||||
let checks = 0;
|
||||
|
||||
@@ -333,4 +338,59 @@ same(
|
||||
globalThis.fetch = originalFetch;
|
||||
globalThis.localStorage = originalLocalStorage;
|
||||
|
||||
// ── ⑬ 分页判据:`hasMore` 权威 / `nextBefore` 权威(契约 v0.80 §4.11.1(a)) ───
|
||||
ok(pageHasOlder(true, 2, 50), "后端说 hasMore ⇒ 可能还有(**哪怕这一页是短页**)");
|
||||
ok(!pageHasOlder(false, 50, 50), "后端说 hasMore=false ⇒ 到头了(**哪怕这一页是满页**,不要白打请求)");
|
||||
ok(pageHasOlder(null, 50, 50), "老后端没给 hasMore ⇒ 退回满页启发式");
|
||||
ok(!pageHasOlder(null, 3, 50), "老后端没给 hasMore + 短页 ⇒ 到头了");
|
||||
same(
|
||||
timelineCursorOf({ nextBefore: r1.startedAt, rounds: [r1, r2] }),
|
||||
r1.startedAt,
|
||||
"游标优先用后端的 nextBefore"
|
||||
);
|
||||
same(
|
||||
timelineCursorOf({ nextBefore: null, rounds: [r1, r2] }),
|
||||
r1.startedAt,
|
||||
"后端没给 nextBefore ⇒ 退回自己算的「最旧那一回合」(原样字符串)"
|
||||
);
|
||||
same(timelineCursorOf({ nextBefore: null, rounds: [] }), null, "都没有 ⇒ 没有游标(不瞎凑)");
|
||||
|
||||
// ── ⑭ 保留期能不能用来解释缺失(`retentionClaimable` 的三支) ────────────────
|
||||
ok(retentionClaimable({ retentionHours: 168, retentionEnabled: null }), "有小时数且 >0 ⇒ 可以解释");
|
||||
ok(!retentionClaimable({ retentionHours: 0, retentionEnabled: null }), "≤0 = 配置为不回收 ⇒ 不许说过期");
|
||||
ok(
|
||||
!retentionClaimable({ retentionHours: 168, retentionEnabled: false }),
|
||||
"后端显式说没启用 ⇒ **永远**不许说过期(哪怕小时数字段还带着 168)"
|
||||
);
|
||||
ok(retentionClaimable({ retentionHours: null, retentionEnabled: true }), "显式说启用了 ⇒ 可以解释");
|
||||
ok(!retentionClaimable({ retentionHours: null, retentionEnabled: null }), "两样都不知道 ⇒ 不说");
|
||||
|
||||
// ── ⑮ 页首三句话的判定(`timelineHints`):互不冒充 ─────────────────────────
|
||||
const hintBase = { truncated: true, canLoadMore: false, retentionHours: 168, retentionEnabled: true, source: null };
|
||||
/** 三支一起比(对象比较要逐字段,`same` 只比标量)。 */
|
||||
function hintsOf(h: TimelineHints): string {
|
||||
return `${h.hidden ? "hidden" : "-"}/${h.expired ? "expired" : "-"}/${h.liveOnly ? "liveOnly" : "-"}`;
|
||||
}
|
||||
same(hintsOf(timelineHints({ ...hintBase, truncated: false })), "-/-/-", "没说缺 ⇒ 三支都为假");
|
||||
same(
|
||||
hintsOf(timelineHints({ ...hintBase, canLoadMore: true })),
|
||||
"hidden/-/-",
|
||||
"还有更早可拉 ⇒ 只能「未显示」(**绝不**说「已过保留期」)"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints(hintBase)),
|
||||
"-/expired/-",
|
||||
"翻到底仍不是全部 + 保留期启用 ⇒ 才说「已过保留期」"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints({ ...hintBase, retentionHours: 0, retentionEnabled: false })),
|
||||
"hidden/-/-",
|
||||
"保留期未启用 ⇒ 只能说「未显示」"
|
||||
);
|
||||
same(
|
||||
hintsOf(timelineHints({ ...hintBase, source: "live" })),
|
||||
"hidden/-/liveOnly",
|
||||
"本页全来自内存(落盘源未接线)⇒ 「只拿到最近一轮」,且**不**说成过期"
|
||||
);
|
||||
|
||||
console.log(`flow timeline logic test: ${checks} checks passed`);
|
||||
Reference in new issue
Block a user