This commit is contained in:
root
2025-12-07 18:06:56 +05:00
parent 4d50cd3da5
commit 33f65f7fca
3 changed files with 892 additions and 49 deletions
@@ -2,6 +2,7 @@ package kz.konturai.parser.dto;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
public class MarketingAnalysisResult {
private String analysisId;
@@ -64,6 +65,8 @@ public class MarketingAnalysisResult {
public static class MarketingReport {
private String summary;
private String fullAnalysis;
private Map<String, Object> chartsData;
private TargetAudience targetAudience;
private List<String> recommendations;
private Strategy strategy;
@@ -80,6 +83,22 @@ public class MarketingAnalysisResult {
this.summary = summary;
}
public String getFullAnalysis() {
return fullAnalysis;
}
public void setFullAnalysis(String fullAnalysis) {
this.fullAnalysis = fullAnalysis;
}
public Map<String, Object> getChartsData() {
return chartsData;
}
public void setChartsData(Map<String, Object> chartsData) {
this.chartsData = chartsData;
}
public TargetAudience getTargetAudience() {
return targetAudience;
}
@@ -197,13 +197,18 @@ public class MarketingAnalysisService {
contextBuilder.append("Типы анализа: ").append(String.join(", ", analysisTypeNames)).append("\n");
String context = contextBuilder.toString();
// Generate unified analysis with all types
String unifiedAnalysisContent = generateUnifiedAnalysis(context, analysisTypes, detailLevel);
// Generate chart data first (numerical data for graphs)
Map<String, Object> chartsData = generateChartData(context, request);
report.put("chartsData", chartsData);
// Generate unified analysis with all types using new prompt structure
String unifiedAnalysisContent = generateUnifiedAnalysis(context, request, detailLevel);
// Store analysis data
report.put("analysisTypes", analysisTypeNames);
report.put("detailLevel", detailLevelName);
report.put("summary", unifiedAnalysisContent != null ? unifiedAnalysisContent : "Анализ не удалось сгенерировать.");
report.put("fullAnalysis", unifiedAnalysisContent != null ? unifiedAnalysisContent : "Анализ не удалось сгенерировать.");
report.put("summary", unifiedAnalysisContent != null ? unifiedAnalysisContent : "Анализ не удалось сгенерировать."); // Keep for backward compatibility
// Generate target audience description (adjusted by detail level)
String audiencePrompt;
@@ -290,10 +295,474 @@ public class MarketingAnalysisService {
return report;
}
private String generateUnifiedAnalysis(String context, List<AnalysisType> analysisTypes, DetailLevel detailLevel) {
if (analysisTypes == null || analysisTypes.isEmpty()) {
analysisTypes = List.of(AnalysisType.РЫНОК); // Default
private Map<String, Object> generateChartData(String context, MarketingAnalysisRequest request) {
Map<String, Object> chartsData = new HashMap<>();
// Build prompt for chart data generation
StringBuilder chartPromptBuilder = new StringBuilder();
chartPromptBuilder.append("Ты — профессиональный маркетолог и аналитик, работающий с малым и средним бизнесом Казахстана. ");
chartPromptBuilder.append("На основе следующей информации о бизнесе сгенерируй ТОЛЬКО числовые данные в виде таблиц для построения графиков. ");
chartPromptBuilder.append("Ответ должен содержать ТОЛЬКО таблицы в формате Markdown, без дополнительного текста.\n\n");
chartPromptBuilder.append("📊 Сгенерируй следующие таблицы:\n\n");
chartPromptBuilder.append("1️⃣ Сезонность спроса (линейный график, 12 месяцев)\n");
chartPromptBuilder.append("Выведи таблицу в формате:\n");
chartPromptBuilder.append("| Месяц | Уровень спроса (0–100) |\n");
chartPromptBuilder.append("|-------|------------------------|\n");
chartPromptBuilder.append("| Январь | [число 0-100] |\n");
chartPromptBuilder.append("| Февраль | [число 0-100] |\n");
chartPromptBuilder.append("... (для всех 12 месяцев)\n");
chartPromptBuilder.append("Значения должны соответствовать нише, иметь сезонную динамику, быть реалистичными.\n\n");
chartPromptBuilder.append("2️⃣ Сравнительный анализ конкурентов (столбчатая диаграмма)\n");
chartPromptBuilder.append("Выведи таблицу в формате:\n");
chartPromptBuilder.append("| Конкурент | Охват аудитории | Активность в соцсетях (0–100) | Количество отзывов | Сильные стороны | Слабые стороны |\n");
chartPromptBuilder.append("|-----------|-----------------|-------------------------------|-------------------|----------------|----------------|\n");
chartPromptBuilder.append("| [Название] | [1000-50000] | [0-100] | [число] | [текст] | [текст] |\n");
chartPromptBuilder.append("... (для 3-5 конкурентов)\n");
chartPromptBuilder.append("Охват — в диапазоне 1 000–50 000, Активность — 0–100.\n\n");
chartPromptBuilder.append("3️⃣ Сегментация целевой аудитории (круговая диаграмма)\n");
chartPromptBuilder.append("Выведи три таблицы:\n");
chartPromptBuilder.append("Таблица 1 - Возрастные группы:\n");
chartPromptBuilder.append("| Возрастная группа | Процент (%) |\n");
chartPromptBuilder.append("|-------------------|-------------|\n");
chartPromptBuilder.append("| [группа] | [число] |\n\n");
chartPromptBuilder.append("Таблица 2 - Пол:\n");
chartPromptBuilder.append("| Пол | Процент (%) |\n");
chartPromptBuilder.append("|-----|-------------|\n");
chartPromptBuilder.append("| [пол] | [число] |\n\n");
chartPromptBuilder.append("Таблица 3 - Сегменты ЦА:\n");
chartPromptBuilder.append("| Сегмент | Процент (%) |\n");
chartPromptBuilder.append("|---------|-------------|\n");
chartPromptBuilder.append("| [сегмент] | [число] |\n");
chartPromptBuilder.append("Сумма процентов в каждой таблице = 100%.\n\n");
chartPromptBuilder.append("4️⃣ Потенциал каналов продвижения (бар-чарт)\n");
chartPromptBuilder.append("Выведи таблицу в формате:\n");
chartPromptBuilder.append("| Канал | Потенциал (0–100) | Обоснование |\n");
chartPromptBuilder.append("|-------|-------------------|-------------|\n");
chartPromptBuilder.append("| Instagram | [0-100] | [текст] |\n");
chartPromptBuilder.append("| TikTok | [0-100] | [текст] |\n");
chartPromptBuilder.append("| Telegram | [0-100] | [текст] |\n");
chartPromptBuilder.append("| 2GIS | [0-100] | [текст] |\n");
chartPromptBuilder.append("| Google Ads | [0-100] | [текст] |\n\n");
chartPromptBuilder.append("5️⃣ Конверсионная воронка (воронка спроса)\n");
chartPromptBuilder.append("Выведи таблицу в формате:\n");
chartPromptBuilder.append("| Этап | Значение | Конверсия (%) |\n");
chartPromptBuilder.append("|------|----------|---------------|\n");
chartPromptBuilder.append("| Охват | [число] | 100 |\n");
chartPromptBuilder.append("| Вовлечённость | [число] | [%] |\n");
chartPromptBuilder.append("| Клики | [число] | [%] |\n");
chartPromptBuilder.append("| Переходы | [число] | [%] |\n");
chartPromptBuilder.append("| Заявки | [число] | [%] |\n");
chartPromptBuilder.append("| Покупки | [число] | [%] |\n");
chartPromptBuilder.append("Числа должны уменьшаться логично от этапа к этапу.\n\n");
chartPromptBuilder.append("Информация о бизнесе:\n");
chartPromptBuilder.append(context);
String chartPrompt = chartPromptBuilder.toString();
String chartDataResponse = openAIAnalyticsService.generateWithInstruction(context, chartPrompt, "ru");
// Parse the response to extract chart data
if (chartDataResponse != null && !chartDataResponse.trim().isEmpty()) {
Map<String, Object> parsedData = parseChartDataFromResponse(chartDataResponse);
chartsData.putAll(parsedData);
}
return chartsData;
}
private Map<String, Object> parseChartDataFromResponse(String response) {
Map<String, Object> chartData = new HashMap<>();
// Parse seasonality data
Map<String, Integer> seasonality = parseSeasonalityData(response);
if (!seasonality.isEmpty()) {
chartData.put("seasonality", seasonality);
}
// Parse competitors data
List<Map<String, Object>> competitors = parseCompetitorsData(response);
if (!competitors.isEmpty()) {
chartData.put("competitors", competitors);
}
// Parse audience segmentation
Map<String, Object> audienceSegmentation = parseAudienceSegmentation(response);
if (!audienceSegmentation.isEmpty()) {
chartData.put("audienceSegmentation", audienceSegmentation);
}
// Parse channels potential
List<Map<String, Object>> channelsPotential = parseChannelsPotential(response);
if (!channelsPotential.isEmpty()) {
chartData.put("channelsPotential", channelsPotential);
}
// Parse conversion funnel
List<Map<String, Object>> conversionFunnel = parseConversionFunnel(response);
if (!conversionFunnel.isEmpty()) {
chartData.put("conversionFunnel", conversionFunnel);
}
return chartData;
}
private Map<String, Integer> parseSeasonalityData(String response) {
Map<String, Integer> seasonality = new LinkedHashMap<>();
String[] months = {"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
"Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"};
// Try to find table with seasonality data
String[] lines = response.split("\n");
boolean inTable = false;
for (String line : lines) {
line = line.trim();
if (line.contains("Месяц") && line.contains("Уровень спроса")) {
inTable = true;
continue;
}
if (inTable && line.startsWith("|") && !line.startsWith("|---")) {
String[] parts = line.split("\\|");
if (parts.length >= 3) {
String month = parts[1].trim();
String valueStr = parts[2].trim();
try {
// Extract number from value string
valueStr = valueStr.replaceAll("[^0-9]", "");
if (!valueStr.isEmpty()) {
int value = Integer.parseInt(valueStr);
if (value >= 0 && value <= 100) {
seasonality.put(month, value);
}
}
} catch (NumberFormatException e) {
// Skip invalid values
}
}
}
if (inTable && line.isEmpty()) {
break;
}
}
// If parsing failed, generate default values
if (seasonality.isEmpty()) {
for (String month : months) {
seasonality.put(month, 50 + (int)(Math.random() * 30)); // Random between 50-80
}
}
return seasonality;
}
private List<Map<String, Object>> parseCompetitorsData(String response) {
List<Map<String, Object>> competitors = new ArrayList<>();
String[] lines = response.split("\n");
boolean inTable = false;
for (String line : lines) {
line = line.trim();
if (line.contains("Конкурент") && line.contains("Охват аудитории")) {
inTable = true;
continue;
}
if (inTable && line.startsWith("|") && !line.startsWith("|---")) {
String[] parts = line.split("\\|");
if (parts.length >= 7) {
Map<String, Object> competitor = new HashMap<>();
competitor.put("name", parts[1].trim());
try {
String reachStr = parts[2].trim().replaceAll("[^0-9]", "");
if (!reachStr.isEmpty()) {
competitor.put("reach", Integer.parseInt(reachStr));
} else {
competitor.put("reach", 10000);
}
} catch (NumberFormatException e) {
competitor.put("reach", 10000);
}
try {
String activityStr = parts[3].trim().replaceAll("[^0-9]", "");
if (!activityStr.isEmpty()) {
int activity = Integer.parseInt(activityStr);
competitor.put("activity", Math.min(100, Math.max(0, activity)));
} else {
competitor.put("activity", 50);
}
} catch (NumberFormatException e) {
competitor.put("activity", 50);
}
try {
String reviewsStr = parts[4].trim().replaceAll("[^0-9]", "");
if (!reviewsStr.isEmpty()) {
competitor.put("reviews", Integer.parseInt(reviewsStr));
} else {
competitor.put("reviews", 100);
}
} catch (NumberFormatException e) {
competitor.put("reviews", 100);
}
competitor.put("strengths", parts[5].trim());
competitor.put("weaknesses", parts[6].trim());
if (!competitor.get("name").toString().isEmpty()) {
competitors.add(competitor);
}
}
}
if (inTable && line.isEmpty() && !competitors.isEmpty()) {
break;
}
}
// If parsing failed, add default competitor
if (competitors.isEmpty()) {
Map<String, Object> defaultCompetitor = new HashMap<>();
defaultCompetitor.put("name", "Основной конкурент");
defaultCompetitor.put("reach", 15000);
defaultCompetitor.put("activity", 65);
defaultCompetitor.put("reviews", 150);
defaultCompetitor.put("strengths", "Сильное присутствие в соцсетях");
defaultCompetitor.put("weaknesses", "Высокая цена");
competitors.add(defaultCompetitor);
}
return competitors;
}
private Map<String, Object> parseAudienceSegmentation(String response) {
Map<String, Object> segmentation = new HashMap<>();
List<Map<String, String>> ageGroups = new ArrayList<>();
List<Map<String, String>> genders = new ArrayList<>();
List<Map<String, String>> segments = new ArrayList<>();
String[] lines = response.split("\n");
boolean inAgeTable = false;
boolean inGenderTable = false;
boolean inSegmentsTable = false;
for (String line : lines) {
line = line.trim();
if (line.contains("Возрастная группа") || line.contains("Возраст")) {
inAgeTable = true;
inGenderTable = false;
inSegmentsTable = false;
continue;
}
if (line.contains("Пол") && !line.contains("Возраст")) {
inAgeTable = false;
inGenderTable = true;
inSegmentsTable = false;
continue;
}
if (line.contains("Сегмент") || (line.contains("Сегменты") && line.contains("ЦА"))) {
inAgeTable = false;
inGenderTable = false;
inSegmentsTable = true;
continue;
}
if (line.startsWith("|") && !line.startsWith("|---")) {
String[] parts = line.split("\\|");
if (parts.length >= 3) {
String name = parts[1].trim();
String percentStr = parts[2].trim().replaceAll("[^0-9]", "");
if (!name.isEmpty() && !percentStr.isEmpty()) {
try {
int percent = Integer.parseInt(percentStr);
Map<String, String> item = new HashMap<>();
item.put("label", name);
item.put("value", String.valueOf(percent));
if (inAgeTable) {
ageGroups.add(item);
} else if (inGenderTable) {
genders.add(item);
} else if (inSegmentsTable) {
segments.add(item);
}
} catch (NumberFormatException e) {
// Skip invalid values
}
}
}
}
}
if (!ageGroups.isEmpty()) {
segmentation.put("ageGroups", ageGroups);
}
if (!genders.isEmpty()) {
segmentation.put("genders", genders);
}
if (!segments.isEmpty()) {
segmentation.put("segments", segments);
}
// Add defaults if parsing failed
if (segmentation.isEmpty()) {
List<Map<String, String>> defaultAgeGroups = new ArrayList<>();
defaultAgeGroups.add(Map.of("label", "18-25", "value", "25"));
defaultAgeGroups.add(Map.of("label", "26-35", "value", "35"));
defaultAgeGroups.add(Map.of("label", "36-45", "value", "30"));
defaultAgeGroups.add(Map.of("label", "46+", "value", "10"));
segmentation.put("ageGroups", defaultAgeGroups);
List<Map<String, String>> defaultGenders = new ArrayList<>();
defaultGenders.add(Map.of("label", "Мужской", "value", "55"));
defaultGenders.add(Map.of("label", "Женский", "value", "45"));
segmentation.put("genders", defaultGenders);
}
return segmentation;
}
private List<Map<String, Object>> parseChannelsPotential(String response) {
List<Map<String, Object>> channels = new ArrayList<>();
String[] channelNames = {"Instagram", "TikTok", "Telegram", "2GIS", "Google Ads"};
String[] lines = response.split("\n");
boolean inTable = false;
for (String line : lines) {
line = line.trim();
if (line.contains("Канал") && line.contains("Потенциал")) {
inTable = true;
continue;
}
if (inTable && line.startsWith("|") && !line.startsWith("|---")) {
String[] parts = line.split("\\|");
if (parts.length >= 4) {
String channelName = parts[1].trim();
String potentialStr = parts[2].trim().replaceAll("[^0-9]", "");
String justification = parts[3].trim();
if (!channelName.isEmpty()) {
Map<String, Object> channel = new HashMap<>();
channel.put("name", channelName);
try {
if (!potentialStr.isEmpty()) {
int potential = Integer.parseInt(potentialStr);
channel.put("potential", Math.min(100, Math.max(0, potential)));
} else {
channel.put("potential", 50);
}
} catch (NumberFormatException e) {
channel.put("potential", 50);
}
channel.put("justification", justification.isEmpty() ? "Подходит для данной ниши" : justification);
channels.add(channel);
}
}
}
if (inTable && line.isEmpty() && !channels.isEmpty()) {
break;
}
}
// If parsing failed, add default channels
if (channels.isEmpty()) {
for (String channelName : channelNames) {
Map<String, Object> channel = new HashMap<>();
channel.put("name", channelName);
channel.put("potential", 50 + (int)(Math.random() * 40)); // Random between 50-90
channel.put("justification", "Подходит для данной ниши");
channels.add(channel);
}
}
return channels;
}
private List<Map<String, Object>> parseConversionFunnel(String response) {
List<Map<String, Object>> funnel = new ArrayList<>();
String[] stages = {"Охват", "Вовлечённость", "Клики", "Переходы", "Заявки", "Покупки"};
String[] lines = response.split("\n");
boolean inTable = false;
for (String line : lines) {
line = line.trim();
if (line.contains("Этап") && line.contains("Значение")) {
inTable = true;
continue;
}
if (inTable && line.startsWith("|") && !line.startsWith("|---")) {
String[] parts = line.split("\\|");
if (parts.length >= 4) {
String stage = parts[1].trim();
String valueStr = parts[2].trim().replaceAll("[^0-9]", "");
String conversionStr = parts[3].trim().replaceAll("[^0-9]", "");
if (!stage.isEmpty()) {
Map<String, Object> funnelStage = new HashMap<>();
funnelStage.put("stage", stage);
try {
if (!valueStr.isEmpty()) {
funnelStage.put("value", Integer.parseInt(valueStr));
} else {
// Generate default decreasing values
int baseValue = 10000;
int index = funnel.size();
funnelStage.put("value", (int)(baseValue * Math.pow(0.6, index)));
}
} catch (NumberFormatException e) {
int baseValue = 10000;
int index = funnel.size();
funnelStage.put("value", (int)(baseValue * Math.pow(0.6, index)));
}
try {
if (!conversionStr.isEmpty()) {
funnelStage.put("conversion", Integer.parseInt(conversionStr));
} else {
// Default conversion rates
int[] defaultConversions = {100, 40, 25, 15, 8, 3};
funnelStage.put("conversion", defaultConversions[Math.min(funnel.size(), defaultConversions.length - 1)]);
}
} catch (NumberFormatException e) {
int[] defaultConversions = {100, 40, 25, 15, 8, 3};
funnelStage.put("conversion", defaultConversions[Math.min(funnel.size(), defaultConversions.length - 1)]);
}
funnel.add(funnelStage);
}
}
}
if (inTable && line.isEmpty() && !funnel.isEmpty()) {
break;
}
}
// If parsing failed, add default funnel
if (funnel.isEmpty()) {
int baseValue = 10000;
int[] conversions = {100, 40, 25, 15, 8, 3};
for (int i = 0; i < stages.length; i++) {
Map<String, Object> funnelStage = new HashMap<>();
funnelStage.put("stage", stages[i]);
funnelStage.put("value", (int)(baseValue * Math.pow(0.6, i)));
funnelStage.put("conversion", conversions[i]);
funnel.add(funnelStage);
}
}
return funnel;
}
private String generateUnifiedAnalysis(String context, MarketingAnalysisRequest request, DetailLevel detailLevel) {
if (detailLevel == null) {
detailLevel = DetailLevel.СТАНДАРТНО; // Default
}
@@ -308,52 +777,158 @@ public class MarketingAnalysisService {
lengthInstruction = "Каждый раздел должен быть структурированным (3-5 абзацев).";
}
StringBuilder promptBuilder = new StringBuilder();
promptBuilder.append("Проведи комплексный маркетинговый анализ на основе предоставленной информации о бизнесе. ");
promptBuilder.append("Анализ должен быть качественным, прикладным к конкретной тематике и задаче.\n\n");
// Build input data section
StringBuilder inputDataBuilder = new StringBuilder();
inputDataBuilder.append("Название компании/продукта: ").append(request.getProduct()).append("\n");
inputDataBuilder.append("Описание продукта: ").append(request.getProduct()).append("\n");
promptBuilder.append("Включи следующие разделы анализа:\n\n");
// Add section for each analysis type
int sectionNumber = 1;
for (AnalysisType analysisType : analysisTypes) {
promptBuilder.append(sectionNumber).append(". ");
switch (analysisType) {
case РЫНОК:
promptBuilder.append("Анализ рынка: ");
promptBuilder.append("Включи размер рынка, динамику роста, основные сегменты, тренды и перспективы развития.\n");
break;
case КОНКУРЕНТЫ:
promptBuilder.append("Анализ конкурентов: ");
promptBuilder.append("Включи основных конкурентов, их сильные и слабые стороны, позиционирование, ");
promptBuilder.append("ценовую политику и маркетинговые стратегии.\n");
break;
case ЦА:
promptBuilder.append("Анализ целевой аудитории: ");
promptBuilder.append("Включи демографические характеристики, психографический профиль, потребности и боли, ");
promptBuilder.append("поведенческие паттерны и предпочтения.\n");
break;
case КАНАЛЫ:
promptBuilder.append("Анализ маркетинговых каналов: ");
promptBuilder.append("Включи оценку эффективности различных каналов коммуникации, рекомендации по выбору каналов, ");
promptBuilder.append("особенности использования каждого канала и бюджетные рекомендации.\n");
break;
case SWOT:
promptBuilder.append("SWOT-анализ: ");
promptBuilder.append("Включи анализ Сильных сторон (Strengths), Слабых сторон (Weaknesses), ");
promptBuilder.append("Возможностей (Opportunities) и Угроз (Threats). ");
promptBuilder.append("Ответ должен быть структурированным с четким разделением по каждому разделу SWOT.\n");
break;
Map<String, Object> targetAudienceData = request.getTargetAudience();
if (targetAudienceData != null) {
StringBuilder audienceBuilder = new StringBuilder();
if (targetAudienceData.containsKey("genders")) {
@SuppressWarnings("unchecked")
List<String> genders = (List<String>) targetAudienceData.get("genders");
if (genders != null && !genders.isEmpty()) {
audienceBuilder.append("Гендер: ").append(String.join(", ", genders)).append(". ");
}
}
sectionNumber++;
if (targetAudienceData.containsKey("ageRanges")) {
@SuppressWarnings("unchecked")
List<String> ageRanges = (List<String>) targetAudienceData.get("ageRanges");
if (ageRanges != null && !ageRanges.isEmpty()) {
audienceBuilder.append("Возраст: ").append(String.join(", ", ageRanges)).append(". ");
}
}
if (targetAudienceData.containsKey("types")) {
@SuppressWarnings("unchecked")
List<String> types = (List<String>) targetAudienceData.get("types");
if (types != null && !types.isEmpty()) {
audienceBuilder.append("Тип: ").append(String.join(", ", types)).append(". ");
}
}
inputDataBuilder.append("Целевая аудитория: ").append(audienceBuilder.toString().trim()).append("\n");
}
inputDataBuilder.append("Конкуренты: ").append(request.getBusinessNiche()).append(" (уточни основных конкурентов)\n");
inputDataBuilder.append("Цель продвижения: ").append(request.getGoal()).append("\n");
List<String> regions = request.getRegion();
if (regions != null && !regions.isEmpty()) {
inputDataBuilder.append("Регион/город: ").append(String.join(", ", regions)).append("\n");
}
promptBuilder.append("\n");
promptBuilder.append(sectionNumber).append(". Общий вывод: ");
promptBuilder.append("Объедини все разделы анализа в единый комплексный вывод, ");
promptBuilder.append("выдели ключевые возможности и особенности бизнеса, ");
promptBuilder.append("сформулируй прикладные рекомендации, применимые к конкретной тематике и задаче.\n\n");
// Build the new prompt according to documentation
StringBuilder promptBuilder = new StringBuilder();
promptBuilder.append("Ты — профессиональный маркетолог и аналитик, работающий с малым и средним бизнесом Казахстана. ");
promptBuilder.append("Твоя задача — выполнить глубокий маркетинговый анализ компании пользователя с генерацией всех необходимых количественных данных для построения графиков и диаграмм. ");
promptBuilder.append("Пиши структурированно, лаконично, без воды, понятным языком, ориентируясь на предпринимателя МСБ.\n\n");
promptBuilder.append("📥 Входные данные пользователя:\n");
promptBuilder.append(inputDataBuilder.toString());
promptBuilder.append("Если данных недостаточно — логично дополни, отметив: «Данные уточнены системой автоматически».\n\n");
promptBuilder.append("📊 Требования к числовым данным\n");
promptBuilder.append("Ты обязан сгенерировать реалистичные числовые значения, подходящие для автоматической визуализации.\n\n");
promptBuilder.append("1️⃣ Сезонность спроса (линейный график, 12 месяцев)\n");
promptBuilder.append("Выведи таблицу:\n");
promptBuilder.append("| Месяц | Уровень спроса (0–100) |\n");
promptBuilder.append("Значения должны:\n");
promptBuilder.append("- соответствовать нише,\n");
promptBuilder.append("- иметь сезонную динамику,\n");
promptBuilder.append("- быть реалистичными.\n\n");
promptBuilder.append("2️⃣ Сравнительный анализ конкурентов (столбчатая диаграмма)\n");
promptBuilder.append("Выведи таблицу:\n");
promptBuilder.append("| Конкурент | Охват аудитории | Активность в соцсетях (0–100) | Количество отзывов | Сильные стороны | Слабые стороны |\n");
promptBuilder.append("Охват — в диапазоне 1 000–50 000, Активность — 0–100.\n\n");
promptBuilder.append("3️⃣ Сегментация целевой аудитории (круговая диаграмма)\n");
promptBuilder.append("Определи:\n");
promptBuilder.append("- возрастные группы (%)\n");
promptBuilder.append("- пол (%)\n");
promptBuilder.append("- 3–5 ключевых сегментов ЦА (%)\n");
promptBuilder.append("Сумма процентов = 100%.\n\n");
promptBuilder.append("4️⃣ Потенциал каналов продвижения (бар-чарт)\n");
promptBuilder.append("Каналы: Instagram, TikTok, Telegram, 2GIS, Google Ads\n");
promptBuilder.append("Выведи таблицу:\n");
promptBuilder.append("| Канал | Потенциал (0–100) | Обоснование |\n\n");
promptBuilder.append("5️⃣ Конверсионная воронка (воронка спроса)\n");
promptBuilder.append("Выведи таблицу:\n");
promptBuilder.append("| Этап | Значение | Конверсия (%) |\n");
promptBuilder.append("Этапы:\n");
promptBuilder.append("- Охват\n");
promptBuilder.append("- Вовлечённость\n");
promptBuilder.append("- Клики\n");
promptBuilder.append("- Переходы\n");
promptBuilder.append("- Заявки\n");
promptBuilder.append("- Покупки\n");
promptBuilder.append("Числа должны уменьшаться логично.\n\n");
promptBuilder.append("📘 Теперь сформируй полный маркетинговый анализ:\n\n");
promptBuilder.append("I. Анализ продукта\n");
promptBuilder.append("- ключевые свойства\n");
promptBuilder.append("- преимущества\n");
promptBuilder.append("- возможные слабости\n");
promptBuilder.append("- ценность для рынка Казахстана\n\n");
promptBuilder.append("II. Анализ рынка Казахстана\n");
promptBuilder.append("- динамика\n");
promptBuilder.append("- тренды\n");
promptBuilder.append("- сезонность (используй данные из таблицы)\n");
promptBuilder.append("- возможности и ограничения\n\n");
promptBuilder.append("III. Анализ целевой аудитории\n");
promptBuilder.append("- портрет покупателя\n");
promptBuilder.append("- боли, потребности, мотивации\n");
promptBuilder.append("- триггеры принятия решения\n");
promptBuilder.append("- сегментация (вставить таблицу)\n\n");
promptBuilder.append("IV. Анализ конкурентов\n");
promptBuilder.append("Вставить таблицу конкурентов. Сформировать вывод о конкурентной среде. Указать рыночные ниши, которые свободны.\n\n");
promptBuilder.append("V. SWOT-анализ\n");
promptBuilder.append("Сформируй матрицу 2×2:\n");
promptBuilder.append("- Strengths\n");
promptBuilder.append("- Weaknesses\n");
promptBuilder.append("- Opportunities\n");
promptBuilder.append("- Threats\n\n");
promptBuilder.append("VI. Уникальное торговое предложение (УТП)\n");
promptBuilder.append("Создай 1–2 варианта УТП. Требования:\n");
promptBuilder.append("- ясно,\n");
promptBuilder.append("- коротко,\n");
promptBuilder.append("- конкретно,\n");
promptBuilder.append("- опираясь на анализ продукта и конкурентов.\n\n");
promptBuilder.append("VII. Рекомендации по позиционированию\n");
promptBuilder.append("Определи:\n");
promptBuilder.append("- образ бренда\n");
promptBuilder.append("- ключевое сообщение\n");
promptBuilder.append("- стиль коммуникации\n");
promptBuilder.append("- что важно подчеркнуть\n\n");
promptBuilder.append("VIII. Рекомендации по каналам продвижения\n");
promptBuilder.append("Вставить таблицу потенциала каналов. Дать объяснение, почему каждый канал подходит для этой ниши.\n\n");
promptBuilder.append("IX. Рекомендации по контенту\n");
promptBuilder.append("Приведи набор контент-направлений:\n");
promptBuilder.append("- продающий\n");
promptBuilder.append("- экспертный\n");
promptBuilder.append("- визуальный\n");
promptBuilder.append("- доверительный\n");
promptBuilder.append("- развлекательный\n");
promptBuilder.append("Дай примеры публикаций.\n\n");
promptBuilder.append("X. Итоговая стратегия продвижения\n");
promptBuilder.append("Сформулируй чётко:\n");
promptBuilder.append("- цель\n");
promptBuilder.append("- ключевые шаги\n");
promptBuilder.append("- что делать в первую очередь\n");
promptBuilder.append("- что даст максимальный эффект\n");
promptBuilder.append("- ожидаемый результат\n\n");
promptBuilder.append(lengthInstruction).append(" ");
promptBuilder.append("Ответ должен быть на русском языке.\n\n");
@@ -573,13 +1148,132 @@ public class MarketingAnalysisService {
}
markdown.append("\n");
// Display unified analysis
// Add chart data tables
@SuppressWarnings("unchecked")
Map<String, Object> chartsData = (Map<String, Object>) reportData.get("chartsData");
if (chartsData != null && !chartsData.isEmpty()) {
markdown.append("## 📊 Числовые данные для графиков\n\n");
// Seasonality table
@SuppressWarnings("unchecked")
Map<String, Integer> seasonality = (Map<String, Integer>) chartsData.get("seasonality");
if (seasonality != null && !seasonality.isEmpty()) {
markdown.append("### 1️⃣ Сезонность спроса (12 месяцев)\n\n");
markdown.append("| Месяц | Уровень спроса (0–100) |\n");
markdown.append("|-------|------------------------|\n");
for (Map.Entry<String, Integer> entry : seasonality.entrySet()) {
markdown.append("| ").append(entry.getKey()).append(" | ").append(entry.getValue()).append(" |\n");
}
markdown.append("\n");
}
// Competitors table
@SuppressWarnings("unchecked")
List<Map<String, Object>> competitors = (List<Map<String, Object>>) chartsData.get("competitors");
if (competitors != null && !competitors.isEmpty()) {
markdown.append("### 2️⃣ Сравнительный анализ конкурентов\n\n");
markdown.append("| Конкурент | Охват аудитории | Активность в соцсетях (0–100) | Количество отзывов | Сильные стороны | Слабые стороны |\n");
markdown.append("|-----------|-----------------|-------------------------------|-------------------|----------------|----------------|\n");
for (Map<String, Object> comp : competitors) {
markdown.append("| ").append(comp.get("name")).append(" | ")
.append(comp.get("reach")).append(" | ")
.append(comp.get("activity")).append(" | ")
.append(comp.get("reviews")).append(" | ")
.append(comp.get("strengths")).append(" | ")
.append(comp.get("weaknesses")).append(" |\n");
}
markdown.append("\n");
}
// Audience segmentation tables
@SuppressWarnings("unchecked")
Map<String, Object> audienceSegmentation = (Map<String, Object>) chartsData.get("audienceSegmentation");
if (audienceSegmentation != null && !audienceSegmentation.isEmpty()) {
markdown.append("### 3️⃣ Сегментация целевой аудитории\n\n");
@SuppressWarnings("unchecked")
List<Map<String, String>> ageGroups = (List<Map<String, String>>) audienceSegmentation.get("ageGroups");
if (ageGroups != null && !ageGroups.isEmpty()) {
markdown.append("#### Возрастные группы\n\n");
markdown.append("| Возрастная группа | Процент (%) |\n");
markdown.append("|-------------------|-------------|\n");
for (Map<String, String> group : ageGroups) {
markdown.append("| ").append(group.get("label")).append(" | ").append(group.get("value")).append(" |\n");
}
markdown.append("\n");
}
@SuppressWarnings("unchecked")
List<Map<String, String>> genders = (List<Map<String, String>>) audienceSegmentation.get("genders");
if (genders != null && !genders.isEmpty()) {
markdown.append("#### Пол\n\n");
markdown.append("| Пол | Процент (%) |\n");
markdown.append("|-----|-------------|\n");
for (Map<String, String> gender : genders) {
markdown.append("| ").append(gender.get("label")).append(" | ").append(gender.get("value")).append(" |\n");
}
markdown.append("\n");
}
@SuppressWarnings("unchecked")
List<Map<String, String>> segments = (List<Map<String, String>>) audienceSegmentation.get("segments");
if (segments != null && !segments.isEmpty()) {
markdown.append("#### Сегменты ЦА\n\n");
markdown.append("| Сегмент | Процент (%) |\n");
markdown.append("|---------|-------------|\n");
for (Map<String, String> segment : segments) {
markdown.append("| ").append(segment.get("label")).append(" | ").append(segment.get("value")).append(" |\n");
}
markdown.append("\n");
}
}
// Channels potential table
@SuppressWarnings("unchecked")
List<Map<String, Object>> channelsPotential = (List<Map<String, Object>>) chartsData.get("channelsPotential");
if (channelsPotential != null && !channelsPotential.isEmpty()) {
markdown.append("### 4️⃣ Потенциал каналов продвижения\n\n");
markdown.append("| Канал | Потенциал (0–100) | Обоснование |\n");
markdown.append("|-------|-------------------|-------------|\n");
for (Map<String, Object> channel : channelsPotential) {
markdown.append("| ").append(channel.get("name")).append(" | ")
.append(channel.get("potential")).append(" | ")
.append(channel.get("justification")).append(" |\n");
}
markdown.append("\n");
}
// Conversion funnel table
@SuppressWarnings("unchecked")
List<Map<String, Object>> conversionFunnel = (List<Map<String, Object>>) chartsData.get("conversionFunnel");
if (conversionFunnel != null && !conversionFunnel.isEmpty()) {
markdown.append("### 5️⃣ Конверсионная воронка\n\n");
markdown.append("| Этап | Значение | Конверсия (%) |\n");
markdown.append("|------|----------|---------------|\n");
for (Map<String, Object> stage : conversionFunnel) {
markdown.append("| ").append(stage.get("stage")).append(" | ")
.append(stage.get("value")).append(" | ")
.append(stage.get("conversion")).append(" |\n");
}
markdown.append("\n");
}
}
// Display full analysis with 10 sections
String fullAnalysis = (String) reportData.get("fullAnalysis");
if (fullAnalysis != null && !fullAnalysis.trim().isEmpty()) {
markdown.append("## 📘 Полный маркетинговый анализ\n\n");
markdown.append(fullAnalysis).append("\n\n");
} else {
// Fallback to summary if fullAnalysis is not available
String summary = (String) reportData.get("summary");
if (summary != null && !summary.trim().isEmpty()) {
markdown.append("## Комплексный маркетинговый анализ\n\n");
markdown.append(summary).append("\n\n");
}
}
// Keep backward compatibility sections
@SuppressWarnings("unchecked")
Map<String, Object> targetAudience = (Map<String, Object>) reportData.get("targetAudience");
if (targetAudience != null) {
@@ -677,7 +1371,21 @@ public class MarketingAnalysisService {
MarketingAnalysis analysis) {
MarketingAnalysisResult.MarketingReport report = new MarketingAnalysisResult.MarketingReport();
// Set summary (backward compatibility)
report.setSummary((String) reportData.get("summary"));
// Set full analysis (new field)
String fullAnalysis = (String) reportData.get("fullAnalysis");
if (fullAnalysis != null) {
report.setFullAnalysis(fullAnalysis);
}
// Set charts data (new field)
@SuppressWarnings("unchecked")
Map<String, Object> chartsData = (Map<String, Object>) reportData.get("chartsData");
if (chartsData != null) {
report.setChartsData(chartsData);
}
@SuppressWarnings("unchecked")
Map<String, Object> targetAudienceData = (Map<String, Object>) reportData.get("targetAudience");
@@ -0,0 +1,116 @@
ФИНАЛЬНЫЙ ПРОМТ ДЛЯ ГЕНЕРАЦИИ МАРКЕТИНГОВОГО АНАЛИЗА С ГРАФИКАМИ И ЧИСЛОВЫМИ ПОКАЗАТЕЛЯМИ
(готов к использованию в backend или системе агентов)
Ты — профессиональный маркетолог и аналитик, работающий с малым и средним бизнесом Казахстана.
Твоя задача — выполнить глубокий маркетинговый анализ компании пользователя с генерацией всех необходимых количественных данных для построения графиков и диаграмм.
Пиши структурированно, лаконично, без воды, понятным языком, ориентируясь на предпринимателя МСБ.
📥 Входные данные пользователя:
Название компании/продукта: {{company_name}}
Описание продукта: {{product_description}}
Целевая аудитория: {{target_audience}}
Конкуренты: {{competitors}}
Цель продвижения: {{goals}}
Регион/город: {{region}}
Если данных недостаточно — логично дополни, отметив:
«Данные уточнены системой автоматически».
📊 Требования к числовым данным
Ты обязан сгенерировать реалистичные числовые значения, подходящие для автоматической визуализации.
1️⃣ Сезонность спроса (линейный график, 12 месяцев)
Выведи таблицу:
| Месяц | Уровень спроса (0–100) |
Значения должны:
соответствовать нише,
иметь сезонную динамику,
быть реалистичными.
2️⃣ Сравнительный анализ конкурентов (столбчатая диаграмма)
Выведи таблицу:
| Конкурент | Охват аудитории | Активность в соцсетях (0–100) | Количество отзывов | Сильные стороны | Слабые стороны |
Охват — в диапазоне 1 000–50 000,
Активность — 0–100.
3️⃣ Сегментация целевой аудитории (круговая диаграмма)
Определи:
возрастные группы (%)
пол (%)
3–5 ключевых сегментов ЦА (%)
Сумма процентов = 100%.
4️⃣ Потенциал каналов продвижения (бар-чарт)
Каналы: Instagram, TikTok, Telegram, 2GIS, Google Ads
Выведи таблицу:
| Канал | Потенциал (0–100) | Обоснование |
5️⃣ Конверсионная воронка (воронка спроса)
Выведи таблицу:
| Этап | Значение | Конверсия (%) |
Этапы:
Охват
Вовлечённость
Клики
Переходы
Заявки
Покупки
Числа должны уменьшаться логично.
📘 Теперь сформируй полный маркетинговый анализ:
I. Анализ продукта
ключевые свойства
преимущества
возможные слабости
ценность для рынка Казахстана
II. Анализ рынка Казахстана
динамика
тренды
сезонность (используй данные из таблицы)
возможности и ограничения
III. Анализ целевой аудитории
портрет покупателя
боли, потребности, мотивации
триггеры принятия решения
сегментация (вставить таблицу)
IV. Анализ конкурентов
Вставить таблицу конкурентов.
Сформировать вывод о конкурентной среде.
Указать рыночные ниши, которые свободны.
V. SWOT-анализ
Сформируй матрицу 2×2:
Strengths
Weaknesses
Opportunities
Threats
VI. Уникальное торговое предложение (УТП)
Создай 1–2 варианта УТП.
Требования:
ясно,
коротко,
конкретно,
опираясь на анализ продукта и конкурентов.
VII. Рекомендации по позиционированию
Определи:
образ бренда
ключевое сообщение
стиль коммуникации
что важно подчеркнуть
VIII. Рекомендации по каналам продвижения
Вставить таблицу потенциала каналов.
Дать объяснение, почему каждый канал подходит для этой ниши.
IX. Рекомендации по контенту
Приведи набор контент-направлений:
продающий
экспертный
визуальный
доверительный
развлекательный
Дай примеры публикаций.
X. Итоговая стратегия продвижения
Сформулируй чётко:
цель
ключевые шаги
что делать в первую очередь
что даст максимальный эффект
ожидаемый результат