feat: register xinda vehicle identities
This commit is contained in:
@@ -10,8 +10,12 @@ import com.lingniu.ingest.api.spi.EventMapper;
|
|||||||
import com.lingniu.ingest.identity.InMemoryVehicleIdentityService;
|
import com.lingniu.ingest.identity.InMemoryVehicleIdentityService;
|
||||||
import com.lingniu.ingest.identity.VehicleIdentity;
|
import com.lingniu.ingest.identity.VehicleIdentity;
|
||||||
import com.lingniu.ingest.identity.VehicleIdentityLookup;
|
import com.lingniu.ingest.identity.VehicleIdentityLookup;
|
||||||
|
import com.lingniu.ingest.identity.VehicleIdentityRegistry;
|
||||||
import com.lingniu.ingest.identity.VehicleIdentityResolver;
|
import com.lingniu.ingest.identity.VehicleIdentityResolver;
|
||||||
import com.lingniu.ingest.identity.VehicleIdentitySource;
|
import com.lingniu.ingest.identity.VehicleIdentitySource;
|
||||||
|
import com.lingniu.ingest.identity.VehicleRegistrationBinding;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -22,7 +26,11 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public final class XindaPushEventMapper implements EventMapper<XindaPushMessage> {
|
public final class XindaPushEventMapper implements EventMapper<XindaPushMessage> {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(XindaPushEventMapper.class);
|
||||||
|
private static final VehicleIdentityRegistry NOOP_REGISTRY = binding -> {};
|
||||||
|
|
||||||
private final VehicleIdentityResolver identityResolver;
|
private final VehicleIdentityResolver identityResolver;
|
||||||
|
private final VehicleIdentityRegistry identityRegistry;
|
||||||
|
|
||||||
public XindaPushEventMapper() {
|
public XindaPushEventMapper() {
|
||||||
this(new InMemoryVehicleIdentityService());
|
this(new InMemoryVehicleIdentityService());
|
||||||
@@ -30,6 +38,12 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
|||||||
|
|
||||||
public XindaPushEventMapper(VehicleIdentityResolver identityResolver) {
|
public XindaPushEventMapper(VehicleIdentityResolver identityResolver) {
|
||||||
this.identityResolver = identityResolver == null ? new InMemoryVehicleIdentityService() : identityResolver;
|
this.identityResolver = identityResolver == null ? new InMemoryVehicleIdentityService() : identityResolver;
|
||||||
|
this.identityRegistry = this.identityResolver instanceof VehicleIdentityRegistry registry ? registry : NOOP_REGISTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XindaPushEventMapper(VehicleIdentityResolver identityResolver, VehicleIdentityRegistry identityRegistry) {
|
||||||
|
this.identityResolver = identityResolver == null ? new InMemoryVehicleIdentityService() : identityResolver;
|
||||||
|
this.identityRegistry = identityRegistry == null ? NOOP_REGISTRY : identityRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -140,12 +154,14 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
|||||||
|
|
||||||
private IdentityResolution resolveIdentity(JSONObject json) {
|
private IdentityResolution resolveIdentity(JSONObject json) {
|
||||||
try {
|
try {
|
||||||
return IdentityResolution.ok(identityResolver.resolve(new VehicleIdentityLookup(
|
IdentityResolution resolution = IdentityResolution.ok(identityResolver.resolve(new VehicleIdentityLookup(
|
||||||
ProtocolId.XINDA_PUSH,
|
ProtocolId.XINDA_PUSH,
|
||||||
firstNonBlank(json, "vin"),
|
firstNonBlank(json, "vin"),
|
||||||
firstNonBlank(json, "sim", "phone", "mobile", "simCode"),
|
phone(json),
|
||||||
firstNonBlank(json, "deviceId", "terminalId", "imei", "tmnKey", "tmnCode"),
|
deviceId(json),
|
||||||
plateNo(json))));
|
plateNo(json))));
|
||||||
|
registerIdentity(json, resolution);
|
||||||
|
return resolution;
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return new IdentityResolution(
|
return new IdentityResolution(
|
||||||
new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN),
|
new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN),
|
||||||
@@ -172,8 +188,8 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
|||||||
out.put("rawJson", message.json());
|
out.put("rawJson", message.json());
|
||||||
out.put("vin", normalizedVin(identity));
|
out.put("vin", normalizedVin(identity));
|
||||||
out.put("externalVin", firstNonBlank(json, "vin"));
|
out.put("externalVin", firstNonBlank(json, "vin"));
|
||||||
out.put("phone", firstNonBlank(json, "sim", "phone", "mobile", "simCode"));
|
out.put("phone", phone(json));
|
||||||
out.put("deviceId", firstNonBlank(json, "deviceId", "terminalId", "imei", "tmnKey", "tmnCode"));
|
out.put("deviceId", deviceId(json));
|
||||||
out.put("plateNo", plateNo(json));
|
out.put("plateNo", plateNo(json));
|
||||||
out.put("identityResolved", Boolean.toString(identity.identity().resolved()));
|
out.put("identityResolved", Boolean.toString(identity.identity().resolved()));
|
||||||
out.put("identitySource", identity.identity().source().name());
|
out.put("identitySource", identity.identity().source().name());
|
||||||
@@ -247,6 +263,40 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
|||||||
return firstNonBlank(node, "plateNo", "carNo", "carName");
|
return firstNonBlank(node, "plateNo", "carNo", "carName");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String phone(JSONObject node) {
|
||||||
|
return firstNonBlank(node, "sim", "phone", "mobile", "simCode", "tmnKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String deviceId(JSONObject node) {
|
||||||
|
return firstNonBlank(node, "deviceId", "terminalId", "imei", "tmnKey", "tmnCode");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerIdentity(JSONObject json, IdentityResolution identity) {
|
||||||
|
String phone = phone(json);
|
||||||
|
String deviceId = deviceId(json);
|
||||||
|
String plate = plateNo(json);
|
||||||
|
if (phone.isBlank() && deviceId.isBlank() && plate.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String vin = identity != null && identity.identity().resolved() ? identity.identity().vin() : "unknown";
|
||||||
|
try {
|
||||||
|
identityRegistry.register(new VehicleRegistrationBinding(
|
||||||
|
ProtocolId.XINDA_PUSH,
|
||||||
|
vin,
|
||||||
|
phone,
|
||||||
|
deviceId,
|
||||||
|
plate,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
optionalInteger(json, "plateColor", "carColor")));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.warn("[xinda-push] vehicle identity registration failed plate={} phone={} deviceId={}",
|
||||||
|
plate, phone, deviceId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static double asDouble(JSONObject node, String... keys) {
|
private static double asDouble(JSONObject node, String... keys) {
|
||||||
Double value = optionalDouble(node, keys);
|
Double value = optionalDouble(node, keys);
|
||||||
return value == null ? 0.0 : value;
|
return value == null ? 0.0 : value;
|
||||||
@@ -271,6 +321,11 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
|||||||
return (int) asDouble(node, keys);
|
return (int) asDouble(node, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Integer optionalInteger(JSONObject node, String... keys) {
|
||||||
|
Double value = optionalDouble(node, keys);
|
||||||
|
return value == null ? null : value.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
private static double normalizeCoord(double value) {
|
private static double normalizeCoord(double value) {
|
||||||
if (Math.abs(value) > 1000) {
|
if (Math.abs(value) > 1000) {
|
||||||
return value / 1_000_000.0;
|
return value / 1_000_000.0;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.lingniu.ingest.inbound.xinda.config;
|
package com.lingniu.ingest.inbound.xinda.config;
|
||||||
|
|
||||||
import com.lingniu.ingest.core.dispatcher.Dispatcher;
|
import com.lingniu.ingest.core.dispatcher.Dispatcher;
|
||||||
|
import com.lingniu.ingest.identity.VehicleIdentityRegistry;
|
||||||
import com.lingniu.ingest.identity.VehicleIdentityResolver;
|
import com.lingniu.ingest.identity.VehicleIdentityResolver;
|
||||||
import com.lingniu.ingest.inbound.xinda.XindaPushClient;
|
import com.lingniu.ingest.inbound.xinda.XindaPushClient;
|
||||||
import com.lingniu.ingest.inbound.xinda.XindaPushEventMapper;
|
import com.lingniu.ingest.inbound.xinda.XindaPushEventMapper;
|
||||||
@@ -28,8 +29,9 @@ public class XindaPushAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public XindaPushEventMapper xindaPushEventMapper(VehicleIdentityResolver identityResolver) {
|
public XindaPushEventMapper xindaPushEventMapper(VehicleIdentityResolver identityResolver,
|
||||||
return new XindaPushEventMapper(identityResolver);
|
VehicleIdentityRegistry identityRegistry) {
|
||||||
|
return new XindaPushEventMapper(identityResolver, identityRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import com.lingniu.ingest.api.event.AlarmPayload;
|
|||||||
import com.lingniu.ingest.api.event.VehicleEvent;
|
import com.lingniu.ingest.api.event.VehicleEvent;
|
||||||
import com.lingniu.ingest.identity.InMemoryVehicleIdentityService;
|
import com.lingniu.ingest.identity.InMemoryVehicleIdentityService;
|
||||||
import com.lingniu.ingest.identity.VehicleIdentityBinding;
|
import com.lingniu.ingest.identity.VehicleIdentityBinding;
|
||||||
|
import com.lingniu.ingest.identity.VehicleIdentityRegistry;
|
||||||
|
import com.lingniu.ingest.identity.VehicleRegistrationBinding;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -134,6 +136,49 @@ class XindaPushEventMapperTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registersXindaIdentityFromCarNamePayload() {
|
||||||
|
RecordingIdentityRegistry registry = new RecordingIdentityRegistry();
|
||||||
|
identity.bind(new VehicleIdentityBinding(
|
||||||
|
ProtocolId.XINDA_PUSH, "LA9GG68L8PBAF4776", "", "", "浙F00780F"));
|
||||||
|
XindaPushEventMapper mapper = new XindaPushEventMapper(identity, registry);
|
||||||
|
|
||||||
|
mapper.toEvents(new XindaPushMessage("0200",
|
||||||
|
"{\"carName\":\"浙F00780F\",\"tmnKey\":\"13307811342\",\"simCode\":\"8986062582008805648\","
|
||||||
|
+ "\"carColor\":\"93\",\"lng\":121.075223,\"lat\":30.583753}"));
|
||||||
|
|
||||||
|
assertThat(registry.registration)
|
||||||
|
.isNotNull()
|
||||||
|
.satisfies(registration -> {
|
||||||
|
assertThat(registration.protocol()).isEqualTo(ProtocolId.XINDA_PUSH);
|
||||||
|
assertThat(registration.vin()).isEqualTo("LA9GG68L8PBAF4776");
|
||||||
|
assertThat(registration.phone()).isEqualTo("8986062582008805648");
|
||||||
|
assertThat(registration.deviceId()).isEqualTo("13307811342");
|
||||||
|
assertThat(registration.plate()).isEqualTo("浙F00780F");
|
||||||
|
assertThat(registration.plateColor()).isEqualTo(93);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registersUnknownVinWhenXindaIdentityIsNotResolved() {
|
||||||
|
RecordingIdentityRegistry registry = new RecordingIdentityRegistry();
|
||||||
|
XindaPushEventMapper mapper = new XindaPushEventMapper(identity, registry);
|
||||||
|
|
||||||
|
mapper.toEvents(new XindaPushMessage("0200",
|
||||||
|
"{\"carName\":\"浙F00780F\",\"tmnKey\":\"13307811342\","
|
||||||
|
+ "\"lng\":121.075223,\"lat\":30.583753}"));
|
||||||
|
|
||||||
|
assertThat(registry.registration)
|
||||||
|
.isNotNull()
|
||||||
|
.satisfies(registration -> {
|
||||||
|
assertThat(registration.protocol()).isEqualTo(ProtocolId.XINDA_PUSH);
|
||||||
|
assertThat(registration.vin()).isEqualTo("unknown");
|
||||||
|
assertThat(registration.phone()).isEqualTo("13307811342");
|
||||||
|
assertThat(registration.deviceId()).isEqualTo("13307811342");
|
||||||
|
assertThat(registration.plate()).isEqualTo("浙F00780F");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void identityResolverFailureStillProducesLocationEventWithUnknownVin() {
|
void identityResolverFailureStillProducesLocationEventWithUnknownVin() {
|
||||||
XindaPushEventMapper mapperWithFailingIdentity = new XindaPushEventMapper(lookup -> {
|
XindaPushEventMapper mapperWithFailingIdentity = new XindaPushEventMapper(lookup -> {
|
||||||
@@ -233,4 +278,17 @@ class XindaPushEventMapperTest {
|
|||||||
.containsEntry("parseError", "false");
|
.containsEntry("parseError", "false");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class RecordingIdentityRegistry implements VehicleIdentityRegistry {
|
||||||
|
private VehicleRegistrationBinding registration;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bind(VehicleIdentityBinding binding) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void register(VehicleRegistrationBinding registration) {
|
||||||
|
this.registration = registration;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user