This commit is contained in:
root
2025-09-24 00:37:46 +05:00
parent 6b52c915ed
commit 0b2f9bc508
@@ -50,6 +50,36 @@ public class ReportGenerationService {
}
}
private static class Sections {
private final String annotation;
private final String introduction;
private final String mainNarrative;
private final String recommendations;
private Sections(String annotation, String introduction, String mainNarrative, String recommendations) {
this.annotation = annotation == null ? "" : annotation;
this.introduction = introduction == null ? "" : introduction;
this.mainNarrative = mainNarrative == null ? "" : mainNarrative;
this.recommendations = recommendations == null ? "" : recommendations;
}
public String getAnnotation() {
return annotation;
}
public String getIntroduction() {
return introduction;
}
public String getMainNarrative() {
return mainNarrative;
}
public String getRecommendations() {
return recommendations;
}
}
private final MarketItemRepository marketItemRepository;
private final OllamaAnalyticsService ollamaAnalyticsService;
private final ReportHistoryRepository reportHistoryRepository;
@@ -73,17 +103,17 @@ public class ReportGenerationService {
.findByPublishedAtBetween(start, end, org.springframework.data.domain.Pageable.unpaged())
.getContent();
String annotation = buildAnnotation(items);
Sections sections = buildSections(req, items);
ReportBinary rb;
if ("DOCX".equalsIgnoreCase(req.getFormat())) {
byte[] docx = buildDocx(req, items, annotation);
byte[] docx = buildDocx(req, items, sections);
String filename = "report_" + end.toLocalDate().toString() + "-" + System.currentTimeMillis() + ".docx";
rb = new ReportBinary(filename,
MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
docx);
} else {
byte[] pdf = buildPdf(req, items, annotation);
byte[] pdf = buildPdf(req, items, sections);
String filename = "report_" + end.toLocalDate().toString() + "-" + System.currentTimeMillis() + ".pdf";
rb = new ReportBinary(filename, MediaType.APPLICATION_PDF, pdf);
}
@@ -114,11 +144,15 @@ public class ReportGenerationService {
}
}
private String buildAnnotation(List<MarketItem> items) {
String combined = items.stream()
private String getCombinedSummaries(List<MarketItem> items) {
return items.stream()
.map(i -> i.getAnalytics() != null ? i.getAnalytics().getSummary() : null)
.filter(Objects::nonNull)
.collect(Collectors.joining("\n\n"));
}
private String buildAnnotation(List<MarketItem> items) {
String combined = getCombinedSummaries(items);
if (combined.isBlank()) {
return "";
}
@@ -127,7 +161,27 @@ public class ReportGenerationService {
return res == null ? "" : res;
}
private byte[] buildDocx(ReportGenerateRequest req, List<MarketItem> items, String annotation) {
private Sections buildSections(ReportGenerateRequest req, List<MarketItem> items) {
String combined = getCombinedSummaries(items);
if (combined.isBlank()) {
return new Sections("", "", "", "");
}
String annotation = ollamaAnalyticsService.generateWithInstruction(combined,
"На основе кратких сводок новостей напиши краткое содержание (2-3 абзаца), выделив ключевые тренды и события. Стиль: деловой, нейтральный.");
String intro = ollamaAnalyticsService.generateWithInstruction(combined,
"Напиши краткое введение к аналитическому отчёту за период. Укажи цель отчёта, источники данных и подход к анализу. Объём: 1 абзац.");
String mainPart = ollamaAnalyticsService.generateWithInstruction(combined,
"Сформируй основную часть отчёта: перечисли и кратко объясни 3-5 ключевых тенденций и событий периода, добавь замеченные риски и возможности. Используй маркированные пункты и короткие абзацы.");
String recs = ollamaAnalyticsService.generateWithInstruction(combined,
"На основе сводок сформулируй практические рекомендации (4-6 пунктов) для бизнеса/маркетинга. Формат: маркированный список, краткие и конкретные формулировки.");
return new Sections(annotation, intro, mainPart, recs);
}
private byte[] buildDocx(ReportGenerateRequest req, List<MarketItem> items, Sections sections) {
try (XWPFDocument doc = new XWPFDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
DateTimeFormatter dt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
@@ -144,16 +198,19 @@ public class ReportGenerationService {
// Annotation
addHeading(doc, "Краткое содержание");
addParagraph(doc, annotation.isBlank() ? "(недоступно)" : annotation);
addParagraph(doc, sections.getAnnotation().isBlank() ? "(недоступно)" : sections.getAnnotation());
// Intro
addHeading(doc, "Введение");
addParagraph(doc, String.format(
"Целью данного отчёта является анализ новостного фона в сфере маркетинга и бизнеса за период с %s по %s. Были проанализированы публикации из ключевых источников для выявления основных трендов.",
req.getStartDate().format(dt), req.getEndDate().format(dt)));
addParagraph(doc, sections.getIntroduction().isBlank()
? String.format(
"Целью данного отчёта является анализ новостного фона в сфере маркетинга и бизнеса за период с %s по %s. Были проанализированы публикации из ключевых источников для выявления основных трендов.",
req.getStartDate().format(dt), req.getEndDate().format(dt))
: sections.getIntroduction());
// Main
addHeading(doc, "Основная часть");
addParagraph(doc, sections.getMainNarrative());
addSubheading(doc, "Ключевые публикации");
List<MarketItem> top = items.stream()
.sorted(Comparator
@@ -187,7 +244,9 @@ public class ReportGenerationService {
// Recommendations
addHeading(doc, "Рекомендации");
addParagraph(doc, "Рассмотрите возможность усиления присутствия в медиа по ключевым темам отчёта.");
addParagraph(doc, sections.getRecommendations().isBlank()
? "Рассмотрите возможность усиления присутствия в медиа по ключевым темам отчёта."
: sections.getRecommendations());
// Bibliography
addHeading(doc, "Список литературы");
@@ -207,7 +266,7 @@ public class ReportGenerationService {
}
}
private byte[] buildPdf(ReportGenerateRequest req, List<MarketItem> items, String annotation) {
private byte[] buildPdf(ReportGenerateRequest req, List<MarketItem> items, Sections sections) {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
Document pdf = new Document();
PdfWriter.getInstance(pdf, out);
@@ -223,14 +282,17 @@ public class ReportGenerationService {
"\nСодержание: Аннотация, Введение, Основная часть, Рекомендации, Список литературы, Приложения\n"));
pdf.add(new Paragraph("Краткое содержание"));
pdf.add(new Paragraph(annotation.isBlank() ? "(недоступно)" : annotation));
pdf.add(new Paragraph(sections.getAnnotation().isBlank() ? "(недоступно)" : sections.getAnnotation()));
pdf.add(new Paragraph("Введение"));
pdf.add(new Paragraph(String.format(
"Целью данного отчёта является анализ новостного фона в сфере маркетинга и бизнеса за период с %s по %s. Были проанализированы публикации из ключевых источников для выявления основных трендов.",
req.getStartDate().format(dt), req.getEndDate().format(dt))));
pdf.add(new Paragraph(sections.getIntroduction().isBlank()
? String.format(
"Целью данного отчёта является анализ новостного фона в сфере маркетинга и бизнеса за период с %s по %s. Были проанализированы публикации из ключевых источников для выявления основных трендов.",
req.getStartDate().format(dt), req.getEndDate().format(dt))
: sections.getIntroduction()));
pdf.add(new Paragraph("Основная часть"));
pdf.add(new Paragraph(sections.getMainNarrative()));
pdf.add(new Paragraph("Ключевые публикации"));
List<MarketItem> top = items.stream()
.sorted(Comparator
@@ -262,7 +324,9 @@ public class ReportGenerationService {
pdf.add(new Paragraph("Персоны: " + formatTop(persons)));
pdf.add(new Paragraph("Рекомендации"));
pdf.add(new Paragraph("Рассмотрите возможность усиления присутствия в медиа по ключевым темам отчёта."));
pdf.add(new Paragraph(sections.getRecommendations().isBlank()
? "Рассмотрите возможность усиления присутствия в медиа по ключевым темам отчёта."
: sections.getRecommendations()));
pdf.add(new Paragraph("Список литературы"));
for (MarketItem mi : items) {