From a89566a83f5d0e05e0fdba9f25744087e9cc264c Mon Sep 17 00:00:00 2001 From: arys Date: Sun, 25 Jan 2026 22:12:33 +0500 Subject: [PATCH] fix --- .../service/MarketingAnalysisService.java | 221 +++++++++--------- 1 file changed, 112 insertions(+), 109 deletions(-) diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index a7cec7e..07b6db2 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -1296,21 +1296,18 @@ public class MarketingAnalysisService { private MarketingAnalysisResponseV2 buildAnalysisV2ExampleForPrompt(MarketingAnalysisRequest request, List competitorCompanies) { MarketingAnalysisResponseV2 response = new MarketingAnalysisResponseV2(); - // Заголовок - String niche = (request.getBusinessNiche() != null && !request.getBusinessNiche().isEmpty()) - ? request.getBusinessNiche() : "Анализируемая ниша"; - response.setReportTitle("Стратегический анализ рынка: " + niche); + // Используем нишу из запроса для заголовка примера, чтобы ГПТ понимал контекст + String nicheTitle = (request.getBusinessNiche() != null) ? request.getBusinessNiche() : "Фитнес услуги"; + response.setReportTitle("Стратегический анализ рынка: " + nicheTitle); MarketingAnalysisResponseV2.Sections sections = new MarketingAnalysisResponseV2.Sections(); - // --------------------------------------------------------- // 1. Market Overview - // --------------------------------------------------------- MarketingAnalysisResponseV2.MarketOverview marketOverview = new MarketingAnalysisResponseV2.MarketOverview(); marketOverview.setTitle("Обзор рынка"); MarketingAnalysisResponseV2.MarketOverviewMetrics moMetrics = new MarketingAnalysisResponseV2.MarketOverviewMetrics(); - moMetrics.setOnlineDemandIndex("Высокий (Активный рост)"); + moMetrics.setOnlineDemandIndex("Высокий (8/10)"); moMetrics.setOnlinePotential("Растущий тренд (+15-20% YoY)"); moMetrics.setDemandGrowthRateYoy("+18.5%"); moMetrics.setSeasonalityCoefficientSpring("1.25 (Высокий сезон)"); @@ -1318,38 +1315,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); monthlyOnlineIndex.setDecember(0.98); marketOverview.setMonthlyOnlineIndex(monthlyOnlineIndex); - // ЛОГИКА ГОРОДОВ: Берем из запроса или ставим дефолт + // Логика городов: берем из запроса или дефолт Map geography = new LinkedHashMap<>(); - List requestedRegions = request.getRegion(); - if (requestedRegions != null && !requestedRegions.isEmpty()) { - // Если юзер выбрал города, используем их - for (String region : requestedRegions) { - geography.put(region, 80); // Примерное значение для промпта + List reqRegions = request.getRegion(); + if (reqRegions != null && !reqRegions.isEmpty()) { + int step = 100 / Math.max(1, reqRegions.size()); + for (int i = 0; i < reqRegions.size(); i++) { + geography.put(reqRegions.get(i), Math.max(10, 90 - (i * 15))); // Разные значения } } else { - // Дефолт для КЗ - geography.put("Алматы", 45); - geography.put("Астана", 30); + geography.put("Алматы", 50); + geography.put("Астана", 35); geography.put("Шымкент", 15); - geography.put("Караганда", 10); } marketOverview.setGeography(geography); - marketOverview.setConclusion("Рынок находится в стадии активного роста. Основной спрос сконцентрирован в мегаполисах (75%), сезонность ярко выражена с пиками весной и осенью."); + marketOverview.setConclusion("Рынок находится в стадии активного роста. Основной спрос сконцентрирован в мегаполисах, сезонность ярко выражена."); 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.setTotalAudienceSize("~450 000 активных пользователей"); taMetrics.setLeadingSegmentShare("Женщины 25-34 года"); taMetrics.setAboveAverageIncomeShare("42% (Средний+)"); targetAudience.setMetrics(taMetrics); @@ -1366,47 +1361,53 @@ public class MarketingAnalysisService { 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", 380000); + incomeByAge.put("35-44", 450000); incomeByAge.put("45+", 320000); targetAudience.setIncomeByAge(incomeByAge); - targetAudience.setConclusion("Ключевой клиент — специалист/менеджер 25-35 лет с доходом выше среднего, ценит сервис и скорость."); + + targetAudience.setConclusion("Ключевой клиент — специалист 25-35 лет с доходом выше среднего. Вторичный сегмент — студенты (чувствительны к цене)."); sections.setTargetAudience(targetAudience); - // --------------------------------------------------------- - // 3. Competitive Environment - // --------------------------------------------------------- + MarketingAnalysisResponseV2.CompetitiveEnvironment competitiveEnvironment = new MarketingAnalysisResponseV2.CompetitiveEnvironment(); competitiveEnvironment.setTitle("Конкурентный ландшафт"); + + // 3.1 Новые метрики MarketingAnalysisResponseV2.CompetitiveEnvironmentMetrics ceMetrics = new MarketingAnalysisResponseV2.CompetitiveEnvironmentMetrics(); ceMetrics.setLeaderMarketShare("45-50%"); ceMetrics.setCompetitionLevel("Высокая (Красный океан)"); - ceMetrics.setAdPressure("Агрессивная"); - ceMetrics.setMarketLeaderName("Сетевые лидеры"); + ceMetrics.setAdPressure("Агрессивная (Высокие ставки)"); + ceMetrics.setMarketLeaderName("Сетевые лидеры / Маркетплейсы"); competitiveEnvironment.setMetrics(ceMetrics); + // 3.2 Список конкурентов (List) - для таблицы и графика List competitorRows = new java.util.ArrayList<>(); - List companies = (competitorCompanies != null && !competitorCompanies.isEmpty()) - ? competitorCompanies - : List.of("Крупный сетевой бренд", "Агрессивный дискаунтер", "Премиум-игрок"); - for (int i = 0; i < Math.min(companies.size(), 5); i++) { + // Берем реальных конкурентов из поиска или дефолтные, если список пуст + List activeCompetitors = (competitorCompanies != null && !competitorCompanies.isEmpty()) + ? competitorCompanies + : List.of("Конкурент A (Лидер)", "Конкурент B (Сетевой)", "Конкурент C (Дискаунтер)"); + + for (int i = 0; i < Math.min(activeCompetitors.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)); - row.setKeyStrengths(List.of("Сильный бренд", "Широкая сеть", "Активный SMM")); + row.setName(activeCompetitors.get(i)); + // Генерируем ссылку-заглушку, которую GPT должен заменить на реальную + row.setWebsite("https://google.com/search?q=" + activeCompetitors.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.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("Паттерны поведения"); + demandAndBehavior.setTitle("Спрос и поведение"); + MarketingAnalysisResponseV2.DemandAndBehaviorMetrics dabMetrics = new MarketingAnalysisResponseV2.DemandAndBehaviorMetrics(); dabMetrics.setSearchVolumeGrowth("+22% YoY"); dabMetrics.setInteractionsBeforePurchase("5-7 касаний"); @@ -1414,87 +1415,89 @@ public class MarketingAnalysisService { demandAndBehavior.setMetrics(dabMetrics); Map searchQueryTypes = new LinkedHashMap<>(); - searchQueryTypes.put("Коммерческие (купить, цена)", "45%"); - searchQueryTypes.put("Информационные (отзывы)", "30%"); - searchQueryTypes.put("Брендовые", "15%"); - searchQueryTypes.put("Гео-запросы", "10%"); + searchQueryTypes.put("direct_interest_catalog", "40% (Прямой спрос)"); + searchQueryTypes.put("question_based_articles", "25% (Как выбрать?)"); + searchQueryTypes.put("competitors_reviews", "20% (Сравнение)"); + searchQueryTypes.put("problem_solving_hot_promo", "15% (Акции)"); demandAndBehavior.setSearchQueryTypes(searchQueryTypes); Map decisionFactors = new LinkedHashMap<>(); - decisionFactors.put("Цена / Рассрочка", "35%"); - decisionFactors.put("Отзывы", "25%"); - decisionFactors.put("Локация", "20%"); - decisionFactors.put("Сервис", "15%"); - decisionFactors.put("Бренд", "5%"); + decisionFactors.put("promo_discount", "35% (Цена)"); + decisionFactors.put("review_recommendation", "25% (Отзывы)"); + decisionFactors.put("warranty_quality", "20% (Качество)"); + decisionFactors.put("convenience_service", "15% (Сервис)"); + decisionFactors.put("availability", "5% (Наличие)"); + decisionFactors.put("price", "Важно"); // Дублирует promo, но нужно для структуры demandAndBehavior.setDecisionFactors(decisionFactors); Map reasonsForRefusal = new LinkedHashMap<>(); - reasonsForRefusal.put("Скрытые условия", "40%"); - reasonsForRefusal.put("Плохие отзывы", "30%"); - reasonsForRefusal.put("Долгая реакция", "30%"); + reasonsForRefusal.put("high_price", "40% (Дорого)"); + reasonsForRefusal.put("no_reviews_social_proof", "30% (Нет доверия)"); + reasonsForRefusal.put("choice_difficulty_unclear_offer", "30% (Непонятно)"); demandAndBehavior.setReasonsForRefusal(reasonsForRefusal); - demandAndBehavior.setConclusion("Пользователи активно сравнивают цены и читают отзывы перед покупкой."); + demandAndBehavior.setConclusion("Покупатели чувствительны к цене и отзывам. Важно наличие социального доказательства."); sections.setDemandAndBehavior(demandAndBehavior); - // --------------------------------------------------------- // 5. Customer Journey - // --------------------------------------------------------- MarketingAnalysisResponseV2.CustomerJourney customerJourney = new MarketingAnalysisResponseV2.CustomerJourney(); - customerJourney.setTitle("Воронка продаж"); + customerJourney.setTitle("Путь клиента"); + MarketingAnalysisResponseV2.FunnelMetrics funnelMetrics = new MarketingAnalysisResponseV2.FunnelMetrics(); - funnelMetrics.setAdTraffic("~15 000 показов"); - funnelMetrics.setSiteVisitors("~800 переходов (CTR 1.5%)"); - funnelMetrics.setLeads(150); - funnelMetrics.setBookings(60); - funnelMetrics.setPayments(45); + funnelMetrics.setAdTraffic("15 000"); + funnelMetrics.setSiteVisitors("12 000 (CTR 80%)"); // Условный переход + funnelMetrics.setLeads(450); // 3-4% конверсия + funnelMetrics.setBookings(200); // Квалифицированные + funnelMetrics.setPayments(120); // Продажи customerJourney.setFunnelMetrics(funnelMetrics); MarketingAnalysisResponseV2.ConversionRates conversionRates = new MarketingAnalysisResponseV2.ConversionRates(); - conversionRates.setLeadToBooking("40%"); - conversionRates.setBookingToPayment("75%"); - conversionRates.setLeadToPaymentTotal("30%"); + conversionRates.setLeadToBooking("45%"); + conversionRates.setBookingToPayment("60%"); + conversionRates.setLeadToPaymentTotal("27%"); customerJourney.setConversionRates(conversionRates); MarketingAnalysisResponseV2.FunnelBottlenecks funnelBottlenecks = new MarketingAnalysisResponseV2.FunnelBottlenecks(); - funnelBottlenecks.setLeadToBooking("Недозвон / Долгая реакция"); - funnelBottlenecks.setBookingToPayment("Возражение 'Дорого'"); + funnelBottlenecks.setLeadToBooking("Долгая обработка заявки / Недозвон"); + funnelBottlenecks.setBookingToPayment("Высокая цена / Нет рассрочки"); customerJourney.setFunnelBottlenecks(funnelBottlenecks); - customerJourney.setConclusion("Узкое место — обработка входящих заявок (этап квалификации)."); + + customerJourney.setConclusion("Основные потери лидов происходят на этапе первого звонка и квалификации."); sections.setCustomerJourney(customerJourney); - // --------------------------------------------------------- // 6. Acquisition Channels - // --------------------------------------------------------- MarketingAnalysisResponseV2.AcquisitionChannels acquisitionChannels = new MarketingAnalysisResponseV2.AcquisitionChannels(); - acquisitionChannels.setTitle("Каналы трафика"); + acquisitionChannels.setTitle("Каналы привлечения"); + MarketingAnalysisResponseV2.AcquisitionChannelsMetrics acMetrics = new MarketingAnalysisResponseV2.AcquisitionChannelsMetrics(); - acMetrics.setSearchTrafficShare("~30% (SEO + Context)"); - acMetrics.setAverageLeadCost("2500 - 4500 KZT"); + acMetrics.setSearchTrafficShare("35%"); + acMetrics.setAverageLeadCost("2500 - 4000 KZT"); acMetrics.setGrowthPotential("TikTok / Reels"); acquisitionChannels.setMetrics(acMetrics); Map channelShares = new LinkedHashMap<>(); - channelShares.put("Instagram", "50%"); - channelShares.put("Google Ads", "20%"); - channelShares.put("TikTok", "15%"); - channelShares.put("2GIS", "10%"); - channelShares.put("Other", "5%"); + channelShares.put("instagram_target", "45%"); + channelShares.put("tiktok_ads", "25%"); + channelShares.put("kaspi_marketing", "15%"); + channelShares.put("google_ads", "10%"); + channelShares.put("2gis_ads", "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("google_search", new MarketingAnalysisResponseV2.ChannelEfficiency("4000-6000 ₸", "Medium")); + channelEfficiency.put("instagram", new MarketingAnalysisResponseV2.ChannelEfficiency("2500 KZT", "High")); + channelEfficiency.put("tiktok", new MarketingAnalysisResponseV2.ChannelEfficiency("1800 KZT", "High")); + channelEfficiency.put("kaspi", new MarketingAnalysisResponseV2.ChannelEfficiency("2000 KZT", "Very High")); + channelEfficiency.put("google_search", new MarketingAnalysisResponseV2.ChannelEfficiency("4500 KZT", "Medium")); channelEfficiency.put("2gis", new MarketingAnalysisResponseV2.ChannelEfficiency("Бесплатно", "Local")); + channelEfficiency.put("threads_meta", new MarketingAnalysisResponseV2.ChannelEfficiency("N/A", "New")); acquisitionChannels.setChannelEfficiency(channelEfficiency); - acquisitionChannels.setConclusion("Instagram остается основным драйвером продаж. Рекомендуется диверсификация через TikTok."); + + acquisitionChannels.setConclusion("Instagram и TikTok — наиболее рентабельные каналы. Kaspi обязателен для товарного бизнеса."); sections.setAcquisitionChannels(acquisitionChannels); - // --------------------------------------------------------- // 7. Marketing Economics - // --------------------------------------------------------- MarketingAnalysisResponseV2.MarketingEconomics marketingEconomics = new MarketingAnalysisResponseV2.MarketingEconomics(); - marketingEconomics.setTitle("Юнит-экономика"); + marketingEconomics.setTitle("Экономика маркетинга"); + MarketingAnalysisResponseV2.MarketingEconomicsMetrics meMetrics = new MarketingAnalysisResponseV2.MarketingEconomicsMetrics(); meMetrics.setCustomerAcquisitionCostCac("3 500 KZT"); meMetrics.setRepeatSalesConversion("30%"); @@ -1502,46 +1505,46 @@ public class MarketingAnalysisService { marketingEconomics.setMetrics(meMetrics); MarketingAnalysisResponseV2.FinancialPerformance financialPerformance = new MarketingAnalysisResponseV2.FinancialPerformance(); + // Используем бюджет ~1.5 млн 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")); + financialPerformance.setRevenueFromClient(new MarketingAnalysisResponseV2.MonthYearValue("20 000 KZT", "240 000 KZT")); + financialPerformance.setProfitFromClient(new MarketingAnalysisResponseV2.MonthYearValue("8 000 KZT", "96 000 KZT")); + marketingEconomics.setFinancialPerformance(financialPerformance); - marketingEconomics.setConclusion("Модель масштабируема. Основной рычаг роста — увеличение LTV."); + marketingEconomics.setConclusion("Экономика сходится с положительным ROI. Рекомендуется реинвестировать прибыль в масштабирование."); sections.setMarketingEconomics(marketingEconomics); - // --------------------------------------------------------- - // 8. Bottlenecks (ЗАПОЛНЯЕМ ОБЯЗАТЕЛЬНО!) - // --------------------------------------------------------- + // 8. Bottlenecks MarketingAnalysisResponseV2.Bottlenecks bottlenecks = new MarketingAnalysisResponseV2.Bottlenecks(); - bottlenecks.setTitle("Точки роста и проблемы"); + bottlenecks.setTitle("Узкие места"); + Map statistics = new LinkedHashMap<>(); - statistics.put("landing_page_issues", "Высокий показатель отказов (60%)"); - statistics.put("slow_manager_responses", "Звонок > 15 мин (30% потерь)"); - statistics.put("repeat_interaction_refusal", "Нет дожима в CRM (20% потерь)"); + statistics.put("landing_page_issues", "25% (Отказы)"); + statistics.put("slow_manager_responses", "30% (Потери)"); + statistics.put("repeat_interaction_refusal", "20% (Недожим)"); bottlenecks.setStatistics(statistics); MarketingAnalysisResponseV2.BottlenecksDetails details = new MarketingAnalysisResponseV2.BottlenecksDetails(); - details.setLanding(List.of("Долгая загрузка", "Непонятный оффер", "Сложная форма")); - details.setLeadManagement(List.of("Менеджеры не перезванивают", "Нет скриптов", "Нет CRM")); - details.setRetention(List.of("Нет программы лояльности", "Нет NPS", "Нет Email-маркетинга")); + details.setLanding(List.of("Медленная загрузка", "Непонятное УТП", "Нет мобильной адаптации")); + details.setLeadManagement(List.of("Долгий ответ (>15 мин)", "Нет скриптов", "Нет CRM")); + details.setRetention(List.of("Нет программы лояльности", "Редкие касания базы", "Высокая цена без ценности")); bottlenecks.setDetails(details); - bottlenecks.setConclusion("Технические ошибки сайта и человеческий фактор в продажах — главные ограничители роста."); + + bottlenecks.setConclusion("Технические проблемы сайта и отсутствие CRM — главные барьеры."); sections.setBottlenecks(bottlenecks); - // --------------------------------------------------------- - // 9. Recommendations (ЗАПОЛНЯЕМ ОБЯЗАТЕЛЬНО!) - // --------------------------------------------------------- + // 9. Recommendations MarketingAnalysisResponseV2.Recommendations recommendations = new MarketingAnalysisResponseV2.Recommendations(); - recommendations.setTitle("План действий"); + recommendations.setTitle("Рекомендации"); recommendations.setCorePillars(List.of( - new MarketingAnalysisResponseV2.CorePillar("Быстрые победы", "Запустить ретаргетинг и авто-ответ в WhatsApp."), - new MarketingAnalysisResponseV2.CorePillar("Оптимизация сайта", "Упростить форму заявки и ускорить загрузку."), - new MarketingAnalysisResponseV2.CorePillar("Контент", "Снять Reels с отзывами для доверия."), - new MarketingAnalysisResponseV2.CorePillar("Систематизация", "Внедрить CRM для контроля лидов.") + new MarketingAnalysisResponseV2.CorePillar("Быстрый старт", "Запустить таргет с лид-магнитом."), + new MarketingAnalysisResponseV2.CorePillar("Оптимизация", "Внедрить CRM и скрипты продаж."), + new MarketingAnalysisResponseV2.CorePillar("Масштабирование", "Подключить TikTok Ads и блогеров."), + new MarketingAnalysisResponseV2.CorePillar("Удержание", "Внедрить систему лояльности.") )); - recommendations.setConclusion("Фокус на ближайший квартал: повышение конверсии и автоматизация."); + recommendations.setConclusion("Реализация плана позволит удвоить количество лидов за 3 месяца."); sections.setRecommendations(recommendations); response.setSections(sections);