车辆单日用氢量
{
+ "date": "2026-08-06",
+ "plateNumbers": ["浙F06618F"]
+}diff --git a/vehicle-data-platform/apps/api/cmd/open-platform-stat/main.go b/vehicle-data-platform/apps/api/cmd/open-platform-stat/main.go index 22a29bbd..f0d42dfb 100644 --- a/vehicle-data-platform/apps/api/cmd/open-platform-stat/main.go +++ b/vehicle-data-platform/apps/api/cmd/open-platform-stat/main.go @@ -7,7 +7,10 @@ import ( "fmt" "log" "os" + "sort" "strconv" + "strings" + "sync" "time" _ "github.com/go-sql-driver/mysql" @@ -22,6 +25,11 @@ func main() { lookback := flag.Int("lookback-days", envInt("OPEN_STAT_LOOKBACK_DAYS", 2), "number of dates ending at -date") noise := flag.Float64("hydrogen-noise-kg", envFloat("OPEN_STAT_HYDROGEN_NOISE_KG", 0.05), "ignored mass jitter") maxDrop := flag.Float64("hydrogen-max-drop-kg", envFloat("OPEN_STAT_HYDROGEN_MAX_DROP_KG", 20), "maximum accepted drop between samples") + vin := flag.String("vin", "", "optional 17-character VIN for a scoped rebuild") + vinWorkers := flag.Int("vin-workers", envInt("OPEN_STAT_VIN_WORKERS", 4), "parallel per-VIN queries for an all-vehicle day") + allDates := flag.Bool("all-dates", false, "rebuild every available completed event date; optionally scoped by -vin") + dryRun := flag.Bool("dry-run", false, "calculate and print results without changing MySQL") + seedStream := flag.Bool("seed-stream-state", false, "atomically seed current-day segment stream watermarks during a controlled writer handoff") flag.Parse() cfg := config.Load() @@ -50,24 +58,190 @@ func main() { if *lookback < 1 || *lookback > 31 { log.Fatal("-lookback-days must be between 1 and 31") } - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + if *vinWorkers < 1 || *vinWorkers > 16 { + log.Fatal("-vin-workers must be between 1 and 16") + } + normalizedVIN := strings.ToUpper(strings.TrimSpace(*vin)) + if *seedStream && normalizedVIN != "" { + log.Fatal("-seed-stream-state requires an all-vehicle rebuild") + } + if *seedStream && *dryRun { + log.Fatal("-seed-stream-state cannot be combined with -dry-run") + } + tdengineDB.SetMaxOpenConns(*vinWorkers) + timeout := 30 * time.Minute + if *allDates { + timeout = 6 * time.Hour + } + ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() capacities, err := openplatform.LoadHydrogenCapacities(ctx, mysqlDB) if err != nil { log.Fatalf("load hydrogen tank capacities: %v", err) } - for offset := *lookback - 1; offset >= 0; offset-- { - start := time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 0, 0, 0, 0, location).AddDate(0, 0, -offset) - observations, err := openplatform.LoadHydrogenObservations(ctx, tdengineDB, cfg.TDengineDatabase, start, start.AddDate(0, 0, 1), capacities) - if err != nil { - log.Fatalf("load hydrogen observations for %s: %v", start.Format("2006-01-02"), err) + if normalizedVIN != "" { + if _, ok := capacities[normalizedVIN]; !ok { + log.Fatalf("no active hydrogen tank capacity for VIN %s", normalizedVIN) } - stats := openplatform.BuildHydrogenDailyStats(observations, start.Format("2006-01-02"), *noise, *maxDrop) - if err := openplatform.ReplaceHydrogenDailyStats(ctx, mysqlDB, start.Format("2006-01-02"), stats); err != nil { - log.Fatalf("persist hydrogen statistics for %s: %v", start.Format("2006-01-02"), err) - } - fmt.Printf("date=%s observations=%d vehicles=%d\n", start.Format("2006-01-02"), len(observations), len(stats)) } + startDate := time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 0, 0, 0, 0, location).AddDate(0, 0, -(*lookback - 1)) + if *allDates { + var first, last time.Time + var found bool + var rangeErr error + if normalizedVIN == "" { + first, last, found, rangeErr = openplatform.HydrogenObservationDateRangeForAll(ctx, tdengineDB, cfg.TDengineDatabase) + } else { + first, last, found, rangeErr = openplatform.HydrogenObservationDateRange(ctx, tdengineDB, cfg.TDengineDatabase, normalizedVIN) + } + if rangeErr != nil { + log.Fatalf("load hydrogen date range: %v", rangeErr) + } + if !found { + log.Fatal("no GB32960 history found") + } + first = first.In(location) + last = last.In(location) + startDate = time.Date(first.Year(), first.Month(), first.Day(), 0, 0, 0, 0, location) + endDate = time.Date(last.Year(), last.Month(), last.Day(), 0, 0, 0, 0, location) + now := time.Now().In(location) + lastCompletedDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, location).AddDate(0, 0, -1) + if endDate.After(lastCompletedDate) { + endDate = lastCompletedDate + } + scope := "all-vehicles" + if normalizedVIN != "" { + scope = normalizedVIN + } + fmt.Printf("scope=%s date_from=%s date_to=%s mode=all-dates\n", scope, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) + } + for start := startDate; !start.After(endDate); start = start.AddDate(0, 0, 1) { + date := start.Format("2006-01-02") + energyParameters, parameterErr := openplatform.LoadHydrogenCalculationParameters(ctx, mysqlDB, start) + if parameterErr != nil { + log.Fatalf("load hydrogen energy parameters for %s: %v", date, parameterErr) + } + var stats []openplatform.HydrogenDailyStat + observationCount := 0 + if normalizedVIN == "" { + var vins []string + vins, err = openplatform.LoadHydrogenObservationVINs(ctx, tdengineDB, cfg.TDengineDatabase, start, start.AddDate(0, 0, 1), capacities) + if err == nil { + stats, observationCount, err = buildHydrogenDailyStatsByVIN(ctx, tdengineDB, cfg.TDengineDatabase, capacities, energyParameters, vins, start, *noise, *maxDrop, *vinWorkers) + } + } else { + var observations []openplatform.HydrogenObservation + observations, err = openplatform.LoadHydrogenObservationsForVIN(ctx, tdengineDB, cfg.TDengineDatabase, start, start.AddDate(0, 0, 1), capacities, normalizedVIN) + observationCount = len(observations) + stats = openplatform.BuildHydrogenDailyStatsOrderedWithParameters(observations, date, *noise, *maxDrop, energyParameters) + } + if err != nil { + log.Fatalf("load hydrogen observations for %s: %v", date, err) + } + if !*dryRun { + if *seedStream { + err = openplatform.ReplaceHydrogenDailyStatsAndSeedStream(ctx, mysqlDB, date, stats) + } else if normalizedVIN == "" { + err = openplatform.ReplaceHydrogenDailyStats(ctx, mysqlDB, date, stats) + } else { + err = openplatform.ReplaceHydrogenDailyStatsForVIN(ctx, mysqlDB, date, normalizedVIN, stats) + } + if err != nil { + log.Fatalf("persist hydrogen statistics for %s: %v", date, err) + } + } + mode := "write" + if *dryRun { + mode = "dry-run" + } + if normalizedVIN != "" && len(stats) == 1 { + fmt.Printf("date=%s vin=%s observations=%d consumption_kg=%.3f refuels=%d quality=%s mode=%s\n", date, normalizedVIN, observationCount, stats[0].ConsumptionKg, stats[0].RefuelCount, stats[0].QualityStatus, mode) + } else { + fmt.Printf("date=%s observations=%d vehicles=%d mode=%s\n", date, observationCount, len(stats), mode) + } + } +} + +type hydrogenVINResult struct { + VIN string + ObservationCount int + Stats []openplatform.HydrogenDailyStat + Err error +} + +func buildHydrogenDailyStatsByVIN( + ctx context.Context, + tdengineDB *sql.DB, + database string, + capacities map[string]float64, + energyParameters map[string]openplatform.HydrogenCalculationParameters, + vins []string, + start time.Time, + noiseKg float64, + maxDropKg float64, + workerCount int, +) ([]openplatform.HydrogenDailyStat, int, error) { + if len(vins) == 0 { + return nil, 0, nil + } + if workerCount > len(vins) { + workerCount = len(vins) + } + workerCtx, cancel := context.WithCancel(ctx) + defer cancel() + jobs := make(chan string) + results := make(chan hydrogenVINResult, len(vins)) + var workers sync.WaitGroup + for worker := 0; worker < workerCount; worker++ { + workers.Add(1) + go func() { + defer workers.Done() + for vin := range jobs { + observations, err := openplatform.LoadHydrogenObservationsForVIN(workerCtx, tdengineDB, database, start, start.AddDate(0, 0, 1), capacities, vin) + if err != nil { + results <- hydrogenVINResult{VIN: vin, Err: err} + continue + } + results <- hydrogenVINResult{ + VIN: vin, ObservationCount: len(observations), + Stats: openplatform.BuildHydrogenDailyStatsOrderedWithParameters(observations, start.Format("2006-01-02"), noiseKg, maxDropKg, energyParameters), + } + } + }() + } + go func() { + defer close(jobs) + for _, vin := range vins { + select { + case jobs <- vin: + case <-workerCtx.Done(): + return + } + } + }() + go func() { + workers.Wait() + close(results) + }() + stats := make([]openplatform.HydrogenDailyStat, 0, len(vins)) + observationCount := 0 + var firstErr error + for result := range results { + if result.Err != nil { + if firstErr == nil { + firstErr = fmt.Errorf("VIN %s: %w", result.VIN, result.Err) + cancel() + } + continue + } + observationCount += result.ObservationCount + stats = append(stats, result.Stats...) + } + if firstErr != nil { + return nil, 0, firstErr + } + sort.Slice(stats, func(i, j int) bool { return stats[i].VIN < stats[j].VIN }) + return stats, observationCount, nil } func envInt(name string, fallback int) int { diff --git a/vehicle-data-platform/apps/api/internal/openplatform/assets/docs.html b/vehicle-data-platform/apps/api/internal/openplatform/assets/docs.html index 5a15285c..b451d30d 100644 --- a/vehicle-data-platform/apps/api/internal/openplatform/assets/docs.html +++ b/vehicle-data-platform/apps/api/internal/openplatform/assets/docs.html @@ -3,197 +3,212 @@
-面向合作方开放车辆单日用氢量、单日里程、区间日里程和指定时刻总里程。接口使用独立的 32 位 appKey 认证。
- -面向合作方开放车辆用氢量、里程、实时状态及加氢车辆停留核验数据。每个接口独立说明请求参数、成功返回与异常情况。
+ Base URLhttps://open.d.lnoneos.com
+ Authorization: Bearer <32位appKey> -Content-Type: application/json-
appKey 由平台管理员创建并授权车辆。Key 和逐车授权都必须完整覆盖所查询的自然日。
+所有接口均使用平台管理员签发的 32 位 appKey。appKey 有效期及车辆授权期必须完整覆盖查询时间。
Authorization: Bearer <YOUR_APP_KEY>
Content-Type: application/json查询指定车辆的单日用氢量,单位 kg。
-查询指定车辆的单日行驶里程、累计总里程、实际来源协议、车辆源数据时间和投影更新时间,单位 km。
-以上两个按日接口的 plateNumbers 可选;省略或传空数组时,返回该应用在查询自然日有效授权的全部车辆。
-按最长 366 天区间分页查询逐车逐日里程。首次请求固化授权车辆清单,后续使用 nextCursor 翻页。
-两个里程接口均可传 protocolPriority,唯一外部值为 GB32960、MQTT、JT808。逐车逐日按数组顺序选择第一个有效协议;未列出的协议完全禁用。省略字段时保持现有默认选源行为。
-查询日没有有效里程但此前存在有效累计里程时,日里程补 0,累计总里程、来源协议和数据时间沿用最近有效统计;updatedAt 显示上一个统计周期的计算时间。
-按 VIN 和北京时间查询不晚于指定时刻的最近一条总里程,返回实际采集协议、记录时间和时间差秒数。
-查询当前有效授权车辆的最新位置、在线及运动状态、速度、累计里程、实际协议和记录时间。请求体传 {} 返回全部授权车辆。
只读查询资产管理库中有有效坐标的加氢站;可按 province、city、cooperateOnly 筛选。
所有接口使用 Content-Type: application/json。以下 JSON 可直接复制;未列出的可选字段可省略。
{
+ "date": "2026-08-06",
+ "plateNumbers": ["浙F06618F"]
+}{
+ "date": "2026-08-06",
+ "plateNumbers": ["浙F06618F"],
+ "protocolPriority": ["GB32960", "MQTT", "JT808"]
+}{
+ "startDate": "2026-08-01",
+ "endDate": "2026-08-06",
+ "pageSize": 100
+}{
+ "vin": "LA9GG68L2PBAF4790",
+ "time": "2026-08-06 10:30:00",
+ "protocol": "GB32960"
+}{
+ "plateNumbers": ["浙F06618F"]
+}{
+ "startTime": "2026-08-06 10:00:00",
+ "endTime": "2026-08-06 12:00:00",
+ "longitude": 120.752312,
+ "latitude": 30.746281,
+ "coordinateSystem": "GCJ02",
+ "radiusMeters": 8
+}{
+ "province": "浙江省",
+ "city": "嘉兴市",
+ "cooperateOnly": true
+}| protocol 唯一规范值 | 总里程含义 |
|---|---|
| GB32960 | 车辆仪表盘累计总里程,对应 GB/T 32960 整车数据累计里程 |
| YUTONG_MQTT | 车辆仪表盘或车端控制器累计总里程,由 MQTT 平台上报 |
| JT808 | 定位终端累计里程,由 GPS/终端侧计算,不等同于车辆仪表盘里程 |
protocol 不传时严格按 GB32960 > YUTONG_MQTT > JT808 选择首个有数据的协议。接口取不晚于请求时间的最近记录,不跨协议拼接里程。
+查询车辆单日用氢量,单位 kg。
| 字段 | 必填 | 说明 |
|---|---|---|
| date | 是 | 日期,格式 yyyy-MM-dd |
| plateNumbers | 否 | 车牌数组;省略或 [] 时查询全部授权车辆 |
{
"code": "SUCCESS",
"data": [{
"plateNumber": "浙F06618F",
"hydrogenConsumptionKg": 12.315,
"status": "NORMAL"
}]
}curl -X POST 'https://your-host/api/v1/vehicles/hydrogen-consumption/query' \
- -H 'Authorization: Bearer YOUR_32_CHARACTER_APP_KEY' \
- -H 'Content-Type: application/json' \
- -d '{
- "plateNumbers": ["粤A12345", "粤B67890"],
- "date": "2026-07-01"
- }'
+ 返回当日行驶里程、当日累计总里程和实际选用的数据协议,单位 km。
| 字段 | 必填 | 说明 |
|---|---|---|
| date | 是 | 日期,格式 yyyy-MM-dd |
| plateNumbers | 否 | 省略时查询全部授权车辆 |
| protocolPriority | 否 | 协议选源顺序,例如 ["GB32960","MQTT","JT808"] |
{
"code": "SUCCESS",
"data": [{
"dailyMileageKm": 182.437,
"totalMileageKm": 12345.679,
"sourceProtocol": "GB32960",
"status": "NORMAL"
}]
}{
- "date": "2026-07-01"
-}
+ 按车辆、日期分页返回区间日里程,最长查询区间为 366 天。
| 字段 | 必填 | 说明 |
|---|---|---|
| startDate / endDate | 是 | 日期区间,yyyy-MM-dd |
| plateNumbers | 否 | 车牌数组,最多 5000 辆 |
| protocolPriority | 否 | 协议选源顺序 |
| pageSize / cursor | 否 | 分页大小及下一页游标 |
{
"code": "SUCCESS",
"data": [{
"date": "2026-08-06",
"dailyMileageKm": 182.437,
"totalMileageKm": 12345.679
}],
"nextCursor": null
}{
- "plateNumbers": ["粤A12345"],
- "date": "2026-07-01",
- "protocolPriority": ["JT808", "GB32960", "MQTT"]
-}
+ 按 VIN 返回不晚于指定时刻的最近一条总里程及实际采集协议。
| 字段 | 必填 | 说明 |
|---|---|---|
| vin | 是 | 17 位已授权 VIN |
| time | 是 | 北京时间 yyyy-MM-dd HH:mm:ss |
| protocol | 否 | GB32960、MQTT 或 JT808;省略时按默认顺序选取 |
{
"code": "SUCCESS",
"data": {
"totalMileageKm": 12345.678,
"protocol": "GB32960",
"recordTime": "2026-08-06 10:29:45",
"timeDifferenceSeconds": 15
}
}curl -X POST 'https://your-host/api/v1/vehicles/total-mileage/query' \
- -H 'Authorization: Bearer YOUR_32_CHARACTER_APP_KEY' \
- -H 'Content-Type: application/json' \
- -d '{
- "vin": "LA9GG68L2PBAF4790",
- "time": "2026-07-21 09:30:00",
- "protocol": "GB32960"
- }'
+ 返回最新位置、在线状态、速度、总里程、实际协议和记录时间。
| 字段 | 必填 | 说明 |
|---|---|---|
| plateNumbers | 否 | 省略或 [] 时返回全部当前有效授权车辆 |
{
"data": [{
"vin": "LA9GG68L2PBAF4790",
"plateNumber": "浙F06618F",
"sourceProtocol": "GB32960",
"longitude": 120.75,
"latitude": 30.74,
"speedKmh": 0,
"totalMileageKm": 12345.678,
"recordTime": "2026-08-17 17:40:12",
"timeDifferenceSeconds": 8,
"online": true,
"activeToday": true,
"motionStatus": "idle",
"locationAvailable": true,
"status": "NORMAL"
}]
}curl -X POST 'https://your-host/api/v1/vehicles/mileage/range/query' \
- -H 'Authorization: Bearer YOUR_32_CHARACTER_APP_KEY' \
- -H 'Content-Type: application/json' \
- -d '{
- "startDate": "2026-07-01",
- "endDate": "2026-07-23",
- "protocolPriority": ["GB32960", "MQTT"],
- "pageSize": 5000
- }'
- 下一页保持原请求参数不变,并传入上一页 nextCursor;同一次分页查询的 snapshotId 保持不变。
+按站点坐标、坐标系、时间段和半径,核验速度接近 0 的车辆停留记录,并按匹配度降序返回。
| 字段 | 必填 | 说明 |
|---|---|---|
| startTime / endTime | 是 | 北京时间;查询区间最长 24 小时 |
| longitude / latitude | 是 | 加氢站经纬度;由 coordinateSystem 说明坐标系 |
| coordinateSystem | 否 | WGS84(默认)或 GCJ02(高德);GCJ02 自动转换后核验 |
| radiusMeters | 否 | 核验半径,单位 m;1–100,默认 5 |
| plateNumbers | 否 | 省略时核验全部有效授权车辆 |
{
"data": [{
"plateNumber": "浙F06618F",
"stayStartTime": "2026-08-06 10:16:02",
"stayEndTime": "2026-08-06 10:42:18",
"stayDurationSeconds": 1576,
"matchScore": 93.842
}]
}curl -X POST 'https://your-host/api/v1/vehicles/realtime/query' \
- -H 'Authorization: Bearer YOUR_32_CHARACTER_APP_KEY' \
- -H 'Content-Type: application/json' -d '{}'
+ 加氢站地图点位
POST/api/v1/hydrogen-stations/query只读查询有有效坐标的加氢站,可按行政区划和合作属性筛选。
#请求参数
字段 必填 说明 province / city 否 行政区划筛选 cooperateOnly 否 true 仅合作站;false 仅外部站
成功返回 · 200
{
"data": [{
"id": "1",
"name": "示例加氢站",
"longitude": 120.752312,
"latitude": 30.746281,
"cooperative": true
}]
}- 400:行政区划参数过长
- 401:appKey 无效
- 无符合站点:data 为空数组
-curl -X POST 'https://your-host/api/v1/hydrogen-stations/query' \
- -H 'Authorization: Bearer YOUR_32_CHARACTER_APP_KEY' \
- -H 'Content-Type: application/json' -d '{}'
+ 所有成功响应外层固定包含 code、message、data 和 traceId;以下为 data 内的字段。无数据时按对应接口返回 null、NO_DATA 或空数组。
| 字段/枚举 | 说明 |
|---|---|
| code = SUCCESS | 请求已成功处理。业务数据是否存在由每条数据的 status 判断。 |
| message | 成功时固定为 success。 |
| traceId | 本次请求唯一追踪标识;异常排查时请完整提供。 |
| status = NORMAL | 存在可用数据,相关数值、协议和时间字段有效。 |
| status = NO_DATA | 在授权范围内未找到可用数据;可选数值、位置、时间字段为 null 或省略。 |
| status = DATA_ANOMALY | 检测到数据异常,当前不返回可能错误的里程;dataQuality 给出原因。 |
| dataQuality = TOTAL_MILEAGE_ROLLBACK | 终端累计里程明显回退,已阻止其作为正常累计里程返回。 |
| 字段 | 说明 |
|---|---|
| plateNumber / date | 车牌与查询自然日(Asia/Shanghai)。 |
| hydrogenConsumptionKg | 当日用氢量,单位 kg;无有效数据时为 null。 |
| status | NORMAL 表示已计算;NO_DATA 表示无可用统计。 |
| 字段 | 说明 |
|---|---|
| vin / plateNumber / date | 车辆唯一 VIN、车牌和统计自然日。 |
| dailyMileageKm | 当日行驶里程,单位 km。缺少当日记录且可前向补齐时为 0。 |
| totalMileageKm | 所选协议的日末累计总里程,单位 km;不会用 GPS 日里程估算值替代。 |
| sourceProtocol | 实际采用的协议:GB32960、MQTT 或 JT808。 |
| dataTime | 实际采用的最后一条车辆源数据时间。 |
| updatedAt | 该统计周期的计算时间;前向补齐时保持上一有效统计周期时间。 |
| snapshotId / nextCursor | 仅区间接口返回;snapshotId 固定本次分页车辆范围,nextCursor 为下一页游标,最后一页为 null。 |
| 字段 | 说明 |
|---|---|
| vin / queryTime | 查询车辆 VIN 与请求的北京时间。 |
| totalMileageKm | 不晚于请求时间的最近一条累计总里程,单位 km。 |
| protocol / protocolInput | protocol 为实际命中协议;protocolInput 为请求显式传入的协议,未传则省略。 |
| recordTime / timeDifferenceSeconds | 实际命中记录时间,以及请求时间与记录时间的差值,单位秒。 |
| mileageMeaning | 该协议累计里程的业务口径;JT808 为定位终端/GPS侧累计值。 |
| 字段/枚举 | 说明 |
|---|---|
| sourceProtocol | 本条位置、速度、累计里程和记录时间实际采用的协议:GB32960、MQTT 或 JT808。 |
| longitude / latitude | 最新有效经纬度;无有效位置时为 null,locationAvailable 为 false。 |
| speedKmh / totalMileageKm | 所选来源的瞬时速度(km/h)和累计总里程(km)。 |
| recordTime / timeDifferenceSeconds | 所选来源的记录时间,以及与当前查询时间的差值,单位秒。 |
| online | true:任一协议最近 60 秒内上报;false:没有任一协议满足该实时阈值。 |
| activeToday | true:任一协议在当前自然日曾上报;不等同于实时 online。 |
| motionStatus = driving | 在线且所选来源速度大于 3 km/h。 |
| motionStatus = idle | 在线且所选来源速度不大于 3 km/h。 |
| motionStatus = offline | 当前不在线,或没有实时记录。 |
| protocol | 旧版兼容字段;新接入请使用 sourceProtocol。 |
| 字段 | 说明 |
|---|---|
| stayStartTime / stayEndTime | 满足核验条件的连续停留开始、结束时间。 |
| stayDurationSeconds / stayDurationMinutes | 停留时长,分别以秒、分钟表示。 |
| matchScore | 0–100;位置越接近、速度越低、停留越久、样本越充分,分数越高。 |
| averageDistanceMeters / maxDistanceMeters | 定位点相对输入站点坐标的平均/最大距离,单位 m。 |
| averageSpeedKmh / maxSpeedKmh / matchedSamples | 核验区间内速度统计与匹配定位样本数。 |
| sourceProtocols | 本停留片段采用过的协议数组,枚举值为 GB32960、MQTT、JT808。 |
| 字段 | 说明 |
|---|---|
| id / name / shortName | 站点唯一标识、标准名称和简称;id 为字符串以避免 JavaScript 精度丢失。 |
| address / province / city / district | 站点地址及行政区划。 |
| longitude / latitude | 站点坐标。 |
| cooperative | true:合作站(内部站或已导入合作名录);false:外部站。 |
{
- "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": "GB32960",
- "status": "NORMAL"
- }],
- "traceId": "b7ff5582ab1a4e13bfb4f10943685599"
-}
- 里程状态为 NORMAL 时,dailyMileageKm、totalMileageKm、dataTime 与 updatedAt 均有值;NO_DATA 时相关数据字段为 null,真实零里程仍为 NORMAL。
-{
- "code": "SUCCESS",
- "message": "success",
- "data": [{
- "vin": "LNB00000000000001",
- "plateNumber": "粤A12345",
- "date": "2026-07-01",
- "dailyMileageKm": 182.437,
- "dataTime": "2026-07-01T23:58:45+08:00",
- "updatedAt": "2026-07-02T05:10:00+08:00",
- "status": "NORMAL"
- }],
- "snapshotId": "9f8a74efbf9846349ae5676f3a5c0de8",
- "nextCursor": null,
- "traceId": "4ccf63c4e51d4d4ab9107d931783a53e"
-}
- {
- "code": "SUCCESS",
- "message": "success",
- "data": [{
- "plateNumber": "粤A12345",
- "date": "2026-07-01",
- "hydrogenConsumptionKg": 12.315,
- "status": "NORMAL"
- }],
- "traceId": "4ccf63c4e51d4d4ab9107d931783a53e"
-}
+ 未指定 protocol 时,按 GB32960 > MQTT > JT808 选择第一个有数据的协议;不会跨协议拼接里程。
| 规范值 | 总里程含义 |
|---|---|
| GB32960 | 车辆仪表盘累计总里程,对应 GB/T 32960 整车数据累计里程。 |
| 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"
-}
-
- | HTTP | code | 说明 |
|---|---|---|
| 200 | SUCCESS | 查询成功;无统计数据的车辆以 NO_DATA 返回 |
| 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 | 服务内部异常 |
所有响应均包含 code、message 和 traceId;请在工单中提供 traceId 以便排查。
| HTTP | code | 说明 |
|---|---|---|
| 200 | SUCCESS | 查询成功;无统计数据时,数据行的 status 为 NO_DATA。 |
| 400 | INVALID_REQUEST | 请求格式、数量、日期、时间、坐标或协议不正确。 |
| 401 | UNAUTHORIZED | appKey 不存在、停用或已过期。 |
| 403 | FORBIDDEN | 车辆或 appKey 授权期未覆盖请求时间。 |
| 500 | INTERNAL_ERROR | 服务内部异常;请提供 traceId。 |