From 332cd7c7f241e2ca83d70d1d73e18c3a0a9ef736 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 18 Dec 2025 20:11:36 +0500 Subject: [PATCH] . --- .../service/MarketingAnalysisService.java | 176 +++++++----------- 1 file changed, 66 insertions(+), 110 deletions(-) diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index acede18..586dc04 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -2506,10 +2506,11 @@ public class MarketingAnalysisService { report.put("detailLevel", detailLevelName); report.put("fullAnalysis", processedTextReport != null ? processedTextReport : "Анализ не удалось сгенерировать."); - report.put("summary", - processedTextReport != null ? processedTextReport : "Анализ не удалось сгенерировать."); // Keep for - // backward - // compatibility + + // Извлекаем только первую секцию (I. Краткое резюме) для поля summary + // чтобы избежать дублирования полного текста отчета + String shortSummary = extractSummarySection(processedTextReport); + report.put("summary", shortSummary != null ? shortSummary : "Анализ не удалось сгенерировать."); // Извлекаем данные из analysisJson вместо повторных вызовов AI logger.info("generateMarketingReport: Extracting targetAudience from analysisJson"); @@ -2944,6 +2945,59 @@ public class MarketingAnalysisService { return strategy; } + /** + * Извлекает только первую секцию "I. Краткое резюме" из полного текстового + * отчета. + * Используется для поля summary, чтобы избежать дублирования полного текста. + * + * @param fullReport Полный текстовый отчет + * @return Текст только первой секции (краткое резюме) + */ + private String extractSummarySection(String fullReport) { + if (fullReport == null || fullReport.trim().isEmpty()) { + return null; + } + + try { + // Ищем начало первой секции "I. Краткое резюме" + int startIndex = fullReport.indexOf("I. Краткое резюме"); + if (startIndex == -1) { + // Если не нашли, пробуем найти просто "Краткое резюме" + startIndex = fullReport.indexOf("Краткое резюме"); + } + + if (startIndex == -1) { + // Если нет секции резюме, возвращаем первые 500 символов + return fullReport.length() > 500 + ? fullReport.substring(0, 500) + "..." + : fullReport; + } + + // Ищем начало второй секции "II." + int endIndex = fullReport.indexOf("II.", startIndex + 1); + if (endIndex == -1) { + // Пробуем найти "II. " с пробелом + endIndex = fullReport.indexOf("\nII. ", startIndex + 1); + } + + if (endIndex == -1) { + // Если не нашли вторую секцию, возвращаем от начала до 1000 символов + int maxLength = Math.min(startIndex + 1000, fullReport.length()); + return fullReport.substring(startIndex, maxLength).trim(); + } + + // Извлекаем текст между I. и II. + return fullReport.substring(startIndex, endIndex).trim(); + + } catch (Exception e) { + logger.warn("extractSummarySection: Error extracting summary: {}", e.getMessage()); + // В случае ошибки возвращаем первые 500 символов + return fullReport.length() > 500 + ? fullReport.substring(0, 500) + "..." + : fullReport; + } + } + private String buildMarkdownFromReport(Map reportData, MarketingAnalysisRequest request) { logger.info("buildMarkdownFromReport: Starting markdown generation for product: {}", request.getProduct()); logger.debug("buildMarkdownFromReport: ReportData keys: {}", reportData != null ? reportData.keySet() : "null"); @@ -3009,109 +3063,14 @@ public class MarketingAnalysisService { } markdown.append("\n"); - // Add additional sections from JSON analysis if available - @SuppressWarnings("unchecked") - Map analysisJson = (Map) reportData.get("analysisJson"); - if (analysisJson != null && !analysisJson.isEmpty()) { - // Positioning section - @SuppressWarnings("unchecked") - Map positioning = (Map) analysisJson.get("positioning"); - if (positioning != null) { - markdown.append("## Позиционирование\n\n"); - if (positioning.containsKey("brandImage")) { - markdown.append("**Образ бренда:** ").append(positioning.get("brandImage")).append("\n\n"); - } - if (positioning.containsKey("keyMessage")) { - markdown.append("**Ключевое сообщение:** ").append(positioning.get("keyMessage")).append("\n\n"); - } - if (positioning.containsKey("toneOfVoice")) { - markdown.append("**Стиль коммуникации:** ").append(positioning.get("toneOfVoice")).append("\n\n"); - } - @SuppressWarnings("unchecked") - List trustFactors = (List) positioning.get("trustFactors"); - if (trustFactors != null && !trustFactors.isEmpty()) { - markdown.append("**Факторы доверия:**\n"); - for (String factor : trustFactors) { - markdown.append("- ").append(factor).append("\n"); - } - markdown.append("\n"); - } - } - - // Value Proposition section - @SuppressWarnings("unchecked") - Map valueProposition = (Map) analysisJson.get("valueProposition"); - if (valueProposition != null) { - markdown.append("## Уникальное торговое предложение (УТП)\n\n"); - @SuppressWarnings("unchecked") - List utpVariants = (List) valueProposition.get("utpVariants"); - if (utpVariants != null && !utpVariants.isEmpty()) { - markdown.append("**Варианты УТП:**\n"); - for (String utp : utpVariants) { - markdown.append("- ").append(utp).append("\n"); - } - markdown.append("\n"); - } - @SuppressWarnings("unchecked") - List mainBenefits = (List) valueProposition.get("mainBenefits"); - if (mainBenefits != null && !mainBenefits.isEmpty()) { - markdown.append("**Основные преимущества:**\n"); - for (String benefit : mainBenefits) { - markdown.append("- ").append(benefit).append("\n"); - } - markdown.append("\n"); - } - } - - // Content Recommendations section - @SuppressWarnings("unchecked") - Map contentRecommendations = (Map) analysisJson - .get("contentRecommendations"); - if (contentRecommendations != null) { - markdown.append("## Рекомендации по контенту\n\n"); - @SuppressWarnings("unchecked") - List salesContent = (List) contentRecommendations.get("salesContent"); - if (salesContent != null && !salesContent.isEmpty()) { - markdown.append("### Продающий контент:\n"); - for (String content : salesContent) { - markdown.append("- ").append(content).append("\n"); - } - markdown.append("\n"); - } - @SuppressWarnings("unchecked") - List expertContent = (List) contentRecommendations.get("expertContent"); - if (expertContent != null && !expertContent.isEmpty()) { - markdown.append("### Экспертный контент:\n"); - for (String content : expertContent) { - markdown.append("- ").append(content).append("\n"); - } - markdown.append("\n"); - } - @SuppressWarnings("unchecked") - List trustContent = (List) contentRecommendations.get("trustContent"); - if (trustContent != null && !trustContent.isEmpty()) { - markdown.append("### Доверительный контент:\n"); - for (String content : trustContent) { - markdown.append("- ").append(content).append("\n"); - } - markdown.append("\n"); - } - @SuppressWarnings("unchecked") - List entertainmentContent = (List) contentRecommendations.get("entertainmentContent"); - if (entertainmentContent != null && !entertainmentContent.isEmpty()) { - markdown.append("### Развлекательный контент:\n"); - for (String content : entertainmentContent) { - markdown.append("- ").append(content).append("\n"); - } - markdown.append("\n"); - } - } - } + // ВАЖНО: Секции "Позиционирование", "УТП", "Рекомендации по контенту" УДАЛЕНЫ, + // так как они уже включены в fullAnalysis (генерируется методом + // generatePolishedTextReport) + // и вызывали дублирование контента в отчете // Display full analysis - это основной блок с полным отчетом - // Секции "Целевая аудитория", "Рекомендации", "Стратегия" уже включены в - // fullAnalysis - // благодаря методу generatePolishedTextReport, поэтому не дублируем их + // Все секции (I-XI) уже включены в fullAnalysis благодаря + // generatePolishedTextReport String fullAnalysis = (String) reportData.get("fullAnalysis"); if (fullAnalysis != null && !fullAnalysis.trim().isEmpty()) { // Получаем данные для графиков и заменяем плейсхолдеры @@ -3119,20 +3078,17 @@ public class MarketingAnalysisService { Map chartsData = (Map) reportData.get("chartsData"); String processedAnalysis = replaceChartPlaceholders(fullAnalysis, chartsData); - markdown.append("## Полный маркетинговый анализ\n\n"); + // Добавляем полный отчет без дополнительного заголовка, + // так как fullAnalysis уже начинается с "I. Краткое резюме" markdown.append(processedAnalysis).append("\n\n"); } else { // Fallback to summary if fullAnalysis is not available String summary = (String) reportData.get("summary"); if (summary != null && !summary.trim().isEmpty()) { - markdown.append("## Комплексный маркетинговый анализ\n\n"); markdown.append(summary).append("\n\n"); } } - // Секции targetAudience, recommendations, strategy УДАЛЕНЫ, - // так как они уже включены в fullAnalysis и вызывали дублирование - return markdown.toString(); }