This commit is contained in:
root
2025-12-05 18:37:30 +05:00
parent cbb57258c2
commit f2906d3eae
@@ -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<MarketingStrategy> optStrategy = repository.findById(strategyId);
if (optStrategy.isEmpty()) {
@@ -119,7 +120,8 @@ public class MarketingStrategyService {
// Generate post calendar
List<MarketingStrategy.PostCalendarItem> 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<String, Object> jsonMap = objectMapper.readValue(jsonStr, new TypeReference<Map<String, Object>>() {});
Map<String, Object> jsonMap = objectMapper.readValue(jsonStr, new TypeReference<Map<String, Object>>() {
});
@SuppressWarnings("unchecked")
List<Map<String, Object>> weeklyPlansList = (List<Map<String, Object>>) jsonMap.get("weeklyPlans");
@@ -258,17 +268,24 @@ public class MarketingStrategyService {
for (Map<String, Object> planMap : weeklyPlansList) {
MarketingStrategy.WeeklyPlan plan = new MarketingStrategy.WeeklyPlan();
plan.setWeekNumber(((Number) planMap.get("weekNumber")).intValue());
@SuppressWarnings("unchecked")
List<String> themes = (List<String>) planMap.get("mainThemes");
plan.setMainThemes(themes != null ? themes : new ArrayList<>());
plan.setContentRecommendations((String) planMap.get("contentRecommendations"));
@SuppressWarnings("unchecked")
List<String> platforms = (List<String>) 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<String, Object> jsonMap = objectMapper.readValue(jsonStr, new TypeReference<Map<String, Object>>() {});
Map<String, Object> jsonMap = objectMapper.readValue(jsonStr, new TypeReference<Map<String, Object>>() {
});
@SuppressWarnings("unchecked")
List<Map<String, Object>> postCalendarList = (List<Map<String, Object>>) jsonMap.get("postCalendar");
@@ -380,7 +404,7 @@ public class MarketingStrategyService {
List<MarketingStrategy.PostCalendarItem> calendar = new ArrayList<>();
for (Map<String, Object> 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<String> hashtags = (List<String>) 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<MarketingStrategyResponse.WeeklyPlan> weeklyPlans = new ArrayList<>();
for (MarketingStrategy.WeeklyPlan plan : strategy.getWeeklyPlans()) {
@@ -613,4 +655,3 @@ public class MarketingStrategyService {
return repository.findById(strategyId);
}
}