diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceRunController.java b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceRunController.java new file mode 100644 index 00000000..7dd6801e --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceRunController.java @@ -0,0 +1,57 @@ +package com.kamco.cd.kamcoback.inference; + +import com.kamco.cd.kamcoback.config.api.ApiResponseDto; +import com.kamco.cd.kamcoback.inference.service.InferenceRunService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "추론 실행", description = "추론 실행") +@Log4j2 +@RequestMapping("/api/inference/run") +@RequiredArgsConstructor +@RestController +public class InferenceRunController { + + private final InferenceRunService inferenceRunService; + + @Operation(summary = "추론 진행 여부 확인", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "검색 성공", + content = + @Content( + mediaType = "application/json", + schema = + @Schema( + description = "진행 여부 (UUID 있으면 진행중)", + type = "UUID", + example = "44709877-2e27-4fc5-bacb-8e0328c69b64"))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping + public ApiResponseDto getProcessing( + @Parameter(description = "비교년도", example = "2021") @RequestParam(required = false) + Integer compareYear, + @Parameter(description = "기준년도", example = "2022") @RequestParam(required = false) + Integer targetYear, + @Parameter(description = "모델 uuid") @RequestParam(required = false) UUID modelUuid) { + + inferenceRunService.run(compareYear, targetYear, modelUuid); + return ApiResponseDto.ok(null); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultsTestingDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultsTestingDto.java index db24097f..96892219 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultsTestingDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultsTestingDto.java @@ -1,10 +1,12 @@ package com.kamco.cd.kamcoback.inference.dto; import com.kamco.cd.kamcoback.postgres.entity.InferenceResultsTestingEntity; +import java.time.ZonedDateTime; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.locationtech.jts.geom.Geometry; public class InferenceResultsTestingDto { @@ -22,4 +24,31 @@ public class InferenceResultsTestingDto { return new ShpDto(e.getBatchId(), e.getUid(), e.getMapId()); } } + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class Basic { + private Double probability; + private Long beforeYear; + private Long afterYear; + private String mapId; + private String modelVersion; + private String clsModelPath; + private String clsModelVersion; + private String cdModelType; + private Long id; + private String modelName; + private Long batchId; + private Double area; + private String beforeC; + private Double beforeP; + private String afterC; + private Double afterP; + private Long seq; + private ZonedDateTime createdDate; + private String uid; + private Geometry geometry; + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualApiController.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualApiController.java new file mode 100644 index 00000000..bb3ca125 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualApiController.java @@ -0,0 +1,10 @@ +package com.kamco.cd.kamcoback.inference.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/inference/manual") +public class InferenceManualApiController {} diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualService.java new file mode 100644 index 00000000..e2128359 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualService.java @@ -0,0 +1,22 @@ +package com.kamco.cd.kamcoback.inference.service; + +import com.kamco.cd.kamcoback.inference.dto.InferenceResultsTestingDto; +import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class InferenceManualService { + private final InferenceResultCoreService inferenceResultCoreService; + + public void getResultsTesting(List batchIds) { + List resultList = + inferenceResultCoreService.getInferenceResults(batchIds); + + if (resultList.isEmpty()) {} + + for (InferenceResultsTestingDto.Basic result : resultList) {} + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java new file mode 100644 index 00000000..7f051ac8 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java @@ -0,0 +1,181 @@ +package com.kamco.cd.kamcoback.inference.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.kamco.cd.kamcoback.common.exception.CustomApiException; +import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene; +import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient; +import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult; +import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto; +import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto.pred_requests_areas; +import com.kamco.cd.kamcoback.model.dto.ModelMngDto.Basic; +import com.kamco.cd.kamcoback.model.dto.ModelMngDto.ModelType; +import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService; +import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; + +@Service +@Log4j2 +@RequiredArgsConstructor +public class InferenceRunService { + private final ExternalHttpClient externalHttpClient; + private final MapSheetMngCoreService mapSheetMngCoreService; + private final ModelMngCoreService modelMngCoreService; + private final ObjectMapper objectMapper; + + @Value("${inference.url}") + private String inferenceUrl; + + public void run(Integer compareYear, Integer targetYear, UUID modelUuid) { + List compareList = mapSheetMngCoreService.getMapSheetMngHst(compareYear); + List targetList = mapSheetMngCoreService.getMapSheetMngHst(targetYear); + + Set compareSet = new HashSet<>(compareList); + + List filteredTargetList = + targetList.stream() + .filter(compareSet::contains) // 2021에 있는 도협만 남김 + .distinct() // 필요 없으면 제거 + .toList(); + + Scene modelComparePath = getSceneInference(compareYear.toString(), filteredTargetList, "", ""); + Scene modelTargetPath = getSceneInference(targetYear.toString(), filteredTargetList, "", ""); + + // ai 서버에 전달할 파라미터 생성 + pred_requests_areas predRequestsAreas = new pred_requests_areas(); + predRequestsAreas.setInput1_year(compareYear); + predRequestsAreas.setInput2_year(targetYear); + predRequestsAreas.setInput1_scene_path(modelComparePath.getFilePath()); + predRequestsAreas.setInput2_scene_path(modelTargetPath.getFilePath()); + + InferenceSendDto m1 = this.getModelInfo(modelUuid); + m1.setPred_requests_areas(predRequestsAreas); + + // ai 추론 실행 api 호출 + Long batchId = ensureAccepted(m1); + } + + private Scene getSceneInference( + String yyyy, List mapSheetNums, String mapSheetScope, String detectOption) { + return mapSheetMngCoreService.getSceneInference( + yyyy, mapSheetNums, mapSheetScope, detectOption); + } + + /** + * 추론 AI API 호출 + * + * @param dto + */ + private Long ensureAccepted(InferenceSendDto dto) { + + if (dto == null) { + log.warn("not InferenceSendDto dto"); + throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST); + } + + // 1) 요청 로그 + try { + log.debug("Inference request dto={}", objectMapper.writeValueAsString(dto)); + } catch (JsonProcessingException e) { + log.warn("Failed to serialize inference dto", e); + } + + // 3) HTTP 호출 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + + ExternalCallResult result = + externalHttpClient.call(inferenceUrl, HttpMethod.POST, dto, headers, String.class); + + if (result.statusCode() < 200 || result.statusCode() >= 300) { + log.error("Inference API failed. status={}, body={}", result.statusCode(), result.body()); + throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY); + } + + // 4) 응답 파싱 + try { + List> list = + objectMapper.readValue(result.body(), new TypeReference<>() {}); + + if (list.isEmpty()) { + throw new IllegalStateException("Inference response is empty"); + } + + Object batchIdObj = list.get(0).get("batch_id"); + if (batchIdObj == null) { + throw new IllegalStateException("batch_id not found in response"); + } + + return Long.valueOf(batchIdObj.toString()); + + } catch (Exception e) { + log.error("Failed to parse inference response. body={}", result.body(), e); + throw new CustomApiException("INVALID_INFERENCE_RESPONSE", HttpStatus.BAD_GATEWAY); + } + } + + /** + * 모델정보 조회 dto 생성 후 반환 + * + * @param uuid + * @return + */ + private InferenceSendDto getModelInfo(UUID uuid) { + + Basic modelInfo = modelMngCoreService.findByModelUuid(uuid); + + String cdModelPath = ""; + String cdModelConfigPath = ""; + String cdClsModelPath = ""; + + if (modelInfo.getCdModelPath() != null && modelInfo.getCdModelFileName() != null) { + cdModelPath = + Paths.get(modelInfo.getCdModelPath(), modelInfo.getCdModelFileName()).toString(); + } + + if (modelInfo.getCdModelConfig() != null && modelInfo.getCdModelConfigFileName() != null) { + cdModelConfigPath = + Paths.get(modelInfo.getCdModelConfig(), modelInfo.getCdModelConfigFileName()).toString(); + } + + if (modelInfo.getClsModelPath() != null && modelInfo.getClsModelFileName() != null) { + cdClsModelPath = + Paths.get(modelInfo.getClsModelPath(), modelInfo.getClsModelFileName()).toString(); + } + + String modelType = ""; + + if (modelInfo.getModelType().equals(ModelType.G1.getId())) { + modelType = ModelType.G1.getId(); + } else if (modelInfo.getModelType().equals(ModelType.G2.getId())) { + modelType = ModelType.G2.getId(); + } else { + modelType = ModelType.G3.getId(); + } + + InferenceSendDto sendDto = new InferenceSendDto(); + sendDto.setModel_version(modelInfo.getModelVer()); + sendDto.setCd_model_path(cdModelPath); + sendDto.setCd_model_config(cdModelConfigPath); + sendDto.setCls_model_path(cdClsModelPath); + sendDto.setCls_model_version(modelInfo.getModelVer()); + sendDto.setCd_model_type(modelType); + sendDto.setPriority(modelInfo.getPriority()); + return sendDto; + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java index fe38b5c4..b8631fa0 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java @@ -498,15 +498,16 @@ public class InferenceResultCoreService { } /** - * 추론 결과 shp파일 생성위해서 조회 + * 추론 결과 조회 * * @param batchIds * @return */ - public List getInferenceResults(List batchIds) { + public List getInferenceResults(List batchIds) { List list = inferenceResultsTestingRepository.getInferenceResultList(batchIds); - return list.stream().map(InferenceResultsTestingDto.ShpDto::fromEntity).toList(); + + return list.stream().map(InferenceResultsTestingEntity::toDto).toList(); } public Long getInferenceResultCnt(List batchIds) { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngCoreService.java index 25cb548b..45d8136c 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngCoreService.java @@ -342,4 +342,9 @@ public class MapSheetMngCoreService { public List getByHstMapSheetCompareList(int mngYyyy, List mapId) { return mapSheetMngYearRepository.findByHstMapSheetCompareList(mngYyyy, mapId); } + + public List getMapSheetMngHst(Integer year) { + List entity = mapSheetMngRepository.getMapSheetMngHst(year); + return entity.stream().map(MapSheetMngHstEntity::getMapSheetNum).toList(); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/InferenceResultsTestingEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/InferenceResultsTestingEntity.java index 6202c399..0ad4c26d 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/InferenceResultsTestingEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/InferenceResultsTestingEntity.java @@ -1,5 +1,6 @@ package com.kamco.cd.kamcoback.postgres.entity; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultsTestingDto; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; @@ -84,4 +85,28 @@ public class InferenceResultsTestingEntity { @Column(name = "geometry", columnDefinition = "geometry") private Geometry geometry; + + public InferenceResultsTestingDto.Basic toDto() { + return new InferenceResultsTestingDto.Basic( + this.probability, + this.beforeYear, + this.afterYear, + this.mapId, + this.modelVersion, + this.clsModelPath, + this.clsModelVersion, + this.cdModelType, + this.id, + this.modelName, + this.batchId, + this.area, + this.beforeC, + this.beforeP, + this.afterC, + this.afterP, + this.seq, + this.createdDate, + this.uid, + this.geometry); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryCustom.java index fa4a8382..a5e14356 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryCustom.java @@ -80,4 +80,6 @@ public interface MapSheetMngRepositoryCustom { void updateMapSheetMngHstUploadId(Long hstUid, UUID uuid, String uploadId); void insertMapSheetMngTile(@Valid AddReq addReq); + + List getMapSheetMngHst(Integer year); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java index a2dba0a8..249aa512 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java @@ -1099,4 +1099,15 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport "{0} like '%" + searchReq.getSearchValue() + "%'", mapSheetMngHstEntity.mapSheetNum)); } + + @Override + public List getMapSheetMngHst(Integer year) { + return queryFactory + .select(mapSheetMngHstEntity) + .from(mapSheetMngHstEntity) + .innerJoin(mapSheetMngFileEntity) + .on(mapSheetMngFileEntity.hstUid.eq(mapSheetMngHstEntity.hstUid)) + .where(mapSheetMngHstEntity.mngYyyy.eq(year).and(mapSheetMngHstEntity.syncState.eq("DONE"))) + .fetch(); + } }