From 20b8520a046eaa3f456b1031091b31e640d260b0 Mon Sep 17 00:00:00 2001 From: hyojin-kim4 Date: Thu, 5 Feb 2026 18:56:56 +0900 Subject: [PATCH] =?UTF-8?q?:bookmark:=20Batch=20Release=20Version=20:=201.?= =?UTF-8?q?1.0=20(#5)=20:sparkles:=20S&P=20=EC=88=98=EC=A7=91=20=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=EA=B0=92=20=EC=B6=94=EA=B0=80=20(=EC=A0=95=EA=B7=9C=ED=99=94?= =?UTF-8?q?=20=EC=9D=B4=EC=A0=84)=20*=20AIS=20*=20Movements=20*=20Events?= =?UTF-8?q?=20*=20Risk&Compliance=20*=20PSC=20*=20Ships=20*=20Facilities?= =?UTF-8?q?=20---------?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kim JiMyeung Co-authored-by: HeungTak Lee Co-authored-by: Claude Opus 4.5 --- pom.xml | 2 +- .../batch/common/batch/entity/BaseEntity.java | 18 + .../common/batch/reader/BaseApiReader.java | 119 +++ .../snp/batch/global/model/BatchApiLog.java | 2 +- .../batch/config/FlagCodeImportJobConfig.java | 38 +- .../config/Stat5CodeImportJobConfig.java | 38 +- .../processor/FlagCodeDataProcessor.java | 10 +- .../processor/Stat5CodeDataProcessor.java | 10 +- .../batch/reader/FlagCodeDataReader.java | 40 +- .../batch/reader/Stat5CodeDataReader.java | 39 +- .../repository/FlagCodeRepositoryImpl.java | 21 +- .../repository/Stat5CodeRepositoryImpl.java | 29 +- ...CompanyComplianceImportRangeJobConfig.java | 14 +- .../ComplianceImportRangeJobConfig.java | 14 +- .../CompanyComplianceDataProcessor.java | 8 +- .../processor/ComplianceDataProcessor.java | 7 + .../CompanyComplianceRepository.java | 2 +- .../CompanyComplianceRepositoryImpl.java | 53 +- .../repository/ComplianceRepository.java | 2 +- .../repository/ComplianceRepositoryImpl.java | 74 +- .../writer/CompanyComplianceDataWriter.java | 2 +- .../batch/writer/ComplianceDataWriter.java | 2 +- .../batch/config/EventImportJobConfig.java | 10 +- .../batch/processor/EventDataProcessor.java | 25 +- .../batch/repository/EventRepositoryImpl.java | 8 + .../jobs/event/batch/repository/EventSql.java | 98 +- .../batch/config/PortImportJobConfig.java | 49 +- .../batch/processor/PortDataProcessor.java | 8 + .../facility/batch/reader/PortDataReader.java | 23 +- .../repository/FacilityRepositoryImpl.java | 67 +- .../config/AnchorageCallsRangeJobConfig.java | 16 +- .../batch/config/BerthCallsRangJobConfig.java | 12 +- .../config/CurrentlyAtRangeJobConfig.java | 12 +- .../config/DestinationsRangeJobConfig.java | 28 +- .../config/ShipPortCallsRangeJobConfig.java | 12 +- .../config/StsOperationRangeJobConfig.java | 11 +- .../config/TerminalCallsRangeJobConfig.java | 11 +- .../batch/config/TransitsRangeJobConfig.java | 10 +- .../batch/entity/AnchorageCallsEntity.java | 9 +- .../batch/entity/BerthCallsEntity.java | 9 +- .../batch/entity/CurrentlyAtEntity.java | 9 +- .../batch/entity/DarkActivityEntity.java | 5 +- .../batch/entity/DestinationEntity.java | 5 +- .../batch/entity/PortCallsEntity.java | 13 +- .../batch/entity/StsOperationEntity.java | 5 +- .../batch/entity/TerminalCallsEntity.java | 5 +- .../movement/batch/entity/TransitsEntity.java | 5 +- .../processor/AnchorageCallsProcessor.java | 23 +- .../batch/processor/BerthCallsProcessor.java | 22 +- .../batch/processor/CurrentlyAtProcessor.java | 22 +- .../batch/processor/DestinationProcessor.java | 23 +- .../batch/processor/PortCallsProcessor.java | 24 +- .../processor/StsOperationProcessor.java | 23 +- .../processor/TerminalCallsProcessor.java | 22 +- .../batch/processor/TransitsProcessor.java | 26 +- .../AnchorageCallsRepositoryImpl.java | 91 +- .../repository/BerthCallsRepositoryImpl.java | 83 +- .../repository/CurrentlyAtRepositoryImpl.java | 46 +- .../repository/DestinationRepositoryImpl.java | 32 +- .../batch/repository/PortCallsRepository.java | 4 - .../repository/PortCallsRepositoryImpl.java | 140 +-- .../StsOperationRepositoryImpl.java | 41 +- .../TerminalCallsRepositoryImpl.java | 42 +- .../repository/TransitsRepositoryImpl.java | 28 +- .../batch/config/PscInspectionJobConfig.java | 13 +- .../batch/entity/PscAllCertificateEntity.java | 5 +- .../batch/entity/PscDefectEntity.java | 5 +- .../batch/entity/PscInspectionEntity.java | 8 +- .../processor/PscInspectionProcessor.java | 25 +- .../PscAllCertificateRepositoryImpl.java | 34 +- .../repository/PscDefectRepositoryImpl.java | 37 +- .../PscInspectionRepositoryImpl.java | 47 +- .../config/RiskImportRangeJobConfig.java | 8 +- .../batch/processor/RiskDataProcessor.java | 7 + .../risk/batch/repository/RiskRepository.java | 2 +- .../batch/repository/RiskRepositoryImpl.java | 82 +- .../risk/batch/writer/RiskDataWriter.java | 2 +- .../config/ShipDetailImportJobConfig.java | 22 +- .../config/ShipDetailUpdateJobConfig.java | 21 +- .../batch/dto/AdditionalInformationDto.java | 19 + .../batch/dto/BareBoatCharterHistoryDto.java | 12 + .../batch/dto/CallSignAndMmsiHistoryDto.java | 13 +- .../shipdetail/batch/dto/ClassHistoryDto.java | 15 + .../batch/dto/CompanyDetailDto.java | 36 + .../dto/CompanyVesselRelationshipDto.java | 28 + .../shipdetail/batch/dto/CrewListDto.java | 19 + .../batch/dto/DarkActivityConfirmedDto.java | 33 + .../shipdetail/batch/dto/FlagHistoryDto.java | 12 + .../dto/GroupBeneficialOwnerHistoryDto.java | 14 +- .../shipdetail/batch/dto/IceClassDto.java | 10 + .../shipdetail/batch/dto/NameHistoryDto.java | 11 + .../batch/dto/OperatorHistoryDto.java | 13 + .../shipdetail/batch/dto/OwnerHistoryDto.java | 13 + .../shipdetail/batch/dto/PandIHistoryDto.java | 13 + ...SafetyManagementCertificateHistoryDto.java | 21 + .../shipdetail/batch/dto/ShipDetailDto.java | 4 +- .../batch/dto/ShipManagerHistoryDto.java | 13 + .../shipdetail/batch/dto/ShipResultDto.java | 5 +- .../batch/dto/SisterShipLinksDto.java | 9 + .../batch/dto/SpecialFeatureDto.java | 11 + .../batch/dto/StatusHistoryDto.java | 12 + .../batch/dto/StowageCommodityDto.java | 13 + .../batch/dto/SurveyDatesHistoryDto.java | 15 + .../dto/SurveyDatesHistoryUniqueDto.java | 12 + .../batch/dto/TechnicalManagerHistoryDto.java | 13 + .../shipdetail/batch/dto/ThrustersDto.java | 16 + .../GroupBeneficialOwnerHistoryEntity.java | 1 - .../batch/entity/ShipDetailEntity.java | 58 +- .../entity/ShipManagerHistoryEntity.java | 1 - .../batch/entity/StowageCommodityEntity.java | 1 - .../processor/ShipDetailDataProcessor.java | 900 +++--------------- .../batch/reader/ShipDetailDataReader.java | 84 +- .../reader/ShipDetailUpdateDataReader.java | 80 +- .../repository/ShipDetailRepositoryImpl.java | 533 ++--------- .../batch/repository/ShipDetailSql.java | 686 ++++--------- .../batch/writer/ShipDetailDataWriter.java | 262 +++-- .../batch/config/ShipImportJobConfig.java | 38 +- .../batch/processor/ShipDataProcessor.java | 51 +- .../batch/reader/ShipDataReader.java | 43 +- .../batch/repository/ShipRepository.java | 23 +- .../batch/repository/ShipRepositoryImpl.java | 118 +-- .../batch/writer/ShipDataWriter.java | 2 +- src/main/resources/application-dev.yml | 14 +- src/main/resources/application-prod.yml | 14 +- src/main/resources/application.yml | 14 +- 125 files changed, 1986 insertions(+), 3361 deletions(-) diff --git a/pom.xml b/pom.xml index c7b1736..d1d5aa1 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.snp - snp-batch + snp-batch-validation 1.0.0 SNP Batch Spring Batch project for JSON to PostgreSQL with Web GUI diff --git a/src/main/java/com/snp/batch/common/batch/entity/BaseEntity.java b/src/main/java/com/snp/batch/common/batch/entity/BaseEntity.java index 1b35a2a..bedd1ee 100644 --- a/src/main/java/com/snp/batch/common/batch/entity/BaseEntity.java +++ b/src/main/java/com/snp/batch/common/batch/entity/BaseEntity.java @@ -43,4 +43,22 @@ public abstract class BaseEntity { * 컬럼: updated_by (VARCHAR(100)) */ private String updatedBy; + + /** + * 배치 잡 실행 ID + * 컬럼: job_execution_id (int8) + */ + private Long jobExecutionId; + + /** + * 배치 공통 필드 설정을 위한 편의 메서드 + */ + @SuppressWarnings("unchecked") + public T setBatchInfo(Long jobExecutionId, String createdBy) { + this.jobExecutionId = jobExecutionId; + this.createdBy = createdBy; + // 필요시 생성일시 강제 설정 (JPA Auditing을 안 쓸 경우) + if (this.createdAt == null) this.createdAt = LocalDateTime.now(); + return (T) this; + } } diff --git a/src/main/java/com/snp/batch/common/batch/reader/BaseApiReader.java b/src/main/java/com/snp/batch/common/batch/reader/BaseApiReader.java index a06d804..03e813b 100644 --- a/src/main/java/com/snp/batch/common/batch/reader/BaseApiReader.java +++ b/src/main/java/com/snp/batch/common/batch/reader/BaseApiReader.java @@ -170,6 +170,125 @@ public abstract class BaseApiReader implements ItemReader { } } + protected List executeWrapperApiCall( + String baseUrl, + String path, + Class responseWrapperClass, // Stat5CodeApiResponse.class 등을 받음 + Function> listExtractor, // 결과 객체에서 리스트를 꺼내는 로직 + BatchApiLogService logService) { + + String fullUri = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(path) + .build() + .toUriString(); + + long startTime = System.currentTimeMillis(); + int statusCode = 200; + String errorMessage = null; + Long responseSize = 0L; + + try { + log.info("[{}] API 요청 시작: {}", getReaderName(), fullUri); + + // 1. List이 아닌 Wrapper 객체(T)로 받아옵니다. + T response = webClient.get() + .uri(uriBuilder -> uriBuilder.path(path).build()) + .retrieve() + .bodyToMono(responseWrapperClass) + .block(); + + // 2. 추출 함수(listExtractor)를 사용하여 내부 리스트를 꺼냅니다. + List result = (response != null) ? listExtractor.apply(response) : Collections.emptyList(); + + responseSize = (long) result.size(); + return result; + + } catch (WebClientResponseException e) { + statusCode = e.getStatusCode().value(); + errorMessage = String.format("API Error: %s", e.getResponseBodyAsString()); + throw e; + } catch (Exception e) { + statusCode = 500; + errorMessage = String.format("System Error: %s", e.getMessage()); + throw e; + } finally { + long duration = System.currentTimeMillis() - startTime; + + logService.saveLog(BatchApiLog.builder() + .apiRequestLocation(getReaderName()) + .requestUri(fullUri) + .httpMethod("GET") + .statusCode(statusCode) + .responseTimeMs(duration) + .responseCount(responseSize) + .errorMessage(errorMessage) + .createdAt(LocalDateTime.now()) + .jobExecutionId(this.jobExecutionId) + .stepExecutionId(this.stepExecutionId) + .build()); + } + } + + protected List executeListApiCall( + String baseUrl, + String path, + ParameterizedTypeReference> typeReference, + BatchApiLogService logService) { + + String fullUri = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(path) + .build() + .toUriString(); + + long startTime = System.currentTimeMillis(); + int statusCode = 200; + String errorMessage = null; + Long responseSize = 0L; + + try { + log.info("[{}] API 요청 시작: {}", getReaderName(), fullUri); + + List result = webClient.get() + .uri(uriBuilder -> { + uriBuilder.path(path); + return uriBuilder.build(); + }) + .retrieve() + .bodyToMono(typeReference) + .block(); + + responseSize = (result != null) ? (long) result.size() : 0L; + return result; + + } catch (WebClientResponseException e) { + // API 서버에서 응답은 왔으나 에러인 경우 (4xx, 5xx) + statusCode = e.getStatusCode().value(); + errorMessage = String.format("API Error: %s", e.getResponseBodyAsString()); + throw e; + } catch (Exception e) { + // 네트워크 오류, 타임아웃 등 기타 예외 + statusCode = 500; + errorMessage = String.format("System Error: %s", e.getMessage()); + throw e; + } finally { + // 성공/실패 여부와 관계없이 무조건 로그 저장 + long duration = System.currentTimeMillis() - startTime; + + logService.saveLog(BatchApiLog.builder() + .apiRequestLocation(getReaderName()) + .requestUri(fullUri) + .httpMethod("GET") + .statusCode(statusCode) + .responseTimeMs(duration) + .responseCount(responseSize) + .errorMessage(errorMessage) + .createdAt(LocalDateTime.now()) + .jobExecutionId(this.jobExecutionId) // 추가 + .stepExecutionId(this.stepExecutionId) // 추가 + .build()); + } + } + /** * API 호출 및 로그 적재 통합 메서드 * Response Json 구조 : { "data": [...] } diff --git a/src/main/java/com/snp/batch/global/model/BatchApiLog.java b/src/main/java/com/snp/batch/global/model/BatchApiLog.java index 41ae691..71161c0 100644 --- a/src/main/java/com/snp/batch/global/model/BatchApiLog.java +++ b/src/main/java/com/snp/batch/global/model/BatchApiLog.java @@ -7,7 +7,7 @@ import org.hibernate.annotations.CreationTimestamp; import java.time.LocalDateTime; @Entity -@Table(name = "batch_api_log", schema = "snp_data") +@Table(name = "batch_api_log", schema = "t_snp_data") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor diff --git a/src/main/java/com/snp/batch/jobs/common/batch/config/FlagCodeImportJobConfig.java b/src/main/java/com/snp/batch/jobs/common/batch/config/FlagCodeImportJobConfig.java index a920d7e..f30fcc9 100644 --- a/src/main/java/com/snp/batch/jobs/common/batch/config/FlagCodeImportJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/common/batch/config/FlagCodeImportJobConfig.java @@ -7,9 +7,12 @@ import com.snp.batch.jobs.common.batch.processor.FlagCodeDataProcessor; import com.snp.batch.jobs.common.batch.reader.FlagCodeDataReader; import com.snp.batch.jobs.common.batch.repository.FlagCodeRepository; import com.snp.batch.jobs.common.batch.writer.FlagCodeDataWriter; +import com.snp.batch.jobs.facility.batch.reader.PortDataReader; +import com.snp.batch.service.BatchApiLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemReader; @@ -25,8 +28,14 @@ import org.springframework.web.reactive.function.client.WebClient; @Configuration public class FlagCodeImportJobConfig extends BaseJobConfig { + private final FlagCodeDataProcessor flagCodeDataProcessor; + private final FlagCodeDataReader flagCodeDataReader; private final FlagCodeRepository flagCodeRepository; private final WebClient maritimeApiWebClient; + private final BatchApiLogService batchApiLogService; + + @Value("${app.batch.ship-api.url}") + private String maritimeApiUrl; @Value("${app.batch.chunk-size:1000}") private int chunkSize; @@ -39,10 +48,16 @@ public class FlagCodeImportJobConfig extends BaseJobConfig createReader() { - return new FlagCodeDataReader(maritimeApiWebClient); + return flagCodeDataReader; + } + @Bean + @StepScope + public FlagCodeDataReader flagCodeDataReader( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, // SpEL로 ID 추출 + @Value("#{stepExecution.id}") Long stepExecutionId + ) { + FlagCodeDataReader reader = new FlagCodeDataReader(maritimeApiWebClient, batchApiLogService, maritimeApiUrl); + reader.setExecutionIds(jobExecutionId, stepExecutionId); // ID 세팅 + return reader; } @Override protected ItemProcessor createProcessor() { - return new FlagCodeDataProcessor(flagCodeRepository); + return flagCodeDataProcessor; + } + @Bean + @StepScope + public FlagCodeDataProcessor flagCodeDataProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + return new FlagCodeDataProcessor(jobExecutionId); } - @Override protected ItemWriter createWriter() { return new FlagCodeDataWriter(flagCodeRepository); diff --git a/src/main/java/com/snp/batch/jobs/common/batch/config/Stat5CodeImportJobConfig.java b/src/main/java/com/snp/batch/jobs/common/batch/config/Stat5CodeImportJobConfig.java index c1fcccf..b27b652 100644 --- a/src/main/java/com/snp/batch/jobs/common/batch/config/Stat5CodeImportJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/common/batch/config/Stat5CodeImportJobConfig.java @@ -7,14 +7,17 @@ import com.snp.batch.jobs.common.batch.processor.Stat5CodeDataProcessor; import com.snp.batch.jobs.common.batch.reader.Stat5CodeDataReader; import com.snp.batch.jobs.common.batch.repository.Stat5CodeRepository; import com.snp.batch.jobs.common.batch.writer.Stat5CodeDataWriter; +import com.snp.batch.service.BatchApiLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; @@ -24,16 +27,27 @@ import org.springframework.web.reactive.function.client.WebClient; @Configuration public class Stat5CodeImportJobConfig extends BaseJobConfig { + private final Stat5CodeDataProcessor stat5CodeDataProcessor; + private final Stat5CodeDataReader stat5CodeDataReader; private final Stat5CodeRepository stat5CodeRepository; + private final BatchApiLogService batchApiLogService; private final WebClient maritimeAisApiWebClient; + @Value("${app.batch.ais-api.url}") + private String maritimeAisApiUrl; public Stat5CodeImportJobConfig( JobRepository jobRepository, PlatformTransactionManager transactionManager, Stat5CodeRepository stat5CodeRepository, - @Qualifier("maritimeAisApiWebClient") WebClient maritimeAisApiWebClient) { + @Qualifier("maritimeAisApiWebClient") WebClient maritimeAisApiWebClient, + Stat5CodeDataProcessor stat5CodeDataProcessor, + Stat5CodeDataReader stat5CodeDataReader, + BatchApiLogService batchApiLogService) { super(jobRepository, transactionManager); this.stat5CodeRepository = stat5CodeRepository; this.maritimeAisApiWebClient = maritimeAisApiWebClient; + this.stat5CodeDataProcessor = stat5CodeDataProcessor; + this.stat5CodeDataReader = stat5CodeDataReader; + this.batchApiLogService = batchApiLogService; } @Override @@ -45,11 +59,27 @@ public class Stat5CodeImportJobConfig extends BaseJobConfig createReader() { return new Stat5CodeDataReader(maritimeAisApiWebClient); } + protected ItemReader createReader() { return stat5CodeDataReader; } + + @Bean + @StepScope + public Stat5CodeDataReader stat5CodeDataReader( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, // SpEL로 ID 추출 + @Value("#{stepExecution.id}") Long stepExecutionId + ) { + Stat5CodeDataReader reader = new Stat5CodeDataReader(maritimeAisApiWebClient, batchApiLogService, maritimeAisApiUrl); + reader.setExecutionIds(jobExecutionId, stepExecutionId); // ID 세팅 + return reader; + } @Override - protected ItemProcessor createProcessor() { return new Stat5CodeDataProcessor(stat5CodeRepository); } - + protected ItemProcessor createProcessor() { return stat5CodeDataProcessor; } + @Bean + @StepScope + public Stat5CodeDataProcessor stat5CodeDataProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + return new Stat5CodeDataProcessor(jobExecutionId); + } @Override protected ItemWriter createWriter() { return new Stat5CodeDataWriter(stat5CodeRepository); } diff --git a/src/main/java/com/snp/batch/jobs/common/batch/processor/FlagCodeDataProcessor.java b/src/main/java/com/snp/batch/jobs/common/batch/processor/FlagCodeDataProcessor.java index fb6966b..a5f95b1 100644 --- a/src/main/java/com/snp/batch/jobs/common/batch/processor/FlagCodeDataProcessor.java +++ b/src/main/java/com/snp/batch/jobs/common/batch/processor/FlagCodeDataProcessor.java @@ -3,16 +3,16 @@ package com.snp.batch.jobs.common.batch.processor; import com.snp.batch.common.batch.processor.BaseProcessor; import com.snp.batch.jobs.common.batch.dto.FlagCodeDto; import com.snp.batch.jobs.common.batch.entity.FlagCodeEntity; -import com.snp.batch.jobs.common.batch.repository.FlagCodeRepository; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; @Slf4j public class FlagCodeDataProcessor extends BaseProcessor { - private final FlagCodeRepository commonCodeRepository; + private final Long jobExecutionId; - public FlagCodeDataProcessor(FlagCodeRepository commonCodeRepository) { - this.commonCodeRepository = commonCodeRepository; + public FlagCodeDataProcessor(@Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + this.jobExecutionId = jobExecutionId; } @Override @@ -24,6 +24,8 @@ public class FlagCodeDataProcessor extends BaseProcessor { - private final Stat5CodeRepository stat5CodeRepository; + private final Long jobExecutionId; - public Stat5CodeDataProcessor(Stat5CodeRepository stat5CodeRepository) { - this.stat5CodeRepository = stat5CodeRepository; + public Stat5CodeDataProcessor(@Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + this.jobExecutionId = jobExecutionId; } @Override @@ -30,6 +30,8 @@ public class Stat5CodeDataProcessor extends BaseProcessor { - - public FlagCodeDataReader(WebClient webClient) { + private final BatchApiLogService batchApiLogService; + String maritimeApiUrl; + public FlagCodeDataReader(WebClient webClient, BatchApiLogService batchApiLogService, String maritimeApiUrl) { super(webClient); // BaseApiReader에 WebClient 전달 + this.batchApiLogService = batchApiLogService; + this.maritimeApiUrl = maritimeApiUrl; } // ======================================== @@ -24,28 +29,25 @@ public class FlagCodeDataReader extends BaseApiReader { protected String getReaderName() { return "FlagCodeDataReader"; } - + @Override + protected String getApiPath() { + return "/MaritimeWCF/APSShipService.svc/RESTFul/GetAssociatedFlagISOByName"; + } @Override protected List fetchDataFromApi() { try { log.info("GetAssociatedFlagISOByName API 호출 시작"); - FlagCodeApiResponse response = webClient - .get() - .uri(uriBuilder -> uriBuilder - .path("/MaritimeWCF/APSShipService.svc/RESTFul/GetAssociatedFlagISOByName") - .build()) - .retrieve() - .bodyToMono(FlagCodeApiResponse.class) - .block(); - - if (response != null && response.getAssociatedFlagISODetails() != null) { - log.info("API 응답 성공: 총 {} 건의 국가코드 데이터 수신", response.getAssociatedCount()); - return response.getAssociatedFlagISODetails(); - } else { - log.warn("API 응답이 null이거나 국가코드 데이터가 없습니다"); - return new ArrayList<>(); - } + // 공통 함수 호출 + List result = executeWrapperApiCall( + maritimeApiUrl, // 베이스 URL (필드 또는 설정값) + getApiPath(), // API 경로 + FlagCodeApiResponse.class, // 응답을 받을 래퍼 클래스 + FlagCodeApiResponse::getAssociatedFlagISODetails, // 리스트 추출 함수 (메서드 참조) + batchApiLogService // 로그 서비스 + ); + // 결과가 null일 경우 빈 리스트 반환 (안전장치) + return result != null ? result : Collections.emptyList(); } catch (Exception e) { log.error("GetAssociatedFlagISOByName API 호출 실패", e); diff --git a/src/main/java/com/snp/batch/jobs/common/batch/reader/Stat5CodeDataReader.java b/src/main/java/com/snp/batch/jobs/common/batch/reader/Stat5CodeDataReader.java index 238519d..e887aa0 100644 --- a/src/main/java/com/snp/batch/jobs/common/batch/reader/Stat5CodeDataReader.java +++ b/src/main/java/com/snp/batch/jobs/common/batch/reader/Stat5CodeDataReader.java @@ -3,43 +3,46 @@ package com.snp.batch.jobs.common.batch.reader; import com.snp.batch.common.batch.reader.BaseApiReader; import com.snp.batch.jobs.common.batch.dto.Stat5CodeApiResponse; import com.snp.batch.jobs.common.batch.dto.Stat5CodeDto; +import com.snp.batch.service.BatchApiLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.web.reactive.function.client.WebClient; import java.util.ArrayList; +import java.util.Collections; import java.util.List; @Slf4j public class Stat5CodeDataReader extends BaseApiReader { - public Stat5CodeDataReader(WebClient webClient) { + private final BatchApiLogService batchApiLogService; + String maritimeAisApiUrl; + public Stat5CodeDataReader(WebClient webClient, BatchApiLogService batchApiLogService, String maritimeAisApiUrl) { super(webClient); // BaseApiReader에 WebClient 전달 + this.batchApiLogService = batchApiLogService; + this.maritimeAisApiUrl = maritimeAisApiUrl; } @Override protected String getReaderName() { return "Stat5CodeDataReader"; } @Override + protected String getApiPath() { + return "/AisSvc.svc/AIS/GetStatcodes"; + } + @Override protected List fetchDataFromApi() { try { log.info("GetStatcodes API 호출 시작"); - Stat5CodeApiResponse response = webClient - .get() - .uri(uriBuilder -> uriBuilder - .path("/AisSvc.svc/AIS/GetStatcodes") - .build()) - .retrieve() - .bodyToMono(Stat5CodeApiResponse.class) - .block(); - - if (response != null && response.getStatcodeArr() != null) { - log.info("API 응답 성공: 총 {} 건의 Stat5Code 데이터 수신", response.getStatcodeArr().size()); - return response.getStatcodeArr(); - } else { - log.warn("API 응답이 null이거나 Stat5Code 데이터가 없습니다"); - return new ArrayList<>(); - } - + // 공통 함수 호출 + List result = executeWrapperApiCall( + maritimeAisApiUrl, // 베이스 URL (필드 또는 설정값) + getApiPath(), // API 경로 + Stat5CodeApiResponse.class, // 응답을 받을 래퍼 클래스 + Stat5CodeApiResponse::getStatcodeArr, // 리스트 추출 함수 (메서드 참조) + batchApiLogService // 로그 서비스 + ); + // 결과가 null일 경우 빈 리스트 반환 (안전장치) + return result != null ? result : Collections.emptyList(); } catch (Exception e) { log.error("GetAssociatedFlagISOByName API 호출 실패", e); log.error("에러 메시지: {}", e.getMessage()); diff --git a/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepositoryImpl.java index ee2e3ab..d87fb20 100644 --- a/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepositoryImpl.java @@ -8,6 +8,7 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; +import java.sql.Types; import java.util.List; @Slf4j @@ -25,7 +26,7 @@ public class FlagCodeRepositoryImpl extends BaseJdbcRepository createProcessor() { return companyComplianceDataProcessor; } - + @Bean + @StepScope + public CompanyComplianceDataProcessor companyComplianceDataProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + return new CompanyComplianceDataProcessor(jobExecutionId); + } @Override protected ItemWriter createWriter() { return companyComplianceDataWriter; diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/config/ComplianceImportRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/compliance/batch/config/ComplianceImportRangeJobConfig.java index 612e821..9c4d624 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/config/ComplianceImportRangeJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/config/ComplianceImportRangeJobConfig.java @@ -28,7 +28,8 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.web.reactive.function.client.WebClient; -import java.time.*; +import java.time.OffsetDateTime; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Map; @@ -47,7 +48,7 @@ public class ComplianceImportRangeJobConfig extends BaseMultiStepJobConfig createWriter() { return complianceDataWriter; diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/processor/CompanyComplianceDataProcessor.java b/src/main/java/com/snp/batch/jobs/compliance/batch/processor/CompanyComplianceDataProcessor.java index ca9d012..211ba1c 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/processor/CompanyComplianceDataProcessor.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/processor/CompanyComplianceDataProcessor.java @@ -4,12 +4,16 @@ import com.snp.batch.common.batch.processor.BaseProcessor; import com.snp.batch.jobs.compliance.batch.dto.CompanyComplianceDto; import com.snp.batch.jobs.compliance.batch.entity.CompanyComplianceEntity; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Slf4j @Component public class CompanyComplianceDataProcessor extends BaseProcessor { - + private final Long jobExecutionId; + public CompanyComplianceDataProcessor(@Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + this.jobExecutionId = jobExecutionId; + } @Override protected CompanyComplianceEntity processItem(CompanyComplianceDto dto) throws Exception { @@ -30,6 +34,8 @@ public class CompanyComplianceDataProcessor extends BaseProcessor { + private final Long jobExecutionId; + public ComplianceDataProcessor(@Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + this.jobExecutionId = jobExecutionId; + } @Override protected ComplianceEntity processItem(ComplianceDto dto) throws Exception { @@ -50,6 +55,8 @@ public class ComplianceDataProcessor extends BaseProcessor items); - void saveCompanyComplianceHistoryAll(List items); +// void saveCompanyComplianceHistoryAll(List items); } diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepositoryImpl.java index 13fe748..7b26406 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepositoryImpl.java @@ -20,7 +20,7 @@ public class CompanyComplianceRepositoryImpl extends BaseJdbcRepository { try { setUpdateParameters(ps, entity); @@ -118,20 +103,4 @@ public class CompanyComplianceRepositoryImpl extends BaseJdbcRepository items) { - if (items == null || items.isEmpty()) { - return; - } - jdbcTemplate.batchUpdate(getUpdateSql("tb_company_compliance_hstry", "owcode, lastupdated"), items, items.size(), - (ps, entity) -> { - try { - setUpdateParameters(ps, entity); - } catch (Exception e) { - log.error("배치 수정 파라미터 설정 실패", e); - throw new RuntimeException(e); - } - }); - log.info("{} 전체 저장 완료: 수정={} 건", getEntityName(), items.size()); - } } diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/repository/ComplianceRepository.java b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/ComplianceRepository.java index 7712a22..b34e42c 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/repository/ComplianceRepository.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/ComplianceRepository.java @@ -6,5 +6,5 @@ import java.util.List; public interface ComplianceRepository { void saveComplianceAll(List items); - void saveComplianceHistoryAll(List items); +// void saveComplianceHistoryAll(List items); } diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/repository/ComplianceRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/ComplianceRepositoryImpl.java index 68585c0..cd9d29e 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/repository/ComplianceRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/ComplianceRepositoryImpl.java @@ -21,7 +21,7 @@ public class ComplianceRepositoryImpl extends BaseJdbcRepository { - try { - setUpdateParameters(ps, entity); - } catch (Exception e) { - log.error("배치 수정 파라미터 설정 실패", e); - throw new RuntimeException(e); - } - }); - log.info("{} 전체 저장 완료: 수정={} 건", getEntityName(), items.size()); - } - - @Override - public void saveComplianceHistoryAll(List items) { - if (items == null || items.isEmpty()) { - return; - } - jdbcTemplate.batchUpdate(getUpdateSql("compliance_history", "lrimoshipno, dateamended"), items, items.size(), + jdbcTemplate.batchUpdate(getUpdateSql(), items, items.size(), (ps, entity) -> { try { setUpdateParameters(ps, entity); diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/writer/CompanyComplianceDataWriter.java b/src/main/java/com/snp/batch/jobs/compliance/batch/writer/CompanyComplianceDataWriter.java index eff49cb..1f75813 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/writer/CompanyComplianceDataWriter.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/writer/CompanyComplianceDataWriter.java @@ -20,6 +20,6 @@ public class CompanyComplianceDataWriter extends BaseWriter items) throws Exception { complianceRepository.saveCompanyComplianceAll(items); - complianceRepository.saveCompanyComplianceHistoryAll(items); +// complianceRepository.saveCompanyComplianceHistoryAll(items); } } diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/writer/ComplianceDataWriter.java b/src/main/java/com/snp/batch/jobs/compliance/batch/writer/ComplianceDataWriter.java index 50e6e8f..e102db3 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/writer/ComplianceDataWriter.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/writer/ComplianceDataWriter.java @@ -19,6 +19,6 @@ public class ComplianceDataWriter extends BaseWriter { @Override protected void writeItems(List items) throws Exception { complianceRepository.saveComplianceAll(items); - complianceRepository.saveComplianceHistoryAll(items); +// complianceRepository.saveComplianceHistoryAll(items); } } diff --git a/src/main/java/com/snp/batch/jobs/event/batch/config/EventImportJobConfig.java b/src/main/java/com/snp/batch/jobs/event/batch/config/EventImportJobConfig.java index 5abb174..8d83634 100644 --- a/src/main/java/com/snp/batch/jobs/event/batch/config/EventImportJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/event/batch/config/EventImportJobConfig.java @@ -44,7 +44,7 @@ public class EventImportJobConfig extends BaseMultiStepJobConfig createWriter() { return eventDataWriter; } diff --git a/src/main/java/com/snp/batch/jobs/event/batch/processor/EventDataProcessor.java b/src/main/java/com/snp/batch/jobs/event/batch/processor/EventDataProcessor.java index 74dd115..899a04b 100644 --- a/src/main/java/com/snp/batch/jobs/event/batch/processor/EventDataProcessor.java +++ b/src/main/java/com/snp/batch/jobs/event/batch/processor/EventDataProcessor.java @@ -1,12 +1,17 @@ package com.snp.batch.jobs.event.batch.processor; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.processor.BaseProcessor; import com.snp.batch.jobs.event.batch.dto.CargoDto; import com.snp.batch.jobs.event.batch.dto.EventDetailDto; import com.snp.batch.jobs.event.batch.dto.HumanCasualtyDto; import com.snp.batch.jobs.event.batch.dto.RelationshipDto; +import com.snp.batch.jobs.event.batch.entity.CargoEntity; import com.snp.batch.jobs.event.batch.entity.EventDetailEntity; +import com.snp.batch.jobs.event.batch.entity.HumanCasualtyEntity; +import com.snp.batch.jobs.event.batch.entity.RelationshipEntity; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.stream.Collectors; @@ -14,6 +19,12 @@ import java.util.stream.Collectors; @Slf4j @Component public class EventDataProcessor extends BaseProcessor { + private static Long jobExecutionId; + public EventDataProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId + ) { + this.jobExecutionId = jobExecutionId; + } @Override protected EventDetailEntity processItem(EventDetailDto dto) throws Exception { log.debug("Event 데이터 처리 시작: Event ID = {}", dto.getEventID()); @@ -61,12 +72,20 @@ public class EventDataProcessor extends BaseProcessor (CargoEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) .humanCasualties(dto.getHumanCasualties() != null ? - dto.getHumanCasualties().stream().map(HumanCasualtyDto::toEntity).collect(Collectors.toList()) : null) + dto.getHumanCasualties().stream() + .map(d -> (HumanCasualtyEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) .relationships(dto.getRelationships() != null ? - dto.getRelationships().stream().map(RelationshipDto::toEntity).collect(Collectors.toList()) : null) + dto.getRelationships().stream() + .map(d -> (RelationshipEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) .build(); log.debug("Event 데이터 처리 완료: Event ID = {}", dto.getEventID()); diff --git a/src/main/java/com/snp/batch/jobs/event/batch/repository/EventRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/event/batch/repository/EventRepositoryImpl.java index 813e14a..15da269 100644 --- a/src/main/java/com/snp/batch/jobs/event/batch/repository/EventRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/event/batch/repository/EventRepositoryImpl.java @@ -182,6 +182,8 @@ public class EventRepositoryImpl extends BaseJdbcRepository { private final JdbcTemplate jdbcTemplate; private final WebClient maritimeServiceApiWebClient; - private final PortDataProcessor portDataProcessor; - private final PortDataWriter portDataWriter; + private final PortDataReader portDataReader; + private final BatchApiLogService batchApiLogService; + + @Value("${app.batch.webservice-api.url}") + private String maritimeServiceApiUrl; @Override protected int getChunkSize() { @@ -37,46 +43,65 @@ public class PortImportJobConfig extends BaseJobConfig { public PortImportJobConfig( JobRepository jobRepository, PlatformTransactionManager transactionManager, - PortDataProcessor portDataProcessor, + PortDataReader portDataReader, PortDataWriter portDataWriter, JdbcTemplate jdbcTemplate, - @Qualifier("maritimeServiceApiWebClient")WebClient maritimeServiceApiWebClient) { + @Qualifier("maritimeServiceApiWebClient")WebClient maritimeServiceApiWebClient, + BatchApiLogService batchApiLogService) { super(jobRepository, transactionManager); this.jdbcTemplate = jdbcTemplate; this.maritimeServiceApiWebClient = maritimeServiceApiWebClient; - this.portDataProcessor = portDataProcessor; this.portDataWriter = portDataWriter; + this.portDataReader = portDataReader; + this.batchApiLogService = batchApiLogService; } @Override protected String getJobName() { - return "portImportJob"; + return "PortImportJob"; } @Override protected String getStepName() { - return "portImportStep"; + return "PortImportStep"; } @Override protected ItemReader createReader() { - return new PortDataReader(maritimeServiceApiWebClient, jdbcTemplate); + return portDataReader; + } + + @Bean + @StepScope + public PortDataReader portDataReader( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, // SpEL로 ID 추출 + @Value("#{stepExecution.id}") Long stepExecutionId + ) { + PortDataReader reader = new PortDataReader(maritimeServiceApiWebClient, jdbcTemplate, batchApiLogService, maritimeServiceApiUrl); + reader.setExecutionIds(jobExecutionId, stepExecutionId); // ID 세팅 + return reader; } @Override protected ItemProcessor createProcessor() { - return portDataProcessor; + // 2. 메서드 호출 방식으로 변경 + return portDataProcessor(null); + } + @Bean + @StepScope + public PortDataProcessor portDataProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + return new PortDataProcessor(jobExecutionId); } - @Override protected ItemWriter createWriter() { return portDataWriter; } - @Bean(name = "portImportJob") + @Bean(name = "PortImportJob") public Job portImportJob() { return job(); } - @Bean(name = "portImportStep") + @Bean(name = "PortImportStep") public Step portImportStep() { return step(); } diff --git a/src/main/java/com/snp/batch/jobs/facility/batch/processor/PortDataProcessor.java b/src/main/java/com/snp/batch/jobs/facility/batch/processor/PortDataProcessor.java index 2e0ce64..391c264 100644 --- a/src/main/java/com/snp/batch/jobs/facility/batch/processor/PortDataProcessor.java +++ b/src/main/java/com/snp/batch/jobs/facility/batch/processor/PortDataProcessor.java @@ -4,11 +4,17 @@ import com.snp.batch.common.batch.processor.BaseProcessor; import com.snp.batch.jobs.facility.batch.dto.PortDto; import com.snp.batch.jobs.facility.batch.entity.PortEntity; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Slf4j @Component public class PortDataProcessor extends BaseProcessor { + private static Long jobExecutionId; + public PortDataProcessor(@Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + this.jobExecutionId = jobExecutionId; + } + @Override protected PortEntity processItem(PortDto dto) throws Exception { log.debug("Port 데이터 처리 시작: Port ID = {}", dto.getPortId()); @@ -71,6 +77,8 @@ public class PortDataProcessor extends BaseProcessor { .freeTradeZone(dto.getFreeTradeZone()) .ecoPort(dto.getEcoPort()) .emissionControlArea(dto.getEmissionControlArea()) + .jobExecutionId(jobExecutionId) + .createdBy("SYSTEM") .build(); log.debug("Port 데이터 처리 완료: Port ID = {}", dto.getPortId()); diff --git a/src/main/java/com/snp/batch/jobs/facility/batch/reader/PortDataReader.java b/src/main/java/com/snp/batch/jobs/facility/batch/reader/PortDataReader.java index 42b7dae..1583b56 100644 --- a/src/main/java/com/snp/batch/jobs/facility/batch/reader/PortDataReader.java +++ b/src/main/java/com/snp/batch/jobs/facility/batch/reader/PortDataReader.java @@ -3,6 +3,7 @@ package com.snp.batch.jobs.facility.batch.reader; import com.snp.batch.common.batch.reader.BaseApiReader; import com.snp.batch.jobs.facility.batch.dto.PortDto; import com.snp.batch.jobs.shipimport.batch.dto.ShipApiResponse; +import com.snp.batch.service.BatchApiLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.core.ParameterizedTypeReference; import org.springframework.jdbc.core.JdbcTemplate; @@ -17,11 +18,14 @@ public class PortDataReader extends BaseApiReader { private final JdbcTemplate jdbcTemplate; private List allImoNumbers; private int currentBatchIndex = 0; - private final int batchSize = 100; - - public PortDataReader(WebClient webClient, JdbcTemplate jdbcTemplate) { + private final int batchSize = 5000; + private final BatchApiLogService batchApiLogService; + String maritimeServiceApiUrl; + public PortDataReader(WebClient webClient, JdbcTemplate jdbcTemplate, BatchApiLogService batchApiLogService, String maritimeServiceApiUrl) { super(webClient); this.jdbcTemplate = jdbcTemplate; + this.batchApiLogService = batchApiLogService; + this.maritimeServiceApiUrl = maritimeServiceApiUrl; } @Override @@ -57,13 +61,12 @@ public class PortDataReader extends BaseApiReader { } private List callFacilityPortApiWithBatch() { - String url = getApiPath(); - log.debug("[{}] API 호출: {}", getReaderName(), url); - return webClient.get() - .uri(url) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() {}) - .block(); + return executeListApiCall( + maritimeServiceApiUrl, + getApiPath(), + new ParameterizedTypeReference>() {}, + batchApiLogService + ); } } diff --git a/src/main/java/com/snp/batch/jobs/facility/batch/repository/FacilityRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/facility/batch/repository/FacilityRepositoryImpl.java index 1ef9e0c..4eaf325 100644 --- a/src/main/java/com/snp/batch/jobs/facility/batch/repository/FacilityRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/facility/batch/repository/FacilityRepositoryImpl.java @@ -21,7 +21,7 @@ public class FacilityRepositoryImpl extends BaseJdbcRepository @Override protected String getTableName() { - return null; + return "t_snp_data.facility_port"; } @Override @@ -42,73 +42,24 @@ public class FacilityRepositoryImpl extends BaseJdbcRepository @Override protected String getUpdateSql() { return """ - INSERT INTO snp_data.facility_port ( + INSERT INTO %s( port_ID, old_ID, status, port_Name, unlocode, countryCode, country_Name, region_Name, continent_Name, master_POID, dec_Lat, dec_Long, position_lat, position_long, position_z, position_m, position_hasZ, position_hasM, position_isNull, position_stSrid, time_Zone, dayLight_Saving_Time, maximum_Draft, max_LOA, max_Beam, max_DWT, max_Offshore_Draught, max_Offshore_LOA, max_Offshore_BCM, max_Offshore_DWT, breakbulk_Facilities, container_Facilities, dry_Bulk_Facilities, liquid_Facilities, roRo_Facilities, passenger_Facilities, dry_Dock_Facilities, lpG_Facilities, lnG_Facilities, lnG_Bunker, dO_Bunker, fO_Bunker, ispS_Compliant, csI_Compliant, free_Trade_Zone, ecO_Port, emission_Control_Area, wS_Port, - last_Update, entry_Date, batch_flag + last_Update, entry_Date, + job_execution_id, created_by ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?::timestamptz, ?::timestamptz, 'N' - ) ON CONFLICT (port_ID) DO UPDATE - SET - old_ID = EXCLUDED.old_ID, - status = EXCLUDED.status, - port_Name = EXCLUDED.port_Name, - unlocode = EXCLUDED.unlocode, - countryCode = EXCLUDED.countryCode, - country_Name = EXCLUDED.country_Name, - region_Name = EXCLUDED.region_Name, - continent_Name = EXCLUDED.continent_Name, - master_POID = EXCLUDED.master_POID, - dec_Lat = EXCLUDED.dec_Lat, - dec_Long = EXCLUDED.dec_Long, - position_lat = EXCLUDED.position_lat, - position_long = EXCLUDED.position_long, - position_z = EXCLUDED.position_z, - position_m = EXCLUDED.position_m, - position_hasZ = EXCLUDED.position_hasZ, - position_hasM = EXCLUDED.position_hasM, - position_isNull = EXCLUDED.position_isNull, - position_stSrid = EXCLUDED.position_stSrid, - time_Zone = EXCLUDED.time_Zone, - dayLight_Saving_Time = EXCLUDED.dayLight_Saving_Time, - maximum_Draft = EXCLUDED.maximum_Draft, - max_LOA = EXCLUDED.max_LOA, - max_Beam = EXCLUDED.max_Beam, - max_DWT = EXCLUDED.max_DWT, - max_Offshore_Draught = EXCLUDED.max_Offshore_Draught, - max_Offshore_LOA = EXCLUDED.max_Offshore_LOA, - max_Offshore_BCM = EXCLUDED.max_Offshore_BCM, - max_Offshore_DWT = EXCLUDED.max_Offshore_DWT, - breakbulk_Facilities = EXCLUDED.breakbulk_Facilities, - container_Facilities = EXCLUDED.container_Facilities, - dry_Bulk_Facilities = EXCLUDED.dry_Bulk_Facilities, - liquid_Facilities = EXCLUDED.liquid_Facilities, - roRo_Facilities = EXCLUDED.roRo_Facilities, - passenger_Facilities = EXCLUDED.passenger_Facilities, - dry_Dock_Facilities = EXCLUDED.dry_Dock_Facilities, - lpG_Facilities = EXCLUDED.lpG_Facilities, - lnG_Facilities = EXCLUDED.lnG_Facilities, - lnG_Bunker = EXCLUDED.lnG_Bunker, - dO_Bunker = EXCLUDED.dO_Bunker, - fO_Bunker = EXCLUDED.fO_Bunker, - ispS_Compliant = EXCLUDED.ispS_Compliant, - csI_Compliant = EXCLUDED.csI_Compliant, - free_Trade_Zone = EXCLUDED.free_Trade_Zone, - ecO_Port = EXCLUDED.ecO_Port, - emission_Control_Area = EXCLUDED.emission_Control_Area, - wS_Port = EXCLUDED.wS_Port, - last_Update = EXCLUDED.last_Update, - entry_Date = EXCLUDED.entry_Date, - batch_flag = 'N' - """; + ?::timestamptz, ?::timestamptz, + ?, ? + ); + """.formatted(getTableName()); } @Override @@ -176,6 +127,8 @@ public class FacilityRepositoryImpl extends BaseJdbcRepository setIntegerOrNull(ps, idx++, entity.getWsPort()); // 원본 위치: 마지막 부분 (INT8에 맞게 setLongOrNull 사용 가정) ps.setString(idx++, entity.getLastUpdate()); // String 대신 Timestamp 타입이 JDBC 표준에 적합합니다. ps.setString(idx++, entity.getEntryDate()); // String 대신 Timestamp 타입이 JDBC 표준에 적합합니다. + ps.setObject(idx++, entity.getJobExecutionId(), Types.INTEGER); + ps.setString(idx++, entity.getCreatedBy()); } @Override diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/config/AnchorageCallsRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/movement/batch/config/AnchorageCallsRangeJobConfig.java index 603c617..e2fc384 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/config/AnchorageCallsRangeJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/config/AnchorageCallsRangeJobConfig.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.movement.batch.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; import com.snp.batch.jobs.movement.batch.dto.AnchorageCallsDto; import com.snp.batch.jobs.movement.batch.entity.AnchorageCallsEntity; @@ -39,12 +40,13 @@ public class AnchorageCallsRangeJobConfig extends BaseMultiStepJobConfig createProcessor() { return anchorageCallsProcessor; } + @Bean + @StepScope + public AnchorageCallsProcessor anchorageCallsProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper + ) { + return new AnchorageCallsProcessor(jobExecutionId, objectMapper); + } @Override protected ItemWriter createWriter() { // 타입 변경 diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/config/BerthCallsRangJobConfig.java b/src/main/java/com/snp/batch/jobs/movement/batch/config/BerthCallsRangJobConfig.java index 0a17a5e..ae8e73e 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/config/BerthCallsRangJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/config/BerthCallsRangJobConfig.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.movement.batch.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; import com.snp.batch.jobs.movement.batch.dto.BerthCallsDto; import com.snp.batch.jobs.movement.batch.entity.BerthCallsEntity; @@ -44,7 +45,7 @@ public class BerthCallsRangJobConfig extends BaseMultiStepJobConfig createWriter() { return berthCallsWriter; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/config/CurrentlyAtRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/movement/batch/config/CurrentlyAtRangeJobConfig.java index 82c2549..d91c51f 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/config/CurrentlyAtRangeJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/config/CurrentlyAtRangeJobConfig.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.movement.batch.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; import com.snp.batch.jobs.movement.batch.dto.CurrentlyAtDto; import com.snp.batch.jobs.movement.batch.entity.CurrentlyAtEntity; @@ -44,7 +45,7 @@ public class CurrentlyAtRangeJobConfig extends BaseMultiStepJobConfig createProcessor() { return currentlyAtProcessor; } - + @Bean + @StepScope + public CurrentlyAtProcessor currentlyAtProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper + ) { + return new CurrentlyAtProcessor(jobExecutionId, objectMapper); + } @Override protected ItemWriter createWriter() { // 타입 변경 return currentlyAtWriter; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/config/DestinationsRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/movement/batch/config/DestinationsRangeJobConfig.java index 928d502..9612c60 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/config/DestinationsRangeJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/config/DestinationsRangeJobConfig.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.movement.batch.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; import com.snp.batch.jobs.movement.batch.dto.DestinationDto; import com.snp.batch.jobs.movement.batch.entity.DestinationEntity; @@ -32,8 +33,8 @@ import org.springframework.web.reactive.function.client.WebClient; @Configuration public class DestinationsRangeJobConfig extends BaseMultiStepJobConfig { - private final DestinationProcessor DestinationProcessor; - private final DestinationWriter DestinationWriter; + private final DestinationProcessor destinationProcessor; + private final DestinationWriter destinationWriter; private final DestinationRangeReader destinationRangeReader; private final WebClient maritimeApiWebClient; private final JdbcTemplate jdbcTemplate; @@ -44,14 +45,14 @@ public class DestinationsRangeJobConfig extends BaseMultiStepJobConfig createProcessor() { - return DestinationProcessor; + return destinationProcessor; + } + + @Bean + @StepScope + public DestinationProcessor destinationProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper + ) { + return new DestinationProcessor(jobExecutionId, objectMapper); } @Override protected ItemWriter createWriter() { // 타입 변경 - return DestinationWriter; + return destinationWriter; } @Override diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/config/ShipPortCallsRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/movement/batch/config/ShipPortCallsRangeJobConfig.java index bc95359..08d5695 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/config/ShipPortCallsRangeJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/config/ShipPortCallsRangeJobConfig.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.movement.batch.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; import com.snp.batch.jobs.movement.batch.dto.PortCallsDto; import com.snp.batch.jobs.movement.batch.entity.PortCallsEntity; @@ -44,7 +45,7 @@ public class ShipPortCallsRangeJobConfig extends BaseMultiStepJobConfig createWriter() { // 타입 변경 return portCallsWriter; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/config/StsOperationRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/movement/batch/config/StsOperationRangeJobConfig.java index 33a94f9..847efd8 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/config/StsOperationRangeJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/config/StsOperationRangeJobConfig.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.movement.batch.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; import com.snp.batch.jobs.movement.batch.dto.StsOperationDto; import com.snp.batch.jobs.movement.batch.entity.StsOperationEntity; @@ -44,7 +45,7 @@ public class StsOperationRangeJobConfig extends BaseMultiStepJobConfig createProcessor() { return stsOperationProcessor; } + @Bean + @StepScope + public StsOperationProcessor stsOperationProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper + ) { + return new StsOperationProcessor(jobExecutionId, objectMapper); + } @Override protected ItemWriter createWriter() { // 타입 변경 diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/config/TerminalCallsRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/movement/batch/config/TerminalCallsRangeJobConfig.java index 0d33208..9de4b63 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/config/TerminalCallsRangeJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/config/TerminalCallsRangeJobConfig.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.movement.batch.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; import com.snp.batch.jobs.movement.batch.dto.TerminalCallsDto; import com.snp.batch.jobs.movement.batch.entity.TerminalCallsEntity; @@ -44,7 +45,7 @@ public class TerminalCallsRangeJobConfig extends BaseMultiStepJobConfig createProcessor() { return terminalCallsProcessor; } + @Bean + @StepScope + public TerminalCallsProcessor terminalCallsProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper + ) { + return new TerminalCallsProcessor(jobExecutionId, objectMapper); + } @Override protected ItemWriter createWriter() { // 타입 변경 diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/config/TransitsRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/movement/batch/config/TransitsRangeJobConfig.java index 11d2bcd..468e5b0 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/config/TransitsRangeJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/config/TransitsRangeJobConfig.java @@ -44,7 +44,7 @@ public class TransitsRangeJobConfig extends BaseMultiStepJobConfig createWriter() { // 타입 변경 return transitsWriter; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/AnchorageCallsEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/AnchorageCallsEntity.java index 7487582..c21cbfd 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/AnchorageCallsEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/AnchorageCallsEntity.java @@ -1,12 +1,10 @@ package com.snp.batch.jobs.movement.batch.entity; import com.fasterxml.jackson.databind.JsonNode; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -16,7 +14,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class AnchorageCallsEntity { +@EqualsAndHashCode(callSuper = true) +public class AnchorageCallsEntity extends BaseEntity { private Long id; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/BerthCallsEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/BerthCallsEntity.java index 5e3e99a..5553e94 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/BerthCallsEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/BerthCallsEntity.java @@ -1,12 +1,10 @@ package com.snp.batch.jobs.movement.batch.entity; import com.fasterxml.jackson.databind.JsonNode; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -16,7 +14,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class BerthCallsEntity { +@EqualsAndHashCode(callSuper = true) +public class BerthCallsEntity extends BaseEntity { private Long id; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/CurrentlyAtEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/CurrentlyAtEntity.java index ffef239..5b5c60a 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/CurrentlyAtEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/CurrentlyAtEntity.java @@ -1,12 +1,10 @@ package com.snp.batch.jobs.movement.batch.entity; import com.fasterxml.jackson.databind.JsonNode; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -16,7 +14,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class CurrentlyAtEntity { +@EqualsAndHashCode(callSuper = true) +public class CurrentlyAtEntity extends BaseEntity { private String movementType; private String imolRorIHSNumber; private LocalDateTime movementDate; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/DarkActivityEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/DarkActivityEntity.java index 7d2fa6f..808c5fb 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/DarkActivityEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/DarkActivityEntity.java @@ -1,8 +1,10 @@ package com.snp.batch.jobs.movement.batch.entity; import com.fasterxml.jackson.databind.JsonNode; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -12,7 +14,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class DarkActivityEntity { +@EqualsAndHashCode(callSuper = true) +public class DarkActivityEntity extends BaseEntity { private Long id; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/DestinationEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/DestinationEntity.java index f3523f6..3395baa 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/DestinationEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/DestinationEntity.java @@ -1,8 +1,10 @@ package com.snp.batch.jobs.movement.batch.entity; import com.fasterxml.jackson.databind.JsonNode; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -12,7 +14,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class DestinationEntity { +@EqualsAndHashCode(callSuper = true) +public class DestinationEntity extends BaseEntity { private String movementType; private String imolRorIHSNumber; private LocalDateTime movementDate; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/PortCallsEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/PortCallsEntity.java index 268f652..651265a 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/PortCallsEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/PortCallsEntity.java @@ -1,12 +1,10 @@ package com.snp.batch.jobs.movement.batch.entity; import com.fasterxml.jackson.databind.JsonNode; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -16,12 +14,9 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class PortCallsEntity { - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ship_movement_id_seq") - @SequenceGenerator(name = "ship_movement_id_seq", sequenceName = "ship_movement_id_seq", allocationSize = 1) +@EqualsAndHashCode(callSuper = true) +public class PortCallsEntity extends BaseEntity { private Long id; - private String movementType; private String imolRorIHSNumber; private LocalDateTime movementDate; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/StsOperationEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/StsOperationEntity.java index 1902bca..81df3cf 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/StsOperationEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/StsOperationEntity.java @@ -1,8 +1,10 @@ package com.snp.batch.jobs.movement.batch.entity; import com.fasterxml.jackson.databind.JsonNode; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -12,7 +14,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class StsOperationEntity { +@EqualsAndHashCode(callSuper = true) +public class StsOperationEntity extends BaseEntity { private Long id; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/TerminalCallsEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/TerminalCallsEntity.java index b362060..bb68aae 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/TerminalCallsEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/TerminalCallsEntity.java @@ -2,8 +2,10 @@ package com.snp.batch.jobs.movement.batch.entity; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -13,7 +15,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class TerminalCallsEntity { +@EqualsAndHashCode(callSuper = true) +public class TerminalCallsEntity extends BaseEntity { private Long id; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/entity/TransitsEntity.java b/src/main/java/com/snp/batch/jobs/movement/batch/entity/TransitsEntity.java index d2875c8..cddbad6 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/entity/TransitsEntity.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/entity/TransitsEntity.java @@ -1,7 +1,9 @@ package com.snp.batch.jobs.movement.batch.entity; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -11,7 +13,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class TransitsEntity { +@EqualsAndHashCode(callSuper = true) +public class TransitsEntity extends BaseEntity { private String movementType; private String imolRorIHSNumber; private LocalDateTime movementDate; diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/processor/AnchorageCallsProcessor.java b/src/main/java/com/snp/batch/jobs/movement/batch/processor/AnchorageCallsProcessor.java index 5f47127..bd560dd 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/processor/AnchorageCallsProcessor.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/processor/AnchorageCallsProcessor.java @@ -6,33 +6,28 @@ import com.snp.batch.common.batch.processor.BaseProcessor; import com.snp.batch.jobs.movement.batch.dto.AnchorageCallsDto; import com.snp.batch.jobs.movement.batch.entity.AnchorageCallsEntity; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.time.LocalDateTime; -/** - * 선박 상세 정보 Processor - * ShipDetailDto → ShipDetailEntity 변환 - */ - -/** - * 선박 상세 정보 Processor (해시 비교 및 증분 데이터 추출) - * I: ShipDetailComparisonData (DB 해시 + API Map Data) - * O: ShipDetailUpdate (변경분) - */ @Slf4j @Component public class AnchorageCallsProcessor extends BaseProcessor { private final ObjectMapper objectMapper; - - public AnchorageCallsProcessor(ObjectMapper objectMapper) { + private final Long jobExecutionId; + public AnchorageCallsProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper) + { + this.jobExecutionId = jobExecutionId; this.objectMapper = objectMapper; } @Override protected AnchorageCallsEntity processItem(AnchorageCallsDto dto) throws Exception { - log.debug("선박 상세 정보 처리 시작: imoNumber={}, facilityName={}", + log.debug("AnchorageCalls 정보 처리 시작: imoNumber={}, facilityName={}", dto.getImolRorIHSNumber(), dto.getFacilityName()); JsonNode positionNode = null; @@ -60,6 +55,8 @@ public class AnchorageCallsProcessor extends BaseProcessor { private final ObjectMapper objectMapper; + private final Long jobExecutionId; - public BerthCallsProcessor(ObjectMapper objectMapper) { + public BerthCallsProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper) + { + this.jobExecutionId = jobExecutionId; this.objectMapper = objectMapper; } @Override protected BerthCallsEntity processItem(BerthCallsDto dto) throws Exception { - log.debug("선박 상세 정보 처리 시작: imoNumber={}, facilityName={}", + log.debug("BerthCalls 정보 처리 시작: imoNumber={}, facilityName={}", dto.getImolRorIHSNumber(), dto.getFacilityName()); JsonNode positionNode = null; @@ -60,6 +56,8 @@ public class BerthCallsProcessor extends BaseProcessor { private final ObjectMapper objectMapper; + private final Long jobExecutionId; - public CurrentlyAtProcessor(ObjectMapper objectMapper) { + public CurrentlyAtProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper) + { + this.jobExecutionId = jobExecutionId; this.objectMapper = objectMapper; } @Override protected CurrentlyAtEntity processItem(CurrentlyAtDto dto) throws Exception { - log.debug("Currently 정보 처리 시작: imoNumber={}, facilityName={}", + log.debug("CurrentlyAt 정보 처리 시작: imoNumber={}, facilityName={}", dto.getImolRorIHSNumber(), dto.getFacilityName()); JsonNode positionNode = null; @@ -63,6 +59,8 @@ public class CurrentlyAtProcessor extends BaseProcessor { private final ObjectMapper objectMapper; - - public DestinationProcessor(ObjectMapper objectMapper) { + private final Long jobExecutionId; + public DestinationProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper) + { + this.jobExecutionId = jobExecutionId; this.objectMapper = objectMapper; } @Override protected DestinationEntity processItem(DestinationDto dto) throws Exception { - log.debug("선박 상세 정보 처리 시작: imoNumber={}, facilityName={}", + log.debug("Destinations 정보 처리 시작: imoNumber={}, facilityName={}", dto.getImolRorIHSNumber(), dto.getFacilityName()); JsonNode positionNode = null; @@ -54,6 +49,8 @@ public class DestinationProcessor extends BaseProcessor { private final ObjectMapper objectMapper; - - public PortCallsProcessor(ObjectMapper objectMapper) { + private final Long jobExecutionId; + public PortCallsProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper) + { + this.jobExecutionId = jobExecutionId; this.objectMapper = objectMapper; } @Override protected PortCallsEntity processItem(PortCallsDto dto) throws Exception { - log.debug("선박 상세 정보 처리 시작: imoNumber={}, facilityName={}", + log.debug("PortCalls 정보 처리 시작: imoNumber={}, facilityName={}", dto.getImolRorIHSNumber(), dto.getFacilityName()); JsonNode positionNode = null; @@ -63,7 +58,8 @@ public class PortCallsProcessor extends BaseProcessor { private final ObjectMapper objectMapper; - - public StsOperationProcessor(ObjectMapper objectMapper) { + private final Long jobExecutionId; + public StsOperationProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper) + { + this.jobExecutionId = jobExecutionId; this.objectMapper = objectMapper; } @Override protected StsOperationEntity processItem(StsOperationDto dto) throws Exception { - log.debug("선박 상세 정보 처리 시작: imoNumber={}, facilityName={}", + log.debug("StsOperations 정보 처리 시작: imoNumber={}, facilityName={}", dto.getImolRorIHSNumber(), dto.getFacilityName()); JsonNode positionNode = null; @@ -61,6 +56,8 @@ public class StsOperationProcessor extends BaseProcessor { private final ObjectMapper objectMapper; + private final Long jobExecutionId; - public TerminalCallsProcessor(ObjectMapper objectMapper) { + public TerminalCallsProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, + ObjectMapper objectMapper) + { + this.jobExecutionId = jobExecutionId; this.objectMapper = objectMapper; } @Override protected TerminalCallsEntity processItem(TerminalCallsDto dto) throws Exception { - log.debug("선박 상세 정보 처리 시작: imoNumber={}, facilityName={}", + log.debug("TerminalCalls 정보 처리 시작: imoNumber={}, facilityName={}", dto.getImolRorIHSNumber(), dto.getFacilityName()); JsonNode positionNode = null; @@ -63,6 +59,8 @@ public class TerminalCallsProcessor extends BaseProcessor { -// private final ObjectMapper objectMapper; + private final Long jobExecutionId; -// public TransitsProcessor(ObjectMapper objectMapper) { -// this.objectMapper = objectMapper; -// } + public TransitsProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) + { + this.jobExecutionId = jobExecutionId; + } @Override protected TransitsEntity processItem(TransitsDto dto) throws Exception { - log.debug("선박 상세 정보 처리 시작: imoNumber={}, facilityName={}", + log.debug("Transits 정보 처리 시작: imoNumber={}, facilityName={}", dto.getImolRorIHSNumber(), dto.getFacilityName()); TransitsEntity entity = TransitsEntity.builder() @@ -40,6 +34,8 @@ public class TransitsProcessor extends BaseProcessor @@ -30,8 +24,7 @@ public class AnchorageCallsRepositoryImpl extends BaseJdbcRepository entities) { if (entities == null || entities.isEmpty()) return; -// log.info("ShipMovement 저장 시작 = {}건", entities.size()); batchInsert(entities); } - - - /** - * ShipDetailEntity RowMapper - */ - private static class AnchorageCallsRowMapper implements RowMapper { - @Override - public AnchorageCallsEntity mapRow(ResultSet rs, int rowNum) throws SQLException { - AnchorageCallsEntity entity = AnchorageCallsEntity.builder() - .id(rs.getLong("id")) - .imolRorIHSNumber(rs.getString("imolRorIHSNumber")) - .portCallId(rs.getObject("portCallId", Integer.class)) - .facilityId(rs.getObject("facilityId", Integer.class)) - .facilityName(rs.getString("facilityName")) - .facilityType(rs.getString("facilityType")) - .subFacilityId(rs.getObject("subFacilityId", Integer.class)) - .subFacilityName(rs.getString("subFacilityName")) - .subFacilityType(rs.getString("subFacilityType")) - .countryCode(rs.getString("countryCode")) - .countryName(rs.getString("countryName")) - .draught(rs.getObject("draught", Double.class)) - .latitude(rs.getObject("latitude", Double.class)) - .longitude(rs.getObject("longitude", Double.class)) - .destination(rs.getString("destination")) - .iso2(rs.getString("iso2")) - .position(parseJson(rs.getString("position"))) - .build(); - - Timestamp movementDate = rs.getTimestamp("movementDate"); - if (movementDate != null) { - entity.setMovementDate(movementDate.toLocalDateTime()); - } - - return entity; - } - - private JsonNode parseJson(String json) { - try { - if (json == null) return null; - return new ObjectMapper().readTree(json); - } catch (Exception e) { - throw new RuntimeException("JSON 파싱 오류: " + json); - } - } - } } diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/repository/BerthCallsRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/movement/batch/repository/BerthCallsRepositoryImpl.java index 5a4642a..b85fdbe 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/repository/BerthCallsRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/repository/BerthCallsRepositoryImpl.java @@ -1,6 +1,5 @@ package com.snp.batch.jobs.movement.batch.repository; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.repository.BaseJdbcRepository; import com.snp.batch.jobs.movement.batch.entity.BerthCallsEntity; @@ -10,15 +9,10 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Timestamp; +import java.sql.Types; import java.util.List; -/** - * 선박 상세 정보 Repository 구현체 - * BaseJdbcRepository를 상속하여 JDBC 기반 CRUD 구현 - */ @Slf4j @Repository("BerthCallsRepository") public class BerthCallsRepositoryImpl extends BaseJdbcRepository @@ -30,8 +24,7 @@ public class BerthCallsRepositoryImpl extends BaseJdbcRepository entities) { if (entities == null || entities.isEmpty()) return; -// log.info("BerthCalls 저장 시작 = {}건", entities.size()); batchInsert(entities); } - - - /** - * ShipDetailEntity RowMapper - */ - private static class BerthCallsRowMapper implements RowMapper { - @Override - public BerthCallsEntity mapRow(ResultSet rs, int rowNum) throws SQLException { - BerthCallsEntity entity = BerthCallsEntity.builder() - .id(rs.getLong("id")) - .imolRorIHSNumber(rs.getString("imolRorIHSNumber")) - .facilityId(rs.getObject("facilityId", Integer.class)) - .facilityName(rs.getString("facilityName")) - .facilityType(rs.getString("facilityType")) - .countryCode(rs.getString("countryCode")) - .countryName(rs.getString("countryName")) - .draught(rs.getObject("draught", Double.class)) - .latitude(rs.getObject("latitude", Double.class)) - .longitude(rs.getObject("longitude", Double.class)) - .position(parseJson(rs.getString("position"))) - .build(); - - Timestamp movementDate = rs.getTimestamp("movementDate"); - if (movementDate != null) { - entity.setMovementDate(movementDate.toLocalDateTime()); - } - - return entity; - } - - private JsonNode parseJson(String json) { - try { - if (json == null) return null; - return new ObjectMapper().readTree(json); - } catch (Exception e) { - throw new RuntimeException("JSON 파싱 오류: " + json); - } - } - } } diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/repository/CurrentlyAtRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/movement/batch/repository/CurrentlyAtRepositoryImpl.java index 5409872..01040ac 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/repository/CurrentlyAtRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/repository/CurrentlyAtRepositoryImpl.java @@ -10,12 +10,9 @@ import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; import java.sql.Timestamp; +import java.sql.Types; import java.util.List; -/** - * 선박 상세 정보 Repository 구현체 - * BaseJdbcRepository를 상속하여 JDBC 기반 CRUD 구현 - */ @Slf4j @Repository("CurrentlyAtRepository") public class CurrentlyAtRepositoryImpl extends BaseJdbcRepository @@ -27,8 +24,7 @@ public class CurrentlyAtRepositoryImpl extends BaseJdbcRepository entities) { if (entities == null || entities.isEmpty()) return; -// log.info("CurrentltAt 저장 시작 = {}건", entities.size()); batchInsert(entities); } diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/repository/DestinationRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/movement/batch/repository/DestinationRepositoryImpl.java index 3d3257e..8aae798 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/repository/DestinationRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/repository/DestinationRepositoryImpl.java @@ -10,12 +10,9 @@ import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; import java.sql.Timestamp; +import java.sql.Types; import java.util.List; -/** - * 선박 상세 정보 Repository 구현체 - * BaseJdbcRepository를 상속하여 JDBC 기반 CRUD 구현 - */ @Slf4j @Repository("DestinationRepository") public class DestinationRepositoryImpl extends BaseJdbcRepository @@ -27,8 +24,7 @@ public class DestinationRepositoryImpl extends BaseJdbcRepository entities); - boolean existsByPortCallId(Integer portCallId); } diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/repository/PortCallsRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/movement/batch/repository/PortCallsRepositoryImpl.java index 81b720b..c8f21ae 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/repository/PortCallsRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/repository/PortCallsRepositoryImpl.java @@ -1,6 +1,5 @@ package com.snp.batch.jobs.movement.batch.repository; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.repository.BaseJdbcRepository; import com.snp.batch.jobs.movement.batch.entity.PortCallsEntity; @@ -10,15 +9,10 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Timestamp; +import java.sql.Types; import java.util.List; -/** - * 선박 상세 정보 Repository 구현체 - * BaseJdbcRepository를 상속하여 JDBC 기반 CRUD 구현 - */ @Slf4j @Repository("ShipMovementRepository") public class PortCallsRepositoryImpl extends BaseJdbcRepository @@ -30,8 +24,7 @@ public class PortCallsRepositoryImpl extends BaseJdbcRepository getRowMapper() { - return new ShipMovementRowMapper(); + return null; } @Override @@ -190,65 +133,4 @@ public class PortCallsRepositoryImpl extends BaseJdbcRepository 0; - } - - - /** - * ShipDetailEntity RowMapper - */ - private static class ShipMovementRowMapper implements RowMapper { - @Override - public PortCallsEntity mapRow(ResultSet rs, int rowNum) throws SQLException { - PortCallsEntity entity = PortCallsEntity.builder() - .id(rs.getLong("id")) - .imolRorIHSNumber(rs.getString("imolRorIHSNumber")) - .portCallId(rs.getObject("portCallId", Integer.class)) - .facilityId(rs.getObject("facilityId", Integer.class)) - .facilityName(rs.getString("facilityName")) - .facilityType(rs.getString("facilityType")) - .subFacilityId(rs.getObject("subFacilityId", Integer.class)) - .subFacilityName(rs.getString("subFacilityName")) - .subFacilityType(rs.getString("subFacilityType")) - .parentFacilityId(rs.getObject("parentFacilityId", Integer.class)) - .parentFacilityName(rs.getString("parentFacilityName")) - .parentFacilityType(rs.getString("parentFacilityType")) - .countryCode(rs.getString("countryCode")) - .countryName(rs.getString("countryName")) - .draught(rs.getObject("draught", Double.class)) - .latitude(rs.getObject("latitude", Double.class)) - .longitude(rs.getObject("longitude", Double.class)) - .destination(rs.getString("destination")) - .iso2(rs.getString("iso2")) - .position(parseJson(rs.getString("position"))) - .schemaType(rs.getString("schemaType")) - .build(); - - Timestamp movementDate = rs.getTimestamp("movementDate"); - if (movementDate != null) { - entity.setMovementDate(movementDate.toLocalDateTime()); - } - - return entity; - } - - private JsonNode parseJson(String json) { - try { - if (json == null) return null; - return new com.fasterxml.jackson.databind.ObjectMapper().readTree(json); - } catch (Exception e) { - throw new RuntimeException("JSON 파싱 오류: " + json); - } - } - } } diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/repository/StsOperationRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/movement/batch/repository/StsOperationRepositoryImpl.java index 09f6325..288465f 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/repository/StsOperationRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/repository/StsOperationRepositoryImpl.java @@ -10,12 +10,9 @@ import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; import java.sql.Timestamp; +import java.sql.Types; import java.util.List; -/** - * 선박 상세 정보 Repository 구현체 - * BaseJdbcRepository를 상속하여 JDBC 기반 CRUD 구현 - */ @Slf4j @Repository("StsOperationRepository") public class StsOperationRepositoryImpl extends BaseJdbcRepository @@ -27,8 +24,7 @@ public class StsOperationRepositoryImpl extends BaseJdbcRepository @@ -27,8 +24,7 @@ public class TerminalCallsRepositoryImpl extends BaseJdbcRepository entities) { if (entities == null || entities.isEmpty()) return; -// log.info("TerminallCalls 저장 시작 = {}건", entities.size()); batchInsert(entities); } diff --git a/src/main/java/com/snp/batch/jobs/movement/batch/repository/TransitsRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/movement/batch/repository/TransitsRepositoryImpl.java index 5e9a738..15ec899 100644 --- a/src/main/java/com/snp/batch/jobs/movement/batch/repository/TransitsRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/movement/batch/repository/TransitsRepositoryImpl.java @@ -10,12 +10,9 @@ import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; import java.sql.Timestamp; +import java.sql.Types; import java.util.List; -/** - * 선박 상세 정보 Repository 구현체 - * BaseJdbcRepository를 상속하여 JDBC 기반 CRUD 구현 - */ @Slf4j @Repository("TransitsRepository") public class TransitsRepositoryImpl extends BaseJdbcRepository @@ -27,8 +24,7 @@ public class TransitsRepositoryImpl extends BaseJdbcRepository { - private final PscInspectionProcessor pscInspectionProcessor; private final PscInspectionWriter pscInspectionWriter; private final PscApiReader pscApiReader; @@ -44,7 +45,7 @@ public class PscInspectionJobConfig extends BaseMultiStepJobConfig createWriter() { // 타입 변경 return pscInspectionWriter; diff --git a/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscAllCertificateEntity.java b/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscAllCertificateEntity.java index f750520..7f7cd96 100644 --- a/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscAllCertificateEntity.java +++ b/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscAllCertificateEntity.java @@ -1,7 +1,9 @@ package com.snp.batch.jobs.pscInspection.batch.entity; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -11,7 +13,8 @@ import java.time.LocalDateTime; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class PscAllCertificateEntity { +@EqualsAndHashCode(callSuper = true) +public class PscAllCertificateEntity extends BaseEntity { private String certificateId; diff --git a/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscDefectEntity.java b/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscDefectEntity.java index e84278e..2eb74ba 100644 --- a/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscDefectEntity.java +++ b/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscDefectEntity.java @@ -1,7 +1,9 @@ package com.snp.batch.jobs.pscInspection.batch.entity; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -9,7 +11,8 @@ import lombok.experimental.SuperBuilder; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class PscDefectEntity { +@EqualsAndHashCode(callSuper = true) +public class PscDefectEntity extends BaseEntity { private String defectId; diff --git a/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscInspectionEntity.java b/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscInspectionEntity.java index 040f3ae..d0ac126 100644 --- a/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscInspectionEntity.java +++ b/src/main/java/com/snp/batch/jobs/pscInspection/batch/entity/PscInspectionEntity.java @@ -1,10 +1,9 @@ package com.snp.batch.jobs.pscInspection.batch.entity; -import com.snp.batch.jobs.pscInspection.batch.dto.PscAllCertificateDto; -import com.snp.batch.jobs.pscInspection.batch.dto.PscCertificateDto; -import com.snp.batch.jobs.pscInspection.batch.dto.PscDefectDto; +import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -16,7 +15,8 @@ import java.util.List; @SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class PscInspectionEntity { +@EqualsAndHashCode(callSuper = true) +public class PscInspectionEntity extends BaseEntity { private String typeId; private String dataSetVersion; diff --git a/src/main/java/com/snp/batch/jobs/pscInspection/batch/processor/PscInspectionProcessor.java b/src/main/java/com/snp/batch/jobs/pscInspection/batch/processor/PscInspectionProcessor.java index a47362d..f451818 100644 --- a/src/main/java/com/snp/batch/jobs/pscInspection/batch/processor/PscInspectionProcessor.java +++ b/src/main/java/com/snp/batch/jobs/pscInspection/batch/processor/PscInspectionProcessor.java @@ -1,12 +1,17 @@ package com.snp.batch.jobs.pscInspection.batch.processor; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.processor.BaseProcessor; -import com.snp.batch.jobs.pscInspection.batch.dto.*; +import com.snp.batch.jobs.pscInspection.batch.dto.PscAllCertificateDto; +import com.snp.batch.jobs.pscInspection.batch.dto.PscCertificateDto; +import com.snp.batch.jobs.pscInspection.batch.dto.PscDefectDto; +import com.snp.batch.jobs.pscInspection.batch.dto.PscInspectionDto; import com.snp.batch.jobs.pscInspection.batch.entity.PscAllCertificateEntity; import com.snp.batch.jobs.pscInspection.batch.entity.PscCertificateEntity; import com.snp.batch.jobs.pscInspection.batch.entity.PscDefectEntity; import com.snp.batch.jobs.pscInspection.batch.entity.PscInspectionEntity; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.math.BigDecimal; @@ -15,17 +20,21 @@ import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import static jakarta.xml.bind.DatatypeConverter.parseDateTime; - @Slf4j @Component public class PscInspectionProcessor extends BaseProcessor { - + private static Long jobExecutionId; + private final ObjectMapper objectMapper; + public PscInspectionProcessor( + ObjectMapper objectMapper, + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + this.objectMapper = objectMapper; + this.jobExecutionId = jobExecutionId; + } @Override public PscInspectionEntity processItem(PscInspectionDto item) throws Exception { @@ -71,6 +80,8 @@ public class PscInspectionProcessor extends BaseProcessor createProcessor() { return riskDataProcessor; } + @Bean + @StepScope + public RiskDataProcessor riskDataProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + return new RiskDataProcessor(jobExecutionId); + } @Override protected ItemWriter createWriter() { return riskDataWriter; } diff --git a/src/main/java/com/snp/batch/jobs/risk/batch/processor/RiskDataProcessor.java b/src/main/java/com/snp/batch/jobs/risk/batch/processor/RiskDataProcessor.java index 3f96d9b..ca2a293 100644 --- a/src/main/java/com/snp/batch/jobs/risk/batch/processor/RiskDataProcessor.java +++ b/src/main/java/com/snp/batch/jobs/risk/batch/processor/RiskDataProcessor.java @@ -4,11 +4,16 @@ import com.snp.batch.common.batch.processor.BaseProcessor; import com.snp.batch.jobs.risk.batch.dto.RiskDto; import com.snp.batch.jobs.risk.batch.entity.RiskEntity; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Slf4j @Component public class RiskDataProcessor extends BaseProcessor { + private final Long jobExecutionId; + public RiskDataProcessor(@Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + this.jobExecutionId = jobExecutionId; + } @Override protected RiskEntity processItem(RiskDto dto) throws Exception { log.debug("Risk 데이터 처리 시작: imoNumber={}", dto.getLrno()); @@ -113,6 +118,8 @@ public class RiskDataProcessor extends BaseProcessor { .russianOwnerRegistrationNarrative(dto.getRussianOwnerRegistrationNarrative()) .russianSTS(dto.getRussianSTS()) .russianSTSNarrative(dto.getRussianSTSNarrative()) + .jobExecutionId(jobExecutionId) + .createdBy("SYSTEM") .build(); log.debug("Risk 데이터 처리 완료: imoNumber={}", dto.getLrno()); diff --git a/src/main/java/com/snp/batch/jobs/risk/batch/repository/RiskRepository.java b/src/main/java/com/snp/batch/jobs/risk/batch/repository/RiskRepository.java index aea972b..21e3d0c 100644 --- a/src/main/java/com/snp/batch/jobs/risk/batch/repository/RiskRepository.java +++ b/src/main/java/com/snp/batch/jobs/risk/batch/repository/RiskRepository.java @@ -6,5 +6,5 @@ import java.util.List; public interface RiskRepository { void saveRiskAll(List items); - void saveRiskHistoryAll(List items); +// void saveRiskHistoryAll(List items); } diff --git a/src/main/java/com/snp/batch/jobs/risk/batch/repository/RiskRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/risk/batch/repository/RiskRepositoryImpl.java index 3818507..58f3637 100644 --- a/src/main/java/com/snp/batch/jobs/risk/batch/repository/RiskRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/risk/batch/repository/RiskRepositoryImpl.java @@ -8,6 +8,7 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; +import java.sql.Types; import java.util.List; @Slf4j @@ -20,7 +21,7 @@ public class RiskRepositoryImpl extends BaseJdbcRepository imp @Override protected String getTableName() { - return null; + return "t_snp_data.risk"; } @Override @@ -40,12 +41,8 @@ public class RiskRepositoryImpl extends BaseJdbcRepository imp @Override protected String getUpdateSql() { - return null; - } - - protected String getUpdateSql(String targetTable, String targetIndex) { return """ - INSERT INTO new_snp.%s ( + INSERT INTO %s( lrno, lastupdated, riskdatamaintained, dayssincelastseenonais, daysunderais, imocorrectonais, sailingundername, anomalousmessagesfrommmsi, mostrecentdarkactivity, portcalls, portrisk, stsoperations, @@ -54,7 +51,8 @@ public class RiskRepositoryImpl extends BaseJdbcRepository imp pscdetentions, currentsmccertificate, docchanges, currentclass, classstatuschanges, pandicoverage, namechanges, gbochanges, ageofship, iuufishingviolation, draughtchanges, mostrecentsanctionedportcall, singleshipoperation, fleetsafety, fleetpsc, - specialsurveyoverdue, ownerunknown, russianportcall, russianownerregistration, russiansts + specialsurveyoverdue, ownerunknown, russianportcall, russianownerregistration, russiansts, + job_execution_id, created_by ) VALUES ( ?, ?::timestamptz, @@ -65,51 +63,10 @@ public class RiskRepositoryImpl extends BaseJdbcRepository imp ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ?, ?, ? - ) - ON CONFLICT (%s) - DO UPDATE SET - riskdatamaintained = EXCLUDED.riskdatamaintained, - dayssincelastseenonais = EXCLUDED.dayssincelastseenonais, - daysunderais = EXCLUDED.daysunderais, - imocorrectonais = EXCLUDED.imocorrectonais, - sailingundername = EXCLUDED.sailingundername, - anomalousmessagesfrommmsi = EXCLUDED.anomalousmessagesfrommmsi, - mostrecentdarkactivity = EXCLUDED.mostrecentdarkactivity, - portcalls = EXCLUDED.portcalls, - portrisk = EXCLUDED.portrisk, - stsoperations = EXCLUDED.stsoperations, - driftinghighseas = EXCLUDED.driftinghighseas, - riskevents = EXCLUDED.riskevents, - flagchanges = EXCLUDED.flagchanges, - flagparismouperformance = EXCLUDED.flagparismouperformance, - flagtokyomoupeformance = EXCLUDED.flagtokyomoupeformance, - flaguscgmouperformance = EXCLUDED.flaguscgmouperformance, - uscgqualship21 = EXCLUDED.uscgqualship21, - timesincepscinspection = EXCLUDED.timesincepscinspection, - pscinspections = EXCLUDED.pscinspections, - pscdefects = EXCLUDED.pscdefects, - pscdetentions = EXCLUDED.pscdetentions, - currentsmccertificate = EXCLUDED.currentsmccertificate, - docchanges = EXCLUDED.docchanges, - currentclass = EXCLUDED.currentclass, - classstatuschanges = EXCLUDED.classstatuschanges, - pandicoverage = EXCLUDED.pandicoverage, - namechanges = EXCLUDED.namechanges, - gbochanges = EXCLUDED.gbochanges, - ageofship = EXCLUDED.ageofship, - iuufishingviolation = EXCLUDED.iuufishingviolation, - draughtchanges = EXCLUDED.draughtchanges, - mostrecentsanctionedportcall = EXCLUDED.mostrecentsanctionedportcall, - singleshipoperation = EXCLUDED.singleshipoperation, - fleetsafety = EXCLUDED.fleetsafety, - fleetpsc = EXCLUDED.fleetpsc, - specialsurveyoverdue = EXCLUDED.specialsurveyoverdue, - ownerunknown = EXCLUDED.ownerunknown, - russianportcall = EXCLUDED.russianportcall, - russianownerregistration = EXCLUDED.russianownerregistration, - russiansts = EXCLUDED.russiansts - """.formatted(targetTable, targetIndex); + ?, ?, ?, ?, ?, + ?, ? + ); + """.formatted(getTableName()); } @Override @@ -162,6 +119,8 @@ public class RiskRepositoryImpl extends BaseJdbcRepository imp ps.setObject(idx++, entity.getRussianPortCall(), java.sql.Types.INTEGER); ps.setObject(idx++, entity.getRussianOwnerRegistration(), java.sql.Types.INTEGER); ps.setObject(idx++, entity.getRussianSTS(), java.sql.Types.INTEGER); + ps.setObject(idx++, entity.getJobExecutionId(), Types.INTEGER); + ps.setString(idx++, entity.getCreatedBy()); } @Override @@ -174,7 +133,7 @@ public class RiskRepositoryImpl extends BaseJdbcRepository imp if (items == null || items.isEmpty()) { return; } - jdbcTemplate.batchUpdate(getUpdateSql("risk", "lrno"), items, items.size(), + jdbcTemplate.batchUpdate(getUpdateSql(), items, items.size(), (ps, entity) -> { try { setUpdateParameters(ps, entity); @@ -187,21 +146,4 @@ public class RiskRepositoryImpl extends BaseJdbcRepository imp log.info("{} 전체 저장 완료: 수정={} 건", getEntityName(), items.size()); } - @Override - public void saveRiskHistoryAll(List items) { - if (items == null || items.isEmpty()) { - return; - } - jdbcTemplate.batchUpdate(getUpdateSql("risk_history", "lrno, lastupdated"), items, items.size(), - (ps, entity) -> { - try { - setUpdateParameters(ps, entity); - } catch (Exception e) { - log.error("배치 수정 파라미터 설정 실패", e); - throw new RuntimeException(e); - } - }); - - log.info("{} 전체 저장 완료: 수정={} 건", getEntityName(), items.size()); - } } diff --git a/src/main/java/com/snp/batch/jobs/risk/batch/writer/RiskDataWriter.java b/src/main/java/com/snp/batch/jobs/risk/batch/writer/RiskDataWriter.java index 05ba743..658e468 100644 --- a/src/main/java/com/snp/batch/jobs/risk/batch/writer/RiskDataWriter.java +++ b/src/main/java/com/snp/batch/jobs/risk/batch/writer/RiskDataWriter.java @@ -19,6 +19,6 @@ public class RiskDataWriter extends BaseWriter { @Override protected void writeItems(List items) throws Exception { riskRepository.saveRiskAll(items); - riskRepository.saveRiskHistoryAll(items); +// riskRepository.saveRiskHistoryAll(items); } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/config/ShipDetailImportJobConfig.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/config/ShipDetailImportJobConfig.java index 3e625a3..42db89b 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/config/ShipDetailImportJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/config/ShipDetailImportJobConfig.java @@ -1,8 +1,9 @@ package com.snp.batch.jobs.shipdetail.batch.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseJobConfig; -import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailComparisonData; // Reader 출력 타입 -import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailUpdate; // Processor 출력 타입 +import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailDto; +import com.snp.batch.jobs.shipdetail.batch.entity.ShipDetailEntity; import com.snp.batch.jobs.shipdetail.batch.processor.ShipDetailDataProcessor; import com.snp.batch.jobs.shipdetail.batch.reader.ShipDetailDataReader; import com.snp.batch.jobs.shipdetail.batch.writer.ShipDetailDataWriter; @@ -19,7 +20,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.web.reactive.function.client.WebClient; -import com.fasterxml.jackson.databind.ObjectMapper; // ObjectMapper 추가 /** * 선박 상세 정보 Import Job Config @@ -27,8 +27,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; // ObjectMapper 추가 * 특징: * - ship_data 테이블에서 IMO 번호 조회 * - IMO 번호를 100개씩 배치로 분할 - * - Maritime API GetShipsByIHSLRorIMONumbers 호출 - * TODO : GetShipsByIHSLRorIMONumbersAll 호출로 변경 + * - Maritime API GetShipsByIHSLRorIMONumbersAll 호출 * - 선박 상세 정보를 ship_detail 테이블에 저장 (UPSERT) * * 데이터 흐름: @@ -41,12 +40,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; // ObjectMapper 추가 */ /** * 선박 상세 정보 Import Job Config - * I: ShipDetailComparisonData (Reader 출력) - * O: ShipDetailUpdate (Processor 출력) + * I: ShipDetailDto (Reader 출력) + * O: ShipDetailEntity (Processor 출력) */ @Slf4j @Configuration -public class ShipDetailImportJobConfig extends BaseJobConfig { +public class ShipDetailImportJobConfig extends BaseJobConfig { private final ShipDetailDataProcessor shipDetailDataProcessor; private final ShipDetailDataWriter shipDetailDataWriter; @@ -81,18 +80,17 @@ public class ShipDetailImportJobConfig extends BaseJobConfig createReader() { // 타입 변경 - // Reader 생성자 수정: ObjectMapper를 전달합니다. + protected ItemReader createReader() { // 타입 변경 return new ShipDetailDataReader(maritimeApiWebClient, jdbcTemplate, objectMapper); } @Override - protected ItemProcessor createProcessor() { + protected ItemProcessor createProcessor() { return shipDetailDataProcessor; } @Override - protected ItemWriter createWriter() { // 타입 변경 + protected ItemWriter createWriter() { // 타입 변경 return shipDetailDataWriter; } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/config/ShipDetailUpdateJobConfig.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/config/ShipDetailUpdateJobConfig.java index 6cad53b..4156866 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/config/ShipDetailUpdateJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/config/ShipDetailUpdateJobConfig.java @@ -2,8 +2,8 @@ package com.snp.batch.jobs.shipdetail.batch.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; -import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailComparisonData; -import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailUpdate; +import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailDto; +import com.snp.batch.jobs.shipdetail.batch.entity.ShipDetailEntity; import com.snp.batch.jobs.shipdetail.batch.processor.ShipDetailDataProcessor; import com.snp.batch.jobs.shipdetail.batch.reader.ShipDetailUpdateDataReader; import com.snp.batch.jobs.shipdetail.batch.writer.ShipDetailDataWriter; @@ -31,7 +31,7 @@ import org.springframework.web.reactive.function.client.WebClient; @Slf4j @Configuration -public class ShipDetailUpdateJobConfig extends BaseMultiStepJobConfig { +public class ShipDetailUpdateJobConfig extends BaseMultiStepJobConfig { private final ShipDetailDataProcessor shipDetailDataProcessor; private final ShipDetailDataWriter shipDetailDataWriter; @@ -46,7 +46,7 @@ public class ShipDetailUpdateJobConfig extends BaseMultiStepJobConfig createReader() { // 타입 변경 + protected ItemReader createReader() { // 타입 변경 return shipDetailUpdateDataReader; } @Override - protected ItemProcessor createProcessor() { + protected ItemProcessor createProcessor() { return shipDetailDataProcessor; } + @Bean + @StepScope + public ShipDetailDataProcessor shipDetailDataProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId + ) { + return new ShipDetailDataProcessor(jobExecutionId); + } @Override - protected ItemWriter createWriter() { // 타입 변경 + protected ItemWriter createWriter() { // 타입 변경 return shipDetailDataWriter; } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/AdditionalInformationDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/AdditionalInformationDto.java index 9afd0ec..ffb07d6 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/AdditionalInformationDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/AdditionalInformationDto.java @@ -2,6 +2,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.AdditionalInformationEntity; import lombok.*; @Getter @@ -44,4 +45,22 @@ public class AdditionalInformationDto { private String satComID; @JsonProperty("SatComAnsBack") private String satComAnsBack; + + public AdditionalInformationEntity toEntity() { + return AdditionalInformationEntity.builder() + .lrno(this.lrno) + .shipemail(this.shipEmail) + .waterdepthmax(this.waterDepthMax) + .drilldepthmax(this.drillDepthMax) + .drillbargeind(this.drillBargeInd) + .productionvesselind(this.productionVesselInd) + .deckheatexchangerind(this.deckHeatExchangerInd) + .deckheatexchangermaterial(this.deckHeatExchangerMaterial) + .tweendeckportable(this.tweenDeckPortable) + .tweendeckfixed(this.tweenDeckFixed) + .satcomid(this.satComID) + .satcomansback(this.satComAnsBack) + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getVersion() : null) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/BareBoatCharterHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/BareBoatCharterHistoryDto.java index 9c8449c..10c2d1f 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/BareBoatCharterHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/BareBoatCharterHistoryDto.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.BareBoatCharterHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -27,4 +28,15 @@ public class BareBoatCharterHistoryDto { private String bbChartererCode; @JsonProperty("BBCharterer") private String bbCharterer; + + public BareBoatCharterHistoryEntity toEntity() { + return BareBoatCharterHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .sequence(this.sequence) + .effectiveDate(this.effectiveDate) + .bbChartererCode(this.bbChartererCode) + .bbCharterer(this.bbCharterer) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CallSignAndMmsiHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CallSignAndMmsiHistoryDto.java index 38153e7..861dc46 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CallSignAndMmsiHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CallSignAndMmsiHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.CallSignAndMmsiHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -28,5 +29,15 @@ public class CallSignAndMmsiHistoryDto { private String mmsi; @JsonProperty("EffectiveDate") private String effectiveDate; - // MMSI는 JSON에 없으므로 DTO에 포함하지 않음. Entity에서 처리. + + public CallSignAndMmsiHistoryEntity toEntity() { + return CallSignAndMmsiHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .sequence(this.seqNo) // SeqNo -> sequence 매핑 + .callsign(this.callSign) + .mmsi(this.mmsi) // DTO에 정의된 mmsi 필드 사용 + .effectiveDate(this.effectiveDate) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ClassHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ClassHistoryDto.java index 75a07be..73630e6 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ClassHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ClassHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.ClassHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -34,4 +35,18 @@ public class ClassHistoryDto { private String lrno; @JsonProperty("Sequence") private String sequence; + + public ClassHistoryEntity toEntity() { + return ClassHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + ._class(this._class) + .classCode(this.classCode) + .classIndicator(this.classIndicator) + .classID(this.classID) + .currentIndicator(this.currentIndicator) + .effectiveDate(this.effectiveDate) + .lrno(this.lrno) + .sequence(this.sequence) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CompanyDetailDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CompanyDetailDto.java index 17f8354..bfd9402 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CompanyDetailDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CompanyDetailDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.CompanyDetailEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -79,4 +80,39 @@ public class CompanyDetailDto { @JsonProperty("DataSetVersion") private String dataSetVersion; } + + public CompanyDetailEntity toEntity() { + return CompanyDetailEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .owcode(this.owcode) + .shortcompanyname(this.shortCompanyName) + .countryname(this.countryName) + .townname(this.townName) + .telephone(this.telephone) + .telex(this.telex) + .emailaddress(this.emailaddress) + .website(this.website) + .fullname(this.fullName) + .careofcode(this.careOfCode) + .roomfloorbuilding1(this.roomFloorBuilding1) + .roomfloorbuilding2(this.roomFloorBuilding2) + .roomfloorbuilding3(this.roomFloorBuilding3) + .pobox(this.poBox) + .streetnumber(this.streetNumber) + .street(this.street) + .prepostcode(this.prePostcode) + .postpostcode(this.postPostcode) + .nationalityofregistration(this.nationalityofRegistration) + .nationalityofcontrol(this.nationalityofControl) + .locationcode(this.locationCode) + .nationalityofregistrationcode(this.nationalityofRegistrationCode) + .nationalityofcontrolcode(this.nationalityofControlCode) + .lastchangedate(this.lastChangeDate) + .parentcompany(this.parentCompany) + .companystatus(this.companyStatus) + .fulladdress(this.fullAddress) + .facsimile(this.facsimile) + .foundeddate(this.foundedDate) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CompanyVesselRelationshipDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CompanyVesselRelationshipDto.java index c9369d7..3dace6e 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CompanyVesselRelationshipDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CompanyVesselRelationshipDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.CompanyVesselRelationshipEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -61,4 +62,31 @@ public class CompanyVesselRelationshipDto { private String technicalManagerGroup; @JsonProperty("TechnicalManagerGroupCode") private String technicalManagerGroupCode; + + public CompanyVesselRelationshipEntity toEntity() { + return CompanyVesselRelationshipEntity.builder() + .datasetversion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .doccode(this.docCode) + .doccompany(this.docCompany) + .docgroup(this.docGroup) + .docgroupcode(this.docGroupCode) + .groupbeneficialowner(this.groupBeneficialOwner) + .groupbeneficialownercode(this.groupBeneficialOwnerCode) + .lrno(this.lrno) + .operator(this.operator) + .operatorcode(this.operatorCode) + .operatorgroup(this.operatorGroup) + .operatorgroupcode(this.operatorGroupCode) + .registeredowner(this.registeredOwner) + .registeredownercode(this.registeredOwnerCode) + .shipmanager(this.shipManager) + .shipmanagercode(this.shipManagerCode) + .shipmanagergroup(this.shipManagerGroup) + .shipmanagergroupcode(this.shipManagerGroupCode) + .technicalmanager(this.technicalManager) + .technicalmanagercode(this.technicalManagerCode) + .technicalmanagergroup(this.technicalManagerGroup) + .technicalmanagergroupcode(this.technicalManagerGroupCode) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CrewListDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CrewListDto.java index 89bf1b3..6a7ec75 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CrewListDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/CrewListDto.java @@ -2,6 +2,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.CrewListEntity; import lombok.*; @Data @@ -58,4 +59,22 @@ public class CrewListDto { @JsonProperty("TotalUndeclared") private String totalUndeclared; + + public CrewListEntity toEntity() { + return CrewListEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .id(this.id) + .lrno(this.lrno) + .shipname(this.shipname) + .crewlistdate(this.crewListDate) + .nationality(this.nationality) + .totalcrew(this.totalCrew) + .totalratings(this.totalRatings) + .totalofficers(this.totalOfficers) + .totalcadets(this.totalCadets) + .totaltrainees(this.totalTrainees) + .totalridingsquad(this.totalRidingSquad) + .totalundeclared(this.totalUndeclared) + .build(); + } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/DarkActivityConfirmedDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/DarkActivityConfirmedDto.java index 918374d..b868ccc 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/DarkActivityConfirmedDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/DarkActivityConfirmedDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.DarkActivityConfirmedEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -45,4 +46,36 @@ public class DarkActivityConfirmedDto { @JsonProperty("Last_Port_of_Call") private String lastPortOfCall; @JsonProperty("Last_Port_Country_Code") private String lastPortCountryCode; @JsonProperty("Last_Port_Country") private String lastPortCountry; + + public DarkActivityConfirmedEntity toEntity() { + return DarkActivityConfirmedEntity.builder() + .datasetversion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .mmsi(this.mmsi) + .vessel_name(this.vesselName) + .dark_hours(this.darkHours) + .dark_activity(this.darkActivity) + .dark_status(this.darkStatus) + .area_id(this.areaId) + .area_name(this.areaName) + .area_country(this.areaCountry) + .dark_time(this.darkTime) + .dark_latitude(this.darkLatitude) + .dark_longitude(this.darkLongitude) + .dark_speed(this.darkSpeed) + .dark_heading(this.darkHeading) + .dark_draught(this.darkDraught) + .nextseen(this.nextSeen) + .nextseen_latitude(this.nextSeenLatitude) + .nextseen_longitude(this.nextSeenLongitude) + .nextseen_speed(this.nextSeenSpeed) + .nextseen_draught(this.nextSeenDraught) + .nextseen_heading(this.nextSeenHeading) + .dark_reported_destination(this.darkReportedDestination) + .nextseen_reported_destination(this.nextSeenReportedDestination) + .last_port_of_call(this.lastPortOfCall) + .last_port_country_code(this.lastPortCountryCode) + .last_port_country(this.lastPortCountry) + .build(); + } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/FlagHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/FlagHistoryDto.java index ae6b5f6..e27f484 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/FlagHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/FlagHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.FlagHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -28,4 +29,15 @@ public class FlagHistoryDto { private String lrno; @JsonProperty("Sequence") private String sequence; + + public FlagHistoryEntity toEntity() { + return FlagHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .effectiveDate(this.effectiveDate) + .flag(this.flag) + .flagCode(this.flagCode) + .lrno(this.lrno) + .sequence(this.sequence) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/GroupBeneficialOwnerHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/GroupBeneficialOwnerHistoryDto.java index e27a2d1..34a04fa 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/GroupBeneficialOwnerHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/GroupBeneficialOwnerHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.GroupBeneficialOwnerHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -12,7 +13,6 @@ import lombok.ToString; @NoArgsConstructor public class GroupBeneficialOwnerHistoryDto { - // Nested class for DataSetVersion object @Getter @Setter @ToString @@ -42,4 +42,16 @@ public class GroupBeneficialOwnerHistoryDto { @JsonProperty("Sequence") private String sequence; // 순번 + + public GroupBeneficialOwnerHistoryEntity toEntity() { + return GroupBeneficialOwnerHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .companyStatus(this.companyStatus) + .effectiveDate(this.effectiveDate) + .groupBeneficialOwner(this.groupBeneficialOwner) + .groupBeneficialOwnerCode(this.groupBeneficialOwnerCode) + .lrno(this.lrno) + .sequence(this.sequence) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/IceClassDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/IceClassDto.java index fe26e93..d639b52 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/IceClassDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/IceClassDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.IceClassEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -24,4 +25,13 @@ public class IceClassDto { private String iceClassCode; @JsonProperty("LRNO") private String lrno; + + public IceClassEntity toEntity() { + return IceClassEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .iceClass(this.iceClass) + .iceClassCode(this.iceClassCode) + .lrno(this.lrno) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/NameHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/NameHistoryDto.java index 9b9df13..1ee30f7 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/NameHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/NameHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.NameHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -26,4 +27,14 @@ public class NameHistoryDto { private String sequence; @JsonProperty("VesselName") private String vesselName; + + public NameHistoryEntity toEntity() { + return NameHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .effectiveDate(this.effectiveDate) + .lrno(this.lrno) + .sequence(this.sequence) + .vesselName(this.vesselName) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/OperatorHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/OperatorHistoryDto.java index 9a8188a..a3db024 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/OperatorHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/OperatorHistoryDto.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.OperatorHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -40,4 +41,16 @@ public class OperatorHistoryDto { @JsonProperty("Sequence") private String sequence; + + public OperatorHistoryEntity toEntity() { + return OperatorHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .companyStatus(this.companyStatus) + .effectiveDate(this.effectiveDate) + .lrno(this.lrno) + .operator(this.operator) + .operatorCode(this.operatorCode) + .sequence(this.sequence) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/OwnerHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/OwnerHistoryDto.java index eddccd5..ceacd38 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/OwnerHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/OwnerHistoryDto.java @@ -2,6 +2,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.OwnerHistoryEntity; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -61,4 +62,16 @@ public class OwnerHistoryDto { @JsonProperty("DataSetVersion") private String version; } + + public OwnerHistoryEntity toEntity(){ + return OwnerHistoryEntity.builder() + .CompanyStatus(this.CompanyStatus) + .EffectiveDate(this.EffectiveDate) + .LRNO(this.LRNO) + .Owner(this.Owner) + .OwnerCode(this.OwnerCode) + .Sequence(this.Sequence) + .dataSetVersion(this.dataSetVersion.version) + .build(); + } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/PandIHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/PandIHistoryDto.java index b129dea..d6e1567 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/PandIHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/PandIHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.PandIHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -30,4 +31,16 @@ public class PandIHistoryDto { private String effectiveDate; @JsonProperty("Source") private String source; + + public PandIHistoryEntity toEntity() { + return PandIHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .sequence(this.sequence) + .pandiclubcode(this.pandIClubCode) + .pandiclubdecode(this.pandIClubDecode) + .effectiveDate(this.effectiveDate) + .source(this.source) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SafetyManagementCertificateHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SafetyManagementCertificateHistoryDto.java index 9dffa4f..c993285 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SafetyManagementCertificateHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SafetyManagementCertificateHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.SafetyManagementCertificateHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -46,4 +47,24 @@ public class SafetyManagementCertificateHistoryDto { private String safetyManagementCertificateCompanyCode; @JsonProperty("Sequence") private String sequence; + + public SafetyManagementCertificateHistoryEntity toEntity() { + return SafetyManagementCertificateHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .safetyManagementCertificateAuditor(this.safetyManagementCertificateAuditor) + .safetyManagementCertificateConventionOrVol(this.safetyManagementCertificateConventionOrVol) + .safetyManagementCertificateDateExpires(this.safetyManagementCertificateDateExpires) + .safetyManagementCertificateDateIssued(this.safetyManagementCertificateDateIssued) + .safetyManagementCertificateDOCCompany(this.safetyManagementCertificateDOCCompany) + .safetyManagementCertificateFlag(this.safetyManagementCertificateFlag) + .safetyManagementCertificateIssuer(this.safetyManagementCertificateIssuer) + .safetyManagementCertificateOtherDescription(this.safetyManagementCertificateOtherDescription) + .safetyManagementCertificateShipName(this.safetyManagementCertificateShipName) + .safetyManagementCertificateShipType(this.safetyManagementCertificateShipType) + .safetyManagementCertificateSource(this.safetyManagementCertificateSource) + .safetyManagementCertificateCompanyCode(this.safetyManagementCertificateCompanyCode) + .sequence(this.sequence) + .build(); + } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipDetailDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipDetailDto.java index b6e76b0..3cee449 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipDetailDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipDetailDto.java @@ -476,7 +476,7 @@ public class ShipDetailDto { * API: CompanyVesselRelationships */ @JsonProperty("CompanyVesselRelationships") - private List CompanyVesselRelationships; + private List companyVesselRelationships; /** * 다크활동이력 정보 List @@ -486,6 +486,6 @@ public class ShipDetailDto { private List darkActivityConfirmed; @JsonProperty("CompanyDetailsComplexWithCodesAndParent") - private List companyDetailDtoList; + private List companyDetail; } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipManagerHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipManagerHistoryDto.java index f316b20..4274828 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipManagerHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipManagerHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.ShipManagerHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -42,4 +43,16 @@ public class ShipManagerHistoryDto { @JsonProperty("ShipManagerCode") private String shipManagerCode; + + public ShipManagerHistoryEntity toEntity() { + return ShipManagerHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .companyStatus(this.companyStatus) + .effectiveDate(this.effectiveDate) + .lrno(this.lrno) + .sequence(this.sequence) + .shipManager(this.shipManager) + .shipManagerCode(this.shipManagerCode) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipResultDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipResultDto.java index 676398d..22d5faa 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipResultDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ShipResultDto.java @@ -19,8 +19,7 @@ public class ShipResultDto { private Integer shipCount; @JsonProperty("APSShipDetail") - private JsonNode shipDetailNode; - // Getter and Setter - public JsonNode getShipDetailNode() { return shipDetailNode; } + private ShipDetailDto shipDetails; + } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SisterShipLinksDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SisterShipLinksDto.java index f651116..d18dcab 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SisterShipLinksDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SisterShipLinksDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.SisterShipLinksEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -22,4 +23,12 @@ public class SisterShipLinksDto { private String lrno; @JsonProperty("LinkedLRNO") private String linkedLRNO; + + public SisterShipLinksEntity toEntity() { + return SisterShipLinksEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .linkedLRNO(this.linkedLRNO) + .build(); + } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SpecialFeatureDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SpecialFeatureDto.java index ae6dd07..571a7d0 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SpecialFeatureDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SpecialFeatureDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.SpecialFeatureEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -26,4 +27,14 @@ public class SpecialFeatureDto { private String specialFeature; @JsonProperty("SpecialFeatureCode") private String specialFeatureCode; + + public SpecialFeatureEntity toEntity() { + return SpecialFeatureEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .sequence(this.sequence) + .specialFeature(this.specialFeature) + .specialFeatureCode(this.specialFeatureCode) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/StatusHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/StatusHistoryDto.java index b969ebf..f36c11c 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/StatusHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/StatusHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.StatusHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -28,4 +29,15 @@ public class StatusHistoryDto { private String statusCode; @JsonProperty("StatusDate") private String statusDate; + + public StatusHistoryEntity toEntity() { + return StatusHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .sequence(this.sequence) + .status(this.status) + .statusCode(this.statusCode) + .statusDate(this.statusDate) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/StowageCommodityDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/StowageCommodityDto.java index 1a80dc5..2b7bf42 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/StowageCommodityDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/StowageCommodityDto.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.StowageCommodityEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -40,4 +41,16 @@ public class StowageCommodityDto { @JsonProperty("StowageDecode") private String stowageDecode; + + public StowageCommodityEntity toEntity() { + return StowageCommodityEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .commodityCode(this.commodityCode) + .commodityDecode(this.commodityDecode) + .lrno(this.lrno) + .sequence(this.sequence) + .stowageCode(this.stowageCode) + .stowageDecode(this.stowageDecode) + .build(); + } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SurveyDatesHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SurveyDatesHistoryDto.java index 6fd2fd7..41937dc 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SurveyDatesHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SurveyDatesHistoryDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.SurveyDatesHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -34,4 +35,18 @@ public class SurveyDatesHistoryDto { private String specialSurvey; @JsonProperty("TailShaftSurvey") private String tailShaftSurvey; + + public SurveyDatesHistoryEntity toEntity() { + return SurveyDatesHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .annualSurvey(this.annualSurvey) + .classSociety(this.classSociety) + .classSocietyCode(this.classSocietyCode) + .continuousMachinerySurvey(this.continuousMachinerySurvey) + .dockingSurvey(this.dockingSurvey) + .lrno(this.lrno) + .specialSurvey(this.specialSurvey) + .tailShaftSurvey(this.tailShaftSurvey) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SurveyDatesHistoryUniqueDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SurveyDatesHistoryUniqueDto.java index 4072a65..d5ec0e5 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SurveyDatesHistoryUniqueDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/SurveyDatesHistoryUniqueDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.SurveyDatesHistoryUniqueEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -28,4 +29,15 @@ public class SurveyDatesHistoryUniqueDto { private String surveyType; @JsonProperty("ClassSociety") private String classSociety; + + public SurveyDatesHistoryUniqueEntity toEntity() { + return SurveyDatesHistoryUniqueEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .classSocietyCode(this.classSocietyCode) + .surveyDate(this.surveyDate) + .surveyType(this.surveyType) + .classSociety(this.classSociety) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/TechnicalManagerHistoryDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/TechnicalManagerHistoryDto.java index 4c87310..250e5a8 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/TechnicalManagerHistoryDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/TechnicalManagerHistoryDto.java @@ -1,5 +1,6 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.TechnicalManagerHistoryEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -29,4 +30,16 @@ public class TechnicalManagerHistoryDto { private String technicalManager; @JsonProperty("TechnicalManagerCode") private String technicalManagerCode; + + public TechnicalManagerHistoryEntity toEntity() { + return TechnicalManagerHistoryEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .companyStatus(this.companyStatus) + .effectiveDate(this.effectiveDate) + .lrno(this.lrno) + .sequence(this.sequence) + .technicalManager(this.technicalManager) + .technicalManagerCode(this.technicalManagerCode) + .build(); + } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ThrustersDto.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ThrustersDto.java index 6ff5830..b6abce5 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ThrustersDto.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/dto/ThrustersDto.java @@ -1,6 +1,7 @@ package com.snp.batch.jobs.shipdetail.batch.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.jobs.shipdetail.batch.entity.ThrustersEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -36,4 +37,19 @@ public class ThrustersDto { private String thrusterKW; // numeric(20) in DB @JsonProperty("TypeOfInstallation") private String typeOfInstallation; + + public ThrustersEntity toEntity() { + return ThrustersEntity.builder() + .dataSetVersion(this.dataSetVersion != null ? this.dataSetVersion.getDataSetVersion() : null) + .lrno(this.lrno) + .sequence(this.sequence) + .thrusterType(this.thrusterType) + .thrusterTypeCode(this.thrusterTypeCode) + .numberOfThrusters(this.numberOfThrusters) + .thrusterPosition(this.thrusterPosition) + .thrusterBHP(this.thrusterBHP) + .thrusterKW(this.thrusterKW) + .typeOfInstallation(this.typeOfInstallation) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/GroupBeneficialOwnerHistoryEntity.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/GroupBeneficialOwnerHistoryEntity.java index 80e0b06..df62d89 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/GroupBeneficialOwnerHistoryEntity.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/GroupBeneficialOwnerHistoryEntity.java @@ -24,5 +24,4 @@ public class GroupBeneficialOwnerHistoryEntity extends BaseEntity { private String lrno; private String sequence; - // DB 관리 컬럼 (shipresultindex, vesselid, rowindex)는 Entity에서 제거됨 } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/ShipDetailEntity.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/ShipDetailEntity.java index a00778a..b0804db 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/ShipDetailEntity.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/ShipDetailEntity.java @@ -7,6 +7,8 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import java.util.List; + /** * 선박 상세 정보 Entity * BaseEntity를 상속하여 감사 필드 자동 포함 @@ -23,33 +25,6 @@ import lombok.experimental.SuperBuilder; @AllArgsConstructor @EqualsAndHashCode(callSuper = true) public class ShipDetailEntity extends BaseEntity { - - /** - * TODO : Core20 Dto 작성 - * shipresultindex int8 NOT NULL, -- 결과인덱스 - * ihslrorimoshipno varchar(7) NOT NULL, -- IMO번호 - * vesselid varchar(7) NOT NULL, -- 선박ID - * maritimemobileserviceidentitymmsinumber varchar(9) NULL, -- MMSI - * shipname varchar(100) NULL, -- 선명 - * callsign varchar(5) NULL, -- 호출부호 - * flagname varchar(100) NULL, -- 국가 - * portofregistry varchar(50) NULL, -- 등록항 - * classificationsociety varchar(50) NULL, -- 선급 - * shiptypelevel5 varchar(15) NULL, -- 선종(Lv5) - * shiptypelevel5subtype varchar(15) NULL, -- 세부선종 - * yearofbuild varchar(4) NULL, -- 건조연도 - * shipbuilder varchar(100) NULL, -- 조선소 - * lengthoverallloa numeric(3, 3) NULL, -- 전장(LOA)[m] - * breadthmoulded numeric(3, 3) NULL, -- 형폭(몰디드)[m] - * "depth" numeric(3, 3) NULL, -- 깊이[m] - * draught numeric(3, 3) NULL, -- 흘수[m] - * grosstonnage varchar(4) NULL, -- 총톤수(GT) - * deadweight varchar(5) NULL, -- 재화중량톤수(DWT) - * teu varchar(1) NULL, -- 컨테이너(TEU) - * speedservice numeric(2, 2) NULL, -- 항속(kt) - * mainenginetype varchar(2) NULL, -- 주기관 형식 - * batch_flag varchar(1) DEFAULT 'N'::character varying NULL -- 업데이트 이력 확인 (N:대기,P:진행,S:완료) - */ private String dataSetVersion; /** * 기본 키 (자동 생성) @@ -188,7 +163,7 @@ public class ShipDetailEntity extends BaseEntity { * 항속(kt) * 컬럼: speedservice (NUMERIC(2, 2)) */ - private Double speedService; + private String speedService; /** * 주기관 형식 @@ -278,4 +253,31 @@ public class ShipDetailEntity extends BaseEntity { private String shipManagerCompanyCode; private String technicalManagerCode; private String registeredOwnerCode; + + private List ownerHistoryEntityList; + private List crewListEntityList; + private List stowageCommodityEntityList; + private List groupBeneficialOwnerHistoryEntityList; + private List shipManagerHistoryEntityList; + private List operatorHistoryEntityList; + private List technicalManagerHistoryEntityList; + private List bareBoatCharterHistoryEntityList; + private List nameHistoryEntityList; + private List flagHistoryEntityList; + private List additionalInformationEntityList; + private List pandIHistoryEntityList; + private List callSignAndMmsiHistoryEntityList; + private List iceClassEntityList; + private List safetyManagementCertificateHistoryEntityList; + private List classHistoryEntityList; + private List surveyDatesHistoryEntityList; + private List surveyDatesHistoryUniqueEntityList; + private List sisterShipLinksEntityList; + private List statusHistoryEntityList; + private List specialFeatureEntityList; + private List thrustersEntityList; + private List darkActivityConfirmedEntityList; + private List companyVesselRelationshipEntityList; + private List companyDetailEntityList; + } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/ShipManagerHistoryEntity.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/ShipManagerHistoryEntity.java index 2fc6c07..4fb5cca 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/ShipManagerHistoryEntity.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/ShipManagerHistoryEntity.java @@ -24,5 +24,4 @@ public class ShipManagerHistoryEntity extends BaseEntity { private String shipManager; private String shipManagerCode; - // DB 관리 컬럼 (shipresultindex, vesselid, rowindex)는 Entity에서 제거됨 } \ No newline at end of file diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/StowageCommodityEntity.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/StowageCommodityEntity.java index 510c270..3934d95 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/StowageCommodityEntity.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/entity/StowageCommodityEntity.java @@ -24,5 +24,4 @@ public class StowageCommodityEntity extends BaseEntity { private String stowageCode; // varchar(2) private String stowageDecode; // varchar(50) - // DB 관리 컬럼 (shipresultindex, vesselid, rowindex)는 Entity에서 제거됨 } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/processor/ShipDetailDataProcessor.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/processor/ShipDetailDataProcessor.java index 1a55189..7186342 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/processor/ShipDetailDataProcessor.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/processor/ShipDetailDataProcessor.java @@ -4,9 +4,11 @@ import com.snp.batch.common.batch.processor.BaseProcessor; import com.snp.batch.jobs.shipdetail.batch.dto.*; import com.snp.batch.jobs.shipdetail.batch.entity.*; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.*; +import java.util.stream.Collectors; /** * 선박 상세 정보 Processor @@ -20,98 +22,15 @@ import java.util.*; */ @Slf4j @Component -public class ShipDetailDataProcessor extends BaseProcessor { +public class ShipDetailDataProcessor extends BaseProcessor { + private final Long jobExecutionId; + + public ShipDetailDataProcessor(@Value("#{stepExecution.jobExecution.id}") Long jobExecutionId){ + this.jobExecutionId = jobExecutionId; + } @Override - protected ShipDetailUpdate processItem(ShipDetailComparisonData comparisonData) throws Exception { - String imo = comparisonData.getImoNumber(); - String previousHash = comparisonData.getPreviousMasterHash(); - String currentHash = comparisonData.getCurrentMasterHash(); - - log.debug("선박 해시 비교 시작: imoNumber={}, DB Hash={}, API Hash={}", imo, previousHash, currentHash); - - // 1. 해시 비교 (변경 감지) - if (isChanged(previousHash, currentHash)) { - //log.info("선박 데이터 변경 감지: imoNumber={}", imo); - - // 2. 증분 데이터 추출 (Map 기반으로 업데이트 데이터 구성) - // Core20 - ShipDetailEntity shipDetailEntity = makeShipDetailEntity(comparisonData.getStructuredDto()); - List ownerHistoryEntityList = makeOwnerHistoryEntityList(comparisonData.getStructuredDto().getOwnerHistory()); - List crewListEntityList = makeCrewListEntityList(comparisonData.getStructuredDto().getCrewList()); - List stowageCommodityEntityList = makeStowageCommodityEntityList(comparisonData.getStructuredDto().getStowageCommodity()); - List groupBeneficialOwnerHistoryEntityList = makeGroupBeneficialOwnerHistoryEntityList(comparisonData.getStructuredDto().getGroupBeneficialOwnerHistory()); - List shipManagerHistoryEntityList = makeShipManagerHistoryEntityList(comparisonData.getStructuredDto().getShipManagerHistory()); - List operatorHistoryEntityList = makeOperatorHistoryEntityList(comparisonData.getStructuredDto().getOperatorHistory()); - List technicalManagerHistoryEntityList = makeTechnicalManagerHistoryEntityList(comparisonData.getStructuredDto().getTechnicalManagerHistory()); - List bareBoatCharterHistoryEntityList = makeBareBoatCharterHistoryEntityList(comparisonData.getStructuredDto().getBareBoatCharterHistory()); - List nameHistoryEntityList = makeNameHistoryEntityList(comparisonData.getStructuredDto().getNameHistory()); - List flagHistoryEntityList = makeFlagHistoryEntityList(comparisonData.getStructuredDto().getFlagHistory()); - List additionalInformationEntityList = makeAdditionalInformationEntityList(comparisonData.getStructuredDto().getAdditionalInformation()); - List pandIHistoryEntityList = makePandIHistoryEntityList(comparisonData.getStructuredDto().getPandIHistory()); - List callSignAndMmsiHistoryEntityList = makeCallSignAndMmsiHistoryEntityList(comparisonData.getStructuredDto().getCallSignAndMmsiHistory()); - List iceClassEntityList = makeIceClassEntityList(comparisonData.getStructuredDto().getIceClass()); - List safetyManagementCertificateHistoryEntityList = makeSafetyManagementCertificateHistoryEntityList(comparisonData.getStructuredDto().getSafetyManagementCertificateHistory()); - List classHistoryEntityList = makeClassHistoryEntityList(comparisonData.getStructuredDto().getClassHistory()); - List surveyDatesHistoryEntityList = makeSurveyDatesHistoryEntityList(comparisonData.getStructuredDto().getSurveyDatesHistory()); - List surveyDatesHistoryUniqueEntityList = makeSurveyDatesHistoryUniqueEntityList(comparisonData.getStructuredDto().getSurveyDatesHistoryUnique()); - List sisterShipLinksEntityList = makeSisterShipLinksEntityList(comparisonData.getStructuredDto().getSisterShipLinks()); - List statusHistoryEntityList = makeStatusHistoryEntityList(comparisonData.getStructuredDto().getStatusHistory()); - List specialFeatureEntityList = makeSpecialFeatureEntityList(comparisonData.getStructuredDto().getSpecialFeature()); - List thrustersEntityList = makeThrustersEntityList(comparisonData.getStructuredDto().getThrusters()); - List darkActivityConfirmedEntityList = makeDarkActivityConfirmedEntityList(comparisonData.getStructuredDto().getDarkActivityConfirmed()); -// List companyComplianceEntityList = makeCompanyComplianceEntityList(comparisonData.getStructuredDto().getCompanyComplianceDetails()); : 2026-01-16 Company Compliance 수집 API 분리로 인한 제거 - List companyVesselRelationshipEntityList = makeCompanyVesselRelationshipEntityList(comparisonData.getStructuredDto().getCompanyVesselRelationships()); - List companyDetailEntityList = makeCompanyDetailEntity(comparisonData.getStructuredDto().getCompanyDetailDtoList()); - - // 3. 최종 업데이트 DTO 생성 (Writer에 전달) - return ShipDetailUpdate.builder() - .imoNumber(imo) - .newMasterHash(currentHash) // 새로운 해시 - .currentMasterMap(comparisonData.getCurrentMasterMap()) // DB JSONB 컬럼 업데이트용 Map - .shipDetailEntity(shipDetailEntity) - .ownerHistoryEntityList(ownerHistoryEntityList) - .crewListEntityList(crewListEntityList) - .stowageCommodityEntityList(stowageCommodityEntityList) - .groupBeneficialOwnerHistoryEntityList(groupBeneficialOwnerHistoryEntityList) - .shipManagerHistoryEntityList(shipManagerHistoryEntityList) - .operatorHistoryEntityList(operatorHistoryEntityList) - .technicalManagerHistoryEntityList(technicalManagerHistoryEntityList) - .bareBoatCharterHistoryEntityList(bareBoatCharterHistoryEntityList) - .nameHistoryEntityList(nameHistoryEntityList) - .flagHistoryEntityList(flagHistoryEntityList) - .additionalInformationEntityList(additionalInformationEntityList) - .pandIHistoryEntityList(pandIHistoryEntityList) - .callSignAndMmsiHistoryEntityList(callSignAndMmsiHistoryEntityList) - .iceClassEntityList(iceClassEntityList) - .safetyManagementCertificateHistoryEntityList(safetyManagementCertificateHistoryEntityList) - .classHistoryEntityList(classHistoryEntityList) - .surveyDatesHistoryEntityList(surveyDatesHistoryEntityList) - .surveyDatesHistoryUniqueEntityList(surveyDatesHistoryUniqueEntityList) - .sisterShipLinksEntityList(sisterShipLinksEntityList) - .statusHistoryEntityList(statusHistoryEntityList) - .specialFeatureEntityList(specialFeatureEntityList) - .thrustersEntityList(thrustersEntityList) - .darkActivityConfirmedEntityList(darkActivityConfirmedEntityList) -// .companyComplianceEntityList(companyComplianceEntityList) : 2026-01-16 Company Compliance 수집 API 분리로 인한 제거 - .companyVesselRelationshipEntityList(companyVesselRelationshipEntityList) - .companyDetailEntityList(companyDetailEntityList) - .shipHashEntity(makeShipHashEntity(imo, currentHash)) - .build(); - } - - // 변경 사항이 없으면 null 반환 (Writer로 전달되지 않음) - return null; - } - - private ShipHashEntity makeShipHashEntity(String imo, String hash){ - return ShipHashEntity.builder() - .imoNumber(imo) - .shipDetailHash(hash) - .build(); - } - - private ShipDetailEntity makeShipDetailEntity(ShipDetailDto dto){ + protected ShipDetailEntity processItem(ShipDetailDto dto) throws Exception { return ShipDetailEntity.builder() .ihslrorimoshipno(safeGetString(dto.getIhslrorimoshipno())) .shipName(safeGetString(dto.getShipName())) @@ -212,707 +131,114 @@ public class ShipDetailDataProcessor extends BaseProcessor (OwnerHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .crewListEntityList(dto.getCrewList() != null ? + dto.getCrewList().stream() + .map(d -> (CrewListEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .stowageCommodityEntityList(dto.getStowageCommodity() != null ? + dto.getStowageCommodity().stream() + .map(d -> (StowageCommodityEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .groupBeneficialOwnerHistoryEntityList(dto.getGroupBeneficialOwnerHistory() != null ? + dto.getGroupBeneficialOwnerHistory().stream() + .map(d -> (GroupBeneficialOwnerHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .shipManagerHistoryEntityList(dto.getShipManagerHistory() != null ? + dto.getShipManagerHistory().stream() + .map(d -> (ShipManagerHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .operatorHistoryEntityList(dto.getOperatorHistory() != null ? + dto.getOperatorHistory().stream() + .map(d -> (OperatorHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .technicalManagerHistoryEntityList(dto.getTechnicalManagerHistory() != null ? + dto.getTechnicalManagerHistory().stream() + .map(d -> (TechnicalManagerHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .bareBoatCharterHistoryEntityList(dto.getBareBoatCharterHistory() != null ? + dto.getBareBoatCharterHistory().stream() + .map(d -> (BareBoatCharterHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .nameHistoryEntityList(dto.getNameHistory() != null ? + dto.getNameHistory().stream() + .map(d -> (NameHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .flagHistoryEntityList(dto.getFlagHistory() != null ? + dto.getFlagHistory().stream() + .map(d -> (FlagHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .additionalInformationEntityList(dto.getAdditionalInformation() != null ? + dto.getAdditionalInformation().stream() + .map(d -> (AdditionalInformationEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .pandIHistoryEntityList(dto.getPandIHistory() != null ? + dto.getPandIHistory().stream() + .map(d -> (PandIHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .callSignAndMmsiHistoryEntityList(dto.getCallSignAndMmsiHistory() != null ? + dto.getCallSignAndMmsiHistory().stream() + .map(d -> (CallSignAndMmsiHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .iceClassEntityList(dto.getIceClass() != null ? + dto.getIceClass().stream() + .map(d -> (IceClassEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .safetyManagementCertificateHistoryEntityList(dto.getSafetyManagementCertificateHistory() != null ? + dto.getSafetyManagementCertificateHistory().stream() + .map(d -> (SafetyManagementCertificateHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .classHistoryEntityList(dto.getClassHistory() != null ? + dto.getClassHistory().stream() + .map(d -> (ClassHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .surveyDatesHistoryEntityList(dto.getSurveyDatesHistory() != null ? + dto.getSurveyDatesHistory().stream() + .map(d -> (SurveyDatesHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .surveyDatesHistoryUniqueEntityList(dto.getSurveyDatesHistoryUnique() != null ? + dto.getSurveyDatesHistoryUnique().stream() + .map(d -> (SurveyDatesHistoryUniqueEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .sisterShipLinksEntityList(dto.getSisterShipLinks() != null ? + dto.getSisterShipLinks().stream() + .map(d -> (SisterShipLinksEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .statusHistoryEntityList(dto.getStatusHistory() != null ? + dto.getStatusHistory().stream() + .map(d -> (StatusHistoryEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .specialFeatureEntityList(dto.getSpecialFeature() != null ? + dto.getSpecialFeature().stream() + .map(d -> (SpecialFeatureEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .thrustersEntityList(dto.getThrusters() != null ? + dto.getThrusters().stream() + .map(d -> (ThrustersEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .companyVesselRelationshipEntityList(dto.getCompanyVesselRelationships() != null ? + dto.getCompanyVesselRelationships().stream() + .map(d -> (CompanyVesselRelationshipEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .darkActivityConfirmedEntityList(dto.getDarkActivityConfirmed() != null ? + dto.getDarkActivityConfirmed().stream() + .map(d -> (DarkActivityConfirmedEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) + .companyDetailEntityList(dto.getCompanyDetail() != null ? + dto.getCompanyDetail().stream() + .map(d -> (CompanyDetailEntity) d.toEntity().setBatchInfo(jobExecutionId, "SYSTEM")) + .collect(Collectors.toList()) : null) .build(); - } - - private List makeOwnerHistoryEntityList(List dtoList){ - List ownerHistoryEntityList = new ArrayList<>(); - - // TODO: ownerHistoryEntityList 생성 로직 - if (dtoList == null || dtoList.isEmpty()) { - return ownerHistoryEntityList; // 빈 리스트 또는 null 입력 시 빈 리스트 반환 - } - - for (OwnerHistoryDto dto : dtoList) { - OwnerHistoryEntity entity = OwnerHistoryEntity.builder() - .CompanyStatus(safeGetString(dto.getCompanyStatus())) - .EffectiveDate(safeGetString(dto.getEffectiveDate())) - .LRNO(safeGetString(dto.getLRNO())) - .Owner(safeGetString(dto.getOwner())) - .OwnerCode(safeGetString(dto.getOwnerCode())) - .Sequence(safeGetString(dto.getSequence())) - .dataSetVersion(safeGetString(dto.getDataSetVersion().getVersion())) - .build(); - - ownerHistoryEntityList.add(entity); - } - - return ownerHistoryEntityList; - } - - private List makeCrewListEntityList(List dtoList){ - List crewListEntityList = new ArrayList<>(); - - if (dtoList == null || dtoList.isEmpty()) { - return crewListEntityList; // 빈 리스트 또는 null 입력 시 빈 리스트 반환 - } - - for (CrewListDto dto : dtoList) { - String datasetVersion = null; - if (dto.getDataSetVersion() != null) { - datasetVersion = dto.getDataSetVersion().getDataSetVersion(); - } - - // superBuilder() 대신 builder() 사용 - CrewListEntity entity = CrewListEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .id(safeGetString(dto.getId())) - .lrno(safeGetString(dto.getLrno())) - .shipname(safeGetString(dto.getShipname())) - .crewlistdate(safeGetString(dto.getCrewListDate())) - .nationality(safeGetString(dto.getNationality())) - .totalcrew(safeGetString(dto.getTotalCrew())) - .totalratings(safeGetString(dto.getTotalRatings())) - .totalofficers(safeGetString(dto.getTotalOfficers())) - .totalcadets(safeGetString(dto.getTotalCadets())) - .totaltrainees(safeGetString(dto.getTotalTrainees())) - .totalridingsquad(safeGetString(dto.getTotalRidingSquad())) - .totalundeclared(safeGetString(dto.getTotalUndeclared())) - // DB에서 관리되는 컬럼 임시 셋팅 로직 제거 - .build(); - - crewListEntityList.add(entity); - } - - return crewListEntityList; - } - - private List makeStowageCommodityEntityList(List dtoList){ - List entityList = new ArrayList<>(); - - if (dtoList == null || dtoList.isEmpty()) { - return entityList; // 빈 리스트 또는 null 입력 시 빈 리스트 반환 - } - - for (StowageCommodityDto dto : dtoList) { - String datasetVersion = null; - if (dto.getDataSetVersion() != null) { - datasetVersion = dto.getDataSetVersion().getDataSetVersion(); - } - - // builder() 사용 및 DB 관리 컬럼 셋팅 로직 제거 - StowageCommodityEntity entity = StowageCommodityEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .commodityCode(safeGetString(dto.getCommodityCode())) - .commodityDecode(safeGetString(dto.getCommodityDecode())) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .stowageCode(safeGetString(dto.getStowageCode())) - .stowageDecode(safeGetString(dto.getStowageDecode())) - .build(); - - entityList.add(entity); - } - - return entityList; - } - - private List makeGroupBeneficialOwnerHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - - if (dtoList == null || dtoList.isEmpty()) { - return entityList; // 빈 리스트 또는 null 입력 시 빈 리스트 반환 - } - - for (GroupBeneficialOwnerHistoryDto dto : dtoList) { - String datasetVersion = null; - if (dto.getDataSetVersion() != null) { - datasetVersion = dto.getDataSetVersion().getDataSetVersion(); - } - - // builder() 사용 및 DB 관리 컬럼 셋팅 로직 제거 - GroupBeneficialOwnerHistoryEntity entity = GroupBeneficialOwnerHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .companyStatus(safeGetString(dto.getCompanyStatus())) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .groupBeneficialOwner(safeGetString(dto.getGroupBeneficialOwner())) - .groupBeneficialOwnerCode(safeGetString(dto.getGroupBeneficialOwnerCode())) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .build(); - - entityList.add(entity); - } - - return entityList; - } - - private List makeShipManagerHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - - if (dtoList == null || dtoList.isEmpty()) { - return entityList; // 빈 리스트 또는 null 입력 시 빈 리스트 반환 - } - - for (ShipManagerHistoryDto dto : dtoList) { - String datasetVersion = null; - if (dto.getDataSetVersion() != null) { - datasetVersion = dto.getDataSetVersion().getDataSetVersion(); - } - - // builder() 사용 및 DB 관리 컬럼 셋팅 로직 제거 - ShipManagerHistoryEntity entity = ShipManagerHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .companyStatus(safeGetString(dto.getCompanyStatus())) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .shipManager(safeGetString(dto.getShipManager())) - .shipManagerCode(safeGetString(dto.getShipManagerCode())) - .build(); - - entityList.add(entity); - } - - return entityList; - } - - private List makeOperatorHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - - for (OperatorHistoryDto dto : dtoList) { - String datasetVersion = null; - if (dto.getDataSetVersion() != null) { - datasetVersion = dto.getDataSetVersion().getDataSetVersion(); - } - - // builder() 사용 및 DB 관리 컬럼 셋팅 로직 제거 - OperatorHistoryEntity entity = OperatorHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .companyStatus(safeGetString(dto.getCompanyStatus())) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .lrno(safeGetString(dto.getLrno())) - .operator(safeGetString(dto.getOperator())) - .operatorCode(safeGetString(dto.getOperatorCode())) - .sequence(safeGetString(dto.getSequence())) - .build(); - - entityList.add(entity); - } - - return entityList; - } - - private List makeTechnicalManagerHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (TechnicalManagerHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - TechnicalManagerHistoryEntity entity = TechnicalManagerHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .companyStatus(safeGetString(dto.getCompanyStatus())) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .technicalManager(safeGetString(dto.getTechnicalManager())) - .technicalManagerCode(safeGetString(dto.getTechnicalManagerCode())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeBareBoatCharterHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (BareBoatCharterHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - BareBoatCharterHistoryEntity entity = BareBoatCharterHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .bbChartererCode(safeGetString(dto.getBbChartererCode())) - .bbCharterer(safeGetString(dto.getBbCharterer())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeNameHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (NameHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - NameHistoryEntity entity = NameHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .vesselName(safeGetString(dto.getVesselName())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeFlagHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (FlagHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - FlagHistoryEntity entity = FlagHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .flag(safeGetString(dto.getFlag())) - .flagCode(safeGetString(dto.getFlagCode())) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeAdditionalInformationEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (AdditionalInformationDto dto : dtoList) { - // DataSetVersion은 DB 스키마에 없으므로 Entity에 매핑하지 않음 - AdditionalInformationEntity entity = AdditionalInformationEntity.builder() - .lrno(safeGetString(dto.getLrno())) - .shipemail(safeGetString(dto.getShipEmail())) - .waterdepthmax(safeGetString(dto.getWaterDepthMax())) - .drilldepthmax(safeGetString(dto.getDrillDepthMax())) - .drillbargeind(safeGetString(dto.getDrillBargeInd())) - .productionvesselind(safeGetString(dto.getProductionVesselInd())) - .deckheatexchangerind(safeGetString(dto.getDeckHeatExchangerInd())) - .deckheatexchangermaterial(safeGetString(dto.getDeckHeatExchangerMaterial())) - .tweendeckportable(safeGetString(dto.getTweenDeckPortable())) - .tweendeckfixed(safeGetString(dto.getTweenDeckFixed())) - .satcomid(safeGetString(dto.getSatComID())) - .satcomansback(safeGetString(dto.getSatComAnsBack())) - .dataSetVersion(safeGetString(dto.getDataSetVersion().getVersion())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makePandIHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (PandIHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - PandIHistoryEntity entity = PandIHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .pandiclubcode(safeGetString(dto.getPandIClubCode())) - .pandiclubdecode(safeGetString(dto.getPandIClubDecode())) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .source(safeGetString(dto.getSource())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeCallSignAndMmsiHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (CallSignAndMmsiHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - CallSignAndMmsiHistoryEntity entity = CallSignAndMmsiHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSeqNo())) // SeqNo -> sequence - .callsign(safeGetString(dto.getCallSign())) - .mmsi(dto.getMmsi()) // JSON에 MMSI 필드가 없으므로 null 또는 기본값 (필요 시 수정) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeIceClassEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (IceClassDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - IceClassEntity entity = IceClassEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .iceClass(safeGetString(dto.getIceClass())) - .iceClassCode(safeGetString(dto.getIceClassCode())) - .lrno(safeGetString(dto.getLrno())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeSafetyManagementCertificateHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (SafetyManagementCertificateHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - SafetyManagementCertificateHistoryEntity entity = SafetyManagementCertificateHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .safetyManagementCertificateAuditor(safeGetString(dto.getSafetyManagementCertificateAuditor())) - .safetyManagementCertificateConventionOrVol(safeGetString(dto.getSafetyManagementCertificateConventionOrVol())) - .safetyManagementCertificateDateExpires(safeGetString(dto.getSafetyManagementCertificateDateExpires())) - .safetyManagementCertificateDateIssued(safeGetString(dto.getSafetyManagementCertificateDateIssued())) - .safetyManagementCertificateDOCCompany(safeGetString(dto.getSafetyManagementCertificateDOCCompany())) - .safetyManagementCertificateFlag(safeGetString(dto.getSafetyManagementCertificateFlag())) - .safetyManagementCertificateIssuer(safeGetString(dto.getSafetyManagementCertificateIssuer())) - .safetyManagementCertificateOtherDescription(safeGetString(dto.getSafetyManagementCertificateOtherDescription())) - .safetyManagementCertificateShipName(safeGetString(dto.getSafetyManagementCertificateShipName())) - .safetyManagementCertificateShipType(safeGetString(dto.getSafetyManagementCertificateShipType())) - .safetyManagementCertificateSource(safeGetString(dto.getSafetyManagementCertificateSource())) - .safetyManagementCertificateCompanyCode(safeGetString(dto.getSafetyManagementCertificateCompanyCode())) - .sequence(safeGetString(dto.getSequence())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeClassHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (ClassHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - ClassHistoryEntity entity = ClassHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - ._class(safeGetString(dto.get_class())) - .classCode(safeGetString(dto.getClassCode())) - .classIndicator(safeGetString(dto.getClassIndicator())) - .classID(safeGetString(dto.getClassID())) - .currentIndicator(safeGetString(dto.getCurrentIndicator())) - .effectiveDate(safeGetString(dto.getEffectiveDate())) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeSurveyDatesHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (SurveyDatesHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - SurveyDatesHistoryEntity entity = SurveyDatesHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .annualSurvey(safeGetString(dto.getAnnualSurvey())) - .classSociety(safeGetString(dto.getClassSociety())) - .classSocietyCode(safeGetString(dto.getClassSocietyCode())) - .continuousMachinerySurvey(safeGetString(dto.getContinuousMachinerySurvey())) - .dockingSurvey(safeGetString(dto.getDockingSurvey())) - .lrno(safeGetString(dto.getLrno())) - .specialSurvey(safeGetString(dto.getSpecialSurvey())) - .tailShaftSurvey(safeGetString(dto.getTailShaftSurvey())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeSurveyDatesHistoryUniqueEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (SurveyDatesHistoryUniqueDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - SurveyDatesHistoryUniqueEntity entity = SurveyDatesHistoryUniqueEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .classSocietyCode(safeGetString(dto.getClassSocietyCode())) - .surveyDate(safeGetString(dto.getSurveyDate())) - .surveyType(safeGetString(dto.getSurveyType())) - .classSociety(safeGetString(dto.getClassSociety())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeSisterShipLinksEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (SisterShipLinksDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - SisterShipLinksEntity entity = SisterShipLinksEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .linkedLRNO(safeGetString(dto.getLinkedLRNO())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeStatusHistoryEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (StatusHistoryDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - StatusHistoryEntity entity = StatusHistoryEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .status(safeGetString(dto.getStatus())) - .statusCode(safeGetString(dto.getStatusCode())) - .statusDate(safeGetString(dto.getStatusDate())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeSpecialFeatureEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (SpecialFeatureDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - SpecialFeatureEntity entity = SpecialFeatureEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .specialFeature(safeGetString(dto.getSpecialFeature())) - .specialFeatureCode(safeGetString(dto.getSpecialFeatureCode())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeThrustersEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (ThrustersDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - ThrustersEntity entity = ThrustersEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .lrno(safeGetString(dto.getLrno())) - .sequence(safeGetString(dto.getSequence())) - .thrusterType(safeGetString(dto.getThrusterType())) - .thrusterTypeCode(safeGetString(dto.getThrusterTypeCode())) - .numberOfThrusters(safeGetString(dto.getNumberOfThrusters())) - .thrusterPosition(safeGetString(dto.getThrusterPosition())) - .thrusterBHP(safeGetString(dto.getThrusterBHP())) - .thrusterKW(safeGetString(dto.getThrusterKW())) - .typeOfInstallation(safeGetString(dto.getTypeOfInstallation())) - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeCompanyVesselRelationshipEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (CompanyVesselRelationshipDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - CompanyVesselRelationshipEntity entity = CompanyVesselRelationshipEntity.builder() - .datasetversion(datasetVersion) - .doccode(dto.getDocCode()) - .doccompany(dto.getDocCompany()) - .groupbeneficialowner(dto.getGroupBeneficialOwner()) - .groupbeneficialownercode(dto.getGroupBeneficialOwnerCode()) - .lrno(dto.getLrno()) - .operator(dto.getOperator()) - .operatorcode(dto.getOperatorCode()) - .registeredowner(dto.getRegisteredOwner()) - .registeredownercode(dto.getRegisteredOwnerCode()) - .shipmanager(dto.getShipManager()) - .shipmanagercode(dto.getShipManagerCode()) - .technicalmanager(dto.getTechnicalManager()) - .technicalmanagercode(dto.getTechnicalManagerCode()) - .docgroup(dto.getDocGroup()) - .docgroupcode(dto.getDocGroupCode()) - .operatorgroup(dto.getOperatorGroup()) - .operatorgroupcode(dto.getOperatorGroupCode()) - .shipmanagergroup(dto.getShipManagerGroup()) - .shipmanagergroupcode(dto.getShipManagerGroupCode()) - .technicalmanagergroup(dto.getTechnicalManagerGroup()) - .technicalmanagergroupcode(dto.getTechnicalManagerGroupCode()) - .vesselid(dto.getLrno()) // LRNO를 VesselID로 매핑 - .build(); - entityList.add(entity); - } - return entityList; - } - - private List makeDarkActivityConfirmedEntityList(List dtoList){ - List entityList = new ArrayList<>(); - if (dtoList == null || dtoList.isEmpty()) { - return entityList; - } - for (DarkActivityConfirmedDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - DarkActivityConfirmedEntity entity = DarkActivityConfirmedEntity.builder() - .datasetversion(datasetVersion) - .lrno(dto.getLrno()) - .mmsi(dto.getMmsi()) - .vessel_name(dto.getVesselName()) - .dark_hours(dto.getDarkHours()) - .dark_activity(dto.getDarkActivity()) - .dark_status(dto.getDarkStatus()) - .area_id(dto.getAreaId()) - .area_name(dto.getAreaName()) - .area_country(dto.getAreaCountry()) - .dark_time(dto.getDarkTime()) - .dark_latitude(dto.getDarkLatitude()) - .dark_longitude(dto.getDarkLongitude()) - .dark_speed(dto.getDarkSpeed()) - .dark_heading(dto.getDarkHeading()) - .dark_draught(dto.getDarkDraught()) - .nextseen(dto.getNextSeen()) - .nextseen_speed(dto.getNextSeenSpeed()) - .nextseen_draught(dto.getNextSeenDraught()) - .nextseen_heading(dto.getNextSeenHeading()) - .dark_reported_destination(dto.getDarkReportedDestination()) - .last_port_of_call(dto.getLastPortOfCall()) - .last_port_country_code(dto.getLastPortCountryCode()) - .last_port_country(dto.getLastPortCountry()) - .nextseen_latitude(dto.getNextSeenLatitude()) - .nextseen_longitude(dto.getNextSeenLongitude()) - .nextseen_reported_destination(dto.getNextSeenReportedDestination()) - .vesselid(dto.getLrno()) - .build(); - entityList.add(entity); - } - return entityList; - } - -// private List makeCompanyComplianceEntityList(List dtoList){ -// List entityList = new ArrayList<>(); -// if (dtoList == null || dtoList.isEmpty()) { -// return entityList; // } -// for (CompanyComplianceDto dto : dtoList) { -// String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; -// CompanyComplianceEntity entity = CompanyComplianceEntity.builder() -// .datasetversion(datasetVersion) -// .owcode(dto.getOwCode()) -// .shortcompanyname(dto.getShortCompanyName()) -// .companyonofacsanctionlist(dto.getCompanyOnOFACSanctionList()) -// .companyonunsanctionlist(dto.getCompanyOnUNSanctionList()) -// .companyoneusanctionlist(dto.getCompanyOnEUSanctionList()) -// .companyonbessanctionlist(dto.getCompanyOnBESSanctionList()) -// .companyinofacsanctionedcountry(dto.getCompanyInOFACSanctionedCountry()) -// .companyinfatfjurisdiction(dto.getCompanyInFATFJurisdiction()) -// .companyoverallcompliancestatus(dto.getCompanyOverallComplianceStatus()) -// .companyonaustraliansanctionlist(dto.getCompanyOnAustralianSanctionList()) -// .companyoncanadiansanctionlist(dto.getCompanyOnCanadianSanctionList()) -// .companyonswisssanctionlist(dto.getCompanyOnSwissSanctionList()) -// .companyonofacssilist(dto.getCompanyOnOFACSSIList()) -// .companyonofacnonsdnsanctionlist(dto.getCompanyOnOFACNonSDNSanctionList()) -// .companyonuaesanctionlist(dto.getCompanyOnUAESanctionList()) -// .parentcompanycompliancerisk(dto.getParentCompanyComplianceRisk()) -// .build(); -// entityList.add(entity); -// } -// return entityList; -// } : 2026-01-16 Company Compliance 수집 API 분리로 인한 제거 - - private List makeCompanyDetailEntity(List dtoList) { - List companyDetailEntityList = new ArrayList<>(); - - if (dtoList == null || dtoList.isEmpty()) { - return companyDetailEntityList; - } - - for (CompanyDetailDto dto : dtoList) { - String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null; - - CompanyDetailEntity entity = CompanyDetailEntity.builder() - .dataSetVersion(safeGetString(datasetVersion)) - .owcode(safeGetString(dto.getOwcode())) - .shortcompanyname(safeGetString(dto.getShortCompanyName())) - .countryname(safeGetString(dto.getCountryName())) - .townname(safeGetString(dto.getTownName())) - .telephone(safeGetString(dto.getTelephone())) - .telex(safeGetString(dto.getTelex())) - .emailaddress(safeGetString(dto.getEmailaddress())) - .website(safeGetString(dto.getWebsite())) - .fullname(safeGetString(dto.getFullName())) - .careofcode(safeGetString(dto.getCareOfCode())) - .roomfloorbuilding1(safeGetString(dto.getRoomFloorBuilding1())) - .roomfloorbuilding2(safeGetString(dto.getRoomFloorBuilding2())) - .roomfloorbuilding3(safeGetString(dto.getRoomFloorBuilding3())) - .pobox(safeGetString(dto.getPoBox())) - .streetnumber(safeGetString(dto.getStreetNumber())) - .street(safeGetString(dto.getStreet())) - .prepostcode(safeGetString(dto.getPrePostcode())) - .postpostcode(safeGetString(dto.getPostPostcode())) - .nationalityofregistration(safeGetString(dto.getNationalityofRegistration())) - .nationalityofcontrol(safeGetString(dto.getNationalityofControl())) - .locationcode(safeGetString(dto.getLocationCode())) - .nationalityofregistrationcode(safeGetString(dto.getNationalityofRegistrationCode())) - .nationalityofcontrolcode(safeGetString(dto.getNationalityofControlCode())) - .lastchangedate(safeGetString(dto.getLastChangeDate())) - .parentcompany(safeGetString(dto.getParentCompany())) - .companystatus(safeGetString(dto.getCompanyStatus())) - .fulladdress(safeGetString(dto.getFullAddress())) - .facsimile(safeGetString(dto.getFacsimile())) - .foundeddate(safeGetString(dto.getFoundedDate())) - .build(); - - companyDetailEntityList.add(entity); - } - return companyDetailEntityList; } - /** - * 해시값을 비교하여 변경 여부를 판단합니다. - */ - private boolean isChanged(String previousHash, String currentHash) { - // DB 해시가 null인 경우 (첫 Insert)는 무조건 변경된 것으로 간주 - if (previousHash == null || previousHash.isEmpty()) { - return true; - } - - // 해시값이 다르면 변경된 것으로 간주 - return !Objects.equals(previousHash, currentHash); - } - - /** - * DTO 필드 값이 null 또는 빈 문자열(empty/blank)인 경우 null을 반환합니다. - * Entity의 String 필드가 null을 허용한다면 이 함수를 사용하세요. - */ private String safeGetString(String value) { if (value == null || value.trim().isEmpty()) { return null; diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/reader/ShipDetailDataReader.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/reader/ShipDetailDataReader.java index ee78b5d..1f22405 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/reader/ShipDetailDataReader.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/reader/ShipDetailDataReader.java @@ -1,18 +1,18 @@ package com.snp.batch.jobs.shipdetail.batch.reader; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.reader.BaseApiReader; -import com.snp.batch.common.util.JsonChangeDetector; import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailApiResponse; -import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailComparisonData; import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailDto; import com.snp.batch.jobs.shipdetail.batch.dto.ShipResultDto; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.reactive.function.client.WebClient; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; /** * 선박 상세 정보 Reader (v2.0 - Chunk 기반) @@ -35,15 +35,13 @@ import java.util.*; * - 신규: 100건씩 로드 → Process → Write (Chunk 1,718회) */ @Slf4j -public class ShipDetailDataReader extends BaseApiReader { +public class ShipDetailDataReader extends BaseApiReader { private final JdbcTemplate jdbcTemplate; private final ObjectMapper objectMapper; // 배치 처리 상태 private List allImoNumbers; - // DB 해시값을 저장할 맵 - private Map dbMasterHashes; private int currentBatchIndex = 0; private final int batchSize = 30; @@ -63,7 +61,6 @@ public class ShipDetailDataReader extends BaseApiReader { - Map map = new HashMap<>(); - while (rs.next()) { - map.put(rs.getString("imo_number"), rs.getString("ship_detail_hash")); - } - return map; - }); - - log.info("[{}] DB Master Hash 조회 완료. 총 {}건.", getReaderName(), dbMasterHashes.size()); - // API 통계 초기화 updateApiCallStats(totalBatches, 0); } @@ -119,7 +99,7 @@ public class ShipDetailDataReader extends BaseApiReader fetchNextBatch() throws Exception { + protected List fetchNextBatch() throws Exception { // 모든 배치 처리 완료 확인 if (allImoNumbers == null || currentBatchIndex >= allImoNumbers.size()) { @@ -149,50 +129,20 @@ public class ShipDetailDataReader extends BaseApiReader comparisonList = new ArrayList<>(); - // 응답 처리 if (response != null && response.getShipResult() != null) { - // ✨ ShipDetailComparisonData 생성 - for (ShipResultDto shipResult : response.getShipResult()) { - JsonNode rawShipDetailNode = shipResult.getShipDetailNode(); - if (rawShipDetailNode == null) continue; + List shipDetailDtoList = response.getShipResult().stream() + .map(ShipResultDto::getShipDetails) // result -> result.getShipDetail() + .filter(Objects::nonNull) // 데이터가 없는 경우 제외 + .collect(Collectors.toList()); - // 💡 1) DTO로 매핑하여 IMO와 구조화된 DTO 확보 - ShipDetailDto structuredDto = null; - try { - structuredDto = objectMapper.treeToValue(rawShipDetailNode, ShipDetailDto.class); - } catch (Exception e) { - log.error("ShipDetailDto 매핑 실패: {}", e.getMessage()); - continue; - } - - String imo = structuredDto.getIhslrorimoshipno(); - if (imo == null || imo.isEmpty()) continue; - - // 💡 2) 원본 JSON 문자열 생성 - String originalJsonString = rawShipDetailNode.toString(); - - // 💡 3) API response json을 Map 형태로 변환, 정렬 및 해시 생성 - Map currentMasterMap = JsonChangeDetector.jsonToSortedFilteredMap(originalJsonString); - String currentMasterHash = JsonChangeDetector.getSha256HashFromMap(currentMasterMap); - - // 💡 4) DB Master Hash 조회 (beforeFetch에서 로드된 맵 사용) - String previousMasterHash = dbMasterHashes.getOrDefault(imo, null); - - // 💡 5) ShipDetailComparisonData DTO 생성 - comparisonList.add(ShipDetailComparisonData.builder() - .imoNumber(imo) - .previousMasterHash(previousMasterHash) - .currentMasterMap(currentMasterMap) - .currentMasterHash(currentMasterHash) - .structuredDto(structuredDto) - .build()); - } + log.info("[{}] 배치 {}/{} 완료: {} 건 조회 (Result 그룹 수: {})", + getReaderName(), currentBatchNumber, totalBatches, + shipDetailDtoList.size(), response.getShipResult().size()); log.info("[{}] 배치 {}/{} 완료: {} 건 조회", - getReaderName(), currentBatchNumber, totalBatches, comparisonList.size()); + getReaderName(), currentBatchNumber, totalBatches, shipDetailDtoList.size()); // API 호출 통계 업데이트 updateApiCallStats(totalBatches, currentBatchNumber); @@ -202,7 +152,7 @@ public class ShipDetailDataReader extends BaseApiReader data) { + protected void afterFetch(List data) { if (data == null) { int totalBatches = (int) Math.ceil((double) allImoNumbers.size() / batchSize); log.info("[{}] 전체 {} 개 배치 처리 완료", getReaderName(), totalBatches); diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/reader/ShipDetailUpdateDataReader.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/reader/ShipDetailUpdateDataReader.java index 340ece3..d070ab4 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/reader/ShipDetailUpdateDataReader.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/reader/ShipDetailUpdateDataReader.java @@ -1,10 +1,12 @@ package com.snp.batch.jobs.shipdetail.batch.reader; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.snp.batch.common.batch.reader.BaseApiReader; -import com.snp.batch.common.util.JsonChangeDetector; -import com.snp.batch.jobs.shipdetail.batch.dto.*; +import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailApiResponse; +import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailDto; +import com.snp.batch.jobs.shipdetail.batch.dto.ShipDto; +import com.snp.batch.jobs.shipdetail.batch.dto.ShipResultDto; +import com.snp.batch.jobs.shipdetail.batch.dto.ShipUpdateApiResponse; import com.snp.batch.service.BatchApiLogService; import com.snp.batch.service.BatchDateService; import lombok.extern.slf4j.Slf4j; @@ -16,7 +18,7 @@ import java.util.*; import java.util.stream.Collectors; @Slf4j -public class ShipDetailUpdateDataReader extends BaseApiReader { +public class ShipDetailUpdateDataReader extends BaseApiReader { private final BatchDateService batchDateService; // ✨ BatchDateService 필드 추가 private final BatchApiLogService batchApiLogService; @@ -27,7 +29,6 @@ public class ShipDetailUpdateDataReader extends BaseApiReader allImoNumbers; // DB 해시값을 저장할 맵 - private Map dbMasterHashes; private int currentBatchIndex = 0; private final int batchSize = 20; public ShipDetailUpdateDataReader(WebClient webClient, JdbcTemplate jdbcTemplate, ObjectMapper objectMapper,BatchDateService batchDateService, BatchApiLogService batchApiLogService, String maritimeApiUrl) { @@ -55,19 +56,13 @@ public class ShipDetailUpdateDataReader extends BaseApiReader { - Map map = new HashMap<>(); - while (rs.next()) { - map.put(rs.getString("imo_number"), rs.getString("ship_detail_hash")); - } - return map; - }); - - log.info("[{}] DB Master Hash 조회 완료. 총 {}건.", getReaderName(), dbMasterHashes.size()); - // API 통계 초기화 updateApiCallStats(totalBatches, 0); } @@ -105,7 +87,7 @@ public class ShipDetailUpdateDataReader extends BaseApiReader fetchNextBatch() throws Exception { + protected List fetchNextBatch() throws Exception { // 모든 배치 처리 완료 확인 if (allImoNumbers == null || currentBatchIndex >= allImoNumbers.size()) { @@ -135,50 +117,16 @@ public class ShipDetailUpdateDataReader extends BaseApiReader comparisonList = new ArrayList<>(); - // 응답 처리 if (response != null && response.getShipResult() != null) { - // ✨ ShipDetailComparisonData 생성 - for (ShipResultDto shipResult : response.getShipResult()) { - JsonNode rawShipDetailNode = shipResult.getShipDetailNode(); - if (rawShipDetailNode == null) continue; - - // 💡 1) DTO로 매핑하여 IMO와 구조화된 DTO 확보 - ShipDetailDto structuredDto = null; - try { - structuredDto = objectMapper.treeToValue(rawShipDetailNode, ShipDetailDto.class); - } catch (Exception e) { - log.error("ShipDetailDto 매핑 실패: {}", e.getMessage()); - continue; - } - - String imo = structuredDto.getIhslrorimoshipno(); - if (imo == null || imo.isEmpty()) continue; - - // 💡 2) 원본 JSON 문자열 생성 - String originalJsonString = rawShipDetailNode.toString(); - - // 💡 3) API response json을 Map 형태로 변환, 정렬 및 해시 생성 - Map currentMasterMap = JsonChangeDetector.jsonToSortedFilteredMap(originalJsonString); - String currentMasterHash = JsonChangeDetector.getSha256HashFromMap(currentMasterMap); - - // 💡 4) DB Master Hash 조회 (beforeFetch에서 로드된 맵 사용) - String previousMasterHash = dbMasterHashes.getOrDefault(imo, null); - - // 💡 5) ShipDetailComparisonData DTO 생성 - comparisonList.add(ShipDetailComparisonData.builder() - .imoNumber(imo) - .previousMasterHash(previousMasterHash) - .currentMasterMap(currentMasterMap) - .currentMasterHash(currentMasterHash) - .structuredDto(structuredDto) - .build()); - } + List shipDetailDtoList = response.getShipResult().stream() + .map(ShipResultDto::getShipDetails) // result -> result.getShipDetail() + .filter(Objects::nonNull) // 데이터가 없는 경우 제외 + .collect(Collectors.toList()); log.info("[{}] 배치 {}/{} 완료: {} 건 조회", - getReaderName(), currentBatchNumber, totalBatches, comparisonList.size()); + getReaderName(), currentBatchNumber, totalBatches, shipDetailDtoList.size()); // API 호출 통계 업데이트 updateApiCallStats(totalBatches, currentBatchNumber); @@ -188,7 +136,7 @@ public class ShipDetailUpdateDataReader extends BaseApiReader data) { + protected void afterFetch(List data) { int totalBatches = (int) Math.ceil((double) allImoNumbers.size() / batchSize); try{ if (data == null) { diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/repository/ShipDetailRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/repository/ShipDetailRepositoryImpl.java index 88cfd22..193588c 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/repository/ShipDetailRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/repository/ShipDetailRepositoryImpl.java @@ -27,7 +27,7 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository getRowMapper() { - return new ShipDetailEntityRowMapper(); + return null; } @Override @@ -559,28 +283,7 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository toInsert = entities.stream() - .filter(e -> extractId(e) == null || !existsByImo(extractId(e))) - .toList(); - - List toUpdate = entities.stream() - .filter(e -> extractId(e) != null && existsByImo(extractId(e))) - .toList(); - - if (!toInsert.isEmpty()) { - batchInsert(toInsert); - } - - if (!toUpdate.isEmpty()) { - batchUpdate(toUpdate); - } - - log.info("{} 전체 저장 완료: 삽입={} 건, 수정={} 건", getEntityName(), toInsert.size(), toUpdate.size()); - + batchInsert(entities); } @Override @@ -603,7 +306,7 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository { - @Override - public ShipDetailEntity mapRow(ResultSet rs, int rowNum) throws SQLException { - ShipDetailEntity entity = ShipDetailEntity.builder() - .id(rs.getLong("id")) - .shipResultIndex(rs.getLong("shipresultindex")) - .ihslrorimoshipno(rs.getString("ihslrorimoshipno")) - .vesselId(rs.getString("vesselid")) - .maritimeMobileServiceIdentityMmsiNumber(rs.getString("maritimemobileserviceidentitymmsinumber")) - .shipName(rs.getString("shipname")) - .callSign(rs.getString("callsign")) - .flagName(rs.getString("flagname")) - .portOfRegistry(rs.getString("portofregistry")) - .classificationSociety(rs.getString("classificationsociety")) - .shipTypeLevel5(rs.getString("shiptypelevel5")) - .shipTypeLevel5SubType(rs.getString("shiptypelevel5subtype")) - .yearOfBuild(rs.getString("yearofbuild")) - .shipBuilder(rs.getString("shipbuilder")) - .lengthOverallLoa(rs.getDouble("lengthoverallloa")) - .breadthMoulded(rs.getDouble("breadthmoulded")) - .depth(rs.getDouble("depth")) - .draught(rs.getDouble("draught")) - .grossTonnage(rs.getString("grosstonnage")) - .deadWeight(rs.getString("deadweight")) - .teu(rs.getString("teu")) - .mainEngineType(rs.getString("mainenginetype")) - .batchFlag(rs.getString("batch_flag")) - .shipTypeLevel2(rs.getString("shiptypelevel2")) - .build(); - - // BaseEntity 필드 매핑 - Timestamp createdAt = rs.getTimestamp("batch_flag"); - if (createdAt != null) { - entity.setCreatedAt(createdAt.toLocalDateTime()); - } - - Timestamp updatedAt = rs.getTimestamp("updated_at"); - if (updatedAt != null) { - entity.setUpdatedAt(updatedAt.toLocalDateTime()); - } - - entity.setCreatedBy(rs.getString("created_by")); - entity.setUpdatedBy(rs.getString("updated_by")); - - return entity; - } - } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/repository/ShipDetailSql.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/repository/ShipDetailSql.java index 86d7d33..dc3412e 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/repository/ShipDetailSql.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/repository/ShipDetailSql.java @@ -1,638 +1,372 @@ package com.snp.batch.jobs.shipdetail.batch.repository; public class ShipDetailSql { + public static final String TARGET_SCHEMA = "t_snp_data"; public static String getOwnerHistorySql(){ return """ - INSERT INTO snp_data.ownerhistory( + INSERT INTO %s.ownerhistory( datasetversion, companystatus, effectivedate, lrno, "owner", - ownercode, "sequence", shipresultindex, vesselid, rowindex, batch_flag - )VALUES(?, ?, ?, ?, ?, ?, ?, nextval('snp_data.ownerhistory_index_seq'::regclass), ?, nextval('snp_data.ownerhistory_row_index_seq'::regclass), 'N') - ON CONFLICT (lrno, ownercode, effectivedate, sequence) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - companystatus = EXCLUDED.companystatus, - "owner" = EXCLUDED.owner, - batch_flag = 'N' - """; + ownercode, "sequence", + job_execution_id, created_by + )VALUES( + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getCrewListSql(){ - // DDL에 정의된 모든 컬럼 포함 - // DB 관리 컬럼 (shipresultindex, vesselid, rowindex)는 쿼리문에서 nextval 또는 특정 값으로 처리합니다. return """ - INSERT INTO snp_data.crewlist( + INSERT INTO %s.crewlist( datasetversion, id, lrno, shipname, crewlistdate, - nationality, totalcrew, totalratings, totalofficers, - totalcadets, totaltrainees, totalridingsquad, totalundeclared, vesselid + nationality, totalcrew, totalratings, totalofficers, totalcadets, + totaltrainees, totalridingsquad, totalundeclared, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - ON CONFLICT (lrno, shipname, nationality) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - crewlistdate = EXCLUDED.crewlistdate, - totalcrew = EXCLUDED.totalcrew, - totalratings = EXCLUDED.totalratings, - totalofficers = EXCLUDED.totalofficers, - totalcadets = EXCLUDED.totalcadets, - totaltrainees = EXCLUDED.totaltrainees, - totalridingsquad = EXCLUDED.totalridingsquad, - totalundeclared = EXCLUDED.totalundeclared, - vesselid = EXCLUDED.vesselid, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getStowageCommoditySql(){ - // DDL에 정의된 모든 컬럼 포함 return """ - INSERT INTO snp_data.stowagecommodity( + INSERT INTO %s.stowagecommodity( datasetversion, commoditycode, commoditydecode, lrno, "sequence", - stowagecode, stowagedecode, shipresultindex, vesselid, rowindex + stowagecode, stowagedecode, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.stowagecommodity_shipresultindex_seq'::regclass), - ?, -- vesselid는 LRNO와 동일하다고 가정하고 ?로 처리 - nextval('snp_data.stowagecommodity_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, sequence, commoditycode, stowagecode) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - commoditydecode = EXCLUDED.commoditydecode, - stowagedecode = EXCLUDED.stowagedecode, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getGroupBeneficialOwnerHistorySql(){ - // UNIQUE INDEX: groupbeneficialownerhistory_effectivedate_idx (effectivedate, groupbeneficialownercode, lrno, sequence) return """ - INSERT INTO snp_data.groupbeneficialownerhistory( - datasetversion, companystatus, effectivedate, groupbeneficialowner, - groupbeneficialownercode, lrno, "sequence", - shipresultindex, vesselid, rowindex + INSERT INTO %s.groupbeneficialownerhistory( + datasetversion, companystatus, effectivedate, groupbeneficialowner, groupbeneficialownercode, + lrno, "sequence", + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.groupbeneficialownerhistory_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.groupbeneficialownerhistory_rowindex_seq'::regclass) - ) - ON CONFLICT (effectivedate, groupbeneficialownercode, lrno, "sequence") - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - companystatus = EXCLUDED.companystatus, - groupbeneficialowner = EXCLUDED.groupbeneficialowner, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getShipManagerHistorySql(){ - // UNIQUE INDEX: shipmanagerhistory_effectivedate_idx (effectivedate, lrno, shipmanagercode, sequence) return """ - INSERT INTO snp_data.shipmanagerhistory( + INSERT INTO %s.shipmanagerhistory( datasetversion, companystatus, effectivedate, lrno, "sequence", - shipmanager, shipmanagercode, shipresultindex, vesselid, rowindex + shipmanager, shipmanagercode, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.shipmanagerhistory_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.shipmanagerhistory_rowindex_seq'::regclass) - ) - ON CONFLICT (effectivedate, lrno, shipmanagercode, "sequence") - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - companystatus = EXCLUDED.companystatus, - shipmanager = EXCLUDED.shipmanager, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getOperatorHistorySql(){ - // UNIQUE INDEX: operatorhistory_effectivedate_idx (effectivedate, lrno, operatorcode, sequence) return """ - INSERT INTO snp_data.operatorhistory( - datasetversion, companystatus, effectivedate, lrno, "operator", - operatorcode, "sequence", shipresultindex, vesselid, rowindex + INSERT INTO %s.operatorhistory( + datasetversion, companystatus, effectivedate, lrno, "operator", + operatorcode, "sequence", + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.operatorhistory_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.operatorhistory_rowindex_seq'::regclass) - ) - ON CONFLICT (effectivedate, lrno, operatorcode, "sequence") - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - companystatus = EXCLUDED.companystatus, - "operator" = EXCLUDED."operator", - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getTechnicalManagerHistorySql(){ - // UNIQUE INDEX: technicalmanagerhistory_effectivedate_idx (effectivedate, lrno, sequence, technicalmanagercode) return """ - INSERT INTO snp_data.technicalmanagerhistory( + INSERT INTO %s.technicalmanagerhistory( datasetversion, companystatus, effectivedate, lrno, "sequence", - technicalmanager, technicalmanagercode, shipresultindex, vesselid, rowindex + technicalmanager, technicalmanagercode, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.technicalmanagerhistory_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.technicalmanagerhistory_rowindex_seq'::regclass) - ) - ON CONFLICT (effectivedate, lrno, "sequence", technicalmanagercode) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - companystatus = EXCLUDED.companystatus, - technicalmanager = EXCLUDED.technicalmanager, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getBareBoatCharterHistorySql(){ - // UNIQUE INDEX: bareboatcharterhistory_lrno_idx (lrno, sequence, effectivedate, bbcharterercode) return """ - INSERT INTO snp_data.bareboatcharterhistory( + INSERT INTO %s.bareboatcharterhistory( datasetversion, lrno, "sequence", effectivedate, bbcharterercode, - bbcharterer, shipresultindex, vesselid, rowindex + bbcharterer, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, - nextval('snp_data.bareboatcharterhistory_shipresultindex_seq'::regclass), + ?, ?, ?, ?, ?, ?, - nextval('snp_data.bareboatcharterhistory_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, "sequence", effectivedate, bbcharterercode) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - bbcharterer = EXCLUDED.bbcharterer, - batch_flag = 'N' - """; + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getNameHistorySql(){ - // UNIQUE INDEX: namehistory_lrno_idx (lrno, sequence, effectivedate) return """ - INSERT INTO snp_data.namehistory( + INSERT INTO %s.namehistory( datasetversion, effectivedate, lrno, "sequence", vesselname, - shipresultindex, vesselid, rowindex + job_execution_id, created_by )VALUES( ?, ?, ?, ?, ?, - nextval('snp_data.namehistory_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.namehistory_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, "sequence", effectivedate) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - vesselname = EXCLUDED.vesselname, - batch_flag = 'N' - """; + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getFlagHistorySql(){ - // UNIQUE INDEX: flaghistory_flagcode_idx (flagcode, lrno) return """ - INSERT INTO snp_data.flaghistory( - datasetversion, effectivedate, flag, flagcode, lrno, "sequence", - shipresultindex, vesselid, rowindex + INSERT INTO %s.flaghistory( + datasetversion, effectivedate, flag, flagcode, lrno, + "sequence", + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, - nextval('snp_data.flaghistory_shipresultindex_seq'::regclass), + ?, ?, ?, ?, ?, ?, - nextval('snp_data.flaghistory_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, flagcode, effectivedate, sequence) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - flag = EXCLUDED.flag, - batch_flag = 'N' - """; + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getAdditionalInformationSql(){ - // SQL Table Name: additionalshipsdata - // UNIQUE INDEX: additionalshipsdata_lrno_idx (lrno) - // rowindex는 serial이므로 INSERT 목록에서 제외 return """ - INSERT INTO snp_data.additionalshipsdata( + INSERT INTO %s.additionalshipsdata( lrno, shipemail, waterdepthmax, drilldepthmax, drillbargeind, - productionvesselind, deckheatexchangerind, deckheatexchangermaterial, - tweendeckportable, tweendeckfixed, satcomid, satcomansback, - datasetversion + productionvesselind, deckheatexchangerind, deckheatexchangermaterial, tweendeckportable, tweendeckfixed, + satcomid, satcomansback, datasetversion, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - ON CONFLICT (lrno) - DO UPDATE SET - shipemail = EXCLUDED.shipemail, - waterdepthmax = EXCLUDED.waterdepthmax, - drilldepthmax = EXCLUDED.drilldepthmax, - drillbargeind = EXCLUDED.drillbargeind, - productionvesselind = EXCLUDED.productionvesselind, - deckheatexchangerind = EXCLUDED.deckheatexchangerind, - deckheatexchangermaterial = EXCLUDED.deckheatexchangermaterial, - tweendeckportable = EXCLUDED.tweendeckportable, - tweendeckfixed = EXCLUDED.tweendeckfixed, - satcomid = EXCLUDED.satcomid, - satcomansback = EXCLUDED.satcomansback, - datasetversion = EXCLUDED.datasetversion, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getPandIHistorySql(){ - // UNIQUE INDEX: pandihistory_lrno_idx (lrno, pandiclubcode, effectivedate) return """ - INSERT INTO snp_data.pandihistory( + INSERT INTO %s.pandihistory( datasetversion, lrno, "sequence", pandiclubcode, pandiclubdecode, - effectivedate, "source", shipresultindex, vesselid, rowindex + effectivedate, "source", + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.pandihistory_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.pandihistory_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, pandiclubcode, effectivedate) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - "sequence" = EXCLUDED."sequence", - pandiclubdecode = EXCLUDED.pandiclubdecode, - "source" = EXCLUDED."source", - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getCallSignAndMmsiHistorySql(){ - // UNIQUE INDEX: callsignandmmsihistory_lrno_idx (lrno, effectivedate, sequence) return """ - INSERT INTO snp_data.callsignandmmsihistory( + INSERT INTO %s.callsignandmmsihistory( datasetversion, lrno, "sequence", callsign, mmsi, - effectivedate, shipresultindex, vesselid, rowindex + effectivedate, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, - nextval('snp_data.callsignandmmsihistory_shipresultindex_seq'::regclass), + ?, ?, ?, ?, ?, ?, - nextval('snp_data.callsignandmmsihistory_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, effectivedate, "sequence") - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - callsign = EXCLUDED.callsign, - mmsi = EXCLUDED.mmsi, - batch_flag = 'N' - """; + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getIceClassSql(){ - // UNIQUE INDEX: iceclass_iceclasscode_idx (iceclasscode, lrno) return """ - INSERT INTO snp_data.iceclass( + INSERT INTO %s.iceclass( datasetversion, iceclass, iceclasscode, lrno, - shipresultindex, vesselid, rowindex + job_execution_id, created_by )VALUES( ?, ?, ?, ?, - nextval('snp_data.iceclass_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.iceclass_rowindex_seq'::regclass) - ) - ON CONFLICT (iceclasscode, lrno) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - iceclass = EXCLUDED.iceclass, - batch_flag = 'N' - """; + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getSafetyManagementCertificateHistorySql(){ - // SQL Table Name: safetymanagementcertificatehist - // UNIQUE INDEX: safetymanagementcertificatehist_lrno_idx (lrno, safetymanagementcertificatecompanycode, sequence) return """ - INSERT INTO snp_data.safetymanagementcertificatehist( - datasetversion, lrno, safetymanagementcertificateauditor, - safetymanagementcertificateconventionorvol, safetymanagementcertificatedateexpires, - safetymanagementcertificatedateissued, safetymanagementcertificatedoccompany, - safetymanagementcertificateflag, safetymanagementcertificateissuer, - safetymanagementcertificateotherdescription, safetymanagementcertificateshipname, - safetymanagementcertificateshiptype, safetymanagementcertificatesource, - safetymanagementcertificatecompanycode, "sequence", shipresultindex, - vesselid, rowindex + INSERT INTO %s.safetymanagementcertificatehist( + datasetversion, lrno, safetymanagementcertificateauditor, safetymanagementcertificateconventionorvol, safetymanagementcertificatedateexpires, + safetymanagementcertificatedateissued, safetymanagementcertificatedoccompany, safetymanagementcertificateflag, safetymanagementcertificateissuer, safetymanagementcertificateotherdescription, + safetymanagementcertificateshipname, safetymanagementcertificateshiptype, safetymanagementcertificatesource, safetymanagementcertificatecompanycode, "sequence", + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.safetymanagementcertificatehist_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.safetymanagementcertificatehist_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, "sequence") - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - safetymanagementcertificateauditor = EXCLUDED.safetymanagementcertificateauditor, - safetymanagementcertificateconventionorvol = EXCLUDED.safetymanagementcertificateconventionorvol, - safetymanagementcertificatedateexpires = EXCLUDED.safetymanagementcertificatedateexpires, - safetymanagementcertificatedateissued = EXCLUDED.safetymanagementcertificatedateissued, - safetymanagementcertificatedoccompany = EXCLUDED.safetymanagementcertificatedoccompany, - safetymanagementcertificateflag = EXCLUDED.safetymanagementcertificateflag, - safetymanagementcertificateissuer = EXCLUDED.safetymanagementcertificateissuer, - safetymanagementcertificateotherdescription = EXCLUDED.safetymanagementcertificateotherdescription, - safetymanagementcertificateshipname = EXCLUDED.safetymanagementcertificateshipname, - safetymanagementcertificateshiptype = EXCLUDED.safetymanagementcertificateshiptype, - safetymanagementcertificatesource = EXCLUDED.safetymanagementcertificatesource, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getClassHistorySql(){ - // UNIQUE INDEX: classhistory_classcode_idx (classcode, effectivedate, lrno, sequence) return """ - INSERT INTO snp_data.classhistory( + INSERT INTO %s.classhistory( datasetversion, "class", classcode, classindicator, currentindicator, - effectivedate, lrno, "sequence", classid, shipresultindex, - vesselid, rowindex + effectivedate, lrno, "sequence", classid, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.classhistory_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.classhistory_rowindex_seq'::regclass) - ) - ON CONFLICT (classcode, effectivedate, lrno, "sequence") - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - "class" = EXCLUDED."class", - classindicator = EXCLUDED.classindicator, - currentindicator = EXCLUDED.currentindicator, - classid = EXCLUDED.classid, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getSurveyDatesHistorySql(){ - // SQL Table Name: surveydates - // UNIQUE INDEX: surveydates_lrno_idx (lrno, classsocietycode) return """ - INSERT INTO snp_data.surveydates( - datasetversion, classsociety, classsocietycode, dockingsurvey, - lrno, specialsurvey, annualsurvey, continuousmachinerysurvey, - tailshaftsurvey, shipresultindex, vesselid, rowindex + INSERT INTO %s.surveydates( + datasetversion, classsociety, classsocietycode, dockingsurvey, lrno, + specialsurvey, annualsurvey, continuousmachinerysurvey, tailshaftsurvey, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.surveydates_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.surveydates_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, classsocietycode) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - classsociety = EXCLUDED.classsociety, - dockingsurvey = EXCLUDED.dockingsurvey, - specialsurvey = EXCLUDED.specialsurvey, - annualsurvey = EXCLUDED.annualsurvey, - continuousmachinerysurvey = EXCLUDED.continuousmachinerysurvey, - tailshaftsurvey = EXCLUDED.tailshaftsurvey, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getSurveyDatesHistoryUniqueSql(){ - // SQL Table Name: surveydateshistoryunique - // UNIQUE INDEX: surveydateshistoryunique_lrno_idx (lrno, classsocietycode, surveydate, surveytype) return """ - INSERT INTO snp_data.surveydateshistoryunique( - datasetversion, lrno, classsocietycode, surveydate, - surveytype, classsociety, shipresultindex, vesselid, rowindex + INSERT INTO %s.surveydateshistoryunique( + datasetversion, lrno, classsocietycode, surveydate, surveytype, + classsociety, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, - nextval('snp_data.surveydateshistoryunique_shipresultindex_seq'::regclass), + ?, ?, ?, ?, ?, ?, - nextval('snp_data.surveydateshistoryunique_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, classsocietycode, surveydate, surveytype) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - classsociety = EXCLUDED.classsociety, - batch_flag = 'N' - """; + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getSisterShipLinksSql(){ - // SQL Table Name: sistershiplinks - // UNIQUE INDEX: sistershiplinks_lrno_idx (lrno, linkedlrno) return """ - INSERT INTO snp_data.sistershiplinks( - datasetversion, lrno, linkedlrno, shipresultindex, - vesselid, rowindex + INSERT INTO %s.sistershiplinks( + datasetversion, lrno, linkedlrno, job_execution_id, created_by )VALUES( - ?, ?, ?, - nextval('snp_data.sistershiplinks_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.sistershiplinks_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, linkedlrno) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - batch_flag = 'N' - """; + ?, ?, ?, ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getStatusHistorySql(){ - // SQL Table Name: statushistory - // UNIQUE INDEX: statushistory_lrno_idx (lrno, sequence, statuscode, statusdate) return """ - INSERT INTO snp_data.statushistory( - datasetversion, lrno, "sequence", status, statuscode, statusdate, - shipresultindex, vesselid, rowindex + INSERT INTO %s.statushistory( + datasetversion, lrno, "sequence", status, statuscode, statusdate, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, - nextval('snp_data.statushistory_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.statushistory_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, "sequence", statuscode, statusdate) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - status = EXCLUDED.status, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getSpecialFeatureSql(){ - // SQL Table Name: specialfeature - // UNIQUE INDEX: specialfeature_lrno_idx (lrno, specialfeaturecode) return """ - INSERT INTO snp_data.specialfeature( + INSERT INTO %s.specialfeature( datasetversion, lrno, "sequence", specialfeature, specialfeaturecode, - shipresultindex, vesselid, rowindex + job_execution_id, created_by )VALUES( ?, ?, ?, ?, ?, - nextval('snp_data.specialfeature_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.specialfeature_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, sequence, specialfeaturecode) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - specialfeature = EXCLUDED.specialfeature, - batch_flag = 'N' - """; + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getThrustersSql(){ - // SQL Table Name: thrusters - // UNIQUE INDEX: thrusters_lrno_idx (lrno, sequence, thrustertypecode) return """ - INSERT INTO snp_data.thrusters( + INSERT INTO %s.thrusters( datasetversion, lrno, "sequence", thrustertype, thrustertypecode, - numberofthrusters, thrusterposition, thrusterbhp, thrusterkw, - typeofinstallation, shipresultindex, vesselid, rowindex + numberofthrusters, thrusterposition, thrusterbhp, thrusterkw, typeofinstallation, + job_execution_id, created_by )VALUES( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - nextval('snp_data.thrusters_shipresultindex_seq'::regclass), - ?, - nextval('snp_data.thrusters_rowindex_seq'::regclass) - ) - ON CONFLICT (lrno, "sequence") - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - thrustertype = EXCLUDED.thrustertype, - numberofthrusters = EXCLUDED.numberofthrusters, - thrusterposition = EXCLUDED.thrusterposition, - thrusterbhp = EXCLUDED.thrusterbhp, - thrusterkw = EXCLUDED.thrusterkw, - typeofinstallation = EXCLUDED.typeofinstallation, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getCompanyVesselRelationshipSql(){ return """ - INSERT INTO snp_data.companyvesselrelationships ( + INSERT INTO %s.companyvesselrelationships ( datasetversion, doccode, doccompany, groupbeneficialowner, groupbeneficialownercode, lrno, "operator", operatorcode, registeredowner, registeredownercode, - shipmanager, shipmanagercode, technicalmanager, technicalmanagercode, - docgroup, docgroupcode, operatorgroup, operatorgroupcode, - shipmanagergroup, shipmanagergroupcode, technicalmanagergroup, - technicalmanagergroupcode, vesselid + shipmanager, shipmanagercode, technicalmanager, technicalmanagercode, docgroup, + docgroupcode, operatorgroup, operatorgroupcode, shipmanagergroup, shipmanagergroupcode, + technicalmanagergroup, technicalmanagergroupcode, + job_execution_id, created_by ) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - ON CONFLICT (lrno) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - doccode = EXCLUDED.doccode, - doccompany = EXCLUDED.doccompany, - groupbeneficialowner = EXCLUDED.groupbeneficialowner, - groupbeneficialownercode = EXCLUDED.groupbeneficialownercode, - "operator" = EXCLUDED."operator", - operatorcode = EXCLUDED.operatorcode, - registeredowner = EXCLUDED.registeredowner, - registeredownercode = EXCLUDED.registeredownercode, - shipmanager = EXCLUDED.shipmanager, - shipmanagercode = EXCLUDED.shipmanagercode, - technicalmanager = EXCLUDED.technicalmanager, - technicalmanagercode = EXCLUDED.technicalmanagercode, - docgroup = EXCLUDED.docgroup, - docgroupcode = EXCLUDED.docgroupcode, - operatorgroup = EXCLUDED.operatorgroup, - operatorgroupcode = EXCLUDED.operatorgroupcode, - shipmanagergroup = EXCLUDED.shipmanagergroup, - shipmanagergroupcode = EXCLUDED.shipmanagergroupcode, - technicalmanagergroup = EXCLUDED.technicalmanagergroup, - technicalmanagergroupcode = EXCLUDED.technicalmanagergroupcode, - vesselid = EXCLUDED.vesselid, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getDarkActivityConfirmedSql(){ return """ - INSERT INTO snp_data.darkactivityconfirmed ( - datasetversion, lrno, mmsi, vessel_name, dark_hours, dark_activity, dark_status, - area_id, area_name, area_country, dark_time, dark_latitude, dark_longitude, dark_speed, - dark_heading, dark_draught, nextseen, nextseen_speed, nextseen_draught, - nextseen_heading, dark_reported_destination, last_port_of_call, - last_port_country_code,last_port_country, nextseen_latitude, nextseen_longitude, - nextseen_reported_destination, vesselid + INSERT INTO %s.darkactivityconfirmed ( + datasetversion, lrno, mmsi, vessel_name, dark_hours, + dark_activity, dark_status, area_id, area_name, area_country, + dark_time, dark_latitude, dark_longitude, dark_speed, dark_heading, + dark_draught, nextseen, nextseen_speed, nextseen_draught, nextseen_heading, + dark_reported_destination, last_port_of_call, last_port_country_code,last_port_country, nextseen_latitude, + nextseen_longitude, nextseen_reported_destination, + job_execution_id, created_by ) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,? - ) - ON CONFLICT (lrno, mmsi, dark_time, dark_status) - DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - vessel_name = EXCLUDED.vessel_name, - dark_hours = EXCLUDED.dark_hours, - dark_activity = EXCLUDED.dark_activity, - area_id = EXCLUDED.area_id, - area_name = EXCLUDED.area_name, - area_country = EXCLUDED.area_country, - dark_latitude = EXCLUDED.dark_latitude, - dark_longitude = EXCLUDED.dark_longitude, - dark_speed = EXCLUDED.dark_speed, - dark_heading = EXCLUDED.dark_heading, - dark_draught = EXCLUDED.dark_draught, - nextseen = EXCLUDED.nextseen, - nextseen_speed = EXCLUDED.nextseen_speed, - nextseen_draught = EXCLUDED.nextseen_draught, - nextseen_heading = EXCLUDED.nextseen_heading, - dark_reported_destination = EXCLUDED.dark_reported_destination, - last_port_of_call = EXCLUDED.last_port_of_call, - last_port_country_code = EXCLUDED.last_port_country_code, - last_port_country = EXCLUDED.last_port_country, - nextseen_latitude = EXCLUDED.nextseen_latitude, - nextseen_longitude = EXCLUDED.nextseen_longitude, - nextseen_reported_destination = EXCLUDED.nextseen_reported_destination, - vesselid = EXCLUDED.vesselid, - batch_flag = 'N' - """; + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } public static String getCompanyDetailSql() { return """ - INSERT INTO snp_data.tb_company_detail ( + INSERT INTO %s.tb_company_detail ( datasetversion, owcode, shortcompanyname, countryname, townname, telephone, telex, emailaddress, website, fullname, careofcode, roomfloorbuilding1, roomfloorbuilding2, roomfloorbuilding3, pobox, streetnumber, street, prepostcode, postpostcode, nationalityofregistration, nationalityofcontrol, locationcode, nationalityofregistrationcode, nationalityofcontrolcode, lastchangedate, - parentcompany, companystatus, fulladdress, facsimile, foundeddate + parentcompany, companystatus, fulladdress, facsimile, foundeddate, + job_execution_id, created_by ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ?, ?, ? - ) - ON CONFLICT (owcode) DO UPDATE SET - datasetversion = EXCLUDED.datasetversion, - shortcompanyname = EXCLUDED.shortcompanyname, - countryname = EXCLUDED.countryname, - townname = EXCLUDED.townname, - telephone = EXCLUDED.telephone, - telex = EXCLUDED.telex, - emailaddress = EXCLUDED.emailaddress, - website = EXCLUDED.website, - fullname = EXCLUDED.fullname, - careofcode = EXCLUDED.careofcode, - roomfloorbuilding1 = EXCLUDED.roomfloorbuilding1, - roomfloorbuilding2 = EXCLUDED.roomfloorbuilding2, - roomfloorbuilding3 = EXCLUDED.roomfloorbuilding3, - pobox = EXCLUDED.pobox, - streetnumber = EXCLUDED.streetnumber, - street = EXCLUDED.street, - prepostcode = EXCLUDED.prepostcode, - postpostcode = EXCLUDED.postpostcode, - nationalityofregistration = EXCLUDED.nationalityofregistration, - nationalityofcontrol = EXCLUDED.nationalityofcontrol, - locationcode = EXCLUDED.locationcode, - nationalityofregistrationcode = EXCLUDED.nationalityofregistrationcode, - nationalityofcontrolcode = EXCLUDED.nationalityofcontrolcode, - lastchangedate = EXCLUDED.lastchangedate, - parentcompany = EXCLUDED.parentcompany, - companystatus = EXCLUDED.companystatus, - fulladdress = EXCLUDED.fulladdress, - facsimile = EXCLUDED.facsimile, - foundeddate = EXCLUDED.foundeddate, - batch_flag = 'N'; - """; + ?, ?, ?, ?, ?, + ?, ? + ); + """.formatted(TARGET_SCHEMA); } } diff --git a/src/main/java/com/snp/batch/jobs/shipdetail/batch/writer/ShipDetailDataWriter.java b/src/main/java/com/snp/batch/jobs/shipdetail/batch/writer/ShipDetailDataWriter.java index 4c6d4ca..dabc3d7 100644 --- a/src/main/java/com/snp/batch/jobs/shipdetail/batch/writer/ShipDetailDataWriter.java +++ b/src/main/java/com/snp/batch/jobs/shipdetail/batch/writer/ShipDetailDataWriter.java @@ -7,6 +7,7 @@ import com.snp.batch.jobs.shipdetail.batch.repository.ShipDetailRepository; import com.snp.batch.jobs.shipdetail.batch.repository.ShipHashRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import java.util.*; import java.util.function.Function; @@ -17,7 +18,7 @@ import java.util.stream.Collectors; */ @Slf4j @Component -public class ShipDetailDataWriter extends BaseWriter { +public class ShipDetailDataWriter extends BaseWriter { private final ShipDetailRepository shipDetailRepository; private final ShipHashRepository shipHashRepository; @@ -29,185 +30,142 @@ public class ShipDetailDataWriter extends BaseWriter { } @Override - protected void writeItems(List items) throws Exception { + protected void writeItems(List items) throws Exception { - if (items.isEmpty()) { return; } + if (CollectionUtils.isEmpty(items)) { + return; + } -// log.info("선박 상세 정보 데이터 저장: {} 건", items.size()); + // 0. ShipDetailRepository (선박 제원정보 데이터) + shipDetailRepository.saveAllCoreData(items); - // 1. List -> 3가지 List 형태로 가공 - // 1-1. List (Core20 데이터 처리용) - List coreEntities = items.stream() - // ShipDetailUpdate DTO의 getShipDetailEntity() 메서드를 사용하여 바로 추출 - .map(ShipDetailUpdate::getShipDetailEntity) - .collect(Collectors.toList()); + for(ShipDetailEntity data : items) { + // 1. OwnerHistory + if (!CollectionUtils.isEmpty(data.getOwnerHistoryEntityList())) { + shipDetailRepository.saveAllOwnerHistoryData(data.getOwnerHistoryEntityList()); + } + // 2. CrewList + if (!CollectionUtils.isEmpty(data.getCrewListEntityList())) { + shipDetailRepository.saveAllCrewListData(data.getCrewListEntityList()); + } - // 1-2. List> -> List (OwnerHistory 데이터 처리용) - List ownerHistoriyListEntities = flattenEntities(items, ShipDetailUpdate::getOwnerHistoryEntityList); - List crewListEntities = flattenEntities(items, ShipDetailUpdate::getCrewListEntityList); - List stowageCommodityListEntities = flattenEntities(items, ShipDetailUpdate::getStowageCommodityEntityList); - List groupBeneficialOwnerHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getGroupBeneficialOwnerHistoryEntityList); - List shipManagerHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getShipManagerHistoryEntityList); - List operatorHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getOperatorHistoryEntityList); - List technicalManagerHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getTechnicalManagerHistoryEntityList); - List bareBoatCharterHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getBareBoatCharterHistoryEntityList); - List nameHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getNameHistoryEntityList); - List flagHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getFlagHistoryEntityList); - List additionalInformationListEntities = flattenEntities(items, ShipDetailUpdate::getAdditionalInformationEntityList); - List pandIHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getPandIHistoryEntityList); - List callSignAndMmsiHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getCallSignAndMmsiHistoryEntityList); - List iceClassListEntities = flattenEntities(items, ShipDetailUpdate::getIceClassEntityList); - List safetyManagementCertificateHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getSafetyManagementCertificateHistoryEntityList); - List classHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getClassHistoryEntityList); - List surveyDatesHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getSurveyDatesHistoryEntityList); - List surveyDatesHistoryUniqueListEntities = flattenEntities(items, ShipDetailUpdate::getSurveyDatesHistoryUniqueEntityList); - List sisterShipLinksListEntities = flattenEntities(items, ShipDetailUpdate::getSisterShipLinksEntityList); - List statusHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getStatusHistoryEntityList); - List specialFeatureListEntities = flattenEntities(items, ShipDetailUpdate::getSpecialFeatureEntityList); - List thrustersListEntities = flattenEntities(items, ShipDetailUpdate::getThrustersEntityList); - List darkActivityConfirmedEntities = flattenEntities(items, ShipDetailUpdate::getDarkActivityConfirmedEntityList); - List companyVesselRelationshipEntities = flattenEntities(items, ShipDetailUpdate::getCompanyVesselRelationshipEntityList); - List companyDetailEntities = flattenEntities(items, ShipDetailUpdate::getCompanyDetailEntityList); + // 3. StowageCommodity + if (!CollectionUtils.isEmpty(data.getStowageCommodityEntityList())) { + shipDetailRepository.saveAllStowageCommodityData(data.getStowageCommodityEntityList()); + } - // 1-3. List (Hash값 데이터 처리용) - List hashEntities = items.stream() - .map(item -> new ShipHashEntity(item.getImoNumber(), item.getNewMasterHash())) - .collect(Collectors.toList()); + // 4. GroupBeneficialOwnerHistory + if (!CollectionUtils.isEmpty(data.getGroupBeneficialOwnerHistoryEntityList())) { + shipDetailRepository.saveAllGroupBeneficialOwnerHistoryData(data.getGroupBeneficialOwnerHistoryEntityList()); + } - // 2. 각 Repository에 전달 - // ✅ 2-1. ShipDetailRepository (Core20 데이터) - log.debug("Core20 데이터 저장 시작: {} 건", coreEntities.size()); - shipDetailRepository.saveAllCoreData(coreEntities); + // 5. ShipManagerHistory + if (!CollectionUtils.isEmpty(data.getShipManagerHistoryEntityList())) { + shipDetailRepository.saveAllShipManagerHistoryData(data.getShipManagerHistoryEntityList()); + } - // ✅ 2-2. 추가적인 Array/List 데이터 - // OwnerHistory 저장 - log.debug("OwnerHistory 데이터 저장 시작: {} 건", ownerHistoriyListEntities.size()); - shipDetailRepository.saveAllOwnerHistoryData(ownerHistoriyListEntities); + // 6. OperatorHistory + if (!CollectionUtils.isEmpty(data.getOperatorHistoryEntityList())) { + shipDetailRepository.saveAllOperatorHistoryData(data.getOperatorHistoryEntityList()); + } - // CrewList 저장 - log.debug("CrewList 데이터 저장 시작: {} 건", crewListEntities.size()); - shipDetailRepository.saveAllCrewListData(crewListEntities); + // 7. TechnicalManagerHistory + if (!CollectionUtils.isEmpty(data.getTechnicalManagerHistoryEntityList())) { + shipDetailRepository.saveAllTechnicalManagerHistoryData(data.getTechnicalManagerHistoryEntityList()); + } - // StowageCommodity 저장 - log.debug("StowageCommodity 저장 시작: {} 건", stowageCommodityListEntities.size()); - shipDetailRepository.saveAllStowageCommodityData(stowageCommodityListEntities); + // 8. BareBoatCharterHistory + if (!CollectionUtils.isEmpty(data.getBareBoatCharterHistoryEntityList())) { + shipDetailRepository.saveAllBareBoatCharterHistoryData(data.getBareBoatCharterHistoryEntityList()); + } - // GroupBeneficialOwnerHistory 저장 - log.debug("GroupBeneficialOwnerHistory 저장 시작: {} 건", groupBeneficialOwnerHistoryListEntities.size()); - shipDetailRepository.saveAllGroupBeneficialOwnerHistoryData(groupBeneficialOwnerHistoryListEntities); + // 9. NameHistory + if (!CollectionUtils.isEmpty(data.getNameHistoryEntityList())) { + shipDetailRepository.saveAllNameHistoryData(data.getNameHistoryEntityList()); + } - // ShipManagerHistory 저장 - log.debug("ShipManagerHistory 저장 시작: {} 건", shipManagerHistoryListEntities.size()); - shipDetailRepository.saveAllShipManagerHistoryData(shipManagerHistoryListEntities); + // 10. FlagHistory + if (!CollectionUtils.isEmpty(data.getFlagHistoryEntityList())) { + shipDetailRepository.saveAllFlagHistoryData(data.getFlagHistoryEntityList()); + } - // OperatorHistory 저장 - log.debug("OperatorHistory 저장 시작: {} 건", operatorHistoryListEntities.size()); - shipDetailRepository.saveAllOperatorHistoryData(operatorHistoryListEntities); + // 11. AdditionalInformation + if (!CollectionUtils.isEmpty(data.getAdditionalInformationEntityList())) { + shipDetailRepository.saveAllAdditionalInformationData(data.getAdditionalInformationEntityList()); + } - // TechnicalManagerHistory 저장 - log.debug("TechnicalManagerHistory 저장 시작: {} 건", technicalManagerHistoryListEntities.size()); - shipDetailRepository.saveAllTechnicalManagerHistoryData(technicalManagerHistoryListEntities); + // 12. PandIHistory + if (!CollectionUtils.isEmpty(data.getPandIHistoryEntityList())) { + shipDetailRepository.saveAllPandIHistoryData(data.getPandIHistoryEntityList()); + } - // BareBoatCharterHistory 저장 - log.debug("BareBoatCharterHistory 저장 시작: {} 건", bareBoatCharterHistoryListEntities.size()); - shipDetailRepository.saveAllBareBoatCharterHistoryData(bareBoatCharterHistoryListEntities); + // 13. CallSignAndMmsiHistory + if (!CollectionUtils.isEmpty(data.getCallSignAndMmsiHistoryEntityList())) { + shipDetailRepository.saveAllCallSignAndMmsiHistoryData(data.getCallSignAndMmsiHistoryEntityList()); + } - // NameHistory 저장 - log.debug("NameHistory 저장 시작: {} 건", nameHistoryListEntities.size()); - shipDetailRepository.saveAllNameHistoryData(nameHistoryListEntities); + // 14. IceClass + if (!CollectionUtils.isEmpty(data.getIceClassEntityList())) { + shipDetailRepository.saveAllIceClassData(data.getIceClassEntityList()); + } - // FlagHistory 저장 - log.debug("FlagHistory 저장 시작: {} 건", flagHistoryListEntities.size()); - shipDetailRepository.saveAllFlagHistoryData(flagHistoryListEntities); + // 15. SafetyManagementCertificateHistory + if (!CollectionUtils.isEmpty(data.getSafetyManagementCertificateHistoryEntityList())) { + shipDetailRepository.saveAllSafetyManagementCertificateHistoryData(data.getSafetyManagementCertificateHistoryEntityList()); + } - // AdditionalInformation 저장 - log.debug("AdditionalInformation 저장 시작: {} 건", additionalInformationListEntities.size()); - shipDetailRepository.saveAllAdditionalInformationData(additionalInformationListEntities); + // 16. ClassHistory + if (!CollectionUtils.isEmpty(data.getClassHistoryEntityList())) { + shipDetailRepository.saveAllClassHistoryData(data.getClassHistoryEntityList()); + } - // PandIHistory 저장 - log.debug("PandIHistory 저장 시작: {} 건", pandIHistoryListEntities.size()); - shipDetailRepository.saveAllPandIHistoryData(pandIHistoryListEntities); + // 17. SurveyDatesHistory + if (!CollectionUtils.isEmpty(data.getSurveyDatesHistoryEntityList())) { + shipDetailRepository.saveAllSurveyDatesHistoryData(data.getSurveyDatesHistoryEntityList()); + } - // CallSignAndMmsiHistory 저장 - log.debug("CallSignAndMmsiHistory 저장 시작: {} 건", callSignAndMmsiHistoryListEntities.size()); - shipDetailRepository.saveAllCallSignAndMmsiHistoryData(callSignAndMmsiHistoryListEntities); + // 18. SurveyDatesHistoryUnique + if (!CollectionUtils.isEmpty(data.getSurveyDatesHistoryUniqueEntityList())) { + shipDetailRepository.saveAllSurveyDatesHistoryUniqueData(data.getSurveyDatesHistoryUniqueEntityList()); + } - // IceClass 저장 - log.debug("IceClass 저장 시작: {} 건", iceClassListEntities.size()); - shipDetailRepository.saveAllIceClassData(iceClassListEntities); + // 19. SisterShipLinks + if (!CollectionUtils.isEmpty(data.getSisterShipLinksEntityList())) { + shipDetailRepository.saveAllSisterShipLinksData(data.getSisterShipLinksEntityList()); + } - // SafetyManagementCertificateHistory 저장 - log.debug("SafetyManagementCertificateHistory 저장 시작: {} 건", safetyManagementCertificateHistoryListEntities.size()); - shipDetailRepository.saveAllSafetyManagementCertificateHistoryData(safetyManagementCertificateHistoryListEntities); + // 20. StatusHistory + if (!CollectionUtils.isEmpty(data.getStatusHistoryEntityList())) { + shipDetailRepository.saveAllStatusHistoryData(data.getStatusHistoryEntityList()); + } - // ClassHistory 저장 - log.debug("ClassHistory 저장 시작: {} 건", classHistoryListEntities.size()); - shipDetailRepository.saveAllClassHistoryData(classHistoryListEntities); + // 21. SpecialFeature + if (!CollectionUtils.isEmpty(data.getSpecialFeatureEntityList())) { + shipDetailRepository.saveAllSpecialFeatureData(data.getSpecialFeatureEntityList()); + } - // SurveyDatesHistory 저장 - log.debug("SurveyDatesHistory 저장 시작: {} 건", surveyDatesHistoryListEntities.size()); - shipDetailRepository.saveAllSurveyDatesHistoryData(surveyDatesHistoryListEntities); + // 22. Thrusters + if (!CollectionUtils.isEmpty(data.getThrustersEntityList())) { + shipDetailRepository.saveAllThrustersData(data.getThrustersEntityList()); + } - // SurveyDatesHistoryUnique 저장 - log.debug("SurveyDatesHistoryUnique 저장 시작: {} 건", surveyDatesHistoryUniqueListEntities.size()); - shipDetailRepository.saveAllSurveyDatesHistoryUniqueData(surveyDatesHistoryUniqueListEntities); + // 23. DarkActivityConfirmed + if (!CollectionUtils.isEmpty(data.getDarkActivityConfirmedEntityList())) { + shipDetailRepository.saveAllDarkActivityConfirmedData(data.getDarkActivityConfirmedEntityList()); + } - // SisterShipLinks 저장 - log.debug("SisterShipLinks 저장 시작: {} 건", sisterShipLinksListEntities.size()); - shipDetailRepository.saveAllSisterShipLinksData(sisterShipLinksListEntities); + // 24. CompanyVesselRelationship + if (!CollectionUtils.isEmpty(data.getCompanyVesselRelationshipEntityList())) { + shipDetailRepository.saveAllCompanyVesselRelationshipData(data.getCompanyVesselRelationshipEntityList()); + } - // StatusHistory 저장 - log.debug("StatusHistory 저장 시작: {} 건", statusHistoryListEntities.size()); - shipDetailRepository.saveAllStatusHistoryData(statusHistoryListEntities); + // 25. CompanyDetail (만약 ShipDetail과 연관관계가 있다면) + if (!CollectionUtils.isEmpty(data.getCompanyDetailEntityList())) { + shipDetailRepository.saveAllCompanyDetailData(data.getCompanyDetailEntityList()); + } - // SpecialFeature 저장 - log.debug("SpecialFeature 저장 시작: {} 건", specialFeatureListEntities.size()); - shipDetailRepository.saveAllSpecialFeatureData(specialFeatureListEntities); + } - // Thrusters 저장 - log.debug("Thrusters 저장 시작: {} 건", thrustersListEntities.size()); - shipDetailRepository.saveAllThrustersData(thrustersListEntities); - - // DarkActivityConfirmed 저장 - log.debug("DarkActivityConfirmed 저장 시작: {} 건", darkActivityConfirmedEntities.size()); - shipDetailRepository.saveAllDarkActivityConfirmedData(darkActivityConfirmedEntities); - - // CompanyVesselRelationship 저장 - log.debug("CompanyVesselRelationship 저장 시작: {} 건", companyVesselRelationshipEntities.size()); - shipDetailRepository.saveAllCompanyVesselRelationshipData(companyVesselRelationshipEntities); - - // CompanyVesselRelationship 저장 - log.debug("Company Detail 저장 시작: {} 건", companyDetailEntities.size()); - shipDetailRepository.saveAllCompanyDetailData(companyDetailEntities); - - // ✅ 2-3. ShipHashRepository (Hash값 데이터) - log.debug("Ship Hash 데이터 저장 시작: {} 건", hashEntities.size()); - shipHashRepository.saveAllData(hashEntities); - - log.info("선박 상세 정보 및 해시 데이터 저장 완료: {} 건", items.size()); - - } - - /** - * List에서 특정 Entity List들을 추출하여 하나의 List로 병합(Flatten)합니다. - * - * @param items 처리할 ShipDetailUpdate 리스트 - * @param entityListGetter ShipDetailUpdate에서 원하는 Entity List를 가져오는 Function (예: ShipDetailUpdate::getCrewListEntityList) - * @param Entity의 타입 - * @return 평탄화된 Entity 리스트 - */ - public static List flattenEntities( - List items, - Function> entityListGetter) { - - return items.stream() - // DTO에서 특정 Entity List를 추출합니다. - .map(entityListGetter) - // List가 null인 경우는 필터링하여 NPE를 방지합니다. - .filter(list -> list != null) - // List> 형태를 List 형태로 평탄화합니다. - .flatMap(Collection::stream) - .collect(Collectors.toList()); } } diff --git a/src/main/java/com/snp/batch/jobs/shipimport/batch/config/ShipImportJobConfig.java b/src/main/java/com/snp/batch/jobs/shipimport/batch/config/ShipImportJobConfig.java index dc704bd..bb1594f 100644 --- a/src/main/java/com/snp/batch/jobs/shipimport/batch/config/ShipImportJobConfig.java +++ b/src/main/java/com/snp/batch/jobs/shipimport/batch/config/ShipImportJobConfig.java @@ -1,15 +1,19 @@ package com.snp.batch.jobs.shipimport.batch.config; import com.snp.batch.common.batch.config.BaseJobConfig; +import com.snp.batch.jobs.common.batch.processor.FlagCodeDataProcessor; +import com.snp.batch.jobs.common.batch.reader.FlagCodeDataReader; import com.snp.batch.jobs.shipimport.batch.dto.ShipDto; import com.snp.batch.jobs.shipimport.batch.entity.ShipEntity; import com.snp.batch.jobs.shipimport.batch.processor.ShipDataProcessor; import com.snp.batch.jobs.shipimport.batch.reader.ShipDataReader; import com.snp.batch.jobs.shipimport.batch.repository.ShipRepository; import com.snp.batch.jobs.shipimport.batch.writer.ShipDataWriter; +import com.snp.batch.service.BatchApiLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; @@ -38,6 +42,11 @@ public class ShipImportJobConfig extends BaseJobConfig { private final ShipRepository shipRepository; private final WebClient maritimeApiWebClient; + private final ShipDataProcessor shipDataProcessor; + private final ShipDataReader shipDataReader; + private final BatchApiLogService batchApiLogService; + @Value("${app.batch.ship-api.url}") + private String maritimeApiUrl; @Value("${app.batch.chunk-size:1000}") private int chunkSize; @@ -50,10 +59,16 @@ public class ShipImportJobConfig extends BaseJobConfig { JobRepository jobRepository, PlatformTransactionManager transactionManager, ShipRepository shipRepository, - @Qualifier("maritimeApiWebClient") WebClient maritimeApiWebClient) { + @Qualifier("maritimeApiWebClient") WebClient maritimeApiWebClient, + ShipDataProcessor shipDataProcessor, + ShipDataReader shipDataReader, + BatchApiLogService batchApiLogService) { super(jobRepository, transactionManager); this.shipRepository = shipRepository; this.maritimeApiWebClient = maritimeApiWebClient; + this.shipDataProcessor = shipDataProcessor; + this.shipDataReader = shipDataReader; + this.batchApiLogService = batchApiLogService; } @Override @@ -68,12 +83,29 @@ public class ShipImportJobConfig extends BaseJobConfig { @Override protected ItemReader createReader() { - return new ShipDataReader(maritimeApiWebClient); + return shipDataReader; + } + + @Bean + @StepScope + public ShipDataReader shipDataReader( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, // SpEL로 ID 추출 + @Value("#{stepExecution.id}") Long stepExecutionId + ) { + ShipDataReader reader = new ShipDataReader(maritimeApiWebClient, batchApiLogService, maritimeApiUrl); + reader.setExecutionIds(jobExecutionId, stepExecutionId); // ID 세팅 + return reader; } @Override protected ItemProcessor createProcessor() { - return new ShipDataProcessor(shipRepository); + return shipDataProcessor; + } + @Bean + @StepScope + public ShipDataProcessor shipDataProcessor( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + return new ShipDataProcessor(jobExecutionId); } @Override diff --git a/src/main/java/com/snp/batch/jobs/shipimport/batch/processor/ShipDataProcessor.java b/src/main/java/com/snp/batch/jobs/shipimport/batch/processor/ShipDataProcessor.java index ff5d98a..55c2570 100644 --- a/src/main/java/com/snp/batch/jobs/shipimport/batch/processor/ShipDataProcessor.java +++ b/src/main/java/com/snp/batch/jobs/shipimport/batch/processor/ShipDataProcessor.java @@ -3,10 +3,8 @@ package com.snp.batch.jobs.shipimport.batch.processor; import com.snp.batch.common.batch.processor.BaseProcessor; import com.snp.batch.jobs.shipimport.batch.dto.ShipDto; import com.snp.batch.jobs.shipimport.batch.entity.ShipEntity; -import com.snp.batch.jobs.shipimport.batch.repository.ShipRepository; import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; +import org.springframework.beans.factory.annotation.Value; /** * ShipDto를 ShipEntity로 변환하는 Processor @@ -16,10 +14,10 @@ import java.time.LocalDateTime; @Slf4j public class ShipDataProcessor extends BaseProcessor { - private final ShipRepository shipRepository; + private final Long jobExecutionId; - public ShipDataProcessor(ShipRepository shipRepository) { - this.shipRepository = shipRepository; + public ShipDataProcessor(@Value("#{stepExecution.jobExecution.id}") Long jobExecutionId) { + this.jobExecutionId = jobExecutionId; } @Override @@ -29,33 +27,20 @@ public class ShipDataProcessor extends BaseProcessor { return null; // 스킵 } -// log.debug("선박 데이터 처리 중: IMO {}", item.getImoNumber()); + ShipEntity entity = ShipEntity.builder() + .imoNumber(item.getImoNumber()) + .coreShipInd(item.getCoreShipInd()) + .datasetVersion( + item.getDataSetVersion() != null ? + item.getDataSetVersion().getVersion() : null + ) + .jobExecutionId(jobExecutionId) + .createdBy("SYSTEM") + .build(); + + log.debug("선박 데이터 처리 완료: FlagCode={}", item.getImoNumber()); + + return entity; - // 중복 체크 및 업데이트 - return shipRepository.findByImoNumber(item.getImoNumber()) - .map(existingShip -> { - // 기존 데이터 업데이트 - existingShip.setCoreShipInd(item.getCoreShipInd()); - existingShip.setDatasetVersion( - item.getDataSetVersion() != null ? - item.getDataSetVersion().getVersion() : null - ); - existingShip.setImportDate(LocalDateTime.now()); - log.debug("기존 선박 업데이트: IMO {}", item.getImoNumber()); - return existingShip; - }) - .orElseGet(() -> { - // 신규 데이터 생성 - log.debug("신규 선박 추가: IMO {}", item.getImoNumber()); - return ShipEntity.builder() - .imoNumber(item.getImoNumber()) - .coreShipInd(item.getCoreShipInd()) - .datasetVersion( - item.getDataSetVersion() != null ? - item.getDataSetVersion().getVersion() : null - ) - .importDate(LocalDateTime.now()) - .build(); - }); } } diff --git a/src/main/java/com/snp/batch/jobs/shipimport/batch/reader/ShipDataReader.java b/src/main/java/com/snp/batch/jobs/shipimport/batch/reader/ShipDataReader.java index edee4aa..b2cfa47 100644 --- a/src/main/java/com/snp/batch/jobs/shipimport/batch/reader/ShipDataReader.java +++ b/src/main/java/com/snp/batch/jobs/shipimport/batch/reader/ShipDataReader.java @@ -1,12 +1,16 @@ package com.snp.batch.jobs.shipimport.batch.reader; import com.snp.batch.common.batch.reader.BaseApiReader; +import com.snp.batch.jobs.common.batch.dto.FlagCodeApiResponse; +import com.snp.batch.jobs.common.batch.dto.FlagCodeDto; import com.snp.batch.jobs.shipimport.batch.dto.ShipApiResponse; import com.snp.batch.jobs.shipimport.batch.dto.ShipDto; +import com.snp.batch.service.BatchApiLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.web.reactive.function.client.WebClient; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -15,9 +19,12 @@ import java.util.List; */ @Slf4j public class ShipDataReader extends BaseApiReader { - - public ShipDataReader(WebClient webClient) { + private final BatchApiLogService batchApiLogService; + String maritimeApiUrl; + public ShipDataReader(WebClient webClient, BatchApiLogService batchApiLogService, String maritimeApiUrl) { super(webClient); // BaseApiReader에 WebClient 전달 + this.batchApiLogService = batchApiLogService; + this.maritimeApiUrl = maritimeApiUrl; } // ======================================== @@ -28,30 +35,26 @@ public class ShipDataReader extends BaseApiReader { protected String getReaderName() { return "ShipDataReader"; } + @Override + protected String getApiPath() { + return "/MaritimeWCF/APSShipService.svc/RESTFul/GetAllIMONumbers"; + } @Override protected List fetchDataFromApi() { try { log.info("선박 API 호출 시작"); - ShipApiResponse response = webClient - .get() - .uri(uriBuilder -> uriBuilder - .path("/MaritimeWCF/APSShipService.svc/RESTFul/GetAllIMONumbers") - .queryParam("includeDeadShips", "0") - .build()) - .retrieve() - .bodyToMono(ShipApiResponse.class) - .block(); - - if (response != null && response.getShips() != null) { - log.info("API 응답 성공: 총 {} 척의 선박 데이터 수신", response.getShipCount()); - log.info("실제 데이터 건수: {} 건", response.getShips().size()); - return response.getShips(); - } else { - log.warn("API 응답이 null이거나 선박 데이터가 없습니다"); - return new ArrayList<>(); - } + // 공통 함수 호출 + List result = executeWrapperApiCall( + maritimeApiUrl, // 베이스 URL (필드 또는 설정값) + getApiPath(), // API 경로 + ShipApiResponse.class, // 응답을 받을 래퍼 클래스 + ShipApiResponse::getShips, // 리스트 추출 함수 (메서드 참조) + batchApiLogService // 로그 서비스 + ); + // 결과가 null일 경우 빈 리스트 반환 (안전장치) + return result != null ? result : Collections.emptyList(); } catch (Exception e) { log.error("선박 데이터 API 호출 실패", e); diff --git a/src/main/java/com/snp/batch/jobs/shipimport/batch/repository/ShipRepository.java b/src/main/java/com/snp/batch/jobs/shipimport/batch/repository/ShipRepository.java index 0120292..4f53cfe 100644 --- a/src/main/java/com/snp/batch/jobs/shipimport/batch/repository/ShipRepository.java +++ b/src/main/java/com/snp/batch/jobs/shipimport/batch/repository/ShipRepository.java @@ -3,32 +3,11 @@ package com.snp.batch.jobs.shipimport.batch.repository; import com.snp.batch.jobs.shipimport.batch.entity.ShipEntity; import java.util.List; -import java.util.Optional; /** * ShipEntity Repository 인터페이스 * 구현체: ShipRepositoryImpl (JdbcTemplate 기반) */ public interface ShipRepository { - - // CRUD 메서드 - Optional findById(Long id); - List findAll(); - long count(); - boolean existsById(Long id); - ShipEntity save(ShipEntity entity); - void saveAll(List entities); - void deleteById(Long id); - void deleteAll(); - - // 커스텀 메서드 - /** - * IMO 번호로 선박 조회 - */ - Optional findByImoNumber(String imoNumber); - - /** - * IMO 번호 존재 여부 확인 - */ - boolean existsByImoNumber(String imoNumber); + void saveAllShipData(List entities); } diff --git a/src/main/java/com/snp/batch/jobs/shipimport/batch/repository/ShipRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/shipimport/batch/repository/ShipRepositoryImpl.java index 14c8e94..9984bb6 100644 --- a/src/main/java/com/snp/batch/jobs/shipimport/batch/repository/ShipRepositoryImpl.java +++ b/src/main/java/com/snp/batch/jobs/shipimport/batch/repository/ShipRepositoryImpl.java @@ -8,10 +8,8 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.Optional; +import java.sql.Types; +import java.util.List; /** * ShipEntity Repository (JdbcTemplate 기반) @@ -26,7 +24,7 @@ public class ShipRepositoryImpl extends BaseJdbcRepository imp @Override protected String getTableName() { - return "snp_data.ship_data"; + return "t_snp_data.ship_data"; } @Override @@ -36,117 +34,59 @@ public class ShipRepositoryImpl extends BaseJdbcRepository imp @Override protected RowMapper getRowMapper() { - return new ShipEntityRowMapper(); + return null; } @Override protected Long extractId(ShipEntity entity) { - return entity.getId(); + return null; } @Override protected String getInsertSql() { - return """ - INSERT INTO snp_data.ship_data (imo_number, core_ship_ind, dataset_version, import_date, created_at, updated_at, created_by, updated_by) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - """; + return null; } @Override protected String getUpdateSql() { return """ - UPDATE snp_data.ship_data - SET core_ship_ind = ?, - dataset_version = ?, - import_date = ?, - updated_at = ?, - updated_by = ? - WHERE id = ? - """; + INSERT INTO %s( + imo_number, core_ship_ind, dataset_version, + job_execution_id, created_by + ) VALUES (?, ?, ?, ?, ?) + """.formatted(getTableName()); } @Override protected void setInsertParameters(PreparedStatement ps, ShipEntity entity) throws Exception { - int idx = 1; - ps.setString(idx++, entity.getImoNumber()); - ps.setString(idx++, entity.getCoreShipInd()); - ps.setString(idx++, entity.getDatasetVersion()); - ps.setTimestamp(idx++, entity.getImportDate() != null ? - Timestamp.valueOf(entity.getImportDate()) : Timestamp.valueOf(now())); - ps.setTimestamp(idx++, entity.getCreatedAt() != null ? - Timestamp.valueOf(entity.getCreatedAt()) : Timestamp.valueOf(now())); - ps.setTimestamp(idx++, entity.getUpdatedAt() != null ? - Timestamp.valueOf(entity.getUpdatedAt()) : Timestamp.valueOf(now())); - ps.setString(idx++, entity.getCreatedBy() != null ? entity.getCreatedBy() : "SYSTEM"); - ps.setString(idx++, entity.getUpdatedBy() != null ? entity.getUpdatedBy() : "SYSTEM"); + } @Override protected void setUpdateParameters(PreparedStatement ps, ShipEntity entity) throws Exception { int idx = 1; + ps.setString(idx++, entity.getImoNumber()); ps.setString(idx++, entity.getCoreShipInd()); ps.setString(idx++, entity.getDatasetVersion()); - ps.setTimestamp(idx++, entity.getImportDate() != null ? - Timestamp.valueOf(entity.getImportDate()) : Timestamp.valueOf(now())); - ps.setTimestamp(idx++, Timestamp.valueOf(now())); - ps.setString(idx++, entity.getUpdatedBy() != null ? entity.getUpdatedBy() : "SYSTEM"); - ps.setLong(idx++, entity.getId()); + ps.setObject(idx++, entity.getJobExecutionId(), Types.INTEGER); + ps.setString(idx++, entity.getCreatedBy()); } - // ==================== 커스텀 쿼리 메서드 ==================== - - /** - * IMO 번호로 선박 조회 - */ @Override - public Optional findByImoNumber(String imoNumber) { - String sql = "SELECT * FROM snp_data.ship_data WHERE imo_number = ?"; - return executeQueryForObject(sql, imoNumber); - } - - /** - * IMO 번호 존재 여부 확인 - */ - @Override - public boolean existsByImoNumber(String imoNumber) { - String sql = "SELECT COUNT(*) FROM snp_data.ship_data WHERE imo_number = ?"; - Long count = jdbcTemplate.queryForObject(sql, Long.class, imoNumber); - return count != null && count > 0; - } - - // ==================== RowMapper ==================== - - private static class ShipEntityRowMapper implements RowMapper { - @Override - public ShipEntity mapRow(ResultSet rs, int rowNum) throws SQLException { - ShipEntity entity = ShipEntity.builder() - .id(rs.getLong("id")) - .imoNumber(rs.getString("imo_number")) - .coreShipInd(rs.getString("core_ship_ind")) - .datasetVersion(rs.getString("dataset_version")) - .build(); - - // import_date 매핑 - Timestamp importDate = rs.getTimestamp("import_date"); - if (importDate != null) { - entity.setImportDate(importDate.toLocalDateTime()); - } - - // BaseEntity 필드 매핑 - Timestamp createdAt = rs.getTimestamp("created_at"); - if (createdAt != null) { - entity.setCreatedAt(createdAt.toLocalDateTime()); - } - - Timestamp updatedAt = rs.getTimestamp("updated_at"); - if (updatedAt != null) { - entity.setUpdatedAt(updatedAt.toLocalDateTime()); - } - - entity.setCreatedBy(rs.getString("created_by")); - entity.setUpdatedBy(rs.getString("updated_by")); - - return entity; + public void saveAllShipData(List entities) { + if (entities == null || entities.isEmpty()) { + return; } + jdbcTemplate.batchUpdate(getUpdateSql(), entities, entities.size(), + (ps, entity) -> { + try { + setUpdateParameters(ps, entity); + } catch (Exception e) { + log.error("배치 삽입 파라미터 설정 실패", e); + throw new RuntimeException(e); + } + }); + + log.info("{} 전체 저장 완료: {} 건", getEntityName(), entities.size()); } } diff --git a/src/main/java/com/snp/batch/jobs/shipimport/batch/writer/ShipDataWriter.java b/src/main/java/com/snp/batch/jobs/shipimport/batch/writer/ShipDataWriter.java index a702c9c..4890c75 100644 --- a/src/main/java/com/snp/batch/jobs/shipimport/batch/writer/ShipDataWriter.java +++ b/src/main/java/com/snp/batch/jobs/shipimport/batch/writer/ShipDataWriter.java @@ -23,6 +23,6 @@ public class ShipDataWriter extends BaseWriter { @Override protected void writeItems(List items) throws Exception { - shipRepository.saveAll(items); + shipRepository.saveAllShipData(items); } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 9227899..b0edeb6 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,6 +1,6 @@ spring: application: - name: snp-batch + name: snp-batch-validation # PostgreSQL Database Configuration datasource: @@ -22,12 +22,12 @@ spring: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true - default_schema: snp_data + default_schema: t_snp_data # Batch Configuration batch: jdbc: - table-prefix: "snp_data.batch_" + table-prefix: "t_snp_data.batch_" initialize-schema: never # Changed to 'never' as tables already exist job: enabled: false # Prevent auto-run on startup @@ -49,13 +49,13 @@ spring: org.quartz.threadPool.threadCount: 10 org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate - org.quartz.jobStore.tablePrefix: snp_data.QRTZ_ + org.quartz.jobStore.tablePrefix: t_snp_data.QRTZ_ org.quartz.jobStore.isClustered: false org.quartz.jobStore.misfireThreshold: 60000 # Server Configuration server: - port: 8081 + port: 8041 # port: 8041 servlet: context-path: /snp-api @@ -110,7 +110,7 @@ app: # Core20 캐시 테이블 설정 (환경별로 테이블/컬럼명이 다를 수 있음) core20: - schema: snp_data # 스키마명 + schema: t_snp_data # 스키마명 table: ship_detail_data # 테이블명 imo-column: ihslrorimoshipno # IMO/LRNO 컬럼명 (PK, NOT NULL) mmsi-column: maritimemobileserviceidentitymmsinumber # MMSI 컬럼명 (NULLABLE) @@ -119,7 +119,7 @@ app: partition: # 일별 파티션 테이블 목록 (네이밍: {table}_YYMMDD) daily-tables: - - schema: snp_data + - schema: t_snp_data table-name: ais_target partition-column: message_timestamp periods-ahead: 3 # 미리 생성할 일수 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index fc2c167..c7a8612 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,6 +1,6 @@ spring: application: - name: snp-batch + name: snp-batch-validation # PostgreSQL Database Configuration datasource: @@ -22,12 +22,12 @@ spring: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true - default_schema: snp_data + default_schema: t_snp_data # Batch Configuration batch: jdbc: - table-prefix: "snp_data.batch_" + table-prefix: "t_snp_data.batch_" initialize-schema: never # Changed to 'never' as tables already exist job: enabled: false # Prevent auto-run on startup @@ -49,13 +49,13 @@ spring: org.quartz.threadPool.threadCount: 10 org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate - org.quartz.jobStore.tablePrefix: snp_data.QRTZ_ + org.quartz.jobStore.tablePrefix: t_snp_data.QRTZ_ org.quartz.jobStore.isClustered: false org.quartz.jobStore.misfireThreshold: 60000 # Server Configuration server: - port: 9000 + port: 8041 # port: 8041 servlet: context-path: /snp-api @@ -112,7 +112,7 @@ app: # Core20 캐시 테이블 설정 (환경별로 테이블/컬럼명이 다를 수 있음) core20: - schema: snp_data # 스키마명 + schema: t_snp_data # 스키마명 table: ship_detail_data # 테이블명 imo-column: ihslrorimoshipno # IMO/LRNO 컬럼명 (PK, NOT NULL) mmsi-column: maritimemobileserviceidentitymmsinumber # MMSI 컬럼명 (NULLABLE) @@ -121,7 +121,7 @@ app: partition: # 일별 파티션 테이블 목록 (네이밍: {table}_YYMMDD) daily-tables: - - schema: snp_data + - schema: t_snp_data table-name: ais_target partition-column: message_timestamp periods-ahead: 3 # 미리 생성할 일수 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6df95eb..ca1c4b6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: application: - name: snp-batch + name: snp-batch-validation # PostgreSQL Database Configuration datasource: @@ -22,12 +22,12 @@ spring: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true - default_schema: snp_data + default_schema: t_snp_data # Batch Configuration batch: jdbc: - table-prefix: "snp_data.batch_" + table-prefix: "t_snp_data.batch_" initialize-schema: never # Changed to 'never' as tables already exist job: enabled: false # Prevent auto-run on startup @@ -49,13 +49,13 @@ spring: org.quartz.threadPool.threadCount: 10 org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate - org.quartz.jobStore.tablePrefix: snp_data.QRTZ_ + org.quartz.jobStore.tablePrefix: t_snp_data.QRTZ_ org.quartz.jobStore.isClustered: false org.quartz.jobStore.misfireThreshold: 60000 # Server Configuration server: - port: 8081 + port: 8041 servlet: context-path: /snp-api @@ -116,7 +116,7 @@ app: # Core20 캐시 테이블 설정 (환경별로 테이블/컬럼명이 다를 수 있음) core20: - schema: snp_data # 스키마명 + schema: t_snp_data # 스키마명 table: ship_detail_data # 테이블명 imo-column: ihslrorimoshipno # IMO/LRNO 컬럼명 (PK, NOT NULL) mmsi-column: maritimemobileserviceidentitymmsinumber # MMSI 컬럼명 (NULLABLE) @@ -125,7 +125,7 @@ app: partition: # 일별 파티션 테이블 목록 (네이밍: {table}_YYMMDD) daily-tables: - - schema: snp_data + - schema: t_snp_data table-name: ais_target partition-column: message_timestamp periods-ahead: 3 # 미리 생성할 일수