chore: enforce kafka-only stat path

This commit is contained in:
lingniu
2026-07-01 14:35:58 +08:00
parent cd12a84e51
commit f7bb5b1554
6 changed files with 21 additions and 8 deletions

View File

@@ -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.

View File

@@ -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=<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.

View File

@@ -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:

View File

@@ -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}

View File

@@ -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);
}
}

View File

@@ -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")