Files
lingniu-vehicle-ingest/docs/vehicle-data-platform-data-model.md

225 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 车辆数据中台一期数据模型
## 1. 数据职责原则
| 存储 | 负责 | 不负责 |
| --- | --- | --- |
| Redis DB 50 | 最新状态、在线 TTL、字段级当前值、热查询缓存 | 历史事实、告警事件、规则、导出任务 |
| TDengine `lingniu_vehicle_ts` | 高频 RAW 证据、位置/稳定时序历史 | 车辆档案、权限、工作流状态 |
| MySQL `lingniu_vehicle_data` | 车辆主数据、身份映射、接入配置、当前业务投影、统计、规则、事件、通知、导出任务 | 每帧完整历史和无限增长的遥测明细 |
统一业务主键为 VIN。车牌、JT808 手机号、设备号、平台标识只是可变标识,通过映射解析到 VIN无法解析 VIN 的数据仍留 RAW/接入排障,不进入正式车辆统计。
## 2. 当前 TDengine 模型
### 2.1 `raw_frames` stable
列:`ts, frame_id, event_id, message_id, event_time, received_at, raw_size_bytes, raw_hex, raw_text, parsed_json, parse_status, parse_error, source_endpoint`
Tags`protocol, vehicle_key, vin, phone, device_id`
用途:证明收到什么、何时收到、解析成什么。`parsed_json` 物理列保存 Gateway 已一次扁平化的 `parsed_fields`,是动态协议字段的历史证据。新字段默认先进入这里,不为每个厂家字段立即新增物理列。
### 2.2 `raw_frame_payload_chunks` stable
列:事件/帧、接收时间、payload 类型、分片序号/总数、分片文本Tags 与 RAW 身份类似。
用途:保存超过主表列长度的 raw/parsed payload不参与常规业务查询。
### 2.3 `vehicle_locations` stable
列:`ts, event_id, received_at, longitude, latitude, altitude_m, speed_kmh, direction_deg, alarm_flag, status_flag, total_mileage_km`
Tags`protocol, vin`
用途VIN 非空车辆的高频位置、速度、方向、告警/状态位、总里程历史。当前平台 DTO 尚未完整透出方向、告警和状态位,也没有 SOC 列;轨迹一期需先补 DTO/查询SOC 可从 RAW 指标取值或按验证后的需求物化。
数据库当前配置 `KEEP 7300 DURATION 10 BUFFER 256`。保留期和磁盘容量应纳入运维监控,不在业务页面查询时临时改变。
## 3. 当前 Redis 模型
| Key | 值/用途 | 约束 |
| --- | --- | --- |
| `vehicle:latest:{vin}` | 跨协议最新核心字段 | 仅 VIN 非空;轻量快照 |
| `vehicle:latest:{vin}:{protocol}` | 单协议最新核心字段 | 可过期/重建 |
| `vehicle:realtime-raw:{protocol}:{vin}` | 单协议最新完整 parsed 状态 | Redis 中完整协议字段唯一副本 |
| `vehicle:rt-kv:{protocol}:{vin}:values` | 扁平字段当前值 | 与类型/时间/meta 配套 |
| `...:types` | 字段值类型 | 支持动态展示和规则类型校验的实时侧依据 |
| `...:times` | 每字段最新归一事件时间 | 防止乱序旧帧覆盖 |
| `...:meta` | 最新事件/接收时间、映射版本 | 用于新鲜度和追踪 |
| `vehicle:online:{protocol}:{vin}` | TTL 在线状态 | 在线事实的快速判断,不应永久保存 |
| `vehicle:online-state:{protocol}:{vin}` | 可分页的 Hash 副本 | 用于在线列表 |
| `vehicle:protocols:{vin}` | 最近出现协议集合 | 车辆来源发现 |
| `vehicle:last_seen` | 最近活跃车辆 ZSET | 分页/排序候选 |
生产只允许 `nats-fast-writer` 写 Redis 当前态Kafka 慢链路的 realtime writer 不应覆盖快路径。在线产品状态应由“最新时间 + 配置阈值”计算并返回阈值/依据,不能只信一个固定 Boolean。
## 4. 当前 MySQL 模型
### 4.1 身份与档案
| 表 | 主键/关键字段 | 用途 |
| --- | --- | --- |
| `vehicle` | `vin``plate, oem, enabled, updated_at` | 轻量车辆主实体;当前字段不足一期完整档案 |
| `vehicle_identifier` | `(protocol, source_code, identifier_type, identifier_value)`VIN/车牌/OEM | 多协议、多来源、多标识解析到 VIN |
| `vehicle_identity_binding` | VIN车牌、phone、OEM | 外部维护的兼容映射事实,运行期只读 |
| `jt808_registration` | `phone`device、plate、VIN、厂家、鉴权、来源、首次/最近注册鉴权/出现时间 | JT808 注册鉴权与绑定证据 |
### 4.2 当前态与来源
| 表 | 主键/关键字段 | 用途 |
| --- | --- | --- |
| `vehicle_realtime_snapshot` | `(protocol, vin)`plate、platform、peer、扁平 `parsed_json`、event/received time | 每协议每 VIN 最新合并遥测快照 |
| `vehicle_realtime_location` | `(protocol, vin)`经纬度、速度、总里程、SOC、海拔、方向、alarm/status、event/received time | 每协议每 VIN 最新位置业务缓存 |
| `vehicle_data_source` | 自增 ID唯一 `(protocol, source_ip)`source code/kind、platform、priority、enabled、first/latest seen | 数据来源配置和可信源选择 |
### 4.3 日里程
| 表 | 主键/关键字段 | 用途 |
| --- | --- | --- |
| `vehicle_daily_mileage_source` | `(vin, stat_date, protocol, source_key)`;首末里程、样本、质量、是否选中 | 多来源日里程事实和质量解释 |
| `vehicle_daily_mileage` | `(vin, stat_date, protocol)`source ID、日里程、最新总里程 | 对外查询结果投影 |
当前明确不存在/不应恢复的旧模型包括泛化 `vehicle_daily_metric`、TDengine `vehicle_mileage_points``raw_frames.fields_json` 重复列。
## 5. 一期统一领域模型
### 5.1 车辆标识
```text
VehicleId = VIN内部规范化大写、去空格
VehicleIdentifier = protocol + sourceCode + type + value -> VIN
Plate = 可变展示标识,不作为跨表唯一主键
Source = protocol + sourceCode/sourceId + endpoint
```
所有 API 都以 VIN 作为详情路径主键搜索接口可接受车牌、VIN、车辆编号/手机号并返回解析结果。多协议数据保留 `protocol``sourceId` 做归因,不能在合并时丢失来源。
### 5.2 时间模型
- `eventTime`:车端/协议事件时间,是轨迹、曲线和告警判断的主要时间。
- `receivedAt`:平台接收时间,用于延迟、迟到数据和链路健康。
- `storedAt/updatedAt`:投影或业务记录更新时间,不冒充车辆上报时间。
- `dataDelaySec = receivedAt - eventTime``silenceSec = now - max(receivedAt/eventTime 的已确认口径)`
### 5.3 在线状态
```text
never_reported: 无任何 latestReceivedAt
online: silenceSec <= thresholdSec
offline: silenceSec > thresholdSec
unknown: 时间非法、未来时间过大或来源状态无法判断
```
行驶/静止是独立 motion 状态,建议以速度阈值 + 最近更新时间判断;告警也是独立状态。不要把 `online/running/alarm` 压成一个互斥枚举UI 可基于多维状态决定主色和徽标。
### 5.4 指标值
```text
MetricValue {
vehicleId, metricKey, value, valueType, unit,
eventTime, receivedAt, protocol, sourceId,
quality, mappingVersion
}
```
`metricKey` 是协议无关的规范键;协议原字段通过映射表关联。厂家扩展字段允许命名空间,但必须有显示名、类型、分类和单位后才可进入图表/告警。
## 6. 建议新增 MySQL 模型
### 6.1 档案与组织
建议优先用关联表而非不断扩宽 `vehicle`
- `vehicle_profile(vin PK, vehicle_no, model_id, vehicle_type, company_id, operation_status, access_vendor_id, first_access_at, ...)`
- `vehicle_model(id, oem_id, code, name, vehicle_type, ...)`
- `organization(id, parent_id, type, code, name, enabled, ...)`
- `access_vendor(id, code, name, enabled, ...)`
外部主数据通过受控同步写入 `source_system/source_version/synced_at`。默认策略保护 `manual` 和其他外部来源;只有管理员显式选择 takeover 才能改变所有权。同一来源版本相同载荷幂等,不同载荷冲突,避免上游版本不可变性被破坏;每次创建/更新都进入 `vehicle_profile_audit`
### 6.2 指标目录
- `metric_definition(metric_key PK, display_name, category, value_type, unit, precision, chartable, alertable, enabled, sort_order, version)`
- `metric_protocol_mapping(metric_key, protocol, source_field, transform, mapping_version, enabled)`
`transform` 不能存任意可执行代码;使用有限转换类型或由版本化 Go 映射实现。目录服务可缓存,变更有版本和审计。
### 6.3 告警域
- `alert_rule(id, name, description, metric_key, operator, threshold_json, severity, duration_sec, recovery_json, repeat_interval_sec, enabled, version, created_by, updated_by, timestamps)`
- `alert_rule_scope(rule_id, scope_type, scope_value)`vehicle/model/oem/protocol/company一期可限制组合复杂度。
- `alert_event(id, rule_id, rule_version, vin, protocol, source_id, status, trigger_value_json, threshold_json, first_triggered_at, last_triggered_at, recovered_at, closed_at, location_json, dedupe_key, assignee, timestamps)`
- `alert_event_action(id, event_id, action, from_status, to_status, operator_id, note, created_at)`
- `notification(id, user_id, event_id, title, body, read_at, created_at)`
`alert_event.dedupe_key` 建唯一约束或等价幂等机制;阈值快照和 rule version 必须保存在事件上,避免规则修改后历史无法解释。
当前实现使用带业务前缀的实体表,完整 DDL 位于 `vehicle-data-platform/deploy/migrations/002_alert_center.sql`
- `vehicle_alert_rule` + `vehicle_alert_rule_audit`:当前规则与每版本不可变快照。
- `vehicle_alert_candidate``(rule_id, vin, protocol)` 持续命中计时,避免单帧达到阈值就误开事件。
- `vehicle_alert_stream_checkpoint``(consumer_group, topic, partition_id)` 保存数据库权威 `next_offset`、Kafka high watermark、处理/非法/迟到/回放计数、最近事件证据以及最近非法代码/时间。流 worker 先提交该事务,再提交 Kafka group offset崩溃后重复抓取的 offset 会被数据库 checkpoint 跳过。累计非法数用于审计,健康状态只对五分钟内仍在发生的非法消息告警。
- `vehicle_alert_event`:保存规则名/版本、触发阈值快照、证据时间、位置和乐观锁版本;`fingerprint + active status` 用于评估器幂等检查。
- `vehicle_alert_event_action`:触发、恢复和人工处置的不可变时间线。
- `vehicle_alert_notification`:站内通知真实已读状态;外部通道只有 `reserved`,没有配置供应商时绝不标记 `sent`
- `vehicle_alert_rule_state`:为 Boolean `changed` 规则保存每个 rule/VIN/protocol 的最近观测值;`003` 同时增加区间上限和 OEM 范围,`004` 为 fingerprint 重复间隔查询增加索引。
一期 evaluator 读取 `vehicle_realtime_location` 的当前快照并用 candidate 表证明持续时长。除 `freshness_sec` 明确按平台墙钟增长外,候选只允许由不同 `source_event_id` 且事件时间不回退的新观测推进;重复快照不能制造持续时长,迟到观测不能回退 candidate 或 Boolean 状态。规则行会在评估事务内加锁,停用规则、清理 candidate/Boolean 状态和写版本审计在同一事务提交,从而避免 evaluator 与管理员停用并发后留下幽灵候选。它仍不是 Kafka event-time 可重放评估器,因此跨当前快照的迟到数据重算、历史窗口规则和消费 offset 仍是阶段 8 的后续能力。
`alert-stream-evaluator` 已以 active 模式消费三类 canonical fields topic。它复用 Kafka consumer group 的分区顺序,校验 `event_kind/field_mapping/protocol/VIN/source_event_id/字段命名空间`,用接收时间识别超过可配置窗口的迟到观测。动态规则锁、当前批次范围内的 candidate/Boolean/活跃事件/重复窗口、事件/动作/通知副作用和 checkpoint 在同一个 MySQL 事务内完成,成功后才 commit Kafka数据库 ahead 时的重复 offset 由 checkpoint 跳过。超过迟到窗口的观测只允许驱动 `data_delay_sec`,不能开关速度/SOC/告警位事件。快照 evaluator 在 active 模式只负责按墙钟增长的 `freshness_sec`,不再读取动态规则。
### 6.4 导出域
- `export_job(id, user_id, type, format, query_json, status, progress, estimated_rows, exported_rows, file_uri, file_size, checksum, error_code, error_message, started_at, completed_at, expires_at, created_at)`
索引至少覆盖 `(user_id, created_at)``(status, created_at)` 和过期清理。`query_json` 保存规范化查询快照,不保存数据库密码或签名下载 URL。
### 6.5 在线阈值与审计
- `online_threshold(scope_type, scope_value, threshold_sec, version, updated_by, updated_at)`,一期建议 scope 仅 global/protocol。
- `audit_log(id, actor, action, resource_type, resource_id, before_json, after_json, trace_id, created_at)`
当前 V2 接入管理已先落地等价的窄表:
- `vehicle_access_threshold_config(id=1, version, default_threshold_sec, delay_threshold_sec, long_offline_sec, protocol_overrides_json, updated_by, updated_at)`
- `vehicle_access_threshold_audit(id, version, actor, summary, config_json, changed_at)`
表仅向前新增,不改变 gateway 的 `vehicle_realtime_snapshot(protocol, vin)` 主键与写入语义。后续统一审计域上线时可迁移到通用 `audit_log`,但必须保留版本历史。
## 7. 查询与物化策略
1. 实时页面优先 RedisMySQL realtime 作为业务查询/降级投影;返回值统一由 BFF 归一化。
2. 轨迹只查 TDengine `vehicle_locations`,必要的 SOC/告警补充应避免对每个点逐条回查 RAW先验证是否需要将 SOC 提升为位置列或建立专用指标 stable。
3. 通用历史指标首版可从 `raw_frames.parsed_json` 按白名单提取,但必须限制车辆数、指标数、时间范围并做基准;高频稳定指标按实际查询热度物化为专用时序模型。
4. 档案和字典在 BFF/Redis 做版本化短缓存,避免每次实时请求重复查不变数据。
5. 地图聚合可从 MySQL realtime/Redis 快照构建短周期服务端缓存;不要把地理历史查询压到实时表,也不要把全量点放浏览器聚合。
6. 告警 worker 使用 Kafka 可重放流和 eventTimeMySQL 保存工作流事实Redis 可保存短期去重/规则缓存但不是唯一事实。
## 8. 数据质量与治理要求
- 数值同时记录单位和精度;未知单位不得参与跨协议比较或规则判断。
- 原始值、规范值和转换版本可追踪;异常值标记不直接篡改 RAW。
- 未来时间、经纬度越界、速度突变、里程回退、重复事件均定义质量码。
- 轨迹过滤返回算法版本、原始/过滤/抽稀数量,可选择查看未过滤证据。
- 协议新增字段先进入扁平 RAW + Redis KV经目录登记后才进入 UI形成稳定高频查询后再物化。
- 所有新增表、字段、索引、保留期和清理任务随 migration 与数据字典发布。
## 9. 容量和保留
- 约 1,000 到 10,000 车辆、530 秒上报意味着时序写入持续增长TDengine 查询必须带 VIN/时间,禁止开放无界扫描。
- RAW 保留期、位置保留期、导出文件保留期和告警/审计保留期分别配置,不能共用一个默认值。
- MySQL 告警事件和审计按时间索引并规划归档;导出任务定时过期,文件删除和 DB 状态更新需幂等。
- 单 ECS 上告警和导出 worker 设置独立并发、内存、CPU 和 systemd 限制,避免影响接入写链路。
## 10. 仍需确认的数据问题
1. `vehicle_identity_binding` 的实际 DDL、权威维护方和未来是否继续兼容。
2. 车型、车辆类型、运营公司、车辆编号、运营状态、接入厂家的权威来源与更新频率。
3. 各协议 SOC、方向、启动、告警等级、电机/燃料电池等规范字段映射和单位。
4. “累计运行时长”是已有车端字段、由状态积分计算,还是一期可不提供。
5. 在线时间基准使用 eventTime 还是 receivedAt以及不同协议的默认阈值。
6. 轨迹停车阈值、漂移判定、迟到点容忍和跨协议主轨迹选择规则。
7. 导出存储与保留期、单用户并发/最大行数。
8. 告警规则适用范围组合、迟到窗口、重复/恢复语义和事件归属人规则。