feat: build vehicle data platform and production pipeline
This commit is contained in:
231
docs/vehicle-data-platform-analysis.md
Normal file
231
docs/vehicle-data-platform-analysis.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# 车辆数据中台一期现状分析与总体方案
|
||||
|
||||
> 结论日期:2026-07-14
|
||||
> 范围:只完成现状分析和方案设计,不进入 V2 功能开发或 ECS 部署。
|
||||
> 代码依据:`/Users/lingniu/project/ai-coding/ln-bi`、`vehicle-data-platform`、`go/vehicle-gateway`,以及 `docs/architecture`、`docs/ops` 中的生产说明。
|
||||
|
||||
## 1. 结论摘要
|
||||
|
||||
当前数据接入链路已经具备可用的生产底座:GB32960、JT808、YUTONG_MQTT 经 NATS/Kafka 分流后,分别形成 Redis 当前态、TDengine RAW/位置历史、MySQL 身份/实时/日里程投影;现有平台 BFF 也已提供车辆、实时位置、轨迹点、RAW、里程、质量和运维健康等只读接口。
|
||||
|
||||
但现有前端不是可直接扩展的一期产品:生产入口加载的是约 6000 行的 `PrototypeApp.tsx`,仍混有 mock 数据和“接口缺失时展示能力边界”的原型逻辑;另一个模块化 `App.tsx` 并非当前入口。现有地图每次最多创建 500 个普通 Marker,没有聚合、MassMarks/Canvas 图层和视口查询,无法满足 1 万辆级监控。所谓告警接口实际上是质量问题接口的别名,没有规则、事件、处理记录和通知持久化。
|
||||
|
||||
建议丢弃现有原型页面的产品实现,但保留经验证的数据适配、领域工具、测试、运行时配置和部署脚手架,建立独立 V2。前端沿用 React + TypeScript + Vite,吸收 `ln-bi` 的视觉语言、壳层、鉴权思想和基础组件,但采用正式路由、查询缓存、虚拟表格和独立地图 SDK 层。后端保留现有接入链路,通过平台 BFF 和新增的告警/导出 worker 补齐业务能力,不让浏览器直接访问 Redis、TDengine 或 MySQL。
|
||||
|
||||
## 2. 当前前端技术栈与目录
|
||||
|
||||
### 2.1 `ln-bi` 参考项目
|
||||
|
||||
| 项目 | 当前实现 | 评价 |
|
||||
| --- | --- | --- |
|
||||
| 框架 | React 19、React DOM 19、TypeScript 5.8、Vite 6.2 | 可作为 V2 目标栈参考;不建议仅为对齐版本立刻升级已有平台依赖 |
|
||||
| 样式 | Tailwind CSS 4、少量 CSS 变量 | 视觉语言清晰,但业务组件 class 较分散,需要提炼 token 和组件配方 |
|
||||
| 图标/动效 | lucide-react、Motion | 可复用设计语言;监控页面动效应克制并支持 reduced motion |
|
||||
| 图表 | Recharts 3.8 | 适合常规 BI;百万点、多 Y 轴时序更推荐现平台已有 ECharts 5 |
|
||||
| 地图 | `@amap/amap-jsapi-loader` + AMap JS API 2.0 | 加载和实例生命周期可借鉴;目前只是热力图业务组件,不是通用地图 SDK |
|
||||
| API | 原生 `fetch` + JWT 注入 | 可借鉴鉴权流程;错误类型、超时、重试、取消、缓存仍不足 |
|
||||
| 服务端 | Hono、MySQL/Postgres、JWT、XLSX | 属于 `ln-bi` 自身 BFF,不应直接搬入车辆接入 Go 数据面 |
|
||||
| 路由 | pathname + hash + `replaceState` 手写 | 适合少量模块,不适合可分享查询、详情层级和权限路由 |
|
||||
| 状态 | React 本地状态/Context | 未使用全局状态库或服务端查询缓存 |
|
||||
|
||||
主要目录:
|
||||
|
||||
```text
|
||||
ln-bi/src/
|
||||
├── App.tsx # 模块注册、权限门禁、懒加载
|
||||
├── auth/ # jumpToken -> JWT、fetch token 注入
|
||||
├── components/
|
||||
│ ├── Shell.tsx # 桌面侧栏、移动底栏、模块切换、水印
|
||||
│ ├── SearchSelect.tsx
|
||||
│ ├── MultiSearchSelect.tsx
|
||||
│ └── ui/surface.tsx # PageFrame、SurfaceCard、MetricTile、状态组件
|
||||
├── modules/
|
||||
│ ├── vehicle-heatmap/ # AMap 热力图和筛选/详情面板
|
||||
│ ├── hydrogen-heatmap/
|
||||
│ ├── mileage/ # 表格、详情弹窗、XLSX 导出
|
||||
│ └── ...
|
||||
├── server/ # Hono BFF、认证、DB 查询
|
||||
├── shared/auth/roles.ts # 角色判断
|
||||
└── index.css # Tailwind、主题变量、地图控件样式
|
||||
```
|
||||
|
||||
### 2.2 当前车辆平台前端
|
||||
|
||||
目录为 `vehicle-data-platform/apps/web`,使用 React 18.3、TypeScript 5.7、Vite 6、Semi UI 2.71、ECharts 5.6。当前入口 `src/main.tsx` 加载 `PrototypeApp`,不是 `src/App.tsx`。
|
||||
|
||||
```text
|
||||
vehicle-data-platform/apps/web/src/
|
||||
├── main.tsx # 当前生产入口 -> PrototypeApp
|
||||
├── PrototypeApp.tsx # 大型原型单体,真实接口与 mock 逻辑混合
|
||||
├── App.tsx # 未作为入口的模块化旧实现
|
||||
├── api/ # 统一响应信封、类型和调用方法
|
||||
├── components/ # VehicleMap、状态标签、空状态等
|
||||
├── config/ # API/高德运行时配置
|
||||
├── domain/ # 路由、导出、车辆查询等纯函数
|
||||
├── integrations/amap.ts # AMap loader、坐标校验、逆地理编码
|
||||
├── layout/AppShell.tsx
|
||||
├── pages/ # 旧模块化页面
|
||||
├── prototype/ # mock、真实数据适配、view model、字段映射
|
||||
└── styles/ # token、全局样式、原型样式
|
||||
```
|
||||
|
||||
V2 的原则是“页面重建、能力甄别复用”:不复制 `PrototypeApp.tsx` 和 `prototype.css`;保留可独立测试的 `api`、`domain`、`integrations`、真实数据归一化逻辑及其测试,再按新接口契约重构。
|
||||
|
||||
## 3. 可复用能力清单
|
||||
|
||||
| 能力 | 来源 | 复用决策 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| 80px 深色侧栏、顶部面包屑、水印、移动底栏 | `ln-bi/components/Shell.tsx` | 设计复用、代码重构 | V2 桌面优先,应改为正式路由和可折叠侧栏 |
|
||||
| PageFrame、SurfaceCard、MetricTile、SegmentedNav | `ln-bi/components/ui/surface.tsx` | 可迁移后组件化 | 统一 token、尺寸和可访问性后复用 |
|
||||
| SearchSelect、MultiSearchSelect | `ln-bi/components` | 交互参考 | 当前仅接收字符串数组,缺少远程搜索、虚拟列表、label/value 和键盘完整性 |
|
||||
| 加载、空、错误状态 | 两项目 | 合并重构 | 建立统一 `AsyncState`,禁止页面各自拼接 |
|
||||
| JWT/jumpToken 门禁、角色判断 | `ln-bi/auth`、`shared/auth` | 复用认证协议思想 | token 仍应保存在 session;权限改为路由/操作级声明,后端必须二次校验 |
|
||||
| AMap loader、实例销毁、运行时密钥 | 两项目 | 合并为通用 SDK | 统一 loader promise、插件注册、安全代理和错误状态 |
|
||||
| 热力图数据归一化 | `ln-bi/vehicle-heatmap` | 可复用算法 | `log1p/sqrt` 强度变换适合密度层,不等于车辆状态点图层 |
|
||||
| 表格/详情弹窗/XLSX | `ln-bi/mileage` | 交互与导出格式参考 | 一期大数据导出必须后端异步,不能沿用浏览器全量 XLSX |
|
||||
| ECharts | 当前车辆平台 | 保留 | 更适合多 Y 轴、dataZoom、断点、阶梯线和大数据采样 |
|
||||
| `api/client.ts` 响应信封和 traceId | 当前车辆平台 | 扩展复用 | 增加鉴权、AbortSignal、超时、错误码、幂等键和查询缓存 |
|
||||
| 车辆 lookup、字段归一化、CSV 纯函数及测试 | 当前车辆平台 | 审核后复用 | 去掉 mock fallback,统一到 V2 DTO/领域模型 |
|
||||
| Semi UI 页面组件 | 当前车辆平台 | 暂不作为 V2 默认 | 与 `ln-bi` Tailwind 视觉体系并存会形成双设计系统;V2 启动前只选一套基础组件策略 |
|
||||
|
||||
不存在可直接复用的“通用图表组件”或“通用高性能表格组件”。`ln-bi` 的 Recharts 与页面耦合,当前平台的 ECharts 也需要建立 `TimeSeriesChart`、轴/单位/空值策略和采样契约;表格需新增服务端分页、列配置、固定列和虚拟滚动封装。
|
||||
|
||||
## 4. 高德地图当前封装方式
|
||||
|
||||
### 4.1 `ln-bi`
|
||||
|
||||
`AmapHeatmapCanvas.tsx` 动态导入 `@amap/amap-jsapi-loader`,写入 `_AMapSecurityConfig.securityJsCode`,加载 AMap 2.0 的 HeatMap、ToolBar、Scale 插件;创建白色地图和 HeatMap,点击地图回传经纬度,数据变化时调用 `setDataSet`,焦点变化时计算 bounds,卸载时销毁实例。
|
||||
|
||||
优点是 loader 官方、生命周期完整、地图与侧面板分屏清楚。限制是车辆和加氢模块各有近似实现,密钥直接下发前端,未封装 Marker/聚合/信息窗/轨迹/播放,也没有实例共享和视口事件节流。
|
||||
|
||||
### 4.2 当前车辆平台
|
||||
|
||||
`integrations/amap.ts` 自行加载 `https://webapi.amap.com/loader.js`,按插件集合缓存 Promise,支持 Scale、Geocoder、坐标合法性检查、URI Marker 链接和浏览器逆地理编码;`appConfig.ts` 从 `window.__LINGNIU_APP_CONFIG__` 或 Vite 环境变量读取 Web JS key、安全代码/代理和 API base URL。
|
||||
|
||||
`VehicleMap.tsx` 在组件内创建 Map、Marker、Polyline,重绘时清除 overlay,最多截取 500 个点,并在未配置地图时提供坐标预览。它适合验证和小规模页面,不适合一期:每点 HTML button、没有聚合、状态图层、InfoWindow 管理、车辆朝向、轨迹抽稀/播放或视口查询。
|
||||
|
||||
### 4.3 V2 建议封装
|
||||
|
||||
建立 `features/map-sdk`,分为:
|
||||
|
||||
- `AMapProvider/useAMap`:唯一 loader、插件按需加载、实例注册、resize/destroy。
|
||||
- `BaseMap`:纯地图容器,只接收中心、缩放、样式和事件。
|
||||
- `VehiclePointLayer`:小规模用 Marker,规模化优先 MarkerCluster/LabelsLayer/MassMarks 或 Canvas/WebGL 能力;详情卡只保留一个 InfoWindow。
|
||||
- `TrackLayer`:Polyline、起终点、停车/告警节点和移动标记;播放状态在业务 hook,不绑入地图实例。
|
||||
- `HeatmapLayer`、`GeocoderService`、`MapControls`:独立能力。
|
||||
- 视口查询使用 `bounds + zoom + filterHash`;移动结束后防抖请求服务器聚合结果,前端不一次拉取全量 1 万点 DOM。
|
||||
|
||||
高德 Web JS key 可由运行时配置下发,但安全密钥优先使用服务端安全代理;逆地理编码优先走现有 `/api/map/reverse-geocode`,避免泄露 REST key并便于限流/缓存。
|
||||
|
||||
## 5. 当前车辆相关接口
|
||||
|
||||
现有平台 BFF 的有效能力如下;详细缺口见 `vehicle-data-platform-api-gap.md`。
|
||||
|
||||
| 领域 | 接口 | 当前用途 |
|
||||
| --- | --- | --- |
|
||||
| 总览 | `GET /api/dashboard/summary` | 在线、今日活跃、帧数、问题数、协议/服务/链路统计 |
|
||||
| 车辆 | `GET /api/vehicles`、`/api/vehicles/resolve`、`/api/vehicles/coverage`、`/summary` | 车辆列表、VIN/车牌/手机号解析、来源覆盖 |
|
||||
| 单车 | `GET /api/vehicle-service`、`/summary`、`/overview`;`POST /overviews` | 聚合身份、实时、历史、RAW、里程、质量证据 |
|
||||
| 实时 | `GET /api/realtime/vehicles`、`/api/realtime/locations` | VIN 聚合实时状态和每协议最新位置 |
|
||||
| 历史 | `GET /api/history/locations`、`GET/POST /api/history/raw-frames` | TDengine 位置点和 RAW 证据分页 |
|
||||
| 里程 | `GET /api/mileage/summary`、`/api/mileage/daily` | MySQL 日里程统计 |
|
||||
| 在线 | `GET /api/statistics/online-summary`、`/online-vehicles` | 当前固定口径的在线统计和车辆状态 |
|
||||
| 数据质量 | `GET /api/quality/summary`、`/issues`、`/notification-plan` | 由当前数据推导的质量信号和静态通知方案 |
|
||||
| 告警兼容 | `GET /api/alert-events/summary`、`/alert-events`、`/notification-plan` | 实际是上述质量接口别名,不是业务告警事件 |
|
||||
| 地图 | `GET /api/map/reverse-geocode` | 服务端高德逆地理编码 |
|
||||
| 运维 | `GET /api/ops/health`、`/source-readiness` | 数据源可写性、Kafka lag、连接/Redis key、发布版本和接入准备度 |
|
||||
|
||||
同时,Go `realtime-api` 还提供面向底层表的查询接口和 OpenAPI,但 V2 浏览器应统一访问平台 BFF,避免前端绑定多个端口与底层存储模型。
|
||||
|
||||
## 6. 数据职责概览
|
||||
|
||||
- TDengine `lingniu_vehicle_ts`:`raw_frames` 是原始接收与扁平解析证据,`raw_frame_payload_chunks` 保存超长 payload,`vehicle_locations` 保存 VIN 级位置、速度、方向、告警标志和总里程历史。它不是车辆档案或告警业务库。
|
||||
- Redis DB 50:`vehicle:latest:*`、`vehicle:realtime-raw:*`、`vehicle:rt-kv:*`、`vehicle:online:*`、`vehicle:protocols:*`、`vehicle:last_seen` 提供可过期、可重建的当前态。它不能作为历史、告警或导出任务事实源。
|
||||
- MySQL `lingniu_vehicle_data`:保存 `vehicle`、多标识映射、JT808 注册鉴权、每协议实时快照/位置、来源配置和日里程事实/结果。现有 `vehicle` 档案很轻,只含 VIN、车牌、OEM、启用状态,尚不足以支撑车型、公司、车辆类型等产品字段。
|
||||
|
||||
统一模型及新增模型详见 `vehicle-data-platform-data-model.md`。
|
||||
|
||||
## 7. 一期缺口结论
|
||||
|
||||
必须新增或升级的核心能力:
|
||||
|
||||
1. 完整车辆档案、组织/车型/协议/接入厂家字典与首次接入事实。
|
||||
2. 地图聚合查询、组合筛选、状态统计和可配置在线口径。
|
||||
3. 包含方向、SOC、告警标志、停车点、异常点过滤和抽稀元数据的轨迹接口。
|
||||
4. 动态指标目录,以及多车、多指标、聚合/抽稀、分页的通用时序查询。
|
||||
5. MySQL 持久化告警规则、告警事件、处理记录、站内通知和告警计算 worker。
|
||||
6. MySQL 异步导出任务、对象/本地文件存储、worker、进度与下载授权。
|
||||
7. 接入状态字段:首次/最近上报、事件时间与接收时间延迟、最近消息类型、最近错误、动态在线阈值。
|
||||
8. 真正的认证、菜单/操作权限审计;现车辆平台 BFF 尚未呈现完整权限体系。
|
||||
|
||||
## 8. 推荐一期路由与菜单
|
||||
|
||||
```text
|
||||
/
|
||||
├── /monitor 全局监控(默认首页)
|
||||
├── /vehicles 车辆查询
|
||||
│ └── /vehicles/:vin 单车数字档案
|
||||
├── /tracks 轨迹查询与回放
|
||||
├── /history 历史数据分析
|
||||
│ └── /history/exports 导出任务
|
||||
├── /alerts
|
||||
│ ├── /alerts/events 告警事件
|
||||
│ ├── /alerts/rules 告警规则
|
||||
│ └── /alerts/inbox 站内通知
|
||||
├── /access 车辆接入状态
|
||||
└── /operations 运维质量(管理员)
|
||||
```
|
||||
|
||||
侧栏一级菜单保持 6 个业务入口:全局监控、车辆中心、轨迹回放、历史分析、告警中心、接入管理;导出任务挂在历史分析二级菜单,运维质量按权限置底。详情、告警、轨迹之间用带 `returnTo` 的上下文跳转,查询条件同步 URL,浏览器返回可恢复筛选、时间和选中车辆。
|
||||
|
||||
## 9. 推荐前后端整体架构
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
UI["V2 React Web"] --> BFF["Platform API / BFF"]
|
||||
BFF --> RT["Redis 当前态"]
|
||||
BFF --> TS["TDengine 时序与 RAW"]
|
||||
BFF --> DB["MySQL 业务事实"]
|
||||
BFF --> MAP["高德 REST / 安全代理"]
|
||||
ING["Gateway + NATS + Kafka Writers"] --> RT
|
||||
ING --> TS
|
||||
ING --> DB
|
||||
K["Kafka fields/raw"] --> AW["Alert evaluator"]
|
||||
AW --> DB
|
||||
BFF --> EQ["Export queue"]
|
||||
EQ --> EW["Export worker"]
|
||||
EW --> TS
|
||||
EW --> DB
|
||||
EW --> FS["文件/对象存储"]
|
||||
```
|
||||
|
||||
前端按 `app/router`、`shared`、`entities/vehicle`、`features`、`pages` 分层。服务端状态使用查询缓存库统一取消、去重、失效和后台刷新;少量 UI 状态用 React Context/局部 store。地图、图表、表格均只接收领域 DTO,不直接处理协议字段名。
|
||||
|
||||
平台 BFF 负责统一鉴权、参数校验、分页、在线状态计算、跨源聚合、字段目录、限流、traceId 和 DTO;禁止让前端分别拼 Redis/MySQL/TDengine。已有接入 writer 保持不变,新告警 worker 消费可重放 fields/raw 流,异步导出 worker 读取 TDengine/MySQL 并把任务状态写 MySQL。
|
||||
|
||||
## 10. 运维与最终 ECS 部署边界
|
||||
|
||||
已查阅 `docs/ops/vehicle-ingest-runbook.md`、`docs/ops/go-vehicle-ingest-memory.md`、`docs/architecture/production-data-plane-inventory.md` 和 `vehicle-data-platform/docs/deployment.md`。当前接入数据面采用 systemd 裸机 release 目录与 `current` 软链,平台使用 `/opt/lingniu-vehicle-platform/{releases,current,env}`,HTTP 端口 20300;部署后应验证 `/api/ops/health`、车辆查询和运行时配置。
|
||||
|
||||
V2 最终仍部署到当前 ECS,但不应在分析阶段执行。实施完成后的发布顺序应是:数据库向前兼容迁移 -> 新 worker(默认禁用规则)-> BFF -> 静态 Web -> 小流量健康验证 -> 启用告警/导出 worker。每一步保留旧 release 和可回滚软链;密钥只进 ECS 环境文件,不进仓库或构建产物。完整发布验收纳入路线图最后阶段。
|
||||
|
||||
## 11. 风险与待确认事项
|
||||
|
||||
| 优先级 | 风险/待确认 | 影响与建议 |
|
||||
| --- | --- | --- |
|
||||
| P0 | `vehicle` 档案缺车型、类型、公司、接入厂家等 | 先确定主数据来源和维护责任,不要从实时 JSON 猜档案 |
|
||||
| P0 | 现有告警接口名与真实语义不符 | V2 使用 `/api/v2/alerts/*` 新契约,旧别名标记 deprecated,避免误把质量信号当已处理事件 |
|
||||
| P0 | 动态指标没有目录、单位、类型和协议映射 | 先落指标元数据;否则图表、规则和导出会各自硬编码 |
|
||||
| P0 | 用户/角色来源尚未确认 | 明确复用 `ln-bi` jumpToken/JWT,还是接入统一 SSO;后端权限不可只靠菜单隐藏 |
|
||||
| P1 | 在线阈值是全局、协议级还是车辆级 | 建议全局默认 + 协议覆盖;一期暂不做单车覆盖 |
|
||||
| P1 | 轨迹异常过滤可能隐藏原始证据 | API 同时返回 raw/filtered 计数和算法版本,允许关闭过滤 |
|
||||
| P1 | TDengine `raw_frames.parsed_json` 是动态字段唯一历史来源 | 通用指标查询先从 JSON 提取会有成本;高频稳定指标应按使用量逐步物化,而非一期全列化 |
|
||||
| P1 | 单 ECS 同时承担接入、查询、告警和导出 | 导出/重查询必须有并发、时间范围、行数和资源配额;压测后再定 worker 并发 |
|
||||
| P1 | 地图 1 万辆的插件/授权与浏览器性能 | 先完成 1k/10k 基准,确定 MarkerCluster、MassMarks 或 Canvas 方案和高德配额 |
|
||||
| P1 | 站内通知实时方式 | 一期可 SSE + 轮询降级;若 ECS 反代不适合长连接,先使用增量轮询 |
|
||||
| P2 | Semi UI 与 Tailwind 双设计系统 | V2 启动前确认唯一基础组件策略;建议 Tailwind token + 无样式/轻量基础组件,ECharts 保留 |
|
||||
| P2 | 导出文件存储位置与保留期 | 确认 OSS 或 ECS 本地盘;推荐 OSS/兼容对象存储,任务和文件设过期清理策略 |
|
||||
|
||||
## 12. 本阶段完成定义
|
||||
|
||||
本阶段仅交付本分析、路线图、API 缺口和数据模型四份文档。V2 代码、数据库迁移、告警/导出 worker、ECS 发布均须在确认 P0 项后按路线图进入下一阶段。
|
||||
Reference in New Issue
Block a user