Files
lingniu-vehicle-ingest/go/vehicle-gateway/internal/realtime/openapi.go
2026-07-03 11:34:52 +08:00

219 lines
8.4 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, vehicle_realtime_location, and vehicle_realtime_kv.",
},
"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/realtime/kv": map[string]any{
"get": map[string]any{
"summary": "查询车辆实时 KV 字段",
"description": "从 vehicle_realtime_kv 查询实时字段投影支持协议、VIN、domain、field 过滤和分页。适合 UI 局部刷新、快速字段查询和统计输入。",
"tags": []string{"Realtime KV API"},
"x-table": "vehicle_realtime_kv",
"parameters": kvRealtimeParameters(),
"responses": map[string]any{
"200": map[string]any{
"description": "KV page",
"content": map[string]any{
"application/json": map[string]any{
"schema": map[string]any{"$ref": "#/components/schemas/KVPage"},
},
},
},
},
},
},
},
"components": map[string]any{
"schemas": map[string]any{
"SnapshotPage": pageSchema("#/components/schemas/SnapshotRow"),
"LocationPage": pageSchema("#/components/schemas/LocationRow"),
"KVPage": pageSchema("#/components/schemas/KVRow"),
"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),
"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"),
},
},
"KVRow": map[string]any{
"type": "object",
"properties": map[string]any{
"protocol": stringSchema("GB32960"),
"vin": stringSchema("LNXNEGRR0SR321372"),
"domain": stringSchema("gd_fc_stack"),
"field": stringSchema("stack_water_outlet_temp_c"),
"value": stringSchema("63"),
"value_type": stringSchema("number"),
"event_time": stringSchema("2026-07-03 11:18:45.000"),
"received_at": stringSchema("2026-07-03 11:18:45.123"),
"event_id": stringSchema("event id"),
"updated_at": stringSchema("2026-07-03 11:18:46"),
},
},
},
},
}
}
func kvRealtimeParameters() []map[string]any {
params := commonRealtimeParameters()
params = append(params,
map[string]any{"name": "domain", "in": "query", "schema": map[string]any{"type": "string", "example": "gd_fc_stack"}, "required": false},
map[string]any{"name": "field", "in": "query", "schema": map[string]any{"type": "string", "example": "stack_water_outlet_temp_c"}, "required": false},
)
return params
}
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>`