Merge pull request 'feat/infer_dev_260107' (#235) from feat/infer_dev_260107 into develop

Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/235
This commit is contained in:
2026-01-14 19:40:35 +09:00
7 changed files with 143 additions and 24 deletions

View File

@@ -1,30 +1,71 @@
package com.kamco.cd.kamcoback.config.resttemplate; package com.kamco.cd.kamcoback.config.resttemplate;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@Component @Component
@Log4j2
@RequiredArgsConstructor @RequiredArgsConstructor
public class ExternalHttpClient { public class ExternalHttpClient {
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
public <T> ResponseEntity<T> exchange(
String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) {
HttpEntity<Object> entity = new HttpEntity<>(body, headers);
return restTemplate.exchange(url, method, entity, responseType);
}
public <T> ExternalCallResult<T> call( public <T> ExternalCallResult<T> call(
String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) { String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) {
ResponseEntity<T> res = exchange(url, method, body, headers, responseType);
int code = res.getStatusCodeValue(); HttpEntity<Object> entity = new HttpEntity<>(body, headers);
return new ExternalCallResult<>(code, code >= 200 && code < 300, res.getBody());
// 요청 로그
log.info("[HTTP-REQ] {} {}", method, url);
if (body != null) {
log.debug("[HTTP-REQ-BODY] {}", body);
}
try {
ResponseEntity<T> res = restTemplate.exchange(url, method, entity, responseType);
int code = res.getStatusCodeValue();
// 응답 로그
log.info("[HTTP-RES] {} {} -> {}", method, url, code);
log.debug("[HTTP-RES-BODY] {}", res.getBody());
return new ExternalCallResult<>(code, code >= 200 && code < 300, res.getBody());
} catch (HttpClientErrorException.NotFound e) {
log.info("[HTTP-RES] {} {} -> 404 (Not Found)", method, url);
log.debug("[HTTP-RES-BODY] {}", e.getResponseBodyAsString());
return new ExternalCallResult<>(404, false, null);
} catch (HttpClientErrorException e) {
// 기타 4xx
log.warn(
"[HTTP-ERR] {} {} -> {} body={}",
method,
url,
e.getStatusCode().value(),
e.getResponseBodyAsString());
throw e;
} catch (HttpServerErrorException e) {
// 5xx
log.error(
"[HTTP-ERR] {} {} -> {} body={}",
method,
url,
e.getStatusCode().value(),
e.getResponseBodyAsString());
throw e;
}
} }
public record ExternalCallResult<T>(int statusCode, boolean success, T body) {} public record ExternalCallResult<T>(int statusCode, boolean success, T body) {}

View File

@@ -24,6 +24,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -139,6 +140,25 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(uuid); return ApiResponseDto.ok(uuid);
} }
@Operation(summary = "추론 종료", description = "추론 종료")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "종료 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Page.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@DeleteMapping("/end")
public ApiResponseDto<Void> getInferenceGeomList() {
inferenceResultService.deleteInferenceEnd();
return null;
}
@Operation(summary = "분석 모델 선택 조회", description = "변화탐지 실행 정보 입력 모델선택 팝업 ") @Operation(summary = "분석 모델 선택 조회", description = "변화탐지 실행 정보 입력 모델선택 팝업 ")
@ApiResponses( @ApiResponses(
value = { value = {

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.common.exception.CustomApiException; import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.common.utils.UserUtil;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient; import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult; import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
@@ -65,9 +66,14 @@ public class InferenceResultService {
@Value("${inference.url}") @Value("${inference.url}")
private String inferenceUrl; private String inferenceUrl;
@Value("${inference.batch-url}")
private String batchUrl;
@Value("${spring.profiles.active}") @Value("${spring.profiles.active}")
private String profile; private String profile;
private final UserUtil userUtil;
/** /**
* 추론관리 목록 * 추론관리 목록
* *
@@ -84,7 +90,11 @@ public class InferenceResultService {
* @return * @return
*/ */
public UUID getProcessing() { public UUID getProcessing() {
return inferenceResultCoreService.getProcessing(); SaveInferenceAiDto dto = inferenceResultCoreService.getProcessing();
if (dto == null) {
return null;
}
return dto.getUuid();
} }
/** /**
@@ -184,7 +194,7 @@ public class InferenceResultService {
m1.setPred_requests_areas(predRequestsAreas); m1.setPred_requests_areas(predRequestsAreas);
// ai 추론 실행 api 호출 // ai 추론 실행 api 호출
Long batchId = 0L; // ensureAccepted(m1); Long batchId = ensureAccepted(m1);
// ai 추론 실행후 응답값 update // ai 추론 실행후 응답값 update
SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto(); SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto();
@@ -487,4 +497,33 @@ public class InferenceResultService {
public Page<Geom> getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) { public Page<Geom> getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) {
return inferenceResultCoreService.getInferenceGeomList(uuid, searchGeoReq); return inferenceResultCoreService.getInferenceGeomList(uuid, searchGeoReq);
} }
/** 추론 종료 */
@Transactional
public void deleteInferenceEnd() {
SaveInferenceAiDto dto = inferenceResultCoreService.getProcessing();
if (dto == null) {
throw new CustomApiException("NOT_FOUND", HttpStatus.NOT_FOUND);
}
Long batchId = dto.getBatchId();
String url = batchUrl + "/" + batchId;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
ExternalCallResult<String> result =
externalHttpClient.call(url, HttpMethod.DELETE, dto, headers, String.class);
if (!result.success()) {
throw new CustomApiException("NOT_FOUND", HttpStatus.NOT_FOUND);
}
System.out.println(result);
SaveInferenceAiDto request = new SaveInferenceAiDto();
request.setStatus(Status.END.getId());
request.setUuid(dto.getUuid());
request.setUpdateUid(userUtil.getId());
inferenceResultCoreService.update(request);
}
} }

View File

@@ -21,6 +21,7 @@ import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearn5kEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearn5kEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import com.kamco.cd.kamcoback.postgres.repository.Inference.InferenceResultRepository;
import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetAnalDataInferenceRepository; import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetAnalDataInferenceRepository;
import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetLearn5kRepository; import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetLearn5kRepository;
import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetLearnRepository; import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetLearnRepository;
@@ -50,6 +51,7 @@ public class InferenceResultCoreService {
private final MapSheetLearnRepository mapSheetLearnRepository; private final MapSheetLearnRepository mapSheetLearnRepository;
private final MapInkx5kRepository mapInkx5kRepository; private final MapInkx5kRepository mapInkx5kRepository;
private final MapSheetLearn5kRepository mapSheetLearn5kRepository; private final MapSheetLearn5kRepository mapSheetLearn5kRepository;
private final InferenceResultRepository inferenceResultRepository;
private final EntityManager entityManager; private final EntityManager entityManager;
private final UserUtil userUtil; private final UserUtil userUtil;
@@ -379,17 +381,26 @@ public class InferenceResultCoreService {
* *
* @return * @return
*/ */
public UUID getProcessing() { public SaveInferenceAiDto getProcessing() {
return mapSheetLearnRepository.getProcessing(); MapSheetLearnEntity entity = mapSheetLearnRepository.getProcessing();
}
/** if (entity == null) {
* @param compareYear 비교년도 return null;
* @param targetYear 기준년도 }
* @return
*/ SaveInferenceAiDto dto = new SaveInferenceAiDto();
public Integer getLearnStage(Integer compareYear, Integer targetYear) { dto.setUuid(entity.getUuid());
return mapSheetLearnRepository.getLearnStage(compareYear, targetYear);
if (entity.getM3ModelBatchId() != null) {
dto.setBatchId(entity.getM3ModelBatchId());
}
if (entity.getM2ModelBatchId() != null) {
dto.setBatchId(entity.getM2ModelBatchId());
}
if (entity.getM1ModelBatchId() != null) {
dto.setBatchId(entity.getM1ModelBatchId());
}
return dto;
} }
public AnalResultInfo getInferenceResultInfo(String uuid) { public AnalResultInfo getInferenceResultInfo(String uuid) {
@@ -403,4 +414,8 @@ public class InferenceResultCoreService {
public Page<Geom> getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) { public Page<Geom> getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) {
return mapSheetLearnRepository.getInferenceGeomList(uuid, searchGeoReq); return mapSheetLearnRepository.getInferenceGeomList(uuid, searchGeoReq);
} }
public void upsertGeomsFromInferenceResults(Long id) {
int inferenceGeomCnt = inferenceResultRepository.upsertGeomsFromInferenceResults();
}
} }

View File

@@ -28,7 +28,7 @@ public interface MapSheetLearnRepositoryCustom {
InferenceStatusDetailDto getInferenceStatus(UUID uuid); InferenceStatusDetailDto getInferenceStatus(UUID uuid);
UUID getProcessing(); MapSheetLearnEntity getProcessing();
Integer getLearnStage(Integer compareYear, Integer targetYear); Integer getLearnStage(Integer compareYear, Integer targetYear);

View File

@@ -259,9 +259,9 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
* *
* @return * @return
*/ */
public UUID getProcessing() { public MapSheetLearnEntity getProcessing() {
return queryFactory return queryFactory
.select(mapSheetLearnEntity.uuid) .select(mapSheetLearnEntity)
.from(mapSheetLearnEntity) .from(mapSheetLearnEntity)
.where(mapSheetLearnEntity.status.eq("IN_PROGRESS")) .where(mapSheetLearnEntity.status.eq("IN_PROGRESS"))
.fetchOne(); .fetchOne();

View File

@@ -133,6 +133,10 @@ public class MapSheetInferenceJobService {
externalHttpClient.call(url, HttpMethod.GET, null, jsonHeaders(), String.class); externalHttpClient.call(url, HttpMethod.GET, null, jsonHeaders(), String.class);
int status = result.statusCode(); int status = result.statusCode();
if (status == 404) {
log.info("Batch not found. batchId={}", batchId);
return null;
}
if (status < 200 || status >= 300) { if (status < 200 || status >= 300) {
return null; return null;
} }