From 7ade14c2eeb4d7c75bc2a98a2003050b1ec7a05c Mon Sep 17 00:00:00 2001 From: root Date: Mon, 15 Dec 2025 20:26:58 +0500 Subject: [PATCH] . --- .../service/MarketingAnalysisService.java | 269 +++++++++++++++++- 1 file changed, 266 insertions(+), 3 deletions(-) diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index d3638cc..28a0351 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -2054,6 +2054,264 @@ public class MarketingAnalysisService { return chartsData; } + /** + * Преобразует сырые данные графиков в формат Chart.js (labels, datasets). + * + * @param rawData сырые данные из parseChartDataFromJson + * @return Map с данными в формате Chart.js для графиков и исходными данными для + * таблиц + */ + private Map transformToChartConfig(Map rawData) { + logger.info("transformToChartConfig: Starting transformation to Chart.js format"); + Map formattedCharts = new HashMap<>(); + + if (rawData == null || rawData.isEmpty()) { + logger.warn("transformToChartConfig: rawData is null or empty"); + return formattedCharts; + } + + try { + // 1. Seasonality (Map) + if (rawData.containsKey("seasonality")) { + @SuppressWarnings("unchecked") + Map seasonality = (Map) rawData.get("seasonality"); + if (seasonality != null && !seasonality.isEmpty()) { + List labels = new ArrayList<>(seasonality.keySet()); + List data = new ArrayList<>(seasonality.values()); + + Map dataset = new HashMap<>(); + dataset.put("label", "Индекс спроса"); + dataset.put("data", data); + + Map 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) + if (rawData.containsKey("competitors")) { + @SuppressWarnings("unchecked") + List> competitors = (List>) rawData.get("competitors"); + if (competitors != null && !competitors.isEmpty()) { + List labels = new ArrayList<>(); + List data = new ArrayList<>(); + + for (Map competitor : competitors) { + labels.add((String) competitor.getOrDefault("name", "Unknown")); + + // Безопасное извлечение digitalMetrics.reach + Object metricsObj = competitor.get("digitalMetrics"); + if (metricsObj instanceof Map) { + @SuppressWarnings("unchecked") + Map digitalMetrics = (Map) metricsObj; + Object reachObj = digitalMetrics.get("reach"); + if (reachObj instanceof Number) { + data.add(((Number) reachObj).intValue()); + } else { + data.add(0); + } + } else { + data.add(0); + } + } + + Map dataset = new HashMap<>(); + dataset.put("label", "Охват аудитории"); + dataset.put("data", data); + + Map 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 segmentation = (Map) rawData.get("audienceSegmentation"); + if (segmentation != null) { + // 3.1. Audience Age Chart + if (segmentation.containsKey("ageGroups")) { + @SuppressWarnings("unchecked") + List> ageGroups = (List>) segmentation.get("ageGroups"); + if (ageGroups != null && !ageGroups.isEmpty()) { + List labels = new ArrayList<>(); + List data = new ArrayList<>(); + + for (Map 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 dataset = new HashMap<>(); + dataset.put("label", "Возрастные группы"); + dataset.put("data", data); + + Map 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> genders = (List>) segmentation.get("genders"); + if (genders != null && !genders.isEmpty()) { + List labels = new ArrayList<>(); + List data = new ArrayList<>(); + + for (Map 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 dataset = new HashMap<>(); + dataset.put("label", "Гендерное распределение"); + dataset.put("data", data); + + Map 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) + if (rawData.containsKey("conversionFunnel")) { + @SuppressWarnings("unchecked") + List> funnel = (List>) rawData.get("conversionFunnel"); + if (funnel != null && !funnel.isEmpty()) { + List labels = new ArrayList<>(); + List data = new ArrayList<>(); + + for (Map 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 dataset = new HashMap<>(); + dataset.put("label", "Воронка"); + dataset.put("data", data); + + Map 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) + if (rawData.containsKey("channelsPotential")) { + @SuppressWarnings("unchecked") + List> channels = (List>) rawData.get("channelsPotential"); + if (channels != null && !channels.isEmpty()) { + List labels = new ArrayList<>(); + List data = new ArrayList<>(); + + for (Map 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 dataset = new HashMap<>(); + dataset.put("label", "Потенциал"); + dataset.put("data", data); + + Map 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 generateMarketingReport(MarketingAnalysisRequest request) { logger.info("generateMarketingReport: Starting report generation for product: {}", request.getProduct()); Map report = new HashMap<>(); @@ -2152,13 +2410,18 @@ public class MarketingAnalysisService { Map chartsData = parseChartDataFromJson(analysisJson); logger.info("generateMarketingReport: Chart data parsed. Keys: {}", chartsData != null ? chartsData.keySet() : "null"); - report.put("chartsData", chartsData); + + // Преобразуем данные в формат Chart.js + Map 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) {