.
This commit is contained in:
@@ -29,512 +29,517 @@ import java.util.stream.Collectors;
|
||||
@RequestMapping("/api/marketing/analysis")
|
||||
public class MarketingController {
|
||||
|
||||
private final MarketingAnalysisService marketingAnalysisService;
|
||||
private final MarketingStrategyService marketingStrategyService;
|
||||
private final MinIOService minIOService;
|
||||
private final JwtService jwtService;
|
||||
private final PostingTaskService postingTaskService;
|
||||
private final MarketingAnalysisService marketingAnalysisService;
|
||||
private final MarketingStrategyService marketingStrategyService;
|
||||
private final MinIOService minIOService;
|
||||
private final JwtService jwtService;
|
||||
private final PostingTaskService postingTaskService;
|
||||
|
||||
public MarketingController(
|
||||
MarketingAnalysisService marketingAnalysisService,
|
||||
MarketingStrategyService marketingStrategyService,
|
||||
MinIOService minIOService,
|
||||
JwtService jwtService,
|
||||
PostingTaskService postingTaskService) {
|
||||
this.marketingAnalysisService = marketingAnalysisService;
|
||||
this.marketingStrategyService = marketingStrategyService;
|
||||
this.minIOService = minIOService;
|
||||
this.jwtService = jwtService;
|
||||
this.postingTaskService = postingTaskService;
|
||||
}
|
||||
|
||||
private String extractUserIdFromHeader(String authHeader) {
|
||||
if (authHeader == null || authHeader.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return jwtService.extractUserIdFromHeader(authHeader);
|
||||
}
|
||||
|
||||
private ResponseEntity<?> unauthorizedResponse() {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"UNAUTHORIZED",
|
||||
"Требуется аутентификация. Пожалуйста, предоставьте валидный JWT токен.");
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||
.body(ApiResponse.error("Не авторизован", error));
|
||||
}
|
||||
|
||||
@PostMapping("/start")
|
||||
public ResponseEntity<?> startAnalysis(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@Valid @RequestBody MarketingAnalysisRequest request) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
public MarketingController(
|
||||
MarketingAnalysisService marketingAnalysisService,
|
||||
MarketingStrategyService marketingStrategyService,
|
||||
MinIOService minIOService,
|
||||
JwtService jwtService,
|
||||
PostingTaskService postingTaskService) {
|
||||
this.marketingAnalysisService = marketingAnalysisService;
|
||||
this.marketingStrategyService = marketingStrategyService;
|
||||
this.minIOService = minIOService;
|
||||
this.jwtService = jwtService;
|
||||
this.postingTaskService = postingTaskService;
|
||||
}
|
||||
|
||||
// Create analysis record
|
||||
MarketingAnalysis analysis = marketingAnalysisService.startAnalysis(request, userId);
|
||||
|
||||
// Start async processing
|
||||
marketingAnalysisService.processAnalysis(analysis.getId(), request);
|
||||
|
||||
// Calculate estimated completion time (5-10 minutes)
|
||||
LocalDateTime estimatedCompletion = LocalDateTime.now().plusMinutes(8);
|
||||
|
||||
MarketingAnalysisResponse response = new MarketingAnalysisResponse(
|
||||
analysis.getId(),
|
||||
"processing",
|
||||
estimatedCompletion,
|
||||
"Анализ запущен успешно. Результаты будут готовы в течение 5-10 минут.");
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success("Анализ запущен успешно", response));
|
||||
}
|
||||
|
||||
@GetMapping("/{analysisId}")
|
||||
public ResponseEntity<?> getAnalysis(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String analysisId) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
private String extractUserIdFromHeader(String authHeader) {
|
||||
if (authHeader == null || authHeader.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return jwtService.extractUserIdFromHeader(authHeader);
|
||||
}
|
||||
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Анализ с указанным ID не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
private ResponseEntity<?> unauthorizedResponse() {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"UNAUTHORIZED",
|
||||
"Требуется аутентификация. Пожалуйста, предоставьте валидный JWT токен.");
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||
.body(ApiResponse.error("Не авторизован", error));
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
@PostMapping("/start")
|
||||
public ResponseEntity<?> startAnalysis(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@Valid @RequestBody MarketingAnalysisRequest request) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
// Create analysis record
|
||||
MarketingAnalysis analysis = marketingAnalysisService.startAnalysis(request, userId);
|
||||
|
||||
// Start async processing
|
||||
marketingAnalysisService.processAnalysis(analysis.getId(), request);
|
||||
|
||||
// Calculate estimated completion time (5-10 minutes)
|
||||
LocalDateTime estimatedCompletion = LocalDateTime.now().plusMinutes(8);
|
||||
|
||||
MarketingAnalysisResponse response = new MarketingAnalysisResponse(
|
||||
analysis.getId(),
|
||||
"processing",
|
||||
estimatedCompletion,
|
||||
"Анализ запущен успешно. Результаты будут готовы в течение 5-10 минут.");
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success("Анализ запущен успешно", response));
|
||||
}
|
||||
|
||||
MarketingAnalysisResult result = marketingAnalysisService.getAnalysisResult(analysisId);
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
@GetMapping("/{analysisId}")
|
||||
public ResponseEntity<?> getAnalysis(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String analysisId) {
|
||||
|
||||
@GetMapping("/{analysisId}/download")
|
||||
public ResponseEntity<?> downloadPdf(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String analysisId) {
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Анализ с указанным ID не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
MarketingAnalysisResult result = marketingAnalysisService.getAnalysisResult(analysisId);
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
@GetMapping("/{analysisId}/download")
|
||||
public ResponseEntity<?> downloadPdf(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String analysisId) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
if (analysis.getPdfFilePath() == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream inputStream = minIOService.downloadFile(analysis.getPdfFilePath());
|
||||
byte[] bytes = inputStream.readAllBytes();
|
||||
inputStream.close();
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + analysis.getPdfFilename() + "\"")
|
||||
.contentType(MediaType.APPLICATION_PDF)
|
||||
.body(bytes);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
@PostMapping("/strategy/generate")
|
||||
public ResponseEntity<?> generateStrategy(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@RequestParam String analysisId,
|
||||
@Valid @RequestBody(required = false) MarketingStrategyRequest request) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
// Check if user owns the analysis
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse("INVALID_ANALYSIS", "Анализ не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
if (request == null) {
|
||||
request = new MarketingStrategyRequest();
|
||||
}
|
||||
|
||||
try {
|
||||
MarketingStrategy strategy = marketingStrategyService.generateStrategy(analysisId, request,
|
||||
userId);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if (analysis.getPdfFilePath() == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
@GetMapping("/strategy/{strategyId}")
|
||||
public ResponseEntity<?> getStrategy(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String strategyId) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
Optional<MarketingStrategy> optStrategy = marketingStrategyService.getStrategyById(strategyId);
|
||||
if (optStrategy.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Стратегия с указанным ID не найдена");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Стратегия не найдена", error));
|
||||
}
|
||||
|
||||
MarketingStrategy strategy = optStrategy.get();
|
||||
if (!userId.equals(strategy.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этой стратегии");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
MarketingStrategyResponse result = marketingStrategyService.getStrategyResult(strategyId);
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream inputStream = minIOService.downloadFile(analysis.getPdfFilePath());
|
||||
byte[] bytes = inputStream.readAllBytes();
|
||||
inputStream.close();
|
||||
@GetMapping("/{analysisId}/strategy")
|
||||
public ResponseEntity<?> getStrategyByAnalysis(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String analysisId) {
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + analysis.getPdfFilename() + "\"")
|
||||
.contentType(MediaType.APPLICATION_PDF)
|
||||
.body(bytes);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
@PostMapping("/strategy/generate")
|
||||
public ResponseEntity<?> generateStrategy(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@RequestParam String analysisId,
|
||||
@Valid @RequestBody(required = false) MarketingStrategyRequest request) {
|
||||
// Check if user owns the analysis
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Анализ с указанным ID не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
}
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// Check if user owns the analysis
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse("INVALID_ANALYSIS", "Анализ не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
@GetMapping("/my")
|
||||
public ResponseEntity<?> getMyAnalyses(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
List<MarketingAnalysis> analyses = marketingAnalysisService.getUserAnalyses(userId);
|
||||
List<AnalysisHistoryResponse> responseList = analyses.stream()
|
||||
.map(this::convertToHistoryResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(responseList));
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
@GetMapping("/strategy/my")
|
||||
public ResponseEntity<?> getMyStrategies(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
List<MarketingStrategy> strategies = marketingStrategyService.getUserStrategies(userId);
|
||||
List<StrategyHistoryResponse> responseList = strategies.stream()
|
||||
.map(this::convertToStrategyHistoryResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(responseList));
|
||||
}
|
||||
|
||||
if (request == null) {
|
||||
request = new MarketingStrategyRequest();
|
||||
@GetMapping("/{analysisId}/history")
|
||||
public ResponseEntity<?> getAnalysisHistory(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String analysisId) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Анализ с указанным ID не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
AnalysisHistoryResponse response = convertToHistoryResponse(analysis);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
try {
|
||||
MarketingStrategy strategy = marketingStrategyService.generateStrategy(analysisId, request, userId);
|
||||
@GetMapping("/strategy/{strategyId}/history")
|
||||
public ResponseEntity<?> getStrategyHistory(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String strategyId) {
|
||||
|
||||
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());
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
Optional<MarketingStrategy> optStrategy = marketingStrategyService.getStrategyById(strategyId);
|
||||
if (optStrategy.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Стратегия с указанным ID не найдена");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Стратегия не найдена", error));
|
||||
}
|
||||
|
||||
@GetMapping("/strategy/{strategyId}")
|
||||
public ResponseEntity<?> getStrategy(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String strategyId) {
|
||||
MarketingStrategy strategy = optStrategy.get();
|
||||
if (!userId.equals(strategy.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этой стратегии");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
StrategyHistoryResponse response = convertToStrategyHistoryResponse(strategy);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
Optional<MarketingStrategy> optStrategy = marketingStrategyService.getStrategyById(strategyId);
|
||||
if (optStrategy.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Стратегия с указанным ID не найдена");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Стратегия не найдена", error));
|
||||
private AnalysisHistoryResponse convertToHistoryResponse(MarketingAnalysis analysis) {
|
||||
AnalysisHistoryResponse response = new AnalysisHistoryResponse();
|
||||
response.setAnalysisId(analysis.getId());
|
||||
response.setBusinessNiche(analysis.getBusinessNiche());
|
||||
response.setProduct(analysis.getProduct());
|
||||
response.setTargetAudience(analysis.getTargetAudience());
|
||||
response.setRegion(analysis.getRegion());
|
||||
response.setGoal(analysis.getGoal());
|
||||
response.setDetailLevel(analysis.getDetailLevel());
|
||||
response.setStrongSide(analysis.getStrongSide());
|
||||
response.setWeakSide(analysis.getWeakSide());
|
||||
response.setAnalysisType(analysis.getAnalysisType());
|
||||
response.setStatus(analysis.getStatus());
|
||||
response.setUserId(analysis.getUserId());
|
||||
response.setCreatedAt(analysis.getCreatedAt());
|
||||
response.setCompletedAt(analysis.getCompletedAt());
|
||||
response.setStatusHistory(analysis.getStatusHistory());
|
||||
return response;
|
||||
}
|
||||
|
||||
MarketingStrategy strategy = optStrategy.get();
|
||||
if (!userId.equals(strategy.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этой стратегии");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
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;
|
||||
}
|
||||
|
||||
MarketingStrategyResponse result = marketingStrategyService.getStrategyResult(strategyId);
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
@PostMapping("/strategy/{strategyId}/start")
|
||||
public ResponseEntity<?> startStrategy(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String strategyId,
|
||||
@RequestBody(required = false) StartStrategyRequest request) {
|
||||
|
||||
@GetMapping("/{analysisId}/strategy")
|
||||
public ResponseEntity<?> getStrategyByAnalysis(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String analysisId) {
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
// Проверяем существование стратегии и права доступа
|
||||
Optional<MarketingStrategy> optStrategy = marketingStrategyService.getStrategyById(strategyId);
|
||||
if (optStrategy.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Стратегия с указанным ID не найдена");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Стратегия не найдена", error));
|
||||
}
|
||||
|
||||
MarketingStrategy strategy = optStrategy.get();
|
||||
if (!userId.equals(strategy.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этой стратегии");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
if (!"completed".equals(strategy.getStatus())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INVALID_STATUS",
|
||||
"Стратегия еще не завершена. Статус: " + strategy.getStatus());
|
||||
return ResponseEntity.status(400)
|
||||
.body(ApiResponse.error("Стратегия не готова к запуску", error));
|
||||
}
|
||||
|
||||
try {
|
||||
// Создаем задачи из стратегии
|
||||
List<PostingTask> tasks = postingTaskService.createTasksFromStrategy(strategyId);
|
||||
|
||||
// Получаем список платформ
|
||||
List<String> platforms = tasks.stream()
|
||||
.map(PostingTask::getPlatform)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
StartStrategyResponse response = new StartStrategyResponse(
|
||||
strategyId,
|
||||
tasks.size(),
|
||||
platforms,
|
||||
"Стратегия успешно запущена. Создано задач: " + tasks.size());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(
|
||||
"Стратегия успешно запущена",
|
||||
response));
|
||||
|
||||
} catch (IllegalStateException e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"MISSING_CREDENTIALS",
|
||||
e.getMessage());
|
||||
return ResponseEntity.status(400)
|
||||
.body(ApiResponse.error("Не удалось запустить стратегию", error));
|
||||
} catch (IllegalArgumentException e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INVALID_STRATEGY",
|
||||
e.getMessage());
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Стратегия не найдена", error));
|
||||
} catch (Exception e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла ошибка при запуске стратегии");
|
||||
return ResponseEntity.status(500)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user owns the analysis
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Анализ с указанным ID не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleValidationException(
|
||||
MethodArgumentNotValidException ex) {
|
||||
|
||||
Map<String, String> details = new HashMap<>();
|
||||
ex.getBindingResult().getFieldErrors().forEach(error -> {
|
||||
details.put(error.getField(), error.getDefaultMessage());
|
||||
});
|
||||
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"VALIDATION_ERROR",
|
||||
"Ошибка валидации входных данных",
|
||||
details);
|
||||
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error("Ошибка валидации", errorResponse));
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleGenericException(Exception e) {
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла внутренняя ошибка сервера. Попробуйте позже.");
|
||||
|
||||
return ResponseEntity.status(500)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", errorResponse));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@GetMapping("/my")
|
||||
public ResponseEntity<?> getMyAnalyses(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
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")
|
||||
public ResponseEntity<?> getMyStrategies(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
List<MarketingStrategy> strategies = marketingStrategyService.getUserStrategies(userId);
|
||||
List<StrategyHistoryResponse> responseList = strategies.stream()
|
||||
.map(this::convertToStrategyHistoryResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(responseList));
|
||||
}
|
||||
|
||||
@GetMapping("/{analysisId}/history")
|
||||
public ResponseEntity<?> getAnalysisHistory(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String analysisId) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
Optional<MarketingAnalysis> optAnalysis = marketingAnalysisService.getAnalysisById(analysisId);
|
||||
if (optAnalysis.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Анализ с указанным ID не найден");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Анализ не найден", error));
|
||||
}
|
||||
|
||||
MarketingAnalysis analysis = optAnalysis.get();
|
||||
if (!userId.equals(analysis.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этому анализу");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
AnalysisHistoryResponse response = convertToHistoryResponse(analysis);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
@GetMapping("/strategy/{strategyId}/history")
|
||||
public ResponseEntity<?> getStrategyHistory(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String strategyId) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
Optional<MarketingStrategy> optStrategy = marketingStrategyService.getStrategyById(strategyId);
|
||||
if (optStrategy.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Стратегия с указанным ID не найдена");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Стратегия не найдена", error));
|
||||
}
|
||||
|
||||
MarketingStrategy strategy = optStrategy.get();
|
||||
if (!userId.equals(strategy.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этой стратегии");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
StrategyHistoryResponse response = convertToStrategyHistoryResponse(strategy);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
private AnalysisHistoryResponse convertToHistoryResponse(MarketingAnalysis analysis) {
|
||||
AnalysisHistoryResponse response = new AnalysisHistoryResponse();
|
||||
response.setAnalysisId(analysis.getId());
|
||||
response.setProduct(analysis.getProduct());
|
||||
response.setLocation(analysis.getLocation());
|
||||
response.setClientType(analysis.getClientType());
|
||||
response.setDifferentiator(analysis.getDifferentiator());
|
||||
response.setAnalysisType(analysis.getAnalysisType());
|
||||
response.setStatus(analysis.getStatus());
|
||||
response.setUserId(analysis.getUserId());
|
||||
response.setCreatedAt(analysis.getCreatedAt());
|
||||
response.setCompletedAt(analysis.getCompletedAt());
|
||||
response.setStatusHistory(analysis.getStatusHistory());
|
||||
return response;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@PostMapping("/strategy/{strategyId}/start")
|
||||
public ResponseEntity<?> startStrategy(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String strategyId,
|
||||
@RequestBody(required = false) StartStrategyRequest request) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
// Проверяем существование стратегии и права доступа
|
||||
Optional<MarketingStrategy> optStrategy = marketingStrategyService.getStrategyById(strategyId);
|
||||
if (optStrategy.isEmpty()) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Стратегия с указанным ID не найдена");
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Стратегия не найдена", error));
|
||||
}
|
||||
|
||||
MarketingStrategy strategy = optStrategy.get();
|
||||
if (!userId.equals(strategy.getUserId())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"FORBIDDEN",
|
||||
"У вас нет доступа к этой стратегии");
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error("Доступ запрещен", error));
|
||||
}
|
||||
|
||||
if (!"completed".equals(strategy.getStatus())) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INVALID_STATUS",
|
||||
"Стратегия еще не завершена. Статус: " + strategy.getStatus());
|
||||
return ResponseEntity.status(400)
|
||||
.body(ApiResponse.error("Стратегия не готова к запуску", error));
|
||||
}
|
||||
|
||||
try {
|
||||
// Создаем задачи из стратегии
|
||||
List<PostingTask> tasks = postingTaskService.createTasksFromStrategy(strategyId);
|
||||
|
||||
// Получаем список платформ
|
||||
List<String> platforms = tasks.stream()
|
||||
.map(PostingTask::getPlatform)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
StartStrategyResponse response = new StartStrategyResponse(
|
||||
strategyId,
|
||||
tasks.size(),
|
||||
platforms,
|
||||
"Стратегия успешно запущена. Создано задач: " + tasks.size());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(
|
||||
"Стратегия успешно запущена",
|
||||
response));
|
||||
|
||||
} catch (IllegalStateException e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"MISSING_CREDENTIALS",
|
||||
e.getMessage());
|
||||
return ResponseEntity.status(400)
|
||||
.body(ApiResponse.error("Не удалось запустить стратегию", error));
|
||||
} catch (IllegalArgumentException e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INVALID_STRATEGY",
|
||||
e.getMessage());
|
||||
return ResponseEntity.status(404)
|
||||
.body(ApiResponse.error("Стратегия не найдена", error));
|
||||
} catch (Exception e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла ошибка при запуске стратегии");
|
||||
return ResponseEntity.status(500)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleValidationException(
|
||||
MethodArgumentNotValidException ex) {
|
||||
|
||||
Map<String, String> details = new HashMap<>();
|
||||
ex.getBindingResult().getFieldErrors().forEach(error -> {
|
||||
details.put(error.getField(), error.getDefaultMessage());
|
||||
});
|
||||
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"VALIDATION_ERROR",
|
||||
"Ошибка валидации входных данных",
|
||||
details);
|
||||
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error("Ошибка валидации", errorResponse));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleGenericException(Exception e) {
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла внутренняя ошибка сервера. Попробуйте позже.");
|
||||
|
||||
return ResponseEntity.status(500)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", errorResponse));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,14 @@ import java.util.List;
|
||||
|
||||
public class AnalysisHistoryResponse {
|
||||
private String analysisId;
|
||||
private String businessNiche;
|
||||
private String product;
|
||||
private String location;
|
||||
private String clientType;
|
||||
private String differentiator;
|
||||
private String targetAudience;
|
||||
private String region;
|
||||
private String goal;
|
||||
private String detailLevel;
|
||||
private String strongSide;
|
||||
private String weakSide;
|
||||
private String analysisType;
|
||||
private String status;
|
||||
private String userId;
|
||||
@@ -27,6 +31,14 @@ public class AnalysisHistoryResponse {
|
||||
this.analysisId = analysisId;
|
||||
}
|
||||
|
||||
public String getBusinessNiche() {
|
||||
return businessNiche;
|
||||
}
|
||||
|
||||
public void setBusinessNiche(String businessNiche) {
|
||||
this.businessNiche = businessNiche;
|
||||
}
|
||||
|
||||
public String getProduct() {
|
||||
return product;
|
||||
}
|
||||
@@ -35,28 +47,52 @@ public class AnalysisHistoryResponse {
|
||||
this.product = product;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
public String getTargetAudience() {
|
||||
return targetAudience;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
public void setTargetAudience(String targetAudience) {
|
||||
this.targetAudience = targetAudience;
|
||||
}
|
||||
|
||||
public String getClientType() {
|
||||
return clientType;
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setClientType(String clientType) {
|
||||
this.clientType = clientType;
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getDifferentiator() {
|
||||
return differentiator;
|
||||
public String getGoal() {
|
||||
return goal;
|
||||
}
|
||||
|
||||
public void setDifferentiator(String differentiator) {
|
||||
this.differentiator = differentiator;
|
||||
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 String getStatus() {
|
||||
|
||||
@@ -4,28 +4,44 @@ import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import kz.konturai.parser.model.AnalysisType;
|
||||
import kz.konturai.parser.model.DetailLevel;
|
||||
import kz.konturai.parser.validator.ValidAnalysisType;
|
||||
import kz.konturai.parser.validator.ValidClientType;
|
||||
import kz.konturai.parser.validator.ValidDetailLevel;
|
||||
import kz.konturai.parser.validator.ValidRegion;
|
||||
|
||||
public class MarketingAnalysisRequest {
|
||||
|
||||
@NotBlank(message = "Поле 'businessNiche' обязательно для заполнения")
|
||||
@Size(min = 3, max = 200, message = "Поле 'businessNiche' должно содержать от 3 до 200 символов")
|
||||
@Pattern(regexp = "^[\\p{L}\\p{N}\\s\\-,]+$", message = "Поле 'businessNiche' содержит недопустимые символы")
|
||||
private String businessNiche;
|
||||
|
||||
@NotBlank(message = "Поле 'product' обязательно для заполнения")
|
||||
@Size(min = 3, max = 200, message = "Поле 'product' должно содержать от 3 до 200 символов")
|
||||
@Pattern(regexp = "^[\\p{L}\\p{N}\\s\\-,]+$", message = "Поле 'product' содержит недопустимые символы")
|
||||
private String product;
|
||||
|
||||
@NotBlank(message = "Поле 'location' обязательно для заполнения")
|
||||
@Size(min = 2, max = 150, message = "Поле 'location' должно содержать от 2 до 150 символов")
|
||||
@Pattern(regexp = "^[\\p{L}\\p{N}\\s\\-,]+$", message = "Поле 'location' содержит недопустимые символы")
|
||||
private String location;
|
||||
@NotBlank(message = "Поле 'targetAudience' обязательно для заполнения")
|
||||
@Size(min = 3, max = 300, message = "Поле 'targetAudience' должно содержать от 3 до 300 символов")
|
||||
private String targetAudience;
|
||||
|
||||
@NotBlank(message = "Поле 'client' обязательно для заполнения")
|
||||
@ValidClientType(message = "Поле 'client' должно быть одним из: B2B клиенты, B2C клиенты, Частные лица, Корпорации, Малый бизнес")
|
||||
private String client;
|
||||
@NotBlank(message = "Поле 'region' обязательно для заполнения")
|
||||
@ValidRegion(message = "Поле 'region' должно быть одним из допустимых городов Казахстана")
|
||||
private String region;
|
||||
|
||||
@NotBlank(message = "Поле 'differentiator' обязательно для заполнения")
|
||||
@Size(min = 10, max = 500, message = "Поле 'differentiator' должно содержать от 10 до 500 символов")
|
||||
private String differentiator;
|
||||
@NotBlank(message = "Поле 'goal' обязательно для заполнения")
|
||||
@Size(min = 10, max = 500, message = "Поле 'goal' должно содержать от 10 до 500 символов")
|
||||
private String goal;
|
||||
|
||||
@NotBlank(message = "Поле 'detailLevel' обязательно для заполнения")
|
||||
@ValidDetailLevel(message = "Поле 'detailLevel' должно быть одним из: КРАТКО, СТАНДАРТНО, ПОДРОБНО")
|
||||
private String detailLevel;
|
||||
|
||||
@Size(max = 500, message = "Поле 'strongSide' должно содержать не более 500 символов")
|
||||
private String strongSide;
|
||||
|
||||
@Size(max = 500, message = "Поле 'weakSide' должно содержать не более 500 символов")
|
||||
private String weakSide;
|
||||
|
||||
@NotBlank(message = "Поле 'analysisType' обязательно для заполнения")
|
||||
@ValidAnalysisType(message = "Поле 'analysisType' должно быть одним из: РЫНОК, КОНКУРЕНТЫ, ЦА, КАНАЛЫ, SWOT")
|
||||
@@ -34,15 +50,27 @@ public class MarketingAnalysisRequest {
|
||||
public MarketingAnalysisRequest() {
|
||||
}
|
||||
|
||||
public MarketingAnalysisRequest(String product, String location, String client, String differentiator,
|
||||
String analysisType) {
|
||||
public MarketingAnalysisRequest(String businessNiche, String product, String targetAudience, String region,
|
||||
String goal, String detailLevel, String strongSide, String weakSide, String analysisType) {
|
||||
this.businessNiche = businessNiche;
|
||||
this.product = product;
|
||||
this.location = location;
|
||||
this.client = client;
|
||||
this.differentiator = differentiator;
|
||||
this.targetAudience = targetAudience;
|
||||
this.region = region;
|
||||
this.goal = goal;
|
||||
this.detailLevel = detailLevel;
|
||||
this.strongSide = strongSide;
|
||||
this.weakSide = weakSide;
|
||||
this.analysisType = analysisType;
|
||||
}
|
||||
|
||||
public String getBusinessNiche() {
|
||||
return businessNiche;
|
||||
}
|
||||
|
||||
public void setBusinessNiche(String businessNiche) {
|
||||
this.businessNiche = businessNiche;
|
||||
}
|
||||
|
||||
public String getProduct() {
|
||||
return product;
|
||||
}
|
||||
@@ -51,28 +79,56 @@ public class MarketingAnalysisRequest {
|
||||
this.product = product;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
public String getTargetAudience() {
|
||||
return targetAudience;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
public void setTargetAudience(String targetAudience) {
|
||||
this.targetAudience = targetAudience;
|
||||
}
|
||||
|
||||
public String getClient() {
|
||||
return client;
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setClient(String client) {
|
||||
this.client = client;
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getDifferentiator() {
|
||||
return differentiator;
|
||||
public String getGoal() {
|
||||
return goal;
|
||||
}
|
||||
|
||||
public void setDifferentiator(String differentiator) {
|
||||
this.differentiator = differentiator;
|
||||
public void setGoal(String goal) {
|
||||
this.goal = goal;
|
||||
}
|
||||
|
||||
public String getDetailLevel() {
|
||||
return detailLevel;
|
||||
}
|
||||
|
||||
public void setDetailLevel(String detailLevel) {
|
||||
this.detailLevel = detailLevel;
|
||||
}
|
||||
|
||||
public DetailLevel getDetailLevelEnum() {
|
||||
return DetailLevel.fromString(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 String getAnalysisType() {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package kz.konturai.parser.model;
|
||||
|
||||
public enum DetailLevel {
|
||||
КРАТКО("Кратко"),
|
||||
СТАНДАРТНО("Стандартно"),
|
||||
ПОДРОБНО("Подробно");
|
||||
|
||||
private final String displayName;
|
||||
|
||||
DetailLevel(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public static DetailLevel fromString(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DetailLevel.valueOf(value.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Try to match by display name
|
||||
for (DetailLevel level : DetailLevel.values()) {
|
||||
if (level.getDisplayName().equalsIgnoreCase(value) ||
|
||||
level.name().equalsIgnoreCase(value)) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,29 @@ public class MarketingAnalysis {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Field("business_niche")
|
||||
private String businessNiche;
|
||||
|
||||
@Field("product")
|
||||
private String product;
|
||||
|
||||
@Field("location")
|
||||
private String location;
|
||||
@Field("target_audience")
|
||||
private String targetAudience;
|
||||
|
||||
@Field("client_type")
|
||||
private String clientType;
|
||||
@Field("region")
|
||||
private String region;
|
||||
|
||||
@Field("differentiator")
|
||||
private String differentiator;
|
||||
@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 String analysisType; // РЫНОК, КОНКУРЕНТЫ, ЦА, КАНАЛЫ, SWOT
|
||||
@@ -61,21 +73,17 @@ public class MarketingAnalysis {
|
||||
this.statusHistory = new ArrayList<>();
|
||||
}
|
||||
|
||||
public MarketingAnalysis(String product, String location, String clientType, String differentiator) {
|
||||
public MarketingAnalysis(String businessNiche, String product, String targetAudience, String region,
|
||||
String goal, String detailLevel, String strongSide, String weakSide, String analysisType) {
|
||||
this();
|
||||
this.businessNiche = businessNiche;
|
||||
this.product = product;
|
||||
this.location = location;
|
||||
this.clientType = clientType;
|
||||
this.differentiator = differentiator;
|
||||
}
|
||||
|
||||
public MarketingAnalysis(String product, String location, String clientType, String differentiator,
|
||||
String analysisType) {
|
||||
this();
|
||||
this.product = product;
|
||||
this.location = location;
|
||||
this.clientType = clientType;
|
||||
this.differentiator = differentiator;
|
||||
this.targetAudience = targetAudience;
|
||||
this.region = region;
|
||||
this.goal = goal;
|
||||
this.detailLevel = detailLevel;
|
||||
this.strongSide = strongSide;
|
||||
this.weakSide = weakSide;
|
||||
this.analysisType = analysisType;
|
||||
}
|
||||
|
||||
@@ -95,28 +103,60 @@ public class MarketingAnalysis {
|
||||
this.product = product;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
public String getBusinessNiche() {
|
||||
return businessNiche;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
public void setBusinessNiche(String businessNiche) {
|
||||
this.businessNiche = businessNiche;
|
||||
}
|
||||
|
||||
public String getClientType() {
|
||||
return clientType;
|
||||
public String getTargetAudience() {
|
||||
return targetAudience;
|
||||
}
|
||||
|
||||
public void setClientType(String clientType) {
|
||||
this.clientType = clientType;
|
||||
public void setTargetAudience(String targetAudience) {
|
||||
this.targetAudience = targetAudience;
|
||||
}
|
||||
|
||||
public String getDifferentiator() {
|
||||
return differentiator;
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setDifferentiator(String differentiator) {
|
||||
this.differentiator = differentiator;
|
||||
public void setRegion(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 String getStatus() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import kz.konturai.parser.dto.MarketingAnalysisRequest;
|
||||
import kz.konturai.parser.dto.MarketingAnalysisResult;
|
||||
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.repository.MarketingAnalysisRepository;
|
||||
import org.slf4j.Logger;
|
||||
@@ -38,10 +39,14 @@ public class MarketingAnalysisService {
|
||||
|
||||
public MarketingAnalysis startAnalysis(MarketingAnalysisRequest request, String userId) {
|
||||
MarketingAnalysis analysis = new MarketingAnalysis(
|
||||
request.getBusinessNiche(),
|
||||
request.getProduct(),
|
||||
request.getLocation(),
|
||||
request.getClient(),
|
||||
request.getDifferentiator(),
|
||||
request.getTargetAudience(),
|
||||
request.getRegion(),
|
||||
request.getGoal(),
|
||||
request.getDetailLevel(),
|
||||
request.getStrongSide(),
|
||||
request.getWeakSide(),
|
||||
request.getAnalysisType());
|
||||
analysis.setUserId(userId);
|
||||
analysis.setStatus("queued");
|
||||
@@ -122,32 +127,52 @@ public class MarketingAnalysisService {
|
||||
private Map<String, Object> generateMarketingReport(MarketingAnalysisRequest request) {
|
||||
Map<String, Object> report = new HashMap<>();
|
||||
|
||||
// Get analysis type
|
||||
// Get analysis type and detail level
|
||||
AnalysisType analysisType = request.getAnalysisTypeEnum();
|
||||
String analysisTypeName = analysisType != null ? analysisType.getDisplayName() : "Общий";
|
||||
DetailLevel detailLevel = request.getDetailLevelEnum();
|
||||
String detailLevelName = detailLevel != null ? detailLevel.getDisplayName() : "Стандартно";
|
||||
|
||||
// Build context for AI
|
||||
String context = String.format(
|
||||
"Продукт/услуга: %s\n" +
|
||||
"Локация: %s\n" +
|
||||
"Тип клиентов: %s\n" +
|
||||
"Уникальные особенности: %s\n" +
|
||||
"Тип анализа: %s\n",
|
||||
request.getProduct(),
|
||||
request.getLocation(),
|
||||
request.getClient(),
|
||||
request.getDifferentiator(),
|
||||
analysisTypeName);
|
||||
// Build context for AI with all new fields
|
||||
StringBuilder contextBuilder = new StringBuilder();
|
||||
contextBuilder.append("Ниша бизнеса: ").append(request.getBusinessNiche()).append("\n");
|
||||
contextBuilder.append("Продукт/услуга: ").append(request.getProduct()).append("\n");
|
||||
contextBuilder.append("Целевая аудитория: ").append(request.getTargetAudience()).append("\n");
|
||||
contextBuilder.append("Регион: ").append(request.getRegion()).append("\n");
|
||||
contextBuilder.append("Цель на 6-12 месяцев: ").append(request.getGoal()).append("\n");
|
||||
contextBuilder.append("Уровень детализации: ").append(detailLevelName).append("\n");
|
||||
if (request.getStrongSide() != null && !request.getStrongSide().trim().isEmpty()) {
|
||||
contextBuilder.append("Сильная сторона: ").append(request.getStrongSide()).append("\n");
|
||||
}
|
||||
if (request.getWeakSide() != null && !request.getWeakSide().trim().isEmpty()) {
|
||||
contextBuilder.append("Слабая сторона: ").append(request.getWeakSide()).append("\n");
|
||||
}
|
||||
contextBuilder.append("Тип анализа: ").append(analysisTypeName).append("\n");
|
||||
String context = contextBuilder.toString();
|
||||
|
||||
// Generate analysis based on type
|
||||
String analysisContent = generateAnalysisByType(context, analysisType);
|
||||
// Generate analysis based on type and detail level
|
||||
String analysisContent = generateAnalysisByType(context, analysisType, detailLevel);
|
||||
report.put("analysisType", analysisTypeName);
|
||||
report.put("detailLevel", detailLevelName);
|
||||
report.put("summary", analysisContent != null ? analysisContent : "Анализ не удалось сгенерировать.");
|
||||
|
||||
// Generate target audience description
|
||||
String audiencePrompt = "На основе информации о бизнесе опиши целевую аудиторию (1-2 абзаца). " +
|
||||
"Укажи, какие каналы коммуникации наиболее подходят для этой аудитории. " +
|
||||
"Ответ должен быть на русском языке.\n\n" + context;
|
||||
// Generate target audience description (adjusted by detail level)
|
||||
String audiencePrompt;
|
||||
if (detailLevel == DetailLevel.КРАТКО) {
|
||||
audiencePrompt = "На основе информации о бизнесе опиши целевую аудиторию кратко (1 абзац). " +
|
||||
"Укажи основные каналы коммуникации. Ответ должен быть на русском языке.\n\n" + context;
|
||||
} else if (detailLevel == DetailLevel.ПОДРОБНО) {
|
||||
audiencePrompt = "На основе информации о бизнесе проведи детальный анализ целевой аудитории (3-4 абзаца). "
|
||||
+
|
||||
"Включи демографические характеристики, психографический профиль, потребности, поведенческие паттерны. "
|
||||
+
|
||||
"Укажи, какие каналы коммуникации наиболее подходят для этой аудитории и почему. " +
|
||||
"Ответ должен быть на русском языке.\n\n" + context;
|
||||
} else {
|
||||
audiencePrompt = "На основе информации о бизнесе опиши целевую аудиторию (1-2 абзаца). " +
|
||||
"Укажи, какие каналы коммуникации наиболее подходят для этой аудитории. " +
|
||||
"Ответ должен быть на русском языке.\n\n" + context;
|
||||
}
|
||||
String audienceDescription = openAIAnalyticsService.generateWithInstruction(context, audiencePrompt, "ru");
|
||||
|
||||
// Extract channels from description or generate separately
|
||||
@@ -164,22 +189,50 @@ public class MarketingAnalysisService {
|
||||
channels.isEmpty() ? Arrays.asList("Instagram", "LinkedIn", "Telegram") : channels);
|
||||
report.put("targetAudience", targetAudience);
|
||||
|
||||
// Generate recommendations
|
||||
String recommendationsPrompt = "На основе информации о бизнесе сформулируй 4-6 практических рекомендаций для маркетинговой стратегии. "
|
||||
+
|
||||
"Каждая рекомендация должна быть конкретной и применимой. " +
|
||||
"Ответ должен быть списком рекомендаций, каждая с новой строки, без нумерации.\n\n" + context;
|
||||
// Generate recommendations (adjusted by detail level)
|
||||
String recommendationsPrompt;
|
||||
if (detailLevel == DetailLevel.КРАТКО) {
|
||||
recommendationsPrompt = "На основе информации о бизнесе сформулируй 3-4 краткие практические рекомендации для маркетинговой стратегии. "
|
||||
+
|
||||
"Каждая рекомендация должна быть конкретной. " +
|
||||
"Ответ должен быть списком рекомендаций, каждая с новой строки, без нумерации.\n\n" + context;
|
||||
} else if (detailLevel == DetailLevel.ПОДРОБНО) {
|
||||
recommendationsPrompt = "На основе информации о бизнесе сформулируй 6-8 детальных практических рекомендаций для маркетинговой стратегии. "
|
||||
+
|
||||
"Каждая рекомендация должна быть конкретной, применимой и содержать обоснование. " +
|
||||
"Ответ должен быть списком рекомендаций, каждая с новой строки, без нумерации.\n\n" + context;
|
||||
} else {
|
||||
recommendationsPrompt = "На основе информации о бизнесе сформулируй 4-6 практических рекомендаций для маркетинговой стратегии. "
|
||||
+
|
||||
"Каждая рекомендация должна быть конкретной и применимой. " +
|
||||
"Ответ должен быть списком рекомендаций, каждая с новой строки, без нумерации.\n\n" + context;
|
||||
}
|
||||
String recommendationsStr = openAIAnalyticsService.generateWithInstruction(context, recommendationsPrompt,
|
||||
"ru");
|
||||
List<String> recommendations = parseRecommendations(recommendationsStr);
|
||||
report.put("recommendations",
|
||||
recommendations.isEmpty() ? Arrays.asList("Рекомендации не удалось сгенерировать.") : recommendations);
|
||||
|
||||
// Generate strategy
|
||||
String strategyPrompt = "На основе информации о бизнесе создай краткую маркетинговую стратегию. " +
|
||||
"Укажи рекомендуемую длительность кампании (например, '2 недели', '1 месяц'), " +
|
||||
"3-5 каналов коммуникации и типы контента (например, 'посты', 'сторис', 'баннеры'). " +
|
||||
"Ответ должен быть структурированным текстом на русском языке.\n\n" + context;
|
||||
// Generate strategy (adjusted by detail level)
|
||||
String strategyPrompt;
|
||||
if (detailLevel == DetailLevel.КРАТКО) {
|
||||
strategyPrompt = "На основе информации о бизнесе создай краткую маркетинговую стратегию. " +
|
||||
"Укажи рекомендуемую длительность кампании (например, '2 недели', '1 месяц'), " +
|
||||
"3-4 канала коммуникации и типы контента (например, 'посты', 'сторис', 'баннеры'). " +
|
||||
"Ответ должен быть кратким структурированным текстом на русском языке.\n\n" + context;
|
||||
} else if (detailLevel == DetailLevel.ПОДРОБНО) {
|
||||
strategyPrompt = "На основе информации о бизнесе создай детальную маркетинговую стратегию. " +
|
||||
"Укажи рекомендуемую длительность кампании с обоснованием (например, '2 недели', '1 месяц'), " +
|
||||
"5-7 каналов коммуникации с описанием их преимуществ, типы контента (например, 'посты', 'сторис', 'баннеры') "
|
||||
+
|
||||
"и примерный план действий. Ответ должен быть подробным структурированным текстом на русском языке.\n\n"
|
||||
+ context;
|
||||
} else {
|
||||
strategyPrompt = "На основе информации о бизнесе создай маркетинговую стратегию. " +
|
||||
"Укажи рекомендуемую длительность кампании (например, '2 недели', '1 месяц'), " +
|
||||
"3-5 каналов коммуникации и типы контента (например, 'посты', 'сторис', 'баннеры'). " +
|
||||
"Ответ должен быть структурированным текстом на русском языке.\n\n" + context;
|
||||
}
|
||||
String strategyText = openAIAnalyticsService.generateWithInstruction(context, strategyPrompt, "ru");
|
||||
|
||||
Map<String, Object> strategy = parseStrategy(strategyText, channels);
|
||||
@@ -188,49 +241,60 @@ public class MarketingAnalysisService {
|
||||
return report;
|
||||
}
|
||||
|
||||
private String generateAnalysisByType(String context, AnalysisType analysisType) {
|
||||
private String generateAnalysisByType(String context, AnalysisType analysisType, DetailLevel detailLevel) {
|
||||
if (analysisType == null) {
|
||||
analysisType = AnalysisType.РЫНОК; // Default
|
||||
}
|
||||
if (detailLevel == null) {
|
||||
detailLevel = DetailLevel.СТАНДАРТНО; // Default
|
||||
}
|
||||
|
||||
String prompt;
|
||||
String lengthInstruction;
|
||||
|
||||
// Adjust length based on detail level
|
||||
if (detailLevel == DetailLevel.КРАТКО) {
|
||||
lengthInstruction = "Ответ должен быть кратким структурированным текстом на русском языке (1-2 абзаца).";
|
||||
} else if (detailLevel == DetailLevel.ПОДРОБНО) {
|
||||
lengthInstruction = "Ответ должен быть подробным структурированным текстом на русском языке (5-8 абзацев) с глубоким анализом.";
|
||||
} else {
|
||||
lengthInstruction = "Ответ должен быть структурированным текстом на русском языке (3-5 абзацев).";
|
||||
}
|
||||
|
||||
switch (analysisType) {
|
||||
case РЫНОК:
|
||||
prompt = "Проведи детальный анализ рынка на основе предоставленной информации о бизнесе. " +
|
||||
prompt = "Проведи анализ рынка на основе предоставленной информации о бизнесе. " +
|
||||
"Включи: размер рынка, динамику роста, основные сегменты, тренды и перспективы развития. " +
|
||||
"Ответ должен быть структурированным текстом на русском языке (3-5 абзацев).\n\n" + context;
|
||||
lengthInstruction + "\n\n" + context;
|
||||
break;
|
||||
case КОНКУРЕНТЫ:
|
||||
prompt = "Проведи анализ конкурентов на основе предоставленной информации о бизнесе. " +
|
||||
"Включи: основных конкурентов, их сильные и слабые стороны, позиционирование, " +
|
||||
"ценовую политику и маркетинговые стратегии. Ответ должен быть структурированным текстом на русском языке (3-5 абзацев).\n\n"
|
||||
+ context;
|
||||
"ценовую политику и маркетинговые стратегии. " + lengthInstruction + "\n\n" + context;
|
||||
break;
|
||||
case ЦА:
|
||||
prompt = "Проведи детальный анализ целевой аудитории на основе предоставленной информации о бизнесе. " +
|
||||
prompt = "Проведи анализ целевой аудитории на основе предоставленной информации о бизнесе. " +
|
||||
"Включи: демографические характеристики, психографический профиль, потребности и боли, " +
|
||||
"поведенческие паттерны и предпочтения. Ответ должен быть структурированным текстом на русском языке (3-5 абзацев).\n\n"
|
||||
+ context;
|
||||
"поведенческие паттерны и предпочтения. " + lengthInstruction + "\n\n" + context;
|
||||
break;
|
||||
case КАНАЛЫ:
|
||||
prompt = "Проведи анализ маркетинговых каналов на основе предоставленной информации о бизнесе. " +
|
||||
"Включи: оценку эффективности различных каналов коммуникации, рекомендации по выбору каналов, "
|
||||
+
|
||||
"особенности использования каждого канала и бюджетные рекомендации. Ответ должен быть структурированным текстом на русском языке (3-5 абзацев).\n\n"
|
||||
+ context;
|
||||
"особенности использования каждого канала и бюджетные рекомендации. " + lengthInstruction
|
||||
+ "\n\n" + context;
|
||||
break;
|
||||
case SWOT:
|
||||
prompt = "Проведи SWOT-анализ на основе предоставленной информации о бизнесе. " +
|
||||
"Включи детальный анализ: Сильных сторон (Strengths), Слабых сторон (Weaknesses), " +
|
||||
"Возможностей (Opportunities) и Угроз (Threats). Ответ должен быть структурированным текстом на русском языке, "
|
||||
+
|
||||
"с четким разделением по каждому разделу SWOT (3-5 абзацев).\n\n" + context;
|
||||
"Включи анализ: Сильных сторон (Strengths), Слабых сторон (Weaknesses), " +
|
||||
"Возможностей (Opportunities) и Угроз (Threats). " +
|
||||
"Ответ должен быть структурированным текстом на русском языке, " +
|
||||
"с четким разделением по каждому разделу SWOT. " + lengthInstruction + "\n\n" + context;
|
||||
break;
|
||||
default:
|
||||
prompt = "На основе следующей информации о бизнесе создай краткое резюме маркетингового анализа (2-3 абзаца). "
|
||||
+
|
||||
prompt = "На основе следующей информации о бизнесе создай резюме маркетингового анализа. " +
|
||||
"Выдели ключевые возможности и особенности бизнеса. " +
|
||||
"Ответ должен быть на русском языке, деловым стилем.\n\n" + context;
|
||||
lengthInstruction + "\n\n" + context;
|
||||
}
|
||||
|
||||
return openAIAnalyticsService.generateWithInstruction(context, prompt, "ru");
|
||||
@@ -329,10 +393,23 @@ public class MarketingAnalysisService {
|
||||
|
||||
markdown.append("# Маркетинговый анализ\n\n");
|
||||
markdown.append("## Информация о бизнесе\n\n");
|
||||
markdown.append("- **Ниша бизнеса:** ").append(request.getBusinessNiche()).append("\n");
|
||||
markdown.append("- **Продукт/услуга:** ").append(request.getProduct()).append("\n");
|
||||
markdown.append("- **Локация:** ").append(request.getLocation()).append("\n");
|
||||
markdown.append("- **Тип клиентов:** ").append(request.getClient()).append("\n");
|
||||
markdown.append("- **Уникальные особенности:** ").append(request.getDifferentiator()).append("\n");
|
||||
markdown.append("- **Целевая аудитория:** ").append(request.getTargetAudience()).append("\n");
|
||||
markdown.append("- **Регион:** ").append(request.getRegion()).append("\n");
|
||||
markdown.append("- **Цель на 6-12 месяцев:** ").append(request.getGoal()).append("\n");
|
||||
|
||||
String detailLevelName = (String) reportData.get("detailLevel");
|
||||
if (detailLevelName != null) {
|
||||
markdown.append("- **Уровень детализации:** ").append(detailLevelName).append("\n");
|
||||
}
|
||||
|
||||
if (request.getStrongSide() != null && !request.getStrongSide().trim().isEmpty()) {
|
||||
markdown.append("- **Сильная сторона:** ").append(request.getStrongSide()).append("\n");
|
||||
}
|
||||
if (request.getWeakSide() != null && !request.getWeakSide().trim().isEmpty()) {
|
||||
markdown.append("- **Слабая сторона:** ").append(request.getWeakSide()).append("\n");
|
||||
}
|
||||
|
||||
String analysisTypeName = (String) reportData.get("analysisType");
|
||||
if (analysisTypeName != null) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package kz.konturai.parser.validator;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import kz.konturai.parser.model.DetailLevel;
|
||||
|
||||
public class DetailLevelValidator implements ConstraintValidator<ValidDetailLevel, String> {
|
||||
|
||||
@Override
|
||||
public void initialize(ValidDetailLevel constraintAnnotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return true; // @NotBlank will handle null/empty
|
||||
}
|
||||
return DetailLevel.fromString(value) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package kz.konturai.parser.validator;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class RegionValidator implements ConstraintValidator<ValidRegion, String> {
|
||||
|
||||
private static final List<String> VALID_REGIONS = Arrays.asList(
|
||||
"Алматы",
|
||||
"Астана",
|
||||
"Шымкент",
|
||||
"Караганда",
|
||||
"Актобе",
|
||||
"Тараз",
|
||||
"Павлодар",
|
||||
"Усть-Каменогорск",
|
||||
"Семей",
|
||||
"Костанай",
|
||||
"Кызылорда",
|
||||
"Уральск",
|
||||
"Петропавловск",
|
||||
"Атырау",
|
||||
"Актау",
|
||||
"Туркестан",
|
||||
"Кокшетау",
|
||||
"Талдыкорган",
|
||||
"Экибастуз",
|
||||
"Рудный");
|
||||
|
||||
@Override
|
||||
public void initialize(ValidRegion constraintAnnotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return true; // @NotBlank will handle null/empty
|
||||
}
|
||||
return VALID_REGIONS.contains(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package kz.konturai.parser.validator;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = DetailLevelValidator.class)
|
||||
@Target({ ElementType.FIELD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ValidDetailLevel {
|
||||
String message() default "Поле 'detailLevel' должно быть одним из: КРАТКО, СТАНДАРТНО, ПОДРОБНО";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package kz.konturai.parser.validator;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = RegionValidator.class)
|
||||
@Target({ ElementType.FIELD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ValidRegion {
|
||||
String message() default "Поле 'region' должно быть одним из допустимых городов Казахстана";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user