feat(platform): expose source slots in realtime vehicles

This commit is contained in:
lingniu
2026-07-04 07:39:09 +08:00
parent f93c8d6ccc
commit 999d36c94d
8 changed files with 40 additions and 6 deletions

View File

@@ -679,6 +679,18 @@ func TestHandlerVehicleRealtime(t *testing.T) {
if len(body.Data.Items) == 0 || body.Data.Items[0].ServiceStatus == nil {
t.Fatalf("realtime vehicle row should include canonical serviceStatus: %s", rec.Body.String())
}
byProtocol := map[string]VehicleSourceStatus{}
for _, source := range body.Data.Items[0].SourceStatus {
byProtocol[source.Protocol] = source
}
for _, protocol := range []string{"GB32960", "JT808", "YUTONG_MQTT"} {
if _, ok := byProtocol[protocol]; !ok {
t.Fatalf("realtime row should expose canonical source slot %s, got %+v body=%s", protocol, body.Data.Items[0].SourceStatus, rec.Body.String())
}
}
if !byProtocol["JT808"].Online || byProtocol["YUTONG_MQTT"].Online || byProtocol["YUTONG_MQTT"].HasRealtime {
t.Fatalf("realtime source slots should expose online and missing evidence, got %+v", byProtocol)
}
}
func TestHandlerVehicleRealtimeFiltersServiceStatus(t *testing.T) {

View File

@@ -321,6 +321,13 @@ func (m *MockStore) VehicleRealtime(_ context.Context, query url.Values) (Page[V
items := make([]VehicleRealtimeRow, 0, len(byVIN))
for _, row := range byVIN {
sort.Strings(row.Protocols)
onlineProtocols := make([]string, 0, row.OnlineSourceCount)
for _, location := range rows {
if location.VIN == row.VIN && location.LastSeen >= "2026-07-03 20:11:00" {
onlineProtocols = append(onlineProtocols, location.Protocol)
}
}
row.SourceStatus = buildVehicleCoverageSourceStatus(row.Protocols, onlineProtocols, row.LastSeen)
row.ServiceStatus = buildRealtimeServiceStatus(*row)
switch strings.TrimSpace(query.Get("online")) {
case "online":

View File

@@ -197,6 +197,7 @@ type VehicleRealtimeRow struct {
Phone string `json:"phone"`
OEM string `json:"oem"`
Protocols []string `json:"protocols"`
SourceStatus []VehicleSourceStatus `json:"sourceStatus"`
SourceCount int `json:"sourceCount"`
OnlineSourceCount int `json:"onlineSourceCount"`
Online bool `json:"online"`

View File

@@ -328,6 +328,7 @@ func buildVehicleRealtimeSQL(query url.Values) SQLQuery {
`COALESCE(NULLIF(MAX(NULLIF(l.plate, '')), ''), b.plate, '') AS plate, ` +
`COALESCE(b.phone, '') AS phone, COALESCE(b.oem, '') AS oem, ` +
`COALESCE(GROUP_CONCAT(DISTINCT l.protocol ORDER BY l.protocol SEPARATOR ','), '') AS protocols, ` +
`COALESCE(GROUP_CONCAT(DISTINCT CASE WHEN l.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN l.protocol END ORDER BY l.protocol SEPARATOR ','), '') AS online_protocols, ` +
`COUNT(DISTINCT l.protocol) AS source_count, ` +
`COUNT(DISTINCT CASE WHEN l.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN l.protocol END) AS online_source_count, ` +
`CASE WHEN COUNT(DISTINCT CASE WHEN l.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN l.protocol END) > 0 THEN 1 ELSE 0 END AS online, ` +

View File

@@ -339,12 +339,14 @@ func (s *ProductionStore) VehicleRealtime(ctx context.Context, query url.Values)
for rows.Next() {
var row VehicleRealtimeRow
var protocols string
var onlineProtocols string
var online int
var longitude, latitude, speed, soc, mileage string
if err := rows.Scan(&row.VIN, &row.Plate, &row.Phone, &row.OEM, &protocols, &row.SourceCount, &row.OnlineSourceCount, &online, &row.BindingStatus, &row.PrimaryProtocol, &longitude, &latitude, &speed, &soc, &mileage, &row.LastSeen); err != nil {
if err := rows.Scan(&row.VIN, &row.Plate, &row.Phone, &row.OEM, &protocols, &onlineProtocols, &row.SourceCount, &row.OnlineSourceCount, &online, &row.BindingStatus, &row.PrimaryProtocol, &longitude, &latitude, &speed, &soc, &mileage, &row.LastSeen); err != nil {
return Page[VehicleRealtimeRow]{}, err
}
row.Protocols = splitCSV(protocols)
row.SourceStatus = buildVehicleCoverageSourceStatus(row.Protocols, splitCSV(onlineProtocols), row.LastSeen)
row.Online = online == 1
row.ServiceStatus = buildRealtimeServiceStatus(row)
row.Longitude = parseFloatString(longitude)