From 0b9e803139bc9106e3f90c993b188a5cd20c880d Mon Sep 17 00:00:00 2001 From: lingniu Date: Fri, 3 Jul 2026 08:01:00 +0800 Subject: [PATCH] perf(go): simplify identity binding lookup --- docs/architecture/storage-minimal-contract.md | 1 + .../internal/identity/resolver.go | 2 +- .../internal/identity/resolver_test.go | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/architecture/storage-minimal-contract.md b/docs/architecture/storage-minimal-contract.md index 04047b59..cd7c524a 100644 --- a/docs/architecture/storage-minimal-contract.md +++ b/docs/architecture/storage-minimal-contract.md @@ -70,6 +70,7 @@ - `vehicle_identity_binding`:VIN 与 plate/phone/device_id 的映射,供 808 等协议反查 VIN。 - `jt808_registration`:808 注册、鉴权、最新活跃和 VIN 匹配状态。 - 状态:`vehicle_identity_binding` 使用 VIN 主键;`jt808_registration` 使用 phone 主键;两张表都不保留代理自增主键和 `created_at`。 + - 查询:`vehicle_identity_binding` 的 phone/device_id/plate 都是唯一键,接入侧按唯一键直查 VIN,不做无意义排序。 - 生产:RDS 已在上线前删除旧 `vehicle_identity_binding_registration`、`vehicle_identity_bindings`,gateway 启动会自动创建最小 schema。 ## 字段提升规则 diff --git a/go/vehicle-gateway/internal/identity/resolver.go b/go/vehicle-gateway/internal/identity/resolver.go index f55ca47f..2e0aad67 100644 --- a/go/vehicle-gateway/internal/identity/resolver.go +++ b/go/vehicle-gateway/internal/identity/resolver.go @@ -161,7 +161,7 @@ func (r *MySQLResolver) Resolve(ctx context.Context, env envelope.FrameEnvelope) } func (r *MySQLResolver) lookup(ctx context.Context, column string, value string) (string, error) { - query := "SELECT vin FROM " + r.table + " WHERE " + column + " = ? AND vin IS NOT NULL AND vin <> '' ORDER BY updated_at DESC LIMIT 1" + query := "SELECT vin FROM " + r.table + " WHERE " + column + " = ? AND vin IS NOT NULL AND vin <> ''" var vin string err := r.db.QueryRowContext(ctx, query, value).Scan(&vin) return vin, err diff --git a/go/vehicle-gateway/internal/identity/resolver_test.go b/go/vehicle-gateway/internal/identity/resolver_test.go index 75bc3822..0e0cb636 100644 --- a/go/vehicle-gateway/internal/identity/resolver_test.go +++ b/go/vehicle-gateway/internal/identity/resolver_test.go @@ -80,6 +80,30 @@ func TestMySQLResolverFillsVINFromPhone(t *testing.T) { } } +func TestMySQLResolverLooksUpVINByUniqueKeyWithoutSort(t *testing.T) { + db, mock := newMockDB(t) + defer db.Close() + mock.ExpectQuery("SELECT vin FROM vehicle_identity_binding WHERE phone = \\? AND vin IS NOT NULL AND vin <> ''$"). + WithArgs("13307795425"). + WillReturnRows(sqlmock.NewRows([]string{"vin"}).AddRow("LNBVIN00000000001")) + + resolver := NewMySQLResolver(db, "vehicle_identity_binding") + env, err := resolver.Resolve(context.Background(), envelope.FrameEnvelope{ + Protocol: envelope.ProtocolJT808, + Phone: "013307795425", + Parsed: map[string]any{}, + }) + if err != nil { + t.Fatalf("Resolve() error = %v", err) + } + if env.VIN != "LNBVIN00000000001" { + t.Fatalf("vin = %q", env.VIN) + } + if err := mock.ExpectationsWereMet(); err != nil { + t.Fatalf("sql expectations: %v", err) + } +} + func TestMySQLResolverFallsBackToDeviceID(t *testing.T) { db, mock := newMockDB(t) defer db.Close()