.
This commit is contained in:
@@ -2,6 +2,7 @@ package kz.konturai.parser.controller;
|
||||
|
||||
import kz.konturai.parser.dto.*;
|
||||
import kz.konturai.parser.model.MarketingAnalysis;
|
||||
import kz.konturai.parser.model.MarketingAnalysisV2Document;
|
||||
import kz.konturai.parser.model.MarketingStrategy;
|
||||
import kz.konturai.parser.model.PostingTask;
|
||||
import kz.konturai.parser.service.JwtService;
|
||||
@@ -78,6 +79,7 @@ public class MarketingController {
|
||||
@PostMapping("/start")
|
||||
public ResponseEntity<?> startAnalysis(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@RequestParam(value = "generateV2", defaultValue = "false") boolean generateV2,
|
||||
@Valid @RequestBody MarketingAnalysisRequest request) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
@@ -89,6 +91,24 @@ public class MarketingController {
|
||||
List<MarketingAnalysis> analyses = marketingAnalysisService.startAnalysis(request, userId);
|
||||
MarketingAnalysis analysis = analyses.get(0);
|
||||
|
||||
// Опционально создаем v2 анализ и связываем его
|
||||
String v2AnalysisId = null;
|
||||
if (generateV2) {
|
||||
try {
|
||||
v2AnalysisId = marketingAnalysisService.generateAndSaveAnalysisV2(request, userId);
|
||||
// Связываем обычный анализ с v2
|
||||
analysis.setV2AnalysisId(v2AnalysisId);
|
||||
marketingAnalysisService.updateAnalysis(analysis);
|
||||
// Связываем v2 анализ с обычным
|
||||
marketingAnalysisService.linkV2AnalysisToAnalysis(v2AnalysisId, analysis.getId());
|
||||
logger.info("Created and linked v2 analysis {} to analysis {}", v2AnalysisId,
|
||||
analysis.getId());
|
||||
} catch (Exception e) {
|
||||
logger.error("Error creating v2 analysis: {}", e.getMessage(), e);
|
||||
// Продолжаем выполнение даже если v2 анализ не создался
|
||||
}
|
||||
}
|
||||
|
||||
// Start async processing
|
||||
marketingAnalysisService.processAnalysis(analysis.getId(), request);
|
||||
|
||||
@@ -107,6 +127,18 @@ public class MarketingController {
|
||||
estimatedCompletion,
|
||||
"Комплексный анализ (" + analysisTypesStr
|
||||
+ ") запущен успешно. Результаты будут готовы в течение 5-10 минут.");
|
||||
|
||||
// Добавляем v2 id в ответ, если он был создан
|
||||
if (v2AnalysisId != null) {
|
||||
Map<String, Object> responseData = new HashMap<>();
|
||||
responseData.put("analysisId", response.getAnalysisId());
|
||||
responseData.put("status", response.getStatus());
|
||||
responseData.put("estimatedCompletionTime", response.getEstimatedCompletionTime());
|
||||
responseData.put("message", response.getMessage());
|
||||
responseData.put("v2AnalysisId", v2AnalysisId);
|
||||
return ResponseEntity.ok(ApiResponse.success("Анализ запущен успешно", responseData));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success("Анализ запущен успешно", response));
|
||||
}
|
||||
|
||||
@@ -120,8 +152,53 @@ public class MarketingController {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
MarketingAnalysisResponseV2 response = marketingAnalysisService.generateAnalysisV2(request);
|
||||
return ResponseEntity.ok(response);
|
||||
try {
|
||||
String analysisId = marketingAnalysisService.generateAndSaveAnalysisV2(request, userId);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("id", analysisId);
|
||||
response.put("message", "Анализ успешно создан и сохранен");
|
||||
return ResponseEntity.ok(ApiResponse.success("Анализ создан", response));
|
||||
} catch (Exception e) {
|
||||
logger.error("Error creating analysis v2: {}", e.getMessage(), e);
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла ошибка при создании анализа");
|
||||
return ResponseEntity.status(500)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/v2/{id}")
|
||||
public ResponseEntity<?> getAnalysisV2(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String id) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
Optional<MarketingAnalysisV2Document> optAnalysis = marketingAnalysisService
|
||||
.getAnalysisV2ById(id);
|
||||
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Анализ с указанным ID не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
}
|
||||
|
||||
MarketingAnalysisV2Document analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(analysis.getAnalysisData()));
|
||||
}
|
||||
|
||||
@GetMapping("/{analysisId}")
|
||||
@@ -561,6 +638,22 @@ public class MarketingController {
|
||||
response.setCreatedAt(analysis.getCreatedAt());
|
||||
response.setCompletedAt(analysis.getCompletedAt());
|
||||
response.setStatusHistory(analysis.getStatusHistory());
|
||||
|
||||
// Ищем связанный v2 анализ
|
||||
String v2AnalysisId = analysis.getV2AnalysisId();
|
||||
if (v2AnalysisId == null || v2AnalysisId.isEmpty()) {
|
||||
// Если v2AnalysisId не установлен, пытаемся найти по analysisId
|
||||
Optional<MarketingAnalysisV2Document> optV2 = marketingAnalysisService
|
||||
.getV2AnalysisByAnalysisId(analysis.getId());
|
||||
if (optV2.isPresent()) {
|
||||
v2AnalysisId = optV2.get().getId();
|
||||
// Обновляем анализ для сохранения связи
|
||||
analysis.setV2AnalysisId(v2AnalysisId);
|
||||
marketingAnalysisService.updateAnalysis(analysis);
|
||||
}
|
||||
}
|
||||
response.setV2AnalysisId(v2AnalysisId);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public class AnalysisHistoryResponse {
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime completedAt;
|
||||
private List<StatusHistoryEntry> statusHistory;
|
||||
private String v2AnalysisId; // ID связанного v2 анализа
|
||||
|
||||
public AnalysisHistoryResponse() {
|
||||
}
|
||||
@@ -142,4 +143,12 @@ public class AnalysisHistoryResponse {
|
||||
public void setAnalysisType(String analysisType) {
|
||||
this.analysisType = analysisType;
|
||||
}
|
||||
|
||||
public String getV2AnalysisId() {
|
||||
return v2AnalysisId;
|
||||
}
|
||||
|
||||
public void setV2AnalysisId(String v2AnalysisId) {
|
||||
this.v2AnalysisId = v2AnalysisId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,9 @@ public class MarketingAnalysis {
|
||||
@Field("status_history")
|
||||
private List<StatusHistoryEntry> statusHistory;
|
||||
|
||||
@Field("v2_analysis_id")
|
||||
private String v2AnalysisId; // ID связанного v2 анализа
|
||||
|
||||
public MarketingAnalysis() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.status = "queued";
|
||||
@@ -231,4 +234,12 @@ public class MarketingAnalysis {
|
||||
public void setAnalysisType(List<String> analysisType) {
|
||||
this.analysisType = analysisType;
|
||||
}
|
||||
|
||||
public String getV2AnalysisId() {
|
||||
return v2AnalysisId;
|
||||
}
|
||||
|
||||
public void setV2AnalysisId(String v2AnalysisId) {
|
||||
this.v2AnalysisId = v2AnalysisId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package kz.konturai.parser.model;
|
||||
|
||||
import kz.konturai.parser.dto.MarketingAnalysisResponseV2;
|
||||
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_analysis_v2")
|
||||
public class MarketingAnalysisV2Document {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Field("user_id")
|
||||
private String userId;
|
||||
|
||||
@Field("business_niche")
|
||||
private String businessNiche;
|
||||
|
||||
@Field("product")
|
||||
private String product;
|
||||
|
||||
@Field("target_audience")
|
||||
private Map<String, Object> targetAudience;
|
||||
|
||||
@Field("region")
|
||||
private List<String> region;
|
||||
|
||||
@Field("goal")
|
||||
private String goal;
|
||||
|
||||
@Field("detail_level")
|
||||
private String detailLevel;
|
||||
|
||||
@Field("strong_side")
|
||||
private String strongSide;
|
||||
|
||||
@Field("weak_side")
|
||||
private String weakSide;
|
||||
|
||||
@Field("analysis_type")
|
||||
private List<String> analysisType;
|
||||
|
||||
@Field("analysis_data")
|
||||
private MarketingAnalysisResponseV2 analysisData;
|
||||
|
||||
@Field("created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Field("updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Field("analysis_id")
|
||||
private String analysisId; // ID связанного обычного анализа
|
||||
|
||||
public MarketingAnalysisV2Document() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getBusinessNiche() {
|
||||
return businessNiche;
|
||||
}
|
||||
|
||||
public void setBusinessNiche(String businessNiche) {
|
||||
this.businessNiche = businessNiche;
|
||||
}
|
||||
|
||||
public String getProduct() {
|
||||
return product;
|
||||
}
|
||||
|
||||
public void setProduct(String product) {
|
||||
this.product = product;
|
||||
}
|
||||
|
||||
public Map<String, Object> getTargetAudience() {
|
||||
return targetAudience;
|
||||
}
|
||||
|
||||
public void setTargetAudience(Map<String, Object> targetAudience) {
|
||||
this.targetAudience = targetAudience;
|
||||
}
|
||||
|
||||
public List<String> getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(List<String> region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getGoal() {
|
||||
return goal;
|
||||
}
|
||||
|
||||
public void setGoal(String goal) {
|
||||
this.goal = goal;
|
||||
}
|
||||
|
||||
public String getDetailLevel() {
|
||||
return detailLevel;
|
||||
}
|
||||
|
||||
public void setDetailLevel(String detailLevel) {
|
||||
this.detailLevel = detailLevel;
|
||||
}
|
||||
|
||||
public String getStrongSide() {
|
||||
return strongSide;
|
||||
}
|
||||
|
||||
public void setStrongSide(String strongSide) {
|
||||
this.strongSide = strongSide;
|
||||
}
|
||||
|
||||
public String getWeakSide() {
|
||||
return weakSide;
|
||||
}
|
||||
|
||||
public void setWeakSide(String weakSide) {
|
||||
this.weakSide = weakSide;
|
||||
}
|
||||
|
||||
public List<String> getAnalysisType() {
|
||||
return analysisType;
|
||||
}
|
||||
|
||||
public void setAnalysisType(List<String> analysisType) {
|
||||
this.analysisType = analysisType;
|
||||
}
|
||||
|
||||
public MarketingAnalysisResponseV2 getAnalysisData() {
|
||||
return analysisData;
|
||||
}
|
||||
|
||||
public void setAnalysisData(MarketingAnalysisResponseV2 analysisData) {
|
||||
this.analysisData = analysisData;
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public String getAnalysisId() {
|
||||
return analysisId;
|
||||
}
|
||||
|
||||
public void setAnalysisId(String analysisId) {
|
||||
this.analysisId = analysisId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package kz.konturai.parser.repository;
|
||||
|
||||
import kz.konturai.parser.model.MarketingAnalysisV2Document;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface MarketingAnalysisV2Repository extends MongoRepository<MarketingAnalysisV2Document, String> {
|
||||
Optional<MarketingAnalysisV2Document> findById(String id);
|
||||
|
||||
List<MarketingAnalysisV2Document> findByUserIdOrderByCreatedAtDesc(String userId);
|
||||
|
||||
Optional<MarketingAnalysisV2Document> findByIdAndUserId(String id, String userId);
|
||||
|
||||
Optional<MarketingAnalysisV2Document> findByAnalysisId(String analysisId);
|
||||
}
|
||||
@@ -9,7 +9,9 @@ import kz.konturai.parser.dto.StatusHistoryEntry;
|
||||
import kz.konturai.parser.model.AnalysisType;
|
||||
import kz.konturai.parser.model.DetailLevel;
|
||||
import kz.konturai.parser.model.MarketingAnalysis;
|
||||
import kz.konturai.parser.model.MarketingAnalysisV2Document;
|
||||
import kz.konturai.parser.repository.MarketingAnalysisRepository;
|
||||
import kz.konturai.parser.repository.MarketingAnalysisV2Repository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -392,6 +394,7 @@ public class MarketingAnalysisService {
|
||||
private static final long RETRY_DELAY_MS = 1000;
|
||||
|
||||
private final MarketingAnalysisRepository repository;
|
||||
private final MarketingAnalysisV2Repository v2Repository;
|
||||
private final OpenAIAnalyticsService openAIAnalyticsService;
|
||||
private final MinIOService minIOService;
|
||||
private final ResearchPdfService researchPdfService;
|
||||
@@ -411,11 +414,13 @@ public class MarketingAnalysisService {
|
||||
|
||||
public MarketingAnalysisService(
|
||||
MarketingAnalysisRepository repository,
|
||||
MarketingAnalysisV2Repository v2Repository,
|
||||
OpenAIAnalyticsService openAIAnalyticsService,
|
||||
MinIOService minIOService,
|
||||
ResearchPdfService researchPdfService,
|
||||
SerperSearchService serperSearchService) {
|
||||
this.repository = repository;
|
||||
this.v2Repository = v2Repository;
|
||||
this.openAIAnalyticsService = openAIAnalyticsService;
|
||||
this.minIOService = minIOService;
|
||||
this.researchPdfService = researchPdfService;
|
||||
@@ -703,6 +708,88 @@ public class MarketingAnalysisService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерирует анализ v2 и сохраняет его в MongoDB
|
||||
*
|
||||
* @param request Запрос на анализ
|
||||
* @param userId ID пользователя
|
||||
* @return ID сохраненного анализа
|
||||
*/
|
||||
public String generateAndSaveAnalysisV2(MarketingAnalysisRequest request, String userId) {
|
||||
try {
|
||||
// Генерируем анализ
|
||||
MarketingAnalysisResponseV2 analysisData = generateAnalysisV2(request);
|
||||
|
||||
// Создаем документ для сохранения
|
||||
MarketingAnalysisV2Document document = new MarketingAnalysisV2Document();
|
||||
document.setUserId(userId);
|
||||
document.setBusinessNiche(request.getBusinessNiche());
|
||||
document.setProduct(request.getProduct());
|
||||
document.setTargetAudience(request.getTargetAudience());
|
||||
document.setRegion(request.getRegion());
|
||||
document.setGoal(request.getGoal());
|
||||
document.setDetailLevel(request.getDetailLevel());
|
||||
document.setStrongSide(request.getStrongSide());
|
||||
document.setWeakSide(request.getWeakSide());
|
||||
document.setAnalysisType(request.getAnalysisType());
|
||||
document.setAnalysisData(analysisData);
|
||||
|
||||
// Сохраняем в MongoDB
|
||||
MarketingAnalysisV2Document saved = v2Repository.save(document);
|
||||
logger.info("Saved marketing analysis v2 with id: {}", saved.getId());
|
||||
return saved.getId();
|
||||
} catch (Exception e) {
|
||||
logger.error("generateAndSaveAnalysisV2: Failed to generate and save analysis: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("Не удалось сохранить marketing analysis v2", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает сохраненный анализ v2 по ID
|
||||
*
|
||||
* @param id ID анализа
|
||||
* @return Документ анализа или null, если не найден
|
||||
*/
|
||||
public Optional<MarketingAnalysisV2Document> getAnalysisV2ById(String id) {
|
||||
return v2Repository.findById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет анализ в базе данных
|
||||
*
|
||||
* @param analysis Анализ для обновления
|
||||
* @return Обновленный анализ
|
||||
*/
|
||||
public MarketingAnalysis updateAnalysis(MarketingAnalysis analysis) {
|
||||
return repository.save(analysis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Связывает v2 анализ с обычным анализом
|
||||
*
|
||||
* @param v2AnalysisId ID v2 анализа
|
||||
* @param analysisId ID обычного анализа
|
||||
*/
|
||||
public void linkV2AnalysisToAnalysis(String v2AnalysisId, String analysisId) {
|
||||
Optional<MarketingAnalysisV2Document> optV2 = v2Repository.findById(v2AnalysisId);
|
||||
if (optV2.isPresent()) {
|
||||
MarketingAnalysisV2Document v2Doc = optV2.get();
|
||||
v2Doc.setAnalysisId(analysisId);
|
||||
v2Repository.save(v2Doc);
|
||||
logger.info("Linked v2 analysis {} to analysis {}", v2AnalysisId, analysisId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает v2 анализ по ID обычного анализа
|
||||
*
|
||||
* @param analysisId ID обычного анализа
|
||||
* @return Документ v2 анализа или null, если не найден
|
||||
*/
|
||||
public Optional<MarketingAnalysisV2Document> getV2AnalysisByAnalysisId(String analysisId) {
|
||||
return v2Repository.findByAnalysisId(analysisId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Example payload used ONLY for prompting the AI with the expected JSON
|
||||
* schema/shape.
|
||||
|
||||
Reference in New Issue
Block a user