This commit is contained in:
root
2025-11-26 00:22:54 +05:00
parent b56dc44530
commit 13cd6819f5
7 changed files with 2087 additions and 1 deletions
@@ -2,7 +2,9 @@ package kz.konturai.parser.controller;
import kz.konturai.parser.dto.*;
import kz.konturai.parser.model.MarketingAnalysis;
import kz.konturai.parser.model.MarketingStrategy;
import kz.konturai.parser.service.MarketingAnalysisService;
import kz.konturai.parser.service.MarketingStrategyService;
import kz.konturai.parser.service.MinIOService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -22,10 +24,15 @@ import java.util.Optional;
public class MarketingController {
private final MarketingAnalysisService marketingAnalysisService;
private final MarketingStrategyService marketingStrategyService;
private final MinIOService minIOService;
public MarketingController(MarketingAnalysisService marketingAnalysisService, MinIOService minIOService) {
public MarketingController(
MarketingAnalysisService marketingAnalysisService,
MarketingStrategyService marketingStrategyService,
MinIOService minIOService) {
this.marketingAnalysisService = marketingAnalysisService;
this.marketingStrategyService = marketingStrategyService;
this.minIOService = minIOService;
}
@@ -97,6 +104,76 @@ public class MarketingController {
}
}
@PostMapping("/strategy/generate")
public ResponseEntity<?> generateStrategy(
@RequestParam String analysisId,
@Valid @RequestBody(required = false) MarketingStrategyRequest request) {
if (request == null) {
request = new MarketingStrategyRequest();
}
try {
MarketingStrategy strategy = marketingStrategyService.generateStrategy(analysisId, request);
MarketingStrategyResponse response = new MarketingStrategyResponse();
response.setStrategyId(strategy.getId());
response.setAnalysisId(strategy.getAnalysisId());
response.setStatus(strategy.getStatus());
response.setCreatedAt(strategy.getCreatedAt());
response.setDurationWeeks(strategy.getDurationWeeks());
response.setPriorityPlatforms(strategy.getPriorityPlatforms());
return ResponseEntity.ok(ApiResponse.success(
"Генерация стратегии запущена успешно. Результаты будут готовы в течение 3-5 минут.",
response));
} catch (IllegalArgumentException e) {
ErrorResponse error = new ErrorResponse("INVALID_ANALYSIS", e.getMessage());
return ResponseEntity.status(404)
.body(ApiResponse.error("Анализ не найден", error));
} catch (IllegalStateException e) {
ErrorResponse error = new ErrorResponse("ANALYSIS_NOT_COMPLETED", e.getMessage());
return ResponseEntity.status(400)
.body(ApiResponse.error("Анализ еще не завершен", error));
} catch (Exception e) {
ErrorResponse error = new ErrorResponse(
"INTERNAL_SERVER_ERROR",
"Произошла ошибка при запуске генерации стратегии");
return ResponseEntity.status(500)
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
}
}
@GetMapping("/strategy/{strategyId}")
public ResponseEntity<?> getStrategy(@PathVariable String strategyId) {
MarketingStrategyResponse result = marketingStrategyService.getStrategyResult(strategyId);
if (result == null) {
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
"Стратегия с указанным ID не найдена");
return ResponseEntity.status(404)
.body(ApiResponse.error("Стратегия не найдена", error));
}
return ResponseEntity.ok(ApiResponse.success(result));
}
@GetMapping("/{analysisId}/strategy")
public ResponseEntity<?> getStrategyByAnalysis(@PathVariable String analysisId) {
MarketingStrategyResponse result = marketingStrategyService.getStrategyByAnalysisId(analysisId);
if (result == null) {
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
"Стратегия для указанного анализа не найдена");
return ResponseEntity.status(404)
.body(ApiResponse.error("Стратегия не найдена", error));
}
return ResponseEntity.ok(ApiResponse.success(result));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<ErrorResponse>> handleValidationException(
MethodArgumentNotValidException ex) {
@@ -0,0 +1,39 @@
package kz.konturai.parser.dto;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Max;
import java.util.List;
public class MarketingStrategyRequest {
@Min(value = 1, message = "Длительность стратегии должна быть не менее 1 недели")
@Max(value = 12, message = "Длительность стратегии должна быть не более 12 недель")
private Integer durationWeeks;
private List<String> priorityPlatforms;
public MarketingStrategyRequest() {
}
public MarketingStrategyRequest(Integer durationWeeks, List<String> priorityPlatforms) {
this.durationWeeks = durationWeeks;
this.priorityPlatforms = priorityPlatforms;
}
public Integer getDurationWeeks() {
return durationWeeks;
}
public void setDurationWeeks(Integer durationWeeks) {
this.durationWeeks = durationWeeks;
}
public List<String> getPriorityPlatforms() {
return priorityPlatforms;
}
public void setPriorityPlatforms(List<String> priorityPlatforms) {
this.priorityPlatforms = priorityPlatforms;
}
}
@@ -0,0 +1,251 @@
package kz.konturai.parser.dto;
import java.time.LocalDateTime;
import java.util.List;
public class MarketingStrategyResponse {
private String strategyId;
private String analysisId;
private String status;
private LocalDateTime createdAt;
private LocalDateTime completedAt;
private Integer durationWeeks;
private List<String> priorityPlatforms;
private StrategyContent strategy;
public MarketingStrategyResponse() {
}
public MarketingStrategyResponse(String strategyId, String analysisId, String status, LocalDateTime createdAt, LocalDateTime completedAt, Integer durationWeeks, List<String> priorityPlatforms, StrategyContent strategy) {
this.strategyId = strategyId;
this.analysisId = analysisId;
this.status = status;
this.createdAt = createdAt;
this.completedAt = completedAt;
this.durationWeeks = durationWeeks;
this.priorityPlatforms = priorityPlatforms;
this.strategy = strategy;
}
public String getStrategyId() {
return strategyId;
}
public void setStrategyId(String strategyId) {
this.strategyId = strategyId;
}
public String getAnalysisId() {
return analysisId;
}
public void setAnalysisId(String analysisId) {
this.analysisId = analysisId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getCompletedAt() {
return completedAt;
}
public void setCompletedAt(LocalDateTime completedAt) {
this.completedAt = completedAt;
}
public Integer getDurationWeeks() {
return durationWeeks;
}
public void setDurationWeeks(Integer durationWeeks) {
this.durationWeeks = durationWeeks;
}
public List<String> getPriorityPlatforms() {
return priorityPlatforms;
}
public void setPriorityPlatforms(List<String> priorityPlatforms) {
this.priorityPlatforms = priorityPlatforms;
}
public StrategyContent getStrategy() {
return strategy;
}
public void setStrategy(StrategyContent strategy) {
this.strategy = strategy;
}
public static class StrategyContent {
private List<WeeklyPlan> weeklyPlans;
private List<PostCalendarItem> postCalendar;
public StrategyContent() {
}
public StrategyContent(List<WeeklyPlan> weeklyPlans, List<PostCalendarItem> postCalendar) {
this.weeklyPlans = weeklyPlans;
this.postCalendar = postCalendar;
}
public List<WeeklyPlan> getWeeklyPlans() {
return weeklyPlans;
}
public void setWeeklyPlans(List<WeeklyPlan> weeklyPlans) {
this.weeklyPlans = weeklyPlans;
}
public List<PostCalendarItem> getPostCalendar() {
return postCalendar;
}
public void setPostCalendar(List<PostCalendarItem> postCalendar) {
this.postCalendar = postCalendar;
}
}
public static class WeeklyPlan {
private Integer weekNumber;
private List<String> mainThemes;
private String contentRecommendations;
private List<String> priorityPlatforms;
public WeeklyPlan() {
}
public WeeklyPlan(Integer weekNumber, List<String> mainThemes, String contentRecommendations, List<String> priorityPlatforms) {
this.weekNumber = weekNumber;
this.mainThemes = mainThemes;
this.contentRecommendations = contentRecommendations;
this.priorityPlatforms = priorityPlatforms;
}
public Integer getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(Integer weekNumber) {
this.weekNumber = weekNumber;
}
public List<String> getMainThemes() {
return mainThemes;
}
public void setMainThemes(List<String> mainThemes) {
this.mainThemes = mainThemes;
}
public String getContentRecommendations() {
return contentRecommendations;
}
public void setContentRecommendations(String contentRecommendations) {
this.contentRecommendations = contentRecommendations;
}
public List<String> getPriorityPlatforms() {
return priorityPlatforms;
}
public void setPriorityPlatforms(List<String> priorityPlatforms) {
this.priorityPlatforms = priorityPlatforms;
}
}
public static class PostCalendarItem {
private LocalDateTime publishDate;
private String platform;
private String contentType;
private String theme;
private String postText;
private List<String> hashtags;
private String publishTime;
public PostCalendarItem() {
}
public PostCalendarItem(LocalDateTime publishDate, String platform, String contentType, String theme, String postText, List<String> hashtags, String publishTime) {
this.publishDate = publishDate;
this.platform = platform;
this.contentType = contentType;
this.theme = theme;
this.postText = postText;
this.hashtags = hashtags;
this.publishTime = publishTime;
}
public LocalDateTime getPublishDate() {
return publishDate;
}
public void setPublishDate(LocalDateTime publishDate) {
this.publishDate = publishDate;
}
public String getPlatform() {
return platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getTheme() {
return theme;
}
public void setTheme(String theme) {
this.theme = theme;
}
public String getPostText() {
return postText;
}
public void setPostText(String postText) {
this.postText = postText;
}
public List<String> getHashtags() {
return hashtags;
}
public void setHashtags(List<String> hashtags) {
this.hashtags = hashtags;
}
public String getPublishTime() {
return publishTime;
}
public void setPublishTime(String publishTime) {
this.publishTime = publishTime;
}
}
}
@@ -0,0 +1,282 @@
package kz.konturai.parser.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Document(collection = "marketing_strategy")
public class MarketingStrategy {
@Id
private String id;
@Field("analysis_id")
private String analysisId;
@Field("status")
private String status; // queued, processing, completed, failed
@Field("duration_weeks")
private Integer durationWeeks;
@Field("priority_platforms")
private List<String> priorityPlatforms;
@Field("weekly_plans")
private List<WeeklyPlan> weeklyPlans;
@Field("post_calendar")
private List<PostCalendarItem> postCalendar;
@Field("created_at")
private LocalDateTime createdAt;
@Field("completed_at")
private LocalDateTime completedAt;
@Field("strategy_data")
private Map<String, Object> strategyData; // JSON data with full strategy content
public MarketingStrategy() {
this.createdAt = LocalDateTime.now();
this.status = "queued";
}
public MarketingStrategy(String analysisId) {
this();
this.analysisId = analysisId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAnalysisId() {
return analysisId;
}
public void setAnalysisId(String analysisId) {
this.analysisId = analysisId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Integer getDurationWeeks() {
return durationWeeks;
}
public void setDurationWeeks(Integer durationWeeks) {
this.durationWeeks = durationWeeks;
}
public List<String> getPriorityPlatforms() {
return priorityPlatforms;
}
public void setPriorityPlatforms(List<String> priorityPlatforms) {
this.priorityPlatforms = priorityPlatforms;
}
public List<WeeklyPlan> getWeeklyPlans() {
return weeklyPlans;
}
public void setWeeklyPlans(List<WeeklyPlan> weeklyPlans) {
this.weeklyPlans = weeklyPlans;
}
public List<PostCalendarItem> getPostCalendar() {
return postCalendar;
}
public void setPostCalendar(List<PostCalendarItem> postCalendar) {
this.postCalendar = postCalendar;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getCompletedAt() {
return completedAt;
}
public void setCompletedAt(LocalDateTime completedAt) {
this.completedAt = completedAt;
}
public Map<String, Object> getStrategyData() {
return strategyData;
}
public void setStrategyData(Map<String, Object> strategyData) {
this.strategyData = strategyData;
}
public static class WeeklyPlan {
@Field("week_number")
private Integer weekNumber;
@Field("main_themes")
private List<String> mainThemes;
@Field("content_recommendations")
private String contentRecommendations;
@Field("priority_platforms")
private List<String> priorityPlatforms;
public WeeklyPlan() {
}
public WeeklyPlan(Integer weekNumber, List<String> mainThemes, String contentRecommendations, List<String> priorityPlatforms) {
this.weekNumber = weekNumber;
this.mainThemes = mainThemes;
this.contentRecommendations = contentRecommendations;
this.priorityPlatforms = priorityPlatforms;
}
public Integer getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(Integer weekNumber) {
this.weekNumber = weekNumber;
}
public List<String> getMainThemes() {
return mainThemes;
}
public void setMainThemes(List<String> mainThemes) {
this.mainThemes = mainThemes;
}
public String getContentRecommendations() {
return contentRecommendations;
}
public void setContentRecommendations(String contentRecommendations) {
this.contentRecommendations = contentRecommendations;
}
public List<String> getPriorityPlatforms() {
return priorityPlatforms;
}
public void setPriorityPlatforms(List<String> priorityPlatforms) {
this.priorityPlatforms = priorityPlatforms;
}
}
public static class PostCalendarItem {
@Field("publish_date")
private LocalDateTime publishDate;
@Field("platform")
private String platform;
@Field("content_type")
private String contentType; // пост, сторис, видео, баннер
@Field("theme")
private String theme;
@Field("post_text")
private String postText;
@Field("hashtags")
private List<String> hashtags;
@Field("publish_time")
private String publishTime; // время публикации в формате HH:mm
public PostCalendarItem() {
}
public PostCalendarItem(LocalDateTime publishDate, String platform, String contentType, String theme, String postText, List<String> hashtags, String publishTime) {
this.publishDate = publishDate;
this.platform = platform;
this.contentType = contentType;
this.theme = theme;
this.postText = postText;
this.hashtags = hashtags;
this.publishTime = publishTime;
}
public LocalDateTime getPublishDate() {
return publishDate;
}
public void setPublishDate(LocalDateTime publishDate) {
this.publishDate = publishDate;
}
public String getPlatform() {
return platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getTheme() {
return theme;
}
public void setTheme(String theme) {
this.theme = theme;
}
public String getPostText() {
return postText;
}
public void setPostText(String postText) {
this.postText = postText;
}
public List<String> getHashtags() {
return hashtags;
}
public void setHashtags(List<String> hashtags) {
this.hashtags = hashtags;
}
public String getPublishTime() {
return publishTime;
}
public void setPublishTime(String publishTime) {
this.publishTime = publishTime;
}
}
}
@@ -0,0 +1,14 @@
package kz.konturai.parser.repository;
import kz.konturai.parser.model.MarketingStrategy;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface MarketingStrategyRepository extends MongoRepository<MarketingStrategy, String> {
Optional<MarketingStrategy> findById(String id);
Optional<MarketingStrategy> findByAnalysisId(String analysisId);
}
@@ -0,0 +1,501 @@
package kz.konturai.parser.service;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import kz.konturai.parser.dto.MarketingAnalysisResult;
import kz.konturai.parser.dto.MarketingStrategyRequest;
import kz.konturai.parser.dto.MarketingStrategyResponse;
import kz.konturai.parser.model.MarketingStrategy;
import kz.konturai.parser.repository.MarketingStrategyRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class MarketingStrategyService {
private static final Logger logger = LoggerFactory.getLogger(MarketingStrategyService.class);
private final MarketingStrategyRepository repository;
private final MarketingAnalysisService marketingAnalysisService;
private final OpenAIAnalyticsService openAIAnalyticsService;
private final ObjectMapper objectMapper = new ObjectMapper();
public MarketingStrategyService(
MarketingStrategyRepository repository,
MarketingAnalysisService marketingAnalysisService,
OpenAIAnalyticsService openAIAnalyticsService) {
this.repository = repository;
this.marketingAnalysisService = marketingAnalysisService;
this.openAIAnalyticsService = openAIAnalyticsService;
}
public MarketingStrategy generateStrategy(String analysisId, MarketingStrategyRequest request) {
// Check if analysis exists and is completed
MarketingAnalysisResult analysisResult = marketingAnalysisService.getAnalysisResult(analysisId);
if (analysisResult == null) {
throw new IllegalArgumentException("Анализ с ID " + analysisId + " не найден");
}
if (!"completed".equals(analysisResult.getStatus())) {
throw new IllegalStateException("Анализ еще не завершен. Статус: " + analysisResult.getStatus());
}
// Check if strategy already exists
Optional<MarketingStrategy> existingStrategy = repository.findByAnalysisId(analysisId);
if (existingStrategy.isPresent()) {
logger.info("Стратегия для анализа {} уже существует: {}", analysisId, existingStrategy.get().getId());
return existingStrategy.get();
}
// Create new strategy
MarketingStrategy strategy = new MarketingStrategy(analysisId);
strategy.setDurationWeeks(request.getDurationWeeks() != null ? request.getDurationWeeks() : 4);
strategy.setPriorityPlatforms(request.getPriorityPlatforms());
strategy.setStatus("queued");
strategy = repository.save(strategy);
logger.info("Marketing strategy created with ID: {}", strategy.getId());
// Start async processing
processStrategyGeneration(strategy.getId(), analysisId, analysisResult);
return strategy;
}
@Async("reportGenerationExecutor")
public void processStrategyGeneration(String strategyId, String analysisId, MarketingAnalysisResult analysisResult) {
try {
Optional<MarketingStrategy> optStrategy = repository.findById(strategyId);
if (optStrategy.isEmpty()) {
logger.error("Marketing strategy not found: {}", strategyId);
return;
}
MarketingStrategy strategy = optStrategy.get();
strategy.setStatus("processing");
repository.save(strategy);
logger.info("Starting marketing strategy generation for ID: {}", strategyId);
// Build context from analysis
String context = buildContextFromAnalysis(analysisResult);
// Generate weekly plans
List<MarketingStrategy.WeeklyPlan> weeklyPlans = generateWeeklyPlans(
context, strategy.getDurationWeeks(), strategy.getPriorityPlatforms());
// Generate post calendar
List<MarketingStrategy.PostCalendarItem> postCalendar = generatePostCalendar(
context, strategy.getDurationWeeks(), strategy.getPriorityPlatforms(), weeklyPlans);
// Update strategy
strategy.setStatus("completed");
strategy.setCompletedAt(LocalDateTime.now());
strategy.setWeeklyPlans(weeklyPlans);
strategy.setPostCalendar(postCalendar);
// Store full strategy data as JSON
Map<String, Object> strategyData = new HashMap<>();
strategyData.put("weeklyPlans", weeklyPlans);
strategyData.put("postCalendar", postCalendar);
strategy.setStrategyData(strategyData);
repository.save(strategy);
logger.info("Marketing strategy generation completed successfully for ID: {}", strategyId);
} catch (Exception e) {
logger.error("Error processing marketing strategy {}: {}", strategyId, e.getMessage(), e);
try {
Optional<MarketingStrategy> optStrategy = repository.findById(strategyId);
if (optStrategy.isPresent()) {
MarketingStrategy strategy = optStrategy.get();
strategy.setStatus("failed");
repository.save(strategy);
}
} catch (Exception saveError) {
logger.error("Failed to update strategy status to failed: {}", saveError.getMessage());
}
}
}
private String buildContextFromAnalysis(MarketingAnalysisResult analysisResult) {
StringBuilder context = new StringBuilder();
if (analysisResult.getReport() != null) {
MarketingAnalysisResult.MarketingReport report = analysisResult.getReport();
if (report.getSummary() != null) {
context.append("Резюме анализа: ").append(report.getSummary()).append("\n\n");
}
if (report.getTargetAudience() != null) {
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("\n");
}
if (report.getRecommendations() != null && !report.getRecommendations().isEmpty()) {
context.append("Рекомендации:\n");
for (String rec : report.getRecommendations()) {
context.append("- ").append(rec).append("\n");
}
context.append("\n");
}
if (report.getStrategy() != null) {
MarketingAnalysisResult.Strategy strategy = report.getStrategy();
context.append("Базовая стратегия:\n");
if (strategy.getDuration() != null) {
context.append("Длительность: ").append(strategy.getDuration()).append("\n");
}
if (strategy.getChannels() != null && !strategy.getChannels().isEmpty()) {
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");
}
}
}
return context.toString();
}
private List<MarketingStrategy.WeeklyPlan> generateWeeklyPlans(
String context, Integer durationWeeks, List<String> priorityPlatforms) {
try {
String platformsStr = priorityPlatforms != null && !priorityPlatforms.isEmpty()
? String.join(", ", priorityPlatforms)
: "Instagram, Facebook, LinkedIn, Telegram, TikTok, YouTube";
String prompt = String.format(
"На основе следующего маркетингового анализа создай детальный недельный план продвижения на %d недель.\n\n" +
"Требования:\n" +
"1. Создай план для каждой недели отдельно\n" +
"2. Для каждой недели укажи:\n" +
" - Основные темы недели (3-5 тем)\n" +
" - Рекомендации по контенту (1-2 абзаца)\n" +
" - Приоритетные платформы для этой недели\n" +
"3. Платформы для использования: %s\n" +
"4. Ответ должен быть структурированным и применимым на практике\n" +
"5. Ответ должен быть на русском языке\n\n" +
"Верни ответ в формате JSON со следующей структурой:\n" +
"{\n" +
" \"weeklyPlans\": [\n" +
" {\n" +
" \"weekNumber\": 1,\n" +
" \"mainThemes\": [\"тема1\", \"тема2\", \"тема3\"],\n" +
" \"contentRecommendations\": \"рекомендации по контенту\",\n" +
" \"priorityPlatforms\": [\"Instagram\", \"Facebook\"]\n" +
" }\n" +
" ]\n" +
"}\n\n" +
"Маркетинговый анализ:\n%s",
durationWeeks, platformsStr, context);
String response = openAIAnalyticsService.generateWithInstruction(context, prompt, "ru");
if (response == null || response.trim().isEmpty()) {
logger.warn("Failed to generate weekly plans, using default");
return generateDefaultWeeklyPlans(durationWeeks, priorityPlatforms);
}
// Extract JSON from response
String jsonStr = extractJsonFromResponse(response);
if (jsonStr == null) {
logger.warn("Could not extract JSON from weekly plans response, using default");
return generateDefaultWeeklyPlans(durationWeeks, priorityPlatforms);
}
// Parse JSON
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");
if (weeklyPlansList == null || weeklyPlansList.isEmpty()) {
logger.warn("Weekly plans list is empty, using default");
return generateDefaultWeeklyPlans(durationWeeks, priorityPlatforms);
}
List<MarketingStrategy.WeeklyPlan> plans = new ArrayList<>();
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");
plan.setPriorityPlatforms(platforms != null ? platforms : new ArrayList<>());
plans.add(plan);
}
return plans;
} catch (Exception e) {
logger.error("Error generating weekly plans: {}", e.getMessage(), e);
return generateDefaultWeeklyPlans(durationWeeks, priorityPlatforms);
}
}
private List<MarketingStrategy.PostCalendarItem> generatePostCalendar(
String context, Integer durationWeeks, List<String> priorityPlatforms,
List<MarketingStrategy.WeeklyPlan> weeklyPlans) {
try {
String platformsStr = priorityPlatforms != null && !priorityPlatforms.isEmpty()
? String.join(", ", priorityPlatforms)
: "Instagram, Facebook, LinkedIn, Telegram, TikTok, YouTube";
// Build weekly plans summary for context
String weeklyPlansSummary = "";
if (weeklyPlans != null && !weeklyPlans.isEmpty()) {
weeklyPlansSummary = weeklyPlans.stream()
.map(plan -> String.format("Неделя %d: %s", plan.getWeekNumber(),
plan.getMainThemes() != null ? String.join(", ", plan.getMainThemes()) : ""))
.collect(Collectors.joining("\n"));
}
LocalDateTime startDate = LocalDateTime.now().plusDays(1); // Start from tomorrow
String prompt = String.format(
"На основе следующего маркетингового анализа и недельного плана создай детальный календарь постов на %d недель.\n\n" +
"Требования:\n" +
"1. Создай конкретные посты для публикации\n" +
"2. Для каждого поста укажи:\n" +
" - Дату публикации (в формате YYYY-MM-DD)\n" +
" - Время публикации (в формате HH:mm, например 10:00, 14:00, 18:00)\n" +
" - Платформу (одну из: %s)\n" +
" - Тип контента (пост, сторис, видео, баннер)\n" +
" - Тему поста\n" +
" - Полный текст поста (готовый к публикации, 100-300 символов)\n" +
" - Хештеги (5-10 релевантных хештегов)\n" +
"3. Распредели посты равномерно по неделям\n" +
"4. Рекомендуемое количество постов: 3-5 постов в неделю\n" +
"5. Чередуй платформы и типы контента\n" +
"6. Ответ должен быть на русском языке\n\n" +
"Верни ответ в формате JSON со следующей структурой:\n" +
"{\n" +
" \"postCalendar\": [\n" +
" {\n" +
" \"publishDate\": \"2024-01-15T10:00:00\",\n" +
" \"platform\": \"Instagram\",\n" +
" \"contentType\": \"пост\",\n" +
" \"theme\": \"тема поста\",\n" +
" \"postText\": \"полный текст поста\",\n" +
" \"hashtags\": [\"#хештег1\", \"#хештег2\"],\n" +
" \"publishTime\": \"10:00\"\n" +
" }\n" +
" ]\n" +
"}\n\n" +
"Начальная дата: %s\n\n" +
"Недельный план:\n%s\n\n" +
"Маркетинговый анализ:\n%s",
durationWeeks, platformsStr, startDate.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), weeklyPlansSummary, context);
String response = openAIAnalyticsService.generateWithInstruction(context, prompt, "ru");
if (response == null || response.trim().isEmpty()) {
logger.warn("Failed to generate post calendar, using default");
return generateDefaultPostCalendar(durationWeeks, priorityPlatforms, startDate);
}
// Extract JSON from response
String jsonStr = extractJsonFromResponse(response);
if (jsonStr == null) {
logger.warn("Could not extract JSON from post calendar response, using default");
return generateDefaultPostCalendar(durationWeeks, priorityPlatforms, startDate);
}
// Parse JSON
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");
if (postCalendarList == null || postCalendarList.isEmpty()) {
logger.warn("Post calendar list is empty, using default");
return generateDefaultPostCalendar(durationWeeks, priorityPlatforms, startDate);
}
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) {
try {
LocalDateTime publishDate = LocalDateTime.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
item.setPublishDate(publishDate);
} catch (Exception e) {
logger.warn("Failed to parse date: {}", dateStr);
item.setPublishDate(startDate.plusDays(calendar.size()));
}
} else {
item.setPublishDate(startDate.plusDays(calendar.size()));
}
item.setPlatform((String) postMap.get("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"));
calendar.add(item);
}
return calendar;
} catch (Exception e) {
logger.error("Error generating post calendar: {}", e.getMessage(), e);
return generateDefaultPostCalendar(durationWeeks, priorityPlatforms, LocalDateTime.now().plusDays(1));
}
}
private String extractJsonFromResponse(String response) {
if (response == null) {
return null;
}
// Try to find JSON object in response
int startIdx = response.indexOf("{");
int endIdx = response.lastIndexOf("}");
if (startIdx >= 0 && endIdx > startIdx) {
return response.substring(startIdx, endIdx + 1);
}
// Try to find JSON array
startIdx = response.indexOf("[");
endIdx = response.lastIndexOf("]");
if (startIdx >= 0 && endIdx > startIdx) {
return "{\"data\":" + response.substring(startIdx, endIdx + 1) + "}";
}
return null;
}
private List<MarketingStrategy.WeeklyPlan> generateDefaultWeeklyPlans(
Integer durationWeeks, List<String> priorityPlatforms) {
List<MarketingStrategy.WeeklyPlan> plans = new ArrayList<>();
List<String> platforms = priorityPlatforms != null && !priorityPlatforms.isEmpty()
? priorityPlatforms
: Arrays.asList("Instagram", "Facebook", "LinkedIn");
for (int i = 1; i <= durationWeeks; i++) {
MarketingStrategy.WeeklyPlan plan = new MarketingStrategy.WeeklyPlan();
plan.setWeekNumber(i);
plan.setMainThemes(Arrays.asList("Презентация продукта", "Преимущества", "Отзывы клиентов"));
plan.setContentRecommendations("Создавайте контент, который демонстрирует ценность продукта для целевой аудитории.");
plan.setPriorityPlatforms(platforms);
plans.add(plan);
}
return plans;
}
private List<MarketingStrategy.PostCalendarItem> generateDefaultPostCalendar(
Integer durationWeeks, List<String> priorityPlatforms, LocalDateTime startDate) {
List<MarketingStrategy.PostCalendarItem> calendar = new ArrayList<>();
List<String> platforms = priorityPlatforms != null && !priorityPlatforms.isEmpty()
? priorityPlatforms
: Arrays.asList("Instagram", "Facebook", "LinkedIn");
List<String> contentTypes = Arrays.asList("пост", "сторис", "видео");
List<String> times = Arrays.asList("10:00", "14:00", "18:00");
int postCount = durationWeeks * 3; // 3 posts per week
for (int i = 0; i < postCount; i++) {
MarketingStrategy.PostCalendarItem item = new MarketingStrategy.PostCalendarItem();
item.setPublishDate(startDate.plusDays(i * 2)); // Every 2 days
item.setPlatform(platforms.get(i % platforms.size()));
item.setContentType(contentTypes.get(i % contentTypes.size()));
item.setTheme("Тема поста " + (i + 1));
item.setPostText("Текст поста для публикации на платформе " + item.getPlatform());
item.setHashtags(Arrays.asList("#маркетинг", "#бизнес", "#продвижение"));
item.setPublishTime(times.get(i % times.size()));
calendar.add(item);
}
return calendar;
}
public MarketingStrategyResponse getStrategyResult(String strategyId) {
Optional<MarketingStrategy> optStrategy = repository.findById(strategyId);
if (optStrategy.isEmpty()) {
return null;
}
MarketingStrategy strategy = optStrategy.get();
MarketingStrategyResponse response = new MarketingStrategyResponse();
response.setStrategyId(strategy.getId());
response.setAnalysisId(strategy.getAnalysisId());
response.setStatus(strategy.getStatus());
response.setCreatedAt(strategy.getCreatedAt());
response.setCompletedAt(strategy.getCompletedAt());
response.setDurationWeeks(strategy.getDurationWeeks());
response.setPriorityPlatforms(strategy.getPriorityPlatforms());
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()) {
MarketingStrategyResponse.WeeklyPlan dtoPlan = new MarketingStrategyResponse.WeeklyPlan();
dtoPlan.setWeekNumber(plan.getWeekNumber());
dtoPlan.setMainThemes(plan.getMainThemes());
dtoPlan.setContentRecommendations(plan.getContentRecommendations());
dtoPlan.setPriorityPlatforms(plan.getPriorityPlatforms());
weeklyPlans.add(dtoPlan);
}
strategyContent.setWeeklyPlans(weeklyPlans);
// Convert PostCalendarItems
List<MarketingStrategyResponse.PostCalendarItem> postCalendar = new ArrayList<>();
for (MarketingStrategy.PostCalendarItem item : strategy.getPostCalendar()) {
MarketingStrategyResponse.PostCalendarItem dtoItem = new MarketingStrategyResponse.PostCalendarItem();
dtoItem.setPublishDate(item.getPublishDate());
dtoItem.setPlatform(item.getPlatform());
dtoItem.setContentType(item.getContentType());
dtoItem.setTheme(item.getTheme());
dtoItem.setPostText(item.getPostText());
dtoItem.setHashtags(item.getHashtags());
dtoItem.setPublishTime(item.getPublishTime());
postCalendar.add(dtoItem);
}
strategyContent.setPostCalendar(postCalendar);
response.setStrategy(strategyContent);
}
return response;
}
public MarketingStrategyResponse getStrategyByAnalysisId(String analysisId) {
Optional<MarketingStrategy> optStrategy = repository.findByAnalysisId(analysisId);
if (optStrategy.isEmpty()) {
return null;
}
return getStrategyResult(optStrategy.get().getId());
}
}