fix: format daily metric query timestamps
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Queryer interface {
|
type Queryer interface {
|
||||||
@@ -63,11 +64,14 @@ func (r *MetricRepository) Query(ctx context.Context, query MetricQuery) ([]Metr
|
|||||||
var out []MetricRow
|
var out []MetricRow
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var row MetricRow
|
var row MetricRow
|
||||||
|
var statDate scanDate
|
||||||
|
var createdAt scanDateTime
|
||||||
|
var updatedAt scanDateTime
|
||||||
var first sql.NullFloat64
|
var first sql.NullFloat64
|
||||||
var latest sql.NullFloat64
|
var latest sql.NullFloat64
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&row.VIN,
|
&row.VIN,
|
||||||
&row.StatDate,
|
&statDate,
|
||||||
&row.Protocol,
|
&row.Protocol,
|
||||||
&row.MetricKey,
|
&row.MetricKey,
|
||||||
&row.MetricValue,
|
&row.MetricValue,
|
||||||
@@ -76,11 +80,14 @@ func (r *MetricRepository) Query(ctx context.Context, query MetricQuery) ([]Metr
|
|||||||
&latest,
|
&latest,
|
||||||
&row.SampleCount,
|
&row.SampleCount,
|
||||||
&row.CalculationMethod,
|
&row.CalculationMethod,
|
||||||
&row.CreatedAt,
|
&createdAt,
|
||||||
&row.UpdatedAt,
|
&updatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
row.StatDate = statDate.String
|
||||||
|
row.CreatedAt = createdAt.String
|
||||||
|
row.UpdatedAt = updatedAt.String
|
||||||
if first.Valid {
|
if first.Valid {
|
||||||
row.FirstTotalMileageKM = &first.Float64
|
row.FirstTotalMileageKM = &first.Float64
|
||||||
}
|
}
|
||||||
@@ -239,3 +246,36 @@ func writeMetricError(w http.ResponseWriter, status int, message string) {
|
|||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
_ = json.NewEncoder(w).Encode(map[string]any{"error": message})
|
_ = json.NewEncoder(w).Encode(map[string]any{"error": message})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type scanDate struct {
|
||||||
|
String string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanDate) Scan(value any) error {
|
||||||
|
s.String = formatSQLTime(value, "2006-01-02")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type scanDateTime struct {
|
||||||
|
String string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanDateTime) Scan(value any) error {
|
||||||
|
s.String = formatSQLTime(value, "2006-01-02 15:04:05")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatSQLTime(value any, layout string) string {
|
||||||
|
switch typed := value.(type) {
|
||||||
|
case nil:
|
||||||
|
return ""
|
||||||
|
case time.Time:
|
||||||
|
return typed.Format(layout)
|
||||||
|
case []byte:
|
||||||
|
return strings.TrimSpace(string(typed))
|
||||||
|
case string:
|
||||||
|
return strings.TrimSpace(typed)
|
||||||
|
default:
|
||||||
|
return strings.TrimSpace(fmt.Sprint(typed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
)
|
)
|
||||||
@@ -24,8 +25,8 @@ func TestMetricRepositoryQueriesDailyMetricsWithFilters(t *testing.T) {
|
|||||||
"first_total_mileage_km", "latest_total_mileage_km", "sample_count", "calculation_method",
|
"first_total_mileage_km", "latest_total_mileage_km", "sample_count", "calculation_method",
|
||||||
"created_at", "updated_at",
|
"created_at", "updated_at",
|
||||||
}).AddRow(
|
}).AddRow(
|
||||||
"LKLG7C4E3NA774736", "2026-07-01", "JT808", MetricDailyTotalMileageKM, 12345.6, "km",
|
"LKLG7C4E3NA774736", time.Date(2026, 7, 1, 0, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)), "JT808", MetricDailyTotalMileageKM, 12345.6, "km",
|
||||||
12345.6, 12345.6, 13, "TOTAL_MILEAGE_DIFF", "2026-07-01 22:49:11", "2026-07-01 23:09:36",
|
12345.6, 12345.6, 13, "TOTAL_MILEAGE_DIFF", 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)),
|
||||||
))
|
))
|
||||||
|
|
||||||
repository := NewMetricRepository(db)
|
repository := NewMetricRepository(db)
|
||||||
@@ -45,6 +46,9 @@ func TestMetricRepositoryQueriesDailyMetricsWithFilters(t *testing.T) {
|
|||||||
if rows[0].MetricKey != MetricDailyTotalMileageKM || rows[0].MetricValue != 12345.6 {
|
if rows[0].MetricKey != MetricDailyTotalMileageKM || rows[0].MetricValue != 12345.6 {
|
||||||
t.Fatalf("unexpected row: %#v", rows[0])
|
t.Fatalf("unexpected row: %#v", rows[0])
|
||||||
}
|
}
|
||||||
|
if rows[0].StatDate != "2026-07-01" || rows[0].UpdatedAt != "2026-07-01 23:09:36" {
|
||||||
|
t.Fatalf("unexpected time formatting: %#v", rows[0])
|
||||||
|
}
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
t.Fatalf("sql expectations: %v", err)
|
t.Fatalf("sql expectations: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user