From fa4bfff326a7b60d231cfff7ccf9711da116e4c1 Mon Sep 17 00:00:00 2001 From: arys Date: Sun, 1 Mar 2026 13:11:21 +0500 Subject: [PATCH] fix --- .../MarketingAnalysisV3Controller.java | 38 +++++++++- .../service/GeminiVideoGenerationService.java | 43 +++++++++-- .../service/MarketingStrategyV3Service.java | 73 +++++++++++++++++-- 3 files changed, 139 insertions(+), 15 deletions(-) diff --git a/src/main/java/kz/konturai/parser/controller/MarketingAnalysisV3Controller.java b/src/main/java/kz/konturai/parser/controller/MarketingAnalysisV3Controller.java index 49e1b27..2ccaa5e 100644 --- a/src/main/java/kz/konturai/parser/controller/MarketingAnalysisV3Controller.java +++ b/src/main/java/kz/konturai/parser/controller/MarketingAnalysisV3Controller.java @@ -13,6 +13,8 @@ import kz.konturai.parser.service.MinIOService; import kz.konturai.parser.service.PostingTaskService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -200,6 +202,30 @@ public class MarketingAnalysisV3Controller { } } + @GetMapping("/videos/{videoFilename}") + public ResponseEntity getPostVideo( + @RequestHeader(value = "Authorization", required = false) String authHeader, + @PathVariable String videoFilename + ) { + String userId = extractUserIdFromHeader(authHeader); + if (userId == null) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + + try { + if (!minIOService.fileExists(videoFilename)) { + return ResponseEntity.notFound().build(); + } + InputStream inputStream = minIOService.downloadFile(videoFilename); + InputStreamResource resource = new InputStreamResource(inputStream); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_TYPE, "video/mp4") + .header(HttpHeaders.CACHE_CONTROL, "public, max-age=3600") + .body(resource); + } catch (Exception e) { + return ResponseEntity.internalServerError().build(); + } + } + @GetMapping("/{analysisId}/strategy-preview") public ResponseEntity previewStrategy( @RequestHeader(value = "Authorization", required = false) String authHeader, @@ -283,7 +309,10 @@ public class MarketingAnalysisV3Controller { MarketingStrategy strategy = strategyOpt.get(); if (!strategy.getUserId().equals(userId)) return forbiddenResponse(); - return ResponseEntity.ok(ApiResponse.success(strategy)); + Map result = strategyService.getStrategyResult(strategyId); + if (result == null) return notFoundResponse("Ошибка сборки стратегии"); + + return ResponseEntity.ok(ApiResponse.success(result)); } catch (Exception e) { return internalErrorResponse(e); } @@ -305,7 +334,10 @@ public class MarketingAnalysisV3Controller { Optional strategyOpt = strategyService.getStrategyByAnalysisId(analysisId); if (strategyOpt.isEmpty()) return notFoundResponse("Стратегия не найдена"); - return ResponseEntity.ok(ApiResponse.success(strategyOpt.get())); + Map result = strategyService.getStrategyResult(strategyOpt.get().getId()); + if (result == null) return notFoundResponse("Ошибка сборки стратегии"); + + return ResponseEntity.ok(ApiResponse.success(result)); } catch (Exception e) { return internalErrorResponse(e); } @@ -451,6 +483,8 @@ public class MarketingAnalysisV3Controller { "postIndex", postIndex, "imageUrl", updatedItem.getImageUrl() != null ? updatedItem.getImageUrl() : "", "imageFilename", updatedItem.getImageFilename() != null ? updatedItem.getImageFilename() : "", + "videoUrl", updatedItem.getVideoUrl() != null ? updatedItem.getVideoUrl() : "", + "videoFilename", updatedItem.getVideoFilename() != null ? updatedItem.getVideoFilename() : "", "theme", updatedItem.getTheme() != null ? updatedItem.getTheme() : "", "platform", updatedItem.getPlatform() != null ? updatedItem.getPlatform() : "", "publishDate", updatedItem.getPublishDate() diff --git a/src/main/java/kz/konturai/parser/service/GeminiVideoGenerationService.java b/src/main/java/kz/konturai/parser/service/GeminiVideoGenerationService.java index 44ab376..1765ec6 100644 --- a/src/main/java/kz/konturai/parser/service/GeminiVideoGenerationService.java +++ b/src/main/java/kz/konturai/parser/service/GeminiVideoGenerationService.java @@ -1,5 +1,7 @@ package kz.konturai.parser.service; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -7,6 +9,7 @@ import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; +import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -15,7 +18,8 @@ import java.util.Map; @Slf4j public class GeminiVideoGenerationService { - private final RestTemplate restTemplate = new RestTemplate(); // Можно заинжектить через @Bean, если есть + private final RestTemplate restTemplate = new RestTemplate(); + private final ObjectMapper objectMapper = new ObjectMapper(); @Value("${gemini.veo.api.url:https://generativelanguage.googleapis.com/v1beta/models/veo:generateVideo}") private String videoApiUrl; @@ -24,7 +28,6 @@ public class GeminiVideoGenerationService { private String apiKey; public byte[] generateVideo(String prompt) { - log.info("Запуск генерации видео через Gemini Veo. Промпт: {}", prompt); try { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); @@ -37,22 +40,46 @@ public class GeminiVideoGenerationService { HttpEntity> entity = new HttpEntity<>(requestBody, headers); - ResponseEntity response = restTemplate.exchange( + ResponseEntity response = restTemplate.exchange( videoApiUrl, HttpMethod.POST, entity, - byte[].class + String.class ); if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) { - log.info("Видео успешно сгенерировано. Размер: {} байт", response.getBody().length); - return response.getBody(); + String jsonBody = response.getBody(); + JsonNode rootNode = objectMapper.readTree(jsonBody); + String base64Video = null; + + if (rootNode.has("predictions") && rootNode.get("predictions").isArray()) { + JsonNode prediction = rootNode.get("predictions").get(0); + if (prediction.has("bytesBase64Encoded")) { + base64Video = prediction.get("bytesBase64Encoded").asText(); + } + } else if (rootNode.has("candidates") && rootNode.get("candidates").isArray()) { + JsonNode parts = rootNode.at("/candidates/0/content/parts"); + if (parts.isArray() && parts.size() > 0 && parts.get(0).has("inlineData")) { + base64Video = parts.get(0).at("/inlineData/data").asText(); + } + } else if (rootNode.has("videoBase64")) { + base64Video = rootNode.get("videoBase64").asText(); + } else if (rootNode.has("base64")) { + base64Video = rootNode.get("base64").asText(); + } + + if (base64Video != null && !base64Video.isEmpty()) { + return Base64.getDecoder().decode(base64Video); + } else { + if (!jsonBody.trim().startsWith("{")) { + return jsonBody.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1); + } + return null; + } } else { - log.error("Ошибка API генерации видео. Код: {}", response.getStatusCode()); return null; } } catch (Exception e) { - log.error("Критическая ошибка при генерации видео: {}", e.getMessage(), e); return null; } } diff --git a/src/main/java/kz/konturai/parser/service/MarketingStrategyV3Service.java b/src/main/java/kz/konturai/parser/service/MarketingStrategyV3Service.java index 4774705..dd6e471 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingStrategyV3Service.java +++ b/src/main/java/kz/konturai/parser/service/MarketingStrategyV3Service.java @@ -8,6 +8,7 @@ import kz.konturai.parser.dto.StatusHistoryEntry; import kz.konturai.parser.enums.StrategyModel; import kz.konturai.parser.model.MarketingAnalysisV3Document; import kz.konturai.parser.model.MarketingStrategy; +import kz.konturai.parser.model.PostingTask; import kz.konturai.parser.repository.MarketingAnalysisV3Repository; import kz.konturai.parser.repository.MarketingStrategyRepository; import lombok.RequiredArgsConstructor; @@ -20,11 +21,7 @@ import org.springframework.stereotype.Service; import java.io.InputStream; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; @Service @RequiredArgsConstructor @@ -36,6 +33,7 @@ public class MarketingStrategyV3Service { private final OpenAIAnalyticsService aiService; private final ImageGenerationService imageGenerationService; private final GeminiVideoGenerationService geminiVideoService; + private final PostingTaskService postingTaskService; private final MinIOService minIOService; private final ObjectMapper objectMapper = new ObjectMapper() @@ -127,6 +125,71 @@ public class MarketingStrategyV3Service { } } + // ИДЕАЛЬНЫЙ СБОРЩИК ОТВЕТА (Интеграция с Автопостингом) + public Map getStrategyResult(String strategyId) { + Optional optStrategy = repository.findById(strategyId); + if (optStrategy.isEmpty()) { + return null; + } + + MarketingStrategy strategy = optStrategy.get(); + Map response = new HashMap<>(); + response.put("strategyId", strategy.getId()); + response.put("analysisId", strategy.getAnalysisId()); + response.put("status", strategy.getStatus()); + response.put("createdAt", strategy.getCreatedAt()); + response.put("completedAt", strategy.getCompletedAt()); + response.put("durationWeeks", strategy.getDurationWeeks()); + response.put("priorityPlatforms", strategy.getPriorityPlatforms()); + response.put("strategyData", strategy.getStrategyData()); + + if (strategy.getWeeklyPlans() != null && strategy.getPostCalendar() != null) { + Map strategyContent = new HashMap<>(); + strategyContent.put("weeklyPlans", strategy.getWeeklyPlans()); + + List> postCalendarList = new ArrayList<>(); + List tasks = postingTaskService.getStrategyTasks(strategyId); + + for (MarketingStrategy.PostCalendarItem item : strategy.getPostCalendar()) { + Map dtoItem = new HashMap<>(); + dtoItem.put("publishDate", item.getPublishDate()); + dtoItem.put("platform", item.getPlatform()); + dtoItem.put("contentType", item.getContentType()); + dtoItem.put("theme", item.getTheme()); + dtoItem.put("postText", item.getPostText()); + dtoItem.put("hashtags", item.getHashtags()); + dtoItem.put("publishTime", item.getPublishTime()); + dtoItem.put("imageUrl", item.getImageUrl()); + dtoItem.put("imageFilename", item.getImageFilename()); + dtoItem.put("videoUrl", item.getVideoUrl()); + dtoItem.put("videoFilename", item.getVideoFilename()); + + Optional matchingTask = tasks.stream() + .filter(task -> task.getPublishDate() != null && item.getPublishDate() != null + && task.getPublishDate().equals(item.getPublishDate()) + && task.getPlatform() != null && item.getPlatform() != null + && task.getPlatform().equalsIgnoreCase(item.getPlatform())) + .findFirst(); + + if (matchingTask.isPresent()) { + dtoItem.put("taskId", matchingTask.get().getId()); + dtoItem.put("taskStatus", matchingTask.get().getStatus()); + } else { + dtoItem.put("taskStatus", null); + } + + postCalendarList.add(dtoItem); + } + strategyContent.put("postCalendar", postCalendarList); + + response.put("strategy", strategyContent); + response.put("postCalendar", postCalendarList); + response.put("weeklyPlans", strategy.getWeeklyPlans()); + } + + return response; + } + public MarketingStrategy.PostCalendarItem regeneratePostImage(String strategyId, int postIndex) { Optional optStrategy = repository.findById(strategyId); if (optStrategy.isEmpty()) {