跳转到主要内容
本页属于设计哲学指南,该页索引全部主题。部署拓扑、目录结构与 CI/CD 请参阅 ARCHITECTURE.md。

实时系统

AIS 中继背压架构

AIS 船舶追踪中继维护到 AISStream.io 的持久 WebSocket 连接,在高峰海运流量时每秒可投递数百条位置报告。如果没有流控,慢消费者(例如网络状况差的客户端)会导致中继消息队列的无界内存增长。 中继实现了三水位背压系统: 此外,中继将追踪船舶总数上限设为 20,000 个位置(每个 MMSI 的最新位置)。辅助密度单元系统将位置聚合到 2°×2° 地理网格单元(最多 5,000 个单元)中,在完整船舶列表超出渲染能力时提供概览可视化。 船舶历史轨迹上限为每船 30 个位置点。新位置到达时,最旧轨迹点被驱逐。这创建了”彗尾”可视化,展示近期移动方向而无无界内存增长。 中继还在前端和中继服务器之间实现 HMAC 认证,防止未授权客户端消费昂贵的 AIS 数据流。

ONNX Runtime 能力检测

浏览器端 ML 管道(嵌入、NER、情感、摘要)使用 ONNX Runtime Web 进行推理。模型执行速度因浏览器和设备及可用硬件加速而异。 系统在初始化时使用级联能力检测策略:
  1. WebGPU — 通过 navigator.gpu 存在性检测。提供 GPU 加速推理,延迟最低。Chrome 113+ 和 Edge 113+ 可用
  2. WebGL — WebGPU 不可用时的回退。通过 WebGL 计算着色器使用现有 GPU。所有现代浏览器可用
  3. WASM + SIMD — 仅 CPU 回退。探测 SharedArrayBuffer 和 WASM SIMD 可用性。SIMD 对向量操作比普通 WASM 快约 2–4 倍
deviceMemory API 守卫在低内存设备(RAM <4GB 的手机)上完全排除 ML 管道,防止加载 384 维 float32 嵌入模型与地图渲染器和实时视频流并发时的内存溢出崩溃。

地图与可视化

地缘政治边界叠加

地图支持带关联元数据的类型化地缘政治边界多边形。每个边界携带一个 boundaryType 判别字段(demilitarized、ceasefire、disputed、armistice),控制渲染样式和弹窗内容。 朝鲜非军事区 — 第一个实现的边界是朝鲜非军事区,定义为源自 OpenStreetMap Way 369265305 和《朝鲜停战协定》第一条分界线的 43 点闭合环多边形。在平面地图上,它以 GeoJsonLayer 渲染,带半透明蓝色填充和带标签的工具提示。在 3D 地球仪上,它作为冲突图层下的 polygonsData 渲染。该边界有专门的帮助条目和图层开关,仅默认在 full 变体上启用。 边界系统设计为可扩展 — 额外的地缘政治边界(克什米尔实际控制线、戈兰高地、北塞浦路斯绿线)可添加到 GEOPOLITICAL_BOUNDARIES 常量并配以适当类型,将自动在两种地图引擎上渲染。

CII 等值线热力图

国家不稳定性指数可在两种地图引擎上投影为全覆盖等值线图层,根据每个国家的实时 CII 评分(0–100)以五段色阶为每个国家多边形着色: 在平面地图(deck.gl)上,GeoJsonLayer 通过 getLevel() 阈值函数将 ISO 3166-1 alpha-2 国家代码映射为固定 RGBA 值。更新由单调版本计数器(ciiScoresVersion)触发 — 图层在每次渲染传递时比较计数器,仅在其递增时重新计算填充色,避免 O(n) 数据展开。 在 3D 地球仪(globe.gl)上,CII 国家多边形与地缘政治边界合并到同一个 polygonsData 数组中。每个多边形对象中的 _kind 判别字段('boundary' | 'cii')让单个 .polygonCapColor() 回调为两种类型分发渲染逻辑。CII 多边形以 polygonAltitude: 0.002 渲染(低于冲突区轮廓使用的 0.006 高度),防止视觉 Z-fighting。 国家 GeoJSON 从共享的 getCountriesGeoJson() 函数惰性加载,首次抓取后缓存,并在 CII 图层和国家检测射线投射服务间共享。

统一图层开关目录

所有地图图层开关定义 — 图标、本地化键、回退显示标签和支持的渲染器类型 — 合并到单一共享注册表(src/config/map-layer-definitions.ts)。每个条目通过 renderers: MapRenderer[] 字段声明其支持的地图渲染器(例如,dayNight 仅平面,ciiChoropleth 平面和地球仪均支持),防止两个地图组件显示不一致的图层选项。 def() 工厂函数减少了每条目的样板代码。变体特定的图层排序(VARIANT_LAYER_ORDER)为六个仪表盘变体定义显示顺序,而无需重复定义本身。添加新地图图层只需一条注册表条目 — 平面地图和 3D 地球仪均自动从此目录派生其开关面板。 同一注册表还承载了高采用率图层的 v1 图层说明目录。每个精选说明记录用途、提供商/来源、新鲜度预期、置信度说明、限制、相关面板/操作和接地路径;不支持的图层有意回退到通用卡片,使 UI 永不暗示虚构的新鲜度或置信度值。

条件数据加载

API 调用成本高昂。系统仅对已启用图层抓取数据,减少不必要的网络流量和速率限制消耗。

图层感知加载

当图层关闭时:
  • 不对该数据源进行 API 调用
  • 不调度刷新间隔
  • WebSocket 连接关闭(AIS)
当图层开启时:
  • 立即抓取数据
  • 开始刷新间隔
  • 切换按钮上显示加载指示器

未配置服务

某些数据源需要 API 密钥(AIS 中继、Cloudflare Radar)。如果未配置凭证:
  • 图层开关完全隐藏
  • 无失败请求污染控制台
  • 用户只看到可用的图层
这在未配置完整 API 访问权限部署时防止混淆。

性能优化

仪表盘实时处理数千数据点。多种技术保持 UI 在重数据负载下仍响应。

用于分析的 Web Worker

CPU 密集操作在专用 Web Worker 中运行以避免阻塞主线程: Worker 管理器实现:
  • 惰性初始化:首次使用时生成 Worker
  • 10 秒就绪超时:Worker 初始化失败则拒绝
  • 30 秒请求超时:防止卡住操作挂起
  • 自动清理:致命错误时终止 Worker

虚拟滚动

大型列表(100+ 新闻条目)使用虚拟化渲染: 固定高度模式(VirtualList):
  • 仅渲染视口中可见条目 + 3 条目 overscan 缓冲
  • 元素池化 — 复用 DOM 节点而非创建新节点
  • 不可见占位器维持滚动位置而无需渲染所有条目
可变高度模式(WindowedList):
  • 基于分块的渲染(每块 10 条目)
  • 滚动时渲染分块,带 1 分块缓冲
  • CSS containment 用于性能隔离
这将 DOM 节点数从数千减少到约 30,显著提升滚动性能。

请求去重

短窗口内的相同请求被去重:
  • 行情报价将多个标的代码批量到单次 API 调用
  • 并发图层切换不会产生重复抓取
  • Promise.allSettled 确保一个失败请求不会阻塞其他

高效数据更新

刷新数据时:
  • 增量更新:仅变化条目触发重新渲染
  • 过期同时重新验证:抓取完成前显示旧数据
  • 增量压缩:基线存储 7 天/30 天增量,而非原始历史

跨模块集成

情报模块不孤立运行。数据在系统间流动以实现复合分析。

数据流架构

模块依赖

告警传播

当阈值被跨越时:
  1. 来源模块 生成告警(例如 CII 激增)
  2. 告警合并 与相关告警(相同国家/地区)
  3. 战略风险 接收复合告警
  4. UI 更新 标题栏徽章和面板指示器
这确保单一升级(例如乌克兰军事航班 + 抗议 + 新闻激增)作为一个连贯信号呈现,而非三个独立告警。

刷新间隔

不同数据源基于波动性和 API 约束以不同频率更新。

轮询计划

实时流

AIS 船舶追踪使用 WebSocket 实现真正实时:
  • 连接:到 Railway 中继的持久 WebSocket
  • 消息:船舶发射时的位置更新
  • 重连:自动配合指数退避(5s → 10s → 20s)

用户控制

时间范围选择器影响显示数据,不影响抓取频率: 历史筛选在客户端 — 所有数据均已抓取,仅用于显示筛选。