This commit is contained in:
root
2025-12-15 20:26:58 +05:00
parent 34fc6bda00
commit 7ade14c2ee
@@ -2054,6 +2054,264 @@ public class MarketingAnalysisService {
return chartsData;
}
/**
* Преобразует сырые данные графиков в формат Chart.js (labels, datasets).
*
* @param rawData сырые данные из parseChartDataFromJson
* @return Map с данными в формате Chart.js для графиков и исходными данными для
* таблиц
*/
private Map<String, Object> transformToChartConfig(Map<String, Object> rawData) {
logger.info("transformToChartConfig: Starting transformation to Chart.js format");
Map<String, Object> formattedCharts = new HashMap<>();
if (rawData == null || rawData.isEmpty()) {
logger.warn("transformToChartConfig: rawData is null or empty");
return formattedCharts;
}
try {
// 1. Seasonality (Map<String, Integer>)
if (rawData.containsKey("seasonality")) {
@SuppressWarnings("unchecked")
Map<String, Integer> seasonality = (Map<String, Integer>) rawData.get("seasonality");
if (seasonality != null && !seasonality.isEmpty()) {
List<String> labels = new ArrayList<>(seasonality.keySet());
List<Integer> data = new ArrayList<>(seasonality.values());
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("seasonality", chart);
logger.debug("transformToChartConfig: Transformed seasonality chart with {} labels", labels.size());
}
}
// 2. Competitors (List<Map>)
if (rawData.containsKey("competitors")) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> competitors = (List<Map<String, Object>>) rawData.get("competitors");
if (competitors != null && !competitors.isEmpty()) {
List<String> labels = new ArrayList<>();
List<Integer> data = new ArrayList<>();
for (Map<String, Object> competitor : competitors) {
labels.add((String) competitor.getOrDefault("name", "Unknown"));
// Безопасное извлечение digitalMetrics.reach
Object metricsObj = competitor.get("digitalMetrics");
if (metricsObj instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> digitalMetrics = (Map<String, Object>) metricsObj;
Object reachObj = digitalMetrics.get("reach");
if (reachObj instanceof Number) {
data.add(((Number) reachObj).intValue());
} else {
data.add(0);
}
} else {
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("competitors", chart);
// Сохраняем исходные данные конкурентов для таблиц
formattedCharts.put("competitorsRaw", competitors);
logger.debug("transformToChartConfig: Transformed competitors chart with {} entries",
labels.size());
}
}
// 3. Audience Segmentation (Map с ageGroups и genders)
if (rawData.containsKey("audienceSegmentation")) {
@SuppressWarnings("unchecked")
Map<String, Object> segmentation = (Map<String, Object>) rawData.get("audienceSegmentation");
if (segmentation != null) {
// 3.1. Audience Age Chart
if (segmentation.containsKey("ageGroups")) {
@SuppressWarnings("unchecked")
List<Map<String, String>> ageGroups = (List<Map<String, String>>) segmentation.get("ageGroups");
if (ageGroups != null && !ageGroups.isEmpty()) {
List<String> labels = new ArrayList<>();
List<Number> data = new ArrayList<>();
for (Map<String, String> ageGroup : ageGroups) {
labels.add(ageGroup.getOrDefault("label", "Unknown"));
String valueStr = ageGroup.getOrDefault("value", "0");
try {
data.add(Integer.parseInt(valueStr));
} catch (NumberFormatException 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);
logger.debug("transformToChartConfig: Transformed audienceAge chart with {} labels",
labels.size());
}
}
// 3.2. Audience Gender Chart
if (segmentation.containsKey("genders")) {
@SuppressWarnings("unchecked")
List<Map<String, String>> genders = (List<Map<String, String>>) segmentation.get("genders");
if (genders != null && !genders.isEmpty()) {
List<String> labels = new ArrayList<>();
List<Number> data = new ArrayList<>();
for (Map<String, String> gender : genders) {
labels.add(gender.getOrDefault("label", "Unknown"));
String valueStr = gender.getOrDefault("value", "0");
try {
data.add(Integer.parseInt(valueStr));
} catch (NumberFormatException 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);
logger.debug("transformToChartConfig: Transformed audienceGender chart with {} labels",
labels.size());
}
}
// Сохраняем остальные данные из audienceSegmentation как есть
if (segmentation.containsKey("segments")) {
formattedCharts.put("segments", segmentation.get("segments"));
}
if (segmentation.containsKey("segmentsChannelMatrix")) {
formattedCharts.put("segmentsChannelMatrix", segmentation.get("segmentsChannelMatrix"));
}
if (segmentation.containsKey("keyTakeaways")) {
formattedCharts.put("keyTakeaways", segmentation.get("keyTakeaways"));
}
// Сохраняем исходную структуру audienceSegmentation для обратной совместимости
formattedCharts.put("audienceSegmentation", segmentation);
}
}
// 4. Conversion Funnel (List<Map>)
if (rawData.containsKey("conversionFunnel")) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> funnel = (List<Map<String, Object>>) rawData.get("conversionFunnel");
if (funnel != null && !funnel.isEmpty()) {
List<String> labels = new ArrayList<>();
List<Number> data = new ArrayList<>();
for (Map<String, Object> stage : funnel) {
labels.add((String) stage.getOrDefault("stage", "Unknown"));
Object valueObj = stage.get("value");
if (valueObj instanceof Number) {
data.add(((Number) valueObj).intValue());
} else {
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("conversionFunnel", chart);
logger.debug("transformToChartConfig: Transformed conversionFunnel chart with {} stages",
labels.size());
}
}
// 5. Channels Potential (List<Map>)
if (rawData.containsKey("channelsPotential")) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> channels = (List<Map<String, Object>>) rawData.get("channelsPotential");
if (channels != null && !channels.isEmpty()) {
List<String> labels = new ArrayList<>();
List<Number> data = new ArrayList<>();
for (Map<String, Object> channel : channels) {
labels.add((String) channel.getOrDefault("name", "Unknown"));
Object potentialObj = channel.get("potential");
if (potentialObj instanceof Number) {
data.add(((Number) potentialObj).intValue());
} else {
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("channelsPotential", chart);
logger.debug("transformToChartConfig: Transformed channelsPotential chart with {} channels",
labels.size());
}
}
// 6. Данные без преобразования (оставить как есть)
if (rawData.containsKey("swot")) {
formattedCharts.put("swot", rawData.get("swot"));
}
if (rawData.containsKey("comparisonTable")) {
formattedCharts.put("comparisonTable", rawData.get("comparisonTable"));
}
if (rawData.containsKey("marketShareChart")) {
formattedCharts.put("marketShareChart", rawData.get("marketShareChart"));
}
if (rawData.containsKey("swotCompetitors")) {
formattedCharts.put("swotCompetitors", rawData.get("swotCompetitors"));
}
if (rawData.containsKey("market")) {
formattedCharts.put("market", rawData.get("market"));
}
} catch (Exception e) {
logger.error("transformToChartConfig: Error transforming chart data: {}", e.getMessage(), e);
logger.error("transformToChartConfig: Stack trace: ", e);
}
logger.info("transformToChartConfig: Transformation completed. Formatted charts keys: {}",
formattedCharts.keySet());
return formattedCharts;
}
private Map<String, Object> generateMarketingReport(MarketingAnalysisRequest request) {
logger.info("generateMarketingReport: Starting report generation for product: {}", request.getProduct());
Map<String, Object> report = new HashMap<>();
@@ -2152,13 +2410,18 @@ public class MarketingAnalysisService {
Map<String, Object> chartsData = parseChartDataFromJson(analysisJson);
logger.info("generateMarketingReport: Chart data parsed. Keys: {}",
chartsData != null ? chartsData.keySet() : "null");
report.put("chartsData", chartsData);
// Преобразуем данные в формат Chart.js
Map<String, Object> readyToRenderCharts = transformToChartConfig(chartsData);
logger.info("generateMarketingReport: Chart data transformed to Chart.js format. Keys: {}",
readyToRenderCharts != null ? readyToRenderCharts.keySet() : "null");
report.put("chartsData", readyToRenderCharts);
// Заменяем плейсхолдеры графиков на JSON-блоки
String processedTextReport = textReport;
if (textReport != null && chartsData != null && !chartsData.isEmpty()) {
if (textReport != null && readyToRenderCharts != null && !readyToRenderCharts.isEmpty()) {
logger.info("generateMarketingReport: Replacing chart placeholders in text report");
processedTextReport = replaceChartPlaceholders(textReport, chartsData);
processedTextReport = replaceChartPlaceholders(textReport, readyToRenderCharts);
logger.info("generateMarketingReport: Chart placeholders replaced. Length: {} chars",
processedTextReport != null ? processedTextReport.length() : 0);
} else if (textReport != null) {