feat: add decoded fact model
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
package com.lingniu.ingest.facts;
|
||||
|
||||
import com.lingniu.ingest.api.ProtocolId;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
public sealed interface DecodedFact
|
||||
permits DecodedFact.Location,
|
||||
DecodedFact.Realtime,
|
||||
DecodedFact.Alarm,
|
||||
DecodedFact.Session,
|
||||
DecodedFact.Register,
|
||||
DecodedFact.Extension {
|
||||
|
||||
String factId();
|
||||
String frameId();
|
||||
ProtocolId protocol();
|
||||
String vehicleKey();
|
||||
String vin();
|
||||
String phone();
|
||||
Instant eventTime();
|
||||
Instant receivedAt();
|
||||
String rawUri();
|
||||
Map<String, String> metadata();
|
||||
|
||||
record Location(
|
||||
String factId,
|
||||
String frameId,
|
||||
ProtocolId protocol,
|
||||
String vehicleKey,
|
||||
String vin,
|
||||
String phone,
|
||||
Instant eventTime,
|
||||
Instant receivedAt,
|
||||
String rawUri,
|
||||
Map<String, String> metadata,
|
||||
Double longitude,
|
||||
Double latitude,
|
||||
Double altitudeM,
|
||||
Double speedKmh,
|
||||
Double directionDeg,
|
||||
Long alarmFlag,
|
||||
Long statusFlag,
|
||||
Double totalMileageKm
|
||||
) implements DecodedFact {
|
||||
public Location {
|
||||
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||
vin = BaseFields.clean(vin);
|
||||
phone = BaseFields.clean(phone);
|
||||
metadata = BaseFields.metadata(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
record Realtime(
|
||||
String factId,
|
||||
String frameId,
|
||||
ProtocolId protocol,
|
||||
String vehicleKey,
|
||||
String vin,
|
||||
String phone,
|
||||
Instant eventTime,
|
||||
Instant receivedAt,
|
||||
String rawUri,
|
||||
Map<String, String> metadata,
|
||||
Map<String, String> fields
|
||||
) implements DecodedFact {
|
||||
public Realtime {
|
||||
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||
vin = BaseFields.clean(vin);
|
||||
phone = BaseFields.clean(phone);
|
||||
metadata = BaseFields.metadata(metadata);
|
||||
fields = fields == null ? Map.of() : Map.copyOf(fields);
|
||||
}
|
||||
}
|
||||
|
||||
record Alarm(
|
||||
String factId,
|
||||
String frameId,
|
||||
ProtocolId protocol,
|
||||
String vehicleKey,
|
||||
String vin,
|
||||
String phone,
|
||||
Instant eventTime,
|
||||
Instant receivedAt,
|
||||
String rawUri,
|
||||
Map<String, String> metadata,
|
||||
String alarmLevel,
|
||||
Long alarmCode,
|
||||
String alarmName,
|
||||
Map<String, String> fields
|
||||
) implements DecodedFact {
|
||||
public Alarm {
|
||||
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||
vin = BaseFields.clean(vin);
|
||||
phone = BaseFields.clean(phone);
|
||||
alarmLevel = BaseFields.clean(alarmLevel);
|
||||
alarmName = BaseFields.clean(alarmName);
|
||||
metadata = BaseFields.metadata(metadata);
|
||||
fields = fields == null ? Map.of() : Map.copyOf(fields);
|
||||
}
|
||||
}
|
||||
|
||||
record Session(
|
||||
String factId,
|
||||
String frameId,
|
||||
ProtocolId protocol,
|
||||
String vehicleKey,
|
||||
String vin,
|
||||
String phone,
|
||||
Instant eventTime,
|
||||
Instant receivedAt,
|
||||
String rawUri,
|
||||
Map<String, String> metadata,
|
||||
String sessionType,
|
||||
Integer resultCode,
|
||||
Map<String, String> fields
|
||||
) implements DecodedFact {
|
||||
public Session {
|
||||
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||
vin = BaseFields.clean(vin);
|
||||
phone = BaseFields.clean(phone);
|
||||
sessionType = BaseFields.clean(sessionType);
|
||||
metadata = BaseFields.metadata(metadata);
|
||||
fields = fields == null ? Map.of() : Map.copyOf(fields);
|
||||
}
|
||||
}
|
||||
|
||||
record Register(
|
||||
String factId,
|
||||
String frameId,
|
||||
ProtocolId protocol,
|
||||
String vehicleKey,
|
||||
String vin,
|
||||
String phone,
|
||||
Instant eventTime,
|
||||
Instant receivedAt,
|
||||
String rawUri,
|
||||
Map<String, String> metadata,
|
||||
Map<String, String> registrationFields
|
||||
) implements DecodedFact {
|
||||
public Register {
|
||||
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||
vin = BaseFields.clean(vin);
|
||||
phone = BaseFields.clean(phone);
|
||||
metadata = BaseFields.metadata(metadata);
|
||||
registrationFields = registrationFields == null ? Map.of() : Map.copyOf(registrationFields);
|
||||
}
|
||||
}
|
||||
|
||||
record Extension(
|
||||
String factId,
|
||||
String frameId,
|
||||
ProtocolId protocol,
|
||||
String vehicleKey,
|
||||
String vin,
|
||||
String phone,
|
||||
Instant eventTime,
|
||||
Instant receivedAt,
|
||||
String rawUri,
|
||||
Map<String, String> metadata,
|
||||
String extensionType,
|
||||
Map<String, String> fields
|
||||
) implements DecodedFact {
|
||||
public Extension {
|
||||
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||
vin = BaseFields.clean(vin);
|
||||
phone = BaseFields.clean(phone);
|
||||
extensionType = BaseFields.clean(extensionType);
|
||||
metadata = BaseFields.metadata(metadata);
|
||||
fields = fields == null ? Map.of() : Map.copyOf(fields);
|
||||
}
|
||||
}
|
||||
|
||||
final class BaseFields {
|
||||
private BaseFields() {
|
||||
}
|
||||
|
||||
static void validate(String factId,
|
||||
String frameId,
|
||||
ProtocolId protocol,
|
||||
String vehicleKey,
|
||||
Instant eventTime,
|
||||
Instant receivedAt,
|
||||
String rawUri) {
|
||||
required(factId, "factId");
|
||||
required(frameId, "frameId");
|
||||
if (protocol == null) {
|
||||
throw new IllegalArgumentException("protocol must not be null");
|
||||
}
|
||||
required(vehicleKey, "vehicleKey");
|
||||
if (eventTime == null) {
|
||||
throw new IllegalArgumentException("eventTime must not be null");
|
||||
}
|
||||
if (receivedAt == null) {
|
||||
throw new IllegalArgumentException("receivedAt must not be null");
|
||||
}
|
||||
required(rawUri, "rawUri");
|
||||
}
|
||||
|
||||
static String clean(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
static Map<String, String> metadata(Map<String, String> metadata) {
|
||||
return metadata == null ? Map.of() : Map.copyOf(metadata);
|
||||
}
|
||||
|
||||
private static void required(String value, String field) {
|
||||
if (clean(value).isBlank()) {
|
||||
throw new IllegalArgumentException(field + " must not be blank");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.lingniu.ingest.facts;
|
||||
|
||||
import com.lingniu.ingest.api.ProtocolId;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DecodedFactTest {
|
||||
|
||||
@Test
|
||||
void locationFactCarriesRawReferenceAndMetadata() {
|
||||
DecodedFact.Location location = new DecodedFact.Location(
|
||||
"fact-1",
|
||||
"frame-1",
|
||||
ProtocolId.JT808,
|
||||
"jt808:013912345678",
|
||||
"",
|
||||
"013912345678",
|
||||
Instant.parse("2026-06-29T00:00:00Z"),
|
||||
Instant.parse("2026-06-29T00:00:01Z"),
|
||||
"archive://jt808/frame-1.bin",
|
||||
Map.of("messageId", "0x0200"),
|
||||
120.123456,
|
||||
30.123456,
|
||||
10.0,
|
||||
42.0,
|
||||
180.0,
|
||||
0L,
|
||||
2L,
|
||||
1000.5);
|
||||
|
||||
assertThat(location.factId()).isEqualTo("fact-1");
|
||||
assertThat(location.rawUri()).isEqualTo("archive://jt808/frame-1.bin");
|
||||
assertThat(location.metadata()).containsEntry("messageId", "0x0200");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user