fix
This commit is contained in:
@@ -5,6 +5,7 @@ import kz.konturai.parser.dto.ApiResponse;
|
||||
import kz.konturai.parser.dto.ErrorResponse;
|
||||
import kz.konturai.parser.dto.MarketingAnalysisV3Request;
|
||||
import kz.konturai.parser.dto.MarketingStrategyRequest;
|
||||
import kz.konturai.parser.dto.StrategyHistoryResponse;
|
||||
import kz.konturai.parser.enums.StrategyModel;
|
||||
import kz.konturai.parser.model.MarketingAnalysisV3Document;
|
||||
import kz.konturai.parser.model.MarketingStrategy;
|
||||
@@ -26,6 +27,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/marketing/v3")
|
||||
@@ -50,10 +52,6 @@ public class MarketingAnalysisV3Controller {
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// БЛОК 1: АНАЛИЗ (ОПРОСНИК И ПАРСИНГ)
|
||||
// ==========================================
|
||||
|
||||
@PostMapping("/start")
|
||||
public ResponseEntity<?> startAnalysis(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@@ -113,10 +111,6 @@ public class MarketingAnalysisV3Controller {
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// БЛОК 2: СТРАТЕГИЯ (СКОРИНГ, ЛОГОТИПЫ, МЕДИА)
|
||||
// ==========================================
|
||||
|
||||
@GetMapping("/{analysisId}/strategy-preview")
|
||||
public ResponseEntity<?> previewStrategy(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@@ -145,7 +139,6 @@ public class MarketingAnalysisV3Controller {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@PostMapping(value = "/{analysisId}/strategy/generate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public ResponseEntity<?> generateStrategy(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@@ -170,7 +163,6 @@ public class MarketingAnalysisV3Controller {
|
||||
log.info("Логотип клиента успешно загружен в MinIO: {}", logoFilename);
|
||||
}
|
||||
|
||||
// Защита от NullPointerException, если фронтенд вообще не прислал объект request
|
||||
if (request == null) {
|
||||
request = new MarketingStrategyRequest();
|
||||
}
|
||||
@@ -212,9 +204,39 @@ public class MarketingAnalysisV3Controller {
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// БЛОК 3: ОБРАБОТКА ОШИБОК И ОТВЕТЫ
|
||||
// ==========================================
|
||||
@GetMapping("/strategy/my")
|
||||
public ResponseEntity<?> getMyStrategies(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader
|
||||
) {
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) return unauthorizedResponse();
|
||||
|
||||
try {
|
||||
List<MarketingStrategy> strategies = strategyService.getUserStrategies(userId);
|
||||
List<StrategyHistoryResponse> responseList = strategies.stream()
|
||||
.map(this::convertToStrategyHistoryResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(responseList));
|
||||
} catch (Exception e) {
|
||||
log.error("Error fetching user strategies", e);
|
||||
return internalErrorResponse(e);
|
||||
}
|
||||
}
|
||||
|
||||
private StrategyHistoryResponse convertToStrategyHistoryResponse(MarketingStrategy strategy) {
|
||||
StrategyHistoryResponse response = new StrategyHistoryResponse();
|
||||
response.setStrategyId(strategy.getId());
|
||||
response.setAnalysisId(strategy.getAnalysisId());
|
||||
response.setStatus(strategy.getStatus());
|
||||
response.setUserId(strategy.getUserId());
|
||||
response.setDurationWeeks(strategy.getDurationWeeks());
|
||||
response.setPriorityPlatforms(strategy.getPriorityPlatforms());
|
||||
response.setCreatedAt(strategy.getCreatedAt());
|
||||
response.setCompletedAt(strategy.getCompletedAt());
|
||||
response.setStatusHistory(strategy.getStatusHistory());
|
||||
return response;
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleValidationException(MethodArgumentNotValidException ex) {
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
package kz.konturai.parser.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class StrategyHistoryResponse {
|
||||
private String strategyId;
|
||||
private String analysisId;
|
||||
@@ -13,80 +22,5 @@ public class StrategyHistoryResponse {
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime completedAt;
|
||||
private List<StatusHistoryEntry> statusHistory;
|
||||
|
||||
public StrategyHistoryResponse() {
|
||||
}
|
||||
|
||||
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 String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
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 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 List<StatusHistoryEntry> getStatusHistory() {
|
||||
return statusHistory;
|
||||
}
|
||||
|
||||
public void setStatusHistory(List<StatusHistoryEntry> statusHistory) {
|
||||
this.statusHistory = statusHistory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,13 +51,17 @@ public class MarketingStrategyV3Service {
|
||||
return repository.findById(id);
|
||||
}
|
||||
|
||||
public List<MarketingStrategy> getUserStrategies(String userId) {
|
||||
return repository.findByUserIdOrderByCreatedAtDesc(userId);
|
||||
}
|
||||
|
||||
public MarketingStrategy generateStrategy(String analysisId, MarketingStrategyRequest request, String userId, String uploadedLogoFilename) {
|
||||
MarketingAnalysisV3Document analysisDoc = analysisRepository.findById(analysisId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Анализ V3 не найден"));
|
||||
|
||||
MarketingStrategy strategy = new MarketingStrategy(analysisId);
|
||||
strategy.setUserId(userId);
|
||||
strategy.setDurationWeeks(4); // Дефолт, ИИ его перезапишет
|
||||
strategy.setDurationWeeks(4);
|
||||
strategy.setPriorityPlatforms(new ArrayList<>());
|
||||
strategy.setStatus("queued");
|
||||
|
||||
@@ -82,11 +86,9 @@ public class MarketingStrategyV3Service {
|
||||
addStatusHistoryEntry(strategy, "processing", "Расчет баллов, генерация постов (минимум 3 в неделю)...");
|
||||
repository.save(strategy);
|
||||
|
||||
// 1. Считаем лучшую модель
|
||||
StrategyModel bestModel = calculateBestScoringModel(analysis.getRequestData());
|
||||
log.info("[Strategy ID: {}] Выбрана модель: {}", strategyId, bestModel.name());
|
||||
|
||||
// 2. Генерируем JSON
|
||||
String systemPrompt = buildSystemPrompt(bestModel);
|
||||
String userPrompt = buildUserPrompt(analysis, bestModel);
|
||||
|
||||
@@ -97,18 +99,15 @@ public class MarketingStrategyV3Service {
|
||||
throw new IllegalStateException("AI не вернул валидный JSON");
|
||||
}
|
||||
|
||||
// 3. Парсим ответ
|
||||
Map<String, Object> strategyData = objectMapper.readValue(jsonResponse, new TypeReference<>() {});
|
||||
|
||||
if (strategy.getStrategyData() != null) {
|
||||
strategy.getStrategyData().forEach(strategyData::putIfAbsent);
|
||||
}
|
||||
|
||||
// 4. Заполняем сущность
|
||||
populateStrategyEntity(strategy, strategyData);
|
||||
repository.save(strategy);
|
||||
|
||||
// 5. Генерируем медиа НАДЕЖНЫМ способом с FALLBACK
|
||||
addStatusHistoryEntry(strategy, "processing", "Создание фото и видео креативов. Это займет несколько минут...");
|
||||
repository.save(strategy);
|
||||
|
||||
@@ -125,7 +124,6 @@ public class MarketingStrategyV3Service {
|
||||
}
|
||||
}
|
||||
|
||||
// Тот самый надежный калькулятор
|
||||
public StrategyModel calculateBestScoringModel(kz.konturai.parser.dto.MarketingAnalysisV3Request req) {
|
||||
int entry = 0, authority = 0, trust = 0, conversion = 0;
|
||||
|
||||
@@ -210,7 +208,7 @@ public class MarketingStrategyV3Service {
|
||||
try {
|
||||
item.setPublishDate(LocalDateTime.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||
} catch (Exception e) {
|
||||
item.setPublishDate(startDate.plusDays(postCalendar.size() * 2L)); // Распределяем по дням
|
||||
item.setPublishDate(startDate.plusDays(postCalendar.size() * 2L));
|
||||
}
|
||||
} else {
|
||||
item.setPublishDate(startDate.plusDays(postCalendar.size() * 2L));
|
||||
@@ -229,10 +227,6 @@ public class MarketingStrategyV3Service {
|
||||
strategy.setPostCalendar(postCalendar);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// ЖЕЛЕЗОБЕТОННАЯ ГЕНЕРАЦИЯ МЕДИА С FALLBACK
|
||||
// ==========================================
|
||||
|
||||
private void generateMediaAssets(MarketingStrategy strategy, MarketingAnalysisV3Document analysis) {
|
||||
String businessContext = getBusinessContext(analysis);
|
||||
String brandName = analysis.getRequestData().getProductName();
|
||||
@@ -255,7 +249,6 @@ public class MarketingStrategyV3Service {
|
||||
|
||||
try {
|
||||
if (contentType.contains("видео") || contentType.contains("reels") || contentType.contains("tiktok")) {
|
||||
// ПРОБУЕМ СГЕНЕРИРОВАТЬ ВИДЕО
|
||||
String videoPrompt = "High quality cinematic commercial video. Business niche: " + businessContext + ". Scene: " + item.getTheme() + ". Photorealistic, dynamic motion, 4k.";
|
||||
log.info("Попытка генерации ВИДЕО: {}", videoPrompt);
|
||||
|
||||
@@ -273,13 +266,11 @@ public class MarketingStrategyV3Service {
|
||||
item.setVideoFilename(filename);
|
||||
log.info("Видео успешно сгенерировано: {}", filename);
|
||||
} else {
|
||||
// ФОЛБЭК: ВИДЕО НЕ ПОЛУЧИЛОСЬ -> ДЕЛАЕМ ФОТО
|
||||
log.warn("Видео не сгенерировалось (вернулся null). Включаю FALLBACK -> генерирую ФОТО для темы: {}", item.getTheme());
|
||||
item.setContentType("фото"); // Меняем тип поста, чтобы фронт отобразил картинку
|
||||
item.setContentType("фото");
|
||||
doGenerateImage(item, businessContext, brandName, clientLogoBytes);
|
||||
}
|
||||
} else {
|
||||
// ГЕНЕРИРУЕМ ФОТО
|
||||
doGenerateImage(item, businessContext, brandName, clientLogoBytes);
|
||||
}
|
||||
|
||||
@@ -291,7 +282,6 @@ public class MarketingStrategyV3Service {
|
||||
}
|
||||
}
|
||||
|
||||
// Выделенный надежный метод для фото
|
||||
private void doGenerateImage(MarketingStrategy.PostCalendarItem item, String businessContext, String brandName, byte[] clientLogoBytes) {
|
||||
String imagePrompt = buildImagePrompt(item, businessContext, brandName);
|
||||
log.info("Генерация ФОТО: {}", imagePrompt);
|
||||
@@ -315,7 +305,6 @@ public class MarketingStrategyV3Service {
|
||||
return analysis.getRequestData().getBusinessNiche() + ", product: " + analysis.getRequestData().getProductName();
|
||||
}
|
||||
|
||||
// ПРОМПТ ДЛЯ ФОТО (НА АНГЛИЙСКОМ ДЛЯ ЛУЧШЕГО КАЧЕСТВА)
|
||||
private String buildImagePrompt(MarketingStrategy.PostCalendarItem item, String businessContext, String brandName) {
|
||||
StringBuilder prompt = new StringBuilder();
|
||||
|
||||
@@ -340,7 +329,6 @@ public class MarketingStrategyV3Service {
|
||||
}
|
||||
|
||||
if (item.getTheme() != null && !item.getTheme().isEmpty()) {
|
||||
// Переводим тему на английский контекст (ИИ поймет)
|
||||
prompt.append("Main subject/action: ").append(item.getTheme()).append(". ");
|
||||
}
|
||||
|
||||
@@ -350,10 +338,6 @@ public class MarketingStrategyV3Service {
|
||||
return prompt.toString();
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// ПРОМПТЫ ДЛЯ ИИ (СТРОГИЕ ПРАВИЛА КОЛИЧЕСТВА)
|
||||
// ==========================================
|
||||
|
||||
private String buildSystemPrompt(StrategyModel bestModel) {
|
||||
return """
|
||||
РОЛЬ: Ты — топовый Голливудский Креативный Директор и Chief Media Planner.
|
||||
|
||||
Reference in New Issue
Block a user