refactor: narrow vehicle stat repository contract
This commit is contained in:
@@ -26,8 +26,7 @@ public final class JdbcVehicleStatMetricRepository implements VehicleStatReposit
|
|||||||
ensureSchema();
|
ensureSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void saveDailyMileageMetric(VehicleDailyStatResult result) {
|
||||||
public void saveDailyStat(VehicleDailyStatResult result) {
|
|
||||||
if (result == null || result.dailyMileageKm().isEmpty()) {
|
if (result == null || result.dailyMileageKm().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -88,7 +87,7 @@ public final class JdbcVehicleStatMetricRepository implements VehicleStatReposit
|
|||||||
statDate,
|
statDate,
|
||||||
OptionalDouble.of(dailyMileageKm),
|
OptionalDouble.of(dailyMileageKm),
|
||||||
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF);
|
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF);
|
||||||
saveDailyStat(result);
|
saveDailyMileageMetric(result);
|
||||||
return Optional.of(result);
|
return Optional.of(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface VehicleStatRepository {
|
public interface VehicleStatRepository {
|
||||||
|
|
||||||
void saveDailyStat(VehicleDailyStatResult result);
|
|
||||||
|
|
||||||
Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate, double totalMileageKm);
|
Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate, double totalMileageKm);
|
||||||
|
|
||||||
Optional<VehicleDailyStatResult> findDailyStat(String vin, LocalDate statDate);
|
Optional<VehicleDailyStatResult> findDailyStat(String vin, LocalDate statDate);
|
||||||
|
|||||||
@@ -5,30 +5,21 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
|||||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.OptionalDouble;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class JdbcVehicleStatMetricRepositoryTest {
|
class JdbcVehicleStatMetricRepositoryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void upsertsDailyMileageIntoCommonMetricTable() {
|
void recordsDailyMileageIntoCommonMetricTable() {
|
||||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(new DriverManagerDataSource(
|
JdbcTemplate jdbcTemplate = new JdbcTemplate(new DriverManagerDataSource(
|
||||||
"jdbc:h2:mem:vehicle_stat_metric;MODE=MySQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1",
|
"jdbc:h2:mem:vehicle_stat_metric;MODE=MySQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1",
|
||||||
"sa",
|
"sa",
|
||||||
""));
|
""));
|
||||||
JdbcVehicleStatMetricRepository repository = new JdbcVehicleStatMetricRepository(jdbcTemplate);
|
JdbcVehicleStatMetricRepository repository = new JdbcVehicleStatMetricRepository(jdbcTemplate);
|
||||||
|
|
||||||
repository.saveDailyStat(new VehicleDailyStatResult(
|
repository.recordDailyMileageSample("VIN001", LocalDate.of(2026, 6, 30), 100.0);
|
||||||
"VIN001",
|
repository.recordDailyMileageSample("VIN001", LocalDate.of(2026, 6, 30), 115.5);
|
||||||
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));
|
|
||||||
|
|
||||||
assertThat(repository.findDailyStat("VIN001", LocalDate.of(2026, 6, 30)))
|
assertThat(repository.findDailyStat("VIN001", LocalDate.of(2026, 6, 30)))
|
||||||
.isPresent()
|
.isPresent()
|
||||||
@@ -36,12 +27,12 @@ class JdbcVehicleStatMetricRepositoryTest {
|
|||||||
.extracting(result -> result.dailyMileageKm().orElseThrow())
|
.extracting(result -> result.dailyMileageKm().orElseThrow())
|
||||||
.isEqualTo(15.5);
|
.isEqualTo(15.5);
|
||||||
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM vehicle_stat_metric", Integer.class))
|
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM vehicle_stat_metric", Integer.class))
|
||||||
.isEqualTo(1);
|
.isEqualTo(3);
|
||||||
assertThat(jdbcTemplate.queryForObject(
|
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");
|
String.class)).isEqualTo("daily_mileage_km");
|
||||||
assertThat(jdbcTemplate.queryForObject(
|
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");
|
String.class)).isEqualTo("JT808_TOTAL_MILEAGE_DIFF");
|
||||||
assertThat(jdbcTemplate.queryForObject("""
|
assertThat(jdbcTemplate.queryForObject("""
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
|
|||||||
@@ -17,11 +17,8 @@ class VehicleStatControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
void returnsDailyStatJson() throws Exception {
|
void returnsDailyStatJson() throws Exception {
|
||||||
InMemoryVehicleStatRepository repository = new InMemoryVehicleStatRepository();
|
InMemoryVehicleStatRepository repository = new InMemoryVehicleStatRepository();
|
||||||
repository.saveDailyStat(new VehicleDailyStatResult(
|
repository.recordDailyMileageSample("VIN001", LocalDate.parse("2026-06-22"), 100.0);
|
||||||
"VIN001",
|
repository.recordDailyMileageSample("VIN001", LocalDate.parse("2026-06-22"), 135.5);
|
||||||
LocalDate.parse("2026-06-22"),
|
|
||||||
OptionalDouble.of(35.5),
|
|
||||||
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF));
|
|
||||||
MockMvc mvc = standaloneSetup(new VehicleStatController(repository)).build();
|
MockMvc mvc = standaloneSetup(new VehicleStatController(repository)).build();
|
||||||
|
|
||||||
mvc.perform(get("/api/vehicle-stat/VIN001/daily").param("date", "2026-06-22"))
|
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 static final class InMemoryVehicleStatRepository implements VehicleStatRepository {
|
||||||
private final java.util.Map<String, VehicleDailyStatResult> results = new java.util.HashMap<>();
|
private final java.util.Map<String, VehicleDailyStatResult> results = new java.util.HashMap<>();
|
||||||
|
private final java.util.Map<String, Double> firstTotals = new java.util.HashMap<>();
|
||||||
@Override
|
|
||||||
public void saveDailyStat(VehicleDailyStatResult result) {
|
|
||||||
results.put(result.vin() + ":" + result.statDate(), result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate,
|
public Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate,
|
||||||
double totalMileageKm) {
|
double totalMileageKm) {
|
||||||
|
String key = vin + ":" + statDate;
|
||||||
|
double firstTotal = firstTotals.computeIfAbsent(key, ignored -> totalMileageKm);
|
||||||
VehicleDailyStatResult result = new VehicleDailyStatResult(
|
VehicleDailyStatResult result = new VehicleDailyStatResult(
|
||||||
vin,
|
vin,
|
||||||
statDate,
|
statDate,
|
||||||
OptionalDouble.of(0.0),
|
OptionalDouble.of(totalMileageKm - firstTotal),
|
||||||
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF);
|
DailyMileageStrategy.JT808_TOTAL_MILEAGE_DIFF);
|
||||||
saveDailyStat(result);
|
results.put(key, result);
|
||||||
return Optional.of(result);
|
return Optional.of(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,11 +101,6 @@ class Jt808MileageStreamProcessorTest {
|
|||||||
private final List<Double> samples = new ArrayList<>();
|
private final List<Double> samples = new ArrayList<>();
|
||||||
private double firstSample = Double.NaN;
|
private double firstSample = Double.NaN;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveDailyStat(VehicleDailyStatResult result) {
|
|
||||||
results.add(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate,
|
public Optional<VehicleDailyStatResult> recordDailyMileageSample(String vin, LocalDate statDate,
|
||||||
double totalMileageKm) {
|
double totalMileageKm) {
|
||||||
|
|||||||
Reference in New Issue
Block a user