refactor(go): simplify daily mileage table key

This commit is contained in:
lingniu
2026-07-03 07:16:49 +08:00
parent e89c9fd98f
commit 04bafb4cd6
6 changed files with 24 additions and 19 deletions

View File

@@ -121,8 +121,13 @@ func TestWriterEnsuresSchemaAndUpsertsDailyMileage(t *testing.T) {
if !strings.Contains(exec.calls[0].query, "CREATE TABLE IF NOT EXISTS vehicle_daily_mileage") {
t.Fatalf("unexpected schema sql: %s", exec.calls[0].query)
}
if strings.Contains(exec.calls[0].query, "vehicle_key") {
t.Fatalf("schema should not include vehicle_key: %s", exec.calls[0].query)
for _, column := range []string{"vehicle_key", "id BIGINT", "AUTO_INCREMENT", "created_at"} {
if strings.Contains(exec.calls[0].query, column) {
t.Fatalf("schema should not include %s: %s", column, exec.calls[0].query)
}
}
if !strings.Contains(exec.calls[0].query, "PRIMARY KEY (vin, stat_date, protocol)") {
t.Fatalf("daily mileage table should key by vin/stat_date/protocol: %s", exec.calls[0].query)
}
if len(exec.calls) != 2 {
t.Fatalf("exec calls = %d", len(exec.calls))

View File

@@ -33,7 +33,6 @@ type MetricRow struct {
FirstTotalMileageKM *float64 `json:"first_total_mileage_km,omitempty"`
LatestTotalMileageKM *float64 `json:"latest_total_mileage_km,omitempty"`
SampleCount int64 `json:"sample_count"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
@@ -61,7 +60,6 @@ func (r *MetricRepository) Query(ctx context.Context, query MetricQuery) ([]Metr
for rows.Next() {
var row MetricRow
var statDate scanDate
var createdAt scanDateTime
var updatedAt scanDateTime
var first sql.NullFloat64
var latest sql.NullFloat64
@@ -73,13 +71,11 @@ func (r *MetricRepository) Query(ctx context.Context, query MetricQuery) ([]Metr
&first,
&latest,
&row.SampleCount,
&createdAt,
&updatedAt,
); err != nil {
return nil, err
}
row.StatDate = statDate.String
row.CreatedAt = createdAt.String
row.UpdatedAt = updatedAt.String
if first.Valid {
row.FirstTotalMileageKM = &first.Float64
@@ -123,7 +119,7 @@ func normalizeMetricQuery(query MetricQuery) MetricQuery {
func buildMetricSQL(query MetricQuery) (string, []any) {
where, args := buildMetricWhere(query)
sqlText := `SELECT vin, stat_date, protocol, daily_mileage_km, first_total_mileage_km, latest_total_mileage_km, sample_count, created_at, updated_at FROM vehicle_daily_mileage`
sqlText := `SELECT vin, stat_date, protocol, daily_mileage_km, first_total_mileage_km, latest_total_mileage_km, sample_count, updated_at FROM vehicle_daily_mileage`
if len(where) > 0 {
sqlText += " WHERE " + strings.Join(where, " AND ")
}

View File

@@ -18,15 +18,15 @@ func TestMetricRepositoryQueriesDailyMetricsWithFilters(t *testing.T) {
t.Fatalf("sqlmock.New() error = %v", err)
}
defer db.Close()
mock.ExpectQuery("SELECT vin, stat_date, protocol, daily_mileage_km, first_total_mileage_km, latest_total_mileage_km, sample_count, created_at, updated_at FROM vehicle_daily_mileage").
mock.ExpectQuery("SELECT vin, stat_date, protocol, daily_mileage_km, first_total_mileage_km, latest_total_mileage_km, sample_count, updated_at FROM vehicle_daily_mileage").
WithArgs("LKLG7C4E3NA774736", "JT808", "2026-07-01", "2026-07-01", 20, 0).
WillReturnRows(sqlmock.NewRows([]string{
"vin", "stat_date", "protocol", "daily_mileage_km",
"first_total_mileage_km", "latest_total_mileage_km", "sample_count",
"created_at", "updated_at",
"updated_at",
}).AddRow(
"LKLG7C4E3NA774736", time.Date(2026, 7, 1, 0, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)), "JT808", 12.3,
12345.6, 12357.9, 13, time.Date(2026, 7, 1, 22, 49, 11, 0, time.FixedZone("Asia/Shanghai", 8*3600)), time.Date(2026, 7, 1, 23, 9, 36, 0, time.FixedZone("Asia/Shanghai", 8*3600)),
12345.6, 12357.9, 13, time.Date(2026, 7, 1, 23, 9, 36, 0, time.FixedZone("Asia/Shanghai", 8*3600)),
))
repository := NewMetricRepository(db)
@@ -63,15 +63,15 @@ func TestMetricHandlerReturnsDailyMetrics(t *testing.T) {
mock.ExpectQuery("SELECT COUNT\\(\\*\\) FROM vehicle_daily_mileage").
WithArgs("LB9A32A21R0LS1707", "GB32960", "2020-07-01", "2020-07-01").
WillReturnRows(sqlmock.NewRows([]string{"total"}).AddRow(42))
mock.ExpectQuery("SELECT vin, stat_date, protocol, daily_mileage_km, first_total_mileage_km, latest_total_mileage_km, sample_count, created_at, updated_at FROM vehicle_daily_mileage").
mock.ExpectQuery("SELECT vin, stat_date, protocol, daily_mileage_km, first_total_mileage_km, latest_total_mileage_km, sample_count, updated_at FROM vehicle_daily_mileage").
WithArgs("LB9A32A21R0LS1707", "GB32960", "2020-07-01", "2020-07-01", 50, 0).
WillReturnRows(sqlmock.NewRows([]string{
"vin", "stat_date", "protocol", "daily_mileage_km",
"first_total_mileage_km", "latest_total_mileage_km", "sample_count",
"created_at", "updated_at",
"updated_at",
}).AddRow(
"LB9A32A21R0LS1707", "2020-07-01", "GB32960", 0.0,
53490.9, 53490.9, 3, "2026-07-01 22:07:58", "2026-07-01 22:28:25",
53490.9, 53490.9, 3, "2026-07-01 22:28:25",
))
handler := NewMetricHandler(NewMetricRepository(db))
@@ -89,6 +89,9 @@ func TestMetricHandlerReturnsDailyMetrics(t *testing.T) {
t.Fatalf("response missing %s: %s", want, body)
}
}
if strings.Contains(body, "created_at") {
t.Fatalf("daily mileage response should not expose created_at: %s", body)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatalf("sql expectations: %v", err)
}
@@ -103,12 +106,12 @@ func TestMetricHandlerReturnsEmptyItemsArrayWhenNoRows(t *testing.T) {
mock.ExpectQuery("SELECT COUNT\\(\\*\\) FROM vehicle_daily_mileage").
WithArgs("YUTONG_MQTT").
WillReturnRows(sqlmock.NewRows([]string{"total"}).AddRow(0))
mock.ExpectQuery("SELECT vin, stat_date, protocol, daily_mileage_km, first_total_mileage_km, latest_total_mileage_km, sample_count, created_at, updated_at FROM vehicle_daily_mileage").
mock.ExpectQuery("SELECT vin, stat_date, protocol, daily_mileage_km, first_total_mileage_km, latest_total_mileage_km, sample_count, updated_at FROM vehicle_daily_mileage").
WithArgs("YUTONG_MQTT", 50, 0).
WillReturnRows(sqlmock.NewRows([]string{
"vin", "stat_date", "protocol", "daily_mileage_km",
"first_total_mileage_km", "latest_total_mileage_km", "sample_count",
"created_at", "updated_at",
"updated_at",
}))
handler := NewMetricHandler(NewMetricRepository(db))

View File

@@ -1,7 +1,6 @@
package stats
const DailyMileageTableSQL = `CREATE TABLE IF NOT EXISTS vehicle_daily_mileage (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
vin VARCHAR(32) NOT NULL,
stat_date DATE NOT NULL,
protocol VARCHAR(32) NOT NULL,
@@ -9,9 +8,8 @@ const DailyMileageTableSQL = `CREATE TABLE IF NOT EXISTS vehicle_daily_mileage (
first_total_mileage_km DECIMAL(18,3) NULL,
latest_total_mileage_km DECIMAL(18,3) NULL,
sample_count BIGINT NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_daily_mileage_vin (vin, stat_date, protocol),
PRIMARY KEY (vin, stat_date, protocol),
KEY idx_vin (vin),
KEY idx_stat_date (stat_date),
KEY idx_protocol_date (protocol, stat_date)