diff --git a/DECISIONS.md b/DECISIONS.md
index 24809262..affc2ebb 100644
--- a/DECISIONS.md
+++ b/DECISIONS.md
@@ -81,3 +81,12 @@
2. JSATL12 仅保留在 `optional-attachments` profile,需要附件上传能力时显式构建。
3. 默认优化和验证优先覆盖活跃接入链路,附件上传能力保持源码可用但不增加默认构建面。
- **Consequences**: 默认构建更轻,生产部署边界更清晰;附件上传相关测试需要通过 `-Poptional-attachments` 显式运行。
+
+## ADR-013 最新状态服务:Optional only
+- **Status**: Accepted
+- **Context**: Redis 最新状态查询是独立消费能力,但当前默认 Portainer 部署和 Woodpecker 镜像发布只包含三条接入链路、history 和 analytics;`vehicle-state-service` 没有独立 app,也不应增加默认构建面。
+- **Decision**:
+ 1. `vehicle-state-service` 不进入默认 Maven reactor。
+ 2. vehicle-state-service 仅保留在 `optional-latest-state` profile,需要 Redis 最新状态查询能力时显式构建。
+ 3. 默认优化和验证优先覆盖活跃接入、TDengine 历史和 MySQL 指标链路。
+- **Consequences**: 默认构建和部署边界继续收窄;最新状态能力保持源码可用,但其测试需要通过 `-Poptional-latest-state` 显式运行。
diff --git a/README.md b/README.md
index 046286f8..ff67a362 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ lingniu-vehicle-ingest/
│ │ └── event-file-store/ 可选兼容索引(旧 Parquet/DuckDB 路径)
│ ├── services/
│ │ ├── event-history-service/ Kafka 全字段事件消费 + 历史查询/导出
-│ │ ├── vehicle-state-service/ Kafka 全字段事件消费 + Redis 热状态查询
+│ │ ├── vehicle-state-service/ Kafka 全字段事件消费 + Redis 热状态查询(optional-latest-state profile)
│ │ └── vehicle-stat-service/ Kafka 全字段事件消费 + 可配置日统计
│ └── apps/
│ ├── command-gateway/ HTTP → 设备下行命令(808 位置/参数/属性/控制/区域删除/报警确认 + 1078 音视频控制)
@@ -80,6 +80,8 @@ mvn -pl :jt808-ingest-app -am spring-boot:run
JSATL12 附件上传不在默认 Maven reactor 中;需要时使用 `-Poptional-attachments` 构建。
+Redis 最新状态查询不在默认 Maven reactor 中;需要时使用 `-Poptional-latest-state` 构建 `vehicle-state-service`。
+
## 迁移说明
本项目是 `lingniu-vehicle-data-reception` 的 v2 重构,采用 strangler fig 渐进式迁移,旧项目保留只读参考。迁移路径与决策参见 `../REFRACTOR_PLAN.md`。
diff --git a/docs/module-data-flow.html b/docs/module-data-flow.html
index 3a68e195..2f5efb97 100644
--- a/docs/module-data-flow.html
+++ b/docs/module-data-flow.html
@@ -325,7 +325,7 @@ flowchart TB
subgraph services["消费服务 modules/services"]
history["event-history-service
Kafka event/raw 到 TDengine 历史查询"]
- state["vehicle-state-service
Kafka 全字段事件到 Redis 热状态"]
+ state["vehicle-state-service
可选 Redis 热状态
optional-latest-state"]
stat["vehicle-stat-service
Kafka 全字段事件到指标表"]
end
@@ -614,7 +614,7 @@ flowchart LR
| modules/services/vehicle-state-service |
- 消费 Kafka 全字段事件,更新车辆最新状态、位置、安全和最后事件到 Redis;生产消费可使用 tryIngest 和自动装配的 EnvelopeConsumerProcessor 隔离坏 protobuf 和缺快照 Envelope,并把失败记录交给死信出口。 |
+ 消费 Kafka 全字段事件,更新车辆最新状态、位置、安全和最后事件到 Redis;仅通过 optional-latest-state profile 显式构建,不属于默认生产 reactor;生产消费可使用 tryIngest 和自动装配的 EnvelopeConsumerProcessor 隔离坏 protobuf 和缺快照 Envelope,并把失败记录交给死信出口。 |
Kafka Protobuf Envelope。 |
vehicle:state:{vin}、vehicle:location:{vin}、vehicle:safety:{vin}。 |
毫秒级热查询氢泄露安全 |
diff --git a/docs/operations/gb32960-service-split-runbook.md b/docs/operations/gb32960-service-split-runbook.md
index 3a239169..a5d04256 100644
--- a/docs/operations/gb32960-service-split-runbook.md
+++ b/docs/operations/gb32960-service-split-runbook.md
@@ -136,7 +136,7 @@ java --sun-misc-unsafe-memory-access=allow \
-jar modules/apps/vehicle-analytics-app/target/vehicle-analytics-app.jar
```
-`vehicle-analytics-app` is intentionally a metric runtime only. Latest-state APIs belong to `vehicle-state-service` and should be deployed as a separate consumer if that surface is needed.
+`vehicle-analytics-app` is intentionally a metric runtime only. Latest-state APIs belong to `vehicle-state-service`, which stays outside the default reactor and should be built with `-Poptional-latest-state` and deployed as a separate consumer if that surface is needed.
## Health Verification
diff --git a/docs/target-architecture.md b/docs/target-architecture.md
index c1faf46d..a9181609 100644
--- a/docs/target-architecture.md
+++ b/docs/target-architecture.md
@@ -140,6 +140,9 @@ require a separate Redis mileage state.
Module: `vehicle-state-service`
+This module is outside the default production reactor. Build it with
+`-Poptional-latest-state` when Redis latest-state APIs are explicitly needed.
+
Responsibilities:
- Consume normalized Kafka envelopes.
@@ -183,6 +186,8 @@ flowchart LR
- Xinda Push is absent from default deployment and history consumer bindings.
- JSATL12 is absent from the default Maven reactor and available through
`optional-attachments`.
+- vehicle-state-service is absent from the default Maven reactor and available
+ through `optional-latest-state`.
- History APIs read from TDengine by default; Parquet/DuckDB is optional
compatibility only.
- Raw frame queries can return complete parsed JSON through `payloadJson.parsed`.
diff --git a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/MavenModuleProfileTest.java b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/MavenModuleProfileTest.java
index ae90f29d..d55a8cb1 100644
--- a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/MavenModuleProfileTest.java
+++ b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/MavenModuleProfileTest.java
@@ -41,6 +41,17 @@ class MavenModuleProfileTest {
.containsExactly("modules/protocols/protocol-jsatl12");
}
+ @Test
+ void latestStateServiceIsOptionalAndOutsideDefaultProductionReactor() throws Exception {
+ Document pom = rootPom();
+
+ assertThat(defaultModules(pom))
+ .doesNotContain("modules/services/vehicle-state-service");
+
+ assertThat(profileModules(pom, "optional-latest-state"))
+ .containsExactly("modules/services/vehicle-state-service");
+ }
+
@Test
void xindaIsLegacyOnlyAndOutsideDefaultProductionReactor() throws Exception {
Document pom = rootPom();
@@ -76,6 +87,7 @@ class MavenModuleProfileTest {
.contains("Eclipse Paho " + projectProperty(pom, "paho-mqtt.version"))
.contains("protocol-jsatl12/ 苏标主动安全报警附件(optional-attachments profile)")
.contains("event-file-store/ 可选兼容索引(旧 Parquet/DuckDB 路径)")
+ .contains("vehicle-state-service/ Kafka 全字段事件消费 + Redis 热状态查询(optional-latest-state profile)")
.contains("vehicle-history-app/ TDengine 历史查询 + RAW JSON")
.contains("vehicle-analytics-app/ JT808 每日里程指标消费")
.doesNotContain("Spring Boot 3.4")
@@ -108,6 +120,7 @@ class MavenModuleProfileTest {
.contains("GB32960、JT808、Yutong MQTT、vehicle-history-app、vehicle-analytics-app")
.contains("Xinda Push 仅保留在 `legacy-xinda` profile")
.contains("JSATL12 仅保留在 `optional-attachments` profile")
+ .contains("vehicle-state-service 仅保留在 `optional-latest-state` profile")
.doesNotContain("## ADR-004 信达 Push:保留模块但彻底重写\n- **Status**: Accepted")
.doesNotContain("Spring Boot 3.4.x");
}
diff --git a/pom.xml b/pom.xml
index 2feb3a3e..0ac5f217 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,6 @@
modules/sinks/event-file-store
modules/sinks/tdengine-history-store
modules/services/event-history-service
- modules/services/vehicle-state-service
modules/services/vehicle-stat-service
modules/protocols/protocol-gb32960
modules/protocols/protocol-jt808
@@ -56,6 +55,12 @@
modules/protocols/protocol-jsatl12
+
+ optional-latest-state
+
+ modules/services/vehicle-state-service
+
+
legacy-xinda