diff --git a/modules/core/vehicle-identity/src/main/java/com/lingniu/ingest/identity/StatelessVehicleIdentityResolver.java b/modules/core/vehicle-identity/src/main/java/com/lingniu/ingest/identity/StatelessVehicleIdentityResolver.java new file mode 100644 index 00000000..ecaa45f2 --- /dev/null +++ b/modules/core/vehicle-identity/src/main/java/com/lingniu/ingest/identity/StatelessVehicleIdentityResolver.java @@ -0,0 +1,34 @@ +package com.lingniu.ingest.identity; + +/** + * Stateless production fallback used when a caller has no injected identity service. + * + *
Persistent binding belongs to {@link MySqlVehicleIdentityService}; this resolver only keeps
+ * ingestion flowing with explicit VIN or stable external identifiers.
+ */
+public final class StatelessVehicleIdentityResolver implements VehicleIdentityResolver {
+
+ @Override
+ public VehicleIdentity resolve(VehicleIdentityLookup lookup) {
+ if (lookup == null) {
+ return unknown();
+ }
+ if (!lookup.vin().isBlank()) {
+ return new VehicleIdentity(lookup.vin(), true, VehicleIdentitySource.EXPLICIT_VIN);
+ }
+ if (!lookup.deviceId().isBlank()) {
+ return new VehicleIdentity(lookup.deviceId(), false, VehicleIdentitySource.FALLBACK_DEVICE_ID);
+ }
+ if (!lookup.phone().isBlank()) {
+ return new VehicleIdentity(lookup.phone(), false, VehicleIdentitySource.FALLBACK_PHONE);
+ }
+ if (!lookup.plate().isBlank()) {
+ return new VehicleIdentity(lookup.plate(), false, VehicleIdentitySource.FALLBACK_PLATE);
+ }
+ return unknown();
+ }
+
+ private static VehicleIdentity unknown() {
+ return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
+ }
+}
diff --git a/modules/core/vehicle-identity/src/test/java/com/lingniu/ingest/identity/StatelessVehicleIdentityResolverTest.java b/modules/core/vehicle-identity/src/test/java/com/lingniu/ingest/identity/StatelessVehicleIdentityResolverTest.java
new file mode 100644
index 00000000..5314cf19
--- /dev/null
+++ b/modules/core/vehicle-identity/src/test/java/com/lingniu/ingest/identity/StatelessVehicleIdentityResolverTest.java
@@ -0,0 +1,57 @@
+package com.lingniu.ingest.identity;
+
+import com.lingniu.ingest.api.ProtocolId;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class StatelessVehicleIdentityResolverTest {
+
+ private final StatelessVehicleIdentityResolver resolver = new StatelessVehicleIdentityResolver();
+
+ @Test
+ void explicitVinWinsWithoutPersistingAnyBinding() {
+ VehicleIdentity identity = resolver.resolve(new VehicleIdentityLookup(
+ ProtocolId.MQTT_YUTONG, "LNVIN000000000001", "13900000000", "DEV001", "粤B12345"));
+
+ assertThat(identity.vin()).isEqualTo("LNVIN000000000001");
+ assertThat(identity.resolved()).isTrue();
+ assertThat(identity.source()).isEqualTo(VehicleIdentitySource.EXPLICIT_VIN);
+ }
+
+ @Test
+ void fallsBackToStableExternalIdentifiersWithoutMarkingResolved() {
+ assertThat(resolver.resolve(new VehicleIdentityLookup(
+ ProtocolId.JT808, "", "13900000001", "DEV001", "粤B12345")))
+ .satisfies(identity -> {
+ assertThat(identity.vin()).isEqualTo("DEV001");
+ assertThat(identity.resolved()).isFalse();
+ assertThat(identity.source()).isEqualTo(VehicleIdentitySource.FALLBACK_DEVICE_ID);
+ });
+
+ assertThat(resolver.resolve(new VehicleIdentityLookup(
+ ProtocolId.JT808, "", "13900000001", "", "粤B12345")))
+ .satisfies(identity -> {
+ assertThat(identity.vin()).isEqualTo("13900000001");
+ assertThat(identity.resolved()).isFalse();
+ assertThat(identity.source()).isEqualTo(VehicleIdentitySource.FALLBACK_PHONE);
+ });
+
+ assertThat(resolver.resolve(new VehicleIdentityLookup(
+ ProtocolId.JT808, "", "", "", "粤B12345")))
+ .satisfies(identity -> {
+ assertThat(identity.vin()).isEqualTo("粤B12345");
+ assertThat(identity.resolved()).isFalse();
+ assertThat(identity.source()).isEqualTo(VehicleIdentitySource.FALLBACK_PLATE);
+ });
+ }
+
+ @Test
+ void returnsUnknownWhenNoIdentitySignalExists() {
+ VehicleIdentity identity = resolver.resolve(new VehicleIdentityLookup(ProtocolId.JT808, "", "", "", ""));
+
+ assertThat(identity.vin()).isEqualTo("unknown");
+ assertThat(identity.resolved()).isFalse();
+ assertThat(identity.source()).isEqualTo(VehicleIdentitySource.UNKNOWN);
+ }
+}
diff --git a/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/client/MqttEndpointManager.java b/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/client/MqttEndpointManager.java
index ae2989bf..61a1ff51 100644
--- a/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/client/MqttEndpointManager.java
+++ b/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/client/MqttEndpointManager.java
@@ -3,7 +3,7 @@ package com.lingniu.ingest.inbound.mqtt.client;
import com.lingniu.ingest.api.ProtocolId;
import com.lingniu.ingest.api.pipeline.RawFrame;
import com.lingniu.ingest.core.dispatcher.Dispatcher;
-import com.lingniu.ingest.identity.InMemoryVehicleIdentityService;
+import com.lingniu.ingest.identity.StatelessVehicleIdentityResolver;
import com.lingniu.ingest.identity.VehicleIdentity;
import com.lingniu.ingest.identity.VehicleIdentityLookup;
import com.lingniu.ingest.identity.VehicleIdentityResolver;
@@ -53,7 +53,7 @@ public final class MqttEndpointManager implements AutoCloseable {
public MqttEndpointManager(MqttInboundProperties props,
MqttProfileRegistry profileRegistry,
Dispatcher dispatcher) {
- this(props, profileRegistry, new InMemoryVehicleIdentityService(), dispatcher);
+ this(props, profileRegistry, new StatelessVehicleIdentityResolver(), dispatcher);
}
public MqttEndpointManager(MqttInboundProperties props,
@@ -62,7 +62,7 @@ public final class MqttEndpointManager implements AutoCloseable {
Dispatcher dispatcher) {
this.props = props;
this.profileRegistry = profileRegistry;
- this.identityResolver = identityResolver == null ? new InMemoryVehicleIdentityService() : identityResolver;
+ this.identityResolver = identityResolver == null ? new StatelessVehicleIdentityResolver() : identityResolver;
this.dispatcher = dispatcher;
}
diff --git a/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/mapper/YutongEventMapper.java b/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/mapper/YutongEventMapper.java
index 756c0706..3087a925 100644
--- a/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/mapper/YutongEventMapper.java
+++ b/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/mapper/YutongEventMapper.java
@@ -6,7 +6,7 @@ import com.lingniu.ingest.api.event.LocationPayload;
import com.lingniu.ingest.api.event.RealtimePayload;
import com.lingniu.ingest.api.event.VehicleEvent;
import com.lingniu.ingest.api.spi.EventMapper;
-import com.lingniu.ingest.identity.InMemoryVehicleIdentityService;
+import com.lingniu.ingest.identity.StatelessVehicleIdentityResolver;
import com.lingniu.ingest.identity.VehicleIdentity;
import com.lingniu.ingest.identity.VehicleIdentityLookup;
import com.lingniu.ingest.identity.VehicleIdentityResolver;
@@ -31,11 +31,11 @@ public final class YutongEventMapper implements EventMapper