target fix

This commit is contained in:
arys
2026-04-03 17:28:59 +05:00
parent 131c4c08e0
commit a629c82fdf
3 changed files with 95 additions and 8 deletions
@@ -0,0 +1,78 @@
package kz.konturai.parser.controller;
import kz.konturai.parser.service.MinIOService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.io.InputStream;
/**
* Controller for public access to marketing images and videos at the root level.
* This handles requests to https://api.konturai.kz/image_*.png or video_*.mp4.
*/
@RestController
@RequiredArgsConstructor
@Slf4j
public class PublicAssetController {
private final MinIOService minIOService;
/**
* Serves images that start with "image_" and end with ".png" from the root.
* Example: https://api.konturai.kz/image_1772616225367_1567876836.png
*/
@GetMapping("/{filename:image_.+\\.png}")
public ResponseEntity<byte[]> getPublicImage(@PathVariable String filename) {
log.info("[PublicAsset] Access request for image: {}", filename);
try {
if (!minIOService.fileExists(filename)) {
return ResponseEntity.notFound().build();
}
try (InputStream is = minIOService.downloadFile(filename)) {
byte[] bytes = is.readAllBytes();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE)
.header(HttpHeaders.CACHE_CONTROL, "public, max-age=3600")
.body(bytes);
}
} catch (Exception e) {
log.error("[PublicAsset] Failed to serve public image {}: {}", filename, e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* Serves videos that start with "video_" and end with ".mp4" from the root.
* Example: https://api.konturai.kz/video_1772616225367_1567876836.mp4
*/
@GetMapping("/{filename:video_.+\\.mp4}")
public ResponseEntity<Resource> getPublicVideo(@PathVariable String filename) {
log.info("[PublicAsset] Access request for video: {}", filename);
try {
if (!minIOService.fileExists(filename)) {
return ResponseEntity.notFound().build();
}
InputStream is = minIOService.downloadFile(filename);
InputStreamResource resource = new InputStreamResource(is);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, "video/mp4")
.header(HttpHeaders.CACHE_CONTROL, "public, max-age=3600")
.body(resource);
} catch (Exception e) {
log.error("[PublicAsset] Failed to serve public video {}: {}", filename, e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
@@ -190,12 +190,15 @@ public class TargetingCampaignController {
@PathVariable String id) {
String userId = resolveUserId(userIdHeader, authHeader);
TargetingCampaign campaign = campaignService.getCampaignById(id, userId);
return ResponseEntity.ok(Map.of(
"id", campaign.getId(),
"status", campaign.getStatus(),
"completedAt", campaign.getCompletedAt() != null ? campaign.getCompletedAt().toString() : "",
"statusHistory", campaign.getStatusHistory() != null ? campaign.getStatusHistory() : List.of()
));
String status = campaign.getStatus();
boolean isTerminal = "active".equals(status) || "failed".equals(status) || "paused".equals(status);
java.util.Map<String, Object> response = new java.util.LinkedHashMap<>();
response.put("id", campaign.getId());
response.put("status", status);
response.put("isTerminal", isTerminal);
response.put("completedAt", campaign.getCompletedAt() != null ? campaign.getCompletedAt().toString() : null);
response.put("statusHistory", campaign.getStatusHistory() != null ? campaign.getStatusHistory() : List.of());
return ResponseEntity.ok(response);
}
// ── AI Предсказания ───────────────────────────────────────────────────────
@@ -18,6 +18,8 @@ import kz.konturai.parser.enums.CampaignObjective;
import kz.konturai.parser.util.RangeParser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
@@ -49,6 +51,10 @@ public class TargetingCampaignService {
private final MarketingStrategyRepository strategyRepository;
private final CampaignPredictionService predictionService;
// Self-reference through Spring proxy so @Async works (avoids self-invocation bypass)
@Autowired
private ApplicationContext applicationContext;
public TargetingCampaign createCampaign(TargetingCampaignRequest req, String userId) {
if (req.getStrategyId() == null || req.getStrategyId().isBlank()) {
throw new IllegalArgumentException("strategyId is required");
@@ -157,8 +163,8 @@ public class TargetingCampaignService {
addStatusHistory(campaign, "CREATED", CampaignStatusMessage.CREATED.getMessage());
TargetingCampaign saved = repository.save(campaign);
// Process Async
processAsync(saved.getId(), req, userId);
// Process Async — вызываем через Spring proxy чтобы @Async работал корректно
applicationContext.getBean(TargetingCampaignService.class).processAsync(saved.getId(), req, userId);
return saved;
}