qcast-api/src/main/java/com/interplug/qcast/batch/master/BomConfiguration.java

90 lines
3.4 KiB
Java

package com.interplug.qcast.batch.master;
import com.fasterxml.jackson.core.type.TypeReference;
import com.interplug.qcast.biz.displayItem.DisplayItemService;
import com.interplug.qcast.biz.displayItem.dto.BomSyncResponse;
import com.interplug.qcast.util.InterfaceQsp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecutionListener;
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.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.transaction.PlatformTransactionManager;
/** Bom Item 마스터 동기화 배치 */
@Configuration
public class BomConfiguration implements JobExecutionListener {
private final DisplayItemService displayItemService;
private final InterfaceQsp interfaceQsp;
List<BomSyncResponse> bomSyncList;
@Value("${qsp.master-bom-batch-url}")
private String qspInterfaceUrl;
public BomConfiguration(DisplayItemService displayItemService, InterfaceQsp interfaceQsp) {
this.displayItemService = displayItemService;
this.interfaceQsp = interfaceQsp;
}
@Bean
public Job bomJob(JobRepository jobRepository, Step bomStep) {
return new JobBuilder("bomJob", jobRepository).start(bomStep).build();
}
@Bean
public Step bomStep(JobRepository jobRepository, PlatformTransactionManager transactionManager)
throws Exception {
return new StepBuilder("bomStep", jobRepository)
.<BomSyncResponse, BomSyncResponse>chunk(100, transactionManager)
.reader(bomReader())
.processor(bomProcessor())
.writer(bomWriter())
.build();
}
@Bean
@StepScope
public ListItemReader<BomSyncResponse> bomReader() throws Exception {
this.bomSyncList =
interfaceQsp.callApiData(
HttpMethod.GET, qspInterfaceUrl, null, new TypeReference<List<BomSyncResponse>>() {});
if (bomSyncList == null) {
return new ListItemReader<>(Collections.emptyList());
}
// 2026-05-20 청크 경계에서 같은 키 A/D 가 분리되어 A 가 먼저 MERGE 된 뒤
// 다음 청크의 D 가 그것을 삭제해버리는 리스크를 차단하기 위해,
// 전체 리스트를 statCd='D' 우선으로 정렬한다. (Spring Batch 는 청크를 순차 처리하므로
// 모든 D 청크가 끝난 뒤에 A/U 청크가 시작되어 전역적으로 'A 가 이김' 이 보장됨)
bomSyncList.sort(
Comparator.comparingInt((BomSyncResponse b) -> "D".equals(b.getStatCd()) ? 0 : 1));
return new ListItemReader<>(bomSyncList);
}
@Bean
public ItemProcessor<BomSyncResponse, BomSyncResponse> bomProcessor() {
return item -> item;
}
@Bean
public ItemWriter<BomSyncResponse> bomWriter() {
return items -> {
displayItemService.setBomSyncSave(new ArrayList<>(items.getItems()));
};
}
}