From e63607a69da70aebaa67046da6bcbf1985a4615a Mon Sep 17 00:00:00 2001 From: hyojin kim Date: Fri, 16 Jan 2026 17:12:04 +0900 Subject: [PATCH] =?UTF-8?q?:sparkles:=20Company=20Compliance=20=EC=88=98?= =?UTF-8?q?=EC=A7=91=20JOB=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...CompanyComplianceImportRangeJobConfig.java | 228 ++++++++++++++++++ .../batch/dto/CompanyComplianceDto.java | 61 +++++ .../batch/dto/ComplianceResponse.java | 17 -- .../batch/entity/CompanyComplianceEntity.java | 47 ++++ .../batch/entity/ComplianceEntity.java | 1 - .../CompanyComplianceDataProcessor.java | 36 +++ .../processor/ComplianceDataProcessor.java | 3 - .../CompanyComplianceDataRangeReader.java | 108 +++++++++ .../CompanyComplianceRepository.java | 9 + .../CompanyComplianceRepositoryImpl.java | 116 +++++++++ .../writer/CompanyComplianceDataWriter.java | 24 ++ 11 files changed, 629 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/config/CompanyComplianceImportRangeJobConfig.java create mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/dto/CompanyComplianceDto.java delete mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/dto/ComplianceResponse.java create mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/entity/CompanyComplianceEntity.java create mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/processor/CompanyComplianceDataProcessor.java create mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/reader/CompanyComplianceDataRangeReader.java create mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepository.java create mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepositoryImpl.java create mode 100644 src/main/java/com/snp/batch/jobs/compliance/batch/writer/CompanyComplianceDataWriter.java diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/config/CompanyComplianceImportRangeJobConfig.java b/src/main/java/com/snp/batch/jobs/compliance/batch/config/CompanyComplianceImportRangeJobConfig.java new file mode 100644 index 0000000..e66ed2c --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/config/CompanyComplianceImportRangeJobConfig.java @@ -0,0 +1,228 @@ +package com.snp.batch.jobs.compliance.batch.config; + +import com.snp.batch.common.batch.config.BaseMultiStepJobConfig; +import com.snp.batch.jobs.compliance.batch.dto.CompanyComplianceDto; +import com.snp.batch.jobs.compliance.batch.dto.ComplianceDto; +import com.snp.batch.jobs.compliance.batch.entity.CompanyComplianceEntity; +import com.snp.batch.jobs.compliance.batch.entity.ComplianceEntity; +import com.snp.batch.jobs.compliance.batch.processor.CompanyComplianceDataProcessor; +import com.snp.batch.jobs.compliance.batch.reader.CompanyComplianceDataRangeReader; +import com.snp.batch.jobs.compliance.batch.reader.ComplianceDataRangeReader; +import com.snp.batch.jobs.compliance.batch.writer.CompanyComplianceDataWriter; +import com.snp.batch.service.BatchApiLogService; +import com.snp.batch.service.BatchDateService; +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; +import org.springframework.batch.core.step.tasklet.Tasklet; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.repeat.RepeatStatus; +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.jdbc.core.JdbcTemplate; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.web.reactive.function.client.WebClient; + +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Map; + +@Slf4j +@Configuration +public class CompanyComplianceImportRangeJobConfig extends BaseMultiStepJobConfig { + + private final JdbcTemplate jdbcTemplate; + private final WebClient maritimeServiceApiWebClient; + private final CompanyComplianceDataRangeReader companyComplianceDataRangeReader; + private final CompanyComplianceDataProcessor companyComplianceDataProcessor; + private final CompanyComplianceDataWriter companyComplianceDataWriter; + private final BatchDateService batchDateService; + private final BatchApiLogService batchApiLogService; + + @Value("${app.batch.webservice-api.url}") + private String maritimeServiceApiUrl; + protected String getApiKey() {return "COMPANY_COMPLIANCE_IMPORT_API";} + protected String getBatchUpdateSql() { + return String.format("UPDATE SNP_DATA.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", getApiKey());} + + @Override + protected int getChunkSize() { + return 5000; + } + public CompanyComplianceImportRangeJobConfig( + JobRepository jobRepository, + PlatformTransactionManager transactionManager, + CompanyComplianceDataRangeReader companyComplianceDataRangeReader, + CompanyComplianceDataProcessor companyComplianceDataProcessor, + CompanyComplianceDataWriter companyComplianceDataWriter, + JdbcTemplate jdbcTemplate, + @Qualifier("maritimeServiceApiWebClient")WebClient maritimeServiceApiWebClient, + BatchDateService batchDateService, + BatchApiLogService batchApiLogService) { + super(jobRepository, transactionManager); + this.jdbcTemplate = jdbcTemplate; + this.companyComplianceDataRangeReader = companyComplianceDataRangeReader; + this.maritimeServiceApiWebClient = maritimeServiceApiWebClient; + this.companyComplianceDataProcessor = companyComplianceDataProcessor; + this.companyComplianceDataWriter = companyComplianceDataWriter; + this.batchDateService = batchDateService; + this.batchApiLogService = batchApiLogService; + } + + @Override + protected String getJobName() { + return "CompanyComplianceImportRangeJob"; + } + @Override + protected String getStepName() { + return "CompanyComplianceImportRangeStep"; + } + + @Override + protected Job createJobFlow(JobBuilder jobBuilder) { + return jobBuilder + .start(companyComplianceImportRangeStep()) // 1단계 실행 + .next(currentCompanyComplianceUpdateStep()) // 2단계 실행 (1단계 실패 시 실행 안 됨) + .next(companyComplianceHistoryValueChangeManageStep()) // 3단계 실행 (2단계 실패 시 실행 안 됨) + .next(companyComplianceLastExecutionUpdateStep()) // 4단계: 모두 완료 시, BATCH_LAST_EXECUTION 마지막 성공일자 업데이트 + .build(); + } + + @Override + protected ItemReader createReader() { + return companyComplianceDataRangeReader; + } + + @Bean + @StepScope + public CompanyComplianceDataRangeReader companyComplianceDataRangeReader( + @Value("#{stepExecution.jobExecution.id}") Long jobExecutionId, // SpEL로 ID 추출 + @Value("#{stepExecution.id}") Long stepExecutionId + ) { + CompanyComplianceDataRangeReader reader = new CompanyComplianceDataRangeReader(maritimeServiceApiWebClient, batchDateService, batchApiLogService, maritimeServiceApiUrl); + reader.setExecutionIds(jobExecutionId, stepExecutionId); // ID 세팅 + return reader; + } + @Override + protected ItemProcessor createProcessor() { + return companyComplianceDataProcessor; + } + + @Override + protected ItemWriter createWriter() { + return companyComplianceDataWriter; + } + + @Bean(name = "CompanyComplianceImportRangeJob") + public Job companyComplianceImportRangeJob() { + return job(); + } + + @Bean(name = "CompanyComplianceImportRangeStep") + public Step companyComplianceImportRangeStep() { + return step(); + } + /** + * 2단계: Current Company Compliance 업데이트 + */ + @Bean + public Tasklet currentCompanyComplianceUpsertTasklet() { + return (contribution, chunkContext) -> { + log.info(">>>>> Company Compliance Upsert 프로시저 호출 시작"); + + // PostgreSQL 기준 프로시저 호출 (CALL) + jdbcTemplate.execute("CALL new_snp.upsert_current_company_compliance()"); + + log.info(">>>>> Company Compliance Upsert 프로시저 호출 완료"); + return RepeatStatus.FINISHED; + }; + } + @Bean(name = "CurrentCompanyComplianceUpdateStep") + public Step currentCompanyComplianceUpdateStep() { + return new StepBuilder("CurrentCompanyComplianceUpdateStep", jobRepository) + .tasklet(currentCompanyComplianceUpsertTasklet(), transactionManager) + .build(); + } + + /** + * 3단계: Compliance History Value Change 관리 + */ + @Bean + public Tasklet companyComplianceHistoryValueChangeManageTasklet() { + return (contribution, chunkContext) -> { + log.info(">>>>> Company Compliance History Value Change Manage 프로시저 호출 시작"); + + // 1. 입력 포맷(UTC 'Z' 포함) 및 프로시저용 타겟 포맷 정의 + DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSX"); + DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + Map params = batchDateService.getDateRangeWithTimezoneParams(getApiKey()); + + String rawFromDate = params.get("fromDate"); + String rawToDate = params.get("toDate"); + + // 2. UTC 문자열 -> OffsetDateTime -> Asia/Seoul 변환 -> LocalDateTime 추출 + String startDt = convertToKstString(rawFromDate, inputFormatter, targetFormatter); + String endDt = convertToKstString(rawToDate, inputFormatter, targetFormatter); + + log.info("Company Compliance History Value Change Manage 프로시저 변수 (KST 변환): 시작일: {}, 종료일: {}", startDt, endDt); + + // 3. 프로시저 호출 (안전한 파라미터 바인딩 권장) + jdbcTemplate.update("CALL new_snp.company_compliance_history_value_change_manage(CAST(? AS TIMESTAMP), CAST(? AS TIMESTAMP))", startDt, endDt); + + log.info(">>>>> Company Compliance History Value Change Manage 프로시저 호출 완료"); + return RepeatStatus.FINISHED; + }; + } + /** + * UTC 문자열을 한국 시간(KST) 문자열로 변환하는 헬퍼 메소드 + */ + private String convertToKstString(String rawDate, DateTimeFormatter input, DateTimeFormatter target) { + if (rawDate == null) return null; + + // 1. 문자열을 OffsetDateTime으로 파싱 (Z를 인식하여 UTC 시간으로 인지함) + return OffsetDateTime.parse(rawDate, input) + // 2. 시간대를 서울(+09:00)로 변경 (값이 9시간 더해짐) + .atZoneSameInstant(ZoneId.of("Asia/Seoul")) + // 3. 프로시저 형식에 맞게 포맷팅 + .format(target); + } + @Bean(name = "CompanyComplianceHistoryValueChangeManageStep") + public Step companyComplianceHistoryValueChangeManageStep() { + return new StepBuilder("CompanyComplianceHistoryValueChangeManageStep", jobRepository) + .tasklet(companyComplianceHistoryValueChangeManageTasklet(), transactionManager) + .build(); + } + + /** + * 4단계: 모든 스텝 성공 시 배치 실행 로그(날짜) 업데이트 + */ + @Bean + public Tasklet companyComplianceLastExecutionUpdateTasklet() { + return (contribution, chunkContext) -> { + log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작"); + + jdbcTemplate.execute(getBatchUpdateSql()); + + log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료"); + return RepeatStatus.FINISHED; + }; + } + @Bean(name = "CompanyComplianceLastExecutionUpdateStep") + public Step companyComplianceLastExecutionUpdateStep() { + return new StepBuilder("CompanyComplianceLastExecutionUpdateStep", jobRepository) + .tasklet(companyComplianceLastExecutionUpdateTasklet(), transactionManager) + .build(); + } + + +} diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/dto/CompanyComplianceDto.java b/src/main/java/com/snp/batch/jobs/compliance/batch/dto/CompanyComplianceDto.java new file mode 100644 index 0000000..ecbc837 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/dto/CompanyComplianceDto.java @@ -0,0 +1,61 @@ +package com.snp.batch.jobs.compliance.batch.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CompanyComplianceDto { + @JsonProperty("owcode") + private String owcode; + + @JsonProperty("lastUpdated") + private String lastUpdated; + + @JsonProperty("companyOverallComplianceStatus") + private Integer companyOverallComplianceStatus; + + @JsonProperty("companyOnAustralianSanctionList") + private Integer companyOnAustralianSanctionList; + + @JsonProperty("companyOnBESSanctionList") + private Integer companyOnBESSanctionList; + + @JsonProperty("companyOnCanadianSanctionList") + private Integer companyOnCanadianSanctionList; + + @JsonProperty("companyInOFACSanctionedCountry") + private Integer companyInOFACSanctionedCountry; + + @JsonProperty("companyInFATFJurisdiction") + private Integer companyInFATFJurisdiction; + + @JsonProperty("companyOnEUSanctionList") + private Integer companyOnEUSanctionList; + + @JsonProperty("companyOnOFACSanctionList") + private Integer companyOnOFACSanctionList; + + @JsonProperty("companyOnOFACNONSDNSanctionList") + private Integer companyOnOFACNONSDNSanctionList; + + @JsonProperty("companyOnOFACSSISanctionList") + private Integer companyOnOFACSSISanctionList; + + @JsonProperty("parentCompanyNonCompliance") + private Integer parentCompanyNonCompliance; + + @JsonProperty("companyOnSwissSanctionList") + private Integer companyOnSwissSanctionList; + + @JsonProperty("companyOnUAESanctionList") + private Integer companyOnUAESanctionList; + + @JsonProperty("companyOnUNSanctionList") + private Integer companyOnUNSanctionList; +} diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/dto/ComplianceResponse.java b/src/main/java/com/snp/batch/jobs/compliance/batch/dto/ComplianceResponse.java deleted file mode 100644 index c4b8600..0000000 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/dto/ComplianceResponse.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.snp.batch.jobs.compliance.batch.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ComplianceResponse { - - private List complianceDtoList; -} diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/entity/CompanyComplianceEntity.java b/src/main/java/com/snp/batch/jobs/compliance/batch/entity/CompanyComplianceEntity.java new file mode 100644 index 0000000..1ea272c --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/entity/CompanyComplianceEntity.java @@ -0,0 +1,47 @@ +package com.snp.batch.jobs.compliance.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; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class CompanyComplianceEntity extends BaseEntity { + private String owcode; + + private String lastUpdated; + + private Integer companyOverallComplianceStatus; + + private Integer companyOnAustralianSanctionList; + + private Integer companyOnBESSanctionList; + + private Integer companyOnCanadianSanctionList; + + private Integer companyInOFACSanctionedCountry; + + private Integer companyInFATFJurisdiction; + + private Integer companyOnEUSanctionList; + + private Integer companyOnOFACSanctionList; + + private Integer companyOnOFACNONSDNSanctionList; + + private Integer companyOnOFACSSISanctionList; + + private Integer parentCompanyNonCompliance; + + private Integer companyOnSwissSanctionList; + + private Integer companyOnUAESanctionList; + + private Integer companyOnUNSanctionList; +} diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/entity/ComplianceEntity.java b/src/main/java/com/snp/batch/jobs/compliance/batch/entity/ComplianceEntity.java index a8d392f..e363042 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/entity/ComplianceEntity.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/entity/ComplianceEntity.java @@ -1,6 +1,5 @@ package com.snp.batch.jobs.compliance.batch.entity; -import com.fasterxml.jackson.annotation.JsonProperty; import com.snp.batch.common.batch.entity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Data; 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 new file mode 100644 index 0000000..ca9d012 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/processor/CompanyComplianceDataProcessor.java @@ -0,0 +1,36 @@ +package com.snp.batch.jobs.compliance.batch.processor; + +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.stereotype.Component; + +@Slf4j +@Component +public class CompanyComplianceDataProcessor extends BaseProcessor { + + @Override + protected CompanyComplianceEntity processItem(CompanyComplianceDto dto) throws Exception { + + CompanyComplianceEntity entity = CompanyComplianceEntity.builder() + .owcode(dto.getOwcode()) + .lastUpdated(dto.getLastUpdated()) + .companyOverallComplianceStatus(dto.getCompanyOverallComplianceStatus()) + .companyOnAustralianSanctionList(dto.getCompanyOnAustralianSanctionList()) + .companyOnBESSanctionList(dto.getCompanyOnBESSanctionList()) + .companyOnCanadianSanctionList(dto.getCompanyOnCanadianSanctionList()) + .companyInOFACSanctionedCountry(dto.getCompanyInOFACSanctionedCountry()) + .companyInFATFJurisdiction(dto.getCompanyInFATFJurisdiction()) + .companyOnEUSanctionList(dto.getCompanyOnEUSanctionList()) + .companyOnOFACSanctionList(dto.getCompanyOnOFACSanctionList()) + .companyOnOFACNONSDNSanctionList(dto.getCompanyOnOFACNONSDNSanctionList()) + .companyOnOFACSSISanctionList(dto.getCompanyOnOFACSSISanctionList()) + .parentCompanyNonCompliance(dto.getParentCompanyNonCompliance()) + .companyOnSwissSanctionList(dto.getCompanyOnSwissSanctionList()) + .companyOnUAESanctionList(dto.getCompanyOnUAESanctionList()) + .companyOnUNSanctionList(dto.getCompanyOnUNSanctionList()) + .build(); + return entity; + } +} diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/processor/ComplianceDataProcessor.java b/src/main/java/com/snp/batch/jobs/compliance/batch/processor/ComplianceDataProcessor.java index e222b04..5245205 100644 --- a/src/main/java/com/snp/batch/jobs/compliance/batch/processor/ComplianceDataProcessor.java +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/processor/ComplianceDataProcessor.java @@ -11,7 +11,6 @@ import org.springframework.stereotype.Component; public class ComplianceDataProcessor extends BaseProcessor { @Override protected ComplianceEntity processItem(ComplianceDto dto) throws Exception { - log.debug("AIS 최신 항적 데이터 처리 시작: imoNumber={}", dto.getLrimoShipNo()); ComplianceEntity entity = ComplianceEntity.builder() // 1. Primary Keys @@ -53,8 +52,6 @@ public class ComplianceDataProcessor extends BaseProcessor { + private final BatchDateService batchDateService; // ✨ BatchDateService 필드 추가 + private final BatchApiLogService batchApiLogService; + String maritimeServiceApiUrl; + private List allData; + private int currentBatchIndex = 0; + private final int batchSize = 5000; + + public CompanyComplianceDataRangeReader(WebClient webClient, BatchDateService batchDateService, BatchApiLogService batchApiLogService, String maritimeServiceApiUrl) { + super(webClient); + this.batchDateService = batchDateService; + this.batchApiLogService = batchApiLogService; + this.maritimeServiceApiUrl = maritimeServiceApiUrl; + enableChunkMode(); + } + @Override + protected String getReaderName() { + return "CompanyComplianceDataRangeReader"; + } + @Override + protected String getApiPath() { + return "/RiskAndCompliance/UpdatedCompanyComplianceList"; + } + protected String getApiKey() { + return "COMPANY_COMPLIANCE_IMPORT_API"; + } + @Override + protected void resetCustomState() { + this.currentBatchIndex = 0; + this.allData = null; + } + + @Override + protected List fetchNextBatch() throws Exception{ + // 모든 배치 처리 완료 확인 + if (allData == null) { + allData = callApiWithBatch(); + + if (allData == null || allData.isEmpty()) { + log.warn("[{}] 조회된 데이터 없음 → 종료", getReaderName()); + return null; + } + + log.info("[{}] 총 {}건 데이터 조회됨. batchSize = {}", getReaderName(), allData.size(), batchSize); + } + + // 2) 이미 끝까지 읽었으면 종료 + if (currentBatchIndex >= allData.size()) { + log.info("[{}] 모든 배치 처리 완료", getReaderName()); + return null; + } + + // 3) 이번 배치의 end 계산 + int end = Math.min(currentBatchIndex + batchSize, allData.size()); + + // 4) 현재 batch 리스트 잘라서 반환 + List batch = allData.subList(currentBatchIndex, end); + + int batchNum = (currentBatchIndex / batchSize) + 1; + int totalBatches = (int) Math.ceil((double) allData.size() / batchSize); + + log.info("[{}] 배치 {}/{} 처리 중: {}건", getReaderName(), batchNum, totalBatches, batch.size()); + + // 다음 batch 인덱스 이동 + currentBatchIndex = end; + updateApiCallStats(totalBatches, batchNum); + + return batch; + } + + @Override + protected void afterFetch(List data){ + try{ + if (data == null) { + log.info("[{}] 배치 처리 성공", getReaderName()); + } + }catch (Exception e){ + log.info("[{}] 배치 처리 실패", getReaderName()); + log.info("[{}] API 호출 종료", getReaderName()); + } + } + private List callApiWithBatch() { + Map params = batchDateService.getDateRangeWithTimezoneParams(getApiKey()); + // 부모 클래스의 공통 모듈 호출 (단 한 줄로 처리 가능) + return executeListApiCall( + maritimeServiceApiUrl, + getApiPath(), + params, + new ParameterizedTypeReference>() {}, + batchApiLogService + ); + } + +} diff --git a/src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepository.java b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepository.java new file mode 100644 index 0000000..b5fbf55 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepository.java @@ -0,0 +1,9 @@ +package com.snp.batch.jobs.compliance.batch.repository; + +import com.snp.batch.jobs.compliance.batch.entity.CompanyComplianceEntity; + +import java.util.List; + +public interface CompanyComplianceRepository { + void saveCompanyComplianceAll(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 new file mode 100644 index 0000000..a8571ae --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/repository/CompanyComplianceRepositoryImpl.java @@ -0,0 +1,116 @@ +package com.snp.batch.jobs.compliance.batch.repository; + +import com.snp.batch.common.batch.repository.BaseJdbcRepository; +import com.snp.batch.jobs.compliance.batch.entity.CompanyComplianceEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.sql.PreparedStatement; +import java.sql.Types; +import java.util.List; + +@Slf4j +@Repository("CompanyComplianceRepository") +public class CompanyComplianceRepositoryImpl extends BaseJdbcRepository implements CompanyComplianceRepository{ + public CompanyComplianceRepositoryImpl(JdbcTemplate jdbcTemplate) { + super(jdbcTemplate); + } + + @Override + protected String getTableName() { + return null; + } + + @Override + protected RowMapper getRowMapper() { + return null; + } + + @Override + protected Long extractId(CompanyComplianceEntity entity) { + return null; + } + + @Override + protected String getInsertSql() { + return null; + } + + @Override + protected String getUpdateSql() { + return """ + INSERT INTO new_snp.tb_company_compliance_hstry( + owcode, lastupdated, + companyoverallcompliancestatus, companyonaustraliansanctionlist, companyonbessanctionlist, companyoncanadiansanctionlist, companyinofacsanctionedcountry, + companyinfatfjurisdiction, companyoneusanctionlist, companyonofacsanctionlist, companyonofacnonsdnsanctionlist, companyonofacssilist, + companyonswisssanctionlist, companyonuaesanctionlist, companyonunsanctionlist, parentcompanycompliancerisk + )VALUES( + ?, ?::timestamp, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + )ON CONFLICT (owcode, lastupdated) + DO UPDATE SET + companyoverallcompliancestatus = EXCLUDED.companyoverallcompliancestatus, + companyonaustraliansanctionlist = EXCLUDED.companyonaustraliansanctionlist, + companyonbessanctionlist = EXCLUDED.companyonbessanctionlist, + companyoncanadiansanctionlist = EXCLUDED.companyoncanadiansanctionlist, + companyinofacsanctionedcountry = EXCLUDED.companyinofacsanctionedcountry, + companyinfatfjurisdiction = EXCLUDED.companyinfatfjurisdiction, + companyoneusanctionlist = EXCLUDED.companyoneusanctionlist, + companyonofacsanctionlist = EXCLUDED.companyonofacsanctionlist, + companyonofacnonsdnsanctionlist = EXCLUDED.companyonofacnonsdnsanctionlist, + companyonofacssilist = EXCLUDED.companyonofacssilist, + companyonswisssanctionlist = EXCLUDED.companyonswisssanctionlist, + companyonuaesanctionlist = EXCLUDED.companyonuaesanctionlist, + companyonunsanctionlist = EXCLUDED.companyonunsanctionlist, + parentcompanycompliancerisk = EXCLUDED.parentcompanycompliancerisk + """; + } + + @Override + protected void setInsertParameters(PreparedStatement ps, CompanyComplianceEntity entity) throws Exception { + } + + @Override + protected void setUpdateParameters(PreparedStatement ps, CompanyComplianceEntity entity) throws Exception { + int idx = 1; + ps.setString(idx++, entity.getOwcode()); + ps.setString(idx++, entity.getLastUpdated()); + ps.setObject(idx++, entity.getCompanyOverallComplianceStatus(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnAustralianSanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnBESSanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnCanadianSanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyInOFACSanctionedCountry(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyInFATFJurisdiction(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnEUSanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnOFACSanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnOFACNONSDNSanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnOFACSSISanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnSwissSanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnUAESanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getCompanyOnUNSanctionList(), Types.INTEGER); + ps.setObject(idx++, entity.getParentCompanyNonCompliance(), Types.INTEGER); + } + + @Override + protected String getEntityName() { + return "CompanyComplianceEntity"; + } + + @Override + public void saveCompanyComplianceAll(List items) { + if (items == null || items.isEmpty()) { + return; + } + jdbcTemplate.batchUpdate(getUpdateSql(), 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/writer/CompanyComplianceDataWriter.java b/src/main/java/com/snp/batch/jobs/compliance/batch/writer/CompanyComplianceDataWriter.java new file mode 100644 index 0000000..7127ab6 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/compliance/batch/writer/CompanyComplianceDataWriter.java @@ -0,0 +1,24 @@ +package com.snp.batch.jobs.compliance.batch.writer; + +import com.snp.batch.common.batch.writer.BaseWriter; +import com.snp.batch.jobs.compliance.batch.entity.CompanyComplianceEntity; +import com.snp.batch.jobs.compliance.batch.repository.CompanyComplianceRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +public class CompanyComplianceDataWriter extends BaseWriter { + private final CompanyComplianceRepository complianceRepository; + public CompanyComplianceDataWriter(CompanyComplianceRepository complianceRepository) { + super("CompanyComplianceRepository"); + this.complianceRepository = complianceRepository; + } + + @Override + protected void writeItems(List items) throws Exception { + complianceRepository.saveCompanyComplianceAll(items); + } +}