This commit is contained in:
root
2025-12-05 19:22:44 +05:00
parent f2906d3eae
commit 894b226b27
5 changed files with 453 additions and 6 deletions
@@ -659,6 +659,85 @@ public class MarketingController {
}
}
@PostMapping("/tasks/{taskId}/execute")
public ResponseEntity<?> executeTaskManually(
@RequestHeader(value = "Authorization", required = false) String authHeader,
@PathVariable String taskId) {
String userId = extractUserIdFromHeader(authHeader);
if (userId == null) {
return unauthorizedResponse();
}
// Проверяем существование задачи и права доступа
Optional<PostingTask> optTask = postingTaskService.getTaskById(taskId);
if (optTask.isEmpty()) {
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
"Задача с указанным ID не найдена");
return ResponseEntity.status(404)
.body(ApiResponse.error("Задача не найдена", error));
}
PostingTask task = optTask.get();
if (!userId.equals(task.getUserId())) {
ErrorResponse error = new ErrorResponse(
"FORBIDDEN",
"У вас нет доступа к этой задаче");
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(ApiResponse.error("Доступ запрещен", error));
}
try {
// Запускаем задачу вручную
postingTaskService.executeTaskManually(taskId);
// Получаем обновленную задачу для ответа
Optional<PostingTask> updatedTask = postingTaskService.getTaskById(taskId);
if (updatedTask.isPresent()) {
PostingTask taskData = updatedTask.get();
Map<String, Object> responseData = new HashMap<>();
responseData.put("taskId", taskData.getId());
responseData.put("status", taskData.getStatus());
responseData.put("platform", taskData.getPlatform());
responseData.put("publishDate", taskData.getPublishDate());
return ResponseEntity.ok(ApiResponse.success(
"Задача успешно запущена",
responseData));
} else {
// Если задача не найдена после выполнения (маловероятно)
Map<String, Object> responseData = new HashMap<>();
responseData.put("taskId", taskId);
responseData.put("status", "processing");
return ResponseEntity.ok(ApiResponse.success(
"Задача успешно запущена",
responseData));
}
} catch (IllegalArgumentException e) {
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
e.getMessage());
return ResponseEntity.status(404)
.body(ApiResponse.error("Задача не найдена", error));
} catch (IllegalStateException e) {
ErrorResponse error = new ErrorResponse(
"INVALID_STATUS",
e.getMessage());
return ResponseEntity.status(400)
.body(ApiResponse.error("Задача не может быть запущена", error));
} catch (Exception e) {
logger.error("Error executing task {} manually: {}", taskId, e.getMessage(), 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) {
@@ -16,7 +16,9 @@ public class MarketingStrategyResponse {
public MarketingStrategyResponse() {
}
public MarketingStrategyResponse(String strategyId, String analysisId, String status, LocalDateTime createdAt, LocalDateTime completedAt, Integer durationWeeks, List<String> priorityPlatforms, StrategyContent strategy) {
public MarketingStrategyResponse(String strategyId, String analysisId, String status, LocalDateTime createdAt,
LocalDateTime completedAt, Integer durationWeeks, List<String> priorityPlatforms,
StrategyContent strategy) {
this.strategyId = strategyId;
this.analysisId = analysisId;
this.status = status;
@@ -129,7 +131,8 @@ public class MarketingStrategyResponse {
public WeeklyPlan() {
}
public WeeklyPlan(Integer weekNumber, List<String> mainThemes, String contentRecommendations, List<String> priorityPlatforms) {
public WeeklyPlan(Integer weekNumber, List<String> mainThemes, String contentRecommendations,
List<String> priorityPlatforms) {
this.weekNumber = weekNumber;
this.mainThemes = mainThemes;
this.contentRecommendations = contentRecommendations;
@@ -179,11 +182,13 @@ public class MarketingStrategyResponse {
private String publishTime;
private String imageUrl;
private String imageFilename;
private String taskId;
public PostCalendarItem() {
}
public PostCalendarItem(LocalDateTime publishDate, String platform, String contentType, String theme, String postText, List<String> hashtags, String publishTime) {
public PostCalendarItem(LocalDateTime publishDate, String platform, String contentType, String theme,
String postText, List<String> hashtags, String publishTime) {
this.publishDate = publishDate;
this.platform = platform;
this.contentType = contentType;
@@ -264,6 +269,13 @@ public class MarketingStrategyResponse {
public void setImageFilename(String imageFilename) {
this.imageFilename = imageFilename;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
}
}
@@ -8,6 +8,7 @@ import kz.konturai.parser.dto.MarketingStrategyResponse;
import kz.konturai.parser.dto.StatusHistoryEntry;
import kz.konturai.parser.model.MarketingAnalysis;
import kz.konturai.parser.model.MarketingStrategy;
import kz.konturai.parser.model.PostingTask;
import kz.konturai.parser.repository.MarketingAnalysisRepository;
import kz.konturai.parser.repository.MarketingStrategyRepository;
import org.slf4j.Logger;
@@ -32,6 +33,7 @@ public class MarketingStrategyService {
private final OpenAIAnalyticsService openAIAnalyticsService;
private final OpenAIImageGenerationService imageGenerationService;
private final MinIOService minIOService;
private final PostingTaskService postingTaskService;
private final ObjectMapper objectMapper = new ObjectMapper();
public MarketingStrategyService(
@@ -40,13 +42,15 @@ public class MarketingStrategyService {
MarketingAnalysisRepository analysisRepository,
OpenAIAnalyticsService openAIAnalyticsService,
OpenAIImageGenerationService imageGenerationService,
MinIOService minIOService) {
MinIOService minIOService,
PostingTaskService postingTaskService) {
this.repository = repository;
this.marketingAnalysisService = marketingAnalysisService;
this.analysisRepository = analysisRepository;
this.openAIAnalyticsService = openAIAnalyticsService;
this.imageGenerationService = imageGenerationService;
this.minIOService = minIOService;
this.postingTaskService = postingTaskService;
}
public MarketingStrategy generateStrategy(String analysisId, MarketingStrategyRequest request, String userId) {
@@ -615,8 +619,11 @@ public class MarketingStrategyService {
}
strategyContent.setWeeklyPlans(weeklyPlans);
// Convert PostCalendarItems
// Convert PostCalendarItems and add taskId if tasks exist
List<MarketingStrategyResponse.PostCalendarItem> postCalendar = new ArrayList<>();
// Получаем все задачи для этой стратегии
List<PostingTask> tasks = postingTaskService.getStrategyTasks(strategyId);
for (MarketingStrategy.PostCalendarItem item : strategy.getPostCalendar()) {
MarketingStrategyResponse.PostCalendarItem dtoItem = new MarketingStrategyResponse.PostCalendarItem();
dtoItem.setPublishDate(item.getPublishDate());
@@ -628,6 +635,21 @@ public class MarketingStrategyService {
dtoItem.setPublishTime(item.getPublishTime());
dtoItem.setImageUrl(item.getImageUrl());
dtoItem.setImageFilename(item.getImageFilename());
// Находим соответствующую задачу по дате публикации, платформе и тексту поста
Optional<PostingTask> matchingTask = tasks.stream()
.filter(task -> task.getPublishDate() != null && item.getPublishDate() != null
&& task.getPublishDate().equals(item.getPublishDate())
&& task.getPlatform() != null && item.getPlatform() != null
&& task.getPlatform().equalsIgnoreCase(item.getPlatform())
&& task.getPostText() != null && item.getPostText() != null
&& task.getPostText().equals(item.getPostText()))
.findFirst();
if (matchingTask.isPresent()) {
dtoItem.setTaskId(matchingTask.get().getId());
}
postCalendar.add(dtoItem);
}
strategyContent.setPostCalendar(postCalendar);
@@ -225,6 +225,44 @@ public class PostingTaskService {
executeTask(taskId);
}
/**
* Ручное выполнение задачи публикации (независимо от времени публикации)
* Разрешает выполнение только для задач со статусом "pending" или "failed"
*
* @param taskId ID задачи
* @throws IllegalArgumentException если задача не найдена
* @throws IllegalStateException если задача уже выполнена или имеет
* недопустимый статус
*/
public void executeTaskManually(String taskId) {
Optional<PostingTask> optTask = taskRepository.findById(taskId);
if (optTask.isEmpty()) {
throw new IllegalArgumentException("Task not found: " + taskId);
}
PostingTask task = optTask.get();
// Проверяем статус - разрешаем только pending или failed
String status = task.getStatus();
if (!"pending".equals(status) && !"failed".equals(status)) {
throw new IllegalStateException(
"Task cannot be executed manually. Current status: " + status +
". Only tasks with status 'pending' or 'failed' can be executed manually.");
}
// Если задача в статусе failed, сбрасываем статус на pending для повторной
// попытки
if ("failed".equals(status)) {
task.setStatus("pending");
task.setErrorMessage(null);
taskRepository.save(task);
logger.info("Task {} status reset from 'failed' to 'pending' for manual execution", taskId);
}
// Выполняем задачу (игнорируя время публикации)
executeTask(taskId);
}
/**
* Получает задачи пользователя
*
@@ -244,4 +282,14 @@ public class PostingTaskService {
public List<PostingTask> getStrategyTasks(String strategyId) {
return taskRepository.findByStrategyId(strategyId);
}
/**
* Получает задачу по ID
*
* @param taskId ID задачи
* @return Optional с задачей, если найдена
*/
public Optional<PostingTask> getTaskById(String taskId) {
return taskRepository.findById(taskId);
}
}