diff --git a/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/mapper/YutongEventMapper.java b/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/mapper/YutongEventMapper.java index e901ea38..93d001a3 100644 --- a/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/mapper/YutongEventMapper.java +++ b/modules/inbound/inbound-mqtt/src/main/java/com/lingniu/ingest/inbound/mqtt/mapper/YutongEventMapper.java @@ -16,6 +16,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.UUID; @@ -96,10 +97,13 @@ public final class YutongEventMapper implements EventMapper { speed == null ? 0.0 : speed, direction == null ? 0.0 : direction, 0, 0); + Map locationMeta = mileage == null + ? meta + : withMeta(meta, "total_mileage_km", formatDouble(mileage)); out.add(new VehicleEvent.Location( UUID.randomUUID().toString(), vin, ProtocolId.MQTT_YUTONG, payload.deviceTime(), ingestTime, - null, meta, loc)); + null, locationMeta, loc)); } return out; } @@ -233,6 +237,13 @@ public final class YutongEventMapper implements EventMapper { } } + private static String formatDouble(double value) { + String formatted = String.format(Locale.ROOT, "%.6f", value); + return formatted.indexOf('.') < 0 + ? formatted + : formatted.replaceAll("0+$", "").replaceAll("\\.$", ""); + } + private record IdentityResolution(VehicleIdentity identity, String errorMessage) { private static IdentityResolution ok(VehicleIdentity identity) { return new IdentityResolution(identity, null); diff --git a/modules/inbound/inbound-mqtt/src/test/java/com/lingniu/ingest/inbound/mqtt/YutongEventMapperTest.java b/modules/inbound/inbound-mqtt/src/test/java/com/lingniu/ingest/inbound/mqtt/YutongEventMapperTest.java index 3aec762e..a2aa3702 100644 --- a/modules/inbound/inbound-mqtt/src/test/java/com/lingniu/ingest/inbound/mqtt/YutongEventMapperTest.java +++ b/modules/inbound/inbound-mqtt/src/test/java/com/lingniu/ingest/inbound/mqtt/YutongEventMapperTest.java @@ -73,6 +73,7 @@ class YutongEventMapperTest { VehicleEvent.Location loc = (VehicleEvent.Location) events.stream() .filter(e -> e instanceof VehicleEvent.Location).findFirst().orElseThrow(); assertThat(loc.payload().longitude()).isEqualTo(116.397128, org.assertj.core.data.Offset.offset(0.000001)); + assertThat(loc.metadata()).containsEntry("total_mileage_km", "123456.7"); } @Test diff --git a/modules/protocols/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapper.java b/modules/protocols/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapper.java index a92b6f45..b2f5a864 100644 --- a/modules/protocols/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapper.java +++ b/modules/protocols/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapper.java @@ -16,6 +16,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -164,10 +165,13 @@ public final class Gb32960EventMapper implements EventMapper { p.longitude, p.latitude, 0.0, v != null && v.speedKmh != null ? v.speedKmh : 0.0, 0.0, 0, p.statusFlag); + Map locationMeta = v != null && v.totalMileageKm != null + ? withMeta(meta, "total_mileage_km", formatDouble(v.totalMileageKm)) + : meta; out.add(new VehicleEvent.Location( UUID.randomUUID().toString(), header.vin(), ProtocolId.GB32960, eventTime, ingestTime, - null, meta, loc)); + null, locationMeta, loc)); } if (alarm != null && shouldEmitAlarm(alarm)) { // 只在存在告警等级或氢安全关键位时发 Alarm,减少正常高频帧对告警消费者的噪声。 @@ -330,4 +334,17 @@ public final class Gb32960EventMapper implements EventMapper { if (codes == null || codes.isEmpty()) return; for (Long c : codes) out.add(prefix + "-" + Long.toHexString(c)); } + + private static Map withMeta(Map meta, String key, String value) { + java.util.HashMap copy = new java.util.HashMap<>(meta); + copy.put(key, value == null ? "" : value); + return Map.copyOf(copy); + } + + private static String formatDouble(double value) { + String formatted = String.format(Locale.ROOT, "%.6f", value); + return formatted.indexOf('.') < 0 + ? formatted + : formatted.replaceAll("0+$", "").replaceAll("\\.$", ""); + } } diff --git a/modules/protocols/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapperTest.java b/modules/protocols/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapperTest.java index 949e1b7b..8f4e1d15 100644 --- a/modules/protocols/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapperTest.java +++ b/modules/protocols/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapperTest.java @@ -42,6 +42,12 @@ class Gb32960EventMapperTest { assertThat(events).anyMatch(e -> e instanceof VehicleEvent.Location); assertThat(events).allMatch(e -> e.source() == ProtocolId.GB32960); assertThat(events).allMatch(e -> e.vin().equals("LTEST000000000002")); + VehicleEvent.Location location = events.stream() + .filter(VehicleEvent.Location.class::isInstance) + .map(VehicleEvent.Location.class::cast) + .findFirst() + .orElseThrow(); + assertThat(location.metadata()).containsEntry("total_mileage_km", "123456.7"); } @Test