package com.interplug.qcast.batch; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.*; import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobRestartException; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor @Slf4j public class JobLauncherController { private final Map jobs; // 여러 Job을 주입받도록 변경 private final JobLauncher jobLauncher; private final JobExplorer jobExplorer; @Value("${qsp.master-admin-user-batch-url}") private String qspInterfaceUrl; @Value("${spring.profiles.scheduler}") private String scheduler; /** * 특정 Job을 매핑으로 실행하는 메소드 * * @param jobName * @return * @throws JobInstanceAlreadyCompleteException * @throws JobExecutionAlreadyRunningException * @throws JobParametersInvalidException * @throws JobRestartException */ @GetMapping("/batch/job/{jobName}") // Path Variable로 jobName을 받음 public Map launchJob(@PathVariable String jobName) throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { Job job = jobs.get(jobName); Map resultMap = new HashMap(); if (job == null) { // return "Job " + jobName + " not found"; resultMap.put("code", "FAILED"); resultMap.put("message", "Job" + jobName + " not found"); return resultMap; } // 실행 중인 Job 확인 (데이터베이스 기반) if (isJobRunning(jobName)) { log.warn("Job {} is already running, skipping execution", jobName); resultMap.put("code", "FAILED"); resultMap.put("message", "Job "+ jobName +" is already running, skipping execution"); return resultMap; } try { log.info("Starting job: {}", jobName); JobParameters jobParameters = new JobParametersBuilder().addString("jobName", jobName) .addDate("time", new Date()).toJobParameters(); JobExecution jobExecution = jobLauncher.run(job, jobParameters); BatchStatus status = jobExecution.getStatus(); ExitStatus exitStatus = jobExecution.getExitStatus(); resultMap.put("code", status.toString()); resultMap.put("message", exitStatus.getExitDescription()); // return "Job " + jobName + " started"; return resultMap; } catch (Exception e) { resultMap.put("code", "FAILED"); resultMap.put("message", e.getMessage()); return resultMap; } } /** * Q.CAST 판매점 / 사용자 / 즐겨찾기 / 노출 아이템 동기화 배치 * */ // @Scheduled(cron = "*/5 * * * * *") @Scheduled(cron = "0 50 23 * * *") public String storeAdditionalJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("storeAdditionalJob"); } /** * 아이템 동기화 배치 * */ @Scheduled(cron = "0 30 02 * * *") public String materialJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("materialJob"); } /** * BOM 아이템 동기화 배치 * */ @Scheduled(cron = "0 40 02 * * *") public String bomJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("bomJob"); } /** * 영업사원 동기화 배치 * */ @Scheduled(cron = "0 40 03 * * *") public String businessChargerJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("businessChargerJob"); } /** * 관리자 유저 동기화 배치 * */ @Scheduled(cron = "0 30 01 * * *") public String adminUserJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("adminUserJob"); } /** * 가격 동기화 배치 * */ @Scheduled(cron = "0 20 00 * * *") public String priceJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("priceJob"); } /** * 공통코드 M_COMM_H, M_COMM_L 동기화 배치 * */ @Scheduled(cron = "0 10 03 * * *") public String commonCodeJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("commonCodeJob"); } /** * Q.CAST 견적특이사항 / 아이템 표시, 미표시 동기화 배치 * */ @Scheduled(cron = "0 30 23 * * *") public String specialNoteDispItemAdditionalInfoJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("specialNoteDispItemAdditionalJob"); } /** * Plan Confrim 동기화 배치 * */ @Scheduled(cron = "0 05 04 * * *") public String planConfirmJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("planConfirmJob"); } /** * 견적서 전송 동기화 배치 * */ @Scheduled(cron = "0 20 04 * * *") public String estimateSyncJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { return executeScheduledJob("estimateSyncJob"); } /** * 공통 스케줄러 실행 메소드 */ private String executeScheduledJob(String jobName) throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { Job job = jobs.get(jobName); if (job == null) { log.error("Job {} not found", jobName); return "Job " + jobName + " not found"; } if (!"Y".equals(scheduler) && !"materialJob".equals(jobName) && !"commonCodeJob".equals(jobName) && !"specialNoteDispItemAdditionalJob".equals(jobName)) { log.info("Scheduler disabled, skipping job {}", jobName); return "Scheduler disabled"; } // 실행 중인 Job 확인 (데이터베이스 기반) if (isJobRunning(jobName)) { log.warn("Job {} is already running, skipping execution", jobName); return "Job already running"; } JobParameters jobParameters = new JobParametersBuilder().addDate("time", new Date()).toJobParameters(); jobLauncher.run(job, jobParameters); return jobName+ " executed successfully"; } /** * Job 실행 상태 확인 */ private boolean isJobRunning(String jobName) { try { List jobInstances = jobExplorer.findJobInstancesByJobName(jobName, 0, 1); if (jobInstances.isEmpty()) { return false; } JobInstance latestJobInstance = jobInstances.get(0); List jobExecutions = jobExplorer.getJobExecutions(latestJobInstance); return jobExecutions.stream() .anyMatch(execution -> execution.getStatus() == BatchStatus.STARTED || execution.getStatus() == BatchStatus.STARTING); } catch (Exception e) { log.error("Error checking job running status: {}", e.getMessage()); return false; } } }