my analysys
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
package kz.konturai.parser.config;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.bson.Document;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class MongoConfig {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MongoConfig.class);
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Bean
|
||||
public MongoCustomConversions customConversions() {
|
||||
List<Converter<?, ?>> converters = new ArrayList<>();
|
||||
converters.add(new StringToMapConverter());
|
||||
converters.add(new DocumentToMapConverter());
|
||||
converters.add(new MapToStringConverter());
|
||||
return new MongoCustomConversions(converters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертер для чтения: String -> Map<String, Object>
|
||||
* Обрабатывает случай, когда target_audience хранится как строка в MongoDB
|
||||
*/
|
||||
@ReadingConverter
|
||||
public static class StringToMapConverter implements Converter<String, Map<String, Object>> {
|
||||
@Override
|
||||
public Map<String, Object> convert(String source) {
|
||||
logger.debug("StringToMapConverter: Converting string to Map: {}", source);
|
||||
if (source == null || source.trim().isEmpty()) {
|
||||
logger.debug("StringToMapConverter: Source is null or empty, returning empty Map");
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
try {
|
||||
// Если это уже JSON строка, парсим её
|
||||
if (source.trim().startsWith("{") || source.trim().startsWith("[")) {
|
||||
Map<String, Object> result = objectMapper.readValue(source,
|
||||
new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
logger.debug("StringToMapConverter: Successfully parsed JSON string to Map");
|
||||
return result;
|
||||
}
|
||||
// Если это простая строка, создаём пустой Map
|
||||
logger.warn("StringToMapConverter: target_audience is a plain string, not JSON: {}", source);
|
||||
return new HashMap<>();
|
||||
} catch (Exception e) {
|
||||
logger.error("StringToMapConverter: Error converting string to Map for target_audience: {}", source, e);
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертер для чтения: Document -> Map<String, Object>
|
||||
* Обрабатывает случай, когда MongoDB возвращает Document вместо Map
|
||||
*/
|
||||
@ReadingConverter
|
||||
public static class DocumentToMapConverter implements Converter<Document, Map<String, Object>> {
|
||||
@Override
|
||||
public Map<String, Object> convert(Document source) {
|
||||
logger.debug("DocumentToMapConverter: Converting Document to Map");
|
||||
if (source == null) {
|
||||
logger.debug("DocumentToMapConverter: Source is null, returning empty Map");
|
||||
return new HashMap<>();
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
for (String key : source.keySet()) {
|
||||
Object value = source.get(key);
|
||||
// Рекурсивно конвертируем вложенные Document
|
||||
if (value instanceof Document) {
|
||||
result.put(key, convert((Document) value));
|
||||
} else {
|
||||
result.put(key, value);
|
||||
}
|
||||
}
|
||||
logger.debug("DocumentToMapConverter: Successfully converted Document to Map with {} keys", result.size());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертер для записи: Map<String, Object> -> String
|
||||
* Не используется, но может быть полезен для обратной совместимости
|
||||
*/
|
||||
@WritingConverter
|
||||
public static class MapToStringConverter implements Converter<Map<String, Object>, String> {
|
||||
@Override
|
||||
public String convert(Map<String, Object> source) {
|
||||
if (source == null || source.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.writeValueAsString(source);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error converting Map to String: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.InputStream;
|
||||
@@ -29,6 +31,8 @@ import java.util.stream.Collectors;
|
||||
@RequestMapping("/api/marketing/analysis")
|
||||
public class MarketingController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MarketingController.class);
|
||||
|
||||
private final MarketingAnalysisService marketingAnalysisService;
|
||||
private final MarketingStrategyService marketingStrategyService;
|
||||
private final MinIOService minIOService;
|
||||
@@ -50,9 +54,17 @@ public class MarketingController {
|
||||
|
||||
private String extractUserIdFromHeader(String authHeader) {
|
||||
if (authHeader == null || authHeader.isEmpty()) {
|
||||
logger.debug("Authorization header is null or empty");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String userId = jwtService.extractUserIdFromHeader(authHeader);
|
||||
logger.debug("Extracted userId from header: {}", userId);
|
||||
return userId;
|
||||
} catch (Exception e) {
|
||||
logger.error("Error extracting userId from header: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
return jwtService.extractUserIdFromHeader(authHeader);
|
||||
}
|
||||
|
||||
private ResponseEntity<?> unauthorizedResponse() {
|
||||
@@ -91,10 +103,14 @@ public class MarketingController {
|
||||
analysis.getId(),
|
||||
"processing",
|
||||
estimatedCompletion,
|
||||
"Анализ типа '" +
|
||||
(analysis.getAnalysisType() != null && !analysis.getAnalysisType().isEmpty()
|
||||
? analysis.getAnalysisType().get(0) : "неизвестно") +
|
||||
"' запущен успешно. Результаты будут готовы в течение 5-10 минут."))
|
||||
"Анализ типа '" +
|
||||
(analysis.getAnalysisType() != null && !analysis
|
||||
.getAnalysisType().isEmpty()
|
||||
? analysis.getAnalysisType()
|
||||
.get(0)
|
||||
: "неизвестно")
|
||||
+
|
||||
"' запущен успешно. Результаты будут готовы в течение 5-10 минут."))
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok(ApiResponse.success(
|
||||
"Создано анализов: " + analyses.size() + ". Все анализы запущены успешно.",
|
||||
@@ -327,17 +343,41 @@ public class MarketingController {
|
||||
public ResponseEntity<?> getMyAnalyses(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
try {
|
||||
logger.info("Getting analyses for user");
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
logger.warn("Unauthorized request to /my endpoint");
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
logger.debug("Fetching analyses for userId: {}", userId);
|
||||
List<MarketingAnalysis> analyses = marketingAnalysisService.getUserAnalyses(userId);
|
||||
logger.debug("Found {} analyses for userId: {}", analyses.size(), userId);
|
||||
|
||||
List<AnalysisHistoryResponse> responseList = analyses.stream()
|
||||
.map(analysis -> {
|
||||
try {
|
||||
return convertToHistoryResponse(analysis);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error converting analysis {} to history response: {}",
|
||||
analysis != null ? analysis.getId() : "null",
|
||||
e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
logger.info("Successfully retrieved {} analyses for userId: {}", responseList.size(), userId);
|
||||
return ResponseEntity.ok(ApiResponse.success(responseList));
|
||||
} catch (Exception e) {
|
||||
logger.error("Error in getMyAnalyses endpoint: {}", e.getMessage(), e);
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла внутренняя ошибка сервера. Попробуйте позже.");
|
||||
return ResponseEntity.status(500)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
|
||||
}
|
||||
|
||||
List<MarketingAnalysis> analyses = marketingAnalysisService.getUserAnalyses(userId);
|
||||
List<AnalysisHistoryResponse> responseList = analyses.stream()
|
||||
.map(this::convertToHistoryResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(responseList));
|
||||
}
|
||||
|
||||
@GetMapping("/strategy/my")
|
||||
@@ -422,11 +462,16 @@ public class MarketingController {
|
||||
}
|
||||
|
||||
private AnalysisHistoryResponse convertToHistoryResponse(MarketingAnalysis analysis) {
|
||||
if (analysis == null) {
|
||||
logger.error("Attempted to convert null analysis to history response");
|
||||
throw new IllegalArgumentException("Analysis cannot be null");
|
||||
}
|
||||
|
||||
AnalysisHistoryResponse response = new AnalysisHistoryResponse();
|
||||
response.setAnalysisId(analysis.getId());
|
||||
response.setBusinessNiche(analysis.getBusinessNiche());
|
||||
response.setProduct(analysis.getProduct());
|
||||
|
||||
|
||||
// Convert targetAudience Map to String representation
|
||||
Map<String, Object> targetAudienceMap = analysis.getTargetAudience();
|
||||
if (targetAudienceMap != null) {
|
||||
@@ -435,14 +480,16 @@ public class MarketingController {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> genders = (List<String>) targetAudienceMap.get("genders");
|
||||
if (genders != null && !genders.isEmpty()) {
|
||||
audienceBuilder.append("Гендер: ").append(String.join(", ", genders)).append(". ");
|
||||
audienceBuilder.append("Гендер: ").append(String.join(", ", genders))
|
||||
.append(". ");
|
||||
}
|
||||
}
|
||||
if (targetAudienceMap.containsKey("ageRanges")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> ageRanges = (List<String>) targetAudienceMap.get("ageRanges");
|
||||
if (ageRanges != null && !ageRanges.isEmpty()) {
|
||||
audienceBuilder.append("Возраст: ").append(String.join(", ", ageRanges)).append(". ");
|
||||
audienceBuilder.append("Возраст: ").append(String.join(", ", ageRanges))
|
||||
.append(". ");
|
||||
}
|
||||
}
|
||||
if (targetAudienceMap.containsKey("types")) {
|
||||
@@ -454,24 +501,24 @@ public class MarketingController {
|
||||
}
|
||||
response.setTargetAudience(audienceBuilder.toString().trim());
|
||||
}
|
||||
|
||||
|
||||
// Convert region List to String
|
||||
List<String> regions = analysis.getRegion();
|
||||
if (regions != null && !regions.isEmpty()) {
|
||||
response.setRegion(String.join(", ", regions));
|
||||
}
|
||||
|
||||
|
||||
response.setGoal(analysis.getGoal());
|
||||
response.setDetailLevel(analysis.getDetailLevel());
|
||||
response.setStrongSide(analysis.getStrongSide());
|
||||
response.setWeakSide(analysis.getWeakSide());
|
||||
|
||||
|
||||
// Convert analysisType List to String
|
||||
List<String> analysisTypes = analysis.getAnalysisType();
|
||||
if (analysisTypes != null && !analysisTypes.isEmpty()) {
|
||||
response.setAnalysisType(String.join(", ", analysisTypes));
|
||||
}
|
||||
|
||||
|
||||
response.setStatus(analysis.getStatus());
|
||||
response.setUserId(analysis.getUserId());
|
||||
response.setCreatedAt(analysis.getCreatedAt());
|
||||
@@ -593,6 +640,7 @@ public class MarketingController {
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleGenericException(Exception e) {
|
||||
logger.error("Unhandled exception in MarketingController: {}", e.getMessage(), e);
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла внутренняя ошибка сервера. Попробуйте позже.");
|
||||
|
||||
@@ -91,7 +91,8 @@ public class MarketingAnalysisService {
|
||||
logger.info("Starting marketing analysis processing for ID: {}", analysisId);
|
||||
|
||||
// Create a modified request with single analysis type for this analysis
|
||||
MarketingAnalysisRequest singleTypeRequest = createSingleTypeRequest(request, analysis.getAnalysisType().get(0));
|
||||
MarketingAnalysisRequest singleTypeRequest = createSingleTypeRequest(request,
|
||||
analysis.getAnalysisType().get(0));
|
||||
|
||||
// Generate marketing report
|
||||
Map<String, Object> reportData = generateMarketingReport(singleTypeRequest);
|
||||
@@ -136,7 +137,8 @@ public class MarketingAnalysisService {
|
||||
}
|
||||
}
|
||||
|
||||
private MarketingAnalysisRequest createSingleTypeRequest(MarketingAnalysisRequest originalRequest, String analysisType) {
|
||||
private MarketingAnalysisRequest createSingleTypeRequest(MarketingAnalysisRequest originalRequest,
|
||||
String analysisType) {
|
||||
MarketingAnalysisRequest singleTypeRequest = new MarketingAnalysisRequest();
|
||||
singleTypeRequest.setBusinessNiche(originalRequest.getBusinessNiche());
|
||||
singleTypeRequest.setProduct(originalRequest.getProduct());
|
||||
@@ -164,7 +166,7 @@ public class MarketingAnalysisService {
|
||||
StringBuilder contextBuilder = new StringBuilder();
|
||||
contextBuilder.append("Ниша бизнеса: ").append(request.getBusinessNiche()).append("\n");
|
||||
contextBuilder.append("Продукт/услуга: ").append(request.getProduct()).append("\n");
|
||||
|
||||
|
||||
// Format target audience
|
||||
Map<String, Object> targetAudienceData = request.getTargetAudience();
|
||||
if (targetAudienceData != null) {
|
||||
@@ -192,7 +194,7 @@ public class MarketingAnalysisService {
|
||||
}
|
||||
contextBuilder.append("Целевая аудитория: ").append(audienceBuilder.toString().trim()).append("\n");
|
||||
}
|
||||
|
||||
|
||||
// Format regions
|
||||
List<String> regions = request.getRegion();
|
||||
if (regions != null && !regions.isEmpty()) {
|
||||
@@ -454,7 +456,7 @@ public class MarketingAnalysisService {
|
||||
markdown.append("## Информация о бизнесе\n\n");
|
||||
markdown.append("- **Ниша бизнеса:** ").append(request.getBusinessNiche()).append("\n");
|
||||
markdown.append("- **Продукт/услуга:** ").append(request.getProduct()).append("\n");
|
||||
|
||||
|
||||
// Format target audience
|
||||
Map<String, Object> targetAudienceData = request.getTargetAudience();
|
||||
if (targetAudienceData != null) {
|
||||
@@ -482,13 +484,13 @@ public class MarketingAnalysisService {
|
||||
}
|
||||
markdown.append("- **Целевая аудитория:** ").append(audienceBuilder.toString().trim()).append("\n");
|
||||
}
|
||||
|
||||
|
||||
// Format regions
|
||||
List<String> regions = request.getRegion();
|
||||
if (regions != null && !regions.isEmpty()) {
|
||||
markdown.append("- **Регион:** ").append(String.join(", ", regions)).append("\n");
|
||||
}
|
||||
|
||||
|
||||
markdown.append("- **Цель на 6-12 месяцев:** ").append(request.getGoal()).append("\n");
|
||||
|
||||
String detailLevelName = (String) reportData.get("detailLevel");
|
||||
@@ -595,7 +597,15 @@ public class MarketingAnalysisService {
|
||||
}
|
||||
|
||||
public List<MarketingAnalysis> getUserAnalyses(String userId) {
|
||||
return repository.findByUserIdOrderByCreatedAtDesc(userId);
|
||||
logger.debug("Fetching analyses for userId: {}", userId);
|
||||
try {
|
||||
List<MarketingAnalysis> analyses = repository.findByUserIdOrderByCreatedAtDesc(userId);
|
||||
logger.debug("Found {} analyses for userId: {}", analyses.size(), userId);
|
||||
return analyses;
|
||||
} catch (Exception e) {
|
||||
logger.error("Error fetching analyses for userId {}: {}", userId, e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private MarketingAnalysisResult.MarketingReport buildReportFromData(Map<String, Object> reportData,
|
||||
|
||||
@@ -53,6 +53,10 @@ logging.level.org.springframework.data.mongodb=INFO
|
||||
logging.level.com.mongodb=WARN
|
||||
|
||||
logging.level.kz.konturai.parser.service.OllamaAnalyticsService=INFO
|
||||
logging.level.kz.konturai.parser.controller.MarketingController=DEBUG
|
||||
logging.level.kz.konturai.parser.service.MarketingAnalysisService=DEBUG
|
||||
logging.level.kz.konturai.parser.config.MongoConfig=DEBUG
|
||||
logging.level.org.springframework.data.mongodb.core.convert=DEBUG
|
||||
|
||||
# OpenAI Configuration
|
||||
openai.api.key=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
|
||||
|
||||
Reference in New Issue
Block a user