refactor: make event file store optional

This commit is contained in:
lingniu
2026-07-01 07:51:45 +08:00
parent 51b1dd4174
commit 56a6c01f95
32 changed files with 139 additions and 84 deletions

View File

@@ -0,0 +1,93 @@
package com.lingniu.ingest.api.history;
import com.lingniu.ingest.api.ProtocolId;
import java.time.Instant;
import java.time.LocalDate;
/**
* Query criteria for the optional file-backed historical event index.
*/
public record EventFileQuery(
ProtocolId protocol,
LocalDate dateFrom,
LocalDate dateTo,
Instant eventTimeFrom,
Instant eventTimeTo,
Order order,
int limit,
String vin,
String eventType,
Instant cursorEventTime,
Instant cursorIngestTime,
String cursorEventId
) {
public enum Order {
ASC,
DESC
}
public EventFileQuery {
if (protocol == null) {
throw new IllegalArgumentException("protocol must not be null");
}
if (dateFrom == null || dateTo == null) {
throw new IllegalArgumentException("date range must not be null");
}
if (dateTo.isBefore(dateFrom)) {
throw new IllegalArgumentException("dateTo must not be before dateFrom");
}
if (eventTimeFrom != null && eventTimeTo != null && eventTimeTo.isBefore(eventTimeFrom)) {
throw new IllegalArgumentException("eventTimeTo must not be before eventTimeFrom");
}
order = order == null ? Order.ASC : order;
limit = limit <= 0 ? 100 : limit;
vin = vin == null || vin.isBlank() ? null : vin.trim();
eventType = eventType == null || eventType.isBlank() ? null : eventType.trim();
cursorEventId = cursorEventId == null || cursorEventId.isBlank() ? null : cursorEventId.trim();
if ((cursorEventTime == null || cursorIngestTime == null || cursorEventId == null)
&& !(cursorEventTime == null && cursorIngestTime == null && cursorEventId == null)) {
throw new IllegalArgumentException("cursor eventTime, ingestTime and eventId must be provided together");
}
}
public EventFileQuery(ProtocolId protocol,
LocalDate dateFrom,
LocalDate dateTo,
Instant eventTimeFrom,
Instant eventTimeTo,
Order order,
int limit,
String vin,
String eventType) {
this(protocol, dateFrom, dateTo, eventTimeFrom, eventTimeTo, order, limit,
vin, eventType, null, null, null);
}
public EventFileQuery(ProtocolId protocol,
LocalDate dateFrom,
LocalDate dateTo,
Order order,
int limit,
String vin,
String eventType) {
this(protocol, dateFrom, dateTo, null, null, order, limit, vin, eventType);
}
public EventFileQuery(ProtocolId protocol,
LocalDate dateFrom,
LocalDate dateTo,
Order order,
int limit) {
this(protocol, dateFrom, dateTo, order, limit, null, null);
}
public EventFileQuery(ProtocolId protocol,
LocalDate dateFrom,
LocalDate dateTo,
Order order,
int limit,
String vin) {
this(protocol, dateFrom, dateTo, order, limit, vin, null);
}
}

View File

@@ -0,0 +1,41 @@
package com.lingniu.ingest.api.history;
import com.lingniu.ingest.api.ProtocolId;
import java.time.Instant;
import java.util.Map;
/**
* Minimal historical event record shared by optional compatibility stores and history queries.
*/
public record EventFileRecord(
String eventId,
ProtocolId protocol,
String eventType,
String vin,
Instant eventTime,
Instant ingestTime,
String rawArchiveUri,
Map<String, String> metadata,
String payloadJson
) {
public EventFileRecord {
if (eventId == null || eventId.isBlank()) {
throw new IllegalArgumentException("eventId must not be blank");
}
if (protocol == null) {
throw new IllegalArgumentException("protocol must not be null");
}
if (eventTime == null) {
throw new IllegalArgumentException("eventTime must not be null");
}
if (ingestTime == null) {
throw new IllegalArgumentException("ingestTime must not be null");
}
eventType = eventType == null ? "" : eventType;
vin = vin == null ? "" : vin;
rawArchiveUri = rawArchiveUri == null ? "" : rawArchiveUri;
metadata = metadata == null ? Map.of() : Map.copyOf(metadata);
payloadJson = payloadJson == null || payloadJson.isBlank() ? "{}" : payloadJson;
}
}

View File

@@ -0,0 +1,26 @@
package com.lingniu.ingest.api.history;
import java.io.IOException;
import java.util.List;
/**
* Historical event index contract.
*
* <p>The default production history path uses TDengine directly. This contract remains in core so
* optional compatibility implementations can still plug into query code without pulling their
* storage engines into the default reactor.
*/
public interface EventFileStore {
default void append(EventFileRecord record) throws IOException {
appendAll(List.of(record));
}
void appendAll(List<EventFileRecord> records) throws IOException;
List<EventFileRecord> query(EventFileQuery query) throws IOException;
default EventFileRecord findByRawArchiveUri(String rawArchiveUri) throws IOException {
return null;
}
}