From dde3573e34c850c3ae16069c968c5efbe4f8189c Mon Sep 17 00:00:00 2001 From: arys Date: Sun, 25 Jan 2026 21:42:57 +0500 Subject: [PATCH] fix --- .../parser/dto/MarketingAnalysisRequest.java | 2 +- .../dto/MarketingAnalysisResponseV2.java | 23 +- .../service/MarketingAnalysisService.java | 753 ++++++++++++------ 3 files changed, 513 insertions(+), 265 deletions(-) diff --git a/src/main/java/kz/konturai/parser/dto/MarketingAnalysisRequest.java b/src/main/java/kz/konturai/parser/dto/MarketingAnalysisRequest.java index ee54279..8ab709c 100644 --- a/src/main/java/kz/konturai/parser/dto/MarketingAnalysisRequest.java +++ b/src/main/java/kz/konturai/parser/dto/MarketingAnalysisRequest.java @@ -53,7 +53,7 @@ public class MarketingAnalysisRequest { @Size(max = 500, message = "Поле 'weakSide' должно содержать не более 500 символов") private String weakSide; - + @NotEmpty(message = "Поле 'analysisType' обязательно для заполнения. Выберите хотя бы один тип анализа") @ValidAnalysisType(message = "Поле 'analysisType' должно содержать один или несколько типов: РЫНОК, КОНКУРЕНТЫ, ЦА, КАНАЛЫ, SWOT") private List analysisType; diff --git a/src/main/java/kz/konturai/parser/dto/MarketingAnalysisResponseV2.java b/src/main/java/kz/konturai/parser/dto/MarketingAnalysisResponseV2.java index 3e05da0..e43dec0 100644 --- a/src/main/java/kz/konturai/parser/dto/MarketingAnalysisResponseV2.java +++ b/src/main/java/kz/konturai/parser/dto/MarketingAnalysisResponseV2.java @@ -128,8 +128,7 @@ public class MarketingAnalysisResponseV2 { public static class CompetitiveEnvironment { private String title; private CompetitiveEnvironmentMetrics metrics; - private Map digitalVisibilityBenchmarks; - private Map> competitorStrengths; + private List competitors; private String conclusion; } @@ -139,11 +138,23 @@ public class MarketingAnalysisResponseV2 { @NoArgsConstructor @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public static class CompetitiveEnvironmentMetrics { - private String marketShareTraffic; - private String visibilityRank; - private String competitorShareInContextAds; - private String benchmarkAstana; + private String leaderMarketShare; + private String competitionLevel; + private String adPressure; + private String marketLeaderName; + } + @Setter + @Getter + @AllArgsConstructor + @NoArgsConstructor + @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) + public static class CompetitorRow { + private String name; + private String website; + private Integer visibilityScore; + private List keyStrengths; + private String priceSegment; } @Setter diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index 3ea204f..d4b2740 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -635,38 +635,57 @@ public class MarketingAnalysisService { } public MarketingAnalysisResponseV2 generateAnalysisV2(MarketingAnalysisRequest request) { - logger.info("Starting Marketing Analysis V2 (Big 4 Standard) for request: {}", request); + logger.info(">>> Starting Marketing Analysis V2 for Niche: '{}', Region: '{}'", + request.getBusinessNiche(), request.getRegion()); try { - // 1. Сбор данных + // ------------------------------------------------------- + // 1. Сбор данных (Research Phase) + // ------------------------------------------------------- + // Собираем данные из Google/SerpApi, чтобы у GPT был контекст (реальные сайты, статьи) Map researchPack = buildResearchPack(request); + + // Пытаемся найти 3-5 реальных конкурентов через поиск для подстановки в промпт List competitorCompanies = resolveCompetitorCompaniesForBenchmarks(request, researchPack, 5); researchPack.put("competitor_companies", competitorCompanies); - // 2. Пример JSON (Обновлен под КЗ) + // ------------------------------------------------------- + // 2. Формирование "Идеального примера" (Structure & Benchmarks) + // ------------------------------------------------------- + // Мы создаем Java-объект с "умными" рыбами (бенчмарками), который GPT будет использовать как шаблон. + // Это гарантирует, что структура JSON будет 1-в-1 как ждет фронтенд. MarketingAnalysisResponseV2 exampleDto = buildAnalysisV2ExampleForPrompt(request, competitorCompanies); + + // Сериализуем пример в строку, чтобы вставить в промпт String structureExampleJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(exampleDto); - // 3. Формирование промпта (Безопасный метод!) + // ------------------------------------------------------- + // 3. Формирование Промпта (System & User Instructions) + // ------------------------------------------------------- String systemPrompt = buildSystemPromptForKazakhstan(); String instruction = buildDetailedInstruction(request, structureExampleJson); + // Собираем весь контекст для отправки String contextJson = objectMapper.writeValueAsString(Map.of( - "request", request, - "evidence", researchPack + "user_request", request, + "market_evidence_from_search", researchPack )); + // ------------------------------------------------------- + // 4. Вызов AI с механизмом Retry (на случай битого JSON) + // ------------------------------------------------------- for (int attempt = 1; attempt <= 2; attempt++) { try { - // Вызов AI + logger.info("Sending request to OpenAI (Attempt {}/2)...", attempt); + String aiResponse = openAIAnalyticsService.generateWithInstructionWithModel( contextJson, instruction, "ru", - textModelName, // GPT-4o + textModelName, // GPT-4o или o1-preview systemPrompt, - 16000, - 180000L + 16000, // Max tokens (увеличенный лимит для детального ответа) + 180000L // Timeout (3 минуты) ); if (aiResponse == null || aiResponse.isBlank()) { @@ -674,16 +693,19 @@ public class MarketingAnalysisService { } String cleaned = cleanJsonFromMarkdown(aiResponse); - if (cleaned != null) cleaned = cleaned.replace("\uFEFF", "").trim(); - - + if (cleaned != null) { + cleaned = cleaned.replace("\uFEFF", "").trim(); + } MarketingAnalysisResponseV2 parsed = objectMapper.readValue(cleaned, MarketingAnalysisResponseV2.class); replacePlaceholderCompanyKeysIfNeeded(parsed, competitorCompanies); + + logger.info("<<< Analysis V2 generated successfully."); return parsed; + } catch (Exception e) { logger.error("JSON Parsing/Generation failed on attempt {}: {}", attempt, e.getMessage()); - if (attempt == 2) throw e; - Thread.sleep(2000); + if (attempt == 2) throw e; // Если второй раз не вышло — пробрасываем ошибку + Thread.sleep(2000); // Пауза перед повтором } } throw new IllegalStateException("Failed to generate valid analysis after retries."); @@ -706,66 +728,81 @@ public class MarketingAnalysisService { private String buildDetailedInstruction(MarketingAnalysisRequest request, String structureExampleJson) { String regionsList = (request.getRegion() != null && !request.getRegion().isEmpty()) ? String.join(", ", request.getRegion()) - : "Алматы, Астана, Шымкент"; + : "Алматы, Астана, Шымкент"; // Дефолтные города, если не выбраны String ageRangesInstruction = ""; if (request.getTargetAudience() != null && request.getTargetAudience().containsKey("ageRanges")) { @SuppressWarnings("unchecked") List ranges = (List) request.getTargetAudience().get("ageRanges"); if (ranges != null && !ranges.isEmpty()) { - ageRangesInstruction = "Используй ТОЛЬКО эти возрастные группы: " + String.join(", ", ranges); + ageRangesInstruction = "Для 'age_structure' и 'income_by_age' используй СТРОГО только эти возрастные группы: " + String.join(", ", ranges) + "."; } } - // Шаблон с плейсхолдерами {{KEY}} вместо %s String template = """ - Твоя задача: Провести глубокий маркетинговый анализ и вернуть его СТРОГО в формате JSON. - - ВХОДНЫЕ ДАННЫЕ: - Ниша: {{NICHE}} - Продукт: {{PRODUCT}} - - ----------------------------------------------------------------------- - КРИТИЧЕСКИЕ ИНСТРУКЦИИ ПО КАЗАХСТАНУ (KZ): - - 1. ГЕОГРАФИЯ (market_overview.geography): - - Ты ОБЯЗАН включить в анализ ВСЕ следующие города: [{{REGIONS}}]. - - Не ограничивайся тремя городами, если в списке их больше. - - Значения (0-100) должны быть разными и неровными (например, 87, 64, 43). - - 2. КОНКУРЕНТЫ (competitive_environment): - - Найди в `evidence` РЕАЛЬНЫХ лидеров рынка (сети, крупные игроки из 2GIS/Kaspi). - - Игнорируй мелкие ИП или выдуманные названия. - - Заполни `digital_visibility_benchmarks` реальными именами. - - 3. КАНАЛЫ (acquisition_channels): - - Приоритет в РК: Instagram (Reels), TikTok, Kaspi Marketing, 2GIS. - - Яндекс.Директ и VK ставь ниже (Low/Medium), если это B2C. - - Указывай стоимость лида (CPL) строкой с валютой (напр. "2350 KZT"). - - 4. ЦИФРЫ И ТИПЫ ДАННЫХ (КРИТИЧНО!): - - `income_by_age` (TargetAudience): ЭТО ЧИСЛОВОЕ ПОЛЕ (Integer). - Пиши ТОЛЬКО ГОЛЫЕ ЦИФРЫ. Без кавычек, без запятых, без пробелов, без "KZT". - ПРАВИЛЬНО: 342500 - ОШИБКА: "342,500", "342 500", "342500 KZT". - - `monthly_online_index`: Дробные числа (0.0 - 1.0). - - `marketSize` (String): Пиши текстом с валютой ("500 млн KZT"). - - ЗАПРЕЩЕНО использовать круглые числа в статистике (20%, 50%, 1000). Пиши: 18.7%, 42.3%, 1240. - - 5. ИСТОЧНИКИ: - - Собери ВСЕ ссылки из `evidence` в ОДИН СПИСОК в самом конце (поле `recommendations.conclusion`). - - Не вставляй ссылки внутрь текста разделов. - - ----------------------------------------------------------------------- - {{AGE_INSTRUCTION}} - - ФОРМАТ ОТВЕТА: - - Только валидный JSON. - - Заполни все поля `EXAMPLE__` и `-9999` реальными данными. - - ЭТАЛОННАЯ СТРУКТУРА: - {{JSON_STRUCTURE}} - """; +Твоя роль: Ты — Ведущий Маркетинговый Аналитик с опытом работы в Big4 (KPMG, PwC) по рынку Казахстана. +Твоя задача: Провести глубокий, честный и критический анализ рынка для реального бизнеса. Верни результат СТРОГО в формате JSON. + +ВХОДНЫЕ ДАННЫЕ: +Ниша: {{NICHE}} +Продукт/Услуга: {{PRODUCT}} +Целевой регион: {{REGIONS}} + +================================================================================ +СТРОГИЕ ИНСТРУКЦИИ ПО ЗАПОЛНЕНИЮ (NO HALLUCINATIONS POLICY): + +1. **Market Overview (Обзор рынка):** + - `online_demand_index`: НЕ ПИШИ ПРОСТО "Высокий". Пиши "Высокий (8/10)" или "Средний (растущий)". + - `demand_growth_rate_yoy`: Используй некруглые числа (напр., "+12.4%", а не "10%"). + - `geography`: + - ОБЯЗАТЕЛЬНО включи в ответ ВСЕ города из списка: [{{REGIONS}}]. + - Если список городов пуст или содержит "Весь Казахстан", добавь топ-5 городов (Алматы, Астана, Шымкент, Караганда, Актобе). + - Значения (Integer 0-100) должны отражать РЕАЛЬНЫЙ спрос (Алматы всегда выше регионов). Разброс должен быть реалистичным (не все по 100). + +2. **Target Audience (Целевая аудитория):** + - `income_by_age`: ЭТО ЧИСЛОВОЕ ПОЛЕ (Integer). Пиши ТОЛЬКО ЦИФРЫ (средняя ЗП в тенге). + - ПРАВИЛЬНО: 350000 + - ОШИБКА: "350 000", "350k KZT". + - Ориентируйся на реальные зарплаты в РК (200k - 600k KZT). + - `metrics.total_audience_size`: Пиши текстом, но реалистично (напр. "450-500 тыс. человек"), а не "10 млн". + +3. **Competitive Environment (Конкуренты - КРИТИЧНО!):** + - В поле `competitors` (массив объектов) найди 3-5 РЕАЛЬНЫХ конкурентов в Казахстане. + - **НАЗВАНИЯ (`name`):** Пиши ТОЛЬКО БРЕНД. + - ПРАВИЛЬНО: "Invictus", "World Class", "1Fit", "Kaspi Shop". + - ОШИБКА: "ТОО Invictus Group", "ИП Иванов", "Фитнес клуб Ромашка". + - **САЙТЫ (`website`):** Если знаешь реальный сайт, пиши его (напр. "invictus.kz"). Если нет — ищи профиль в Instagram ("instagram.com/invictus.kz"). + - `visibility_score`: Оценка от 0 до 100. Лидеры рынка > 80. + - `metrics.leader_market_share`: Оцени долю лидера (напр. "35%"). + +4. **Acquisition Channels (Каналы):** + - В Казахстане (B2C) доминируют: Instagram (Reels/Stories), TikTok, Kaspi Marketing (для товаров), 2GIS (для оффлайн-точек). + - `average_lead_cost`: Пиши текстом с валютой ("2500 KZT"). Оценивай реально: фитнес ~3000-5000тг, товары ~1500-3000тг. + +5. **Marketing Economics (Экономика - Бюджет до 3 млн KZT):** + - Мы рассчитываем модель для малого/среднего бизнеса. + - `marketing_expenses`: Указывай бюджет в диапазоне 500 000 - 3 000 000 KZT в месяц. НЕ БОЛЬШЕ. + - `cac` (Стоимость клиента): Должна быть адекватной (не 50 тенге). + - `roi`: Реалистичный (100% - 300%), а не 5000%. + +6. **Customer Journey (Воронка):** + - `funnel_metrics`: Используй реалистичную воронку. + - Traffic: ~10 000 - 50 000 + - Leads: ~2% - 5% от трафика + - Sales: ~10% - 30% от лидов. + +7. **Источники (Sources):** + - Собери РЕАЛЬНЫЕ ссылки (Forbes.kz, Kapital.kz, Stat.gov.kz, сайты конкурентов) в массив `sources`. + +================================================================================ +{{AGE_INSTRUCTION}} + +ФОРМАТ ОТВЕТА: ТОЛЬКО JSON. Без Markdown, без "```json". +Используй структуру ниже как ОБРАЗЕЦ, но заполни её СВОИМИ ДАННЫМИ для ниши {{NICHE}}. + +ОБРАЗЕЦ СТРУКТУРЫ (JSON): +{{JSON_STRUCTURE}} +"""; return template .replace("{{NICHE}}", request.getBusinessNiche()) @@ -776,21 +813,182 @@ public class MarketingAnalysisService { } private String buildJsonStructureExample() { return """ - { - "report_title": "Стратегический анализ рынка...", - "sources": ["https://stat.gov.kz/...", "https://forbes.kz/..."], - "sections": { - "market_overview": { "metrics": { "online_demand_index": "0.8" }, "conclusion": "Текст..." }, - "acquisition_channels": { - "channel_shares": { "Instagram": "40%", "Kaspi Shop": "30%", "Google SEO": "20%" }, - "conclusion": "Текст..." +{ + "report_title": "Стратегический анализ рынка: [Ниша]", + "sources": [ + "[https://stat.gov.kz/](https://stat.gov.kz/)...", + "[https://forbes.kz/](https://forbes.kz/)...", + "[https://kapital.kz/](https://kapital.kz/)..." + ], + "sections": { + "market_overview": { + "title": "Обзор рынка", + "metrics": { + "online_demand_index": "Высокий (8/10)", + "online_potential": "Растущий (+15% YoY)", + "demand_growth_rate_yoy": "+18.5%", + "seasonality_coefficient_spring": "1.2", + "seasonality_range": "Сентябрь - Май" + }, + "monthly_online_index": { + "january": 0.8, "february": 0.9, "march": 1.0, "april": 0.9, "may": 0.8, + "june": 0.6, "july": 0.5, "august": 0.7, "september": 0.9, + "october": 0.95, "november": 1.0, "december": 0.9 + }, + "geography": { + "Алматы": 100, + "Астана": 85, + "Шымкент": 50 + }, + "conclusion": "Рынок демонстрирует устойчивый рост..." + }, + "target_audience": { + "title": "Портрет аудитории", + "metrics": { + "total_audience_size": "~450 000 чел.", + "leading_segment_share": "Женщины 25-34", + "above_average_income_share": "35%" + }, + "income_by_age": { + "25-34": 350000, + "35-44": 420000 + }, + "segments": [ + { "name": "Ядро (Core)", "share": "45%" }, + { "name": "Потенциальные", "share": "30%" } + ], + "age_structure": { + "25-34": "45%", + "35-44": "30%" + }, + "conclusion": "Ключевой сегмент - платежеспособные специалисты..." + }, + "competitive_environment": { + "title": "Конкурентная среда", + "metrics": { + "leader_market_share": "35%", + "competition_level": "Высокая", + "ad_pressure": "Агрессивная", + "market_leader_name": "Invictus" + }, + "competitors": [ + { + "name": "Invictus", + "website": "invictus.kz", + "visibility_score": 95, + "key_strengths": ["Сильный бренд", "Сеть залов"], + "price_segment": "Premium" }, - "recommendations": { "core_pillars": [ { "strategy": "...", "action": "..." } ] } - } + { + "name": "1Fit", + "website": "1fit.app", + "visibility_score": 88, + "key_strengths": ["Единый абонемент", "Удобное приложение"], + "price_segment": "Aggregator" + } + ], + "conclusion": "Рынок консолидирован крупными игроками..." + }, + "demand_and_behavior": { + "title": "Поведение потребителей", + "metrics": { + "search_volume_growth": "+22%", + "interactions_before_purchase": "5-7 касаний", + "repeat_refusal_probability": "15%" + }, + "search_query_types": { + "Коммерческие": "45%", + "Информационные": "30%" + }, + "decision_factors": { + "Цена": "Высокая важность", + "Отзывы": "Средняя важность" + }, + "reasons_for_refusal": { + "Высокая цена": "40%", + "Плохой сервис": "30%" + }, + "conclusion": "Пользователи активно сравнивают цены..." + }, + "customer_journey": { + "title": "Воронка продаж", + "funnel_metrics": { + "ad_traffic": "15 000 кликов", + "site_visitors": "12 000 (CTR 1.5%)", + "leads": 450, + "bookings": 200, + "payments": 120 + }, + "conversion_rates": { + "lead_to_booking": "45%", + "booking_to_payment": "60%", + "lead_to_payment_total": "27%" + }, + "funnel_bottlenecks": { + "lead_to_booking": "Долгий ответ менеджера", + "booking_to_payment": "Нет рассрочки" + }, + "conclusion": "Основная потеря лидов происходит на этапе..." + }, + "acquisition_channels": { + "title": "Каналы привлечения", + "metrics": { + "search_traffic_share": "30%", + "average_lead_cost": "3500 KZT", + "growth_potential": "TikTok/Reels" + }, + "channel_shares": { + "Instagram": "50%", + "Google Ads": "20%", + "TikTok": "15%" + }, + "channel_efficiency": { + "instagram": { "cost": "2500 KZT", "status": "High" }, + "google_search": { "cost": "4500 KZT", "status": "Medium" } + }, + "conclusion": "Instagram остается самым эффективным каналом..." + }, + "marketing_economics": { + "title": "Юнит-экономика", + "metrics": { + "customer_acquisition_cost_cac": "3500 KZT", + "repeat_sales_conversion": "30%", + "roi": "250%" + }, + "financial_performance": { + "marketing_expenses": { "month": "1.5 млн KZT", "year": "18 млн KZT" }, + "clients": { "month": "~430", "year": "~5160" }, + "cac": { "month": "3500 KZT", "year": "3200 KZT" }, + "revenue_from_client": { "month": "25 000 KZT", "year": "300 000 KZT" }, + "profit_from_client": { "month": "7 500 KZT", "year": "90 000 KZT" } + }, + "conclusion": "Модель масштабируема при сохранении CAC..." + }, + "bottlenecks": { + "title": "Узкие места", + "statistics": { + "landing_page_issues": "Отказы 60%", + "slow_manager_responses": "30% потерь" + }, + "details": { + "landing": ["Медленная загрузка", "Слабый оффер"], + "lead_management": ["Нет скриптов", "Звонок > 15 мин"], + "retention": ["Нет программы лояльности"] + }, + "conclusion": "Необходимо оптимизировать мобильную версию сайта..." + }, + "recommendations": { + "title": "План действий", + "core_pillars": [ + { "strategy": "Быстрый старт", "action": "Запустить таргет..." }, + { "strategy": "Удержание", "action": "Внедрить систему бонусов..." } + ], + "conclusion": "Рекомендуется начать с..." } - """; + } +} +"""; } - private boolean isPlaceholderCompetitorKey(String key) { if (key == null) { return false; @@ -1002,7 +1200,8 @@ public class MarketingAnalysisService { } private void replacePlaceholderCompanyKeysIfNeeded(MarketingAnalysisResponseV2 parsed, - List competitorCompanies) { + List competitorCompanies) { + // 1. Базовые проверки на null if (parsed == null || competitorCompanies == null || competitorCompanies.isEmpty()) { return; } @@ -1012,31 +1211,35 @@ public class MarketingAnalysisService { MarketingAnalysisResponseV2.CompetitiveEnvironment ce = parsed.getSections().getCompetitiveEnvironment(); - Map dvb = ce.getDigitalVisibilityBenchmarks(); - if (dvb != null && !dvb.isEmpty()) { - boolean hasPlaceholders = dvb.keySet().stream().anyMatch(this::isPlaceholderCompetitorKey); - if (hasPlaceholders) { - List values = new ArrayList<>(dvb.values()); - LinkedHashMap rewritten = new LinkedHashMap<>(); - int n = Math.min(values.size(), competitorCompanies.size()); - for (int i = 0; i < n; i++) { - rewritten.put(competitorCompanies.get(i), values.get(i)); - } - ce.setDigitalVisibilityBenchmarks(rewritten); - } - } + // Получаем список конкурентов из DTO + List rows = ce.getCompetitors(); + + if (rows != null && !rows.isEmpty()) { + // Проверяем, есть ли заглушки хотя бы у одного элемента + boolean hasPlaceholders = rows.stream() + .anyMatch(row -> isPlaceholderCompetitorKey(row.getName())); - Map> strengths = ce.getCompetitorStrengths(); - if (strengths != null && !strengths.isEmpty()) { - boolean hasPlaceholders = strengths.keySet().stream().anyMatch(this::isPlaceholderCompetitorKey); if (hasPlaceholders) { - List> values = new ArrayList<>(strengths.values()); - LinkedHashMap> rewritten = new LinkedHashMap<>(); - int n = Math.min(values.size(), competitorCompanies.size()); + // Определяем, сколько элементов мы можем обновить (чтобы не выйти за пределы массивов) + int n = Math.min(rows.size(), competitorCompanies.size()); + for (int i = 0; i < n; i++) { - rewritten.put(competitorCompanies.get(i), values.get(i)); + MarketingAnalysisResponseV2.CompetitorRow row = rows.get(i); + + // Если имя текущего конкурента - заглушка, меняем на реальное + if (isPlaceholderCompetitorKey(row.getName())) { + String realCompetitorName = competitorCompanies.get(i); + row.setName(realCompetitorName); + + // ЛОГИКА ДЛЯ САЙТА: + // Если у нас нет сайта от ГПТ, или если реальное имя похоже на домен, + // можно записать его и в website для надежности. + if (row.getWebsite() == null || row.getWebsite().isEmpty() || row.getWebsite().equals("—")) { + // Предполагаем, что в competitorCompanies могут лежать домены + row.setWebsite(realCompetitorName); + } + } } - ce.setCompetitorStrengths(rewritten); } } } @@ -1094,242 +1297,276 @@ public class MarketingAnalysisService { } private MarketingAnalysisResponseV2 buildAnalysisV2ExampleForPrompt(MarketingAnalysisRequest request, - List competitorCompanies) { + List competitorCompanies) { MarketingAnalysisResponseV2 response = new MarketingAnalysisResponseV2(); - response.setReportTitle("EXAMPLE__REPORT_TITLE"); + response.setReportTitle("Стратегический анализ рынка: " + request.getBusinessNiche()); MarketingAnalysisResponseV2.Sections sections = new MarketingAnalysisResponseV2.Sections(); + // 1. Market Overview MarketingAnalysisResponseV2.MarketOverview marketOverview = new MarketingAnalysisResponseV2.MarketOverview(); - marketOverview.setTitle("EXAMPLE__MARKET_OVERVIEW_TITLE"); + marketOverview.setTitle("Обзор рынка"); + MarketingAnalysisResponseV2.MarketOverviewMetrics moMetrics = new MarketingAnalysisResponseV2.MarketOverviewMetrics(); - moMetrics.setOnlineDemandIndex("EXAMPLE__ONLINE_DEMAND_INDEX"); - moMetrics.setOnlinePotential("EXAMPLE__ONLINE_POTENTIAL"); - moMetrics.setDemandGrowthRateYoy("EXAMPLE__DEMAND_GROWTH_RATE_YOY"); - moMetrics.setSeasonalityCoefficientSpring("EXAMPLE__SEASONALITY_COEFFICIENT_SPRING"); - moMetrics.setSeasonalityRange("EXAMPLE__SEASONALITY_RANGE"); + moMetrics.setOnlineDemandIndex("Высокий (Активный рост)"); + moMetrics.setOnlinePotential("Растущий тренд (+15-20% YoY)"); + moMetrics.setDemandGrowthRateYoy("+18.5%"); + moMetrics.setSeasonalityCoefficientSpring("1.25 (Высокий сезон)"); + moMetrics.setSeasonalityRange("Сентябрь - Май"); marketOverview.setMetrics(moMetrics); + MarketingAnalysisResponseV2.MonthlyOnlineIndex monthlyOnlineIndex = new MarketingAnalysisResponseV2.MonthlyOnlineIndex(); - monthlyOnlineIndex.setJanuary(-9999.0); - monthlyOnlineIndex.setFebruary(-9999.0); - monthlyOnlineIndex.setMarch(-9999.0); - monthlyOnlineIndex.setApril(-9999.0); - monthlyOnlineIndex.setMay(-9999.0); - monthlyOnlineIndex.setJune(-9999.0); - monthlyOnlineIndex.setJuly(-9999.0); - monthlyOnlineIndex.setAugust(-9999.0); - monthlyOnlineIndex.setSeptember(-9999.0); - monthlyOnlineIndex.setOctober(-9999.0); - monthlyOnlineIndex.setNovember(-9999.0); - monthlyOnlineIndex.setDecember(-9999.0); + // Реалистичная кривая сезонности (спад летом, рост к НГ и весне) + monthlyOnlineIndex.setJanuary(0.95); + monthlyOnlineIndex.setFebruary(0.90); + monthlyOnlineIndex.setMarch(1.0); // Пик + monthlyOnlineIndex.setApril(0.95); + monthlyOnlineIndex.setMay(0.85); + monthlyOnlineIndex.setJune(0.70); // Спад + monthlyOnlineIndex.setJuly(0.60); + monthlyOnlineIndex.setAugust(0.75); // Подготовка к сезону + monthlyOnlineIndex.setSeptember(0.90); + monthlyOnlineIndex.setOctober(0.95); + monthlyOnlineIndex.setNovember(1.0); // Black Friday + monthlyOnlineIndex.setDecember(0.98); marketOverview.setMonthlyOnlineIndex(monthlyOnlineIndex); + + // География (Проценты, сумма ~100, приоритет Алматы/Астана) marketOverview.setGeography(Map.of( - "EXAMPLE__CITY_1", -9999, - "EXAMPLE__CITY_2", -9999, - "EXAMPLE__CITY_3", -9999)); - marketOverview.setConclusion("EXAMPLE__MARKET_OVERVIEW_CONCLUSION"); + "Алматы", 45, + "Астана", 30, + "Шымкент", 10, + "Караганда", 8, + "Актобе", 5, + "Другие регионы", 2 + )); + marketOverview.setConclusion("Рынок находится в стадии активного роста. Основной спрос сконцентрирован в мегаполисах (75%), сезонность ярко выражена с пиками весной и осенью."); sections.setMarketOverview(marketOverview); + // 2. Target Audience MarketingAnalysisResponseV2.TargetAudience targetAudience = new MarketingAnalysisResponseV2.TargetAudience(); - targetAudience.setTitle("EXAMPLE__TARGET_AUDIENCE_TITLE"); + targetAudience.setTitle("Портрет целевой аудитории"); + MarketingAnalysisResponseV2.TargetAudienceMetrics taMetrics = new MarketingAnalysisResponseV2.TargetAudienceMetrics(); - taMetrics.setTotalAudienceSize("EXAMPLE__TOTAL_AUDIENCE_SIZE"); - taMetrics.setLeadingSegmentShare("EXAMPLE__LEADING_SEGMENT_SHARE"); - taMetrics.setAboveAverageIncomeShare("EXAMPLE__ABOVE_AVERAGE_INCOME_SHARE"); + taMetrics.setTotalAudienceSize("~350 000 - 500 000 активных пользователей"); + taMetrics.setLeadingSegmentShare("Женщины 25-34 года"); + taMetrics.setAboveAverageIncomeShare("42% (Средний+)"); targetAudience.setMetrics(taMetrics); + targetAudience.setSegments(List.of( - new MarketingAnalysisResponseV2.AudienceSegment("EXAMPLE__AUDIENCE_SEGMENT_1", - "EXAMPLE__AUDIENCE_SEGMENT_SHARE_1"), - new MarketingAnalysisResponseV2.AudienceSegment("EXAMPLE__AUDIENCE_SEGMENT_2", - "EXAMPLE__AUDIENCE_SEGMENT_SHARE_2"), - new MarketingAnalysisResponseV2.AudienceSegment("EXAMPLE__AUDIENCE_SEGMENT_3", - "EXAMPLE__AUDIENCE_SEGMENT_SHARE_3"))); + new MarketingAnalysisResponseV2.AudienceSegment("Ядро (Core)", "45% - Покупают регулярно"), + new MarketingAnalysisResponseV2.AudienceSegment("Потенциальные", "30% - Сравнивают цены"), + new MarketingAnalysisResponseV2.AudienceSegment("Ситуативные", "25% - Ищут акции") + )); + + // Возрастная структура (Реальная демография активных покупателей) Map ageStructure = new LinkedHashMap<>(); - Map targetAudienceData = request.getTargetAudience(); - if (targetAudienceData != null && targetAudienceData.containsKey("ageRanges")) { - @SuppressWarnings("unchecked") - List ageRanges = (List) targetAudienceData.get("ageRanges"); - if (ageRanges != null && !ageRanges.isEmpty()) { - for (String ageRange : ageRanges) { - String normalizedKey = ageRange.replace("+", "_plus"); - String placeholderKey = normalizedKey.replace("-", "_").toUpperCase(); - ageStructure.put(normalizedKey, "EXAMPLE__AGE_" + placeholderKey + "_SHARE"); - } - } else { - ageStructure.put("25-34", "EXAMPLE__AGE_25_34_SHARE"); - ageStructure.put("35-44", "EXAMPLE__AGE_35_44_SHARE"); - } - } else { - ageStructure.put("25-34", "EXAMPLE__AGE_25_34_SHARE"); - ageStructure.put("35-44", "EXAMPLE__AGE_35_44_SHARE"); - } + ageStructure.put("18-24", "15%"); + ageStructure.put("25-34", "45%"); // Платежеспособное ядро + ageStructure.put("35-44", "25%"); + ageStructure.put("45+", "15%"); targetAudience.setAgeStructure(ageStructure); - targetAudience.setConclusion("EXAMPLE__TARGET_AUDIENCE_CONCLUSION"); + + // Доходы (Медиана по РК для среднего класса) + Map incomeByAge = new LinkedHashMap<>(); + incomeByAge.put("18-24", 180000); + incomeByAge.put("25-34", 350000); + incomeByAge.put("35-44", 420000); + incomeByAge.put("45+", 300000); + targetAudience.setIncomeByAge(incomeByAge); + + targetAudience.setConclusion("Ключевой клиент — специалист/менеджер 25-35 лет с доходом выше среднего, ценит сервис и скорость. Вторичный сегмент — студенты и молодежь (чувствительны к цене)."); sections.setTargetAudience(targetAudience); + // 3. Competitive Environment (ОБНОВЛЕННАЯ СТРУКТУРА) MarketingAnalysisResponseV2.CompetitiveEnvironment competitiveEnvironment = new MarketingAnalysisResponseV2.CompetitiveEnvironment(); - competitiveEnvironment.setTitle("EXAMPLE__COMPETITIVE_ENVIRONMENT_TITLE"); + competitiveEnvironment.setTitle("Конкурентный ландшафт"); + MarketingAnalysisResponseV2.CompetitiveEnvironmentMetrics ceMetrics = new MarketingAnalysisResponseV2.CompetitiveEnvironmentMetrics(); - ceMetrics.setMarketShareTraffic("EXAMPLE__MARKET_SHARE_TRAFFIC"); - ceMetrics.setVisibilityRank("EXAMPLE__VISIBILITY_RANK"); - ceMetrics.setCompetitorShareInContextAds("EXAMPLE__COMPETITOR_SHARE_IN_CONTEXT_ADS"); - ceMetrics.setBenchmarkAstana("EXAMPLE__BENCHMARK_ASTANA"); + ceMetrics.setLeaderMarketShare("45-50%"); // Доля топ-игроков + ceMetrics.setCompetitionLevel("Высокая (Красный океан)"); + ceMetrics.setAdPressure("Агрессивная (Высокие ставки CPI)"); + ceMetrics.setMarketLeaderName("Сетевые лидеры / Маркетплейсы"); competitiveEnvironment.setMetrics(ceMetrics); - Map digitalVisibilityBenchmarks = new LinkedHashMap<>(); - List companies = (competitorCompanies != null) ? competitorCompanies : List.of(); - if (companies.isEmpty()) { - companies = List.of("Компания 1", "Компания 2", "Компания 3", "Компания 4", "Компания 5"); - } + + // Список конкурентов (для таблицы и графика) + List competitorRows = new java.util.ArrayList<>(); + List companies = (competitorCompanies != null && !competitorCompanies.isEmpty()) + ? competitorCompanies + : List.of("Крупный сетевой бренд", "Агрессивный дискаунтер", "Премиум-игрок"); + + // Заполняем 3-5 конкурентов данными для примера for (int i = 0; i < Math.min(companies.size(), 5); i++) { - digitalVisibilityBenchmarks.put(companies.get(i), -9999); + MarketingAnalysisResponseV2.CompetitorRow row = new MarketingAnalysisResponseV2.CompetitorRow(); + row.setName(companies.get(i)); + row.setWebsite("https://google.com/search?q=" + companies.get(i).replace(" ", "+")); // Ссылка-заглушка + row.setVisibilityScore(90 - (i * 12)); // 90, 78, 66... + row.setKeyStrengths(List.of("Сильный бренд", "Широкая сеть", "Активный SMM")); + row.setPriceSegment(i == 0 ? "Premium" : (i == 1 ? "Low-cost" : "Middle")); + competitorRows.add(row); } - competitiveEnvironment.setDigitalVisibilityBenchmarks(digitalVisibilityBenchmarks); - Map> competitorStrengths = new LinkedHashMap<>(); - for (int i = 0; i < Math.min(companies.size(), 5); i++) { - competitorStrengths.put(companies.get(i), - List.of("EXAMPLE__COMPETITOR_STRENGTH_1", "EXAMPLE__COMPETITOR_STRENGTH_2")); - } - competitiveEnvironment.setCompetitorStrengths(competitorStrengths); - competitiveEnvironment.setConclusion("EXAMPLE__COMPETITIVE_ENVIRONMENT_CONCLUSION"); + competitiveEnvironment.setCompetitors(competitorRows); + + competitiveEnvironment.setConclusion("Рынок консолидирован крупными игроками. Для входа требуется четкое УТП или узкая нишевание, прямая ценовая конкуренция неэффективна."); sections.setCompetitiveEnvironment(competitiveEnvironment); + // 4. Demand And Behavior MarketingAnalysisResponseV2.DemandAndBehavior demandAndBehavior = new MarketingAnalysisResponseV2.DemandAndBehavior(); - demandAndBehavior.setTitle("EXAMPLE__DEMAND_AND_BEHAVIOR_TITLE"); + demandAndBehavior.setTitle("Паттерны поведения"); + MarketingAnalysisResponseV2.DemandAndBehaviorMetrics dabMetrics = new MarketingAnalysisResponseV2.DemandAndBehaviorMetrics(); - dabMetrics.setSearchVolumeGrowth("EXAMPLE__SEARCH_VOLUME_GROWTH"); - dabMetrics.setInteractionsBeforePurchase("EXAMPLE__INTERACTIONS_BEFORE_PURCHASE"); - dabMetrics.setRepeatRefusalProbability("EXAMPLE__REPEAT_REFUSAL_PROBABILITY"); + dabMetrics.setSearchVolumeGrowth("+22% YoY"); + dabMetrics.setInteractionsBeforePurchase("5-7 касаний"); + dabMetrics.setRepeatRefusalProbability("~15% (Сравнение цен)"); demandAndBehavior.setMetrics(dabMetrics); + Map searchQueryTypes = new LinkedHashMap<>(); - searchQueryTypes.put("direct_interest_catalog", "EXAMPLE__QUERY_TYPE_DIRECT"); - searchQueryTypes.put("question_based_articles", "EXAMPLE__QUERY_TYPE_QUESTION"); - searchQueryTypes.put("competitors_reviews", "EXAMPLE__QUERY_TYPE_COMPETITORS"); - searchQueryTypes.put("problem_solving_hot_promo", "EXAMPLE__QUERY_TYPE_PROMO"); + searchQueryTypes.put("Коммерческие (купить, цена)", "45%"); + searchQueryTypes.put("Информационные (отзывы, как выбрать)", "30%"); + searchQueryTypes.put("Брендовые (имя конкурента)", "15%"); + searchQueryTypes.put("Гео-запросы (рядом, адрес)", "10%"); demandAndBehavior.setSearchQueryTypes(searchQueryTypes); + Map decisionFactors = new LinkedHashMap<>(); - decisionFactors.put("promo_discount", "EXAMPLE__FACTOR_PROMO"); - decisionFactors.put("review_recommendation", "EXAMPLE__FACTOR_REVIEWS"); - decisionFactors.put("warranty_quality", "EXAMPLE__FACTOR_WARRANTY"); - decisionFactors.put("convenience_service", "EXAMPLE__FACTOR_CONVENIENCE"); - decisionFactors.put("availability", "EXAMPLE__FACTOR_AVAILABILITY"); - decisionFactors.put("price", "EXAMPLE__FACTOR_PRICE"); + decisionFactors.put("Цена / Рассрочка", "35%"); + decisionFactors.put("Отзывы (Social Proof)", "25%"); + decisionFactors.put("Скорость / Сервис", "20%"); + decisionFactors.put("Личный бренд / Доверие", "15%"); + decisionFactors.put("Удобство оплаты", "5%"); demandAndBehavior.setDecisionFactors(decisionFactors); + Map reasonsForRefusal = new LinkedHashMap<>(); - reasonsForRefusal.put("high_price", "EXAMPLE__REFUSAL_HIGH_PRICE"); - reasonsForRefusal.put("no_reviews_social_proof", "EXAMPLE__REFUSAL_NO_REVIEWS"); - reasonsForRefusal.put("choice_difficulty_unclear_offer", "EXAMPLE__REFUSAL_UNCLEAR_OFFER"); + reasonsForRefusal.put("Скрытые условия / Нет цены", "40%"); + reasonsForRefusal.put("Плохие отзывы / Нет кейсов", "30%"); + reasonsForRefusal.put("Долгая реакция менеджера", "30%"); demandAndBehavior.setReasonsForRefusal(reasonsForRefusal); - demandAndBehavior.setConclusion("EXAMPLE__DEMAND_AND_BEHAVIOR_CONCLUSION"); + + demandAndBehavior.setConclusion("Покупатель стал избирательным: он читает отзывы, сравнивает цены и ищет социальные доказательства. Критически важна прозрачность условий и скорость ответа."); sections.setDemandAndBehavior(demandAndBehavior); + // 5. Customer Journey MarketingAnalysisResponseV2.CustomerJourney customerJourney = new MarketingAnalysisResponseV2.CustomerJourney(); - customerJourney.setTitle("EXAMPLE__CUSTOMER_JOURNEY_TITLE"); + customerJourney.setTitle("Воронка продаж"); + MarketingAnalysisResponseV2.FunnelMetrics funnelMetrics = new MarketingAnalysisResponseV2.FunnelMetrics(); - funnelMetrics.setAdTraffic("EXAMPLE__FUNNEL_AD_TRAFFIC"); - funnelMetrics.setSiteVisitors("EXAMPLE__FUNNEL_SITE_VISITORS"); - funnelMetrics.setLeads(-9999); - funnelMetrics.setBookings(-9999); - funnelMetrics.setPayments(-9999); + funnelMetrics.setAdTraffic("~15 000 показов"); + funnelMetrics.setSiteVisitors("~800 переходов (CTR 1.5%)"); // Средний CTR + funnelMetrics.setLeads(150); // Конверсия лендинга ~3-5% (Реалистично для B2C) + funnelMetrics.setBookings(60); // Квал. лиды (40% от заявок) + funnelMetrics.setPayments(45); // Продажи (Close Rate 30% от квал. лидов) customerJourney.setFunnelMetrics(funnelMetrics); + MarketingAnalysisResponseV2.ConversionRates conversionRates = new MarketingAnalysisResponseV2.ConversionRates(); - conversionRates.setLeadToBooking("EXAMPLE__LEAD_TO_BOOKING"); - conversionRates.setBookingToPayment("EXAMPLE__BOOKING_TO_PAYMENT"); - conversionRates.setLeadToPaymentTotal("EXAMPLE__LEAD_TO_PAYMENT_TOTAL"); + conversionRates.setLeadToBooking("40%"); // Квалификация + conversionRates.setBookingToPayment("75%"); // Дожим + conversionRates.setLeadToPaymentTotal("30%"); // Общая конверсия в продажу customerJourney.setConversionRates(conversionRates); + MarketingAnalysisResponseV2.FunnelBottlenecks funnelBottlenecks = new MarketingAnalysisResponseV2.FunnelBottlenecks(); - funnelBottlenecks.setLeadToBooking("EXAMPLE__BOTTLENECK_LEAD_TO_BOOKING"); - funnelBottlenecks.setBookingToPayment("EXAMPLE__BOTTLENECK_BOOKING_TO_PAYMENT"); + funnelBottlenecks.setLeadToBooking("Недозвон / Долгая реакция на заявку"); + funnelBottlenecks.setBookingToPayment("Возражение 'Дорого' / Отсутствие рассрочки"); customerJourney.setFunnelBottlenecks(funnelBottlenecks); - customerJourney.setConclusion("EXAMPLE__CUSTOMER_JOURNEY_CONCLUSION"); + + customerJourney.setConclusion("Узкое место — обработка входящих заявок. Потеря 60% лидов на этапе квалификации говорит о проблемах в скриптах или скорости реакции."); sections.setCustomerJourney(customerJourney); - // acquisition_channels + // 6. Acquisition Channels MarketingAnalysisResponseV2.AcquisitionChannels acquisitionChannels = new MarketingAnalysisResponseV2.AcquisitionChannels(); - acquisitionChannels.setTitle("EXAMPLE__ACQUISITION_CHANNELS_TITLE"); + acquisitionChannels.setTitle("Каналы трафика"); + MarketingAnalysisResponseV2.AcquisitionChannelsMetrics acMetrics = new MarketingAnalysisResponseV2.AcquisitionChannelsMetrics(); - acMetrics.setSearchTrafficShare("EXAMPLE__SEARCH_TRAFFIC_SHARE"); - acMetrics.setAverageLeadCost("EXAMPLE__AVERAGE_LEAD_COST"); - acMetrics.setGrowthPotential("EXAMPLE__GROWTH_POTENTIAL"); + acMetrics.setSearchTrafficShare("~30% (SEO + Context)"); + acMetrics.setAverageLeadCost("2500 - 4500 KZT"); // Реальный CPL для услуг + acMetrics.setGrowthPotential("TikTok / Reels (Short Video)"); acquisitionChannels.setMetrics(acMetrics); + Map channelShares = new LinkedHashMap<>(); - channelShares.put("instagram_target", "EXAMPLE__SHARE_INSTA"); - channelShares.put("tiktok_ads", "EXAMPLE__SHARE_TIKTOK"); - channelShares.put("kaspi_marketing", "EXAMPLE__SHARE_KASPI"); - channelShares.put("google_ads", "EXAMPLE__SHARE_GOOGLE"); - channelShares.put("2gis_ads", "EXAMPLE__SHARE_2GIS"); - acquisitionChannels.setChannelShares(channelShares); + channelShares.put("Instagram (Target + Content)", "50%"); + channelShares.put("Google Ads (Search)", "20%"); + channelShares.put("TikTok (Ads + Organic)", "15%"); + channelShares.put("2GIS / Maps", "10%"); + channelShares.put("Other (Referral)", "5%"); acquisitionChannels.setChannelShares(channelShares); + Map channelEfficiency = new LinkedHashMap<>(); channelEfficiency.put("instagram", - new MarketingAnalysisResponseV2.ChannelEfficiency("EXAMPLE__CPL_INSTA", "EXAMPLE__EFF_HIGH")); + new MarketingAnalysisResponseV2.ChannelEfficiency("2000-3500 ₸", "High (Масштаб)")); channelEfficiency.put("tiktok", - new MarketingAnalysisResponseV2.ChannelEfficiency("EXAMPLE__CPL_TIKTOK", "EXAMPLE__EFF_HIGH")); + new MarketingAnalysisResponseV2.ChannelEfficiency("1500-2500 ₸", "High (Дешево)")); channelEfficiency.put("kaspi", - new MarketingAnalysisResponseV2.ChannelEfficiency("EXAMPLE__CPL_KASPI", "EXAMPLE__EFF_VERY_HIGH")); + new MarketingAnalysisResponseV2.ChannelEfficiency("5% CPA", "Very High (Горячие)")); channelEfficiency.put("google_search", - new MarketingAnalysisResponseV2.ChannelEfficiency("EXAMPLE__CPL_GOOGLE", "EXAMPLE__EFF_MEDIUM")); + new MarketingAnalysisResponseV2.ChannelEfficiency("4000-6000 ₸", "Medium (Дорого)")); channelEfficiency.put("2gis", - new MarketingAnalysisResponseV2.ChannelEfficiency("EXAMPLE__CPL_2GIS", "EXAMPLE__EFF_LOCAL")); - channelEfficiency.put("threads_meta", - new MarketingAnalysisResponseV2.ChannelEfficiency("EXAMPLE__CPL_THREADS", "EXAMPLE__EFF_NEW")); + new MarketingAnalysisResponseV2.ChannelEfficiency("Бесплатно", "Local (Гео)")); acquisitionChannels.setChannelEfficiency(channelEfficiency); - acquisitionChannels.setConclusion("EXAMPLE__ACQUISITION_CHANNELS_CONCLUSION"); + + acquisitionChannels.setConclusion("Instagram остается основным драйвером продаж. Google Ads эффективен, но дорог. Рекомендуется диверсификация через TikTok для снижения стоимости лида."); sections.setAcquisitionChannels(acquisitionChannels); - // marketing_economics + // 7. Marketing Economics MarketingAnalysisResponseV2.MarketingEconomics marketingEconomics = new MarketingAnalysisResponseV2.MarketingEconomics(); - marketingEconomics.setTitle("EXAMPLE__MARKETING_ECONOMICS_TITLE"); + marketingEconomics.setTitle("Юнит-экономика"); + MarketingAnalysisResponseV2.MarketingEconomicsMetrics meMetrics = new MarketingAnalysisResponseV2.MarketingEconomicsMetrics(); - meMetrics.setCustomerAcquisitionCostCac("EXAMPLE__CAC"); - meMetrics.setRepeatSalesConversion("EXAMPLE__REPEAT_SALES_CONVERSION"); - meMetrics.setRoi("EXAMPLE__ROI"); + meMetrics.setCustomerAcquisitionCostCac("3 500 KZT"); // Целевой CAC + meMetrics.setRepeatSalesConversion("25-30%"); // LTV потенциал + meMetrics.setRoi("250%"); // Здоровый ROI (3.5x ROAS) marketingEconomics.setMetrics(meMetrics); + MarketingAnalysisResponseV2.FinancialPerformance financialPerformance = new MarketingAnalysisResponseV2.FinancialPerformance(); - financialPerformance - .setMarketingExpenses( - new MarketingAnalysisResponseV2.MonthYearValue("EXAMPLE__MARKETING_EXPENSES_MONTH", - "EXAMPLE__MARKETING_EXPENSES_YEAR")); - financialPerformance.setClients(new MarketingAnalysisResponseV2.MonthYearValue("EXAMPLE__CLIENTS_MONTH", - "EXAMPLE__CLIENTS_YEAR")); - financialPerformance - .setCac(new MarketingAnalysisResponseV2.MonthYearValue("EXAMPLE__CAC_MONTH", "EXAMPLE__CAC_YEAR")); - financialPerformance - .setRevenueFromClient(new MarketingAnalysisResponseV2.MonthYearValue("EXAMPLE__REV_PER_CLIENT_MONTH", - "EXAMPLE__REV_PER_CLIENT_YEAR")); - financialPerformance - .setProfitFromClient(new MarketingAnalysisResponseV2.MonthYearValue("EXAMPLE__PROFIT_PER_CLIENT_MONTH", - "EXAMPLE__PROFIT_PER_CLIENT_YEAR")); + // Бюджет 1.5 млн (Адекватная цифра для малого/среднего бизнеса) + financialPerformance.setMarketingExpenses( + new MarketingAnalysisResponseV2.MonthYearValue("1.5 млн KZT", "18 млн KZT")); + + // Клиенты: 1.5млн / 3500 = ~428 клиентов + financialPerformance.setClients( + new MarketingAnalysisResponseV2.MonthYearValue("~430", "~5 160")); + + financialPerformance.setCac( + new MarketingAnalysisResponseV2.MonthYearValue("3 500 KZT", "3 200 KZT (Оптимизация)")); + + // Выручка (Чек 25 000 * 430 = 10.7 млн) + financialPerformance.setRevenueFromClient( + new MarketingAnalysisResponseV2.MonthYearValue("25 000 KZT (Чек)", "300 000 KZT (LTV)")); + + // Прибыль (Маржа 30%) + financialPerformance.setProfitFromClient( + new MarketingAnalysisResponseV2.MonthYearValue("7 500 KZT", "90 000 KZT")); + marketingEconomics.setFinancialPerformance(financialPerformance); - marketingEconomics.setConclusion("EXAMPLE__MARKETING_ECONOMICS_CONCLUSION"); + marketingEconomics.setConclusion("При текущем CAC модель масштабируема. Основной рычаг роста прибыли — увеличение LTV (повторных покупок) и среднего чека."); sections.setMarketingEconomics(marketingEconomics); - // bottlenecks + // 8. Bottlenecks MarketingAnalysisResponseV2.Bottlenecks bottlenecks = new MarketingAnalysisResponseV2.Bottlenecks(); - bottlenecks.setTitle("EXAMPLE__BOTTLENECKS_TITLE"); + bottlenecks.setTitle("Точки роста и проблемы"); + Map statistics = new LinkedHashMap<>(); - statistics.put("landing_page_issues", "EXAMPLE__STAT_LANDING_PAGE_ISSUES"); - statistics.put("slow_manager_responses", "EXAMPLE__STAT_SLOW_MANAGER_RESPONSES"); - statistics.put("repeat_interaction_refusal", "EXAMPLE__STAT_REPEAT_INTERACTION_REFUSAL"); + statistics.put("landing_page_issues", "Высокий показатель отказов (60%)"); + statistics.put("slow_manager_responses", "Звонок > 15 мин (30% потерь)"); + statistics.put("repeat_interaction_refusal", "Нет дожима в CRM (20% потерь)"); bottlenecks.setStatistics(statistics); + MarketingAnalysisResponseV2.BottlenecksDetails details = new MarketingAnalysisResponseV2.BottlenecksDetails(); - details.setLanding(List.of("EXAMPLE__LANDING_ISSUE_1", "EXAMPLE__LANDING_ISSUE_2", "EXAMPLE__LANDING_ISSUE_3")); - details.setLeadManagement( - List.of("EXAMPLE__LEAD_MGMT_ISSUE_1", "EXAMPLE__LEAD_MGMT_ISSUE_2", "EXAMPLE__LEAD_MGMT_ISSUE_3")); - details.setRetention( - List.of("EXAMPLE__RETENTION_ISSUE_1", "EXAMPLE__RETENTION_ISSUE_2", "EXAMPLE__RETENTION_ISSUE_3")); + details.setLanding(List.of("Долгая загрузка на мобильных", "Непонятный первый экран (УТП)", "Сложная форма заявки")); + details.setLeadManagement(List.of("Менеджеры не перезванивают", "Нет скриптов продаж", "Нет интеграции с WhatsApp")); + details.setRetention(List.of("Нет программы лояльности", "Нет сбора обратной связи (NPS)", "Нет Email-маркетинга")); bottlenecks.setDetails(details); - bottlenecks.setConclusion("EXAMPLE__BOTTLENECKS_CONCLUSION"); + + bottlenecks.setConclusion("Технические ошибки сайта и человеческий фактор в продажах — главные ограничители роста. Их устранение может удвоить конверсию без увеличения бюджета."); sections.setBottlenecks(bottlenecks); - // recommendations + // 9. Recommendations MarketingAnalysisResponseV2.Recommendations recommendations = new MarketingAnalysisResponseV2.Recommendations(); - recommendations.setTitle("EXAMPLE__RECOMMENDATIONS_TITLE"); + recommendations.setTitle("План действий"); recommendations.setCorePillars(List.of( - new MarketingAnalysisResponseV2.CorePillar("EXAMPLE__PILLAR_1_TITLE", "EXAMPLE__PILLAR_1_DESC"), - new MarketingAnalysisResponseV2.CorePillar("EXAMPLE__PILLAR_2_TITLE", "EXAMPLE__PILLAR_2_DESC"), - new MarketingAnalysisResponseV2.CorePillar("EXAMPLE__PILLAR_3_TITLE", "EXAMPLE__PILLAR_3_DESC"), - new MarketingAnalysisResponseV2.CorePillar("EXAMPLE__PILLAR_4_TITLE", "EXAMPLE__PILLAR_4_DESC"))); - recommendations.setConclusion("EXAMPLE__RECOMMENDATIONS_CONCLUSION"); + new MarketingAnalysisResponseV2.CorePillar("Быстрые победы", "Запустить ретаргетинг на посетителей сайта и внедрить авто-ответ в WhatsApp."), + new MarketingAnalysisResponseV2.CorePillar("Оптимизация сайта", "Упростить форму заявки до 2 полей (Имя, Телефон) и ускорить мобильную версию."), + new MarketingAnalysisResponseV2.CorePillar("Контент-маркетинг", "Снять 10 Reels с отзывами клиентов и процессом работы для повышения доверия."), + new MarketingAnalysisResponseV2.CorePillar("Систематизация", "Внедрить CRM для контроля пропущенных звонков и задач менеджеров.") + )); + recommendations.setConclusion("Фокус на ближайший квартал: повышение конверсии сайта и автоматизация первичной обработки лидов."); sections.setRecommendations(recommendations); response.setSections(sections);