접속오류 수정
This commit is contained in:
+4
-4
@@ -12,20 +12,20 @@ import org.springframework.stereotype.Component;
|
|||||||
public class StrategyEvaluationAiVerifyJobRunner {
|
public class StrategyEvaluationAiVerifyJobRunner {
|
||||||
|
|
||||||
private final StrategyEvaluationLlmService llmService;
|
private final StrategyEvaluationLlmService llmService;
|
||||||
private final StrategyEvaluationAiVerifyJobService jobService;
|
private final StrategyEvaluationAiVerifyJobStore jobStore;
|
||||||
|
|
||||||
@Async("aiVerifyExecutor")
|
@Async("aiVerifyExecutor")
|
||||||
public void execute(String jobId, JsonNode context, Long userId, String deviceId) {
|
public void execute(String jobId, JsonNode context, Long userId, String deviceId) {
|
||||||
jobService.markRunning(jobId);
|
jobStore.markRunning(jobId);
|
||||||
try {
|
try {
|
||||||
var result = llmService.verify(context, userId, deviceId);
|
var result = llmService.verify(context, userId, deviceId);
|
||||||
jobService.markCompleted(jobId, result);
|
jobStore.markCompleted(jobId, result);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("[AiVerifyJob] fail jobId={}: {}", jobId, e.getMessage());
|
log.warn("[AiVerifyJob] fail jobId={}: {}", jobId, e.getMessage());
|
||||||
String msg = e.getMessage() != null && !e.getMessage().isBlank()
|
String msg = e.getMessage() != null && !e.getMessage().isBlank()
|
||||||
? e.getMessage()
|
? e.getMessage()
|
||||||
: "AI 검증 중 오류가 발생했습니다.";
|
: "AI 검증 중 오류가 발생했습니다.";
|
||||||
jobService.markFailed(jobId, msg);
|
jobStore.markFailed(jobId, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-85
@@ -3,110 +3,29 @@ package com.goldenchart.service;
|
|||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStartResponse;
|
import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStartResponse;
|
||||||
import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStatusResponse;
|
import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStatusResponse;
|
||||||
import com.goldenchart.dto.StrategyEvaluationAiVerifyResponse;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class StrategyEvaluationAiVerifyJobService {
|
public class StrategyEvaluationAiVerifyJobService {
|
||||||
|
|
||||||
private static final long JOB_TTL_MS = 60L * 60 * 1000;
|
|
||||||
|
|
||||||
private final StrategyEvaluationAiVerifyJobRunner jobRunner;
|
private final StrategyEvaluationAiVerifyJobRunner jobRunner;
|
||||||
|
private final StrategyEvaluationAiVerifyJobStore jobStore;
|
||||||
private final ConcurrentHashMap<String, JobRecord> jobs = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public StrategyEvaluationAiVerifyJobStartResponse startJob(JsonNode context, Long userId, String deviceId) {
|
public StrategyEvaluationAiVerifyJobStartResponse startJob(JsonNode context, Long userId, String deviceId) {
|
||||||
purgeExpired();
|
|
||||||
String jobId = UUID.randomUUID().toString();
|
String jobId = UUID.randomUUID().toString();
|
||||||
JobRecord record = new JobRecord(jobId, userId, System.currentTimeMillis());
|
jobStore.register(jobId, userId);
|
||||||
jobs.put(jobId, record);
|
|
||||||
jobRunner.execute(jobId, context, userId, deviceId);
|
jobRunner.execute(jobId, context, userId, deviceId);
|
||||||
return StrategyEvaluationAiVerifyJobStartResponse.builder()
|
return StrategyEvaluationAiVerifyJobStartResponse.builder()
|
||||||
.jobId(jobId)
|
.jobId(jobId)
|
||||||
.status(record.status)
|
.status(jobStore.initialStatus(jobId))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public StrategyEvaluationAiVerifyJobStatusResponse getJob(String jobId, Long userId) {
|
public StrategyEvaluationAiVerifyJobStatusResponse getJob(String jobId, Long userId) {
|
||||||
purgeExpired();
|
return jobStore.getJob(jobId, userId);
|
||||||
JobRecord record = jobs.get(jobId);
|
|
||||||
if (record == null || !record.userId.equals(userId)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return toResponse(record);
|
|
||||||
}
|
|
||||||
|
|
||||||
void markRunning(String jobId) {
|
|
||||||
JobRecord record = jobs.get(jobId);
|
|
||||||
if (record != null) {
|
|
||||||
record.status = "RUNNING";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void markCompleted(String jobId, StrategyEvaluationAiVerifyResponse result) {
|
|
||||||
JobRecord record = jobs.get(jobId);
|
|
||||||
if (record != null) {
|
|
||||||
record.status = "COMPLETED";
|
|
||||||
record.result = result;
|
|
||||||
record.error = null;
|
|
||||||
record.completedAtMs = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void markFailed(String jobId, String error) {
|
|
||||||
JobRecord record = jobs.get(jobId);
|
|
||||||
if (record != null) {
|
|
||||||
record.status = "FAILED";
|
|
||||||
record.error = error;
|
|
||||||
record.completedAtMs = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StrategyEvaluationAiVerifyJobStatusResponse toResponse(JobRecord record) {
|
|
||||||
return StrategyEvaluationAiVerifyJobStatusResponse.builder()
|
|
||||||
.jobId(record.jobId)
|
|
||||||
.status(record.status)
|
|
||||||
.error(record.error)
|
|
||||||
.result(record.result)
|
|
||||||
.createdAtMs(record.createdAtMs)
|
|
||||||
.completedAtMs(record.completedAtMs)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void purgeExpired() {
|
|
||||||
long cutoff = System.currentTimeMillis() - JOB_TTL_MS;
|
|
||||||
Iterator<Map.Entry<String, JobRecord>> it = jobs.entrySet().iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Map.Entry<String, JobRecord> e = it.next();
|
|
||||||
long ts = e.getValue().completedAtMs != null
|
|
||||||
? e.getValue().completedAtMs
|
|
||||||
: e.getValue().createdAtMs;
|
|
||||||
if (ts < cutoff) {
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class JobRecord {
|
|
||||||
private final String jobId;
|
|
||||||
private final Long userId;
|
|
||||||
private final long createdAtMs;
|
|
||||||
private String status = "QUEUED";
|
|
||||||
private String error;
|
|
||||||
private StrategyEvaluationAiVerifyResponse result;
|
|
||||||
private Long completedAtMs;
|
|
||||||
|
|
||||||
private JobRecord(String jobId, Long userId, long createdAtMs) {
|
|
||||||
this.jobId = jobId;
|
|
||||||
this.userId = userId;
|
|
||||||
this.createdAtMs = createdAtMs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
package com.goldenchart.service;
|
||||||
|
|
||||||
|
import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStatusResponse;
|
||||||
|
import com.goldenchart.dto.StrategyEvaluationAiVerifyResponse;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/** AI 검증 job 상태 저장 — Service·Runner 간 순환 참조 방지 */
|
||||||
|
@Component
|
||||||
|
public class StrategyEvaluationAiVerifyJobStore {
|
||||||
|
|
||||||
|
private static final long JOB_TTL_MS = 60L * 60 * 1000;
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<String, JobRecord> jobs = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void register(String jobId, Long userId) {
|
||||||
|
purgeExpired();
|
||||||
|
jobs.put(jobId, new JobRecord(jobId, userId, System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public StrategyEvaluationAiVerifyJobStatusResponse getJob(String jobId, Long userId) {
|
||||||
|
purgeExpired();
|
||||||
|
JobRecord record = jobs.get(jobId);
|
||||||
|
if (record == null || !record.userId.equals(userId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return toResponse(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String initialStatus(String jobId) {
|
||||||
|
JobRecord record = jobs.get(jobId);
|
||||||
|
return record != null ? record.status : "QUEUED";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markRunning(String jobId) {
|
||||||
|
JobRecord record = jobs.get(jobId);
|
||||||
|
if (record != null) {
|
||||||
|
record.status = "RUNNING";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markCompleted(String jobId, StrategyEvaluationAiVerifyResponse result) {
|
||||||
|
JobRecord record = jobs.get(jobId);
|
||||||
|
if (record != null) {
|
||||||
|
record.status = "COMPLETED";
|
||||||
|
record.result = result;
|
||||||
|
record.error = null;
|
||||||
|
record.completedAtMs = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markFailed(String jobId, String error) {
|
||||||
|
JobRecord record = jobs.get(jobId);
|
||||||
|
if (record != null) {
|
||||||
|
record.status = "FAILED";
|
||||||
|
record.error = error;
|
||||||
|
record.completedAtMs = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StrategyEvaluationAiVerifyJobStatusResponse toResponse(JobRecord record) {
|
||||||
|
return StrategyEvaluationAiVerifyJobStatusResponse.builder()
|
||||||
|
.jobId(record.jobId)
|
||||||
|
.status(record.status)
|
||||||
|
.error(record.error)
|
||||||
|
.result(record.result)
|
||||||
|
.createdAtMs(record.createdAtMs)
|
||||||
|
.completedAtMs(record.completedAtMs)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void purgeExpired() {
|
||||||
|
long cutoff = System.currentTimeMillis() - JOB_TTL_MS;
|
||||||
|
Iterator<Map.Entry<String, JobRecord>> it = jobs.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Map.Entry<String, JobRecord> e = it.next();
|
||||||
|
long ts = e.getValue().completedAtMs != null
|
||||||
|
? e.getValue().completedAtMs
|
||||||
|
: e.getValue().createdAtMs;
|
||||||
|
if (ts < cutoff) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class JobRecord {
|
||||||
|
private final String jobId;
|
||||||
|
private final Long userId;
|
||||||
|
private final long createdAtMs;
|
||||||
|
private String status = "QUEUED";
|
||||||
|
private String error;
|
||||||
|
private StrategyEvaluationAiVerifyResponse result;
|
||||||
|
private Long completedAtMs;
|
||||||
|
|
||||||
|
private JobRecord(String jobId, Long userId, long createdAtMs) {
|
||||||
|
this.jobId = jobId;
|
||||||
|
this.userId = userId;
|
||||||
|
this.createdAtMs = createdAtMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user