refine Semi UI vehicle directory

This commit is contained in:
lingniu
2026-07-18 06:47:39 +08:00
parent 4bca0482c1
commit 4b3bb3b3dd
8 changed files with 475 additions and 184 deletions

View File

@@ -73,9 +73,8 @@ func buildVehicleCoverageSQL(query url.Values) SQLQuery {
limit := parseSQLPageSize(query.Get("limit"), 20)
offset := parsePositive(query.Get("offset"), 0)
canonicalSourceCount := strconv.Itoa(len(canonicalVehicleProtocols))
args := []any{}
vehicleSetSQL, args := buildVehicleCoverageSetSQL(query.Get("scopeVins"))
where := []string{"v.vin IS NOT NULL", "v.vin <> ''"}
where, args = appendVINListFilter(where, args, "v.vin", query.Get("scopeVins"))
having := []string{}
if keyword := strings.TrimSpace(query.Get("keyword")); keyword != "" {
where = append(where, "(v.vin LIKE ? OR s.plate LIKE ? OR b.vin LIKE ? OR b.plate LIKE ? OR b.phone LIKE ? OR b.oem LIKE ?)")
@@ -146,7 +145,6 @@ 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 <> ''`
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 ` +
@@ -174,9 +172,8 @@ func buildVehicleCoverageSQL(query url.Values) SQLQuery {
func buildVehicleCoverageSummarySQL(query url.Values) SQLQuery {
canonicalSourceCount := strconv.Itoa(len(canonicalVehicleProtocols))
args := []any{}
vehicleSetSQL, args := buildVehicleCoverageSetSQL(query.Get("scopeVins"))
where := []string{"v.vin IS NOT NULL", "v.vin <> ''"}
where, args = appendVINListFilter(where, args, "v.vin", query.Get("scopeVins"))
having := []string{}
if keyword := strings.TrimSpace(query.Get("keyword")); keyword != "" {
where = append(where, "(v.vin LIKE ? OR s.plate LIKE ? OR b.vin LIKE ? OR b.plate LIKE ? OR b.phone LIKE ? OR b.oem LIKE ?)")
@@ -245,7 +242,6 @@ 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 <> ''`
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, ` +
@@ -279,6 +275,28 @@ func buildVehicleCoverageSummarySQL(query url.Values) SQLQuery {
}
}
func buildVehicleCoverageSetSQL(scope string) (string, []any) {
scope = strings.TrimSpace(scope)
if scope == "" {
return `SELECT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> ''`, nil
}
vins := splitCSV(scope)
if len(vins) == 0 || (len(vins) == 1 && vins[0] == "__NO_VEHICLE_SCOPE__") {
return `SELECT CAST(NULL AS CHAR(64)) AS vin WHERE 1 = 0`, nil
}
selects := make([]string, len(vins))
args := make([]any, len(vins))
for index, vin := range vins {
if index == 0 {
selects[index] = `SELECT ? AS vin`
} else {
selects[index] = `SELECT ?`
}
args[index] = vin
}
return `SELECT DISTINCT scoped.vin FROM (` + strings.Join(selects, ` UNION ALL `) + `) scoped`, args
}
func archiveMissingFieldPredicate(field string) string {
switch strings.TrimSpace(field) {
case "plate":

View File

@@ -105,6 +105,40 @@ func TestBuildVehicleCoverageSQLIncludesNoDataVehicles(t *testing.T) {
}
}
func TestBuildVehicleCoverageSQLUsesAuthorizedVINsAsPopulation(t *testing.T) {
query := url.Values{"scopeVins": {"VIN001,VIN002"}, "limit": {"10"}, "offset": {"20"}}
built := buildVehicleCoverageSQL(query)
for _, want := range []string{
"SELECT DISTINCT scoped.vin",
"SELECT ? AS vin UNION ALL SELECT ?",
"LEFT JOIN vehicle_identity_binding b ON b.vin = v.vin",
"LEFT JOIN vehicle_realtime_snapshot s ON s.vin = v.vin",
} {
if !strings.Contains(built.Text, want) {
t.Fatalf("authorized vehicle coverage SQL missing %q: %s", want, built.Text)
}
}
if strings.Contains(built.Text, "FROM (SELECT vin FROM vehicle_identity_binding") {
t.Fatalf("authorized vehicle coverage must not drop granted VINs without identity bindings: %s", built.Text)
}
if !slices.Equal(built.Args, []any{"VIN001", "VIN002", 10, 20}) {
t.Fatalf("args = %#v", built.Args)
}
if !slices.Equal(built.CountArgs, []any{"VIN001", "VIN002"}) {
t.Fatalf("count args = %#v", built.CountArgs)
}
}
func TestBuildVehicleCoverageSQLFailsClosedForEmptyAuthorizedScope(t *testing.T) {
built := buildVehicleCoverageSQL(url.Values{"scopeVins": {"__NO_VEHICLE_SCOPE__"}})
if !strings.Contains(built.Text, "SELECT CAST(NULL AS CHAR(64)) AS vin WHERE 1 = 0") {
t.Fatalf("empty authorized scope must use an empty vehicle population: %s", built.Text)
}
if len(built.Args) != 2 || built.Args[0] != 20 || built.Args[1] != 0 || len(built.CountArgs) != 0 {
t.Fatalf("args = %#v count args = %#v", built.Args, built.CountArgs)
}
}
func TestBuildVehicleCoverageSQLFiltersMissingProtocol(t *testing.T) {
query := url.Values{"missingProtocol": {"YUTONG_MQTT"}, "limit": {"8"}}
built := buildVehicleCoverageSQL(query)