From f2906d3eae0feabf1dd8f139d34c5c0b5f095b22 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 5 Dec 2025 18:37:30 +0500 Subject: [PATCH] . --- .../service/MarketingStrategyService.java | 115 ++++++++++++------ 1 file changed, 78 insertions(+), 37 deletions(-) diff --git a/src/main/java/kz/konturai/parser/service/MarketingStrategyService.java b/src/main/java/kz/konturai/parser/service/MarketingStrategyService.java index a7eef7d..5a8365c 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingStrategyService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingStrategyService.java @@ -92,7 +92,8 @@ public class MarketingStrategyService { } @Async("reportGenerationExecutor") - public void processStrategyGeneration(String strategyId, String analysisId, MarketingAnalysisResult analysisResult) { + public void processStrategyGeneration(String strategyId, String analysisId, + MarketingAnalysisResult analysisResult) { try { Optional optStrategy = repository.findById(strategyId); if (optStrategy.isEmpty()) { @@ -119,7 +120,8 @@ public class MarketingStrategyService { // Generate post calendar List postCalendar = generatePostCalendar( - context, strategy.getDurationWeeks(), strategy.getPriorityPlatforms(), weeklyPlans, businessContext); + context, strategy.getDurationWeeks(), strategy.getPriorityPlatforms(), weeklyPlans, + businessContext); // Update strategy strategy.setStatus("completed"); @@ -168,7 +170,8 @@ public class MarketingStrategyService { MarketingAnalysisResult.TargetAudience audience = report.getTargetAudience(); context.append("Целевая аудитория: ").append(audience.getDescription()).append("\n"); if (audience.getChannels() != null && !audience.getChannels().isEmpty()) { - context.append("Рекомендуемые каналы: ").append(String.join(", ", audience.getChannels())).append("\n"); + context.append("Рекомендуемые каналы: ").append(String.join(", ", audience.getChannels())) + .append("\n"); } context.append("\n"); } @@ -191,7 +194,8 @@ public class MarketingStrategyService { context.append("Каналы: ").append(String.join(", ", strategy.getChannels())).append("\n"); } if (strategy.getContentTypes() != null && !strategy.getContentTypes().isEmpty()) { - context.append("Типы контента: ").append(String.join(", ", strategy.getContentTypes())).append("\n"); + context.append("Типы контента: ").append(String.join(", ", strategy.getContentTypes())) + .append("\n"); } } } @@ -207,14 +211,19 @@ public class MarketingStrategyService { : "Instagram, Facebook, LinkedIn, Telegram, TikTok, YouTube"; String prompt = String.format( - "На основе следующего маркетингового анализа создай детальный недельный план продвижения на %d недель.\n\n" + + "На основе следующего маркетингового анализа создай детальный недельный план продвижения на %d недель.\n\n" + + "Требования:\n" + "1. Создай план для каждой недели отдельно\n" + "2. Для каждой недели укажи:\n" + " - Основные темы недели (3-5 тем)\n" + " - Рекомендации по контенту (1-2 абзаца)\n" + " - Приоритетные платформы для этой недели\n" + - "3. Платформы для использования: %s\n" + + "3. Платформы для использования (СТРОГО ТОЛЬКО ЭТИ ПЛАТФОРМЫ, НЕ ДОБАВЛЯЙ ДРУГИЕ): %s\n" + + " ВАЖНО: Используй ТОЛЬКО указанные платформы. Если указан Facebook, используй ТОЛЬКО Facebook (НЕ добавляй Instagram).\n" + + + " Если указан Instagram, используй ТОЛЬКО Instagram (НЕ добавляй Facebook).\n" + + " Каждая платформа независима - не предполагай связи между платформами.\n" + "4. Ответ должен быть структурированным и применимым на практике\n" + "5. Ответ должен быть на русском языке\n\n" + "Верни ответ в формате JSON со следующей структурой:\n" + @@ -245,7 +254,8 @@ public class MarketingStrategyService { } // Parse JSON - Map jsonMap = objectMapper.readValue(jsonStr, new TypeReference>() {}); + Map jsonMap = objectMapper.readValue(jsonStr, new TypeReference>() { + }); @SuppressWarnings("unchecked") List> weeklyPlansList = (List>) jsonMap.get("weeklyPlans"); @@ -258,17 +268,24 @@ public class MarketingStrategyService { for (Map planMap : weeklyPlansList) { MarketingStrategy.WeeklyPlan plan = new MarketingStrategy.WeeklyPlan(); plan.setWeekNumber(((Number) planMap.get("weekNumber")).intValue()); - + @SuppressWarnings("unchecked") List themes = (List) planMap.get("mainThemes"); plan.setMainThemes(themes != null ? themes : new ArrayList<>()); - + plan.setContentRecommendations((String) planMap.get("contentRecommendations")); - + @SuppressWarnings("unchecked") List platforms = (List) planMap.get("priorityPlatforms"); + // Filter platforms to only include those from the original request + if (platforms != null && priorityPlatforms != null && !priorityPlatforms.isEmpty()) { + platforms = platforms.stream() + .filter(p -> priorityPlatforms.stream() + .anyMatch(pp -> pp.equalsIgnoreCase(p))) + .collect(Collectors.toList()); + } plan.setPriorityPlatforms(platforms != null ? platforms : new ArrayList<>()); - + plans.add(plan); } @@ -320,13 +337,14 @@ public class MarketingStrategyService { LocalDateTime startDate = LocalDateTime.now().plusDays(1); // Start from tomorrow String prompt = String.format( - "На основе следующего маркетингового анализа и недельного плана создай детальный календарь постов на %d недель.\n\n" + + "На основе следующего маркетингового анализа и недельного плана создай детальный календарь постов на %d недель.\n\n" + + "Требования:\n" + "1. Создай конкретные посты для публикации\n" + "2. Для каждого поста укажи:\n" + " - Дату публикации (в формате YYYY-MM-DD)\n" + " - Время публикации (в формате HH:mm, например 10:00, 14:00, 18:00)\n" + - " - Платформу (одну из: %s)\n" + + " - Платформу (СТРОГО ОДНУ ИЗ: %s)\n" + " - Тип контента (пост, сторис, видео, баннер)\n" + " - Тему поста\n" + " - Полный текст поста (готовый к публикации, 100-300 символов)\n" + @@ -334,7 +352,11 @@ public class MarketingStrategyService { "3. Распредели посты равномерно по неделям\n" + "4. Рекомендуемое количество постов: 3-5 постов в неделю\n" + "5. Чередуй платформы и типы контента\n" + - "6. Ответ должен быть на русском языке\n\n" + + "6. ВАЖНО: Используй ТОЛЬКО указанные платформы. Если указан Facebook, используй ТОЛЬКО Facebook (НЕ добавляй Instagram).\n" + + + " Если указан Instagram, используй ТОЛЬКО Instagram (НЕ добавляй Facebook).\n" + + " Каждая платформа независима - не предполагай связи между платформами.\n" + + "7. Ответ должен быть на русском языке\n\n" + "Верни ответ в формате JSON со следующей структурой:\n" + "{\n" + " \"postCalendar\": [\n" + @@ -352,7 +374,8 @@ public class MarketingStrategyService { "Начальная дата: %s\n\n" + "Недельный план:\n%s\n\n" + "Маркетинговый анализ:\n%s", - durationWeeks, platformsStr, startDate.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), weeklyPlansSummary, context); + durationWeeks, platformsStr, startDate.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), + weeklyPlansSummary, context); String response = openAIAnalyticsService.generateWithInstruction(context, prompt, "ru"); if (response == null || response.trim().isEmpty()) { @@ -368,7 +391,8 @@ public class MarketingStrategyService { } // Parse JSON - Map jsonMap = objectMapper.readValue(jsonStr, new TypeReference>() {}); + Map jsonMap = objectMapper.readValue(jsonStr, new TypeReference>() { + }); @SuppressWarnings("unchecked") List> postCalendarList = (List>) jsonMap.get("postCalendar"); @@ -380,7 +404,7 @@ public class MarketingStrategyService { List calendar = new ArrayList<>(); for (Map postMap : postCalendarList) { MarketingStrategy.PostCalendarItem item = new MarketingStrategy.PostCalendarItem(); - + // Parse date String dateStr = (String) postMap.get("publishDate"); if (dateStr != null) { @@ -394,21 +418,38 @@ public class MarketingStrategyService { } else { item.setPublishDate(startDate.plusDays(calendar.size())); } - - item.setPlatform((String) postMap.get("platform")); + + String platformRaw = (String) postMap.get("platform"); + // Validate platform - only use if it's in the requested platforms + final String platform; + if (platformRaw != null && priorityPlatforms != null && !priorityPlatforms.isEmpty()) { + boolean isValidPlatform = priorityPlatforms.stream() + .anyMatch(pp -> pp.equalsIgnoreCase(platformRaw)); + if (!isValidPlatform) { + // If platform is not in the list, use the first valid platform instead + logger.warn("Platform '{}' not in requested platforms {}, using '{}' instead", + platformRaw, priorityPlatforms, priorityPlatforms.get(0)); + platform = priorityPlatforms.get(0); + } else { + platform = platformRaw; + } + } else { + platform = platformRaw; + } + item.setPlatform(platform); item.setContentType((String) postMap.get("contentType")); item.setTheme((String) postMap.get("theme")); item.setPostText((String) postMap.get("postText")); - + @SuppressWarnings("unchecked") List hashtags = (List) postMap.get("hashtags"); item.setHashtags(hashtags != null ? hashtags : new ArrayList<>()); - + item.setPublishTime((String) postMap.get("publishTime")); - + // Generate image for the post generateAndSaveImageForPost(item, businessContext); - + calendar.add(item); } @@ -455,7 +496,8 @@ public class MarketingStrategyService { MarketingStrategy.WeeklyPlan plan = new MarketingStrategy.WeeklyPlan(); plan.setWeekNumber(i); plan.setMainThemes(Arrays.asList("Презентация продукта", "Преимущества", "Отзывы клиентов")); - plan.setContentRecommendations("Создавайте контент, который демонстрирует ценность продукта для целевой аудитории."); + plan.setContentRecommendations( + "Создавайте контент, который демонстрирует ценность продукта для целевой аудитории."); plan.setPriorityPlatforms(platforms); plans.add(plan); } @@ -467,19 +509,19 @@ public class MarketingStrategyService { try { // Create prompt for image generation String imagePrompt = buildImagePrompt(item, businessContext); - + // Generate image byte[] imageBytes = imageGenerationService.generateImage(imagePrompt); - + if (imageBytes != null && imageBytes.length > 0) { // Save image to MinIO String filename = "post_image_" + System.currentTimeMillis() + "_" + item.hashCode() + ".png"; minIOService.uploadFile(filename, imageBytes, MediaType.IMAGE_PNG.toString()); - + // Save filename and URL in item item.setImageFilename(filename); item.setImageUrl(filename); // In MinIO, filename is also the URL/path - + logger.info("Successfully generated and saved image for post: {}", filename); } else { logger.warn("Failed to generate image for post with theme: {}", item.getTheme()); @@ -493,26 +535,26 @@ public class MarketingStrategyService { private String buildImagePrompt(MarketingStrategy.PostCalendarItem item, String businessContext) { StringBuilder prompt = new StringBuilder(); prompt.append("Создай привлекательное маркетинговое изображение для поста в социальных сетях. "); - + if (businessContext != null && !businessContext.isEmpty()) { prompt.append(businessContext).append(" "); } - + if (item.getTheme() != null && !item.getTheme().isEmpty()) { prompt.append("Тема поста: ").append(item.getTheme()).append(". "); } - + if (item.getPostText() != null && !item.getPostText().isEmpty()) { // Use first 100 characters of post text for context - String textPreview = item.getPostText().length() > 100 - ? item.getPostText().substring(0, 100) + "..." + String textPreview = item.getPostText().length() > 100 + ? item.getPostText().substring(0, 100) + "..." : item.getPostText(); prompt.append("Текст поста: ").append(textPreview).append(". "); } - + prompt.append("Стиль: современный, профессиональный, привлекающий внимание, подходящий для социальных сетей. "); prompt.append("Изображение должно быть ярким, но не перегруженным, с акцентом на главную идею поста."); - + return prompt.toString(); } @@ -560,7 +602,7 @@ public class MarketingStrategyService { if (strategy.getWeeklyPlans() != null && strategy.getPostCalendar() != null && "completed".equals(strategy.getStatus())) { MarketingStrategyResponse.StrategyContent strategyContent = new MarketingStrategyResponse.StrategyContent(); - + // Convert WeeklyPlans List weeklyPlans = new ArrayList<>(); for (MarketingStrategy.WeeklyPlan plan : strategy.getWeeklyPlans()) { @@ -613,4 +655,3 @@ public class MarketingStrategyService { return repository.findById(strategyId); } } -