diff --git a/src/main/java/kz/konturai/parser/config/AsyncConfig.java b/src/main/java/kz/konturai/parser/config/AsyncConfig.java new file mode 100644 index 0000000..00b6a40 --- /dev/null +++ b/src/main/java/kz/konturai/parser/config/AsyncConfig.java @@ -0,0 +1,24 @@ +package kz.konturai.parser.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@Configuration +@EnableAsync +public class AsyncConfig { + + @Bean(name = "reportGenerationExecutor") + public Executor reportGenerationExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(2); + executor.setMaxPoolSize(5); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("ReportGeneration-"); + executor.initialize(); + return executor; + } +} diff --git a/src/main/java/kz/konturai/parser/controller/ReportController.java b/src/main/java/kz/konturai/parser/controller/ReportController.java index 781bb69..b06b334 100644 --- a/src/main/java/kz/konturai/parser/controller/ReportController.java +++ b/src/main/java/kz/konturai/parser/controller/ReportController.java @@ -1,10 +1,11 @@ package kz.konturai.parser.controller; import kz.konturai.parser.dto.ReportGenerateRequest; +import kz.konturai.parser.dto.ReportGenerationResponse; +import kz.konturai.parser.dto.ApiResponse; import kz.konturai.parser.model.ReportHistory; import kz.konturai.parser.repository.ReportHistoryRepository; import kz.konturai.parser.service.ReportGenerationService; -import kz.konturai.parser.service.ReportGenerationService.ReportBinary; import kz.konturai.parser.service.MinIOService; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -17,6 +18,8 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.Optional; +import java.time.LocalDateTime; +import java.util.UUID; import kz.konturai.parser.dto.PageResponse; @@ -37,12 +40,25 @@ public class ReportController { } @PostMapping("/generate") - public ResponseEntity generate(@Validated @RequestBody ReportGenerateRequest request) { - ReportBinary rb = reportGenerationService.simpleGenerate(request); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + rb.getFilename() + "\"") - .contentType(rb.getMediaType()) - .body(rb.getBytes()); + public ResponseEntity> generate( + @Validated @RequestBody ReportGenerateRequest request) { + // Generate unique task ID + String taskId = UUID.randomUUID().toString(); + + // Start async report generation + reportGenerationService.generateAsync(request); + + // Calculate estimated completion time (5-10 minutes for complex reports) + LocalDateTime estimatedCompletion = LocalDateTime.now().plusMinutes(8); + + ReportGenerationResponse response = new ReportGenerationResponse( + taskId, + "Отчёт будет сгенерирован в фоновом режиме. Ожидаемое время завершения: " + + estimatedCompletion.format(java.time.format.DateTimeFormatter.ofPattern("HH:mm")), + estimatedCompletion, + "PROCESSING"); + + return ResponseEntity.ok(ApiResponse.success("Отчёт поставлен в очередь на генерацию", response)); } @GetMapping("/history") diff --git a/src/main/java/kz/konturai/parser/dto/ReportGenerationResponse.java b/src/main/java/kz/konturai/parser/dto/ReportGenerationResponse.java new file mode 100644 index 0000000..d8ed311 --- /dev/null +++ b/src/main/java/kz/konturai/parser/dto/ReportGenerationResponse.java @@ -0,0 +1,53 @@ +package kz.konturai.parser.dto; + +import java.time.LocalDateTime; + +public class ReportGenerationResponse { + private String taskId; + private String message; + private LocalDateTime estimatedCompletionTime; + private String status; + + public ReportGenerationResponse() { + } + + public ReportGenerationResponse(String taskId, String message, LocalDateTime estimatedCompletionTime, + String status) { + this.taskId = taskId; + this.message = message; + this.estimatedCompletionTime = estimatedCompletionTime; + this.status = status; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public LocalDateTime getEstimatedCompletionTime() { + return estimatedCompletionTime; + } + + public void setEstimatedCompletionTime(LocalDateTime estimatedCompletionTime) { + this.estimatedCompletionTime = estimatedCompletionTime; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/src/main/java/kz/konturai/parser/service/ReportGenerationService.java b/src/main/java/kz/konturai/parser/service/ReportGenerationService.java index 984b3db..89195d0 100644 --- a/src/main/java/kz/konturai/parser/service/ReportGenerationService.java +++ b/src/main/java/kz/konturai/parser/service/ReportGenerationService.java @@ -6,6 +6,7 @@ import kz.konturai.parser.repository.MarketItemRepository; import kz.konturai.parser.model.ReportHistory; import kz.konturai.parser.repository.ReportHistoryRepository; import org.springframework.http.MediaType; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; @@ -130,6 +131,17 @@ public class ReportGenerationService { generated.getBytes(java.nio.charset.StandardCharsets.UTF_8)); } + @Async("reportGenerationExecutor") + public void generateAsync(ReportGenerateRequest req) { + try { + generate(req); + // Report is automatically saved to database and MinIO in the generate() method + } catch (Exception e) { + // Log error but don't throw to avoid async method issues + System.err.println("Error generating report asynchronously: " + e.getMessage()); + } + } + public ReportBinary generate(ReportGenerateRequest req) { LocalDateTime start = req.getStartDate(); LocalDateTime end = req.getEndDate();