feat(platform): filter vehicle coverage

This commit is contained in:
lingniu
2026-07-03 21:59:03 +08:00
parent 110041a9db
commit 2d064e3c53
5 changed files with 101 additions and 7 deletions

View File

@@ -45,6 +45,7 @@ func buildVehicleCoverageSQL(query url.Values) SQLQuery {
offset := parsePositive(query.Get("offset"), 0)
args := []any{}
where := []string{"s.vin IS NOT NULL", "s.vin <> ''"}
having := []string{}
if keyword := strings.TrimSpace(query.Get("keyword")); keyword != "" {
where = append(where, "(s.vin LIKE ? OR s.plate LIKE ? OR b.vin LIKE ? OR b.plate LIKE ? OR b.phone LIKE ? OR b.oem LIKE ?)")
like := "%" + keyword + "%"
@@ -54,21 +55,44 @@ func buildVehicleCoverageSQL(query url.Values) SQLQuery {
where = append(where, "s.protocol = ?")
args = append(args, protocol)
}
switch strings.TrimSpace(query.Get("coverage")) {
case "single":
having = append(having, "COUNT(DISTINCT s.protocol) = 1")
case "multi":
having = append(having, "COUNT(DISTINCT s.protocol) > 1")
}
switch strings.TrimSpace(query.Get("online")) {
case "online":
having = append(having, "COUNT(DISTINCT CASE WHEN s.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN s.protocol END) > 0")
case "offline":
having = append(having, "COUNT(DISTINCT CASE WHEN s.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN s.protocol END) = 0")
}
switch strings.TrimSpace(query.Get("bindingStatus")) {
case "bound":
having = append(having, "MAX(CASE WHEN b.vin IS NOT NULL AND b.vin <> '' THEN 1 ELSE 0 END) = 1")
case "unbound":
having = append(having, "MAX(CASE WHEN b.vin IS NOT NULL AND b.vin <> '' THEN 1 ELSE 0 END) = 0")
}
args = append(args, limit, offset)
havingSQL := ""
if len(having) > 0 {
havingSQL = ` HAVING ` + strings.Join(having, " AND ") + ` `
}
return SQLQuery{
Text: `SELECT s.vin, ` +
`COALESCE(NULLIF(MAX(NULLIF(s.plate, '')), ''), b.plate, '') AS plate, ` +
`COALESCE(b.phone, '') AS phone, COALESCE(b.oem, '') AS oem, ` +
`COALESCE(GROUP_CONCAT(DISTINCT s.protocol ORDER BY s.protocol SEPARATOR ','), '') AS protocols, ` +
`COUNT(DISTINCT s.protocol) AS source_count, ` +
`SUM(CASE WHEN s.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN 1 ELSE 0 END) AS online_source_count, ` +
`CASE WHEN SUM(CASE WHEN s.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN 1 ELSE 0 END) > 0 THEN 1 ELSE 0 END AS online, ` +
`COUNT(DISTINCT CASE WHEN s.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN s.protocol END) AS online_source_count, ` +
`CASE WHEN COUNT(DISTINCT CASE WHEN s.updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) THEN s.protocol END) > 0 THEN 1 ELSE 0 END AS online, ` +
`COALESCE(DATE_FORMAT(MAX(s.updated_at), '%Y-%m-%d %H:%i:%s'), '') AS last_seen, ` +
`CASE WHEN b.vin IS NOT NULL AND b.vin <> '' THEN 'bound' ELSE 'unbound' END AS binding_status ` +
`FROM vehicle_realtime_snapshot s ` +
`LEFT JOIN vehicle_identity_binding b ON b.vin = s.vin ` +
`WHERE ` + strings.Join(where, " AND ") + ` ` +
`GROUP BY s.vin, b.plate, b.phone, b.oem, b.vin ` +
havingSQL +
`ORDER BY MAX(s.updated_at) DESC LIMIT ? OFFSET ?`,
Args: args,
}