WarehouseDispatcherController.java
package com.v1rex.liftnexus.planning.controller;
import com.v1rex.liftnexus.planning.dto.DispatchJobResponse;
import com.v1rex.liftnexus.planning.service.WarehouseDispatcherService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.Map;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/dispatcher/jobs")
@Slf4j
@RequiredArgsConstructor
@Tag(name = "Dispatcher", description = "Submit and manage Timefold optimization jobs")
public class WarehouseDispatcherController {
private final WarehouseDispatcherService dispatcherService;
@Operation(
summary = "Get optimization job status",
description =
"Retrieves the current status and result of a previously submitted optimization job.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Job status retrieved"),
@ApiResponse(
responseCode = "404",
description = "Job not found",
content = @io.swagger.v3.oas.annotations.media.Content)
})
@GetMapping("/{jobId}")
public ResponseEntity<DispatchJobResponse> getJobStatus(@PathVariable UUID jobId) {
log.debug("API request received to fetch status for job: {}", jobId);
DispatchJobResponse job = dispatcherService.getJobStatusAndReconcile(jobId);
return ResponseEntity.ok(job);
}
@Operation(
summary = "Submit a new optimization job",
description =
"Triggers the Timefold optimization engine to compute optimal forklift routing and task assignments asynchronously. Returns a job ID for tracking progress.")
@ApiResponse(responseCode = "202", description = "Optimization job accepted and queued")
@PostMapping
public ResponseEntity<Map<String, UUID>> submitJob() {
log.info("API request received to trigger warehouse optimization engine.");
UUID jobId = dispatcherService.submitOptimizationJob();
// Returning 202 Accepted with a structured JSON body
return ResponseEntity.status(HttpStatus.ACCEPTED).body(Map.of("jobId", jobId));
}
@Operation(
summary = "Terminate an optimization job",
description =
"Aborts a running or queued optimization job. Jobs that have already completed will remain in COMPLETED state.")
@ApiResponses({
@ApiResponse(responseCode = "204", description = "Job terminated"),
@ApiResponse(
responseCode = "404",
description = "Job not found",
content = @io.swagger.v3.oas.annotations.media.Content)
})
@DeleteMapping("/{jobId}")
public ResponseEntity<Void> terminateJob(@PathVariable UUID jobId) {
log.info("API request received to manually abort optimization job: {}", jobId);
dispatcherService.terminateOptimizationJob(jobId);
return ResponseEntity.noContent().build();
}
}