.
This commit is contained in:
@@ -83,60 +83,89 @@ public class MarketingAnalysisService {
|
||||
@Async("reportGenerationExecutor")
|
||||
public void processAnalysis(String analysisId, MarketingAnalysisRequest request) {
|
||||
try {
|
||||
logger.info("processAnalysis: Starting for analysisId: {}", analysisId);
|
||||
Optional<MarketingAnalysis> optAnalysis = repository.findById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
logger.error("Marketing analysis not found: {}", analysisId);
|
||||
logger.error("processAnalysis: Marketing analysis not found: {}", analysisId);
|
||||
return;
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
analysis.setStatus("processing");
|
||||
addStatusHistoryEntry(analysis, "processing", "Начата обработка анализа");
|
||||
logger.debug("processAnalysis: Saving analysis with 'processing' status to MongoDB for ID: {}", analysisId);
|
||||
repository.save(analysis);
|
||||
logger.debug("processAnalysis: Analysis status set to 'processing' and saved for ID: {}", analysisId);
|
||||
|
||||
logger.info("Starting marketing analysis processing for ID: {}", analysisId);
|
||||
logger.info("processAnalysis: Starting marketing analysis processing for ID: {}", analysisId);
|
||||
|
||||
// Generate marketing report with all analysis types
|
||||
logger.info("processAnalysis: Starting generateMarketingReport for ID: {}", analysisId);
|
||||
Map<String, Object> reportData = generateMarketingReport(request);
|
||||
logger.info("processAnalysis: generateMarketingReport completed for ID: {}. Report keys: {}",
|
||||
analysisId, reportData != null ? reportData.keySet() : "null");
|
||||
|
||||
// Generate markdown content for PDF
|
||||
logger.info("processAnalysis: Starting buildMarkdownFromReport for ID: {}", analysisId);
|
||||
String markdownContent = buildMarkdownFromReport(reportData, request);
|
||||
logger.info("processAnalysis: buildMarkdownFromReport completed for ID: {}. Markdown length: {} chars",
|
||||
analysisId, markdownContent != null ? markdownContent.length() : 0);
|
||||
// Save markdown content into report data so frontend gets JSON code blocks
|
||||
reportData.put("fullAnalysis", markdownContent);
|
||||
|
||||
// Generate PDF
|
||||
logger.info("processAnalysis: Starting PDF generation for ID: {}", analysisId);
|
||||
byte[] pdfBytes = researchPdfService.generatePdfReport(
|
||||
"Маркетинговый анализ: " + request.getProduct(),
|
||||
markdownContent,
|
||||
new ArrayList<>());
|
||||
logger.info("processAnalysis: PDF generation completed for ID: {}. PDF size: {} bytes",
|
||||
analysisId, pdfBytes != null ? pdfBytes.length : 0);
|
||||
|
||||
// Save PDF to MinIO
|
||||
String filename = "marketing_analysis_" + analysisId + "_" + System.currentTimeMillis() + ".pdf";
|
||||
logger.info("processAnalysis: Starting MinIO upload for ID: {}. Filename: {}", analysisId, filename);
|
||||
minIOService.uploadFile(filename, pdfBytes, MediaType.APPLICATION_PDF.toString());
|
||||
logger.info("processAnalysis: MinIO upload completed for ID: {}", analysisId);
|
||||
|
||||
// Update analysis with results
|
||||
logger.info("processAnalysis: Updating analysis status to 'completed' for ID: {}", analysisId);
|
||||
analysis.setStatus("completed");
|
||||
analysis.setCompletedAt(LocalDateTime.now());
|
||||
logger.debug("processAnalysis: Setting reportData. ReportData keys: {}",
|
||||
reportData != null ? reportData.keySet() : "null");
|
||||
analysis.setReportData(reportData);
|
||||
analysis.setPdfFilename(filename);
|
||||
analysis.setPdfFilePath(filename);
|
||||
addStatusHistoryEntry(analysis, "completed", "Анализ успешно завершен");
|
||||
logger.info("processAnalysis: Saving analysis to MongoDB for ID: {}", analysisId);
|
||||
repository.save(analysis);
|
||||
logger.info("processAnalysis: Analysis saved successfully to MongoDB for ID: {}", analysisId);
|
||||
|
||||
logger.info("Marketing analysis completed successfully for ID: {}", analysisId);
|
||||
logger.info("processAnalysis: Marketing analysis completed successfully for ID: {}", analysisId);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Error processing marketing analysis {}: {}", analysisId, e.getMessage(), e);
|
||||
logger.error("processAnalysis: Error processing marketing analysis {}: {}", analysisId, e.getMessage(), e);
|
||||
logger.error("processAnalysis: Stack trace for analysisId {}: ", analysisId, e);
|
||||
try {
|
||||
logger.info("processAnalysis: Attempting to update analysis status to 'failed' for ID: {}", analysisId);
|
||||
Optional<MarketingAnalysis> optAnalysis = repository.findById(analysisId);
|
||||
if (optAnalysis.isPresent()) {
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
analysis.setStatus("failed");
|
||||
addStatusHistoryEntry(analysis, "failed", "Ошибка при обработке: " + e.getMessage());
|
||||
logger.debug("processAnalysis: Saving analysis with 'failed' status to MongoDB for ID: {}",
|
||||
analysisId);
|
||||
repository.save(analysis);
|
||||
logger.info("processAnalysis: Analysis status updated to 'failed' and saved for ID: {}",
|
||||
analysisId);
|
||||
} else {
|
||||
logger.error("processAnalysis: Cannot update status - analysis not found: {}", analysisId);
|
||||
}
|
||||
} catch (Exception saveError) {
|
||||
logger.error("Failed to update analysis status to failed: {}", saveError.getMessage());
|
||||
logger.error("processAnalysis: Failed to update analysis status to failed for ID {}: {}",
|
||||
analysisId, saveError.getMessage(), saveError);
|
||||
logger.error("processAnalysis: Stack trace for saveError: ", saveError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,22 +429,30 @@ public class MarketingAnalysisService {
|
||||
+ "High = сегмент активно использует канал, Medium = умеренное использование, Low = редкое использование.\n");
|
||||
|
||||
String prompt = promptBuilder.toString();
|
||||
logger.debug("Generating audience analysis with detailed prompt (length: {} chars)", prompt.length());
|
||||
logger.debug("getAudienceAnalysis: Generating audience analysis with detailed prompt (length: {} chars)",
|
||||
prompt.length());
|
||||
logger.info("getAudienceAnalysis: Calling OpenAI API for audience analysis");
|
||||
String jsonResponse = openAIAnalyticsService.generateWithInstruction(context, prompt, "ru");
|
||||
logger.debug("getAudienceAnalysis: OpenAI response received. Length: {} chars",
|
||||
jsonResponse != null ? jsonResponse.length() : 0);
|
||||
|
||||
if (jsonResponse == null || jsonResponse.trim().isEmpty()) {
|
||||
logger.warn("Empty JSON response from AI for audience analysis");
|
||||
logger.warn("getAudienceAnalysis: Empty JSON response from AI for audience analysis");
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
logger.debug("getAudienceAnalysis: Cleaning JSON from markdown");
|
||||
String cleanedJson = cleanJsonFromMarkdown(jsonResponse);
|
||||
logger.debug("getAudienceAnalysis: Parsing JSON response");
|
||||
Map<String, Object> jsonMap = objectMapper.readValue(cleanedJson, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
logger.debug("getAudienceAnalysis: JSON parsed successfully. Keys: {}", jsonMap.keySet());
|
||||
|
||||
logger.info("Audience analysis generated successfully");
|
||||
logger.info("getAudienceAnalysis: Audience analysis generated successfully");
|
||||
return jsonMap;
|
||||
} catch (Exception e) {
|
||||
logger.error("Error generating audience analysis: {}", e.getMessage(), e);
|
||||
logger.error("getAudienceAnalysis: Error generating audience analysis: {}", e.getMessage(), e);
|
||||
logger.error("getAudienceAnalysis: Stack trace: ", e);
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
@@ -524,22 +561,31 @@ public class MarketingAnalysisService {
|
||||
"12. channelsHeatmap должна содержать оценки (0-100) для всех основных каналов: Instagram, TikTok, Telegram, YouTube, Facebook.\n");
|
||||
|
||||
String prompt = promptBuilder.toString();
|
||||
logger.debug("Generating competitor analysis with detailed prompt (length: {} chars)", prompt.length());
|
||||
logger.debug(
|
||||
"getCompetitorAnalysis: Generating competitor analysis with detailed prompt (length: {} chars)",
|
||||
prompt.length());
|
||||
logger.info("getCompetitorAnalysis: Calling OpenAI API for competitor analysis");
|
||||
String jsonResponse = openAIAnalyticsService.generateWithInstruction(context, prompt, "ru");
|
||||
logger.debug("getCompetitorAnalysis: OpenAI response received. Length: {} chars",
|
||||
jsonResponse != null ? jsonResponse.length() : 0);
|
||||
|
||||
if (jsonResponse == null || jsonResponse.trim().isEmpty()) {
|
||||
logger.warn("Empty JSON response from AI for competitor analysis");
|
||||
logger.warn("getCompetitorAnalysis: Empty JSON response from AI for competitor analysis");
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
logger.debug("getCompetitorAnalysis: Cleaning JSON from markdown");
|
||||
String cleanedJson = cleanJsonFromMarkdown(jsonResponse);
|
||||
logger.debug("getCompetitorAnalysis: Parsing JSON response");
|
||||
Map<String, Object> jsonMap = objectMapper.readValue(cleanedJson, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
logger.debug("getCompetitorAnalysis: JSON parsed successfully. Keys: {}", jsonMap.keySet());
|
||||
|
||||
logger.info("Competitor analysis generated successfully");
|
||||
logger.info("getCompetitorAnalysis: Competitor analysis generated successfully");
|
||||
return jsonMap;
|
||||
} catch (Exception e) {
|
||||
logger.error("Error generating competitor analysis: {}", e.getMessage(), e);
|
||||
logger.error("getCompetitorAnalysis: Error generating competitor analysis: {}", e.getMessage(), e);
|
||||
logger.error("getCompetitorAnalysis: Stack trace: ", e);
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
@@ -621,23 +667,32 @@ public class MarketingAnalysisService {
|
||||
"11. НЕ включай поле \"estimated\" ни в seasonality, ни в market.\n");
|
||||
|
||||
String prompt = promptBuilder.toString();
|
||||
logger.debug("Generating seasonality and market analysis with detailed prompt (length: {} chars)",
|
||||
logger.debug(
|
||||
"getSeasonalityAndMarket: Generating seasonality and market analysis with detailed prompt (length: {} chars)",
|
||||
prompt.length());
|
||||
logger.info("getSeasonalityAndMarket: Calling OpenAI API for seasonality and market analysis");
|
||||
String jsonResponse = openAIAnalyticsService.generateWithInstruction(context, prompt, "ru");
|
||||
logger.debug("getSeasonalityAndMarket: OpenAI response received. Length: {} chars",
|
||||
jsonResponse != null ? jsonResponse.length() : 0);
|
||||
|
||||
if (jsonResponse == null || jsonResponse.trim().isEmpty()) {
|
||||
logger.warn("Empty JSON response from AI for seasonality and market analysis");
|
||||
logger.warn("getSeasonalityAndMarket: Empty JSON response from AI for seasonality and market analysis");
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
logger.debug("getSeasonalityAndMarket: Cleaning JSON from markdown");
|
||||
String cleanedJson = cleanJsonFromMarkdown(jsonResponse);
|
||||
logger.debug("getSeasonalityAndMarket: Parsing JSON response");
|
||||
Map<String, Object> jsonMap = objectMapper.readValue(cleanedJson, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
logger.debug("getSeasonalityAndMarket: JSON parsed successfully. Keys: {}", jsonMap.keySet());
|
||||
|
||||
logger.info("Seasonality and market analysis generated successfully");
|
||||
logger.info("getSeasonalityAndMarket: Seasonality and market analysis generated successfully");
|
||||
return jsonMap;
|
||||
} catch (Exception e) {
|
||||
logger.error("Error generating seasonality and market analysis: {}", e.getMessage(), e);
|
||||
logger.error("getSeasonalityAndMarket: Error generating seasonality and market analysis: {}",
|
||||
e.getMessage(), e);
|
||||
logger.error("getSeasonalityAndMarket: Stack trace: ", e);
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
@@ -646,14 +701,18 @@ public class MarketingAnalysisService {
|
||||
* Промпт №2: Генерация текстового отчёта на основе JSON
|
||||
*/
|
||||
private String generateTextReportFromJson(Map<String, Object> analysisJson) {
|
||||
logger.info("generateTextReportFromJson: Starting text report generation from JSON");
|
||||
if (analysisJson == null || analysisJson.isEmpty()) {
|
||||
logger.warn("Empty analysisJson provided for text report generation");
|
||||
logger.warn("generateTextReportFromJson: Empty analysisJson provided for text report generation");
|
||||
return "Не удалось сгенерировать текстовый отчёт: отсутствуют данные JSON-анализа.";
|
||||
}
|
||||
logger.debug("generateTextReportFromJson: AnalysisJson keys: {}", analysisJson.keySet());
|
||||
|
||||
try {
|
||||
// Преобразование JSON в строку для промпта
|
||||
logger.debug("generateTextReportFromJson: Converting JSON to string");
|
||||
String jsonString = objectMapper.writeValueAsString(analysisJson);
|
||||
logger.debug("generateTextReportFromJson: JSON string length: {} chars", jsonString.length());
|
||||
|
||||
// Построение промпта №2
|
||||
StringBuilder promptBuilder = new StringBuilder();
|
||||
@@ -702,10 +761,14 @@ public class MarketingAnalysisService {
|
||||
String prompt = promptBuilder.toString();
|
||||
String context = jsonString; // Используем JSON как контекст
|
||||
|
||||
logger.info("generateTextReportFromJson: Calling OpenAI API for text report generation");
|
||||
String textReport = openAIAnalyticsService.generateWithInstruction(context, prompt, "ru");
|
||||
logger.info("generateTextReportFromJson: Text report generated. Length: {} chars",
|
||||
textReport != null ? textReport.length() : 0);
|
||||
return textReport != null ? textReport : "Не удалось сгенерировать текстовый отчёт.";
|
||||
} catch (Exception e) {
|
||||
logger.error("Error generating text report from JSON: {}", e.getMessage(), e);
|
||||
logger.error("generateTextReportFromJson: Error generating text report from JSON: {}", e.getMessage(), e);
|
||||
logger.error("generateTextReportFromJson: Stack trace: ", e);
|
||||
return "Ошибка при генерации текстового отчёта: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
@@ -715,10 +778,12 @@ public class MarketingAnalysisService {
|
||||
* AI-суммирования)
|
||||
*/
|
||||
private String generateDeepTextReport(Map<String, Object> analysisJson) {
|
||||
logger.info("generateDeepTextReport: Starting deep text report generation");
|
||||
if (analysisJson == null || analysisJson.isEmpty()) {
|
||||
logger.warn("Empty analysisJson provided for deep text report generation");
|
||||
logger.warn("generateDeepTextReport: Empty analysisJson provided for deep text report generation");
|
||||
return "Не удалось сгенерировать текстовый отчёт: отсутствуют данные JSON-анализа.";
|
||||
}
|
||||
logger.debug("generateDeepTextReport: AnalysisJson keys: {}", analysisJson.keySet());
|
||||
|
||||
try {
|
||||
StringBuilder fullReport = new StringBuilder();
|
||||
@@ -1427,11 +1492,14 @@ public class MarketingAnalysisService {
|
||||
* competitorAnalysis, seasonality, market
|
||||
*/
|
||||
private Map<String, Object> parseChartDataFromJson(Map<String, Object> analysisJson) {
|
||||
logger.info("parseChartDataFromJson: Starting chart data parsing");
|
||||
Map<String, Object> chartsData = new HashMap<>();
|
||||
|
||||
if (analysisJson == null || analysisJson.isEmpty()) {
|
||||
logger.warn("parseChartDataFromJson: analysisJson is null or empty");
|
||||
return chartsData;
|
||||
}
|
||||
logger.debug("parseChartDataFromJson: AnalysisJson keys: {}", analysisJson.keySet());
|
||||
|
||||
try {
|
||||
// 1. Сезонность (структура не изменилась)
|
||||
@@ -1640,22 +1708,27 @@ public class MarketingAnalysisService {
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Error parsing chart data from JSON: {}", e.getMessage(), e);
|
||||
logger.error("parseChartDataFromJson: Error parsing chart data from JSON: {}", e.getMessage(), e);
|
||||
logger.error("parseChartDataFromJson: Stack trace: ", e);
|
||||
}
|
||||
|
||||
logger.info("parseChartDataFromJson: Chart data parsing completed. Extracted keys: {}", chartsData.keySet());
|
||||
return chartsData;
|
||||
}
|
||||
|
||||
private Map<String, Object> generateMarketingReport(MarketingAnalysisRequest request) {
|
||||
logger.info("generateMarketingReport: Starting report generation for product: {}", request.getProduct());
|
||||
Map<String, Object> report = new HashMap<>();
|
||||
|
||||
// Get all analysis types and detail level
|
||||
List<AnalysisType> analysisTypes = request.getAnalysisTypeEnums();
|
||||
if (analysisTypes == null || analysisTypes.isEmpty()) {
|
||||
logger.warn("generateMarketingReport: No analysis types provided, using default: РЫНОК");
|
||||
analysisTypes = List.of(AnalysisType.РЫНОК); // Default
|
||||
}
|
||||
DetailLevel detailLevel = request.getDetailLevelEnum();
|
||||
String detailLevelName = detailLevel != null ? detailLevel.getDisplayName() : "Стандартно";
|
||||
logger.debug("generateMarketingReport: Analysis types: {}, Detail level: {}", analysisTypes, detailLevelName);
|
||||
|
||||
// Build list of analysis type names for display
|
||||
List<String> analysisTypeNames = analysisTypes.stream()
|
||||
@@ -1712,25 +1785,35 @@ public class MarketingAnalysisService {
|
||||
String context = contextBuilder.toString();
|
||||
|
||||
// Шаг 1: Генерация JSON-анализа (Промпт №1)
|
||||
logger.info("generateMarketingReport: Starting JSON analysis generation");
|
||||
Map<String, Object> analysisJson = generateJsonAnalysis(request);
|
||||
|
||||
if (analysisJson == null || analysisJson.isEmpty()) {
|
||||
logger.error("Failed to generate JSON analysis");
|
||||
logger.error("generateMarketingReport: Failed to generate JSON analysis");
|
||||
// При ошибке возвращаем базовую структуру с сообщением об ошибке
|
||||
report.put("analysisTypes", analysisTypeNames);
|
||||
report.put("detailLevel", detailLevelName);
|
||||
report.put("fullAnalysis", "Не удалось сгенерировать анализ. Пожалуйста, попробуйте позже.");
|
||||
report.put("summary", "Не удалось сгенерировать анализ. Пожалуйста, попробуйте позже.");
|
||||
report.put("chartsData", new HashMap<>());
|
||||
logger.warn("generateMarketingReport: Returning report with error message");
|
||||
} else {
|
||||
logger.info("generateMarketingReport: JSON analysis generated successfully. Keys: {}",
|
||||
analysisJson.keySet());
|
||||
// Сохраняем JSON в отчёт
|
||||
report.put("analysisJson", analysisJson);
|
||||
|
||||
// Шаг 2: Глубокая генерация текстового отчёта на основе JSON (4 этапа)
|
||||
logger.info("generateMarketingReport: Starting deep text report generation");
|
||||
String textReport = generateDeepTextReport(analysisJson);
|
||||
logger.info("generateMarketingReport: Deep text report generated. Length: {} chars",
|
||||
textReport != null ? textReport.length() : 0);
|
||||
|
||||
// Извлекаем данные для графиков из JSON
|
||||
logger.info("generateMarketingReport: Starting chart data parsing from JSON");
|
||||
Map<String, Object> chartsData = parseChartDataFromJson(analysisJson);
|
||||
logger.info("generateMarketingReport: Chart data parsed. Keys: {}",
|
||||
chartsData != null ? chartsData.keySet() : "null");
|
||||
report.put("chartsData", chartsData);
|
||||
|
||||
// Сохраняем данные анализа
|
||||
@@ -1743,6 +1826,7 @@ public class MarketingAnalysisService {
|
||||
}
|
||||
|
||||
// Generate target audience description (adjusted by detail level)
|
||||
logger.info("generateMarketingReport: Starting target audience description generation");
|
||||
String audiencePrompt;
|
||||
if (detailLevel == DetailLevel.КРАТКО) {
|
||||
audiencePrompt = "На основе информации о бизнесе опиши целевую аудиторию кратко (1 абзац). " +
|
||||
@@ -1759,14 +1843,21 @@ public class MarketingAnalysisService {
|
||||
"Укажи, какие каналы коммуникации наиболее подходят для этой аудитории. " +
|
||||
"Ответ должен быть на русском языке.\n\n" + context;
|
||||
}
|
||||
logger.debug("generateMarketingReport: Calling OpenAI for audience description");
|
||||
String audienceDescription = openAIAnalyticsService.generateWithInstruction(context, audiencePrompt, "ru");
|
||||
logger.info("generateMarketingReport: Audience description generated. Length: {} chars",
|
||||
audienceDescription != null ? audienceDescription.length() : 0);
|
||||
|
||||
// Extract channels from description or generate separately
|
||||
logger.info("generateMarketingReport: Starting channels generation");
|
||||
String channelsPrompt = "На основе описания бизнеса перечисли 3-5 наиболее подходящих маркетинговых каналов. " +
|
||||
"Ответ должен быть простым списком через запятую, без нумерации. Пример: Instagram, LinkedIn, Telegram\n\n"
|
||||
+ context;
|
||||
logger.debug("generateMarketingReport: Calling OpenAI for channels");
|
||||
String channelsStr = openAIAnalyticsService.generateWithInstruction(context, channelsPrompt, "ru");
|
||||
logger.debug("generateMarketingReport: Channels response received: {}", channelsStr);
|
||||
List<String> channels = parseChannels(channelsStr);
|
||||
logger.info("generateMarketingReport: Channels parsed. Count: {}", channels.size());
|
||||
|
||||
Map<String, Object> targetAudience = new HashMap<>();
|
||||
targetAudience.put("description", audienceDescription != null ? audienceDescription
|
||||
@@ -1776,6 +1867,7 @@ public class MarketingAnalysisService {
|
||||
report.put("targetAudience", targetAudience);
|
||||
|
||||
// Generate recommendations (adjusted by detail level)
|
||||
logger.info("generateMarketingReport: Starting recommendations generation");
|
||||
String recommendationsPrompt;
|
||||
if (detailLevel == DetailLevel.КРАТКО) {
|
||||
recommendationsPrompt = "На основе информации о бизнесе сформулируй 3-4 краткие практические рекомендации для маркетинговой стратегии. "
|
||||
@@ -1793,13 +1885,18 @@ public class MarketingAnalysisService {
|
||||
"Каждая рекомендация должна быть конкретной и применимой. " +
|
||||
"Ответ должен быть списком рекомендаций, каждая с новой строки, без нумерации.\n\n" + context;
|
||||
}
|
||||
logger.debug("generateMarketingReport: Calling OpenAI for recommendations");
|
||||
String recommendationsStr = openAIAnalyticsService.generateWithInstruction(context, recommendationsPrompt,
|
||||
"ru");
|
||||
logger.debug("generateMarketingReport: Recommendations response received. Length: {} chars",
|
||||
recommendationsStr != null ? recommendationsStr.length() : 0);
|
||||
List<String> recommendations = parseRecommendations(recommendationsStr);
|
||||
logger.info("generateMarketingReport: Recommendations parsed. Count: {}", recommendations.size());
|
||||
report.put("recommendations",
|
||||
recommendations.isEmpty() ? Arrays.asList("Рекомендации не удалось сгенерировать.") : recommendations);
|
||||
|
||||
// Generate strategy (adjusted by detail level)
|
||||
logger.info("generateMarketingReport: Starting strategy generation");
|
||||
String strategyPrompt;
|
||||
if (detailLevel == DetailLevel.КРАТКО) {
|
||||
strategyPrompt = "На основе информации о бизнесе создай краткую маркетинговую стратегию. " +
|
||||
@@ -1819,10 +1916,14 @@ public class MarketingAnalysisService {
|
||||
"3-5 каналов коммуникации и типы контента (например, 'посты', 'сторис', 'баннеры'). " +
|
||||
"Ответ должен быть структурированным текстом на русском языке.\n\n" + context;
|
||||
}
|
||||
logger.debug("generateMarketingReport: Calling OpenAI for strategy");
|
||||
String strategyText = openAIAnalyticsService.generateWithInstruction(context, strategyPrompt, "ru");
|
||||
logger.info("generateMarketingReport: Strategy generated. Length: {} chars",
|
||||
strategyText != null ? strategyText.length() : 0);
|
||||
|
||||
Map<String, Object> strategy = parseStrategy(strategyText, channels);
|
||||
report.put("strategy", strategy);
|
||||
logger.info("generateMarketingReport: Report generation completed. Report keys: {}", report.keySet());
|
||||
|
||||
return report;
|
||||
}
|
||||
@@ -1975,6 +2076,8 @@ public class MarketingAnalysisService {
|
||||
}
|
||||
|
||||
private String buildMarkdownFromReport(Map<String, Object> reportData, MarketingAnalysisRequest request) {
|
||||
logger.info("buildMarkdownFromReport: Starting markdown generation for product: {}", request.getProduct());
|
||||
logger.debug("buildMarkdownFromReport: ReportData keys: {}", reportData != null ? reportData.keySet() : "null");
|
||||
StringBuilder markdown = new StringBuilder();
|
||||
|
||||
markdown.append("# Маркетинговый анализ\n\n");
|
||||
@@ -2248,11 +2351,18 @@ public class MarketingAnalysisService {
|
||||
* @return текст отчета с замененными плейсхолдерами на JSON-блоки
|
||||
*/
|
||||
private String replaceChartPlaceholders(String fullAnalysis, Map<String, Object> chartsData) {
|
||||
logger.info("replaceChartPlaceholders: Starting placeholder replacement");
|
||||
logger.debug("replaceChartPlaceholders: FullAnalysis length: {}, ChartsData keys: {}",
|
||||
fullAnalysis != null ? fullAnalysis.length() : 0,
|
||||
chartsData != null ? chartsData.keySet() : "null");
|
||||
|
||||
if (fullAnalysis == null || fullAnalysis.trim().isEmpty()) {
|
||||
logger.warn("replaceChartPlaceholders: fullAnalysis is null or empty");
|
||||
return fullAnalysis;
|
||||
}
|
||||
|
||||
if (chartsData == null || chartsData.isEmpty()) {
|
||||
logger.warn("replaceChartPlaceholders: chartsData is null or empty, removing all placeholders");
|
||||
// Если данных нет, просто удаляем все плейсхолдеры
|
||||
return fullAnalysis
|
||||
.replace("[[CHART_SEASONALITY]]", "")
|
||||
@@ -2303,8 +2413,12 @@ public class MarketingAnalysisService {
|
||||
|
||||
// Fallback: добавляем графики в конец соответствующих разделов, если
|
||||
// плейсхолдер отсутствовал
|
||||
logger.debug("replaceChartPlaceholders: Adding missing charts to sections");
|
||||
result = addMissingChartsToSections(result, chartsData, placeholderMap, processedPlaceholders);
|
||||
|
||||
logger.info(
|
||||
"replaceChartPlaceholders: Placeholder replacement completed. Processed placeholders: {}, Result length: {} chars",
|
||||
processedPlaceholders.size(), result.length());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user