fix(master-data): enforce authoritative vehicle totals
This commit is contained in:
@@ -146,8 +146,7 @@ func buildVehicleCoverageSQL(query url.Values) SQLQuery {
|
||||
if len(having) > 0 {
|
||||
havingSQL = ` HAVING ` + strings.Join(having, " AND ") + ` `
|
||||
}
|
||||
vehicleSetSQL := `SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> '' ` +
|
||||
`UNION SELECT vin FROM vehicle_realtime_snapshot WHERE vin IS NOT NULL AND vin <> ''`
|
||||
vehicleSetSQL := `SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> ''`
|
||||
groupSQL := `FROM (` + vehicleSetSQL + `) v ` +
|
||||
`LEFT JOIN vehicle_identity_binding b ON b.vin = v.vin ` +
|
||||
`LEFT JOIN vehicle_realtime_snapshot s ON s.vin = v.vin ` +
|
||||
@@ -246,8 +245,7 @@ func buildVehicleCoverageSummarySQL(query url.Values) SQLQuery {
|
||||
if len(having) > 0 {
|
||||
havingSQL = ` HAVING ` + strings.Join(having, " AND ") + ` `
|
||||
}
|
||||
vehicleSetSQL := `SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> '' ` +
|
||||
`UNION SELECT vin FROM vehicle_realtime_snapshot WHERE vin IS NOT NULL AND vin <> ''`
|
||||
vehicleSetSQL := `SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> ''`
|
||||
groupSQL := `SELECT v.vin, ` +
|
||||
`COUNT(DISTINCT s.protocol) AS source_count, ` +
|
||||
`COUNT(DISTINCT CASE WHEN s.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN s.protocol END) AS online_source_count, ` +
|
||||
|
||||
@@ -132,22 +132,26 @@ func (s *ProductionStore) serviceStatusStats(ctx context.Context) ([]ServiceStat
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *ProductionStore) VehicleServiceSummary(ctx context.Context) (VehicleServiceSummary, error) {
|
||||
func buildVehicleServiceSummarySQL() string {
|
||||
canonicalSourceCount := strconv.Itoa(len(canonicalVehicleProtocols))
|
||||
rows, err := s.db.QueryContext(ctx, `SELECT
|
||||
return `SELECT
|
||||
COUNT(*) AS total_vehicles,
|
||||
COALESCE(SUM(CASE WHEN bound = 1 THEN 1 ELSE 0 END), 0) AS bound_vehicles,
|
||||
COALESCE(SUM(CASE WHEN online_source_count > 0 THEN 1 ELSE 0 END), 0) AS online_vehicles,
|
||||
COALESCE(SUM(CASE WHEN source_count = 1 THEN 1 ELSE 0 END), 0) AS single_source_vehicles,
|
||||
COALESCE(SUM(CASE WHEN source_count > 1 THEN 1 ELSE 0 END), 0) AS multi_source_vehicles,
|
||||
COALESCE(SUM(CASE WHEN source_count = 0 THEN 1 ELSE 0 END), 0) AS no_data_vehicles,
|
||||
COALESCE(SUM(CASE WHEN bound = 0 THEN 1 ELSE 0 END), 0) AS identity_required_vehicles,
|
||||
(SELECT COUNT(DISTINCT unbound_source.vin)
|
||||
FROM vehicle_realtime_snapshot unbound_source
|
||||
LEFT JOIN vehicle_identity_binding bound_identity ON bound_identity.vin = unbound_source.vin
|
||||
WHERE unbound_source.vin IS NOT NULL AND unbound_source.vin <> ''
|
||||
AND bound_identity.vin IS NULL) AS identity_required_vehicles,
|
||||
COALESCE(SUM(CASE WHEN archive_complete = 0 THEN 1 ELSE 0 END), 0) AS archive_incomplete_vehicles,
|
||||
COALESCE(SUM(CASE WHEN missing_plate = 1 THEN 1 ELSE 0 END), 0) AS missing_plate_vehicles,
|
||||
COALESCE(SUM(CASE WHEN missing_phone = 1 THEN 1 ELSE 0 END), 0) AS missing_phone_vehicles,
|
||||
COALESCE(SUM(CASE WHEN missing_oem = 1 THEN 1 ELSE 0 END), 0) AS missing_oem_vehicles,
|
||||
COALESCE(SUM(CASE WHEN bound = 1 AND source_count = `+canonicalSourceCount+` AND online_source_count = source_count THEN 1 ELSE 0 END), 0) AS healthy_vehicles,
|
||||
COALESCE(SUM(CASE WHEN bound = 1 AND source_count > 0 AND online_source_count > 0 AND (online_source_count < source_count OR source_count < `+canonicalSourceCount+`) THEN 1 ELSE 0 END), 0) AS degraded_vehicles,
|
||||
COALESCE(SUM(CASE WHEN bound = 1 AND source_count = ` + canonicalSourceCount + ` AND online_source_count = source_count THEN 1 ELSE 0 END), 0) AS healthy_vehicles,
|
||||
COALESCE(SUM(CASE WHEN bound = 1 AND source_count > 0 AND online_source_count > 0 AND (online_source_count < source_count OR source_count < ` + canonicalSourceCount + `) THEN 1 ELSE 0 END), 0) AS degraded_vehicles,
|
||||
COALESCE(SUM(CASE WHEN bound = 1 AND source_count > 0 AND online_source_count = 0 THEN 1 ELSE 0 END), 0) AS offline_vehicles
|
||||
FROM (
|
||||
SELECT v.vin,
|
||||
@@ -164,13 +168,15 @@ CASE WHEN NULLIF(MAX(NULLIF(b.phone, '')), '') IS NULL THEN 1 ELSE 0 END AS miss
|
||||
CASE WHEN NULLIF(MAX(NULLIF(b.oem, '')), '') IS NULL THEN 1 ELSE 0 END AS missing_oem
|
||||
FROM (
|
||||
SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> ''
|
||||
UNION
|
||||
SELECT vin FROM vehicle_realtime_snapshot WHERE vin IS NOT NULL AND vin <> ''
|
||||
) v
|
||||
LEFT JOIN vehicle_identity_binding b ON b.vin = v.vin
|
||||
LEFT JOIN vehicle_realtime_snapshot s ON s.vin = v.vin
|
||||
GROUP BY v.vin
|
||||
) vehicle_service_summary`)
|
||||
) vehicle_service_summary`
|
||||
}
|
||||
|
||||
func (s *ProductionStore) VehicleServiceSummary(ctx context.Context) (VehicleServiceSummary, error) {
|
||||
rows, err := s.db.QueryContext(ctx, buildVehicleServiceSummarySQL())
|
||||
if err != nil {
|
||||
return VehicleServiceSummary{}, err
|
||||
}
|
||||
@@ -223,9 +229,7 @@ GROUP BY v.vin
|
||||
}
|
||||
|
||||
func (s *ProductionStore) missingSourceStats(ctx context.Context) ([]MissingSourceStat, error) {
|
||||
const vehicleSetSQL = `SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> ''
|
||||
UNION
|
||||
SELECT vin FROM vehicle_realtime_snapshot WHERE vin IS NOT NULL AND vin <> ''`
|
||||
const vehicleSetSQL = `SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> ''`
|
||||
out := make([]MissingSourceStat, 0, len(canonicalVehicleProtocols))
|
||||
for _, protocol := range canonicalVehicleProtocols {
|
||||
var count int
|
||||
|
||||
@@ -90,7 +90,6 @@ func TestBuildVehicleCoverageSQLIncludesNoDataVehicles(t *testing.T) {
|
||||
built := buildVehicleCoverageSQL(query)
|
||||
for _, want := range []string{
|
||||
"vehicle_identity_binding",
|
||||
"UNION",
|
||||
"vehicle_realtime_snapshot",
|
||||
"LEFT JOIN vehicle_realtime_snapshot s ON s.vin = v.vin",
|
||||
"COUNT(DISTINCT s.protocol) = 0",
|
||||
@@ -100,6 +99,10 @@ func TestBuildVehicleCoverageSQLIncludesNoDataVehicles(t *testing.T) {
|
||||
t.Fatalf("no-data coverage SQL missing %q: %s / %s", want, built.Text, built.CountText)
|
||||
}
|
||||
}
|
||||
if strings.Contains(built.Text, "UNION SELECT vin FROM vehicle_realtime_snapshot") ||
|
||||
strings.Contains(built.CountText, "UNION SELECT vin FROM vehicle_realtime_snapshot") {
|
||||
t.Fatalf("formal vehicle coverage must not promote unbound realtime identities into the vehicle population: %s / %s", built.Text, built.CountText)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildVehicleCoverageSQLFiltersMissingProtocol(t *testing.T) {
|
||||
@@ -173,11 +176,32 @@ func TestBuildVehicleCoverageSummarySQL(t *testing.T) {
|
||||
if strings.Contains(built.Text, "LIMIT") || strings.Contains(built.Text, "OFFSET") {
|
||||
t.Fatalf("summary SQL should not paginate: %s", built.Text)
|
||||
}
|
||||
if strings.Contains(built.Text, "UNION SELECT vin FROM vehicle_realtime_snapshot") {
|
||||
t.Fatalf("formal vehicle coverage summary must use the authoritative identity population: %s", built.Text)
|
||||
}
|
||||
if len(built.Args) != 6 || built.Args[0] != "%粤A%" {
|
||||
t.Fatalf("args = %#v", built.Args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildVehicleServiceSummarySQLKeepsFormalAndUnboundPopulationsSeparate(t *testing.T) {
|
||||
built := buildVehicleServiceSummarySQL()
|
||||
for _, want := range []string{
|
||||
"SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> ''",
|
||||
"SELECT COUNT(DISTINCT unbound_source.vin)",
|
||||
"LEFT JOIN vehicle_identity_binding bound_identity ON bound_identity.vin = unbound_source.vin",
|
||||
"bound_identity.vin IS NULL",
|
||||
} {
|
||||
if !strings.Contains(built, want) {
|
||||
t.Fatalf("vehicle service summary SQL missing %q: %s", want, built)
|
||||
}
|
||||
}
|
||||
if strings.Contains(built, "UNION\nSELECT vin FROM vehicle_realtime_snapshot") ||
|
||||
strings.Contains(built, "UNION SELECT vin FROM vehicle_realtime_snapshot") {
|
||||
t.Fatalf("vehicle service total must not include unbound realtime-only identities: %s", built)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRealtimeLocationSQL(t *testing.T) {
|
||||
query := url.Values{"vin": {"粤A"}, "protocol": {"GB32960"}, "limit": {"10"}, "offset": {"20"}}
|
||||
built := buildRealtimeLocationSQL(query)
|
||||
|
||||
Reference in New Issue
Block a user