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. 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 ## 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. 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 ## Current Design
- Only supported message backbone: Kafka.
- Source topic: `vehicle.event.jt808.v1` - Source topic: `vehicle.event.jt808.v1`
- Runtime app: `vehicle-analytics-app` - Runtime app: `vehicle-analytics-app`
- Runtime state: none outside `vehicle_stat_metric` - Runtime state: none outside `vehicle_stat_metric`
@@ -42,4 +43,4 @@ MYSQL_PASSWORD=<password>
## Notes ## 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 history consumes and indexes, analytics derives metrics, and business systems
read through explicit APIs or Kafka. read through explicit APIs or Kafka.
Only supported message backbone: Kafka.
## Active Scope ## Active Scope
Active production protocols: 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} group-id: ${KAFKA_GROUP_HISTORY_YUTONG_MQTT_RAW:${KAFKA_GROUP_HISTORY:vehicle-history}-yutong-mqtt-raw}
topics: topics:
- ${KAFKA_TOPIC_YUTONG_MQTT_RAW:vehicle.raw.mqtt-yutong.v1} - ${KAFKA_TOPIC_YUTONG_MQTT_RAW:vehicle.raw.mqtt-yutong.v1}
archive:
enabled: false
tdengine-history: tdengine-history:
enabled: ${TDENGINE_HISTORY_ENABLED:false} enabled: ${TDENGINE_HISTORY_ENABLED:false}
database: ${TDENGINE_HISTORY_DATABASE:vehicle_history} 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.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
@@ -14,7 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class VehicleHistoryAppDefaultsTest { class VehicleHistoryAppDefaultsTest {
@Test @Test
void applicationDefaultsKeepHistoryAsDecoderConsumerAndStorageRuntime() { void applicationDefaultsKeepHistoryAsDecoderConsumerAndStorageRuntime() throws IOException {
Properties properties = applicationProperties(); Properties properties = applicationProperties();
assertThat(properties) assertThat(properties)
@@ -25,7 +27,6 @@ class VehicleHistoryAppDefaultsTest {
.containsEntry("spring.cloud.nacos.config.server-addr", "${NACOS_SERVER_ADDR:127.0.0.1:8848}") .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.enabled", true)
.containsEntry("lingniu.ingest.gb32960.server.enabled", false) .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.enabled", "${TDENGINE_HISTORY_ENABLED:false}")
.containsEntry("lingniu.ingest.tdengine-history.database", "${TDENGINE_HISTORY_DATABASE:vehicle_history}") .containsEntry("lingniu.ingest.tdengine-history.database", "${TDENGINE_HISTORY_DATABASE:vehicle_history}")
.containsEntry( .containsEntry(
@@ -104,12 +105,13 @@ class VehicleHistoryAppDefaultsTest {
assertThat(properties.stringPropertyNames()) assertThat(properties.stringPropertyNames())
.noneMatch(name -> name.startsWith("lingniu.ingest.vehicle-state.")) .noneMatch(name -> name.startsWith("lingniu.ingest.vehicle-state."))
.noneMatch(name -> name.startsWith("lingniu.ingest.vehicle-stat.")) .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.")); .noneMatch(name -> name.startsWith("lingniu.ingest.event-file-store."));
assertThat(properties.stringPropertyNames()) assertThat(properties.stringPropertyNames())
.noneMatch(name -> name.equals("lingniu.ingest.tdengine-history.telemetry-fields-enabled")); .noneMatch(name -> name.equals("lingniu.ingest.tdengine-history.telemetry-fields-enabled"));
assertThat(properties.stringPropertyNames()) assertThat(applicationYaml())
.noneMatch(name -> name.equals("lingniu.ingest.sink.archive.type") .doesNotContain("\n archive:\n")
|| name.equals("lingniu.ingest.sink.archive.path")); .doesNotContain("sink.archive");
} }
@Test @Test
@@ -134,4 +136,9 @@ class VehicleHistoryAppDefaultsTest {
assertThat(properties).isNotNull(); assertThat(properties).isNotNull();
return properties; 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")); .resolve("docs/superpowers/plans/2026-06-30-kafka-streaming-mileage.md"));
assertThat(plan) assertThat(plan)
.contains("Only supported message backbone: Kafka.")
.contains("Runtime state: none outside `vehicle_stat_metric`") .contains("Runtime state: none outside `vehicle_stat_metric`")
.contains("daily_mileage_km = max_total_mileage_km - min_total_mileage_km") .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.") .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")); String architecture = Files.readString(repositoryRoot().resolve("docs/target-architecture.md"));
assertThat(architecture) assertThat(architecture)
.contains("Only supported message backbone: Kafka.")
.contains("Runtime state: none outside `vehicle_stat_metric`") .contains("Runtime state: none outside `vehicle_stat_metric`")
.contains("Restart recovery reads the same metric row") .contains("Restart recovery reads the same metric row")
.contains("JT808 daily mileage is stored only in `vehicle_stat_metric`") .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")); .resolve("docs/operations/jt808-daily-mileage-runbook.md"));
assertThat(runbook) assertThat(runbook)
.contains("Only supported message backbone: Kafka.")
.contains("Runtime state: none outside `vehicle_stat_metric`") .contains("Runtime state: none outside `vehicle_stat_metric`")
.contains("Restart recovery reads the same `daily_mileage_km` metric row") .contains("Restart recovery reads the same `daily_mileage_km` metric row")
.doesNotContain("Redis mileage state") .doesNotContain("Redis mileage state")