feat(platform): filter vehicle archive gaps

This commit is contained in:
lingniu
2026-07-04 09:50:41 +08:00
parent b127b22a4b
commit b5a72ce0ec
11 changed files with 174 additions and 26 deletions

View File

@@ -111,6 +111,9 @@ func buildVehicleCoverageSQL(query url.Values) SQLQuery {
case "incomplete":
having = append(having, "(v.vin IS NULL OR v.vin = '' OR COALESCE(NULLIF(MAX(NULLIF(s.plate, '')), ''), NULLIF(MAX(NULLIF(b.plate, '')), '')) IS NULL OR NULLIF(MAX(NULLIF(b.phone, '')), '') IS NULL OR NULLIF(MAX(NULLIF(b.oem, '')), '') IS NULL)")
}
if predicate := archiveMissingFieldPredicate(query.Get("archiveMissing")); predicate != "" {
having = append(having, predicate)
}
switch strings.TrimSpace(query.Get("serviceStatus")) {
case "identity_required":
having = append(having, "MAX(CASE WHEN b.vin IS NOT NULL AND b.vin <> '' THEN 1 ELSE 0 END) = 0")
@@ -209,6 +212,9 @@ func buildVehicleCoverageSummarySQL(query url.Values) SQLQuery {
case "incomplete":
having = append(having, "(v.vin IS NULL OR v.vin = '' OR COALESCE(NULLIF(MAX(NULLIF(s.plate, '')), ''), NULLIF(MAX(NULLIF(b.plate, '')), '')) IS NULL OR NULLIF(MAX(NULLIF(b.phone, '')), '') IS NULL OR NULLIF(MAX(NULLIF(b.oem, '')), '') IS NULL)")
}
if predicate := archiveMissingFieldPredicate(query.Get("archiveMissing")); predicate != "" {
having = append(having, predicate)
}
switch strings.TrimSpace(query.Get("serviceStatus")) {
case "identity_required":
having = append(having, "MAX(CASE WHEN b.vin IS NOT NULL AND b.vin <> '' THEN 1 ELSE 0 END) = 0")
@@ -243,7 +249,10 @@ func buildVehicleCoverageSummarySQL(query url.Values) SQLQuery {
`AND COALESCE(NULLIF(MAX(NULLIF(s.plate, '')), ''), NULLIF(MAX(NULLIF(b.plate, '')), '')) IS NOT NULL ` +
`AND NULLIF(MAX(NULLIF(b.phone, '')), '') IS NOT NULL ` +
`AND NULLIF(MAX(NULLIF(b.oem, '')), '') IS NOT NULL ` +
`THEN 1 ELSE 0 END AS archive_complete ` +
`THEN 1 ELSE 0 END AS archive_complete, ` +
`CASE WHEN COALESCE(NULLIF(MAX(NULLIF(s.plate, '')), ''), NULLIF(MAX(NULLIF(b.plate, '')), '')) IS NULL THEN 1 ELSE 0 END AS missing_plate, ` +
`CASE WHEN NULLIF(MAX(NULLIF(b.phone, '')), '') IS NULL THEN 1 ELSE 0 END AS missing_phone, ` +
`CASE WHEN NULLIF(MAX(NULLIF(b.oem, '')), '') IS NULL THEN 1 ELSE 0 END AS missing_oem ` +
`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 ` +
@@ -256,12 +265,28 @@ func buildVehicleCoverageSummarySQL(query url.Values) SQLQuery {
`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 unbound_vehicles, ` +
`COALESCE(SUM(CASE WHEN archive_complete = 0 THEN 1 ELSE 0 END), 0) AS archive_incomplete_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 ` +
`FROM (` + groupSQL + `) vehicle_coverage_summary`,
Args: args,
}
}
func archiveMissingFieldPredicate(field string) string {
switch strings.TrimSpace(field) {
case "plate":
return "COALESCE(NULLIF(MAX(NULLIF(s.plate, '')), ''), NULLIF(MAX(NULLIF(b.plate, '')), '')) IS NULL"
case "phone":
return "NULLIF(MAX(NULLIF(b.phone, '')), '') IS NULL"
case "oem":
return "NULLIF(MAX(NULLIF(b.oem, '')), '') IS NULL"
default:
return ""
}
}
func buildRealtimeLocationSQL(query url.Values) SQLQuery {
limit := parsePositive(query.Get("limit"), 20)
offset := parsePositive(query.Get("offset"), 0)