From 68e526292587ec1e58eed66225a95aee0019a93f Mon Sep 17 00:00:00 2001 From: arys Date: Sun, 25 Jan 2026 21:54:07 +0500 Subject: [PATCH] fix --- .../service/MarketingAnalysisService.java | 347 ++++++++---------- 1 file changed, 161 insertions(+), 186 deletions(-) diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index d4b2740..a7cec7e 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -726,83 +726,80 @@ public class MarketingAnalysisService { } private String buildDetailedInstruction(MarketingAnalysisRequest request, String structureExampleJson) { - String regionsList = (request.getRegion() != null && !request.getRegion().isEmpty()) - ? String.join(", ", request.getRegion()) - : "Алматы, Астана, Шымкент"; // Дефолтные города, если не выбраны + String regionsList; + if (request.getRegion() != null && !request.getRegion().isEmpty()) { + regionsList = String.join(", ", request.getRegion()); + } else { + regionsList = "Алматы, Астана, Шымкент, Караганда"; + } + // Логика возрастных групп 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 = "Для 'age_structure' и 'income_by_age' используй СТРОГО только эти возрастные группы: " + String.join(", ", ranges) + "."; + ageRangesInstruction = "В полях 'age_structure' и 'income_by_age' используй СТРОГО только эти возрастные группы: " + String.join(", ", ranges) + "."; } } String template = """ -Твоя роль: Ты — Ведущий Маркетинговый Аналитик с опытом работы в Big4 (KPMG, PwC) по рынку Казахстана. -Твоя задача: Провести глубокий, честный и критический анализ рынка для реального бизнеса. Верни результат СТРОГО в формате JSON. + Твоя роль: Ты — Ведущий Маркетинговый Стратег (Senior Strategy Consultant) по рынку Казахстана. + Задача: Провести глубокий анализ для РЕАЛЬНОГО бизнеса (B2C/B2B). Никакой воды. Только факты, цифры и применимые советы. -ВХОДНЫЕ ДАННЫЕ: -Ниша: {{NICHE}} -Продукт/Услуга: {{PRODUCT}} -Целевой регион: {{REGIONS}} + ВХОДНЫЕ ДАННЫЕ: + Ниша: {{NICHE}} + Продукт: {{PRODUCT}} + Регионы для анализа: [{{REGIONS}}] -================================================================================ -СТРОГИЕ ИНСТРУКЦИИ ПО ЗАПОЛНЕНИЮ (NO HALLUCINATIONS POLICY): + ================================================================================ + КРИТИЧЕСКИЕ ИНСТРУКЦИИ (KZ STANDARDS & NO NULLS): -1. **Market Overview (Обзор рынка):** - - `online_demand_index`: НЕ ПИШИ ПРОСТО "Высокий". Пиши "Высокий (8/10)" или "Средний (растущий)". - - `demand_growth_rate_yoy`: Используй некруглые числа (напр., "+12.4%", а не "10%"). - - `geography`: - - ОБЯЗАТЕЛЬНО включи в ответ ВСЕ города из списка: [{{REGIONS}}]. - - Если список городов пуст или содержит "Весь Казахстан", добавь топ-5 городов (Алматы, Астана, Шымкент, Караганда, Актобе). - - Значения (Integer 0-100) должны отражать РЕАЛЬНЫЙ спрос (Алматы всегда выше регионов). Разброс должен быть реалистичным (не все по 100). + 0. **ЗАПРЕЩЕНО ВОЗВРАЩАТЬ NULL**: + - Каждая секция (`demand_and_behavior`, `bottlenecks`, `recommendations`) ДОЛЖНА быть заполнена. + - Если данных нет, используй экспертную оценку или бенчмарки по нише. + - НИКАКИХ ПУСТЫХ ПОЛЕЙ. + - ВСЕГДА ИСПОЛЬЗУЙ ПРАВИЛЬНЫЙ JSON, ПО ПЛАНКЕ КОТОРУЮ МЫ СКИНУЛ, И ЧТО БЫ ВСЕ ПРАВАИЛЬНО -2. **Target Audience (Целевая аудитория):** - - `income_by_age`: ЭТО ЧИСЛОВОЕ ПОЛЕ (Integer). Пиши ТОЛЬКО ЦИФРЫ (средняя ЗП в тенге). - - ПРАВИЛЬНО: 350000 - - ОШИБКА: "350 000", "350k KZT". - - Ориентируйся на реальные зарплаты в РК (200k - 600k KZT). - - `metrics.total_audience_size`: Пиши текстом, но реалистично (напр. "450-500 тыс. человек"), а не "10 млн". + 1. **ГЕОГРАФИЯ (Market Overview):** + - Ты ОБЯЗАН включить в анализ (`geography`) ТОЛЬКО указанные города: [{{REGIONS}}]. + - Если указан "Весь Казахстан" или список пуст — бери Топ-5 (Алматы, Астана, Шымкент...). + - Распределение: Алматы обычно лидер (40-50%), Астана (30%), регионы меньше. -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%"). + 2. **КОНКУРЕНТЫ (Competitive Environment):** + - Используй данные из `market_evidence_from_search`. Найди 3-5 РЕАЛЬНЫХ игроков. + - **ВАЖНО:** В поле `name` пиши ТОЛЬКО БРЕНД (например, "Fitnation", а не "ТОО Fitnation Group"). Без кавычек и юр. форм. + - В поле `website` попытайся найти реальный домен или ссылку на Instagram. + - Оценка видимости (`visibility_score`) должна быть разной (например: 95, 82, 60, 45). Не ставь всем 100. -4. **Acquisition Channels (Каналы):** - - В Казахстане (B2C) доминируют: Instagram (Reels/Stories), TikTok, Kaspi Marketing (для товаров), 2GIS (для оффлайн-точек). - - `average_lead_cost`: Пиши текстом с валютой ("2500 KZT"). Оценивай реально: фитнес ~3000-5000тг, товары ~1500-3000тг. + 3. **КАНАЛЫ (Acquisition Channels):** + - Приоритет в Казахстане (B2C): **Instagram (Reels/Stories), TikTok, Kaspi (магазин/платежи), 2GIS (карты)**. + - Google Ads — дорогой канал, ставь эффективность "Medium". + - `average_lead_cost`: Пиши текстом с валютой, диапазон (напр. "2500 - 4500 KZT"). -5. **Marketing Economics (Экономика - Бюджет до 3 млн KZT):** - - Мы рассчитываем модель для малого/среднего бизнеса. - - `marketing_expenses`: Указывай бюджет в диапазоне 500 000 - 3 000 000 KZT в месяц. НЕ БОЛЬШЕ. - - `cac` (Стоимость клиента): Должна быть адекватной (не 50 тенге). - - `roi`: Реалистичный (100% - 300%), а не 5000%. + 4. **ЭКОНОМИКА (Marketing Economics):** + - **БЮДЖЕТ:** Рассчитывай модель для малого/среднего бизнеса (SME). + - Бюджет на маркетинг: **500 000 - 3 000 000 KZT** в месяц. НЕ БОЛЬШЕ. + - `cac` (Стоимость привлечения): Адекватная (2000 - 7000 KZT для масс-маркета услуг). + - `roi`: Реалистичный (150% - 300%). Не пиши фантастические 1000% на старте. -6. **Customer Journey (Воронка):** - - `funnel_metrics`: Используй реалистичную воронку. - - Traffic: ~10 000 - 50 000 - - Leads: ~2% - 5% от трафика - - Sales: ~10% - 30% от лидов. + 5. **ТИПЫ ДАННЫХ (Строго соблюдай типы):** + - `income_by_age`: Значения - ТОЛЬКО ЧИСЛА (Integer). Пример: 350000. (Никаких кавычек, запятых, "KZT"). + - `monthly_online_index`: Дробные числа (0.0 - 1.0). -7. **Источники (Sources):** - - Собери РЕАЛЬНЫЕ ссылки (Forbes.kz, Kapital.kz, Stat.gov.kz, сайты конкурентов) в массив `sources`. + 6. **ИСТОЧНИКИ:** + - Собери все ссылки из `evidence` и добавь авторитетные источники (stat.gov.kz, kase.kz, forbes.kz) в массив `sources` в самом конце. -================================================================================ -{{AGE_INSTRUCTION}} + ================================================================================ + {{AGE_INSTRUCTION}} -ФОРМАТ ОТВЕТА: ТОЛЬКО JSON. Без Markdown, без "```json". -Используй структуру ниже как ОБРАЗЕЦ, но заполни её СВОИМИ ДАННЫМИ для ниши {{NICHE}}. + ФОРМАТ ОТВЕТА: + Верни СТРОГО валидный JSON, соответствующий структуре ниже. + Замени все значения `EXAMPLE__` и `-9999` на реальные аналитические данные, специфичные для ниши {{NICHE}}. -ОБРАЗЕЦ СТРУКТУРЫ (JSON): -{{JSON_STRUCTURE}} -"""; + ЭТАЛОННАЯ СТРУКТУРА JSON (Следуй ей неукоснительно): + {{JSON_STRUCTURE}} + """; return template .replace("{{NICHE}}", request.getBusinessNiche()) @@ -1299,11 +1296,16 @@ public class MarketingAnalysisService { private MarketingAnalysisResponseV2 buildAnalysisV2ExampleForPrompt(MarketingAnalysisRequest request, List competitorCompanies) { MarketingAnalysisResponseV2 response = new MarketingAnalysisResponseV2(); - response.setReportTitle("Стратегический анализ рынка: " + request.getBusinessNiche()); + // Заголовок + String niche = (request.getBusinessNiche() != null && !request.getBusinessNiche().isEmpty()) + ? request.getBusinessNiche() : "Анализируемая ниша"; + response.setReportTitle("Стратегический анализ рынка: " + niche); MarketingAnalysisResponseV2.Sections sections = new MarketingAnalysisResponseV2.Sections(); + // --------------------------------------------------------- // 1. Market Overview + // --------------------------------------------------------- MarketingAnalysisResponseV2.MarketOverview marketOverview = new MarketingAnalysisResponseV2.MarketOverview(); marketOverview.setTitle("Обзор рынка"); @@ -1316,37 +1318,36 @@ public class MarketingAnalysisService { marketOverview.setMetrics(moMetrics); MarketingAnalysisResponseV2.MonthlyOnlineIndex monthlyOnlineIndex = new MarketingAnalysisResponseV2.MonthlyOnlineIndex(); - // Реалистичная кривая сезонности (спад летом, рост к НГ и весне) - 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); + 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); monthlyOnlineIndex.setDecember(0.98); marketOverview.setMonthlyOnlineIndex(monthlyOnlineIndex); - // География (Проценты, сумма ~100, приоритет Алматы/Астана) - marketOverview.setGeography(Map.of( - "Алматы", 45, - "Астана", 30, - "Шымкент", 10, - "Караганда", 8, - "Актобе", 5, - "Другие регионы", 2 - )); + // ЛОГИКА ГОРОДОВ: Берем из запроса или ставим дефолт + Map geography = new LinkedHashMap<>(); + List requestedRegions = request.getRegion(); + if (requestedRegions != null && !requestedRegions.isEmpty()) { + // Если юзер выбрал города, используем их + for (String region : requestedRegions) { + geography.put(region, 80); // Примерное значение для промпта + } + } else { + // Дефолт для КЗ + geography.put("Алматы", 45); + geography.put("Астана", 30); + geography.put("Шымкент", 15); + geography.put("Караганда", 10); + } + marketOverview.setGeography(geography); marketOverview.setConclusion("Рынок находится в стадии активного роста. Основной спрос сконцентрирован в мегаполисах (75%), сезонность ярко выражена с пиками весной и осенью."); sections.setMarketOverview(marketOverview); + // --------------------------------------------------------- // 2. Target Audience + // --------------------------------------------------------- MarketingAnalysisResponseV2.TargetAudience targetAudience = new MarketingAnalysisResponseV2.TargetAudience(); targetAudience.setTitle("Портрет целевой аудитории"); - MarketingAnalysisResponseV2.TargetAudienceMetrics taMetrics = new MarketingAnalysisResponseV2.TargetAudienceMetrics(); taMetrics.setTotalAudienceSize("~350 000 - 500 000 активных пользователей"); taMetrics.setLeadingSegmentShare("Женщины 25-34 года"); @@ -1359,189 +1360,162 @@ public class MarketingAnalysisService { new MarketingAnalysisResponseV2.AudienceSegment("Ситуативные", "25% - Ищут акции") )); - // Возрастная структура (Реальная демография активных покупателей) Map ageStructure = new LinkedHashMap<>(); - ageStructure.put("18-24", "15%"); - ageStructure.put("25-34", "45%"); // Платежеспособное ядро - ageStructure.put("35-44", "25%"); - ageStructure.put("45+", "15%"); + ageStructure.put("18-24", "15%"); ageStructure.put("25-34", "45%"); + ageStructure.put("35-44", "25%"); ageStructure.put("45+", "15%"); targetAudience.setAgeStructure(ageStructure); - // Доходы (Медиана по РК для среднего класса) Map incomeByAge = new LinkedHashMap<>(); - incomeByAge.put("18-24", 180000); - incomeByAge.put("25-34", 350000); - incomeByAge.put("35-44", 420000); - incomeByAge.put("45+", 300000); + 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 лет с доходом выше среднего, ценит сервис и скорость. Вторичный сегмент — студенты и молодежь (чувствительны к цене)."); + targetAudience.setConclusion("Ключевой клиент — специалист/менеджер 25-35 лет с доходом выше среднего, ценит сервис и скорость."); sections.setTargetAudience(targetAudience); - // 3. Competitive Environment (ОБНОВЛЕННАЯ СТРУКТУРА) + // --------------------------------------------------------- + // 3. Competitive Environment + // --------------------------------------------------------- MarketingAnalysisResponseV2.CompetitiveEnvironment competitiveEnvironment = new MarketingAnalysisResponseV2.CompetitiveEnvironment(); competitiveEnvironment.setTitle("Конкурентный ландшафт"); - MarketingAnalysisResponseV2.CompetitiveEnvironmentMetrics ceMetrics = new MarketingAnalysisResponseV2.CompetitiveEnvironmentMetrics(); - ceMetrics.setLeaderMarketShare("45-50%"); // Доля топ-игроков + ceMetrics.setLeaderMarketShare("45-50%"); ceMetrics.setCompetitionLevel("Высокая (Красный океан)"); - ceMetrics.setAdPressure("Агрессивная (Высокие ставки CPI)"); - ceMetrics.setMarketLeaderName("Сетевые лидеры / Маркетплейсы"); + ceMetrics.setAdPressure("Агрессивная"); + ceMetrics.setMarketLeaderName("Сетевые лидеры"); competitiveEnvironment.setMetrics(ceMetrics); - // Список конкурентов (для таблицы и графика) 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++) { 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.setWebsite("https://google.com/search?q=" + companies.get(i).replace(" ", "+")); + row.setVisibilityScore(90 - (i * 12)); row.setKeyStrengths(List.of("Сильный бренд", "Широкая сеть", "Активный SMM")); row.setPriceSegment(i == 0 ? "Premium" : (i == 1 ? "Low-cost" : "Middle")); competitorRows.add(row); } competitiveEnvironment.setCompetitors(competitorRows); - - competitiveEnvironment.setConclusion("Рынок консолидирован крупными игроками. Для входа требуется четкое УТП или узкая нишевание, прямая ценовая конкуренция неэффективна."); + competitiveEnvironment.setConclusion("Рынок консолидирован крупными игроками. Для входа требуется четкое УТП."); sections.setCompetitiveEnvironment(competitiveEnvironment); - // 4. Demand And Behavior + // --------------------------------------------------------- + // 4. Demand And Behavior (ЗАПОЛНЯЕМ ОБЯЗАТЕЛЬНО!) + // --------------------------------------------------------- MarketingAnalysisResponseV2.DemandAndBehavior demandAndBehavior = new MarketingAnalysisResponseV2.DemandAndBehavior(); demandAndBehavior.setTitle("Паттерны поведения"); - MarketingAnalysisResponseV2.DemandAndBehaviorMetrics dabMetrics = new MarketingAnalysisResponseV2.DemandAndBehaviorMetrics(); dabMetrics.setSearchVolumeGrowth("+22% YoY"); dabMetrics.setInteractionsBeforePurchase("5-7 касаний"); - dabMetrics.setRepeatRefusalProbability("~15% (Сравнение цен)"); + dabMetrics.setRepeatRefusalProbability("~15%"); demandAndBehavior.setMetrics(dabMetrics); Map searchQueryTypes = new LinkedHashMap<>(); searchQueryTypes.put("Коммерческие (купить, цена)", "45%"); - searchQueryTypes.put("Информационные (отзывы, как выбрать)", "30%"); - searchQueryTypes.put("Брендовые (имя конкурента)", "15%"); - searchQueryTypes.put("Гео-запросы (рядом, адрес)", "10%"); + searchQueryTypes.put("Информационные (отзывы)", "30%"); + searchQueryTypes.put("Брендовые", "15%"); + searchQueryTypes.put("Гео-запросы", "10%"); demandAndBehavior.setSearchQueryTypes(searchQueryTypes); Map decisionFactors = new LinkedHashMap<>(); decisionFactors.put("Цена / Рассрочка", "35%"); - decisionFactors.put("Отзывы (Social Proof)", "25%"); - decisionFactors.put("Скорость / Сервис", "20%"); - decisionFactors.put("Личный бренд / Доверие", "15%"); - decisionFactors.put("Удобство оплаты", "5%"); + decisionFactors.put("Отзывы", "25%"); + decisionFactors.put("Локация", "20%"); + decisionFactors.put("Сервис", "15%"); + decisionFactors.put("Бренд", "5%"); demandAndBehavior.setDecisionFactors(decisionFactors); Map reasonsForRefusal = new LinkedHashMap<>(); - reasonsForRefusal.put("Скрытые условия / Нет цены", "40%"); - reasonsForRefusal.put("Плохие отзывы / Нет кейсов", "30%"); - reasonsForRefusal.put("Долгая реакция менеджера", "30%"); + reasonsForRefusal.put("Скрытые условия", "40%"); + reasonsForRefusal.put("Плохие отзывы", "30%"); + reasonsForRefusal.put("Долгая реакция", "30%"); demandAndBehavior.setReasonsForRefusal(reasonsForRefusal); - - demandAndBehavior.setConclusion("Покупатель стал избирательным: он читает отзывы, сравнивает цены и ищет социальные доказательства. Критически важна прозрачность условий и скорость ответа."); + demandAndBehavior.setConclusion("Пользователи активно сравнивают цены и читают отзывы перед покупкой."); sections.setDemandAndBehavior(demandAndBehavior); + // --------------------------------------------------------- // 5. Customer Journey + // --------------------------------------------------------- MarketingAnalysisResponseV2.CustomerJourney customerJourney = new MarketingAnalysisResponseV2.CustomerJourney(); customerJourney.setTitle("Воронка продаж"); - MarketingAnalysisResponseV2.FunnelMetrics funnelMetrics = new MarketingAnalysisResponseV2.FunnelMetrics(); 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% от квал. лидов) + funnelMetrics.setSiteVisitors("~800 переходов (CTR 1.5%)"); + funnelMetrics.setLeads(150); + funnelMetrics.setBookings(60); + funnelMetrics.setPayments(45); customerJourney.setFunnelMetrics(funnelMetrics); MarketingAnalysisResponseV2.ConversionRates conversionRates = new MarketingAnalysisResponseV2.ConversionRates(); - conversionRates.setLeadToBooking("40%"); // Квалификация - conversionRates.setBookingToPayment("75%"); // Дожим - conversionRates.setLeadToPaymentTotal("30%"); // Общая конверсия в продажу + conversionRates.setLeadToBooking("40%"); + conversionRates.setBookingToPayment("75%"); + conversionRates.setLeadToPaymentTotal("30%"); customerJourney.setConversionRates(conversionRates); MarketingAnalysisResponseV2.FunnelBottlenecks funnelBottlenecks = new MarketingAnalysisResponseV2.FunnelBottlenecks(); - funnelBottlenecks.setLeadToBooking("Недозвон / Долгая реакция на заявку"); - funnelBottlenecks.setBookingToPayment("Возражение 'Дорого' / Отсутствие рассрочки"); + funnelBottlenecks.setLeadToBooking("Недозвон / Долгая реакция"); + funnelBottlenecks.setBookingToPayment("Возражение 'Дорого'"); customerJourney.setFunnelBottlenecks(funnelBottlenecks); - - customerJourney.setConclusion("Узкое место — обработка входящих заявок. Потеря 60% лидов на этапе квалификации говорит о проблемах в скриптах или скорости реакции."); + customerJourney.setConclusion("Узкое место — обработка входящих заявок (этап квалификации)."); sections.setCustomerJourney(customerJourney); + // --------------------------------------------------------- // 6. Acquisition Channels + // --------------------------------------------------------- MarketingAnalysisResponseV2.AcquisitionChannels acquisitionChannels = new MarketingAnalysisResponseV2.AcquisitionChannels(); acquisitionChannels.setTitle("Каналы трафика"); - MarketingAnalysisResponseV2.AcquisitionChannelsMetrics acMetrics = new MarketingAnalysisResponseV2.AcquisitionChannelsMetrics(); acMetrics.setSearchTrafficShare("~30% (SEO + Context)"); - acMetrics.setAverageLeadCost("2500 - 4500 KZT"); // Реальный CPL для услуг - acMetrics.setGrowthPotential("TikTok / Reels (Short Video)"); + acMetrics.setAverageLeadCost("2500 - 4500 KZT"); + acMetrics.setGrowthPotential("TikTok / Reels"); acquisitionChannels.setMetrics(acMetrics); Map channelShares = new LinkedHashMap<>(); - 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%"); + channelShares.put("Instagram", "50%"); + channelShares.put("Google Ads", "20%"); + channelShares.put("TikTok", "15%"); + channelShares.put("2GIS", "10%"); + channelShares.put("Other", "5%"); acquisitionChannels.setChannelShares(channelShares); Map channelEfficiency = new LinkedHashMap<>(); - channelEfficiency.put("instagram", - new MarketingAnalysisResponseV2.ChannelEfficiency("2000-3500 ₸", "High (Масштаб)")); - channelEfficiency.put("tiktok", - new MarketingAnalysisResponseV2.ChannelEfficiency("1500-2500 ₸", "High (Дешево)")); - channelEfficiency.put("kaspi", - new MarketingAnalysisResponseV2.ChannelEfficiency("5% CPA", "Very High (Горячие)")); - channelEfficiency.put("google_search", - new MarketingAnalysisResponseV2.ChannelEfficiency("4000-6000 ₸", "Medium (Дорого)")); - channelEfficiency.put("2gis", - new MarketingAnalysisResponseV2.ChannelEfficiency("Бесплатно", "Local (Гео)")); + channelEfficiency.put("instagram", new MarketingAnalysisResponseV2.ChannelEfficiency("2000-3500 ₸", "High")); + channelEfficiency.put("tiktok", new MarketingAnalysisResponseV2.ChannelEfficiency("1500-2500 ₸", "High")); + channelEfficiency.put("google_search", new MarketingAnalysisResponseV2.ChannelEfficiency("4000-6000 ₸", "Medium")); + channelEfficiency.put("2gis", new MarketingAnalysisResponseV2.ChannelEfficiency("Бесплатно", "Local")); acquisitionChannels.setChannelEfficiency(channelEfficiency); - - acquisitionChannels.setConclusion("Instagram остается основным драйвером продаж. Google Ads эффективен, но дорог. Рекомендуется диверсификация через TikTok для снижения стоимости лида."); + acquisitionChannels.setConclusion("Instagram остается основным драйвером продаж. Рекомендуется диверсификация через TikTok."); sections.setAcquisitionChannels(acquisitionChannels); + // --------------------------------------------------------- // 7. Marketing Economics + // --------------------------------------------------------- MarketingAnalysisResponseV2.MarketingEconomics marketingEconomics = new MarketingAnalysisResponseV2.MarketingEconomics(); marketingEconomics.setTitle("Юнит-экономика"); - MarketingAnalysisResponseV2.MarketingEconomicsMetrics meMetrics = new MarketingAnalysisResponseV2.MarketingEconomicsMetrics(); - meMetrics.setCustomerAcquisitionCostCac("3 500 KZT"); // Целевой CAC - meMetrics.setRepeatSalesConversion("25-30%"); // LTV потенциал - meMetrics.setRoi("250%"); // Здоровый ROI (3.5x ROAS) + meMetrics.setCustomerAcquisitionCostCac("3 500 KZT"); + meMetrics.setRepeatSalesConversion("30%"); + meMetrics.setRoi("250%"); marketingEconomics.setMetrics(meMetrics); MarketingAnalysisResponseV2.FinancialPerformance financialPerformance = new MarketingAnalysisResponseV2.FinancialPerformance(); - // Бюджет 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")); - + financialPerformance.setMarketingExpenses(new MarketingAnalysisResponseV2.MonthYearValue("1.5 млн KZT", "18 млн KZT")); + financialPerformance.setClients(new MarketingAnalysisResponseV2.MonthYearValue("~430", "~5 160")); + financialPerformance.setCac(new MarketingAnalysisResponseV2.MonthYearValue("3 500 KZT", "3 200 KZT")); + financialPerformance.setRevenueFromClient(new MarketingAnalysisResponseV2.MonthYearValue("25 000 KZT", "300 000 KZT")); + financialPerformance.setProfitFromClient(new MarketingAnalysisResponseV2.MonthYearValue("7 500 KZT", "90 000 KZT")); marketingEconomics.setFinancialPerformance(financialPerformance); - marketingEconomics.setConclusion("При текущем CAC модель масштабируема. Основной рычаг роста прибыли — увеличение LTV (повторных покупок) и среднего чека."); + marketingEconomics.setConclusion("Модель масштабируема. Основной рычаг роста — увеличение LTV."); sections.setMarketingEconomics(marketingEconomics); - // 8. Bottlenecks + // --------------------------------------------------------- + // 8. Bottlenecks (ЗАПОЛНЯЕМ ОБЯЗАТЕЛЬНО!) + // --------------------------------------------------------- MarketingAnalysisResponseV2.Bottlenecks bottlenecks = new MarketingAnalysisResponseV2.Bottlenecks(); bottlenecks.setTitle("Точки роста и проблемы"); - Map statistics = new LinkedHashMap<>(); statistics.put("landing_page_issues", "Высокий показатель отказов (60%)"); statistics.put("slow_manager_responses", "Звонок > 15 мин (30% потерь)"); @@ -1549,24 +1523,25 @@ public class MarketingAnalysisService { bottlenecks.setStatistics(statistics); MarketingAnalysisResponseV2.BottlenecksDetails details = new MarketingAnalysisResponseV2.BottlenecksDetails(); - details.setLanding(List.of("Долгая загрузка на мобильных", "Непонятный первый экран (УТП)", "Сложная форма заявки")); - details.setLeadManagement(List.of("Менеджеры не перезванивают", "Нет скриптов продаж", "Нет интеграции с WhatsApp")); - details.setRetention(List.of("Нет программы лояльности", "Нет сбора обратной связи (NPS)", "Нет Email-маркетинга")); + details.setLanding(List.of("Долгая загрузка", "Непонятный оффер", "Сложная форма")); + details.setLeadManagement(List.of("Менеджеры не перезванивают", "Нет скриптов", "Нет CRM")); + details.setRetention(List.of("Нет программы лояльности", "Нет NPS", "Нет Email-маркетинга")); bottlenecks.setDetails(details); - - bottlenecks.setConclusion("Технические ошибки сайта и человеческий фактор в продажах — главные ограничители роста. Их устранение может удвоить конверсию без увеличения бюджета."); + bottlenecks.setConclusion("Технические ошибки сайта и человеческий фактор в продажах — главные ограничители роста."); sections.setBottlenecks(bottlenecks); - // 9. Recommendations + // --------------------------------------------------------- + // 9. Recommendations (ЗАПОЛНЯЕМ ОБЯЗАТЕЛЬНО!) + // --------------------------------------------------------- MarketingAnalysisResponseV2.Recommendations recommendations = new MarketingAnalysisResponseV2.Recommendations(); recommendations.setTitle("План действий"); recommendations.setCorePillars(List.of( - new MarketingAnalysisResponseV2.CorePillar("Быстрые победы", "Запустить ретаргетинг на посетителей сайта и внедрить авто-ответ в WhatsApp."), - new MarketingAnalysisResponseV2.CorePillar("Оптимизация сайта", "Упростить форму заявки до 2 полей (Имя, Телефон) и ускорить мобильную версию."), - new MarketingAnalysisResponseV2.CorePillar("Контент-маркетинг", "Снять 10 Reels с отзывами клиентов и процессом работы для повышения доверия."), - new MarketingAnalysisResponseV2.CorePillar("Систематизация", "Внедрить CRM для контроля пропущенных звонков и задач менеджеров.") + new MarketingAnalysisResponseV2.CorePillar("Быстрые победы", "Запустить ретаргетинг и авто-ответ в WhatsApp."), + new MarketingAnalysisResponseV2.CorePillar("Оптимизация сайта", "Упростить форму заявки и ускорить загрузку."), + new MarketingAnalysisResponseV2.CorePillar("Контент", "Снять Reels с отзывами для доверия."), + new MarketingAnalysisResponseV2.CorePillar("Систематизация", "Внедрить CRM для контроля лидов.") )); - recommendations.setConclusion("Фокус на ближайший квартал: повышение конверсии сайта и автоматизация первичной обработки лидов."); + recommendations.setConclusion("Фокус на ближайший квартал: повышение конверсии и автоматизация."); sections.setRecommendations(recommendations); response.setSections(sections);