SSE Patch 增量推送架构全链路实现
- web/sse_manager.py: asyncio.Queue 连接池 + broadcast + event_stream - web/routers/sse_router.py: GET /api/events + POST /api/internal/collector-patch - weather_sources.py: 采集后 POST patch 给 web - Nginx polyweather.conf: /api/events proxy_buffering off - frontend/api/events/route.ts: Next.js SSE 代理 - frontend/hooks/use-sse-patches.ts: EventSource + useLatestPatch + 重连 - LiveTemperatureThresholdChart: mergePatchIntoHourly 增量合并 - scan-terminal-query: 接入 SSE patch 更新行数据 - 新增 ssePatchArchitecture.test.ts 架构测试
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
DASHBOARD_REFRESH_POLICY_SEC,
|
||||
} from "@/lib/refresh-policy";
|
||||
import { scanTerminalQueryPolicy } from "@/components/dashboard/scan-terminal/scan-terminal-client";
|
||||
import { __shouldPollLiveChartForTest } from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
|
||||
|
||||
function assert(condition: unknown, message: string) {
|
||||
if (!condition) throw new Error(message);
|
||||
@@ -16,7 +17,7 @@ export function runTests() {
|
||||
assert(DASHBOARD_REFRESH_POLICY_MS.marketOverview === 10 * 60_000, "market overview should refresh every 10 minutes");
|
||||
assert(DASHBOARD_REFRESH_POLICY_MS.model === 30 * 60_000, "DEB and multi-model data should refresh every 30 minutes");
|
||||
assert(DASHBOARD_REFRESH_POLICY_SEC.metar === 5 * 60, "METAR polling should be 5 minutes");
|
||||
assert(scanTerminalQueryPolicy.autoRefreshMs === DASHBOARD_REFRESH_POLICY_MS.scanRows, "scan terminal auto refresh should use the shared row cadence");
|
||||
assert(scanTerminalQueryPolicy.autoRefreshMs === null, "scan terminal auto refresh should be disabled after SSE patch migration");
|
||||
|
||||
const projectRoot = process.cwd();
|
||||
const querySource = fs.readFileSync(
|
||||
@@ -29,8 +30,9 @@ export function runTests() {
|
||||
);
|
||||
|
||||
assert(
|
||||
querySource.includes("DASHBOARD_REFRESH_POLICY_MS.scanRows"),
|
||||
"scan list local cache should use the shared 5-minute row cadence",
|
||||
querySource.includes("useSsePatchVersion") &&
|
||||
!querySource.includes("window.setInterval"),
|
||||
"scan list should subscribe to SSE patch state instead of running a 5-minute interval",
|
||||
);
|
||||
assert(
|
||||
chartSource.includes("DASHBOARD_REFRESH_POLICY_MS.metar") &&
|
||||
@@ -38,8 +40,23 @@ export function runTests() {
|
||||
"selected city detail chart cache should align with 5-minute scan/metar cadence",
|
||||
);
|
||||
assert(
|
||||
chartSource.includes("setInterval(poll, 60_000)"),
|
||||
"selected city chart should poll live temperature and runway curves every 60 seconds",
|
||||
chartSource.includes("useLatestPatch") &&
|
||||
chartSource.includes("latestPatch") &&
|
||||
chartSource.includes("2 * 60_000"),
|
||||
"selected city chart should consume SSE patches and use a 2-minute no-patch fallback",
|
||||
);
|
||||
assert(
|
||||
chartSource.includes("fetchHourlyForecastForCity(city, { ignoreCache: true })") &&
|
||||
chartSource.includes("setHourly(data)"),
|
||||
"visible chart fallback must refresh the full city detail payload when SSE patches stop",
|
||||
);
|
||||
assert(
|
||||
__shouldPollLiveChartForTest({ city: "shanghai", compact: true, isActive: false, isMaximized: false }) === true,
|
||||
"compact grid slots are visible charts and should run the no-patch fallback guard",
|
||||
);
|
||||
assert(
|
||||
__shouldPollLiveChartForTest({ city: "shanghai", compact: false, isActive: false, isMaximized: false }) === false,
|
||||
"inactive non-compact charts should not run live polling",
|
||||
);
|
||||
assert(
|
||||
chartSource.includes("_hourlyRequestCache") &&
|
||||
|
||||
Reference in New Issue
Block a user