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.VehicleIdentity;
|
||||
import com.lingniu.ingest.identity.VehicleIdentityLookup;
|
||||
import com.lingniu.ingest.identity.VehicleIdentityRegistry;
|
||||
import com.lingniu.ingest.identity.VehicleIdentityResolver;
|
||||
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.time.Instant;
|
||||
@@ -22,7 +26,11 @@ import java.util.UUID;
|
||||
|
||||
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 VehicleIdentityRegistry identityRegistry;
|
||||
|
||||
public XindaPushEventMapper() {
|
||||
this(new InMemoryVehicleIdentityService());
|
||||
@@ -30,6 +38,12 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
||||
|
||||
public XindaPushEventMapper(VehicleIdentityResolver 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
|
||||
@@ -140,12 +154,14 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
||||
|
||||
private IdentityResolution resolveIdentity(JSONObject json) {
|
||||
try {
|
||||
return IdentityResolution.ok(identityResolver.resolve(new VehicleIdentityLookup(
|
||||
IdentityResolution resolution = IdentityResolution.ok(identityResolver.resolve(new VehicleIdentityLookup(
|
||||
ProtocolId.XINDA_PUSH,
|
||||
firstNonBlank(json, "vin"),
|
||||
firstNonBlank(json, "sim", "phone", "mobile", "simCode"),
|
||||
firstNonBlank(json, "deviceId", "terminalId", "imei", "tmnKey", "tmnCode"),
|
||||
phone(json),
|
||||
deviceId(json),
|
||||
plateNo(json))));
|
||||
registerIdentity(json, resolution);
|
||||
return resolution;
|
||||
} catch (RuntimeException e) {
|
||||
return new IdentityResolution(
|
||||
new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN),
|
||||
@@ -172,8 +188,8 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
||||
out.put("rawJson", message.json());
|
||||
out.put("vin", normalizedVin(identity));
|
||||
out.put("externalVin", firstNonBlank(json, "vin"));
|
||||
out.put("phone", firstNonBlank(json, "sim", "phone", "mobile", "simCode"));
|
||||
out.put("deviceId", firstNonBlank(json, "deviceId", "terminalId", "imei", "tmnKey", "tmnCode"));
|
||||
out.put("phone", phone(json));
|
||||
out.put("deviceId", deviceId(json));
|
||||
out.put("plateNo", plateNo(json));
|
||||
out.put("identityResolved", Boolean.toString(identity.identity().resolved()));
|
||||
out.put("identitySource", identity.identity().source().name());
|
||||
@@ -247,6 +263,40 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
||||
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) {
|
||||
Double value = optionalDouble(node, keys);
|
||||
return value == null ? 0.0 : value;
|
||||
@@ -271,6 +321,11 @@ public final class XindaPushEventMapper implements EventMapper<XindaPushMessage>
|
||||
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) {
|
||||
if (Math.abs(value) > 1000) {
|
||||
return value / 1_000_000.0;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.lingniu.ingest.inbound.xinda.config;
|
||||
|
||||
import com.lingniu.ingest.core.dispatcher.Dispatcher;
|
||||
import com.lingniu.ingest.identity.VehicleIdentityRegistry;
|
||||
import com.lingniu.ingest.identity.VehicleIdentityResolver;
|
||||
import com.lingniu.ingest.inbound.xinda.XindaPushClient;
|
||||
import com.lingniu.ingest.inbound.xinda.XindaPushEventMapper;
|
||||
@@ -28,8 +29,9 @@ public class XindaPushAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public XindaPushEventMapper xindaPushEventMapper(VehicleIdentityResolver identityResolver) {
|
||||
return new XindaPushEventMapper(identityResolver);
|
||||
public XindaPushEventMapper xindaPushEventMapper(VehicleIdentityResolver identityResolver,
|
||||
VehicleIdentityRegistry identityRegistry) {
|
||||
return new XindaPushEventMapper(identityResolver, identityRegistry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -5,6 +5,8 @@ import com.lingniu.ingest.api.event.AlarmPayload;
|
||||
import com.lingniu.ingest.api.event.VehicleEvent;
|
||||
import com.lingniu.ingest.identity.InMemoryVehicleIdentityService;
|
||||
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 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
|
||||
void identityResolverFailureStillProducesLocationEventWithUnknownVin() {
|
||||
XindaPushEventMapper mapperWithFailingIdentity = new XindaPushEventMapper(lookup -> {
|
||||
@@ -233,4 +278,17 @@ class XindaPushEventMapperTest {
|
||||
.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