Skip to content

Commit

Permalink
Merge pull request #96 from cgk95/feat/#90
Browse files Browse the repository at this point in the history
Feature :: 위해우려제품 상세 정보를 초록누리 API 로 불러와서 저장하기
  • Loading branch information
chan99k authored Jan 26, 2024
2 parents 337d30d + d498693 commit f35b3f7
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.kernel360.modulebatch.concernedproduct.client;

import com.kernel360.ecolife.entity.ConcernedProduct;
import jakarta.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriComponentsBuilder;

@Slf4j
@Component
public class ConcernedProductDetailClient {

@Value("${external.ecolife-api.path}")
private String BASE_PATH;

@Value("${external.ecolife-api.service-key}")
private String AUTH_KEY;

private final RestClient restClient;

public ConcernedProductDetailClient() {
this.restClient = RestClient.builder()
.build();
log.info("ConcernedProductDetailClient initialized with BASE_PATH: " + BASE_PATH);
}
@PostConstruct
public void postConstruct() {
log.info("ConcernedProductDetailClient postConstruct with BASE_PATH: " + BASE_PATH);
}

public String getXmlResponse(ConcernedProduct concernedProduct) {

return restClient.get().uri(buildUri(concernedProduct))
.accept(MediaType.APPLICATION_XML)
.acceptCharset(StandardCharsets.UTF_8)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
((request, response) -> {
log.error("[ERROR] :: 4XX 에러 발생"
+ response.getStatusText());
throw new RuntimeException(
response.getStatusCode()
+ response.getHeaders()
.toString());
}))
.onStatus(HttpStatusCode::is5xxServerError,
(request, response) -> {
log.error("[ERROR] :: 5XX 에러 발생"
+ response.getStatusText());
throw new RuntimeException(
response.getStatusCode()
+ response.getHeaders()
.toString());
})
.onStatus(HttpStatusCode::is2xxSuccessful,
((request, response) -> log.info("[INFO] :: 2XX API 요청 성공"
+ response.getBody())))
.body(String.class);
}

public String buildUri(ConcernedProduct concernedProduct) {

return UriComponentsBuilder.fromHttpUrl(BASE_PATH)
.queryParam("AuthKey", AUTH_KEY)
.queryParam("ServiceName", "slfsfcfst01Detail")
.queryParam("prdtNo", concernedProduct.getProductNo())
.toUriString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ public class ConcernedProductListClient {

@Value("${external.ecolife-api.service-key}")
private String AUTH_KEY;

private final RestClient restClient;

public ConcernedProductListClient() {
this.restClient = RestClient.builder()
.build();
}

public String getXmlResponse(Brand brand, Long pageNumber) {
return restClient.post().uri(buildUri(brand, pageNumber))
.accept(MediaType.APPLICATION_XML)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.kernel360.modulebatch.concernedproduct.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.kernel360.ecolife.entity.ConcernedProduct;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@JsonIgnoreProperties(ignoreUnknown = true)
@JacksonXmlRootElement(localName = "row")
Expand All @@ -19,9 +20,7 @@ public record ConcernedProductDetailDto(
String inspectedOrganization,

@JacksonXmlProperty(localName = "issu_date")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
LocalDate issuedDate,

String issuedDate,
@JacksonXmlProperty(localName = "upper_item")
String upperItem,

Expand Down Expand Up @@ -53,27 +52,15 @@ public record ConcernedProductDetailDto(
String manufacture,

@JacksonXmlProperty(localName = "comp_nm")
String companyName
) {
public static ConcernedProductDetailDto of(
String productName,
String productMasterId,
String inspectedOrganization,
LocalDate issuedDate,
String upperItem,
String productType,
String renewedType,
String reportNumber,
String safetyInspectionStandard,
String kidProtectPackage,
String manufactureNation,
String productDefinition,
String item,
String manufacture,
String companyName) {
return new ConcernedProductDetailDto(productName, productMasterId, inspectedOrganization, issuedDate,
upperItem, productType, renewedType, reportNumber, safetyInspectionStandard, kidProtectPackage,
manufactureNation, productDefinition, item, manufacture, companyName);
String companyName) {

public static ConcernedProduct toEntity(ConcernedProductDetailDto detailDto, ConcernedProductDto productDto) {
return ConcernedProduct.of(productDto.productNo(), productDto.productName(), productDto.reportNumber(),
productDto.item(), productDto.companyName(), detailDto.inspectedOrganization,
LocalDate.parse(detailDto.issuedDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")),
detailDto.upperItem, detailDto.productType,
detailDto.renewedType(), detailDto.safetyInspectionStandard(), detailDto.kidProtectPackage,
detailDto.manufactureNation(),
detailDto.productDefinition(), detailDto.manufacture());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ public static ConcernedProduct toEntity(ConcernedProductDto dto) {
return ConcernedProduct.of(dto.productNo, dto.productName(),
dto.reportNumber(), dto.item(), dto.companyName());
}

public static ConcernedProductDto of(String productNo, String productName, String reportNumber, String item,
String companyName) {

return new ConcernedProductDto(productNo, productName, reportNumber, item, companyName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.kernel360.modulebatch.concernedproduct.job.core;

import com.kernel360.ecolife.entity.ConcernedProduct;
import com.kernel360.modulebatch.concernedproduct.job.infra.ConcernedProductDetailItemProcessor;
import jakarta.persistence.EntityManagerFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.database.JpaItemWriter;
import org.springframework.batch.item.database.JpaPagingItemReader;
import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;

@Slf4j
@Configuration
@RequiredArgsConstructor
public class FetchConcernedProductDetailJobConfig {

private final ConcernedProductDetailItemProcessor concernedProductDetailItemProcessor;

private final EntityManagerFactory emf;

@Bean
public Job fetchConcernedProductDetailJob(JobRepository jobRepository,
@Qualifier("fetchConcernedProductDetailStep") Step fetchConcernedProductDetailStep) {
log.info("FetchConcernedProductDetailJobConfig initialized");

return new JobBuilder("fetchConcernedProductDetailJobConfig", jobRepository)
.start(fetchConcernedProductDetailStep)
.incrementer(new RunIdIncrementer())
.listener(new FetchConcernedProductDetailJobListener())
.build();
}

@Bean
public Step fetchConcernedProductDetailStep(JobRepository jobRepository,
PlatformTransactionManager transactionManager) {

return new StepBuilder("fetchConcernedProductDetailStep", jobRepository)
.<ConcernedProduct, ConcernedProduct>chunk(100, transactionManager)
.listener(new FetchConcernedProductDetailStepListener())
.reader(concernedProductDetailItemReader())
.processor(concernedProductDetailItemProcessor)
.writer(concernedProductItemWriter(emf))
.build();
}

@Bean
@StepScope
public JpaPagingItemReader<ConcernedProduct> concernedProductDetailItemReader() {
String jpql = "SELECT cp FROM ConcernedProduct cp";

return new JpaPagingItemReaderBuilder<ConcernedProduct>()
.queryString(jpql)
.entityManagerFactory(emf)
.name("concernedProductDetailJpaItemReader")
// FIXME :: 트랜잭션 관리 개선, 작업의 병렬성 조정, 사용자 정의 Reader 를 만드는 식으로 페이지 크기에 따른 읽기 문제를 해결해야 함.
.pageSize(1000)
.build();
}

@Bean
@StepScope
public JpaItemWriter<ConcernedProduct> concernedProductItemWriter(EntityManagerFactory emf) {
JpaItemWriter<ConcernedProduct> jpaItemWriter = new JpaItemWriter<>();
jpaItemWriter.setEntityManagerFactory(emf);

return jpaItemWriter;
}

//-- Execution Listener --//

public static class FetchConcernedProductDetailJobListener implements JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
log.info("{} starts", jobExecution.getJobInstance().getJobName());
}

@Override
public void afterJob(JobExecution jobExecution) {
log.info("{} ends", jobExecution.getJobInstance().getJobName());
}
}

public static class FetchConcernedProductDetailStepListener implements StepExecutionListener{
@Override
public void beforeStep(StepExecution stepExecution) {
log.info("{} starts", stepExecution.getStepName());
}

@Override
public ExitStatus afterStep(StepExecution stepExecution) {
log.info("StepExecutionListener - afterStep, step name: {}, status: {}", stepExecution.getStepName(), stepExecution.getStatus());
log.info("StepExecutionListener - ReadCount: {}, WriteCount: {}, FilterCount: {}, ReadSkipCount: {}, ProcessSkipCount: {}, WriteSkipCount: {}",
stepExecution.getReadCount(), stepExecution.getWriteCount(), stepExecution.getFilterCount(),
stepExecution.getReadSkipCount(), stepExecution.getProcessSkipCount(), stepExecution.getWriteSkipCount());
return StepExecutionListener.super.afterStep(stepExecution);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.kernel360.modulebatch.concernedproduct.job.core;

import com.kernel360.brand.entity.Brand;
import com.kernel360.modulebatch.concernedproduct.client.ConcernedProductListClient;
import com.kernel360.modulebatch.concernedproduct.dto.ConcernedProductDto;
import com.kernel360.modulebatch.concernedproduct.job.infra.ConcernedProductListItemProcessor;
import com.kernel360.modulebatch.concernedproduct.job.infra.ConcernedProductListItemWriter;
import com.kernel360.modulebatch.concernedproduct.service.ConcernedProductService;
import jakarta.persistence.EntityManagerFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,14 +25,17 @@
@Configuration
@RequiredArgsConstructor
public class FetchConcernedProductListFromBrandJobConfig {
private final ConcernedProductListClient client;
private final ConcernedProductService service;
private final EntityManagerFactory emf;

private final ConcernedProductListItemProcessor concernedProductListItemProcessor;

private final ConcernedProductListItemWriter concernedProductListItemWriter;

private final EntityManagerFactory emf;

@Bean
public Job fetchConcernedProductListFromBrandJob(JobRepository jobRepository,
@Qualifier("fetchConcernedProductListFromBrandStep") Step fetchConcernedProductListStep) {
@Qualifier("fetchConcernedProductListFromBrandStep") Step fetchConcernedProductListStep) {
log.info("FetchConcernedProductListFromBrandJobConfig initialized");

return new JobBuilder("fetchConcernedProductListFromBrandJob", jobRepository)
.start(fetchConcernedProductListStep)
Expand All @@ -45,17 +46,16 @@ public Job fetchConcernedProductListFromBrandJob(JobRepository jobRepository,
//-- List Step --//
@Bean
public Step fetchConcernedProductListFromBrandStep(JobRepository jobRepository,
PlatformTransactionManager transactionManager) throws Exception {
PlatformTransactionManager transactionManager) throws Exception {

return new StepBuilder("fetchConcernedProductListFromBrandStep", jobRepository)
.<Brand, List<ConcernedProductDto>>chunk(1, transactionManager)
.reader(readBrandForConcernedProduct())
.processor(concernedProductListItemProcessor())
.writer(concernedProductListItemWriter())
.processor(concernedProductListItemProcessor)
.writer(concernedProductListItemWriter)
.build();
}


@Bean
@StepScope
@Qualifier("readBrandForConcernedProduct")
Expand All @@ -68,15 +68,4 @@ public JpaPagingItemReader<Brand> readBrandForConcernedProduct() throws Exceptio
return jpaPagingItemReader;
}

@Bean
@StepScope
public ConcernedProductListItemProcessor concernedProductListItemProcessor() {
return new ConcernedProductListItemProcessor(client, service);
}

@Bean
@StepScope
public ConcernedProductListItemWriter concernedProductListItemWriter() {
return new ConcernedProductListItemWriter(service);
}
}
Loading

0 comments on commit f35b3f7

Please sign in to comment.