877 lines
48 KiB
Go
877 lines
48 KiB
Go
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、remark;source_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>`
|