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 = ` Lingniu Vehicle Realtime API
`