This commit is contained in:
arys
2026-01-25 21:42:57 +05:00
parent b51b0b003f
commit dde3573e34
3 changed files with 513 additions and 265 deletions
@@ -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<String> analysisType;
@@ -128,8 +128,7 @@ public class MarketingAnalysisResponseV2 {
public static class CompetitiveEnvironment {
private String title;
private CompetitiveEnvironmentMetrics metrics;
private Map<String, Integer> digitalVisibilityBenchmarks;
private Map<String, List<String>> competitorStrengths;
private List<CompetitorRow> 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<String> keyStrengths;
private String priceSegment;
}
@Setter
@@ -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<String, Object> researchPack = buildResearchPack(request);
// Пытаемся найти 3-5 реальных конкурентов через поиск для подстановки в промпт
List<String> 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<String> ranges = (List<String>) 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<String> competitorCompanies) {
List<String> 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<String, Integer> dvb = ce.getDigitalVisibilityBenchmarks();
if (dvb != null && !dvb.isEmpty()) {
boolean hasPlaceholders = dvb.keySet().stream().anyMatch(this::isPlaceholderCompetitorKey);
if (hasPlaceholders) {
List<Integer> values = new ArrayList<>(dvb.values());
LinkedHashMap<String, Integer> 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<MarketingAnalysisResponseV2.CompetitorRow> rows = ce.getCompetitors();
if (rows != null && !rows.isEmpty()) {
// Проверяем, есть ли заглушки хотя бы у одного элемента
boolean hasPlaceholders = rows.stream()
.anyMatch(row -> isPlaceholderCompetitorKey(row.getName()));
Map<String, List<String>> strengths = ce.getCompetitorStrengths();
if (strengths != null && !strengths.isEmpty()) {
boolean hasPlaceholders = strengths.keySet().stream().anyMatch(this::isPlaceholderCompetitorKey);
if (hasPlaceholders) {
List<List<String>> values = new ArrayList<>(strengths.values());
LinkedHashMap<String, List<String>> 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<String> competitorCompanies) {
List<String> 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<String, String> ageStructure = new LinkedHashMap<>();
Map<String, Object> targetAudienceData = request.getTargetAudience();
if (targetAudienceData != null && targetAudienceData.containsKey("ageRanges")) {
@SuppressWarnings("unchecked")
List<String> ageRanges = (List<String>) 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<String, Integer> 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<String, Integer> digitalVisibilityBenchmarks = new LinkedHashMap<>();
List<String> companies = (competitorCompanies != null) ? competitorCompanies : List.of();
if (companies.isEmpty()) {
companies = List.of("Компания 1", "Компания 2", "Компания 3", "Компания 4", "Компания 5");
}
// Список конкурентов (для таблицы и графика)
List<MarketingAnalysisResponseV2.CompetitorRow> competitorRows = new java.util.ArrayList<>();
List<String> 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<String, List<String>> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, MarketingAnalysisResponseV2.ChannelEfficiency> 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<String, String> 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);