package com.interplug.qcast.batch; 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.JobOperator; import org.springframework.web.bind.annotation.*; import java.time.Duration; import java.time.LocalDateTime; import java.util.*; @RestController @RequiredArgsConstructor @Slf4j public class BatchMonitorController { private final JobExplorer jobExplorer; private final JobOperator jobOperator; @GetMapping("/batch/status") public Map getBatchStatus() { Map status = new HashMap<>(); String[] jobNames = {"storeAdditionalJob", "priceJob", "materialJob", "bomJob", "businessChargerJob", "adminUserJob", "commonCodeJob", "specialNoteDispItemAdditionalJob", "planConfirmJob", "estimateSyncJob"}; for (String jobName : jobNames) { status.put(jobName, getJobStatus(jobName)); } return status; } private Map getJobStatus(String jobName) { Map jobStatus = new HashMap<>(); try { List jobInstances = jobExplorer.findJobInstancesByJobName(jobName, 0, 1); if (jobInstances.isEmpty()) { jobStatus.put("status", "NEVER_EXECUTED"); return jobStatus; } JobInstance latestJobInstance = jobInstances.get(0); List jobExecutions = jobExplorer.getJobExecutions(latestJobInstance); if (!jobExecutions.isEmpty()) { JobExecution latestExecution = jobExecutions.get(0); jobStatus.put("status", latestExecution.getStatus().toString()); jobStatus.put("startTime", latestExecution.getStartTime()); jobStatus.put("endTime", latestExecution.getEndTime()); // Duration 계산 (LocalDateTime용) if (latestExecution.getEndTime() != null && latestExecution.getStartTime() != null) { Duration duration = Duration.between(latestExecution.getStartTime(), latestExecution.getEndTime()); jobStatus.put("durationSeconds", duration.getSeconds()); jobStatus.put("durationMinutes", duration.toMinutes()); } else if (latestExecution.getStartTime() != null) { // 실행 중인 경우 현재까지의 경과 시간 Duration duration = Duration.between(latestExecution.getStartTime(), LocalDateTime.now()); jobStatus.put("runningDurationSeconds", duration.getSeconds()); jobStatus.put("runningDurationMinutes", duration.toMinutes()); } } } catch (Exception e) { jobStatus.put("status", "ERROR"); jobStatus.put("error", e.getMessage()); } return jobStatus; } /** * 강제 Job 중지 */ @PostMapping("/batch/stop/{jobName}") public Map stopJob(@PathVariable String jobName) { Map result = new HashMap<>(); try { Set runningExecutions = jobOperator.getRunningExecutions(jobName); if (runningExecutions.isEmpty()) { result.put("code", "NO_RUNNING_JOBS"); result.put("message", "No running executions found for job: " + jobName); return result; } List stopResults = new ArrayList<>(); for (Long executionId : runningExecutions) { boolean stopped = jobOperator.stop(executionId); stopResults.add("Execution " + executionId + ": " + (stopped ? "STOPPED" : "FAILED_TO_STOP")); } result.put("code", "SUCCESS"); result.put("message", "Stop commands sent"); result.put("results", stopResults); } catch (Exception e) { result.put("code", "ERROR"); result.put("message", "Error stopping job: " + e.getMessage()); } return result; } }