From f7bb5b1554fdf45978c2cb1530e2f8120228871b Mon Sep 17 00:00:00 2001 From: lingniu Date: Wed, 1 Jul 2026 14:35:58 +0800 Subject: [PATCH] chore: enforce kafka-only stat path --- docs/operations/jt808-daily-mileage-runbook.md | 2 ++ .../plans/2026-06-30-kafka-streaming-mileage.md | 3 ++- docs/target-architecture.md | 2 ++ .../src/main/resources/application.yml | 2 -- .../VehicleHistoryAppDefaultsTest.java | 17 ++++++++++++----- .../VehicleStatRepositoryContractTest.java | 3 +++ 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/operations/jt808-daily-mileage-runbook.md b/docs/operations/jt808-daily-mileage-runbook.md index b67c6078..982d9e75 100644 --- a/docs/operations/jt808-daily-mileage-runbook.md +++ b/docs/operations/jt808-daily-mileage-runbook.md @@ -4,6 +4,8 @@ The analytics app can calculate daily mileage from JT808 telemetry only. It consumes `vehicle.event.jt808.v1` and saves the current daily result into the common `vehicle-stat` metric repository. +Only supported message backbone: Kafka. + ## Storage Runtime state: none outside `vehicle_stat_metric`. There is no separate JT808 daily-mileage table. The derived value is stored as one `daily_mileage_km` metric row in the common JDBC/MySQL table `vehicle_stat_metric`; the local-day minimum and maximum GPS total mileage values are kept on that same row as calculation source columns. diff --git a/docs/superpowers/plans/2026-06-30-kafka-streaming-mileage.md b/docs/superpowers/plans/2026-06-30-kafka-streaming-mileage.md index e314fe18..6132ecb2 100644 --- a/docs/superpowers/plans/2026-06-30-kafka-streaming-mileage.md +++ b/docs/superpowers/plans/2026-06-30-kafka-streaming-mileage.md @@ -6,6 +6,7 @@ Consume JT808 Kafka location events and write the derived daily mileage into the ## Current Design +- Only supported message backbone: Kafka. - Source topic: `vehicle.event.jt808.v1` - Runtime app: `vehicle-analytics-app` - Runtime state: none outside `vehicle_stat_metric` @@ -42,4 +43,4 @@ MYSQL_PASSWORD= ## Notes -Do not create or write a protocol-specific JT808 daily-mileage table. Do not add distance accumulation, integral calculation, Redis state, or memory state back into this path unless the mileage definition changes again. +Do not create or write a protocol-specific JT808 daily-mileage table. Do not add MQ, distance accumulation, integral calculation, Redis state, or memory state back into this path unless the mileage definition changes again. diff --git a/docs/target-architecture.md b/docs/target-architecture.md index 66d4fbdf..abf4c097 100644 --- a/docs/target-architecture.md +++ b/docs/target-architecture.md @@ -5,6 +5,8 @@ The project should stay small at runtime: protocol apps ingest and publish, history consumes and indexes, analytics derives metrics, and business systems read through explicit APIs or Kafka. +Only supported message backbone: Kafka. + ## Active Scope Active production protocols: diff --git a/modules/apps/vehicle-history-app/src/main/resources/application.yml b/modules/apps/vehicle-history-app/src/main/resources/application.yml index c9688ac0..21833dd2 100644 --- a/modules/apps/vehicle-history-app/src/main/resources/application.yml +++ b/modules/apps/vehicle-history-app/src/main/resources/application.yml @@ -79,8 +79,6 @@ lingniu: group-id: ${KAFKA_GROUP_HISTORY_YUTONG_MQTT_RAW:${KAFKA_GROUP_HISTORY:vehicle-history}-yutong-mqtt-raw} topics: - ${KAFKA_TOPIC_YUTONG_MQTT_RAW:vehicle.raw.mqtt-yutong.v1} - archive: - enabled: false tdengine-history: enabled: ${TDENGINE_HISTORY_ENABLED:false} database: ${TDENGINE_HISTORY_DATABASE:vehicle_history} diff --git a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/VehicleHistoryAppDefaultsTest.java b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/VehicleHistoryAppDefaultsTest.java index ba266fc7..fec38b7c 100644 --- a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/VehicleHistoryAppDefaultsTest.java +++ b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/VehicleHistoryAppDefaultsTest.java @@ -4,7 +4,9 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.io.ClassPathResource; +import java.io.IOException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import java.util.Properties; @@ -14,7 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; class VehicleHistoryAppDefaultsTest { @Test - void applicationDefaultsKeepHistoryAsDecoderConsumerAndStorageRuntime() { + void applicationDefaultsKeepHistoryAsDecoderConsumerAndStorageRuntime() throws IOException { Properties properties = applicationProperties(); assertThat(properties) @@ -25,7 +27,6 @@ class VehicleHistoryAppDefaultsTest { .containsEntry("spring.cloud.nacos.config.server-addr", "${NACOS_SERVER_ADDR:127.0.0.1:8848}") .containsEntry("lingniu.ingest.gb32960.enabled", true) .containsEntry("lingniu.ingest.gb32960.server.enabled", false) - .containsEntry("lingniu.ingest.sink.archive.enabled", false) .containsEntry("lingniu.ingest.tdengine-history.enabled", "${TDENGINE_HISTORY_ENABLED:false}") .containsEntry("lingniu.ingest.tdengine-history.database", "${TDENGINE_HISTORY_DATABASE:vehicle_history}") .containsEntry( @@ -104,12 +105,13 @@ class VehicleHistoryAppDefaultsTest { assertThat(properties.stringPropertyNames()) .noneMatch(name -> name.startsWith("lingniu.ingest.vehicle-state.")) .noneMatch(name -> name.startsWith("lingniu.ingest.vehicle-stat.")) + .noneMatch(name -> name.startsWith("lingniu.ingest.sink.archive.")) .noneMatch(name -> name.startsWith("lingniu.ingest.event-file-store.")); assertThat(properties.stringPropertyNames()) .noneMatch(name -> name.equals("lingniu.ingest.tdengine-history.telemetry-fields-enabled")); - assertThat(properties.stringPropertyNames()) - .noneMatch(name -> name.equals("lingniu.ingest.sink.archive.type") - || name.equals("lingniu.ingest.sink.archive.path")); + assertThat(applicationYaml()) + .doesNotContain("\n archive:\n") + .doesNotContain("sink.archive"); } @Test @@ -134,4 +136,9 @@ class VehicleHistoryAppDefaultsTest { assertThat(properties).isNotNull(); return properties; } + + private static String applicationYaml() throws IOException { + return new String(new ClassPathResource("application.yml").getInputStream().readAllBytes(), + StandardCharsets.UTF_8); + } } diff --git a/modules/services/vehicle-stat-service/src/test/java/com/lingniu/ingest/vehiclestat/VehicleStatRepositoryContractTest.java b/modules/services/vehicle-stat-service/src/test/java/com/lingniu/ingest/vehiclestat/VehicleStatRepositoryContractTest.java index 46143e60..b1071c28 100644 --- a/modules/services/vehicle-stat-service/src/test/java/com/lingniu/ingest/vehiclestat/VehicleStatRepositoryContractTest.java +++ b/modules/services/vehicle-stat-service/src/test/java/com/lingniu/ingest/vehiclestat/VehicleStatRepositoryContractTest.java @@ -25,6 +25,7 @@ class VehicleStatRepositoryContractTest { .resolve("docs/superpowers/plans/2026-06-30-kafka-streaming-mileage.md")); assertThat(plan) + .contains("Only supported message backbone: Kafka.") .contains("Runtime state: none outside `vehicle_stat_metric`") .contains("daily_mileage_km = max_total_mileage_km - min_total_mileage_km") .contains("Do not create or write a protocol-specific JT808 daily-mileage table.") @@ -40,6 +41,7 @@ class VehicleStatRepositoryContractTest { String architecture = Files.readString(repositoryRoot().resolve("docs/target-architecture.md")); assertThat(architecture) + .contains("Only supported message backbone: Kafka.") .contains("Runtime state: none outside `vehicle_stat_metric`") .contains("Restart recovery reads the same metric row") .contains("JT808 daily mileage is stored only in `vehicle_stat_metric`") @@ -54,6 +56,7 @@ class VehicleStatRepositoryContractTest { .resolve("docs/operations/jt808-daily-mileage-runbook.md")); assertThat(runbook) + .contains("Only supported message backbone: Kafka.") .contains("Runtime state: none outside `vehicle_stat_metric`") .contains("Restart recovery reads the same `daily_mileage_km` metric row") .doesNotContain("Redis mileage state")