[Schedule]
- Removed schedule (schedules are managed in sys-rest-api) [Odometer] - Created call Samsara to get odometer
This commit is contained in:
parent
ce146cd41a
commit
dc10f8b1d7
|
|
@ -1,29 +0,0 @@
|
|||
package com.goi.integration.common.config;
|
||||
|
||||
import com.goi.integration.common.config.ScheduleJobConfigDto;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class InMemoryScheduleJobConfigProvider implements ScheduleJobConfigProvider {
|
||||
|
||||
private final Map<String, ScheduleJobConfigDto> configs = Map.of(
|
||||
"SAMSARA_DVIR",
|
||||
ScheduleJobConfigDto.builder()
|
||||
.sjcJobCode("SAMSARA_DVIR")
|
||||
.sjcEnabled(true)
|
||||
.sjcCronExpression("0 */10 * * * *")
|
||||
.sjcLookbackHours(24)
|
||||
.sjcOverlapMinutes(10)
|
||||
.sjcMaxRecords(252)
|
||||
.sjcTimezone("UTC")
|
||||
.build()
|
||||
);
|
||||
|
||||
@Override
|
||||
public Optional<ScheduleJobConfigDto> getJobConfig(String jobCode) {
|
||||
return Optional.ofNullable(configs.get(jobCode));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.goi.integration.common.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ScheduleJobConfigDto {
|
||||
private String sjcJobCode; // SAMSARA_DVIR
|
||||
private String sjcCronExpression; // "0 */10 * * * *"
|
||||
private Integer sjcLookbackHours; // 24
|
||||
private Integer sjcOverlapMinutes;// 10
|
||||
private Integer sjcMaxRecords; // 252
|
||||
private Boolean sjcEnabled; // true
|
||||
private String sjcTimezone; // "UTC"
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package com.goi.integration.common.config;
|
||||
|
||||
import com.goi.integration.common.config.ScheduleJobConfigDto;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ScheduleJobConfigProvider {
|
||||
Optional<ScheduleJobConfigDto> getJobConfig(String jobCode);
|
||||
}
|
||||
|
||||
|
|
@ -11,16 +11,14 @@ import lombok.NoArgsConstructor;
|
|||
@Builder
|
||||
public class ExtIngestResult {
|
||||
private String source;
|
||||
private String recordType;
|
||||
private int received;
|
||||
private int inserted;
|
||||
private int updated;
|
||||
private int skipped;
|
||||
|
||||
public static ExtIngestResult empty(String source, String recordType) {
|
||||
public static ExtIngestResult empty(String source) {
|
||||
return ExtIngestResult.builder()
|
||||
.source(source)
|
||||
.recordType(recordType)
|
||||
.received(0)
|
||||
.inserted(0)
|
||||
.updated(0)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,37 @@ public final class DateTimeUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JsonNode → LocalDateTime (UTC)
|
||||
*/
|
||||
public static LocalDateTime parseToUTC(JsonNode node) {
|
||||
if (node == null || node.isMissingNode() || node.isNull()) {
|
||||
return null;
|
||||
}
|
||||
return parseToUTC(node.asText(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* String → LocalDateTime (UTC)
|
||||
* ex) 2025-12-22T13:30:24.365Z
|
||||
* ex) 2025-12-22T13:30:24+00:00
|
||||
* ex) 2025-12-22T08:30:24-05:00
|
||||
*/
|
||||
public static LocalDateTime parseToUTC(String value) {
|
||||
if (value == null || value.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return OffsetDateTime
|
||||
.parse(value)
|
||||
.atZoneSameInstant(ZoneId.of("UTC"))
|
||||
.toLocalDateTime();
|
||||
} catch (DateTimeParseException e) {
|
||||
return null; // ingest 안정성 우선
|
||||
}
|
||||
}
|
||||
|
||||
public static LocalDateTime parseToToronto(String value) {
|
||||
if (value == null || value.isBlank()) return null;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,25 @@ public final class ExtPayloadHashUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 문자열 조합 기반 SHA-256
|
||||
* (odometer snapshot idempotent / 변경 감지용)
|
||||
*/
|
||||
public static String sha256FromStrings(String... values) {
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String v : values) {
|
||||
if (v != null) {
|
||||
sb.append(v);
|
||||
}
|
||||
sb.append('|'); // 구분자
|
||||
}
|
||||
return sha256(sb.toString());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to hash from strings", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- internal ---------- */
|
||||
|
||||
@SuppressWarnings({ "serial", "deprecation" })
|
||||
|
|
|
|||
|
|
@ -33,9 +33,8 @@ public class OprIngestClient {
|
|||
// .writeValueAsString(command);
|
||||
//
|
||||
log.info(
|
||||
"[OPR_INGEST][REQUEST] source={}, type={}, records={}, fetchedAt={}",
|
||||
"[OPR_INGEST][REQUEST] source={}, type={}, fetchedAt={}",
|
||||
command.getSource(),
|
||||
command.getRecordType(),
|
||||
command.getRecords() != null ? command.getRecords().size() : 0,
|
||||
command.getFetchedAt()
|
||||
);
|
||||
|
|
@ -90,9 +89,8 @@ public class OprIngestClient {
|
|||
} catch (Exception e) {
|
||||
// 실패 로그
|
||||
log.error(
|
||||
"[OPR_INGEST][FAILED] source={}, type={}, error={}",
|
||||
"[OPR_INGEST][FAILED] source={}, error={}",
|
||||
command.getSource(),
|
||||
command.getRecordType(),
|
||||
e.getMessage(),
|
||||
e
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.goi.integration.samsara.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.goi.integration.samsara.dto.ScheduleWorkerRequestDto;
|
||||
import com.goi.integration.samsara.dto.ScheduleWorkerResponseDto;
|
||||
import com.goi.integration.samsara.service.InspectionIngestWorker;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/samsara/inspection")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class InspectionWorkerController {
|
||||
|
||||
private final InspectionIngestWorker inspectionIngestWorker;
|
||||
|
||||
@PostMapping("/worker")
|
||||
public ScheduleWorkerResponseDto runDvir(
|
||||
@RequestBody ScheduleWorkerRequestDto request
|
||||
) {
|
||||
return inspectionIngestWorker.execute(request);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package com.goi.integration.samsara.controller;
|
||||
|
||||
import com.goi.integration.samsara.job.DvirIngestJob;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/internal/test")
|
||||
@RequiredArgsConstructor
|
||||
public class SamsaraTestController {
|
||||
|
||||
private final DvirIngestJob dvirJob;
|
||||
|
||||
@PostMapping("/samsara-dvir")
|
||||
public String runDvirNow() {
|
||||
dvirJob.run();
|
||||
return "SAMSARA_DVIR_TRIGGERED";
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,9 @@ package com.goi.integration.samsara.controller;
|
|||
|
||||
import com.goi.integration.samsara.dto.VehicleOdometerHistoryResponseDto;
|
||||
import com.goi.integration.samsara.dto.VehicleGpsResponseDto;
|
||||
import com.goi.integration.samsara.dto.VehicleOdometerCommand;
|
||||
import com.goi.integration.samsara.service.VehicleOdometerHistoryService;
|
||||
import com.goi.integration.samsara.service.VehicleOdometerService;
|
||||
import com.goi.integration.samsara.service.VehicleGpsService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -19,6 +21,7 @@ public class VehicleController {
|
|||
|
||||
private final VehicleGpsService vehicleStatService;
|
||||
private final VehicleOdometerHistoryService vehicleOdometerHistoryService;
|
||||
private final VehicleOdometerService vehicleOdometerService;
|
||||
|
||||
@GetMapping("/stat/gps")
|
||||
public List<VehicleGpsResponseDto> getGps(
|
||||
|
|
@ -41,6 +44,22 @@ public class VehicleController {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vehicle odometer raw fetch (for opr-rest-api ingest)
|
||||
*/
|
||||
@PostMapping("/odometer/fetch")
|
||||
public VehicleOdometerCommand fetchOdometer(
|
||||
@RequestParam List<String> vehicleIds,
|
||||
@RequestParam Instant startTime,
|
||||
@RequestParam Instant endTime
|
||||
) {
|
||||
return vehicleOdometerService.fetchOdometerCommand(
|
||||
vehicleIds,
|
||||
startTime,
|
||||
endTime
|
||||
);
|
||||
}
|
||||
|
||||
// @GetMapping("/safety/events")
|
||||
// public List<VehicleSafetyEventResponseDto> getSafetyEvents(
|
||||
// @RequestParam Instant startTime,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import java.util.List;
|
|||
@Builder
|
||||
public class ExtInspectionIngestCommand {
|
||||
private String source;
|
||||
private String recordType;
|
||||
private LocalDateTime fetchedAt;
|
||||
private List<ExtInspectionRecordDto> records;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.goi.integration.samsara.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ScheduleWorkerRequestDto {
|
||||
|
||||
private String jobCode;
|
||||
private LocalDateTime from;
|
||||
private LocalDateTime to;
|
||||
private Integer maxRecords;
|
||||
private Map<String, Map<String, String>> config;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.goi.integration.samsara.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ScheduleWorkerResponseDto {
|
||||
|
||||
private boolean success;
|
||||
|
||||
private int processedCount;
|
||||
private int successCount;
|
||||
private int failCount;
|
||||
|
||||
private String errorCode;
|
||||
private String errorMessage;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.goi.integration.samsara.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class VehicleOdometerCommand {
|
||||
private String source;
|
||||
private LocalDateTime fetchedAt;
|
||||
private List<VehicleOdometerRecordDto> records;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.goi.integration.samsara.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class VehicleOdometerRecordDto {
|
||||
private String source; // SAMSARA
|
||||
private String vehicleExternalId; // samsara vehicle.id
|
||||
private LocalDateTime sampleTime; // snapshot time (UTC)
|
||||
private Long odometerMeters; // cumulative meters
|
||||
private String payloadHash; // idempotent hash
|
||||
private JsonNode payloadJson; // minimal raw payload
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package com.goi.integration.samsara.job;
|
||||
|
||||
import com.goi.integration.common.config.ScheduleJobConfigDto;
|
||||
import com.goi.integration.common.config.ScheduleJobConfigProvider;
|
||||
import com.goi.integration.common.dto.ExtIngestResult;
|
||||
import com.goi.integration.samsara.client.SamsaraClient;
|
||||
import com.goi.integration.samsara.service.InspectionIngestService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DvirIngestJob {
|
||||
|
||||
private static final String JOB_CODE = "SAMSARA_DVIR";
|
||||
|
||||
private final ScheduleJobConfigProvider configProvider;
|
||||
private final SamsaraClient samsaraClient;
|
||||
private final InspectionIngestService ingestService;
|
||||
|
||||
@Scheduled(cron = "${ext.samsara.jobs.dvir.cron:0 */10 * * * *}")
|
||||
public void run() {
|
||||
|
||||
ScheduleJobConfigDto cfg = configProvider.getJobConfig(JOB_CODE)
|
||||
.filter(ScheduleJobConfigDto::getSjcEnabled)
|
||||
.orElse(null);
|
||||
|
||||
if (cfg == null) {
|
||||
log.info("[{}] disabled or config missing", JOB_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
int limit = (cfg.getSjcMaxRecords() != null) ? cfg.getSjcMaxRecords() : 252;
|
||||
|
||||
Instant now = Instant.now();
|
||||
long lookbackSec = Duration.ofHours(cfg.getSjcLookbackHours() != null ? cfg.getSjcLookbackHours() : 24).getSeconds();
|
||||
long overlapSec = Duration.ofMinutes(cfg.getSjcOverlapMinutes() != null ? cfg.getSjcOverlapMinutes() : 0).getSeconds();
|
||||
|
||||
Instant start = now.minusSeconds(lookbackSec + overlapSec);
|
||||
Instant end = now;
|
||||
|
||||
log.info("[{}] calling samsara dvir history start={} end={} limit={}", JOB_CODE, start, end, limit);
|
||||
|
||||
String rawJson = samsaraClient.getDvirHistory(limit, start, end);
|
||||
|
||||
// opr-rest-api ingest 호출
|
||||
ExtIngestResult result = ingestService.ingestFromRawJson(rawJson);
|
||||
|
||||
log.info("[{}] ingest result received={}, inserted={}, updated={}, skipped={}",
|
||||
JOB_CODE, result.getReceived(), result.getInserted(), result.getUpdated(), result.getSkipped());
|
||||
}
|
||||
}
|
||||
|
|
@ -21,8 +21,6 @@ import java.util.List;
|
|||
@RequiredArgsConstructor
|
||||
public class InspectionIngestService {
|
||||
|
||||
private static final String JOB_CODE = "SAMSARA_DVIR";
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final OprIngestClient oprIngestClient;
|
||||
|
||||
|
|
@ -42,12 +40,12 @@ public class InspectionIngestService {
|
|||
// data[] 없음
|
||||
if (!data.isArray()) {
|
||||
log.warn("Samsara DVIR response has no data[] array");
|
||||
return ExtIngestResult.empty("SAMSARA", "DVIR");
|
||||
return ExtIngestResult.empty("SAMSARA");
|
||||
}
|
||||
|
||||
//
|
||||
List<ExtInspectionRecordDto> records = new ArrayList<>();
|
||||
log.info("[{}] data={}", JOB_CODE, data.size());
|
||||
log.info("inspection data size={}", data.size());
|
||||
|
||||
// data[] 각 node 가 하나의 inspection (preTrip / postTrip)
|
||||
for (JsonNode node : data) {
|
||||
|
|
@ -87,7 +85,6 @@ public class InspectionIngestService {
|
|||
ExtInspectionIngestCommand command =
|
||||
ExtInspectionIngestCommand.builder()
|
||||
.source("SAMSARA")
|
||||
.recordType("DVIR")
|
||||
.fetchedAt(LocalDateTime.now())
|
||||
.records(records)
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
package com.goi.integration.samsara.service;
|
||||
|
||||
import com.goi.integration.common.dto.ExtIngestResult;
|
||||
import com.goi.integration.samsara.client.SamsaraClient;
|
||||
import com.goi.integration.samsara.dto.ScheduleWorkerRequestDto;
|
||||
import com.goi.integration.samsara.dto.ScheduleWorkerResponseDto;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class InspectionIngestWorker {
|
||||
|
||||
private final SamsaraClient samsaraClient;
|
||||
private final InspectionIngestService ingestService;
|
||||
|
||||
public ScheduleWorkerResponseDto execute(ScheduleWorkerRequestDto request) {
|
||||
|
||||
Instant start = request.getFrom().toInstant(ZoneOffset.UTC);
|
||||
Instant end = request.getTo().toInstant(ZoneOffset.UTC);
|
||||
int limit = request.getMaxRecords() != null ? request.getMaxRecords() : 252;
|
||||
|
||||
log.info("[{}] worker start={} end={} limit={}", request.getJobCode(), start, end, limit);
|
||||
|
||||
try {
|
||||
String rawJson = samsaraClient.getDvirHistory(limit, start, end);
|
||||
|
||||
ExtIngestResult result = ingestService.ingestFromRawJson(rawJson);
|
||||
|
||||
log.info("[{}] ingest success received={}, inserted={}, updated={}, skipped={}",
|
||||
request.getJobCode(),
|
||||
result.getReceived(),
|
||||
result.getInserted(),
|
||||
result.getUpdated(),
|
||||
result.getSkipped()
|
||||
);
|
||||
|
||||
return ScheduleWorkerResponseDto.builder()
|
||||
.success(true)
|
||||
.processedCount(result.getReceived())
|
||||
.successCount(result.getInserted() + result.getUpdated())
|
||||
.failCount(result.getSkipped())
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[{}] ingest failed", request.getJobCode(), e);
|
||||
|
||||
return ScheduleWorkerResponseDto.builder()
|
||||
.success(false)
|
||||
.errorCode("INSPECTION_INGEST_ERROR")
|
||||
.errorMessage(e.getMessage())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package com.goi.integration.samsara.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.goi.integration.common.util.DateTimeUtil;
|
||||
import com.goi.integration.common.util.ExtPayloadHashUtil;
|
||||
import com.goi.integration.samsara.client.SamsaraClient;
|
||||
import com.goi.integration.samsara.dto.VehicleOdometerCommand;
|
||||
import com.goi.integration.samsara.dto.VehicleOdometerRecordDto;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VehicleOdometerService {
|
||||
|
||||
private final SamsaraClient samsaraClient;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Fetch samsara odometer data and build command
|
||||
*/
|
||||
public VehicleOdometerCommand fetchOdometerCommand(
|
||||
List<String> vehicleExternalIds,
|
||||
Instant startTime,
|
||||
Instant endTime
|
||||
) {
|
||||
|
||||
String rawJson =
|
||||
samsaraClient.getVehicleOdometerHistory(
|
||||
vehicleExternalIds,
|
||||
startTime,
|
||||
endTime
|
||||
);
|
||||
|
||||
List<VehicleOdometerRecordDto> records =
|
||||
parseRawOdometer(rawJson);
|
||||
|
||||
return VehicleOdometerCommand.builder()
|
||||
.source("SAMSARA")
|
||||
.fetchedAt(LocalDateTime.now())
|
||||
.records(records)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse samsara raw JSON → odometer snapshot records
|
||||
*/
|
||||
private List<VehicleOdometerRecordDto> parseRawOdometer(String rawJson) {
|
||||
|
||||
List<VehicleOdometerRecordDto> records = new ArrayList<>();
|
||||
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(rawJson);
|
||||
JsonNode dataArray = root.path("data");
|
||||
|
||||
if (!dataArray.isArray()) {
|
||||
log.warn("[ODOMETER] no data[] in samsara response");
|
||||
return records;
|
||||
}
|
||||
|
||||
for (JsonNode vehicleNode : dataArray) {
|
||||
|
||||
String vehicleExtId =
|
||||
vehicleNode.path("id").asText(null);
|
||||
|
||||
JsonNode odoArray =
|
||||
vehicleNode.path("obdOdometerMeters");
|
||||
|
||||
if (!odoArray.isArray() || odoArray.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (JsonNode sample : odoArray) {
|
||||
|
||||
String timeStr =
|
||||
sample.path("time").asText(null);
|
||||
|
||||
if (timeStr == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LocalDateTime sampleTime =
|
||||
DateTimeUtil.parseToUTC(timeStr);
|
||||
|
||||
long meters =
|
||||
sample.path("value").asLong();
|
||||
|
||||
String hash =
|
||||
ExtPayloadHashUtil.sha256FromStrings(
|
||||
vehicleExtId,
|
||||
timeStr,
|
||||
String.valueOf(meters)
|
||||
);
|
||||
|
||||
records.add(
|
||||
VehicleOdometerRecordDto.builder()
|
||||
.source("SAMSARA")
|
||||
.vehicleExternalId(vehicleExtId)
|
||||
.sampleTime(sampleTime)
|
||||
.odometerMeters(meters)
|
||||
.payloadHash(hash)
|
||||
.payloadJson(sample)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[ODOMETER][PARSE_FAIL]", e);
|
||||
throw new RuntimeException("Failed to parse samsara odometer payload", e);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue