feat(monitor): aggregate nationwide fleet by province

This commit is contained in:
lingniu
2026-07-16 18:36:31 +08:00
parent ea4d576793
commit 21d1baada5
9 changed files with 598 additions and 32 deletions

View File

@@ -25,13 +25,14 @@ type MonitorSummary struct {
}
type MonitorMapResponse struct {
Mode string `json:"mode"`
Zoom int `json:"zoom"`
Total int `json:"total"`
Truncated bool `json:"truncated"`
Points []MonitorMapPoint `json:"points"`
Clusters []MonitorMapCluster `json:"clusters"`
AsOf string `json:"asOf"`
Mode string `json:"mode"`
Zoom int `json:"zoom"`
Total int `json:"total"`
Truncated bool `json:"truncated"`
Points []MonitorMapPoint `json:"points"`
Clusters []MonitorMapCluster `json:"clusters"`
ProvincePoints []MonitorMapProvincePoint `json:"provincePoints"`
AsOf string `json:"asOf"`
}
type MonitorWorkspaceResponse struct {
@@ -69,6 +70,16 @@ type MonitorMapCluster struct {
Unknown int `json:"unknown"`
}
// MonitorMapProvincePoint is deliberately compact and identity-free. At the
// nationwide zoom the browser groups these already-scoped coordinates with
// AMap's cached administrative boundary node, while Clusters remain available
// as a no-blank-map fallback if the boundary resource cannot be loaded.
type MonitorMapProvincePoint struct {
Longitude float64 `json:"longitude"`
Latitude float64 `json:"latitude"`
Status string `json:"status"`
}
type TrackPlaybackResponse struct {
VIN string `json:"vin"`
Plate string `json:"plate"`

View File

@@ -373,6 +373,7 @@ const (
monitorVehicleLimit = 10000
monitorPointLimit = 2000
monitorPointZoom = 11
monitorProvinceMaxZoom = 5
monitorClusterGridPixels = 64
vehicleSearchKeywordLimit = 100
)
@@ -606,17 +607,19 @@ func buildMonitorMapResponse(vehicles Page[VehicleRealtimeRow], query url.Values
if zoom > 20 {
zoom = 20
}
provinceMode := zoom <= monitorProvinceMaxZoom
bounds, hasBounds, err := parseMonitorBounds(query.Get("bounds"))
if err != nil {
return MonitorMapResponse{}, err
}
result := MonitorMapResponse{
Mode: "clusters",
Zoom: zoom,
Truncated: vehicles.Total > len(vehicles.Items),
Points: []MonitorMapPoint{},
Clusters: []MonitorMapCluster{},
AsOf: time.Now().UTC().Format(time.RFC3339),
Mode: "clusters",
Zoom: zoom,
Truncated: vehicles.Total > len(vehicles.Items),
Points: []MonitorMapPoint{},
Clusters: []MonitorMapCluster{},
ProvincePoints: []MonitorMapProvincePoint{},
AsOf: time.Now().UTC().Format(time.RFC3339),
}
points := make([]MonitorMapPoint, 0, len(vehicles.Items))
for _, vehicle := range vehicles.Items {
@@ -627,7 +630,17 @@ func buildMonitorMapResponse(vehicles Page[VehicleRealtimeRow], query url.Values
continue
}
result.Total++
if hasBounds && !bounds.contains(vehicle.Longitude, vehicle.Latitude) {
status := monitorVehicleStatus(vehicle)
if provinceMode {
result.ProvincePoints = append(result.ProvincePoints, MonitorMapProvincePoint{
Longitude: vehicle.Longitude,
Latitude: vehicle.Latitude,
Status: status,
})
}
// Nationwide province counts describe the complete filtered fleet, not
// only the current slippy-map rectangle. Bounds resume after drill-down.
if hasBounds && !provinceMode && !bounds.contains(vehicle.Longitude, vehicle.Latitude) {
continue
}
points = append(points, MonitorMapPoint{
@@ -637,7 +650,7 @@ func buildMonitorMapResponse(vehicles Page[VehicleRealtimeRow], query url.Values
ReportIntervalMs: vehicle.ReportIntervalMs,
LocationSource: vehicle.LocationSource,
LocationConflict: vehicle.LocationConflict,
Status: monitorVehicleStatus(vehicle),
Status: status,
})
}
if zoom >= monitorPointZoom && len(points) <= monitorPointLimit {
@@ -708,6 +721,9 @@ func buildMonitorMapResponse(vehicles Page[VehicleRealtimeRow], query url.Values
result.Mode = "points"
}
}
if provinceMode {
result.Mode = "provinces"
}
return result, nil
}

View File

@@ -541,6 +541,33 @@ func TestMonitorMapUsesMeaningfulClustersAndReleasesPointsAtDetailZoom(t *testin
}
}
func TestMonitorMapNationwideReturnsScopedProvinceSeedsAndGridFallback(t *testing.T) {
vehicles := Page[VehicleRealtimeRow]{Items: []VehicleRealtimeRow{
{VIN: "GUANGDONG-1", Online: true, LocationAvailable: true, Longitude: 113.260, Latitude: 23.130},
{VIN: "GUANGDONG-2", Online: false, LocationAvailable: true, Longitude: 113.265, Latitude: 23.135, LastSeen: "2026-07-15T08:00:00+08:00"},
{VIN: "OUTSIDE-STATUS", Online: true, LocationAvailable: true, Longitude: 121.47, Latitude: 31.23, SpeedKmh: 20},
{VIN: "NO-LOCATION", Online: true},
}, Total: 4, Limit: 4}
result, err := buildMonitorMapResponse(vehicles, url.Values{
"zoom": {"5"},
"status": {"offline"},
"bounds": {"113,22,114,24"},
})
if err != nil {
t.Fatal(err)
}
if result.Mode != "provinces" || result.Total != 1 || len(result.ProvincePoints) != 1 {
t.Fatalf("nationwide response must expose only filtered, located province seeds: %+v", result)
}
if result.ProvincePoints[0].Status != "offline" || result.ProvincePoints[0].Longitude != 113.265 {
t.Fatalf("unexpected province seed: %+v", result.ProvincePoints[0])
}
if len(result.Points)+len(result.Clusters) == 0 {
t.Fatal("nationwide response must retain a grid fallback when AMap district data is unavailable")
}
}
func BenchmarkMonitorMapTenThousandVehicles(b *testing.B) {
vehicles := syntheticMonitorVehicles(10_000)
query := url.Values{"zoom": {"5"}}