feat: productionize raw history ingestion

This commit is contained in:
lingniu
2026-06-30 23:21:58 +08:00
parent 3cc7ac9669
commit cbba617801
100 changed files with 2995 additions and 1697 deletions

View File

@@ -1,5 +1,8 @@
package com.lingniu.ingest.core.dispatcher;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.lingniu.ingest.api.event.RawArchiveKeys;
import com.lingniu.ingest.api.event.VehicleEvent;
import com.lingniu.ingest.api.pipeline.IngestContext;
@@ -36,6 +39,9 @@ public final class Dispatcher {
private static final Logger log = LoggerFactory.getLogger(Dispatcher.class);
private static final AtomicLong RAW_ARCHIVE_SEQUENCE = new AtomicLong();
private static final String REQUIRED_DURABLE_SINK = "kafka";
private static final ObjectMapper RAW_ARCHIVE_JSON = JsonMapper.builder()
.findAndAddModules()
.build();
private final HandlerRegistry registry;
private final InterceptorChain interceptors;
@@ -167,7 +173,10 @@ public final class Dispatcher {
byte[] bytes = frame.rawBytes();
if (bytes == null || bytes.length == 0) return RawArchiveLookup.empty();
Map<String, String> meta = frame.sourceMeta() == null ? Map.of() : frame.sourceMeta();
Map<String, String> meta = frame.sourceMeta() == null
? Map.of()
: new LinkedHashMap<>(frame.sourceMeta());
String parsedJson = parsedJson(frame.payload(), meta.remove(RawArchiveKeys.META_PARSED_JSON));
String vin = meta.getOrDefault("vin", "");
Instant ingestTime = frame.receivedAt() != null ? frame.receivedAt() : Instant.now();
String eventId = nextRawArchiveEventId(ingestTime);
@@ -184,11 +193,34 @@ public final class Dispatcher {
archiveMeta,
frame.command(),
frame.infoType(),
bytes);
bytes,
parsedJson);
out.add(raw);
return new RawArchiveLookup(eventId, key, RawArchiveKeys.logicalUri(key));
}
private static String parsedJson(Object payload, String explicitParsedJson) {
if (explicitParsedJson != null && !explicitParsedJson.isBlank()) {
return explicitParsedJson;
}
if (payload == null) {
return "{}";
}
try {
return RAW_ARCHIVE_JSON.writeValueAsString(payload);
} catch (JsonProcessingException ex) {
return "{\"serializationError\":\"" + escapeJson(ex.getMessage()) + "\",\"payloadType\":\""
+ escapeJson(payload.getClass().getName()) + "\"}";
}
}
private static String escapeJson(String value) {
if (value == null) {
return "";
}
return value.replace("\\", "\\\\").replace("\"", "\\\"");
}
private static String nextRawArchiveEventId(Instant ingestTime) {
// 文件名需要可排序且单进程内唯一:毫秒时间放大到微秒量级,再用 AtomicLong 递增兜底。
long base = (ingestTime == null ? Instant.now() : ingestTime).toEpochMilli() * 1000L;