feat: add vehicle key derivation
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package com.lingniu.ingest.facts;
|
||||
|
||||
import com.lingniu.ingest.api.ProtocolId;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
|
||||
public final class VehicleKey {
|
||||
|
||||
private VehicleKey() {
|
||||
}
|
||||
|
||||
public static String derive(ProtocolId protocol, String vin, String phone, String rawIdentitySeed) {
|
||||
if (protocol == null) {
|
||||
throw new IllegalArgumentException("protocol must not be null");
|
||||
}
|
||||
String normalizedVin = clean(vin);
|
||||
if (!normalizedVin.isBlank()) {
|
||||
return normalizedVin;
|
||||
}
|
||||
String normalizedPhone = clean(phone);
|
||||
if (protocol == ProtocolId.JT808 && !normalizedPhone.isBlank()) {
|
||||
return "jt808:" + normalizedPhone;
|
||||
}
|
||||
String seed = clean(rawIdentitySeed);
|
||||
if (seed.isBlank()) {
|
||||
seed = protocol.name();
|
||||
}
|
||||
return "unknown:" + protocol.name() + ":" + sha256(seed).substring(0, 16);
|
||||
}
|
||||
|
||||
private static String clean(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
private static String sha256(String value) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
return HexFormat.of().formatHex(digest.digest(value.getBytes(StandardCharsets.UTF_8)));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("SHA-256 is unavailable", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.lingniu.ingest.facts;
|
||||
|
||||
import com.lingniu.ingest.api.ProtocolId;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class VehicleKeyTest {
|
||||
|
||||
@Test
|
||||
void usesVinForGb32960() {
|
||||
assertThat(VehicleKey.derive(ProtocolId.GB32960, " LB9A32A20P0LS1257 ", "", "abc"))
|
||||
.isEqualTo("LB9A32A20P0LS1257");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesResolvedVinForJt808WhenPresent() {
|
||||
assertThat(VehicleKey.derive(ProtocolId.JT808, "VIN001", "013912345678", "abc"))
|
||||
.isEqualTo("VIN001");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesPhoneForJt808WhenVinIsMissing() {
|
||||
assertThat(VehicleKey.derive(ProtocolId.JT808, "", "013912345678", "abc"))
|
||||
.isEqualTo("jt808:013912345678");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesStableUnknownHashWhenVehicleIdentifiersAreMissing() {
|
||||
assertThat(VehicleKey.derive(ProtocolId.JT808, "", "", "abc"))
|
||||
.isEqualTo(VehicleKey.derive(ProtocolId.JT808, null, null, "abc"))
|
||||
.startsWith("unknown:JT808:");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsMissingProtocol() {
|
||||
assertThatThrownBy(() -> VehicleKey.derive(null, "VIN001", "", "abc"))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining("protocol");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user