修复数据链路 P0 瓶颈:缓存炸弹、Context 重渲染、LGBM 循环、TTL 不匹配

P0-1 sessionStorage 限制:
- writeCityDetailCacheBundle 只保留最近 3 个城市的详情
- 避免 3-10MB JSON 序列化阻塞主线程

P0-2 Context 拆分:
- 新增 CityDetailsContext 独立管理 cityDetailsByName 变更
- 新增 useCityDetails hook,只订阅详情的组件不再因 cities/proAccess 变化重渲染
- DashboardStoreContext 保持不变,向后兼容

P0-3 扫描终端 TTL:
- SCAN_TERMINAL_PAYLOAD_TTL_SEC 30s → 120s
- 匹配 ThreadPoolExecutor(4) x 60 城的实际重算耗时

P0-4 LGBM 循环依赖:
- LGBM 预测值不再作为 DEB 输入参与权重计算
- 保留为独立参考字段 lgbm.prediction 输出给前端展示
- 消除 DEB → LGBM 训练 → DEB 的循环

新增 docs/data-architecture-review.md 完整数据链路审查报告

Tested: npx tsc --noEmit, python -m ruff check .
This commit is contained in:
2569718930@qq.com
2026-05-10 16:48:28 +08:00
parent 8cc7e9a996
commit c0bb2acf78
5 changed files with 233 additions and 16 deletions
+12 -8
View File
@@ -542,15 +542,19 @@ export const dashboardClient = {
meta: Record<string, CityCacheMeta>,
) {
if (!isClient()) return;
// Keep only the 3 most-recently-accessed cities to prevent sessionStorage bloat
const MAX_CACHED_CITIES = 3;
const allEntries = Object.entries(details).map(([cityName, detail]) => ({
cityName,
cachedAt: meta[cityName]?.cachedAt || 0,
detail,
revision: meta[cityName]?.revision || getCityRevision(detail),
}));
const topEntries = allEntries
.sort((a, b) => b.cachedAt - a.cachedAt)
.slice(0, MAX_CACHED_CITIES);
const entries = Object.fromEntries(
Object.entries(details).map(([cityName, detail]) => [
cityName,
{
cachedAt: meta[cityName]?.cachedAt || Date.now(),
detail,
revision: meta[cityName]?.revision || getCityRevision(detail),
},
]),
topEntries.map((e) => [e.cityName, { cachedAt: e.cachedAt, detail: e.detail, revision: e.revision }]),
);
window.sessionStorage.setItem(
CACHE_KEY,