- gateway:优先读落库的 rec.Urgency(精确档位,与安静时段窗口无关);仅空串才回落到既有下限推导,并打 urgencyLowerBound 标记(**只在推导值上为 true**)
- 面板三态:精确(例常/紧急(高)/紧急·安全域)|下限(一律加 ≥ 前缀)|未知;下限悬停说明写清「为什么旧行只能是下限」
- 实时帧零协议改动:Record 的 JSON 键就是 urgency ⇒ 帧自然带上;wsParse 早已防御性读取(只加一处 urgencyLowerBound 防御读)
- 契约:v0.69(directMsgPerMin 透传修复,逐字照抄附录 A)与 v0.70(精确化 + 更正 §4.10(b)「实时帧不带这两键」)一起登记
- 变异实测:旧行不标下限 ⇒ 3 条测试红(结构体对比);前端去掉 ≥ ⇒ 渲染测试红
- ⚠️ 诚实边界(§7.3 已登记):实时旧记录刷新前显示「档位未知」、刷新后显示「≥ 例常」——同一条记录两路下的已知差异
86 lines
3.9 KiB
TypeScript
86 lines
3.9 KiB
TypeScript
import type { BackthinkItem } from "../../lib/types";
|
||
|
||
/**
|
||
* 后台思考行「紧急度 / 穿透安静时段」标记的**纯逻辑**(契约 v0.70 §4.10(b))。
|
||
*
|
||
* 三种态必须画得**不一样**,否则就是把猜当事实:
|
||
* - **精确档位**:后端 `urgency` 就是该轮实际生效的档位(随记录落库,`urgencyLowerBound` 缺席);
|
||
* - **≥ 下限**:`urgencyLowerBound:true` ⇒ 该行没有落库档位(旧行 / 未走到档位判定点的行),
|
||
* 此值由安静时段窗口规则推导:`≥ 高` = 至少 high、可能更高;`≥ 例常` = 只确定不低于最低档;
|
||
* - **未知**:`urgency` 键缺席 ⇒ 存档里没有这个事实(**不是**「例常」)。
|
||
* - `quietHoursBypassed` 只在 true 时渲染「穿透安静时段」。
|
||
*/
|
||
|
||
export type UrgencyTagTone = "ok" | "warn" | "err" | "neutral" | "accent";
|
||
|
||
export interface UrgencyTag {
|
||
label: string;
|
||
tone: UrgencyTagTone;
|
||
/** 悬停说明(为什么是这个档位 / 为什么只是下限 / 为什么未知),给人看的口径。 */
|
||
hint: string;
|
||
}
|
||
|
||
/** 档位未知时的标签(键缺席 = 后端没有这个事实)。 */
|
||
export const URGENCY_UNKNOWN_LABEL = "档位未知";
|
||
|
||
/** 下限标签的前缀:推导值一律带它,保证与精确档位**一眼可辨**。 */
|
||
export const URGENCY_LOWER_BOUND_PREFIX = "≥ ";
|
||
|
||
/** 穿透安静时段的标签。 */
|
||
export const QUIET_HOURS_BYPASS_LABEL = "穿透安静时段";
|
||
|
||
/** 精确档位的标签:后端 `urgency` 词汇 → 人话。 */
|
||
const EXACT_LABEL: Record<string, string> = {
|
||
routine: "例常",
|
||
warning: "紧急(高)",
|
||
critical: "紧急·安全域",
|
||
};
|
||
|
||
/** 精确档位的色调(安全域 err / 高 warn / 其余中性)。 */
|
||
function exactTone(level: string): UrgencyTagTone {
|
||
if (level === "critical") return "err";
|
||
if (level === "warning") return "warn";
|
||
return "neutral";
|
||
}
|
||
|
||
/** 下限的悬停说明:点明它是怎么推出来的、以及为什么它**不是**精确值。 */
|
||
const LOWER_BOUND_HINT =
|
||
"这一行没有存档档位(档位列出现之前的旧记录,或从未走到档位判定点的行)⇒ 精确档位已无从得知," +
|
||
"此值是安静时段窗口规则推导出的下限:窗口内被放行 ⇒ 至少 high(可能是安全域);" +
|
||
"被扣下 ⇒ 只确定不低于最低档。不是精确档位。";
|
||
|
||
/** urgencyTag 把后端 `urgency` + `urgencyLowerBound` 映射成标签。 */
|
||
export function urgencyTag(item: Pick<BackthinkItem, "urgency" | "urgencyLowerBound">): UrgencyTag {
|
||
const level = item.urgency;
|
||
if (level === undefined || level === "") {
|
||
return {
|
||
label: URGENCY_UNKNOWN_LABEL,
|
||
tone: "neutral",
|
||
hint: "这一轮的档位在存档里不存在(该行没有落库档位,且不在安静时段窗口内 —— 窗口之外判定不看档位)——不是「例常」",
|
||
};
|
||
}
|
||
if (item.urgencyLowerBound === true) {
|
||
return {
|
||
// 未知取值原样显示(后端加了本面板不认识的档位时不猜一个更好听的词),但下限前缀照加。
|
||
label: URGENCY_LOWER_BOUND_PREFIX + (EXACT_LABEL[level] ?? level),
|
||
tone: exactTone(level),
|
||
hint: LOWER_BOUND_HINT,
|
||
};
|
||
}
|
||
const exact = EXACT_LABEL[level];
|
||
if (exact === undefined) {
|
||
return { label: level, tone: "neutral", hint: "后端给了本面板不认识的档位,原样显示" };
|
||
}
|
||
return { label: exact, tone: exactTone(level), hint: `该轮实际生效的档位(精确值):${level}` };
|
||
}
|
||
|
||
/** quietHoursBypassTag 只在后端确实标记了穿透时返回标签。 */
|
||
export function quietHoursBypassTag(item: Pick<BackthinkItem, "quietHoursBypassed">): UrgencyTag | null {
|
||
if (item.quietHoursBypassed !== true) return null;
|
||
return {
|
||
label: QUIET_HOURS_BYPASS_LABEL,
|
||
tone: "accent",
|
||
hint: "这一轮落在安静时段窗口内、但仍被放行发出(档位 ≥ high)",
|
||
};
|
||
}
|