feat: expand vehicle data platform capabilities
This commit is contained in:
382
vehicle-data-platform/docs/open-platform-api.md
Normal file
382
vehicle-data-platform/docs/open-platform-api.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# 车辆数据开放平台 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 位展示前缀。
|
||||
|
||||
## 授权规则
|
||||
|
||||
```http
|
||||
Authorization: Bearer <32位appKey>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
请求需要同时满足:
|
||||
|
||||
1. appKey 状态为 `enabled`,当前调用时间在 Key 有效期内;
|
||||
2. 查询自然日完整落在 Key 有效期内;
|
||||
3. 每辆车的授权完整覆盖查询自然日;
|
||||
4. 车牌能映射到授权 VIN。
|
||||
|
||||
任一车辆未授权时整批返回 `403 FORBIDDEN`,不会返回部分结果。单日聚合不开放授权首日或结束日的半日数据。
|
||||
|
||||
## 单日用氢量
|
||||
|
||||
```http
|
||||
POST /api/v1/vehicles/hydrogen-consumption/query
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"plateNumbers": ["粤A12345", "粤B67890"],
|
||||
"date": "2026-07-01"
|
||||
}
|
||||
```
|
||||
|
||||
`plateNumbers` 可选。省略或传空数组时,返回应用在该自然日有效授权的全部车辆;传入时最多 200 个,不允许空字符串或重复值。`date` 固定为 `yyyy-MM-dd`。
|
||||
|
||||
查询全部授权车辆:
|
||||
|
||||
```json
|
||||
{
|
||||
"date": "2026-07-01"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"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"
|
||||
}
|
||||
```
|
||||
|
||||
## 单日里程
|
||||
|
||||
```http
|
||||
POST /api/v1/vehicles/mileage/query
|
||||
```
|
||||
|
||||
除 `date` 和可选 `plateNumbers` 外,里程接口支持可选的 `protocolPriority`:
|
||||
|
||||
```json
|
||||
{
|
||||
"plateNumbers": ["粤A12345"],
|
||||
"date": "2026-07-01",
|
||||
"protocolPriority": ["GB32960", "MQTT", "JT808"]
|
||||
}
|
||||
```
|
||||
|
||||
外部协议值只允许 `GB32960`、`MQTT`、`JT808`。传入时数组必须非空、不能重复;每辆车独立按数组顺序选择第一个有效协议,未列出的协议被完全禁用,不能兜底。不传该字段时保持现网默认选源行为。
|
||||
|
||||
支持的典型模式包括:`["GB32960","MQTT","JT808"]`、`["JT808","GB32960","MQTT"]`、`["GB32960","MQTT"]`、`["JT808"]`。
|
||||
|
||||
```json
|
||||
{
|
||||
"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`。真实零日里程返回 `NORMAL` 和 `dailyMileageKm: 0`。
|
||||
|
||||
里程与时间字段始终取自 `sourceProtocol` 指定的同一协议统计记录,不跨协议拼接。内部数据源 `YUTONG_MQTT` 对外统一返回 `MQTT`。
|
||||
|
||||
当查询日没有任何有效日里程和累计总里程,但查询日前存在有效累计里程时,接口执行前向填充:
|
||||
|
||||
- `dailyMileageKm` 返回 `0`;
|
||||
- `totalMileageKm` 沿用此前最近的有效累计总里程;
|
||||
- `sourceProtocol`、`dataTime` 沿用该历史统计记录;
|
||||
- `updatedAt` 返回上一个有效统计周期的计算时间,不伪装成查询日计算时间;
|
||||
- 只有查询日前也不存在有效累计里程时才返回 `NO_DATA`。
|
||||
|
||||
## 区间日里程
|
||||
|
||||
```http
|
||||
POST /api/v1/vehicles/mileage/range/query
|
||||
```
|
||||
|
||||
最长支持 366 天。`plateNumbers` 可省略,省略时查询整个区间均有效授权的全部车辆;指定时最多 5,000 个。`pageSize` 为 1–5,000,默认 5,000。
|
||||
|
||||
首次请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"startDate": "2026-07-01",
|
||||
"endDate": "2026-07-23",
|
||||
"protocolPriority": ["JT808", "GB32960", "MQTT"],
|
||||
"pageSize": 5000
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"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` 的校验、选源和缺日前向填充规则与单日接口完全一致,并对区间内每辆车、每个自然日独立执行。有下一页时,保持 `startDate`、`endDate`、`plateNumbers`、`protocolPriority` 和 `pageSize` 不变,把上一页 `nextCursor` 放入下一次请求。同一次查询的 `snapshotId` 保持不变。快照有效期为 2 小时。
|
||||
|
||||
接口只固化授权车辆清单,不预生成“车辆数 × 天数”的明细;每页读取带复合索引的日统计投影,因此大范围查询不会扫描原始时序明细。
|
||||
|
||||
## 指定时刻总里程
|
||||
|
||||
```http
|
||||
POST /api/v1/vehicles/total-mileage/query
|
||||
```
|
||||
|
||||
接口返回“不晚于请求时间的最近一条有效总里程记录”。`time` 使用北京时间,格式必须为 `yyyy-MM-dd HH:mm:ss`。
|
||||
|
||||
```json
|
||||
{
|
||||
"vin": "LA9GG68L2PBAF4790",
|
||||
"time": "2026-07-21 09:30:00",
|
||||
"protocol": "GB32960"
|
||||
}
|
||||
```
|
||||
|
||||
`protocol` 可选,只接受车辆数据中台、接收、统计和开放平台共同使用的唯一规范值:`GB32960`、`YUTONG_MQTT`、`JT808`。不再接受 `32960`、`mqtt`、`808` 等别名。未指定时严格按以下优先级查找首个有数据的协议:
|
||||
|
||||
```text
|
||||
GB32960 > YUTONG_MQTT > JT808
|
||||
```
|
||||
|
||||
协议总里程含义:
|
||||
|
||||
| 协议 | 含义 |
|
||||
|---|---|
|
||||
| `GB32960` | 车辆仪表盘累计总里程,对应 GB/T 32960 整车数据累计里程 |
|
||||
| `YUTONG_MQTT` | 车辆仪表盘或车端控制器累计总里程,由 MQTT 平台上报 |
|
||||
| `JT808` | 定位终端累计里程,由 GPS/终端侧计算,不等同于车辆仪表盘里程 |
|
||||
|
||||
三种协议可能使用不同里程源、标定值和重置机制,不能直接拼接成同一条连续里程曲线。响应会返回实际命中的协议和记录时间,调用方应结合时间差判断记录是否足够新。
|
||||
|
||||
```json
|
||||
{
|
||||
"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`:实际命中的采集记录时间;
|
||||
- `timeDifferenceSeconds`:`queryTime - 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` 可调用。
|
||||
|
||||
```http
|
||||
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
|
||||
```
|
||||
|
||||
创建应用:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "示例合作方",
|
||||
"status": "enabled",
|
||||
"validFrom": "2026-07-01T00:00:00+08:00",
|
||||
"validTo": "2027-07-01T00:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
创建和轮换响应中的 `data.appKey` 是唯一一次明文交付机会。轮换后旧 Key 立即失效。
|
||||
|
||||
车辆授权 `PUT` 使用完整替换语义,单次最多 5000 辆:
|
||||
|
||||
```json
|
||||
{
|
||||
"vehicles": [
|
||||
{
|
||||
"vin": "LTEST32960VIN0001",
|
||||
"validFrom": "2026-07-01T00:00:00+08:00",
|
||||
"validTo": "2027-07-01T00:00:00+08:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
VIN 必须已存在于平台身份绑定表。
|
||||
|
||||
管理员可以从车辆目录按 VIN、车牌或品牌搜索并勾选,也可以批量粘贴 VIN
|
||||
或车牌自动匹配,或者一次选择当前车辆主数据中的全部车辆。批量匹配会报告
|
||||
未识别项,不会静默忽略。“全部车辆”是保存时点的车辆快照,不会自动包含
|
||||
以后新增的车辆。
|
||||
|
||||
管理端可通过下列接口读取可授权车辆目录:
|
||||
|
||||
```http
|
||||
GET /portal-api/admin/vehicles
|
||||
Authorization: Bearer <admin-token>
|
||||
```
|
||||
|
||||
合作伙伴用户可配置账号有效期,并通过 `owner`、`developer`、`viewer`
|
||||
角色绑定一个或多个应用。密码至少 12 位且必须同时包含大小写字母和数字。
|
||||
连续 5 次登录失败后账号锁定 15 分钟。
|
||||
|
||||
内部车辆平台中启用的本地 `admin` 账号可使用同一用户名和密码登录开放平台。
|
||||
开放平台校验内部密码哈希后签发独立会话,不复制密码,也不共享两个域名的
|
||||
浏览器 Token。管理员可在“平台管理”中创建开放应用、合作伙伴账号并配置
|
||||
车辆与授权有效期。
|
||||
|
||||
## 合作伙伴门户
|
||||
|
||||
独立开放平台默认监听 `20310`,不再与内部平台 `20300` 共用外部 HTTP
|
||||
入口。门户 API 使用短期会话 Token,数据 API 仍使用应用的 32 位 AppKey。
|
||||
|
||||
```http
|
||||
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 仍只显示一次。门户登录、
|
||||
密码修改、用户配置和密钥操作均写入审计表。
|
||||
|
||||
## 用氢量统计
|
||||
|
||||
当天数据由 `vehicle-stat-writer` 直接消费 GB32960 fields Kafka 流,统一采用压力法:
|
||||
|
||||
```text
|
||||
剩余氢量 kg = NIST氢气密度(最高氢压 MPa, 最高氢温 ℃) × 车型氢瓶容量 L / 1000
|
||||
```
|
||||
|
||||
- 不使用广东扩展直接上报的剩余氢量、飞驰剩余百分比或百公里氢耗推算质量;
|
||||
- `vehicle-stat-writer` 每六小时从 `ln_asset_management.vehicle_info → vehicle_model.tank_capacity` 同步 VIN 容积到本地 `vehicle_hydrogen_tank_capacity`;
|
||||
- 同步完成后一次性加载到进程内存,高频帧计算不访问资产库、Redis或MySQL;缺少VIN容积时拒绝生成氢量;
|
||||
- 压力法使用 NIST 实氢气密度方程,允许 0–70 MPa、220–1000 K 的输入范围。
|
||||
|
||||
每个有效样本在同一个 MySQL 事务中更新
|
||||
`vehicle_open_hydrogen_stream_state`,并投影到
|
||||
`vehicle_open_daily_energy`。Kafka offset 只在事务提交成功后提交,因此服务重启或消息重放不会重复累计。状态按
|
||||
`VIN + 日期 + source_endpoint` 保存,每条消息只做常数次数据库操作,不扫描当天历史数据。
|
||||
|
||||
统计规则:
|
||||
|
||||
- 先按 VIN 和 `source_endpoint` 分别统计,避免多个采集源的序列交错产生虚假变化;
|
||||
- 同一 VIN 优先采用质量正常、样本数量更多的数据源,并记录入库;
|
||||
- 每次加氢之间维护质量低水位,只累计新的有效最低质量,避免压力温度波动反复计量;
|
||||
- 抖动阈值按该VIN容积对应的 `0.2 MPa` 质量变化动态计算,最小 `0.05 kg`、最大 `1 kg`;
|
||||
- 质量相对本轮低水位上升超过 `max(1 kg, 当前质量×5%)` 时识别为加氢并重置低水位;
|
||||
- 单次下降超过 `OPEN_STAT_HYDROGEN_MAX_DROP_KG` 时过滤并标记 `SUSPECT`,默认 `20 kg`;
|
||||
- 至少两条有效样本才标记 `OK`;
|
||||
- 对外只返回 `quality_status=OK` 的结果。
|
||||
|
||||
当天接口结果是准实时的进行中累计值。乱序、重复和迟到样本不会覆盖更新的状态;跨日迟到数据由
|
||||
`open-platform-stat` 夜间批处理兜底。批处理默认重算昨天和前天,以修正迟到上报,并作为已结束日期的权威结果。
|
||||
|
||||
```bash
|
||||
/opt/lingniu-vehicle-platform/current/open-platform-stat \
|
||||
-date 2026-07-01 \
|
||||
-lookback-days 1
|
||||
|
||||
systemctl enable --now lingniu-vehicle-open-stat.timer
|
||||
```
|
||||
Reference in New Issue
Block a user