refactor: narrow vehicle stat repository contract

This commit is contained in:
lingniu
2026-07-01 05:15:05 +08:00
parent 6c9459bb64
commit e269c0ad55
6 changed files with 33 additions and 37 deletions

View File

@@ -26,8 +26,7 @@ public final class JdbcVehicleStatMetricRepository implements VehicleStatReposit
ensureSchema();
}
@Override
public void saveDailyStat(VehicleDailyStatResult result) {
private void saveDailyMileageMetric(VehicleDailyStatResult result) {
if (result == null || result.dailyMileageKm().isEmpty()) {
return;
}
@@ -88,7 +87,7 @@ public final class JdbcVehicleStatMetricRepository implements VehicleStatReposit
statDate,
OptionalDouble.of(dailyMileageKm),
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF);
saveDailyStat(result);
saveDailyMileageMetric(result);
return Optional.of(result);
}

View File

@@ -5,8 +5,6 @@ import java.util.Optional;
public interface VehicleStatRepository {
void saveDailyStat(VehicleDailyStatResult result);
Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate, double totalMileageKm);
Optional<VehicleDailyStatResult> findDailyStat(String vin, LocalDate statDate);

View File

@@ -5,30 +5,21 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.time.LocalDate;
import java.util.OptionalDouble;
import static org.assertj.core.api.Assertions.assertThat;
class JdbcVehicleStatMetricRepositoryTest {
@Test
void upsertsDailyMileageIntoCommonMetricTable() {
void recordsDailyMileageIntoCommonMetricTable() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(new DriverManagerDataSource(
"jdbc:h2:mem:vehicle_stat_metric;MODE=MySQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1",
"sa",
""));
JdbcVehicleStatMetricRepository repository = new JdbcVehicleStatMetricRepository(jdbcTemplate);
repository.saveDailyStat(new VehicleDailyStatResult(
"VIN001",
LocalDate.of(2026, 6, 30),
OptionalDouble.of(12.345),
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF));
repository.saveDailyStat(new VehicleDailyStatResult(
"VIN001",
LocalDate.of(2026, 6, 30),
OptionalDouble.of(15.5),
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF));
repository.recordDailyMileageSample("VIN001", LocalDate.of(2026, 6, 30), 100.0);
repository.recordDailyMileageSample("VIN001", LocalDate.of(2026, 6, 30), 115.5);
assertThat(repository.findDailyStat("VIN001", LocalDate.of(2026, 6, 30)))
.isPresent()
@@ -36,12 +27,12 @@ class JdbcVehicleStatMetricRepositoryTest {
.extracting(result -> result.dailyMileageKm().orElseThrow())
.isEqualTo(15.5);
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM vehicle_stat_metric", Integer.class))
.isEqualTo(1);
.isEqualTo(3);
assertThat(jdbcTemplate.queryForObject(
"SELECT metric_key FROM vehicle_stat_metric WHERE vin = 'VIN001'",
"SELECT metric_key FROM vehicle_stat_metric WHERE vin = 'VIN001' AND metric_key = 'daily_mileage_km'",
String.class)).isEqualTo("daily_mileage_km");
assertThat(jdbcTemplate.queryForObject(
"SELECT calculation_method FROM vehicle_stat_metric WHERE vin = 'VIN001'",
"SELECT calculation_method FROM vehicle_stat_metric WHERE vin = 'VIN001' AND metric_key = 'daily_mileage_km'",
String.class)).isEqualTo("JT808_TOTAL_MILEAGE_DIFF");
assertThat(jdbcTemplate.queryForObject("""
SELECT COUNT(*)

View File

@@ -17,11 +17,8 @@ class VehicleStatControllerTest {
@Test
void returnsDailyStatJson() throws Exception {
InMemoryVehicleStatRepository repository = new InMemoryVehicleStatRepository();
repository.saveDailyStat(new VehicleDailyStatResult(
"VIN001",
LocalDate.parse("2026-06-22"),
OptionalDouble.of(35.5),
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF));
repository.recordDailyMileageSample("VIN001", LocalDate.parse("2026-06-22"), 100.0);
repository.recordDailyMileageSample("VIN001", LocalDate.parse("2026-06-22"), 135.5);
MockMvc mvc = standaloneSetup(new VehicleStatController(repository)).build();
mvc.perform(get("/api/vehicle-stat/VIN001/daily").param("date", "2026-06-22"))
@@ -34,21 +31,19 @@ class VehicleStatControllerTest {
private static final class InMemoryVehicleStatRepository implements VehicleStatRepository {
private final java.util.Map<String, VehicleDailyStatResult> results = new java.util.HashMap<>();
@Override
public void saveDailyStat(VehicleDailyStatResult result) {
results.put(result.vin() + ":" + result.statDate(), result);
}
private final java.util.Map<String, Double> firstTotals = new java.util.HashMap<>();
@Override
public Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate,
double totalMileageKm) {
String key = vin + ":" + statDate;
double firstTotal = firstTotals.computeIfAbsent(key, ignored -> totalMileageKm);
VehicleDailyStatResult result = new VehicleDailyStatResult(
vin,
statDate,
OptionalDouble.of(0.0),
OptionalDouble.of(totalMileageKm - firstTotal),
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF);
saveDailyStat(result);
results.put(key, result);
return Optional.of(result);
}

View File

@@ -0,0 +1,18 @@
package com.lingniu.ingest.vehiclestat;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
class VehicleStatRepositoryContractTest {
@Test
void exposesOnlyMileageSampleAndQueryOperations() {
assertThat(Arrays.stream(VehicleStatRepository.class.getDeclaredMethods())
.map(Method::getName))
.containsExactlyInAnyOrder("recordDailyMileageSample", "findDailyStat");
}
}

View File

@@ -101,11 +101,6 @@ class Jt808MileageStreamProcessorTest {
private final List<Double> samples = new ArrayList<>();
private double firstSample = Double.NaN;
@Override
public void saveDailyStat(VehicleDailyStatResult result) {
results.add(result);
}
@Override
public Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate,
double totalMileageKm) {