Files
lingniu-vehicle-ingest/go/vehicle-gateway/internal/realtime/openapi.go

877 lines
48 KiB
Go
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.
package realtime
import (
"encoding/json"
"net/http"
"strings"
)
func NewOpenAPIHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
if strings.Trim(r.URL.Path, "/") != "openapi.json" {
writeError(w, http.StatusNotFound, "route not found")
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(realtimeOpenAPISpec())
})
}
func NewSwaggerUIHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write([]byte(swaggerHTML))
})
}
func realtimeOpenAPISpec() map[string]any {
return map[string]any{
"openapi": "3.0.3",
"info": map[string]any{
"title": "Lingniu Vehicle Realtime API",
"version": "1.0.0",
"description": "MySQL realtime query APIs for vehicle_realtime_snapshot and vehicle_realtime_location.",
},
"paths": map[string]any{
"/api/realtime/snapshots": map[string]any{
"get": map[string]any{
"summary": "查询车辆实时快照",
"description": "从 vehicle_realtime_snapshot 查询各协议最新在线快照支持协议、VIN、车牌过滤和分页。默认不执行 COUNT(*)total 默认表示本页返回条数;需要精确总数时传 includeTotal=true。",
"tags": []string{"Realtime Snapshot API"},
"x-table": "vehicle_realtime_snapshot",
"parameters": commonRealtimeParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Snapshot page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/SnapshotPage"},
},
},
},
},
},
},
"/api/realtime/locations": map[string]any{
"get": map[string]any{
"summary": "查询车辆实时位置",
"description": "从 vehicle_realtime_location 查询各协议最新位置支持协议、VIN、车牌过滤和分页。默认不执行 COUNT(*)total 默认表示本页返回条数;需要精确总数时传 includeTotal=true。",
"tags": []string{"Realtime Location API"},
"x-table": "vehicle_realtime_location",
"parameters": commonRealtimeParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Location page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/LocationPage"},
},
},
},
},
},
},
"/api/stats/daily-metrics": map[string]any{
"get": map[string]any{
"summary": "查询车辆每日里程",
"description": "从 vehicle_daily_mileage 查询最终选举后的每日里程,并通过 source_id 关联 vehicle_data_source 返回来源证据。默认不执行 COUNT(*)total 默认表示本页返回条数;需要精确总数时传 includeTotal=true。",
"tags": []string{"Stats API"},
"x-table": "vehicle_daily_mileage,vehicle_data_source",
"parameters": dailyMetricParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Daily metric page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DailyMetricPage"},
},
},
},
},
},
},
"/api/stats/daily-metrics/sources": map[string]any{
"get": map[string]any{
"summary": "查询每日里程候选来源",
"description": "从 vehicle_daily_mileage_source 查询每辆车、每天、每个协议下各来源独立计算出的里程候选,并关联 vehicle_data_source 返回平台、来源类型和可信优先级,用于解释最终日里程为什么选中某个来源。",
"tags": []string{"Stats API"},
"x-table": "vehicle_daily_mileage_source,vehicle_data_source",
"parameters": dailyMetricSourceParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Daily metric source page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DailyMetricSourcePage"},
},
},
},
},
},
},
"/api/stats/daily-metrics/sources/quality": map[string]any{
"get": map[string]any{
"summary": "汇总每日里程候选质量",
"description": "按日期、协议、quality_status、quality_reason 汇总 vehicle_daily_mileage_source用于发现某协议或某来源类型是否正在批量产生无基线、异常跳变等候选里程质量问题。",
"tags": []string{"Stats API"},
"x-table": "vehicle_daily_mileage_source",
"parameters": dailyMetricSourceParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Daily metric source quality summary page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DailyMetricSourceQualityPage"},
},
},
},
},
},
},
"/api/stats/daily-metrics/sources/selection": map[string]any{
"get": map[string]any{
"summary": "汇总每日里程来源选举",
"description": "按日期、协议、selection_status、selection_reason 汇总 vehicle_daily_mileage_source并结合 vehicle_data_source 解释各来源被选中、质量淘汰、禁用淘汰或低优先级未选中的原因。",
"tags": []string{"Stats API"},
"x-table": "vehicle_daily_mileage_source,vehicle_data_source",
"parameters": dailyMetricSourceParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Daily metric source selection summary page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DailyMetricSourceSelectionPage"},
},
},
},
},
},
},
"/api/stats/daily-metrics/diagnostics": map[string]any{
"get": map[string]any{
"summary": "诊断实时在线但日里程缺失",
"description": "从 vehicle_realtime_snapshot/location 按 event_time/received_at 找到指定日期活跃车辆,并关联 vehicle_daily_mileage 与 vehicle_daily_mileage_source解释车辆有实时数据但日里程缺失的原因。默认不执行 COUNT(*)total 默认表示本页返回条数;需要精确总数时传 includeTotal=true。",
"tags": []string{"Stats API"},
"x-table": "vehicle_realtime_snapshot,vehicle_realtime_location,vehicle_daily_mileage,vehicle_daily_mileage_source",
"parameters": dailyMetricDiagnosisParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Daily metric diagnostics page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DailyMetricDiagnosisPage"},
},
},
},
},
},
},
"/api/stats/daily-metrics/diagnostics/summary": map[string]any{
"get": map[string]any{
"summary": "汇总每日里程诊断",
"description": "按协议汇总指定日期 event_time/received_at 活跃车辆的日里程诊断结果,返回 OK、MISSING_DAILY、NO_SOURCE_SAMPLE、NO_TOTAL_MILEAGE 等分类数量,用于大屏、告警和排障入口。",
"tags": []string{"Stats API"},
"x-table": "vehicle_realtime_snapshot,vehicle_realtime_location,vehicle_daily_mileage,vehicle_daily_mileage_source",
"parameters": dailyMetricDiagnosisSummaryParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Daily metric diagnostics summary",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DailyMetricDiagnosisSummaryPage"},
},
},
},
},
},
},
"/api/stats/daily-metrics/diagnostics/reasons": map[string]any{
"get": map[string]any{
"summary": "按原因汇总每日里程诊断",
"description": "按协议、诊断分类和 reason 汇总指定日期 event_time/received_at 活跃车辆的日里程诊断结果,用于大屏和告警直接区分源头未上报、总里程为 0、旧总里程未更新、统计抽取缺失等问题。",
"tags": []string{"Stats API"},
"x-table": "vehicle_realtime_snapshot,vehicle_realtime_location,vehicle_daily_mileage,vehicle_daily_mileage_source",
"parameters": dailyMetricDiagnosisReasonSummaryParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Daily metric diagnostics reason summary",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DailyMetricDiagnosisReasonSummaryPage"},
},
},
},
},
},
},
"/api/stats/daily-metrics/diagnostics/field-status": map[string]any{
"get": map[string]any{
"summary": "按字段状态汇总每日里程诊断",
"description": "按协议、诊断分类、reason 和实时总里程字段状态汇总,直接区分源头没有可用总里程、疑似字段未映射、标准总里程为 0、标准总里程停留在旧日期、统计消费缺失等问题。",
"tags": []string{"Stats API"},
"x-table": "vehicle_realtime_snapshot,vehicle_realtime_location,vehicle_daily_mileage,vehicle_daily_mileage_source",
"parameters": dailyMetricDiagnosisReasonSummaryParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "Daily metric diagnostics field status summary",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DailyMetricDiagnosisFieldStatusSummaryPage"},
},
},
},
},
},
},
"/api/stats/data-sources": map[string]any{
"get": map[string]any{
"summary": "查询数据来源",
"description": "查询 vehicle_data_source 中自动发现的协议来源用于人工维护平台名称、source_code、可信优先级和启停状态。",
"tags": []string{"Data Source API"},
"x-table": "vehicle_data_source",
"parameters": []map[string]any{
{"name": "protocol", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"GB32960", "JT808", "YUTONG_MQTT"}}, "required": false},
{"name": "sourceIP", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "sourceCode", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "sourceKind", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"UNKNOWN", "PLATFORM", "DIRECT"}}, "required": false},
{"name": "sourceCodeMissing", "in": "query", "schema": map[string]any{"type": "boolean"}, "required": false},
{"name": "enabled", "in": "query", "schema": map[string]any{"type": "boolean"}, "required": false},
{"name": "includeTotal", "in": "query", "schema": map[string]any{"type": "boolean", "default": false}, "required": false},
{"name": "limit", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 1000, "default": 50}, "required": false},
{"name": "offset", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 1000000, "default": 0}, "required": false},
},
"responses": map[string]any{
"200": map[string]any{
"description": "Data source page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DataSourcePage"},
},
},
},
},
},
},
"/api/stats/data-sources/diagnostics": map[string]any{
"get": map[string]any{
"summary": "诊断 JT808 来源映射",
"description": "按来源 IP 汇总 jt808_registration 与 vehicle_identifier 的匹配情况,帮助判断缺失 source_code 的 808 来源应维护到哪个平台。",
"tags": []string{"Data Source API"},
"x-table": "vehicle_data_source,jt808_registration,vehicle_identifier",
"parameters": []map[string]any{
{"name": "protocol", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"JT808"}, "default": "JT808"}, "required": false},
{"name": "sourceIP", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "sourceCodeMissing", "in": "query", "schema": map[string]any{"type": "boolean", "default": true}, "required": false},
{"name": "includeTotal", "in": "query", "schema": map[string]any{"type": "boolean", "default": false}, "required": false},
{"name": "limit", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 1000, "default": 50}, "required": false},
{"name": "offset", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 1000000, "default": 0}, "required": false},
},
"responses": map[string]any{
"200": map[string]any{
"description": "Data source diagnostics page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DataSourceDiagnosticsPage"},
},
},
},
},
},
},
"/api/stats/data-sources/kind-suggestions": map[string]any{
"get": map[string]any{
"summary": "建议 JT808 来源类型",
"description": "基于来源活跃时长、注册手机号、vehicle_identifier 匹配和 source_code 情况,对 UNKNOWN 来源给出 PLATFORM/DIRECT/UNKNOWN 的只读建议。",
"tags": []string{"Data Source API"},
"x-table": "vehicle_data_source,jt808_registration,vehicle_identifier",
"parameters": []map[string]any{
{"name": "protocol", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"JT808"}, "default": "JT808"}, "required": false},
{"name": "sourceIP", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "sourceKind", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"UNKNOWN", "PLATFORM", "DIRECT"}, "default": "UNKNOWN"}, "required": false},
{"name": "sourceCodeMissing", "in": "query", "schema": map[string]any{"type": "boolean"}, "required": false},
{"name": "includeTotal", "in": "query", "schema": map[string]any{"type": "boolean", "default": false}, "required": false},
{"name": "limit", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 1000, "default": 50}, "required": false},
{"name": "offset", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 1000000, "default": 0}, "required": false},
},
"responses": map[string]any{
"200": map[string]any{
"description": "Data source kind suggestion page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DataSourceDiagnosticsPage"},
},
},
},
},
},
},
"/api/stats/data-sources/jt808-identity-gaps": map[string]any{
"get": map[string]any{
"summary": "诊断 JT808 未绑定设备",
"description": "列出 jt808_registration 中仍无法通过 phone/plate 关联 VIN 的设备,用于定位 0x0200 no_binding 和补充 vehicle_identifier 映射。",
"tags": []string{"Data Source API"},
"x-table": "jt808_registration,vehicle_identifier,vehicle_identity_binding,vehicle_data_source",
"parameters": []map[string]any{
{"name": "sourceIP", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "sourceCode", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "recentSeconds", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 2592000, "default": 86400}, "required": false},
{"name": "includeTotal", "in": "query", "schema": map[string]any{"type": "boolean", "default": false}, "required": false},
{"name": "limit", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 1000, "default": 50}, "required": false},
{"name": "offset", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 1000000, "default": 0}, "required": false},
},
"responses": map[string]any{
"200": map[string]any{
"description": "JT808 identity gap page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/JT808IdentityGapPage"},
},
},
},
},
},
},
"/api/stats/data-sources/jt808-mapping-gaps": map[string]any{
"get": map[string]any{
"summary": "诊断 JT808 来源映射缺口",
"description": "列出已配置来源平台但缺少当前 source_code 下 JT808_PHONE 到 VIN 映射的注册手机号,用于维护 vehicle_identifier 并提升 VIN 解析、在线统计和里程来源选举覆盖率。",
"tags": []string{"Data Source API"},
"x-table": "jt808_registration,vehicle_identifier,vehicle_data_source",
"parameters": []map[string]any{
{"name": "sourceIP", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "sourceCode", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "recentSeconds", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 2592000, "default": 86400}, "required": false},
{"name": "includeTotal", "in": "query", "schema": map[string]any{"type": "boolean", "default": false}, "required": false},
{"name": "limit", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 1000, "default": 50}, "required": false},
{"name": "offset", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 1000000, "default": 0}, "required": false},
},
"responses": map[string]any{
"200": map[string]any{
"description": "JT808 mapping gap page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/JT808MappingGapPage"},
},
},
},
},
},
},
"/api/stats/data-sources/{id}": map[string]any{
"patch": map[string]any{
"summary": "维护数据来源人工字段",
"description": "只允许更新 platform_name、source_code、source_kind、trust_priority、enabled、remarksource_ip、latest_seen_at 等运行态字段由接入链路自动维护。",
"tags": []string{"Data Source API"},
"parameters": []map[string]any{
{"name": "id", "in": "path", "schema": map[string]any{"type": "integer"}, "required": true},
},
"requestBody": map[string]any{
"required": true,
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/DataSourceUpdate"},
},
},
},
"responses": map[string]any{
"200": map[string]any{"description": "Updated"},
},
},
},
},
"components": map[string]any{
"schemas": map[string]any{
"SnapshotPage": pageSchema("#/components/schemas/SnapshotRow"),
"LocationPage": pageSchema("#/components/schemas/LocationRow"),
"DailyMetricPage": pageSchema("#/components/schemas/DailyMetricRow"),
"DailyMetricSourcePage": pageSchema("#/components/schemas/DailyMetricSourceRow"),
"DailyMetricSourceQualityPage": pageSchema("#/components/schemas/DailyMetricSourceQualityRow"),
"DailyMetricSourceSelectionPage": pageSchema("#/components/schemas/DailyMetricSourceSelectionRow"),
"DailyMetricDiagnosisPage": pageSchema("#/components/schemas/DailyMetricDiagnosisRow"),
"DailyMetricDiagnosisSummaryPage": map[string]any{
"type": "object",
"properties": map[string]any{
"items": map[string]any{"type": "array", "items": map[string]any{"$ref": "#/components/schemas/DailyMetricDiagnosisSummaryRow"}},
"total": integerSchema(3),
"active_total": integerSchema(256),
"actionable_issue_total": integerSchema(6),
},
},
"DailyMetricDiagnosisReasonSummaryPage": map[string]any{
"type": "object",
"properties": map[string]any{
"items": map[string]any{"type": "array", "items": map[string]any{"$ref": "#/components/schemas/DailyMetricDiagnosisReasonSummaryRow"}},
"total": integerSchema(5),
"vehicle_total": integerSchema(347),
"actionable_issue_total": integerSchema(9),
"pipeline_issue_total": integerSchema(0),
"source_data_issue_total": integerSchema(9),
},
},
"DailyMetricDiagnosisFieldStatusSummaryPage": map[string]any{
"type": "object",
"properties": map[string]any{
"items": map[string]any{"type": "array", "items": map[string]any{"$ref": "#/components/schemas/DailyMetricDiagnosisFieldStatusSummaryRow"}},
"total": integerSchema(7),
"vehicle_total": integerSchema(347),
"actionable_issue_total": integerSchema(9),
"pipeline_issue_total": integerSchema(2),
"source_data_issue_total": integerSchema(7),
},
},
"DataSourcePage": pageSchema("#/components/schemas/DataSourceRow"),
"DataSourceDiagnosticsPage": pageSchema("#/components/schemas/DataSourceDiagnosticRow"),
"JT808IdentityGapPage": pageSchema("#/components/schemas/JT808IdentityGapRow"),
"JT808MappingGapPage": pageSchema("#/components/schemas/JT808MappingGapRow"),
"SnapshotRow": map[string]any{
"type": "object",
"properties": map[string]any{
"protocol": stringSchema("GB32960"),
"vin": stringSchema("LB9A32A21R0LS1707"),
"plate": stringSchema("浙A12345"),
"event_time": stringSchema("2026-07-02 16:01:02.123"),
"received_at": stringSchema("2026-07-02 16:01:03.456"),
"event_id": stringSchema("event id"),
"updated_at": stringSchema("2026-07-02 16:01:04"),
},
},
"LocationRow": map[string]any{
"type": "object",
"properties": map[string]any{
"protocol": stringSchema("JT808"),
"vin": stringSchema("LKLG7C4E3NA774736"),
"plate": stringSchema("粤B98765"),
"event_time": stringSchema("2026-07-02 16:11:02.000"),
"latitude": numberSchema(30.123456),
"longitude": numberSchema(120.654321),
"speed_kmh": numberSchema(54.3),
"total_mileage_km": numberSchema(48798.9),
"total_mileage_event_time": stringSchema("2026-07-02 16:11:02.000"),
"soc_percent": numberSchema(81.5),
"altitude_m": numberSchema(19.0),
"direction_deg": numberSchema(88.0),
"alarm_flag": integerSchema(0),
"status_flag": integerSchema(3),
"received_at": stringSchema("2026-07-02 16:11:03.000"),
"event_id": stringSchema("event id"),
"updated_at": stringSchema("2026-07-02 16:11:04"),
},
},
"DailyMetricRow": map[string]any{
"type": "object",
"properties": map[string]any{
"vin": stringSchema("LA9GG64L7PBAF4001"),
"stat_date": stringSchema("2026-07-08"),
"protocol": stringSchema("JT808"),
"source_id": integerSchema(3),
"source_ip": stringSchema("115.231.168.135"),
"latest_source_endpoint": stringSchema("115.231.168.135:41561"),
"platform_name": stringSchema("G7 平台"),
"source_code": stringSchema("G7S"),
"source_kind": stringSchema("PLATFORM"),
"daily_mileage_km": numberSchema(23.1),
"latest_total_mileage_km": numberSchema(4123.9),
"updated_at": stringSchema("2026-07-08 13:30:57"),
},
},
"DailyMetricSourceRow": map[string]any{
"type": "object",
"properties": map[string]any{
"vin": stringSchema("LA9GG64L7PBAF4001"),
"stat_date": stringSchema("2026-07-08"),
"protocol": stringSchema("JT808"),
"source_key": stringSchema("JT808:13307795425@115.231.168.135"),
"source_ip": stringSchema("115.231.168.135"),
"source_endpoint": stringSchema("115.231.168.135:41561"),
"phone": stringSchema("13307795425"),
"platform_name": stringSchema("信达"),
"source_id": integerSchema(5),
"source_code": stringSchema("xinda"),
"source_kind": stringSchema("PLATFORM"),
"source_enabled": map[string]any{"type": "boolean", "example": true},
"trust_priority": integerSchema(10),
"first_total_mileage_km": numberSchema(4100.8),
"latest_total_mileage_km": numberSchema(4123.9),
"daily_mileage_km": numberSchema(23.1),
"sample_count": integerSchema(128),
"first_event_time": stringSchema("2026-07-08 00:01:00"),
"latest_event_time": stringSchema("2026-07-08 23:59:00"),
"quality_status": stringSchema("OK"),
"quality_reason": stringSchema("historical_source_baseline"),
"is_selected": map[string]any{"type": "boolean", "example": true},
"selection_status": stringSchema("selected"),
"selection_reason": stringSchema("selected_current_projection"),
"selection_action": stringSchema("当前来源已被投影到最终日里程表"),
"updated_at": stringSchema("2026-07-08 23:59:10"),
},
},
"DailyMetricSourceQualityRow": map[string]any{
"type": "object",
"properties": map[string]any{
"stat_date": stringSchema("2026-07-12"),
"protocol": stringSchema("JT808"),
"quality_status": stringSchema("INVALID_DELTA"),
"quality_reason": stringSchema("outside_daily_range"),
"source_count": integerSchema(3),
"vehicle_count": integerSchema(3),
"selected_count": integerSchema(0),
"sample_count": integerSchema(128),
"daily_mileage_km": numberSchema(12435.2),
},
},
"DailyMetricSourceSelectionRow": map[string]any{
"type": "object",
"properties": map[string]any{
"stat_date": stringSchema("2026-07-12"),
"protocol": stringSchema("JT808"),
"selection_status": stringSchema("not_selected"),
"selection_reason": stringSchema("lower_trust_priority_or_sample_count"),
"selection_action": stringSchema("来源质量可用,但被更高可信优先级、更多样本或更新时间更新的来源覆盖"),
"source_count": integerSchema(36),
"vehicle_count": integerSchema(31),
"selected_count": integerSchema(0),
"sample_count": integerSchema(3200),
"daily_mileage_km": numberSchema(812.5),
},
},
"DailyMetricDiagnosisRow": map[string]any{
"type": "object",
"properties": map[string]any{
"vin": stringSchema("LA9GG64L7PBAF4001"),
"stat_date": stringSchema("2026-07-12"),
"protocol": stringSchema("JT808"),
"plate": stringSchema("粤A12345"),
"platform_name": stringSchema("信达"),
"peer": stringSchema("115.231.168.135:47849"),
"snapshot_event_time": stringSchema("2026-07-12 10:01:02.000"),
"location_event_time": stringSchema("2026-07-12 10:01:02.000"),
"snapshot_updated_at": stringSchema("2026-07-12 10:01:03"),
"location_updated_at": stringSchema("2026-07-12 10:01:03"),
"realtime_total_mileage_km": numberSchema(25246.6),
"realtime_total_mileage_event_time": stringSchema("2026-07-12 10:01:02.000"),
"daily_mileage_km": numberSchema(0.3),
"daily_latest_total_mileage_km": numberSchema(25246.6),
"source_sample_count": integerSchema(36),
"ok_source_count": integerSchema(1),
"selectable_source_count": integerSchema(1),
"latest_stat_event_time": stringSchema("2026-07-12 10:01:02"),
"quality_statuses": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "example": []string{"OK"}},
"realtime_field_count": integerSchema(32),
"realtime_sample_fields": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "example": []string{"yutong_mqtt.data.latitude", "yutong_mqtt.data.longitude", "yutong_mqtt.data.meter_speed"}},
"realtime_mileage_field_status": map[string]any{"type": "string", "enum": dailyMetricMileageFieldStatusEnum(), "example": "no_candidate_mileage_field"},
"realtime_mileage_candidate_fields": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "example": []string{"yutong_mqtt.data.odometer"}},
"realtime_mileage_evidence": stringSchema("实时快照已有字段,但没有发现 mileage/odometer/odo 等疑似总里程字段"),
"diagnosis": stringSchema("OK"),
"reason": stringSchema("daily_metric_exists"),
"severity": stringSchema("ok"),
},
},
"DailyMetricDiagnosisSummaryRow": map[string]any{
"type": "object",
"properties": map[string]any{
"stat_date": stringSchema("2026-07-12"),
"protocol": stringSchema("JT808"),
"active_count": integerSchema(244),
"ok_count": integerSchema(244),
"missing_daily_count": integerSchema(0),
"no_source_sample_count": integerSchema(0),
"no_total_mileage_count": integerSchema(0),
"actionable_issue_count": integerSchema(0),
},
},
"DailyMetricDiagnosisReasonSummaryRow": map[string]any{
"type": "object",
"properties": map[string]any{
"stat_date": stringSchema("2026-07-12"),
"protocol": stringSchema("YUTONG_MQTT"),
"diagnosis": stringSchema("NO_TOTAL_MILEAGE"),
"reason": stringSchema("realtime_total_mileage_not_reported_on_stat_date"),
"count": integerSchema(7),
"severity": stringSchema("source_data"),
},
},
"DailyMetricDiagnosisFieldStatusSummaryRow": map[string]any{
"type": "object",
"properties": map[string]any{
"stat_date": stringSchema("2026-07-12"),
"protocol": stringSchema("YUTONG_MQTT"),
"diagnosis": stringSchema("NO_TOTAL_MILEAGE"),
"reason": stringSchema("realtime_total_mileage_missing"),
"realtime_mileage_field_status": map[string]any{"type": "string", "enum": dailyMetricMileageFieldStatusEnum(), "example": "no_candidate_mileage_field"},
"count": integerSchema(7),
"severity": stringSchema("source_data"),
"field_status_severity": stringSchema("source_data"),
"recommended_operator_action": stringSchema("当日实时数据缺少总里程字段,核对平台是否上报该字段以及协议字段映射是否覆盖"),
"field_status_action": stringSchema("实时字段中没有疑似里程字段,优先向源平台核对是否上报总里程"),
},
},
"DataSourceRow": map[string]any{
"type": "object",
"properties": map[string]any{
"id": integerSchema(3),
"protocol": stringSchema("JT808"),
"source_ip": stringSchema("115.231.168.135"),
"latest_source_endpoint": stringSchema("115.231.168.135:41561"),
"platform_name": stringSchema("G7 平台"),
"source_code": stringSchema("G7S"),
"source_kind": stringSchema("PLATFORM"),
"trust_priority": integerSchema(10),
"enabled": map[string]any{"type": "boolean", "example": true},
"first_seen_at": stringSchema("2026-07-09 14:48:30"),
"latest_seen_at": stringSchema("2026-07-12 01:16:04"),
"remark": stringSchema("可信来源"),
"updated_at": stringSchema("2026-07-12 01:16:05"),
},
},
"DataSourceDiagnosticRow": map[string]any{
"type": "object",
"properties": map[string]any{
"id": integerSchema(242190),
"protocol": stringSchema("JT808"),
"source_ip": stringSchema("117.132.194.31"),
"latest_source_endpoint": stringSchema("117.132.194.31:20471"),
"platform_name": stringSchema("G7 平台"),
"source_code": stringSchema("G7S"),
"source_kind": stringSchema("PLATFORM"),
"first_seen_at": stringSchema("2026-07-12 01:00:00"),
"latest_seen_at": stringSchema("2026-07-12 01:36:26"),
"active_span_seconds": integerSchema(2186),
"latest_seen_age_seconds": integerSchema(1800),
"registration_rows": integerSchema(5),
"phone_count": integerSchema(4),
"unknown_vin_rows": integerSchema(2),
"identifier_matched_phones": integerSchema(3),
"unmapped_phone_count": integerSchema(1),
"identifier_match_ratio": map[string]any{"type": "number", "example": 0.75},
"configured_source_code_matched_phones": integerSchema(3),
"configured_source_code_platform_name": stringSchema("东方北斗"),
"configured_source_code_conflict": map[string]any{"type": "boolean", "example": false},
"source_platform_name_mismatch": map[string]any{"type": "boolean", "example": true},
"matched_source_code_count": integerSchema(1),
"candidate_source_code": stringSchema("g7s"),
"candidate_platform_name": stringSchema("G7s"),
"matched_source_codes": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "example": []string{"g7s"}},
"matched_platform_names": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "example": []string{"G7s"}},
"sample_phones": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "example": []string{"13307795425"}},
"reason": stringSchema("candidate_available"),
"recommended_operator_action": stringSchema("可用候选 source_code执行 identity-import -sync-data-sources -apply 或在来源管理中确认"),
"suggested_source_kind": stringSchema("PLATFORM"),
"suggestion_confidence": stringSchema("HIGH"),
"suggestion_reason": stringSchema("single_source_code_with_many_phones_or_long_activity"),
},
},
"JT808IdentityGapRow": map[string]any{
"type": "object",
"properties": map[string]any{
"phone": stringSchema("13307795425"),
"device_id": stringSchema("TERM-001"),
"plate": stringSchema("沪A63305F"),
"vin": stringSchema("unknown"),
"source_ip": stringSchema("117.132.194.31"),
"source_endpoint": stringSchema("117.132.194.31:20471"),
"source_code": stringSchema("guangan_beidou"),
"platform_name": stringSchema("广安北斗"),
"source_kind": stringSchema("PLATFORM"),
"first_registered_at": stringSchema("2026-07-12 09:00:00"),
"latest_registered_at": stringSchema("2026-07-12 09:00:00"),
"latest_authenticated_at": stringSchema("2026-07-12 09:00:03"),
"latest_seen_at": stringSchema("2026-07-12 22:24:53"),
"latest_seen_age_seconds": integerSchema(32),
"reason": stringSchema("missing_phone_and_plate_binding"),
"recommended_operator_action": stringSchema("将该 phone 或 plate 维护到 vehicle_identifier并确认 VIN、source_code、oem"),
"raw_frame_query_path": stringSchema("/api/history/raw-frames?protocol=JT808&phone=13307795425&includeFields=true"),
"data_source_query_path": stringSchema("/api/stats/data-sources?protocol=JT808&sourceIP=117.132.194.31"),
},
},
"JT808MappingGapRow": map[string]any{
"type": "object",
"properties": map[string]any{
"phone": stringSchema("64646848246"),
"device_id": stringSchema("TERM-001"),
"plate": stringSchema("粤AG18312"),
"vin": stringSchema("LKLG7C4E8NA774778"),
"source_ip": stringSchema("115.159.85.149"),
"source_endpoint": stringSchema("115.159.85.149:16885"),
"source_code": stringSchema("dongfang_beidou"),
"platform_name": stringSchema("G7易流"),
"source_kind": stringSchema("PLATFORM"),
"identifier_vin": stringSchema(""),
"identifier_plate": stringSchema(""),
"matched_source_codes": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "example": []string{"g7s"}},
"matched_platform_names": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "example": []string{"G7s"}},
"latest_seen_at": stringSchema("2026-07-12 23:38:22"),
"latest_seen_age_seconds": integerSchema(32),
"reason": stringSchema("missing_source_phone_identifier"),
"recommended_action": stringSchema("按当前来源 source_code 维护 JT808_PHONE 到 VIN 的映射;如平台名与 source_code 不一致,先修正来源配置"),
"suggested_source_code": stringSchema("dongfang_beidou"),
"suggested_platform_name": stringSchema("G7易流"),
"suggested_identifier_type": stringSchema("JT808_PHONE"),
"suggested_identifier_value": stringSchema("64646848246"),
"suggested_vin": stringSchema("LKLG7C4E8NA774778"),
"suggested_plate": stringSchema("粤AG18312"),
"raw_frame_query_path": stringSchema("/api/history/raw-frames?protocol=JT808&phone=64646848246&includeFields=true"),
"data_source_query_path": stringSchema("/api/stats/data-sources?protocol=JT808&sourceIP=115.159.85.149"),
"vehicle_identifier_example": stringSchema("protocol=JT808, source_code=dongfang_beidou, identifier_type=JT808_PHONE, identifier_value=64646848246, vin=LKLG7C4E8NA774778, plate=粤AG18312, oem=G7易流"),
},
},
"DataSourceUpdate": map[string]any{
"type": "object",
"properties": map[string]any{
"platform_name": stringSchema("G7 平台"),
"source_code": stringSchema("G7S"),
"source_kind": stringSchema("PLATFORM"),
"trust_priority": integerSchema(10),
"enabled": map[string]any{"type": "boolean", "example": true},
"remark": stringSchema("可信来源"),
},
},
},
},
}
}
func dailyMetricSourceParameters() []map[string]any {
parameters := dailyMetricParameters()
parameters = append(parameters,
map[string]any{"name": "sourceIP", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
map[string]any{"name": "qualityStatus", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"OK", "NO_PREVIOUS_BASELINE", "INVALID_DELTA"}}, "required": false},
map[string]any{"name": "qualityReason", "in": "query", "schema": map[string]any{"type": "string", "example": "historical_source_baseline"}, "required": false},
map[string]any{"name": "selected", "in": "query", "schema": map[string]any{"type": "boolean"}, "required": false},
)
return parameters
}
func dailyMetricDiagnosisParameters() []map[string]any {
return []map[string]any{
{"name": "vin", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "protocol", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"GB32960", "JT808", "YUTONG_MQTT"}}, "required": false},
{"name": "date", "in": "query", "schema": map[string]any{"type": "string", "format": "date"}, "required": false, "description": "按东八区业务日期诊断;不传时默认服务当前日期。"},
{"name": "diagnosis", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"OK", "MISSING_DAILY", "NO_SOURCE_SAMPLE", "NO_TOTAL_MILEAGE"}}, "required": false},
{"name": "reason", "in": "query", "schema": map[string]any{"type": "string", "enum": dailyMetricDiagnosisReasonEnum()}, "required": false},
{"name": "severity", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"ok", "pipeline", "source_data"}}, "required": false},
{"name": "includeTotal", "in": "query", "schema": map[string]any{"type": "boolean", "default": false}, "required": false, "description": "默认 false不执行 COUNT(*)true 时返回精确总数。"},
{"name": "limit", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 1000, "default": 50}, "required": false},
{"name": "offset", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 1000000, "default": 0}, "required": false},
}
}
func dailyMetricDiagnosisSummaryParameters() []map[string]any {
return []map[string]any{
{"name": "vin", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "protocol", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"GB32960", "JT808", "YUTONG_MQTT"}}, "required": false},
{"name": "date", "in": "query", "schema": map[string]any{"type": "string", "format": "date"}, "required": false, "description": "按东八区业务日期诊断;不传时默认服务当前日期。"},
}
}
func dailyMetricDiagnosisReasonSummaryParameters() []map[string]any {
return []map[string]any{
{"name": "vin", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "protocol", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"GB32960", "JT808", "YUTONG_MQTT"}}, "required": false},
{"name": "date", "in": "query", "schema": map[string]any{"type": "string", "format": "date"}, "required": false, "description": "按东八区业务日期诊断;不传时默认服务当前日期。"},
{"name": "diagnosis", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"OK", "MISSING_DAILY", "NO_SOURCE_SAMPLE", "NO_TOTAL_MILEAGE"}}, "required": false},
{"name": "reason", "in": "query", "schema": map[string]any{"type": "string", "enum": dailyMetricDiagnosisReasonEnum()}, "required": false},
{"name": "severity", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"ok", "pipeline", "source_data"}}, "required": false},
}
}
func dailyMetricDiagnosisReasonEnum() []string {
return []string{
"daily_metric_exists",
"source_samples_all_invalid",
"source_samples_all_excluded",
"source_samples_exist_but_final_metric_missing",
"realtime_location_has_total_mileage_but_no_stat_sample",
"realtime_total_mileage_missing",
"realtime_total_mileage_non_positive",
"realtime_total_mileage_time_missing",
"realtime_total_mileage_not_reported_on_stat_date",
"realtime_active_without_total_mileage",
}
}
func dailyMetricMileageFieldStatusEnum() []string {
return []string{
"daily_metric_exists",
"source_sample_exists",
"standard_field_not_consumed",
"standard_field_non_positive",
"standard_field_time_missing",
"standard_field_stale",
"candidate_field_unmapped",
"mapped_protocol_field_without_fresh_evidence",
"no_candidate_mileage_field",
"no_realtime_fields",
}
}
func dailyMetricParameters() []map[string]any {
return []map[string]any{
{"name": "vin", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "protocol", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"GB32960", "JT808", "YUTONG_MQTT"}}, "required": false},
{"name": "dateFrom", "in": "query", "schema": map[string]any{"type": "string", "format": "date"}, "required": false},
{"name": "dateTo", "in": "query", "schema": map[string]any{"type": "string", "format": "date"}, "required": false},
{"name": "includeTotal", "in": "query", "schema": map[string]any{"type": "boolean", "default": false}, "required": false, "description": "默认 false不执行 COUNT(*)true 时返回精确总数。"},
{"name": "limit", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 1000, "default": 50}, "required": false},
{"name": "offset", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 1000000, "default": 0}, "required": false},
}
}
func commonRealtimeParameters() []map[string]any {
return []map[string]any{
{"name": "protocol", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"GB32960", "JT808", "YUTONG_MQTT"}}, "required": false},
{"name": "vin", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "plate", "in": "query", "schema": map[string]any{"type": "string"}, "required": false},
{"name": "includeTotal", "in": "query", "schema": map[string]any{"type": "boolean", "default": false}, "required": false, "description": "默认 false不执行 COUNT(*)true 时返回精确总数。"},
{"name": "limit", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 1000, "default": 50}, "required": false},
{"name": "offset", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "maximum": 1000000, "default": 0}, "required": false},
}
}
func pageSchema(itemRef string) map[string]any {
return map[string]any{
"type": "object",
"properties": map[string]any{
"items": map[string]any{"type": "array", "items": map[string]any{"$ref": itemRef}},
"total": map[string]any{"type": "integer", "example": 1, "description": "默认表示本页返回条数includeTotal=true 时表示精确总数。"},
"limit": integerSchema(50),
"offset": integerSchema(0),
},
}
}
func stringSchema(example string) map[string]any {
return map[string]any{"type": "string", "example": example}
}
func numberSchema(example float64) map[string]any {
return map[string]any{"type": "number", "example": example}
}
func integerSchema(example int64) map[string]any {
return map[string]any{"type": "integer", "example": example}
}
const swaggerHTML = `<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Lingniu Vehicle Realtime API</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
<script>
window.ui = SwaggerUIBundle({ url: "/openapi.json", dom_id: "#swagger-ui" });
</script>
</body>
</html>`