fix: avoid jt808 fallback ids as vin
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -177,20 +177,20 @@ public class Jt808ChannelHandler extends SimpleChannelInboundHandler<Object> {
|
|||||||
private IdentityResolution resolveIdentity(Jt808Message msg) {
|
private IdentityResolution resolveIdentity(Jt808Message msg) {
|
||||||
String phone = msg.header().phone();
|
String phone = msg.header().phone();
|
||||||
if (identityResolver == null) {
|
if (identityResolver == null) {
|
||||||
// 没有绑定表时用手机号兜底为 VIN,保证会话/下行仍可按 phone 找到连接。
|
// 没有绑定表时不伪造 VIN;phone 会保留在 metadata/session 中用于下行定位。
|
||||||
return new IdentityResolution(
|
return new IdentityResolution(
|
||||||
new VehicleIdentity(phone, false, VehicleIdentitySource.FALLBACK_PHONE),
|
new VehicleIdentity("unknown", false, VehicleIdentitySource.FALLBACK_PHONE),
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// 优先把 JT808 phone/device/plate 映射到内部 VIN,后续所有事件都用内部 VIN 做主键。
|
// 优先把 JT808 phone/device/plate 映射到内部 VIN,后续所有事件都用内部 VIN 做主键。
|
||||||
return new IdentityResolution(
|
return new IdentityResolution(
|
||||||
identityResolver.resolve(new VehicleIdentityLookup(
|
normalizeUnresolvedIdentity(identityResolver.resolve(new VehicleIdentityLookup(
|
||||||
ProtocolId.JT808,
|
ProtocolId.JT808,
|
||||||
"",
|
"",
|
||||||
phone,
|
phone,
|
||||||
deviceId(msg.body()),
|
deviceId(msg.body()),
|
||||||
plate(msg.body()))),
|
plate(msg.body())))),
|
||||||
null);
|
null);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return new IdentityResolution(
|
return new IdentityResolution(
|
||||||
@@ -199,6 +199,16 @@ public class Jt808ChannelHandler extends SimpleChannelInboundHandler<Object> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static VehicleIdentity normalizeUnresolvedIdentity(VehicleIdentity identity) {
|
||||||
|
if (identity == null) {
|
||||||
|
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
|
||||||
|
}
|
||||||
|
if (identity.resolved()) {
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
return new VehicleIdentity("unknown", false, identity.source());
|
||||||
|
}
|
||||||
|
|
||||||
private void dispatchFrameError(ChannelHandlerContext ctx, Jt808MalformedFrame malformed) {
|
private void dispatchFrameError(ChannelHandlerContext ctx, Jt808MalformedFrame malformed) {
|
||||||
String peer = malformed.peer() == null || malformed.peer().isBlank() ? addr(ctx) : malformed.peer();
|
String peer = malformed.peer() == null || malformed.peer().isBlank() ? addr(ctx) : malformed.peer();
|
||||||
dispatchMalformed(peer, malformed.rawBytes(), malformed.reason(), true);
|
dispatchMalformed(peer, malformed.rawBytes(), malformed.reason(), true);
|
||||||
|
|||||||
@@ -173,8 +173,8 @@ public final class Jt808EventMapper implements EventMapper<Jt808Message> {
|
|||||||
}
|
}
|
||||||
var header = message.header();
|
var header = message.header();
|
||||||
try {
|
try {
|
||||||
return IdentityResolution.ok(identityResolver.resolve(new VehicleIdentityLookup(
|
return IdentityResolution.ok(normalizeUnresolvedIdentity(identityResolver.resolve(new VehicleIdentityLookup(
|
||||||
ProtocolId.JT808, "", header.phone(), deviceId(message.body()), plate(message.body()))));
|
ProtocolId.JT808, "", header.phone(), deviceId(message.body()), plate(message.body())))));
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return new IdentityResolution(
|
return new IdentityResolution(
|
||||||
new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN),
|
new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN),
|
||||||
@@ -182,6 +182,16 @@ public final class Jt808EventMapper implements EventMapper<Jt808Message> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static VehicleIdentity normalizeUnresolvedIdentity(VehicleIdentity identity) {
|
||||||
|
if (identity == null) {
|
||||||
|
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
|
||||||
|
}
|
||||||
|
if (identity.resolved()) {
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
return new VehicleIdentity("unknown", false, identity.source());
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<String, String> baseMeta(Jt808Header header, IdentityResolution identity) {
|
private static Map<String, String> baseMeta(Jt808Header header, IdentityResolution identity) {
|
||||||
java.util.HashMap<String, String> meta = new java.util.HashMap<>();
|
java.util.HashMap<String, String> meta = new java.util.HashMap<>();
|
||||||
meta.put("messageId", "0x" + Integer.toHexString(header.messageId()));
|
meta.put("messageId", "0x" + Integer.toHexString(header.messageId()));
|
||||||
|
|||||||
@@ -197,6 +197,46 @@ class Jt808ChannelHandlerTest {
|
|||||||
eventBus.close();
|
eventBus.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void unboundLocationArchiveUsesUnknownVinInsteadOfPhoneFallback() throws Exception {
|
||||||
|
RecordingSink sink = new RecordingSink(1);
|
||||||
|
DisruptorEventBus eventBus = new DisruptorEventBus(1024, "blocking", List.of(sink));
|
||||||
|
AsyncBatchExecutor batchExecutor = new AsyncBatchExecutor(eventBus::publish);
|
||||||
|
Dispatcher dispatcher = new Dispatcher(
|
||||||
|
new HandlerRegistry(),
|
||||||
|
new InterceptorChain(List.of()),
|
||||||
|
new HandlerInvoker(),
|
||||||
|
eventBus,
|
||||||
|
batchExecutor);
|
||||||
|
InMemoryVehicleIdentityService identity = new InMemoryVehicleIdentityService();
|
||||||
|
Jt808ChannelHandler handler = new Jt808ChannelHandler(
|
||||||
|
new Jt808MessageDecoder(new BodyParserRegistry(List.of(new LocationBodyParser()))),
|
||||||
|
dispatcher,
|
||||||
|
new InMemorySessionStore(),
|
||||||
|
identity,
|
||||||
|
new Jt808ChannelRegistry(),
|
||||||
|
new Jt808PendingRequests());
|
||||||
|
EmbeddedChannel channel = new EmbeddedChannel(handler);
|
||||||
|
byte[] frame = buildFrame(Jt808MessageId.TERMINAL_LOCATION, "123456789012", 1, buildLocationBody());
|
||||||
|
|
||||||
|
channel.writeInbound(frame);
|
||||||
|
|
||||||
|
assertThat(sink.await()).isTrue();
|
||||||
|
assertThat(sink.events).singleElement().satisfies(event -> {
|
||||||
|
assertThat(event).isInstanceOf(VehicleEvent.RawArchive.class);
|
||||||
|
VehicleEvent.RawArchive archive = (VehicleEvent.RawArchive) event;
|
||||||
|
assertThat(archive.vin()).isEqualTo("unknown");
|
||||||
|
assertThat(archive.metadata())
|
||||||
|
.containsEntry("vin", "unknown")
|
||||||
|
.containsEntry("phone", "123456789012")
|
||||||
|
.containsEntry("identityResolved", "false")
|
||||||
|
.containsEntry("identitySource", "FALLBACK_PHONE");
|
||||||
|
});
|
||||||
|
|
||||||
|
batchExecutor.close();
|
||||||
|
eventBus.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void malformedFrameIsArchivedAndDispatchedAsPassthrough() throws Exception {
|
void malformedFrameIsArchivedAndDispatchedAsPassthrough() throws Exception {
|
||||||
RecordingSink sink = new RecordingSink(2);
|
RecordingSink sink = new RecordingSink(2);
|
||||||
|
|||||||
@@ -34,10 +34,12 @@ class Jt808EventMapperTest {
|
|||||||
assertThat(events).hasSize(1);
|
assertThat(events).hasSize(1);
|
||||||
assertThat(events.get(0)).isInstanceOf(VehicleEvent.Location.class);
|
assertThat(events.get(0)).isInstanceOf(VehicleEvent.Location.class);
|
||||||
assertThat(events.get(0).source()).isEqualTo(ProtocolId.JT808);
|
assertThat(events.get(0).source()).isEqualTo(ProtocolId.JT808);
|
||||||
assertThat(events.get(0).vin()).isEqualTo("123456789012");
|
assertThat(events.get(0).vin()).isEqualTo("unknown");
|
||||||
assertThat(events.get(0).metadata())
|
assertThat(events.get(0).metadata())
|
||||||
.containsEntry("vin", "123456789012")
|
.containsEntry("vin", "unknown")
|
||||||
.containsEntry("identityResolved", "false");
|
.containsEntry("phone", "123456789012")
|
||||||
|
.containsEntry("identityResolved", "false")
|
||||||
|
.containsEntry("identitySource", "FALLBACK_PHONE");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -197,7 +199,10 @@ class Jt808EventMapperTest {
|
|||||||
assertThat(passthrough.passthroughType()).isEqualTo(0x0F01);
|
assertThat(passthrough.passthroughType()).isEqualTo(0x0F01);
|
||||||
assertThat(passthrough.data()).containsExactly(0x11, 0x22, 0x33);
|
assertThat(passthrough.data()).containsExactly(0x11, 0x22, 0x33);
|
||||||
assertThat(passthrough.metadata())
|
assertThat(passthrough.metadata())
|
||||||
.containsEntry("vin", "123456789012")
|
.containsEntry("vin", "unknown")
|
||||||
|
.containsEntry("phone", "123456789012")
|
||||||
|
.containsEntry("identityResolved", "false")
|
||||||
|
.containsEntry("identitySource", "FALLBACK_PHONE")
|
||||||
.containsEntry("rawBody", "true");
|
.containsEntry("rawBody", "true");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user