feat(api-pull): 完善全悉设备接入并修复 NDR 边界重复采集
- 增加全悉设备 API 日志拉取及消费处理 - 完善全悉日志解析、字段映射和标准化逻辑 - NDR 采集窗口统一采用左闭右开规则 - 对设备秒级查询范围向外取整,并在本地精确过滤 - 区分接口原始返回数和实际接收数,保证分页偏移正确推进 - 补充时间窗口、边界过滤和分页推进测试 - 更新设备 API 拉取及对接说明文档
This commit is contained in:
+5
@@ -944,6 +944,11 @@ public class LogNormalProcessor {
|
||||
(HashMap<String, Object>) ((HashMap<String, Object>) map.get("action")).get("param");
|
||||
normalColumMap.put("dest_field_value",
|
||||
getMappingValue(action_param, entry.getValue().toString()));
|
||||
} else if (((HashMap<String, Object>) map.get("action")).get("type").equals("bounded_text")) {
|
||||
Map<String, Object> params = (Map<String, Object>)
|
||||
((Map<String, Object>) map.get("action")).get("param");
|
||||
int bytes = com.common.util.BoundedText.limit(params.get("max_utf8_bytes"));
|
||||
normalColumMap.put("dest_field_value", com.common.util.BoundedText.map(entry.getValue(), bytes));
|
||||
} else if (((HashMap<String, Object>) map.get("action")).get("type").equals("time")) {
|
||||
normalColumMap.put("action_param",
|
||||
((HashMap<String, Object>) map.get("action")).get("param"));
|
||||
|
||||
+4
-1
@@ -8,7 +8,7 @@ import java.util.Map;
|
||||
|
||||
/** 单个字段映射的不可变执行计划。 */
|
||||
public final class CompiledMapper {
|
||||
public enum Action { EQUAL, MAPPING, TIME, LEGACY_NULL }
|
||||
public enum Action { EQUAL, MAPPING, TIME, BOUNDED_TEXT, LEGACY_NULL }
|
||||
|
||||
private final String originField;
|
||||
private final String destinationField;
|
||||
@@ -33,6 +33,9 @@ public final class CompiledMapper {
|
||||
case MAPPING:
|
||||
// 保持旧执行器语义:源值为 null 时规则执行失败,而不是静默映射成字符串 "null"。
|
||||
return sanitize(mapping.get(sourceValue.toString()));
|
||||
case BOUNDED_TEXT:
|
||||
return sanitize(com.common.util.BoundedText.map(sourceValue,
|
||||
com.common.util.BoundedText.limit(mapping.get("max_utf8_bytes"))));
|
||||
case TIME:
|
||||
try {
|
||||
return TimeConversionUtils.convertToMillis(sourceValue.toString(), timezone);
|
||||
|
||||
+7
@@ -144,6 +144,13 @@ public class CompiledRuleCompiler {
|
||||
mapping.put(entry.getKey(), freeze(objectMapper.convertValue(entry.getValue(), Object.class)));
|
||||
}
|
||||
compiled = new CompiledMapper(origin, destination, CompiledMapper.Action.MAPPING, mapping, null);
|
||||
} else if ("bounded_text".equals(type)) {
|
||||
JsonNode limit = param == null ? null : param.get("max_utf8_bytes");
|
||||
if (limit == null || !limit.isIntegralNumber() || !limit.canConvertToInt())
|
||||
throw invalid(source, "bounded_text requires integer max_utf8_bytes");
|
||||
int bytes = com.common.util.BoundedText.limit(limit.intValue());
|
||||
compiled = new CompiledMapper(origin, destination, CompiledMapper.Action.BOUNDED_TEXT,
|
||||
Collections.singletonMap("max_utf8_bytes", bytes), null);
|
||||
} else if ("time".equals(type)) {
|
||||
String timezone = param == null ? null : text(param.get("timezone"));
|
||||
compiled = new CompiledMapper(origin, destination, CompiledMapper.Action.TIME,
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.common.util;
|
||||
|
||||
/** Explicit opt-in rule action: keep text only within its UTF-8 budget, never truncate or decode. */
|
||||
public final class BoundedText {
|
||||
private BoundedText() { }
|
||||
|
||||
public static int limit(Object value) {
|
||||
if (!(value instanceof Number)) throw new IllegalArgumentException("max_utf8_bytes must be an integer");
|
||||
Number n = (Number) value;
|
||||
int limit = n.intValue();
|
||||
if (n.doubleValue() != limit || limit < 1 || limit > 1048576)
|
||||
throw new IllegalArgumentException("max_utf8_bytes out of range");
|
||||
return limit;
|
||||
}
|
||||
|
||||
public static String map(Object value, int maxBytes) {
|
||||
if (!(value instanceof String)) return null;
|
||||
String text = (String) value;
|
||||
int bytes = 0;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c <= 0x7f) bytes++;
|
||||
else if (c <= 0x7ff) bytes += 2;
|
||||
else if (Character.isHighSurrogate(c) && i + 1 < text.length()
|
||||
&& Character.isLowSurrogate(text.charAt(i + 1))) { bytes += 4; i++; }
|
||||
else if (Character.isSurrogate(c)) return null;
|
||||
else bytes += 3;
|
||||
if (bytes > maxBytes) return null;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user