diff --git a/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java b/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java index 55de4dc1..b964b25e 100644 --- a/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java +++ b/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java @@ -574,30 +574,6 @@ public class RMNCHBeneficiaryDetailsRmnch { @Column(name = "gpsUnavailableReason") private String gpsUnavailableReason; - @Expose - @Column(name = "gpsLatitude") - private Double gpsLatitude; - - @Expose - @Column(name = "gpsLongitude") - private Double gpsLongitude; - - @Expose - @Column(name = "digipin") - private String digipin; - - @Expose - @Column(name = "gpsTimestamp") - private Timestamp gpsTimestamp; - - @Expose - @Column(name = "isGpsUnavailable", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0") - private Boolean isGpsUnavailable = false; - - @Expose - @Column(name = "gpsUnavailableReason") - private String gpsUnavailableReason; - // Anthropometry fields sent by Stop TB mobile app via beneficiaryDetails payload. // i_beneficiarydetails_rmnch has no these columns — stored in i_beneficiarydetails.otherFields instead. @Expose diff --git a/src/main/java/com/iemr/common/identity/mapper/InputMapper.java b/src/main/java/com/iemr/common/identity/mapper/InputMapper.java index 9a46ba4f..704fe671 100644 --- a/src/main/java/com/iemr/common/identity/mapper/InputMapper.java +++ b/src/main/java/com/iemr/common/identity/mapper/InputMapper.java @@ -21,6 +21,13 @@ */ package com.iemr.common.identity.mapper; +import java.io.IOException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +40,10 @@ import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import com.google.gson.LongSerializationPolicy; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; import com.iemr.common.identity.exception.IEMRException; public class InputMapper @@ -42,11 +53,41 @@ public class InputMapper private static GsonBuilder builder; private static InputMapper instance = null; + private static final DateTimeFormatter ISO_WITH_Z = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + private static final DateTimeFormatter CLIENT_DATE_FORMAT = + DateTimeFormatter.ofPattern("MMM dd, yyyy, h:mm:ss a", Locale.ENGLISH); + + private static final TypeAdapter TIMESTAMP_ADAPTER = new TypeAdapter() { + @Override + public void write(JsonWriter out, Timestamp value) throws IOException { + if (value == null) out.nullValue(); + else out.value(value.getTime()); + } + + @Override + public Timestamp read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } + if (in.peek() == JsonToken.NUMBER) return new Timestamp(in.nextLong()); + String s = in.nextString(); + // epoch millis as string + try { return new Timestamp(Long.parseLong(s)); } catch (NumberFormatException ignored) {} + // ISO 8601 with Z e.g. "2021-06-18T00:00:00.000Z" + try { return Timestamp.from(Instant.parse(s)); } catch (Exception ignored) {} + // Mobile client format e.g. "Jun 18, 2021, 5:30:00 AM" + try { return Timestamp.valueOf(LocalDateTime.parse(s, CLIENT_DATE_FORMAT)); } catch (Exception ignored) {} + // Fallback: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" literal Z + try { return Timestamp.valueOf(LocalDateTime.parse(s, ISO_WITH_Z)); } catch (Exception ignored) {} + return null; + } + }; + private InputMapper() { builder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") // .excludeFieldsWithoutExposeAnnotation() - .serializeNulls().setLongSerializationPolicy(LongSerializationPolicy.STRING); + .serializeNulls().setLongSerializationPolicy(LongSerializationPolicy.STRING) + .registerTypeAdapter(Timestamp.class, TIMESTAMP_ADAPTER); } public static InputMapper getInstance() diff --git a/src/main/java/com/iemr/common/identity/utils/mapper/InputMapper.java b/src/main/java/com/iemr/common/identity/utils/mapper/InputMapper.java index 4c8db37d..fb419105 100644 --- a/src/main/java/com/iemr/common/identity/utils/mapper/InputMapper.java +++ b/src/main/java/com/iemr/common/identity/utils/mapper/InputMapper.java @@ -21,6 +21,13 @@ */ package com.iemr.common.identity.utils.mapper; +import java.io.IOException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -28,6 +35,10 @@ import com.google.gson.ExclusionStrategy; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; import com.iemr.common.identity.utils.exception.IEMRException; /** @@ -48,6 +59,30 @@ public InputMapper() { if (builder == null) { builder = new GsonBuilder(); builder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + builder.registerTypeAdapter(Timestamp.class, new TypeAdapter() { + private final DateTimeFormatter isoWithZ = DateTimeFormatter.ISO_INSTANT; + private final DateTimeFormatter isoNoTz = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); + + @Override + public void write(JsonWriter out, Timestamp value) throws IOException { + if (value == null) out.nullValue(); + else out.value(value.getTime()); + } + + @Override + public Timestamp read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } + if (in.peek() == JsonToken.NUMBER) return new Timestamp(in.nextLong()); + String s = in.nextString(); + // epoch millis as string + try { return new Timestamp(Long.parseLong(s)); } catch (NumberFormatException ignored) {} + // ISO 8601 with timezone (e.g. "2026-05-28T03:24:35.000Z") + try { return Timestamp.from(Instant.parse(s)); } catch (Exception ignored) {} + // ISO without timezone (e.g. "2026-05-28T03:24:35.000") + try { return Timestamp.from(LocalDateTime.parse(s, isoNoTz).toInstant(ZoneOffset.UTC)); } catch (Exception ignored) {} + return null; + } + }); } }