From 10112d2f5809a0bff4f8dc4b19475f385015ad0c Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 25 May 2026 07:04:59 +0800 Subject: [PATCH] =?UTF-8?q?LiveTemperatureThresholdChart=20=E7=AC=AC?= =?UTF-8?q?=E4=BA=8C=E8=BD=AE=E4=BF=AE=E5=A4=8D=EF=BC=9AhourlyCache?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=8C=96=E3=80=81=E5=8E=BB=E9=97=AA=E7=83=81?= =?UTF-8?q?=E3=80=81=E6=95=B0=E6=8D=AE=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P0: hourlyCache 从组件体移到模块作用域+移除 setHourly(null)闪烁。P1: normObs 过滤无时间戳数据点。seriesStats 标签 15m→Δ15。buildMarketTemperatureOptions 停止从 market_question 文本暴力提取数字。 --- .../LiveTemperatureThresholdChart.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx index c7e84b01..1c7bdf8f 100644 --- a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx +++ b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx @@ -34,6 +34,8 @@ type EvidenceSeries = { // Sliding window: keep at most this many observation points (24h at 1-min ≈ 1440) const MAX_OBS_POINTS = 1440; +const HOURLY_CACHE_TTL_MS = 30 * 60 * 1000; +const _hourlyCache = new Map(); function validNumber(value: unknown): number | null { return typeof value === "number" && Number.isFinite(value) ? value : null; @@ -66,10 +68,10 @@ function formatTimestamp(ts: number): string { function normObs(points?: ObsPoint[] | null, limit = MAX_OBS_POINTS) { return (points || []) - .filter((p) => validNumber(p.temp) !== null) + .filter((p) => validNumber(p.temp) !== null && toTimestamp(p.time) !== null) .slice(-limit) - .map((p, i) => ({ - ts: toTimestamp(p.time) ?? (Date.now() - (limit - i) * 60_000), + .map((p) => ({ + ts: toTimestamp(p.time)!, value: Number(p.temp), })); } @@ -291,7 +293,6 @@ function buildMarketTemperatureOptions(row: ScanOpportunityRow | null) { [row?.target_lower, row?.target_upper, row?.target_value, row?.target_threshold] .forEach((v) => { if (validNumber(v) !== null) values.add(validNumber(v)!); }); parseTemperatureOptionsFromText(row?.target_label).forEach((x) => values.add(x)); - parseTemperatureOptionsFromText(row?.market_question).forEach((x) => values.add(x)); const sorted = [...values].sort((a, b) => a - b); if (sorted.length) return sorted; @@ -323,20 +324,16 @@ export function LiveTemperatureThresholdChart({ isEn: boolean; row: ScanOpportunityRow | null; }) { -const hourlyCache = new Map(); -const HOURLY_CACHE_TTL_MS = 30 * 60 * 1000; // 30 min - const [hourly, setHourly] = useState(null); const city = String(row?.city || "").toLowerCase().trim(); useEffect(() => { if (!city) return; - const cached = hourlyCache.get(city); + const cached = _hourlyCache.get(city); if (cached && Date.now() - cached.ts < HOURLY_CACHE_TTL_MS) { setHourly(cached.data); return; } - setHourly(null); let cancelled = false; fetch(`/api/city/${encodeURIComponent(city)}/detail?depth=panel&force_refresh=false`, { cache: "no-store", @@ -355,7 +352,7 @@ const HOURLY_CACHE_TTL_MS = 30 * 60 * 1000; // 30 min temps: json.hourly.temps || [], modelCurves: json.models_hourly?.curves || undefined, }; - hourlyCache.set(city, { ts: Date.now(), data }); + _hourlyCache.set(city, { ts: Date.now(), data }); setHourly(data); }) .catch(() => {}); @@ -414,7 +411,7 @@ const HOURLY_CACHE_TTL_MS = 30 * 60 * 1000; // 30 min
now: {temp(item.latest)} max: {temp(item.high)} - 15m: {item.delta15 === null ? "--" : `${item.delta15 >= 0 ? "+" : ""}${item.delta15.toFixed(1)}°`} + Δ15: {item.delta15 === null ? "--" : `${item.delta15 >= 0 ? "+" : ""}${item.delta15.toFixed(1)}°`}
)}