Files
lingniu-vehicle-ingest/vehicle-data-platform/docs/open-platform-api.md
T

16 KiB
Raw Blame History

车辆数据开放平台 API

在线文档:

  • 简洁中文说明:/open-api/docs/
  • Swagger UI/open-api/swagger/
  • OpenAPI 3.0/open-api/openapi.yaml

Swagger 的认证信息仅保存在当前页面内存中,页面刷新后清除,不会由平台预填或持久化。

模块边界

一期向合作方开放经过预统计的车辆单日用氢量、单日里程和指定时刻总里程,并提供:

  • 32 位无连字符 UUID appKey
  • appKey 启停和起止有效期;
  • appKey 下的 VIN 白名单和逐车起止有效期;
  • 外部调用审计和管理员变更审计。

外部接口与平台后台登录令牌相互独立。appKey 明文只在创建或轮换时返回一次,数据库仅保存 SHA-256 和前 8 位展示前缀。

授权规则

Authorization: Bearer <32位appKey>
Content-Type: application/json

请求需要同时满足:

  1. appKey 状态为 enabled,当前调用时间在 Key 有效期内;
  2. 查询自然日完整落在 Key 有效期内;
  3. 每辆车的授权完整覆盖查询自然日;
  4. 车牌能映射到授权 VIN。

任一车辆未授权时整批返回 403 FORBIDDEN,不会返回部分结果。单日聚合不开放授权首日或结束日的半日数据。

单日用氢量

POST /api/v1/vehicles/hydrogen-consumption/query
{
  "plateNumbers": ["粤A12345", "粤B67890"],
  "date": "2026-07-01"
}

plateNumbers 可选。省略或传空数组时,返回应用在该自然日有效授权的全部车辆;传入时最多 200 个,不允许空字符串或重复值。date 固定为 yyyy-MM-dd

查询全部授权车辆:

{
  "date": "2026-07-01"
}
{
  "code": "SUCCESS",
  "message": "success",
  "data": [
    {
      "vin": "LNB00000000000001",
      "plateNumber": "粤A12345",
      "date": "2026-07-01",
      "hydrogenConsumptionKg": 12.315,
      "status": "NORMAL"
    },
    {
      "plateNumber": "粤B67890",
      "date": "2026-07-01",
      "hydrogenConsumptionKg": null,
      "status": "NO_DATA"
    }
  ],
  "traceId": "4ccf63c4e51d4d4ab9107d931783a53e"
}

单日里程

POST /api/v1/vehicles/mileage/query

date 和可选 plateNumbers 外,里程接口支持可选的 protocolPriority

{
  "plateNumbers": ["粤A12345"],
  "date": "2026-07-01",
  "protocolPriority": ["GB32960", "MQTT", "JT808"]
}

外部协议值只允许 GB32960MQTTJT808。传入时数组必须非空、不能重复;每辆车独立按数组顺序选择第一个有效协议,未列出的协议被完全禁用,不能兜底。不传该字段时保持现网默认选源行为。

支持的典型模式包括:["GB32960","MQTT","JT808"]["JT808","GB32960","MQTT"]["GB32960","MQTT"]["JT808"]

{
  "code": "SUCCESS",
  "message": "success",
  "data": [
    {
      "plateNumber": "粤A12345",
      "date": "2026-07-01",
      "dailyMileageKm": 182.437,
      "totalMileageKm": 12345.679,
      "dataTime": "2026-07-01T23:58:45+08:00",
      "updatedAt": "2026-07-02T05:10:00+08:00",
      "sourceProtocol": "GB32960",
      "status": "NORMAL"
    }
  ],
  "traceId": "b7ff5582ab1a4e13bfb4f10943685599"
}

dailyMileageKm 表示该自然日行驶里程;totalMileageKm 表示同一协议统计记录在该日最后有效时刻的累计总里程;sourceProtocol 是该行实际选中的协议;dataTime 是统计实际采用的最后一条车辆源数据时间;updatedAt 是日统计投影最后更新时间。时间均为带 +08:00 的 ISO 8601。

status=NORMAL 时数据字段均有值;任一关键字段缺失时整车返回 NO_DATA,相关数据字段和 sourceProtocol 均为 null。真实零日里程返回 NORMALdailyMileageKm: 0

里程与时间字段始终取自 sourceProtocol 指定的同一协议统计记录,不跨协议拼接。内部数据源 YUTONG_MQTT 对外统一返回 MQTT

当查询日没有任何有效日里程和累计总里程,但查询日前存在有效累计里程时,接口执行前向填充:

  • dailyMileageKm 返回 0
  • totalMileageKm 沿用此前最近的有效累计总里程;
  • sourceProtocoldataTime 沿用该历史统计记录;
  • updatedAt 返回上一个有效统计周期的计算时间,不伪装成查询日计算时间;
  • 只有查询日前也不存在有效累计里程时才返回 NO_DATA

区间日里程

POST /api/v1/vehicles/mileage/range/query

最长支持 366 天。plateNumbers 可省略,省略时查询整个区间均有效授权的全部车辆;指定时最多 5,000 个。pageSize 为 15,000,默认 5,000。

首次请求:

{
  "startDate": "2026-07-01",
  "endDate": "2026-07-23",
  "protocolPriority": ["JT808", "GB32960", "MQTT"],
  "pageSize": 5000
}

响应:

{
  "code": "SUCCESS",
  "message": "success",
  "data": [
    {
      "vin": "LNB00000000000001",
      "plateNumber": "粤A12345",
      "date": "2026-07-01",
      "dailyMileageKm": 182.437,
      "totalMileageKm": 12345.679,
      "dataTime": "2026-07-01T23:58:45+08:00",
      "updatedAt": "2026-07-02T05:10:00+08:00",
      "sourceProtocol": "JT808",
      "status": "NORMAL"
    }
  ],
  "snapshotId": "9f8a74efbf9846349ae5676f3a5c0de8",
  "nextCursor": null,
  "traceId": "4ccf63c4e51d4d4ab9107d931783a53e"
}

protocolPriority 的校验、选源和缺日前向填充规则与单日接口完全一致,并对区间内每辆车、每个自然日独立执行。有下一页时,保持 startDateendDateplateNumbersprotocolPrioritypageSize 不变,把上一页 nextCursor 放入下一次请求。同一次查询的 snapshotId 保持不变。快照有效期为 2 小时。

接口只固化授权车辆清单,不预生成“车辆数 × 天数”的明细;每页读取带复合索引的日统计投影,因此大范围查询不会扫描原始时序明细。

指定时刻总里程

POST /api/v1/vehicles/total-mileage/query

接口返回“不晚于请求时间的最近一条有效总里程记录”。time 使用北京时间,格式必须为 yyyy-MM-dd HH:mm:ss

{
  "vin": "LA9GG68L2PBAF4790",
  "time": "2026-07-21 09:30:00",
  "protocol": "GB32960"
}

protocol 可选,只接受车辆数据中台、接收、统计和开放平台共同使用的唯一规范值:GB32960YUTONG_MQTTJT808。不再接受 32960mqtt808 等别名。未指定时严格按以下优先级查找首个有数据的协议:

GB32960 > YUTONG_MQTT > JT808

协议总里程含义:

协议 含义
GB32960 车辆仪表盘累计总里程,对应 GB/T 32960 整车数据累计里程
YUTONG_MQTT 车辆仪表盘或车端控制器累计总里程,由 MQTT 平台上报
JT808 定位终端累计里程,由 GPS/终端侧计算,不等同于车辆仪表盘里程

三种协议可能使用不同里程源、标定值和重置机制,不能直接拼接成同一条连续里程曲线。响应会返回实际命中的协议和记录时间,调用方应结合时间差判断记录是否足够新。

{
  "code": "SUCCESS",
  "message": "success",
  "data": {
    "vin": "LA9GG68L2PBAF4790",
    "queryTime": "2026-07-21 09:30:00",
    "totalMileageKm": 12345.678,
    "protocol": "GB32960",
    "protocolInput": "GB32960",
    "mileageMeaning": "车辆仪表盘累计总里程(GB/T 32960整车数据累计里程)",
    "recordTime": "2026-07-21 09:29:45",
    "timeDifferenceSeconds": 15,
    "selectionPolicy": "GB32960 > YUTONG_MQTT > JT808",
    "status": "NORMAL"
  },
  "traceId": "95bddca78133474fa2bf56ecdf758e22"
}

字段说明:

  • recordTime:实际命中的采集记录时间;
  • timeDifferenceSecondsqueryTime - recordTime,单位秒,正常为非负数;
  • protocol:实际采用的采集协议,而不是默认优先级描述;
  • NO_DATA:授权通过,但所有候选协议在请求时间之前都没有有效总里程。

错误码

HTTP code 说明
400 INVALID_REQUEST 请求结构、车牌或数量不正确
400 INVALID_DATE_FORMAT 日期不是 yyyy-MM-dd
400 INVALID_DATETIME_FORMAT 时间不是 yyyy-MM-dd HH:mm:ss
401 UNAUTHORIZED appKey 不存在、停用或过期
403 FORBIDDEN Key 或车辆授权未覆盖查询自然日
500 INTERNAL_ERROR 服务内部异常

管理 API

管理 API 使用车辆数据平台后台 Bearer Token,仅 admin 可调用。

POST /api/v2/open-platform/apps
GET  /api/v2/open-platform/apps
PUT  /api/v2/open-platform/apps/{id}
POST /api/v2/open-platform/apps/{id}/rotate-key
GET  /api/v2/open-platform/apps/{id}/vehicles
PUT  /api/v2/open-platform/apps/{id}/vehicles
GET  /api/v2/open-platform/users
POST /api/v2/open-platform/users
PUT  /api/v2/open-platform/users/{id}
GET  /api/v2/open-platform/users/{id}/apps
PUT  /api/v2/open-platform/users/{id}/apps

创建应用:

{
  "name": "示例合作方",
  "status": "enabled",
  "validFrom": "2026-07-01T00:00:00+08:00",
  "validTo": "2027-07-01T00:00:00+08:00"
}

创建和轮换响应中的 data.appKey 是唯一一次明文交付机会。轮换后旧 Key 立即失效。

车辆授权 PUT 使用完整替换语义,单次最多 5000 辆:

{
  "vehicles": [
    {
      "vin": "LTEST32960VIN0001",
      "validFrom": "2026-07-01T00:00:00+08:00",
      "validTo": "2027-07-01T00:00:00+08:00"
    }
  ]
}

VIN 必须已存在于平台身份绑定表。

管理员可以从车辆目录按 VIN、车牌或品牌搜索并勾选,也可以批量粘贴 VIN 或车牌自动匹配,或者一次选择当前车辆主数据中的全部车辆。批量匹配会报告 未识别项,不会静默忽略。“全部车辆”是保存时点的车辆快照,不会自动包含 以后新增的车辆。

管理端可通过下列接口读取可授权车辆目录:

GET /portal-api/admin/vehicles
Authorization: Bearer <admin-token>

合作伙伴用户可配置账号有效期,并通过 ownerdeveloperviewer 角色绑定一个或多个应用。密码至少 12 位且必须同时包含大小写字母和数字。 连续 5 次登录失败后账号锁定 15 分钟。

内部车辆平台中启用的本地 admin 账号可使用同一用户名和密码登录开放平台。 开放平台校验内部密码哈希后签发独立会话,不复制密码,也不共享两个域名的 浏览器 Token。管理员可在“平台管理”中创建开放应用、合作伙伴账号并配置 车辆与授权有效期。

合作伙伴门户

独立开放平台默认监听 20310,不再与内部平台 20300 共用外部 HTTP 入口。门户 API 使用短期会话 Token,数据 API 仍使用应用的 32 位 AppKey。

POST /portal-api/auth/login
POST /portal-api/auth/logout
GET  /portal-api/session
GET  /portal-api/catalog
GET  /portal-api/apps
GET  /portal-api/apps/{id}/vehicles
GET  /portal-api/apps/{id}/audit
POST /portal-api/apps/{id}/rotate-key
PUT  /portal-api/account/password

只有应用 owner 可以在门户轮换 AppKey;新 Key 仍只显示一次。门户登录、 密码修改、用户配置和密钥操作均写入审计表。

用氢量统计

用氢量只接收 GB32960 帧,不读取 JT808、宇通 MQTT 或其他协议。统一采用压力法:

剩余氢量 kg = NIST氢气密度(最高氢压 MPa, 最高氢温 ℃) × 车型氢瓶容量 L / 1000
  • 不使用广东扩展直接上报的剩余氢量、飞驰剩余百分比、宇通 MQTT 剩余氢量或百公里氢耗推算质量;
  • vehicle-stat-writer 启动时及每六小时从 ln_asset_management.vehicle_info → vehicle_model 同步车型参数:tank_capacity 按 VIN 投影到 vehicle_hydrogen_tank_capacitybattery_capacity 按 VIN 投影到 vehicle_hydrogen_energy_parameter
  • 额定电量按车辆关联的具体车型读取,不按吨位写死;后续生效日期更晚的人工业务参数仍可覆盖自动同步基线;
  • 储氢容积同步后一次性加载到统计进程内存,高频帧计算不访问资产库、Redis或MySQL;额定电量由日氢耗任务按VIN和统计日期读取;缺少VIN容积时拒绝生成氢量;
  • 压力法使用 NIST 实氢气密度方程,允许 070 MPa、220–1000 K 的输入范围;
  • GB32960 上报的是“最高氢压”和“最高氢温”,两者不保证来自同一探头,也不保证代表静置平衡后的储氢瓶状态。因此压力法结果是车载信号估算值,不能用加氢站计量值直接反标公式系数。

当天准实时数据由 vehicle-stat-writer 消费 GB32960 fields Kafka 流。每个有效样本在同一个 MySQL 事务中更新 vehicle_open_hydrogen_segment_stream_state,并投影到 vehicle_open_daily_energy。Kafka offset 只在事务提交成功后提交,因此服务重启或消息重放不会重复累计。状态按 VIN + 日期 保存并合并全部连接端点;每条消息只做常数次数据库操作,不扫描当天历史数据。

统计规则:

  • 已结束日期先按 VIN 合并全部 source_endpoint,同一事件时间只保留一条可信样本;连接端口仅用于追踪,不再作为互斥统计来源;
  • 只有相邻两条样本都确认燃料电池处于工作状态时,才纳入工作段;优先采用广东扩展 engine_work_state=2,缺失时以燃料电池电流大于 1 A 作为回退;完全没有工作区间的日期标记为 NO_DATA
  • 加氢、超过 5 分钟的数据断点、燃料电池停机和异常跳变会切分工作段;每段开始与结束各取 5 条样本的氢质量中位数做差,再累加为当日用氢量;
  • 每个可计算工作段至少需要 10 条有效样本;段首尾差值未超过动态噪声阈值时不累计;
  • 燃料电池未工作时,下降的压力质量只更新防重复计算的低水位,不降低对外剩余氢量;压力或质量恢复上升后再更新剩余量;
  • 抖动阈值按该VIN容积对应的 0.2 MPa 质量变化动态计算,最小 0.05 kg、最大 1 kg
  • 质量相对本轮低水位上升超过 max(1 kg, 当前质量×5%) 时识别为加氢并重置低水位;
  • 相邻样本在 60 秒内无工作耗氢却恢复至少 8 MPa 时,按压力信号恢复处理,重置低水位但不增加加氢次数;
  • 单次下降超过 OPEN_STAT_HYDROGEN_MAX_DROP_KG 时过滤并标记 SUSPECT,默认 20 kg
  • 对外只返回 quality_status=OK 的结果。

分段计算由流式状态机直接执行:每车仅保留当前段的首 5 帧、尾 5 帧、低水位、上一帧和事件时间水位,状态不会随当天帧数增长。水位只覆盖当前自然日 00:00:0024:00:00,不向前后日期扩窗,也不借用次日帧;早于或等于当前事件时间水位的延迟帧直接忽略。流式结果采用与日终相同的工作段、中位数、加氢和异常跳变规则;已结束日期仍由 open-platform-stat 重算,作为最终对账结果。

历史重放只接收事件时间和接收时间同时落在统计日内的 GB32960 帧。每个统计日先读取有数据且配置了氢瓶容积的 VIN,再在同一进程内按单车单日并行查询;默认 4 个工作线程,全部车辆成功后才一次性事务替换当天结果。单车帧按 event_time 排序并单遍去重;JSON 只读取压力、温度和工作状态字段,不展开整帧,从而减少全车排序与反序列化开销。可通过 -vin-workersOPEN_STAT_VIN_WORKERS 调整工作线程数。

/opt/lingniu-vehicle-platform/current/open-platform-stat \
  -date 2026-07-01 \
  -lookback-days 1

systemctl enable --now lingniu-vehicle-open-stat.timer

流式算法首次切换时,先停止 lingniu-go-stat-writer.service,再对当天执行一次 -seed-stream-state。该参数会在同一个 MySQL 事务中替换当天结果并写入每车最后事件时间水位;恢复 Kafka 消费后,已经包含在重算结果中的 backlog 不会再次累计。正常日终任务不使用此参数。