.
This commit is contained in:
@@ -31,6 +31,14 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MarketingAnalysisService {
|
public class MarketingAnalysisService {
|
||||||
|
private static final String AI_MANDATORY_JSON_USAGE_WARNING = "ВНИМАНИЕ: Если в предоставленном JSON есть данные, ты ОБЯЗАН использовать их и вставить соответствующий тег [[CHART_...]]. "
|
||||||
|
+ "Никогда не пиши, что данных нет, если JSON не пустой.\n\n";
|
||||||
|
|
||||||
|
private void appendMandatoryJsonUsageWarning(StringBuilder promptBuilder) {
|
||||||
|
if (promptBuilder != null) {
|
||||||
|
promptBuilder.append(AI_MANDATORY_JSON_USAGE_WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MarketingAnalysisService.class);
|
private static final Logger logger = LoggerFactory.getLogger(MarketingAnalysisService.class);
|
||||||
private static final int MAX_RETRY_ATTEMPTS = 3;
|
private static final int MAX_RETRY_ATTEMPTS = 3;
|
||||||
@@ -1209,6 +1217,7 @@ public class MarketingAnalysisService {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "market":
|
case "market":
|
||||||
|
// ОБЯЗАТЕЛЬНО: seasonality + market должны доходить до секции рынка
|
||||||
if (fullJson.containsKey("seasonality")) {
|
if (fullJson.containsKey("seasonality")) {
|
||||||
context.put("seasonality", fullJson.get("seasonality"));
|
context.put("seasonality", fullJson.get("seasonality"));
|
||||||
}
|
}
|
||||||
@@ -1220,6 +1229,7 @@ public class MarketingAnalysisService {
|
|||||||
// Новая структура: audienceAnalysis (ageGroups, genderDistribution, segments,
|
// Новая структура: audienceAnalysis (ageGroups, genderDistribution, segments,
|
||||||
// keyTakeaways, etc.)
|
// keyTakeaways, etc.)
|
||||||
if (fullJson.containsKey("audienceAnalysis")) {
|
if (fullJson.containsKey("audienceAnalysis")) {
|
||||||
|
// ОБЯЗАТЕЛЬНО: audienceAnalysis должен доходить до секции аудитории
|
||||||
context.put("audienceAnalysis", fullJson.get("audienceAnalysis"));
|
context.put("audienceAnalysis", fullJson.get("audienceAnalysis"));
|
||||||
} else if (fullJson.containsKey("audienceSegments")) {
|
} else if (fullJson.containsKey("audienceSegments")) {
|
||||||
// Backward compatibility (старая структура)
|
// Backward compatibility (старая структура)
|
||||||
@@ -1231,6 +1241,7 @@ public class MarketingAnalysisService {
|
|||||||
// competitorsDetails, swotCompetitors, etc.)
|
// competitorsDetails, swotCompetitors, etc.)
|
||||||
if (fullJson.containsKey("competitorAnalysis")) {
|
if (fullJson.containsKey("competitorAnalysis")) {
|
||||||
Object competitorAnalysisObj = fullJson.get("competitorAnalysis");
|
Object competitorAnalysisObj = fullJson.get("competitorAnalysis");
|
||||||
|
// ОБЯЗАТЕЛЬНО: competitorAnalysis должен доходить до секции конкурентов
|
||||||
context.put("competitorAnalysis", competitorAnalysisObj);
|
context.put("competitorAnalysis", competitorAnalysisObj);
|
||||||
// Aliases для совместимости: плоские ключи для секции конкурентов
|
// Aliases для совместимости: плоские ключи для секции конкурентов
|
||||||
// (данные физически лежат в competitorAnalysis.*)
|
// (данные физически лежат в competitorAnalysis.*)
|
||||||
@@ -1410,6 +1421,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder
|
promptBuilder
|
||||||
.append("На основе следующего JSON напиши РАЗДЕЛ I (Краткое резюме) маркетингового отчета.\n\n");
|
.append("На основе следующего JSON напиши РАЗДЕЛ I (Краткое резюме) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -1459,6 +1471,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder
|
promptBuilder
|
||||||
.append("На основе следующего JSON напиши РАЗДЕЛ II (Анализ продукта) маркетингового отчета.\n\n");
|
.append("На основе следующего JSON напиши РАЗДЕЛ II (Анализ продукта) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -1513,6 +1526,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder.append(
|
promptBuilder.append(
|
||||||
"На основе следующего JSON напиши РАЗДЕЛ III (Анализ рынка и сезонности) маркетингового отчета.\n\n");
|
"На основе следующего JSON напиши РАЗДЕЛ III (Анализ рынка и сезонности) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -1570,6 +1584,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder.append(
|
promptBuilder.append(
|
||||||
"На основе следующего JSON напиши РАЗДЕЛ IV (Анализ целевой аудитории) маркетингового отчета.\n\n");
|
"На основе следующего JSON напиши РАЗДЕЛ IV (Анализ целевой аудитории) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -1651,6 +1666,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder.append(
|
promptBuilder.append(
|
||||||
"На основе следующего JSON напиши РАЗДЕЛ V (Анализ конкурентов) маркетингового отчета.\n\n");
|
"На основе следующего JSON напиши РАЗДЕЛ V (Анализ конкурентов) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -1737,6 +1753,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder.append("На основе следующего JSON напиши РАЗДЕЛ VI (SWOT-анализ) маркетингового отчета.\n\n");
|
promptBuilder.append("На основе следующего JSON напиши РАЗДЕЛ VI (SWOT-анализ) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
promptBuilder.append("1. Объем: минимум 500-700 слов\n");
|
promptBuilder.append("1. Объем: минимум 500-700 слов\n");
|
||||||
@@ -1801,6 +1818,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder.append(
|
promptBuilder.append(
|
||||||
"На основе следующего JSON напиши РАЗДЕЛ VII (Каналы продвижения и их потенциал) маркетингового отчета.\n\n");
|
"На основе следующего JSON напиши РАЗДЕЛ VII (Каналы продвижения и их потенциал) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -1864,6 +1882,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder
|
promptBuilder
|
||||||
.append("На основе следующего JSON напиши РАЗДЕЛ VIII (Воронка спроса) маркетингового отчета.\n\n");
|
.append("На основе следующего JSON напиши РАЗДЕЛ VIII (Воронка спроса) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -1933,6 +1952,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder.append(
|
promptBuilder.append(
|
||||||
"На основе следующего JSON напиши РАЗДЕЛ IX (Рекомендации по позиционированию и УТП) маркетингового отчета.\n\n");
|
"На основе следующего JSON напиши РАЗДЕЛ IX (Рекомендации по позиционированию и УТП) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -1987,6 +2007,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder.append(
|
promptBuilder.append(
|
||||||
"На основе следующего JSON напиши РАЗДЕЛ X (Рекомендации по контенту) маркетингового отчета.\n\n");
|
"На основе следующего JSON напиши РАЗДЕЛ X (Рекомендации по контенту) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -2039,6 +2060,7 @@ public class MarketingAnalysisService {
|
|||||||
String jsonString = objectMapper.writeValueAsString(contextJson);
|
String jsonString = objectMapper.writeValueAsString(contextJson);
|
||||||
StringBuilder promptBuilder = new StringBuilder();
|
StringBuilder promptBuilder = new StringBuilder();
|
||||||
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
promptBuilder.append("Ты — высокооплачиваемый бизнес-консультант и маркетинговый аналитик.\n\n");
|
||||||
|
appendMandatoryJsonUsageWarning(promptBuilder);
|
||||||
promptBuilder.append(
|
promptBuilder.append(
|
||||||
"На основе следующего JSON напиши РАЗДЕЛ XI (Итоговая стратегия: что делать в первую очередь) маркетингового отчета.\n\n");
|
"На основе следующего JSON напиши РАЗДЕЛ XI (Итоговая стратегия: что делать в первую очередь) маркетингового отчета.\n\n");
|
||||||
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
promptBuilder.append("ТРЕБОВАНИЯ:\n");
|
||||||
@@ -2255,6 +2277,13 @@ public class MarketingAnalysisService {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Map<String, Object> ageGroupsMap = (Map<String, Object>) audienceAnalysis.get("ageGroups");
|
Map<String, Object> ageGroupsMap = (Map<String, Object>) audienceAnalysis.get("ageGroups");
|
||||||
if (ageGroupsMap != null) {
|
if (ageGroupsMap != null) {
|
||||||
|
// CRITICAL: отдельный ключ для графика возраста (для
|
||||||
|
// [[CHART_AGE_DISTRIBUTION]])
|
||||||
|
// Дальше он будет преобразован в Chart.js формат в transformToChartConfig()
|
||||||
|
if (!ageGroupsMap.isEmpty()) {
|
||||||
|
chartsData.put("audienceAge", ageGroupsMap);
|
||||||
|
}
|
||||||
|
|
||||||
List<Map<String, String>> ageGroupsList = new ArrayList<>();
|
List<Map<String, String>> ageGroupsList = new ArrayList<>();
|
||||||
for (Map.Entry<String, Object> entry : ageGroupsMap.entrySet()) {
|
for (Map.Entry<String, Object> entry : ageGroupsMap.entrySet()) {
|
||||||
Map<String, String> ageGroup = new HashMap<>();
|
Map<String, String> ageGroup = new HashMap<>();
|
||||||
@@ -2270,6 +2299,13 @@ public class MarketingAnalysisService {
|
|||||||
Map<String, Object> genderDistributionMap = (Map<String, Object>) audienceAnalysis
|
Map<String, Object> genderDistributionMap = (Map<String, Object>) audienceAnalysis
|
||||||
.get("genderDistribution");
|
.get("genderDistribution");
|
||||||
if (genderDistributionMap != null) {
|
if (genderDistributionMap != null) {
|
||||||
|
// CRITICAL: отдельный ключ для графика гендера (для
|
||||||
|
// [[CHART_GENDER_DISTRIBUTION]])
|
||||||
|
// Дальше он будет преобразован в Chart.js формат в transformToChartConfig()
|
||||||
|
if (!genderDistributionMap.isEmpty()) {
|
||||||
|
chartsData.put("audienceGender", genderDistributionMap);
|
||||||
|
}
|
||||||
|
|
||||||
List<Map<String, String>> gendersList = new ArrayList<>();
|
List<Map<String, String>> gendersList = new ArrayList<>();
|
||||||
for (Map.Entry<String, Object> entry : genderDistributionMap.entrySet()) {
|
for (Map.Entry<String, Object> entry : genderDistributionMap.entrySet()) {
|
||||||
Map<String, String> genderData = new HashMap<>();
|
Map<String, String> genderData = new HashMap<>();
|
||||||
@@ -2607,6 +2643,73 @@ public class MarketingAnalysisService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3b. Audience Age (CRITICAL): прямой источник из audienceAnalysis.ageGroups
|
||||||
|
// Формат rawData.audienceAge: Map<String, Object> ("18-24"->число, ...)
|
||||||
|
if (rawData.containsKey("audienceAge") && !formattedCharts.containsKey("audienceAge")) {
|
||||||
|
Object ageObj = rawData.get("audienceAge");
|
||||||
|
if (ageObj instanceof Map) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> ageMap = (Map<String, Object>) ageObj;
|
||||||
|
if (ageMap != null && !ageMap.isEmpty()) {
|
||||||
|
List<String> labels = new ArrayList<>(ageMap.keySet());
|
||||||
|
List<Number> data = new ArrayList<>();
|
||||||
|
for (String k : labels) {
|
||||||
|
Object v = ageMap.get(k);
|
||||||
|
if (v instanceof Number) {
|
||||||
|
data.add(((Number) v).intValue());
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
data.add(Integer.parseInt(String.valueOf(v)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
data.add(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, Object> dataset = new HashMap<>();
|
||||||
|
dataset.put("label", "Возрастные группы");
|
||||||
|
dataset.put("data", data);
|
||||||
|
Map<String, Object> chart = new HashMap<>();
|
||||||
|
chart.put("labels", labels);
|
||||||
|
chart.put("datasets", List.of(dataset));
|
||||||
|
formattedCharts.put("audienceAge", chart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3c. Audience Gender (CRITICAL): прямой источник из
|
||||||
|
// audienceAnalysis.genderDistribution
|
||||||
|
// Формат rawData.audienceGender: Map<String, Object> ("Женщины"->число, ...)
|
||||||
|
if (rawData.containsKey("audienceGender") && !formattedCharts.containsKey("audienceGender")) {
|
||||||
|
Object genderObj = rawData.get("audienceGender");
|
||||||
|
if (genderObj instanceof Map) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> genderMap = (Map<String, Object>) genderObj;
|
||||||
|
if (genderMap != null && !genderMap.isEmpty()) {
|
||||||
|
List<String> labels = new ArrayList<>(genderMap.keySet());
|
||||||
|
List<Number> data = new ArrayList<>();
|
||||||
|
for (String k : labels) {
|
||||||
|
Object v = genderMap.get(k);
|
||||||
|
if (v instanceof Number) {
|
||||||
|
data.add(((Number) v).intValue());
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
data.add(Integer.parseInt(String.valueOf(v)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
data.add(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, Object> dataset = new HashMap<>();
|
||||||
|
dataset.put("label", "Гендерное распределение");
|
||||||
|
dataset.put("data", data);
|
||||||
|
Map<String, Object> chart = new HashMap<>();
|
||||||
|
chart.put("labels", labels);
|
||||||
|
chart.put("datasets", List.of(dataset));
|
||||||
|
formattedCharts.put("audienceGender", chart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Conversion Funnel (List<Map>)
|
// 4. Conversion Funnel (List<Map>)
|
||||||
if (rawData.containsKey("conversionFunnel")) {
|
if (rawData.containsKey("conversionFunnel")) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -2679,7 +2782,36 @@ public class MarketingAnalysisService {
|
|||||||
formattedCharts.put("comparisonTable", rawData.get("comparisonTable"));
|
formattedCharts.put("comparisonTable", rawData.get("comparisonTable"));
|
||||||
}
|
}
|
||||||
if (rawData.containsKey("marketShareChart")) {
|
if (rawData.containsKey("marketShareChart")) {
|
||||||
formattedCharts.put("marketShareChart", rawData.get("marketShareChart"));
|
// CRITICAL: marketShareChart должен быть Chart.js config для вставки в
|
||||||
|
// fullAnalysis
|
||||||
|
// raw формат: List<{name,value}>. Преобразуем в {labels,datasets}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Map<String, Object>> marketShare = (List<Map<String, Object>>) rawData.get("marketShareChart");
|
||||||
|
if (marketShare != null && !marketShare.isEmpty()) {
|
||||||
|
List<String> labels = new ArrayList<>();
|
||||||
|
List<Number> data = new ArrayList<>();
|
||||||
|
for (Map<String, Object> item : marketShare) {
|
||||||
|
labels.add(String.valueOf(item.getOrDefault("name", "Unknown")));
|
||||||
|
Object v = item.get("value");
|
||||||
|
if (v instanceof Number) {
|
||||||
|
data.add(((Number) v).intValue());
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
data.add(Integer.parseInt(String.valueOf(v)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
data.add(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, Object> dataset = new HashMap<>();
|
||||||
|
dataset.put("label", "Доли рынка (%)");
|
||||||
|
dataset.put("data", data);
|
||||||
|
|
||||||
|
Map<String, Object> chart = new HashMap<>();
|
||||||
|
chart.put("labels", labels);
|
||||||
|
chart.put("datasets", List.of(dataset));
|
||||||
|
formattedCharts.put("marketShareChart", chart);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (rawData.containsKey("swotCompetitors")) {
|
if (rawData.containsKey("swotCompetitors")) {
|
||||||
formattedCharts.put("swotCompetitors", rawData.get("swotCompetitors"));
|
formattedCharts.put("swotCompetitors", rawData.get("swotCompetitors"));
|
||||||
@@ -3212,8 +3344,6 @@ public class MarketingAnalysisService {
|
|||||||
if (!result.contains(placeholder)) {
|
if (!result.contains(placeholder)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
processedPlaceholders.add(placeholder);
|
|
||||||
Object chartData = chartsData.get(mapping.dataKey);
|
Object chartData = chartsData.get(mapping.dataKey);
|
||||||
|
|
||||||
if (!isEmptyChartData(chartData)) {
|
if (!isEmptyChartData(chartData)) {
|
||||||
@@ -3222,6 +3352,9 @@ public class MarketingAnalysisService {
|
|||||||
String jsonBlock = "\n```json:" + mapping.chartKey + "\n" + jsonStr + "\n```\n";
|
String jsonBlock = "\n```json:" + mapping.chartKey + "\n" + jsonStr + "\n```\n";
|
||||||
String replacement = java.util.regex.Matcher.quoteReplacement(jsonBlock);
|
String replacement = java.util.regex.Matcher.quoteReplacement(jsonBlock);
|
||||||
result = result.replaceAll(regex, replacement);
|
result = result.replaceAll(regex, replacement);
|
||||||
|
// CRITICAL: считаем плейсхолдер обработанным ТОЛЬКО если реально вставили
|
||||||
|
// JSON-блок
|
||||||
|
processedPlaceholders.add(placeholder);
|
||||||
logger.info("Successfully replaced placeholder: {} with data from key: {}", placeholder,
|
logger.info("Successfully replaced placeholder: {} with data from key: {}", placeholder,
|
||||||
mapping.dataKey);
|
mapping.dataKey);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
@@ -3291,6 +3424,8 @@ public class MarketingAnalysisService {
|
|||||||
String result = text;
|
String result = text;
|
||||||
|
|
||||||
Map<String, List<String>> sectionToPlaceholders = new HashMap<>();
|
Map<String, List<String>> sectionToPlaceholders = new HashMap<>();
|
||||||
|
// NOTE: Используем regex-паттерны, чтобы совпасть с вариациями заголовков,
|
||||||
|
// которые может генерировать модель
|
||||||
sectionToPlaceholders.put("III\\.\\s*Анализ рынка и сезонности", List.of("[[CHART_SEASONALITY]]"));
|
sectionToPlaceholders.put("III\\.\\s*Анализ рынка и сезонности", List.of("[[CHART_SEASONALITY]]"));
|
||||||
sectionToPlaceholders.put("IV\\.\\s*Анализ целевой аудитории",
|
sectionToPlaceholders.put("IV\\.\\s*Анализ целевой аудитории",
|
||||||
List.of("[[CHART_AGE_DISTRIBUTION]]", "[[CHART_GENDER_DISTRIBUTION]]"));
|
List.of("[[CHART_AGE_DISTRIBUTION]]", "[[CHART_GENDER_DISTRIBUTION]]"));
|
||||||
@@ -3298,7 +3433,9 @@ public class MarketingAnalysisService {
|
|||||||
List.of("[[CHART_MARKET_SHARE]]", "[[TABLE_COMPARISON]]"));
|
List.of("[[CHART_MARKET_SHARE]]", "[[TABLE_COMPARISON]]"));
|
||||||
sectionToPlaceholders.put("VII\\.\\s*Каналы продвижения", List.of("[[CHART_CHANNELS]]"));
|
sectionToPlaceholders.put("VII\\.\\s*Каналы продвижения", List.of("[[CHART_CHANNELS]]"));
|
||||||
sectionToPlaceholders.put("VIII\\.\\s*Воронка спроса", List.of("[[CHART_FUNNEL]]"));
|
sectionToPlaceholders.put("VIII\\.\\s*Воронка спроса", List.of("[[CHART_FUNNEL]]"));
|
||||||
sectionToPlaceholders.put("VI\\.\\s*SWOT-анализ", List.of("[[CHART_SWOT]]"));
|
// SWOT может быть написан как SWOT-анализ / SWOT анализ / SWОT-анализ, поэтому
|
||||||
|
// делаем мягче
|
||||||
|
sectionToPlaceholders.put("VI\\.\\s*SWOT[-\\s]?анализ", List.of("[[CHART_SWOT]]"));
|
||||||
|
|
||||||
for (Map.Entry<String, List<String>> sectionEntry : sectionToPlaceholders.entrySet()) {
|
for (Map.Entry<String, List<String>> sectionEntry : sectionToPlaceholders.entrySet()) {
|
||||||
String sectionPattern = sectionEntry.getKey();
|
String sectionPattern = sectionEntry.getKey();
|
||||||
|
|||||||
Reference in New Issue
Block a user