From 6672056aec7fac0a98ea1f07a8d2218c011820b2 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 11 Dec 2025 11:09:43 +0500 Subject: [PATCH] . --- .../service/MarketingAnalysisService.java | 324 +++++++++++++++++- 1 file changed, 322 insertions(+), 2 deletions(-) diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index 49a89cb..db56814 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -403,6 +403,326 @@ public class MarketingAnalysisService { } } + /** + * Глубокая генерация текстового отчёта на основе JSON (4 этапа) + */ + private String generateDeepTextReport(Map analysisJson) { + if (analysisJson == null || analysisJson.isEmpty()) { + logger.warn("Empty analysisJson provided for deep text report generation"); + return "Не удалось сгенерировать текстовый отчёт: отсутствуют данные JSON-анализа."; + } + + try { + // Преобразование JSON в строку для контекста + String jsonContext = objectMapper.writeValueAsString(analysisJson); + + // Генерация всех 4 этапов последовательно + String stage1 = generateStage1MarketAndProduct(jsonContext); + String stage2 = generateStage2AudienceAndCompetitors(jsonContext); + String stage3 = generateStage3StrategyAndFunnel(jsonContext); + String stage4 = generateStage4RecommendationsAndContent(jsonContext); + + // Склеивание всех частей с двойными переносами строк + StringBuilder fullReport = new StringBuilder(); + + if (stage1 != null && !stage1.trim().isEmpty()) { + fullReport.append(stage1.trim()); + } else { + fullReport.append("I. Краткое резюме\n\nII. Анализ продукта\n\nIII. Анализ рынка и сезонности\n\n"); + logger.warn("Stage 1 generation returned empty result"); + } + + if (stage2 != null && !stage2.trim().isEmpty()) { + if (fullReport.length() > 0) { + fullReport.append("\n\n"); + } + fullReport.append(stage2.trim()); + } else { + if (fullReport.length() > 0) { + fullReport.append("\n\n"); + } + fullReport.append("IV. Анализ целевой аудитории\n\nV. Анализ конкурентов\n\n"); + logger.warn("Stage 2 generation returned empty result"); + } + + if (stage3 != null && !stage3.trim().isEmpty()) { + if (fullReport.length() > 0) { + fullReport.append("\n\n"); + } + fullReport.append(stage3.trim()); + } else { + if (fullReport.length() > 0) { + fullReport.append("\n\n"); + } + fullReport.append( + "VI. SWOT-анализ\n\nVII. Каналы продвижения и их потенциал\n\nVIII. Воронка спроса\n\n"); + logger.warn("Stage 3 generation returned empty result"); + } + + if (stage4 != null && !stage4.trim().isEmpty()) { + if (fullReport.length() > 0) { + fullReport.append("\n\n"); + } + fullReport.append(stage4.trim()); + } else { + if (fullReport.length() > 0) { + fullReport.append("\n\n"); + } + fullReport.append( + "IX. Рекомендации по позиционированию и УТП\n\nX. Рекомендации по контенту\n\nXI. Итоговая стратегия: что делать в первую очередь\n\n"); + logger.warn("Stage 4 generation returned empty result"); + } + + return fullReport.toString(); + } catch (Exception e) { + logger.error("Error generating deep text report from JSON: {}", e.getMessage(), e); + return "Ошибка при генерации текстового отчёта: " + e.getMessage(); + } + } + + /** + * Этап 1: Рынок и Продукт + * Разделы: I. Краткое резюме, II. Анализ продукта, III. Анализ рынка и + * сезонности + */ + private String generateStage1MarketAndProduct(String jsonContext) { + try { + StringBuilder promptBuilder = new StringBuilder(); + promptBuilder.append("Ты — профессиональный маркетолог и аналитик.\n\n"); + promptBuilder.append("Тебе передан JSON с результатами маркетингового анализа.\n\n"); + promptBuilder + .append("ТВОЯ ЗАДАЧА: Напиши ТОЛЬКО следующие разделы отчета (без вступлений, без пояснений):\n\n"); + promptBuilder.append("I. Краткое резюме\n"); + promptBuilder.append(" Опиши бизнес, его цель и общий вывод (2-3 абзаца).\n\n"); + promptBuilder.append("II. Анализ продукта\n"); + promptBuilder.append( + " Детально проанализируй продукт/услугу, его особенности, преимущества и позиционирование.\n"); + promptBuilder.append(" Пиши максимально подробно, анализируй тренды.\n\n"); + promptBuilder.append("III. Анализ рынка и сезонности\n"); + promptBuilder.append(" Проанализируй рынок, его динамику, сезонные колебания спроса.\n"); + promptBuilder.append(" Используй данные из поля \"seasonality\" JSON.\n"); + promptBuilder.append( + " Вставь плейсхолдер [[CHART_SEASONALITY]] в логичном месте раздела, где график будет уместен.\n"); + promptBuilder.append(" Пиши максимально подробно, анализируй тренды.\n\n"); + promptBuilder.append("ВАЖНО:\n"); + promptBuilder.append("- Верни ТОЛЬКО эти три раздела, без вступлений типа \"Конечно, вот ваш отчет...\"\n"); + promptBuilder.append("- Начинай сразу с \"I. Краткое резюме\"\n"); + promptBuilder.append("- Используй данные ТОЛЬКО из переданного JSON\n"); + promptBuilder.append("- Пиши простым, деловым языком, с акцентом на практическую пользу\n"); + promptBuilder.append("- Не выдумывай новые цифры — используй только те, что есть в JSON\n"); + + String prompt = promptBuilder.toString(); + String result = openAIAnalyticsService.generateWithInstruction(jsonContext, prompt, "ru"); + + if (result == null || result.trim().isEmpty()) { + logger.warn("Stage 1 returned null or empty"); + return null; + } + + // Очистка от возможных вступлений + String cleaned = result.trim(); + if (cleaned.toLowerCase().startsWith("конечно") || + cleaned.toLowerCase().startsWith("вот") || + cleaned.toLowerCase().startsWith("ниже")) { + // Пытаемся найти начало первого раздела + int firstSection = cleaned.indexOf("I. "); + if (firstSection > 0) { + cleaned = cleaned.substring(firstSection); + } + } + + return cleaned; + } catch (Exception e) { + logger.error("Error generating stage 1 (Market and Product): {}", e.getMessage(), e); + return null; + } + } + + /** + * Этап 2: Аудитория и Конкуренты + * Разделы: IV. Анализ целевой аудитории, V. Анализ конкурентов + */ + private String generateStage2AudienceAndCompetitors(String jsonContext) { + try { + StringBuilder promptBuilder = new StringBuilder(); + promptBuilder.append("Ты — профессиональный маркетолог и аналитик.\n\n"); + promptBuilder.append("Тебе передан JSON с результатами маркетингового анализа.\n\n"); + promptBuilder + .append("ТВОЯ ЗАДАЧА: Напиши ТОЛЬКО следующие разделы отчета (без вступлений, без пояснений):\n\n"); + promptBuilder.append("IV. Анализ целевой аудитории\n"); + promptBuilder.append(" Проведи глубокий психологический разбор целевой аудитории.\n"); + promptBuilder.append(" Используй данные из поля \"audienceSegments\" JSON.\n"); + promptBuilder.append( + " Опиши демографические характеристики, психографический профиль, потребности, поведенческие паттерны.\n"); + promptBuilder.append( + " Вставь плейсхолдер [[CHART_AUDIENCE]] в логичном месте раздела, где график будет уместен.\n"); + promptBuilder.append(" Сделай глубокий психологический разбор аудитории.\n\n"); + promptBuilder.append("V. Анализ конкурентов\n"); + promptBuilder.append(" Детально сравни конкурентов.\n"); + promptBuilder.append(" Используй данные из поля \"competitors\" JSON.\n"); + promptBuilder.append(" Опиши их сильные и слабые стороны, позиционирование, стратегии.\n"); + promptBuilder.append( + " Вставь плейсхолдер [[CHART_COMPETITORS]] в логичном месте раздела, где график будет уместен.\n"); + promptBuilder.append(" Детально сравни конкурентов.\n\n"); + promptBuilder.append("ВАЖНО:\n"); + promptBuilder.append("- Верни ТОЛЬКО эти два раздела, без вступлений типа \"Конечно, вот ваш отчет...\"\n"); + promptBuilder.append("- Начинай сразу с \"IV. Анализ целевой аудитории\"\n"); + promptBuilder.append("- Используй данные ТОЛЬКО из переданного JSON\n"); + promptBuilder.append("- Пиши простым, деловым языком, с акцентом на практическую пользу\n"); + promptBuilder.append("- Не выдумывай новые цифры — используй только те, что есть в JSON\n"); + + String prompt = promptBuilder.toString(); + String result = openAIAnalyticsService.generateWithInstruction(jsonContext, prompt, "ru"); + + if (result == null || result.trim().isEmpty()) { + logger.warn("Stage 2 returned null or empty"); + return null; + } + + // Очистка от возможных вступлений + String cleaned = result.trim(); + if (cleaned.toLowerCase().startsWith("конечно") || + cleaned.toLowerCase().startsWith("вот") || + cleaned.toLowerCase().startsWith("ниже")) { + // Пытаемся найти начало первого раздела + int firstSection = cleaned.indexOf("IV. "); + if (firstSection > 0) { + cleaned = cleaned.substring(firstSection); + } + } + + return cleaned; + } catch (Exception e) { + logger.error("Error generating stage 2 (Audience and Competitors): {}", e.getMessage(), e); + return null; + } + } + + /** + * Этап 3: Стратегия и Воронка + * Разделы: VI. SWOT-анализ, VII. Каналы продвижения, VIII. Воронка спроса + */ + private String generateStage3StrategyAndFunnel(String jsonContext) { + try { + StringBuilder promptBuilder = new StringBuilder(); + promptBuilder.append("Ты — профессиональный маркетолог и аналитик.\n\n"); + promptBuilder.append("Тебе передан JSON с результатами маркетингового анализа.\n\n"); + promptBuilder + .append("ТВОЯ ЗАДАЧА: Напиши ТОЛЬКО следующие разделы отчета (без вступлений, без пояснений):\n\n"); + promptBuilder.append("VI. SWOT-анализ\n"); + promptBuilder.append( + " Представь SWOT-анализ в виде 4 списков: Сильные стороны, Слабые стороны, Возможности, Угрозы.\n"); + promptBuilder.append(" Используй данные из поля \"swot\" JSON.\n"); + promptBuilder.append( + " Вставь плейсхолдер [[CHART_SWOT]] в логичном месте раздела, где график/таблица будет уместна.\n\n"); + promptBuilder.append("VII. Каналы продвижения и их потенциал\n"); + promptBuilder.append(" Опиши рекомендуемые каналы продвижения.\n"); + promptBuilder.append(" Используй данные из поля \"channels\" JSON.\n"); + promptBuilder.append(" Дай обоснование каждому каналу (почему он важен, как его использовать).\n"); + promptBuilder.append( + " Вставь плейсхолдер [[CHART_CHANNELS]] в логичном месте раздела, где график будет уместен.\n\n"); + promptBuilder.append("VIII. Воронка спроса\n"); + promptBuilder.append(" Опиши этапы воронки спроса.\n"); + promptBuilder.append(" Используй данные из поля \"funnel\" JSON.\n"); + promptBuilder.append(" Опиши узкие места воронки (где происходят наибольшие потери конверсии).\n"); + promptBuilder.append( + " Вставь плейсхолдер [[CHART_FUNNEL]] в логичном месте раздела, где график будет уместен.\n\n"); + promptBuilder.append("ВАЖНО:\n"); + promptBuilder.append("- Верни ТОЛЬКО эти три раздела, без вступлений типа \"Конечно, вот ваш отчет...\"\n"); + promptBuilder.append("- Начинай сразу с \"VI. SWOT-анализ\"\n"); + promptBuilder.append("- Используй данные ТОЛЬКО из переданного JSON\n"); + promptBuilder.append("- Пиши простым, деловым языком, с акцентом на практическую пользу\n"); + promptBuilder.append("- Не выдумывай новые цифры — используй только те, что есть в JSON\n"); + + String prompt = promptBuilder.toString(); + String result = openAIAnalyticsService.generateWithInstruction(jsonContext, prompt, "ru"); + + if (result == null || result.trim().isEmpty()) { + logger.warn("Stage 3 returned null or empty"); + return null; + } + + // Очистка от возможных вступлений + String cleaned = result.trim(); + if (cleaned.toLowerCase().startsWith("конечно") || + cleaned.toLowerCase().startsWith("вот") || + cleaned.toLowerCase().startsWith("ниже")) { + // Пытаемся найти начало первого раздела + int firstSection = cleaned.indexOf("VI. "); + if (firstSection > 0) { + cleaned = cleaned.substring(firstSection); + } + } + + return cleaned; + } catch (Exception e) { + logger.error("Error generating stage 3 (Strategy and Funnel): {}", e.getMessage(), e); + return null; + } + } + + /** + * Этап 4: Рекомендации и Контент + * Разделы: IX. Позиционирование и УТП, X. Рекомендации по контенту, XI. + * Итоговая стратегия (Roadmap) + */ + private String generateStage4RecommendationsAndContent(String jsonContext) { + try { + StringBuilder promptBuilder = new StringBuilder(); + promptBuilder.append("Ты — профессиональный маркетолог и аналитик.\n\n"); + promptBuilder.append("Тебе передан JSON с результатами маркетингового анализа.\n\n"); + promptBuilder + .append("ТВОЯ ЗАДАЧА: Напиши ТОЛЬКО следующие разделы отчета (без вступлений, без пояснений):\n\n"); + promptBuilder.append("IX. Рекомендации по позиционированию и УТП\n"); + promptBuilder.append(" Опиши позиционирование бренда и уникальное торговое предложение.\n"); + promptBuilder.append(" Используй данные из полей \"positioning\" и \"valueProposition\" JSON.\n"); + promptBuilder.append(" Представь практические рекомендации по позиционированию.\n\n"); + promptBuilder.append("X. Рекомендации по контенту\n"); + promptBuilder.append(" Дай практические рекомендации по типам контента для маркетинга.\n"); + promptBuilder.append(" Используй данные из поля \"contentRecommendations\" JSON.\n"); + promptBuilder + .append(" Опиши, какой контент использовать для продаж, экспертизы, доверия, развлечения.\n\n"); + promptBuilder.append("XI. Итоговая стратегия: что делать в первую очередь\n"); + promptBuilder.append(" Создай практический Roadmap (дорожную карту) действий.\n"); + promptBuilder.append(" Укажи конкретные шаги, приоритеты, последовательность действий.\n"); + promptBuilder.append( + " Пиши практические шаги, бери данные из полей \"positioning\", \"valueProposition\", \"contentRecommendations\" JSON.\n\n"); + promptBuilder.append("ВАЖНО:\n"); + promptBuilder.append("- Верни ТОЛЬКО эти три раздела, без вступлений типа \"Конечно, вот ваш отчет...\"\n"); + promptBuilder.append("- Начинай сразу с \"IX. Рекомендации по позиционированию и УТП\"\n"); + promptBuilder.append("- Используй данные ТОЛЬКО из переданного JSON\n"); + promptBuilder.append("- Пиши простым, деловым языком, с акцентом на практическую пользу\n"); + promptBuilder.append("- Не выдумывай новые цифры — используй только те, что есть в JSON\n"); + promptBuilder.append( + "- Пиши практические шаги, бери данные из полей positioning, valueProposition, contentRecommendations\n"); + + String prompt = promptBuilder.toString(); + String result = openAIAnalyticsService.generateWithInstruction(jsonContext, prompt, "ru"); + + if (result == null || result.trim().isEmpty()) { + logger.warn("Stage 4 returned null or empty"); + return null; + } + + // Очистка от возможных вступлений + String cleaned = result.trim(); + if (cleaned.toLowerCase().startsWith("конечно") || + cleaned.toLowerCase().startsWith("вот") || + cleaned.toLowerCase().startsWith("ниже")) { + // Пытаемся найти начало первого раздела + int firstSection = cleaned.indexOf("IX. "); + if (firstSection > 0) { + cleaned = cleaned.substring(firstSection); + } + } + + return cleaned; + } catch (Exception e) { + logger.error("Error generating stage 4 (Recommendations and Content): {}", e.getMessage(), e); + return null; + } + } + /** * Валидация JSON-анализа */ @@ -778,8 +1098,8 @@ public class MarketingAnalysisService { // Сохраняем JSON в отчёт report.put("analysisJson", analysisJson); - // Шаг 2: Генерация текстового отчёта на основе JSON (Промпт №2) - String textReport = generateTextReportFromJson(analysisJson); + // Шаг 2: Глубокая генерация текстового отчёта на основе JSON (4 этапа) + String textReport = generateDeepTextReport(analysisJson); // Извлекаем данные для графиков из JSON Map chartsData = parseChartDataFromJson(analysisJson);