This commit is contained in:
root
2025-10-05 17:27:19 +05:00
parent 51a5fc398c
commit f57e614c7f
@@ -75,7 +75,7 @@ public class ReportController {
}
@PostMapping
public ResponseEntity<byte[]> generateResearchReport(
public ResponseEntity<ApiResponse<ReportGenerationResponse>> generateResearchReport(
@Validated @RequestBody ResearchReportRequest request) {
// Валидация входных данных
if (request.getQuery() == null || request.getQuery().trim().isEmpty()) {
@@ -90,33 +90,63 @@ public class ReportController {
request.getBreadth(),
request.getReportType());
// Синхронно выполняем: deep-research -> synthesis via Ollama -> Markdown
DeepResearchResponse researchResponse = deepResearchService.generateReportAsync(deepRequest)
// Генерация уникального ID задачи
String taskId = UUID.randomUUID().toString();
// Предполагаемое время завершения
LocalDateTime estimatedCompletion = LocalDateTime.now().plusMinutes(8);
// Асинхронный процесс: deep-research -> synthesis via Ollama -> Markdown ->
// сохранение
deepResearchService.generateReportAsync(deepRequest)
.publishOn(Schedulers.boundedElastic())
.block();
if (researchResponse == null || researchResponse.getLearnings() == null
|| researchResponse.getLearnings().isEmpty()) {
return ResponseEntity.internalServerError().build();
}
.flatMap(researchResponse -> {
if (researchResponse == null
|| researchResponse.getLearnings() == null
|| researchResponse.getLearnings().isEmpty()) {
return reactor.core.publisher.Mono.error(
new RuntimeException(
"Empty research response content"));
}
return reportSynthesisService
.synthesizeReport(request.getQuery(),
researchResponse.getLearnings(),
request.getLang())
.map(synthesizedMarkdown -> new Object[] { researchResponse,
synthesizedMarkdown });
})
.map(tuple -> {
DeepResearchResponse researchResponse = (DeepResearchResponse) tuple[0];
String synthesizedMarkdown = (String) tuple[1];
String finalMarkdown = buildMarkdownWithSources(synthesizedMarkdown,
researchResponse.getVisitedUrls());
byte[] mdBytes = finalMarkdown
.getBytes(java.nio.charset.StandardCharsets.UTF_8);
String filename = "research_report_" + System.currentTimeMillis() + ".md";
saveResearchMarkdownToHistory(request, filename, mdBytes, researchResponse);
return true;
})
.doOnError(e -> {
System.err.println("Error generating research report async: " + e.getMessage());
})
.subscribe(
ok -> {
},
err -> {
// Avoid onErrorDropped by consuming error here
System.err.println("Async pipeline error: " + err.getMessage());
});
String synthesizedMarkdown = reportSynthesisService
.synthesizeReport(request.getQuery(), researchResponse.getLearnings(),
request.getLang())
.publishOn(Schedulers.boundedElastic())
.block();
if (synthesizedMarkdown == null) {
return ResponseEntity.internalServerError().build();
}
ReportGenerationResponse response = new ReportGenerationResponse(
taskId,
"Исследование запущено в фоновом режиме. Ожидаемое время завершения: "
+ estimatedCompletion
.format(java.time.format.DateTimeFormatter.ofPattern(
"HH:mm")),
estimatedCompletion,
"PROCESSING");
String finalMarkdown = buildMarkdownWithSources(synthesizedMarkdown, researchResponse.getVisitedUrls());
byte[] bytes = finalMarkdown.getBytes(java.nio.charset.StandardCharsets.UTF_8);
String filename = "research_report.md";
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"")
.contentType(MediaType.parseMediaType("text/markdown; charset=UTF-8"))
.body(bytes);
return ResponseEntity.ok(ApiResponse.success("Исследовательский отчёт поставлен в очередь", response));
}
private String buildMarkdownWithSources(String synthesizedMarkdown, java.util.List<String> visitedUrls) {
@@ -164,6 +194,32 @@ public class ReportController {
}
}
private void saveResearchMarkdownToHistory(ResearchReportRequest request, String filename,
byte[] mdBytes, DeepResearchResponse researchResponse) {
try {
// Загрузка в MinIO
minIOService.uploadFile(filename, mdBytes,
MediaType.parseMediaType("text/markdown; charset=UTF-8").toString());
// Сохранение в базу данных
ReportHistory reportHistory = new ReportHistory();
reportHistory.setReportTitle(request.getQuery());
reportHistory.setAuthorName("AI Research Agent");
reportHistory.setCompanyName("Deep Research Service");
reportHistory.setStartDate(LocalDateTime.now());
reportHistory.setEndDate(LocalDateTime.now());
reportHistory.setFormat("MD");
reportHistory.setFilename(filename);
reportHistory.setFilePath(filename);
reportHistory.setFileSize(mdBytes.length);
reportHistory.setCreatedAt(LocalDateTime.now());
reportHistoryRepository.save(reportHistory);
} catch (Exception e) {
System.err.println("Error saving research markdown to history: " + e.getMessage());
}
}
@GetMapping("/history")
public ResponseEntity<PageResponse<ReportHistory>> history(
@RequestParam(name = "page", defaultValue = "0") int page,
@@ -198,10 +254,17 @@ public class ReportController {
byte[] bytes = inputStream.readAllBytes();
inputStream.close();
MediaType mt = rh.getFormat() != null && rh.getFormat().equalsIgnoreCase("DOCX")
? MediaType
.parseMediaType("application/vnd.openxmlformats-officedocument.wordprocessingml.document")
: MediaType.APPLICATION_PDF;
MediaType mt;
if (rh.getFormat() != null && rh.getFormat().equalsIgnoreCase("DOCX")) {
mt = MediaType.parseMediaType(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document");
} else if (rh.getFormat() != null && (rh.getFormat().equalsIgnoreCase("MD")
|| rh.getFilename() != null
&& rh.getFilename().toLowerCase().endsWith(".md"))) {
mt = MediaType.parseMediaType("text/markdown; charset=UTF-8");
} else {
mt = MediaType.APPLICATION_PDF;
}
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + rh.getFilename() + "\"")