From 57438830c669061a2827cae9a9894cc98b2d4ae6 Mon Sep 17 00:00:00 2001 From: teddy Date: Wed, 14 Jan 2026 14:25:14 +0900 Subject: [PATCH] =?UTF-8?q?[KC-103]=20=EC=B6=94=EB=A1=A0=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=20=EC=A1=B0=EA=B1=B4=20=EB=B3=80=EA=B2=BD,=20exceptio?= =?UTF-8?q?n=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kamcoback/config/api/ApiResponseDto.java | 4 + .../inference/dto/InferenceResultDto.java | 7 +- .../service/InferenceResultService.java | 235 ++++++++++-------- .../mapsheet/dto/MapSheetMngDto.java | 2 + .../core/InferenceResultCoreService.java | 104 ++++---- .../postgres/core/MapSheetMngCoreService.java | 94 ++++--- .../entity/MapSheetLearn5kEntity.java | 6 + .../postgres/entity/MapSheetLearnEntity.java | 3 + .../MapSheetLearnRepositoryCustom.java | 2 + .../MapSheetLearnRepositoryImpl.java | 22 ++ .../mapsheet/MapSheetMngRepositoryCustom.java | 3 +- .../mapsheet/MapSheetMngRepositoryImpl.java | 62 ++++- .../scene/MapInkx5kRepositoryCustom.java | 3 - .../scene/MapInkx5kRepositoryImpl.java | 13 - 14 files changed, 359 insertions(+), 201 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/config/api/ApiResponseDto.java b/src/main/java/com/kamco/cd/kamcoback/config/api/ApiResponseDto.java index 827b606e..c32c958e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/config/api/ApiResponseDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/config/api/ApiResponseDto.java @@ -173,6 +173,10 @@ public class ApiResponseDto { + "To reset your password again, please submit a new request through \"Forgot" + " Password.\""), PAYLOAD_TOO_LARGE("업로드 용량 제한을 초과했습니다."), + NOT_FOUND_TARGET_YEAR("기준년도 도엽을 찾을 수 없습니다."), + NOT_FOUND_COMPARE_YEAR("비교년도 도엽을 찾을 수 없습니다."), + FAIL_SAVE_MAP_SHEET("도엽 저장 중 오류가 발생했습니다."), + FAIL_CREATE_MAP_SHEET_FILE("도엽 설정파일 생성 중 오류가 발생했습니다."), ; // @formatter:on private final String message; diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index cccbfe71..fecbb7e1 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -181,12 +181,9 @@ public class InferenceResultDto { message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") private String detectOption; - @Schema( - description = "5k 도협 번호 목록", - example = - "[{\"mapSheetNum\":33605099,\"mapSheetName\":\"비양도\"},{\"mapSheetNum\":33605100,\"mapSheetName\":\"비양도\"},{\"mapSheetNum\":33606059,\"mapSheetName\":\"한림\"}]") + @Schema(description = "5k 도협 번호 목록", example = "[33605,33606, 33610, 34802, 35603, 35611]") @NotNull - private List mapSheetNum; + private List mapSheetNum; } @Getter diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java index be9483a9..617b8bbc 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java @@ -1,5 +1,6 @@ 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; @@ -14,7 +15,6 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetNumDto; -import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SaveInferenceAiDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Status; @@ -26,10 +26,10 @@ import com.kamco.cd.kamcoback.model.dto.ModelMngDto.Basic; import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService; import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService; import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService; -import jakarta.persistence.EntityNotFoundException; import jakarta.validation.constraints.NotNull; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -57,6 +57,7 @@ public class InferenceResultService { private final MapSheetMngCoreService mapSheetMngCoreService; private final ModelMngCoreService modelMngCoreService; private final ExternalHttpClient externalHttpClient; + private final ObjectMapper objectMapper; @Value("${inference.url}") private String inferenceUrl; @@ -91,34 +92,107 @@ public class InferenceResultService { @Transactional public UUID saveInferenceInfo(InferenceResultDto.RegReq req) { - // 분석대상 도엽이 전체일때 - if (MapSheetScope.ALL.getId().equals(req.getMapSheetScope())) { + // 변화탐지 실행 가능 기준 년도 조회 + List targetList = mapSheetMngCoreService.getHstMapSheetList(req); - // 기준년도 조회 - List targetList = mapSheetMngCoreService.getHstMapSheetList(req.getTargetYyyy()); - req.setMapSheetNum(createdMngDto(req, targetList)); - - } else { - // 부분 - - List mapTargetIds = new ArrayList<>(); - req.getMapSheetNum().forEach(dto -> mapTargetIds.add(dto.getMapSheetNum())); - - // 기준년도 조회 - List targetList = - mapSheetMngCoreService.getHstMapSheetList(req.getTargetYyyy(), mapTargetIds); - req.setMapSheetNum(createdMngDto(req, targetList)); + if (targetList.isEmpty()) { + throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND); } - if (req.getMapSheetNum().isEmpty()) { - throw new EntityNotFoundException("분석 대상 정보가 부족합니다."); + List mapTargetIds = new ArrayList<>(); + for (MngListDto target : targetList) { + mapTargetIds.add(target.getMapSheetNum()); } - // 추론 테이블 저장 - UUID uuid = inferenceResultCoreService.saveInferenceInfo(req); + // 변화탐지 실행 가능 비교년도 조회 + List compareList = + mapSheetMngCoreService.getByHstMapSheetCompareList(req.getCompareYyyy(), mapTargetIds); - // 추론 실행 API 호출 - startInference(req, uuid); + if (compareList.isEmpty()) { + throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND); + } + + List> totalNumList = new ArrayList<>(); + + if (DetectOption.EXCL.getId().equals(req.getDetectOption())) { + // "추론제외" 일때 전년도 이전 값이 있어도 전년도 도엽이 없으면 비교 안함 + for (MngListCompareDto dto : compareList) { + if (Objects.equals(dto.getBeforeYear(), req.getCompareYyyy())) { + Map map = new HashMap<>(); + map.put("beforeYear", dto.getBeforeYear()); + map.put("mapSheetNum", dto.getMapSheetNum()); + totalNumList.add(map); + } + } + } else if (DetectOption.PREV.getId().equals(req.getDetectOption())) { + // "이전 년도 도엽 사용" 이면 전년도 이전 도엽도 사용 + for (MngListCompareDto dto : compareList) { + if (dto.getBeforeYear() != 0) { + Map map = new HashMap<>(); + map.put("beforeYear", dto.getBeforeYear()); + map.put("mapSheetNum", dto.getMapSheetNum()); + totalNumList.add(map); + } + } + } + + if (totalNumList.isEmpty()) { + throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND); + } + + for (MngListDto target : targetList) { + for (Map map : totalNumList) { + if (target.getMapSheetNum().equals(map.get("mapSheetNum").toString())) { + target.setBeforeYear(map.get("beforeYear").toString()); + target.setIsSuccess(true); + } + } + } + + // 목록 및 추론 대상 도엽정보 저장 + UUID uuid = inferenceResultCoreService.saveInferenceInfo(req, targetList); + + // 추론에 필요한 geojson 파일 생성 + List mapSheetNumList = + targetList.stream() + .filter(t -> Boolean.TRUE.equals(t.getIsSuccess())) + .map(MngListDto::getMapSheetNum) + .toList(); + + // 비교년도 geojson 파일 생성하여 경로 받기 + String modelComparePath = + getSceneInference( + String.valueOf(req.getCompareYyyy()), mapSheetNumList, req.getMapSheetScope()); + + // 기준년도 geojson 파일 생성하여 경로 받기 + String modelTargetPath = + getSceneInference( + String.valueOf(req.getTargetYyyy()), mapSheetNumList, req.getMapSheetScope()); + + // ai 서버에 전달할 파라미터 생성 + pred_requests_areas predRequestsAreas = new pred_requests_areas(); + predRequestsAreas.setInput1_year(req.getCompareYyyy()); + predRequestsAreas.setInput2_year(req.getTargetYyyy()); + predRequestsAreas.setInput1_scene_path(modelComparePath); + predRequestsAreas.setInput2_scene_path(modelTargetPath); + + InferenceSendDto m1 = this.getModelInfo(req.getModel1Uuid()); + m1.setPred_requests_areas(predRequestsAreas); + + // ai 추론 실행 api 호출 + Long batchId = ensureAccepted(m1); + + // ai 추론 실행후 응답값 update + SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto(); + saveInferenceAiDto.setUuid(uuid); + saveInferenceAiDto.setBatchId(batchId); + saveInferenceAiDto.setStatus(Status.IN_PROGRESS.getId()); + saveInferenceAiDto.setType("M1"); + saveInferenceAiDto.setInferStartDttm(ZonedDateTime.now()); + saveInferenceAiDto.setModelComparePath(modelComparePath); + saveInferenceAiDto.setModelTargetPath(modelTargetPath); + saveInferenceAiDto.setModelStartDttm(ZonedDateTime.now()); + inferenceResultCoreService.update(saveInferenceAiDto); return uuid; } @@ -185,104 +259,67 @@ public class InferenceResultService { return mapSheetNum; } - /** - * 추론 실행 API 호출 - * - * @param req - */ - private void startInference(InferenceResultDto.RegReq req, UUID uuid) { - - List mapSheetNum = req.getMapSheetNum(); - List mapSheetNumList = new ArrayList<>(); - - for (MapSheetNumDto mapSheetDto : mapSheetNum) { - mapSheetNumList.add(mapSheetDto.getMapSheetNum()); - } - - String modelComparePath = - getSceneInference(String.valueOf(req.getCompareYyyy()), mapSheetNumList); - String modelTargetPath = - getSceneInference(String.valueOf(req.getTargetYyyy()), mapSheetNumList); - - pred_requests_areas predRequestsAreas = new pred_requests_areas(); - predRequestsAreas.setInput1_year(req.getCompareYyyy()); - predRequestsAreas.setInput2_year(req.getTargetYyyy()); - predRequestsAreas.setInput1_scene_path(modelComparePath); - predRequestsAreas.setInput2_scene_path(modelTargetPath); - - InferenceSendDto m1 = this.getModelInfo(req.getModel1Uuid()); - InferenceSendDto m2 = this.getModelInfo(req.getModel2Uuid()); - InferenceSendDto m3 = this.getModelInfo(req.getModel3Uuid()); - - m1.setPred_requests_areas(predRequestsAreas); - m2.setPred_requests_areas(predRequestsAreas); - m3.setPred_requests_areas(predRequestsAreas); - - Long batchId = this.ensureAccepted(m1); - - SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto(); - saveInferenceAiDto.setUuid(uuid); - saveInferenceAiDto.setBatchId(batchId); - saveInferenceAiDto.setStatus(Status.IN_PROGRESS.getId()); - saveInferenceAiDto.setType("M1"); - saveInferenceAiDto.setInferStartDttm(ZonedDateTime.now()); - saveInferenceAiDto.setModelComparePath(modelComparePath); - saveInferenceAiDto.setModelTargetPath(modelTargetPath); - saveInferenceAiDto.setModelStartDttm(ZonedDateTime.now()); - inferenceResultCoreService.update(saveInferenceAiDto); - } - /** * 추론 AI API 호출 * * @param dto */ private Long ensureAccepted(InferenceSendDto dto) { - log.info("dto null? {}", dto == null); - ObjectMapper om = new ObjectMapper(); - try { - log.info("dto json={}", om.writeValueAsString(dto)); - } catch (Exception e) { - log.error(e.getMessage()); + + if (dto == null) { + log.warn("not InferenceSendDto dto"); + throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST); } - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + // 1) 요청 로그 (debug 권장) + try { + log.debug("Inference request dto={}", objectMapper.writeValueAsString(dto)); + } catch (JsonProcessingException e) { + log.warn("Failed to serialize inference dto", e); + } - // TODO 추후 삭제 + // 2) local 환경 임시 처리 (NPE 방어) if ("local".equals(profile)) { + if (dto.getPred_requests_areas() == null) { + throw new IllegalStateException("pred_requests_areas is null"); + } dto.getPred_requests_areas().setInput1_scene_path("/kamco-nfs/requests/2023_local.geojson"); dto.getPred_requests_areas().setInput2_scene_path("/kamco-nfs/requests/2024_local.geojson"); } + // 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); - int status = result.statusCode(); - String body = result.body(); - - if (status < 200 || status >= 300) { + 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); } - Long batchId = 0L; - + // 4) 응답 파싱 try { List> list = - om.readValue(body, new TypeReference>>() {}); + objectMapper.readValue(result.body(), new TypeReference<>() {}); - Integer batchIdInt = (Integer) list.get(0).get("batch_id"); - batchId = batchIdInt.longValue(); if (list.isEmpty()) { throw new IllegalStateException("Inference response is empty"); } - } catch (Exception e) { - log.error(e.getMessage()); - } + Object batchIdObj = list.get(0).get("batch_id"); + if (batchIdObj == null) { + throw new IllegalStateException("batch_id not found in response"); + } - return batchId; + 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); + } } /** @@ -335,11 +372,13 @@ public class InferenceResultService { /** * geojson 파일 생성 * - * @param yyyy - * @param mapSheetNums + * @param yyyy 영상관리 파일별 년도 + * @param mapSheetNums 5k 도엽 번호 리스트 + * @param mapSheetScope EXCL : 추론제외, PREV 이전 년도 도엽 사용 + * @return */ - private String getSceneInference(String yyyy, List mapSheetNums) { - return mapSheetMngCoreService.getSceneInference(yyyy, mapSheetNums); + private String getSceneInference(String yyyy, List mapSheetNums, String mapSheetScope) { + return mapSheetMngCoreService.getSceneInference(yyyy, mapSheetNums, mapSheetScope); } /** diff --git a/src/main/java/com/kamco/cd/kamcoback/mapsheet/dto/MapSheetMngDto.java b/src/main/java/com/kamco/cd/kamcoback/mapsheet/dto/MapSheetMngDto.java index deba4b38..d560e19d 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/dto/MapSheetMngDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/dto/MapSheetMngDto.java @@ -427,6 +427,8 @@ public class MapSheetMngDto { private int mngYyyy; private String mapSheetNum; private String mapSheetName; + private String beforeYear; + private Boolean isSuccess; } @Schema(name = "MngListDto", description = "영상파일내역 검색 목록") 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 e2b5bdf3..a7347cdd 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 @@ -1,6 +1,6 @@ package com.kamco.cd.kamcoback.postgres.core; -import com.kamco.cd.kamcoback.common.enums.CommonUseStatus; +import com.kamco.cd.kamcoback.common.exception.CustomApiException; import com.kamco.cd.kamcoback.common.utils.UserUtil; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard; @@ -10,9 +10,9 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceProgressDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto; -import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetNumDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SaveInferenceAiDto; +import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto; import com.kamco.cd.kamcoback.postgres.entity.MapInkx5kEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity; @@ -28,14 +28,17 @@ import jakarta.validation.constraints.NotNull; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.UUID; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.dao.DataAccessException; import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@Log4j2 @RequiredArgsConstructor public class InferenceResultCoreService { @@ -63,17 +66,26 @@ public class InferenceResultCoreService { * * @param req */ - public UUID saveInferenceInfo(InferenceResultDto.RegReq req) { + public UUID saveInferenceInfo(InferenceResultDto.RegReq req, List targetList) { + String firstMapSheetName = null; + String mapSheetName = ""; + int detectingCnt = 0; - String mapSheetName = - req.getMapSheetNum().get(0).getMapSheetName() - + " 외 " - + (req.getMapSheetNum().size() - 1) - + "건"; + for (MngListDto dto : targetList) { + if (Boolean.TRUE.equals(dto.getIsSuccess())) { + if (detectingCnt == 0) { + firstMapSheetName = dto.getMapSheetName(); + } + detectingCnt++; + } + } - if (req.getMapSheetNum().size() == 1) { - mapSheetName = - req.getMapSheetNum().get(0).getMapSheetName() + " " + req.getMapSheetNum().size() + "건"; + if (detectingCnt == 0) { + mapSheetName = ""; + } else if (detectingCnt == 1) { + mapSheetName = firstMapSheetName + " 1건"; + } else { + mapSheetName = firstMapSheetName + " 외 " + (detectingCnt - 1) + "건"; } MapSheetLearnEntity mapSheetLearnEntity = new MapSheetLearnEntity(); @@ -87,7 +99,9 @@ public class InferenceResultCoreService { mapSheetLearnEntity.setDetectOption(req.getDetectOption()); mapSheetLearnEntity.setCreatedUid(userUtil.getId()); mapSheetLearnEntity.setMapSheetCnt(mapSheetName); - mapSheetLearnEntity.setDetectingCnt((long) req.getMapSheetNum().size()); + mapSheetLearnEntity.setDetectingCnt((long) detectingCnt); + mapSheetLearnEntity.setStage( + mapSheetLearnRepository.getLearnStage(req.getCompareYyyy(), req.getTargetYyyy())); // learn 테이블 저장 MapSheetLearnEntity savedLearn = mapSheetLearnRepository.save(mapSheetLearnEntity); @@ -95,14 +109,16 @@ public class InferenceResultCoreService { final int CHUNK = 1000; List buffer = new ArrayList<>(CHUNK); - // learn 도엽 저장 - for (MapSheetNumDto mapSheetNum : req.getMapSheetNum()) { - MapSheetLearn5kEntity e = new MapSheetLearn5kEntity(); - e.setLearn(savedLearn); - e.setMapSheetNum(Long.parseLong(mapSheetNum.getMapSheetNum())); - e.setCreatedUid(userUtil.getId()); - buffer.add(e); + // learn 도엽별 저장 + for (MngListDto mngDto : targetList) { + MapSheetLearn5kEntity entity = new MapSheetLearn5kEntity(); + entity.setLearn(savedLearn); + entity.setMapSheetNum(Long.parseLong(mngDto.getMapSheetNum())); + entity.setBeforeYear(Integer.valueOf(mngDto.getBeforeYear())); + entity.setIsSuccess(mngDto.getIsSuccess() != null && mngDto.getIsSuccess()); + entity.setCreatedUid(userUtil.getId()); + buffer.add(entity); if (buffer.size() == CHUNK) { flushChunk(buffer); buffer.clear(); @@ -117,33 +133,24 @@ public class InferenceResultCoreService { return savedLearn.getUuid(); } + /** + * 도엽별 저장 + * + * @param buffer + */ private void flushChunk(List buffer) { - // 청크 번호 추출 in 조건 만들기 - List chunkNums = - buffer.stream().map(e -> String.valueOf(e.getMapSheetNum())).distinct().toList(); - - // 추론 제외 - List usedEntities = - mapInkx5kRepository.findByMapSheetNumInAndUseInference(chunkNums, CommonUseStatus.USE); - - // TODO 추론 제외 했으면 파일 있는지도 확인 해야함 - // 조회 결과에서 번호만 Set으로 - Set usedSet = - usedEntities.stream() - .map(MapInkx5kEntity::getMapidcdNo) - .collect(java.util.stream.Collectors.toSet()); - - // 필터 후 저장 - List toSave = - buffer.stream().filter(e -> usedSet.contains(String.valueOf(e.getMapSheetNum()))).toList(); - - if (!toSave.isEmpty()) { - mapSheetLearn5kRepository.saveAll(toSave); - mapSheetLearn5kRepository.flush(); + try { + if (!buffer.isEmpty()) { + mapSheetLearn5kRepository.saveAll(buffer); + mapSheetLearn5kRepository.flush(); + } + } catch (DataAccessException e) { + log.error("FAIL_SAVE_MAP_SHEET(도엽 저장 중 오류가 발생했습니다.): bufferSize={}", buffer.size(), e); + throw new CustomApiException("FAIL_SAVE_MAP_SHEET", HttpStatus.INTERNAL_SERVER_ERROR); + } finally { + entityManager.clear(); } - - entityManager.clear(); } /****/ @@ -354,4 +361,13 @@ public class InferenceResultCoreService { public UUID getProcessing() { return mapSheetLearnRepository.getProcessing(); } + + /** + * @param compareYear 비교년도 + * @param targetYear 기준년도 + * @return + */ + public Integer getLearnStage(Integer compareYear, Integer targetYear) { + return mapSheetLearnRepository.getLearnStage(compareYear, targetYear); + } } 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 1868dd16..cf56f24d 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 @@ -1,7 +1,10 @@ package com.kamco.cd.kamcoback.postgres.core; +import com.kamco.cd.kamcoback.common.exception.CustomApiException; import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter; import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListCompareDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto; @@ -14,6 +17,7 @@ import jakarta.persistence.EntityNotFoundException; import jakarta.validation.Valid; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; @@ -23,6 +27,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -206,49 +211,82 @@ public class MapSheetMngCoreService { } } - public String getSceneInference(String yyyy, List scenes) { - String outputPath = ""; + /** + * 추론 실행에 필요한 geojson 파일 생성 + * + * @param yyyy 영상관리 파일별 년도 + * @param scenes 5k 도엽 번호 리스트 + * @param mapSheetScope EXCL : 추론제외, PREV 이전 년도 도엽 사용 + * @return + */ + public String getSceneInference(String yyyy, List scenes, String mapSheetScope) { - try { - List sceneInference = mapSheetMngRepository.getSceneInference(yyyy, scenes); + boolean isAll = MapSheetScope.ALL.getId().equals(mapSheetScope); - if (sceneInference == null || sceneInference.isEmpty()) { - log.warn("No scene data found for year: {}", yyyy); - return outputPath; - } + // 1) 경로/파일명 결정 + String targetDir = + "local".equals(activeEnv) ? System.getProperty("user.home") + "/geojson" : inferenceDir; - if (activeEnv.equals("local")) { - inferenceDir = System.getProperty("user.home") + "/geojson"; - } - String filename = String.format("%s_%s.geojson", yyyy, activeEnv); - outputPath = Paths.get(inferenceDir, filename).toString(); + String filename = + isAll + ? String.format("%s_%s_ALL.geojson", yyyy, activeEnv) + : String.format("%s_%s.geojson", yyyy, activeEnv); - // 디렉토리가 없으면 생성 - Files.createDirectories(Paths.get(inferenceDir)); + Path outputPath = Paths.get(targetDir, filename); - // GeoJSON 파일 생성 (EPSG:5186 - Korea 2000 / Central Belt 2010) - GeoJsonFileWriter writer = new GeoJsonFileWriter(); - writer.exportToFile(sceneInference, "scene_inference_" + yyyy, 5186, outputPath); - - log.info("GeoJSON file created successfully: {}", outputPath); - log.info("Total features exported: {}", sceneInference.size()); - - } catch (IOException e) { - log.error("Failed to create GeoJSON file for year: {}", yyyy, e); - throw new RuntimeException("GeoJSON 파일 생성 실패: " + e.getMessage(), e); + // 2) ALL일 때만 재사용 + if (isAll && Files.exists(outputPath)) { + return outputPath.toString(); } - return outputPath; + // 3) 데이터 조회 (파일 없을 때만) + List sceneInference = mapSheetMngRepository.getSceneInference(yyyy, scenes); + if (sceneInference == null || sceneInference.isEmpty()) { + log.warn( + "NOT_FOUND_TARGET_YEAR: yyyy={}, isAll={}, scenesSize={}", + yyyy, + isAll, + scenes == null ? 0 : scenes.size()); + throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND); + } + + // 4) 파일 생성 + try { + Files.createDirectories(outputPath.getParent()); + + new GeoJsonFileWriter() + .exportToFile(sceneInference, "scene_inference_" + yyyy, 5186, outputPath.toString()); + + return outputPath.toString(); + + } catch (IOException e) { + log.error( + "FAIL_CREATE_MAP_SHEET_FILE: yyyy={}, isAll={}, path={}", yyyy, isAll, outputPath, e); + throw new CustomApiException("FAIL_CREATE_MAP_SHEET_FILE", HttpStatus.INTERNAL_SERVER_ERROR); + } } - public List getHstMapSheetList(int mngYyyy) { - return mapSheetMngRepository.findByHstMapSheetTargetList(mngYyyy); + /** + * 변화탐지 실행 가능 기준 년도 조회 + * + * @param req + * @return + */ + public List getHstMapSheetList(InferenceResultDto.RegReq req) { + return mapSheetMngRepository.findByHstMapSheetTargetList(req); } public List getHstMapSheetList(int mngYyyy, List mapIds) { return mapSheetMngRepository.findByHstMapSheetTargetList(mngYyyy, mapIds); } + /** + * 변화탐지 실행 가능 비교년도 조회 + * + * @param mngYyyy + * @param mapId + * @return + */ public List getByHstMapSheetCompareList(int mngYyyy, List mapId) { return mapSheetMngRepository.findByHstMapSheetCompareList(mngYyyy, mapId); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearn5kEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearn5kEntity.java index 8c44ef8a..56dc4d4e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearn5kEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearn5kEntity.java @@ -48,4 +48,10 @@ public class MapSheetLearn5kEntity { @Column(name = "created_uid") private Long createdUid; + + @Column(name = "befroe_year") + private Integer beforeYear; + + @Column(name = "is_success") + private Boolean isSuccess; } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearnEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearnEntity.java index 26fe8129..4606ba34 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearnEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearnEntity.java @@ -142,6 +142,9 @@ public class MapSheetLearnEntity { @Column(name = "m3_model_end_dttm") private ZonedDateTime m3ModelEndDttm; + @Column(name = "stage") + private Integer stage; + public InferenceResultDto.ResultList toDto() { return new InferenceResultDto.ResultList( this.uuid, diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryCustom.java index 074a868f..3a5e4dd4 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryCustom.java @@ -25,4 +25,6 @@ public interface MapSheetLearnRepositoryCustom { InferenceStatusDetailDto getInferenceStatus(UUID uuid); UUID getProcessing(); + + Integer getLearnStage(Integer compareYear, Integer targetYear); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryImpl.java index 4b04fbcc..be97f06e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryImpl.java @@ -239,4 +239,26 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto .where(mapSheetLearnEntity.status.eq("IN_PROGRESS")) .fetchOne(); } + + @Override + public Integer getLearnStage(Integer compareYear, Integer targetYear) { + MapSheetLearnEntity entity = new MapSheetLearnEntity(); + entity.setCompareYyyy(compareYear); + entity.setTargetYyyy(targetYear); + + Integer stage = + queryFactory + .select(mapSheetLearnEntity.stage) + .from(mapSheetLearnEntity) + .where( + mapSheetLearnEntity + .compareYyyy + .eq(compareYear) + .and(mapSheetLearnEntity.targetYyyy.eq(targetYear))) + .orderBy(mapSheetLearnEntity.stage.desc()) + .limit(1) + .fetchOne(); + + return stage == null ? 1 : stage + 1; + } } 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 669f81f0..3d30f371 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 @@ -1,6 +1,7 @@ package com.kamco.cd.kamcoback.postgres.repository.mapsheet; import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListCompareDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto; @@ -61,7 +62,7 @@ public interface MapSheetMngRepositoryCustom { List findByHstUidMapSheetFileList(Long hstUid); - List findByHstMapSheetTargetList(int mngYyyy); + List findByHstMapSheetTargetList(InferenceResultDto.RegReq req); List findByHstMapSheetTargetList(int mngYyyy, List mapIds); 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 3405e344..8f1a74f6 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 @@ -5,8 +5,11 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngEntity.mapSheet import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngFileEntity.mapSheetMngFileEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity.mapSheetMngHstEntity; import static com.kamco.cd.kamcoback.postgres.entity.QYearEntity.yearEntity; +import static com.querydsl.core.types.dsl.Expressions.nullExpression; import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListCompareDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto; @@ -478,23 +481,57 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport return foundContent; } + /** + * 기준년도 추론 실행 가능 도엽 조회 + * + * @param req + * @return + */ @Override - public List findByHstMapSheetTargetList(int mngYyyy) { + public List findByHstMapSheetTargetList(InferenceResultDto.RegReq req) { + BooleanBuilder whereBuilder = new BooleanBuilder(); + + whereBuilder.and(mapSheetMngHstEntity.dataState.eq("DONE")); + whereBuilder.and( + mapSheetMngHstEntity + .syncState + .eq("DONE") + .or(mapSheetMngHstEntity.syncCheckState.eq("DONE"))); + whereBuilder.and(mapSheetMngHstEntity.useInference.eq("USE")); + + whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(req.getTargetYyyy())); + + BooleanBuilder likeBuilder = new BooleanBuilder(); + + if (MapSheetScope.PART.getId().equals(req.getMapSheetScope())) { + List list = req.getMapSheetNum(); + if (list == null || list.isEmpty()) { + return List.of(); + } + + for (String prefix : list) { + if (prefix == null || prefix.isBlank()) { + continue; + } + likeBuilder.or(mapSheetMngHstEntity.mapSheetNum.like(prefix.trim() + "%")); + } + } + + if (likeBuilder.hasValue()) { + whereBuilder.and(likeBuilder); + } + return queryFactory .select( Projections.constructor( MngListDto.class, mapSheetMngHstEntity.mngYyyy, mapSheetMngHstEntity.mapSheetNum, - mapSheetMngHstEntity.mapSheetName)) + mapSheetMngHstEntity.mapSheetName, + nullExpression(String.class), + nullExpression(Boolean.class))) .from(mapSheetMngHstEntity) - .where( - mapSheetMngHstEntity - .mngYyyy - .eq(mngYyyy) - .and(mapSheetMngHstEntity.dataState.eq("DONE")) - .and(mapSheetMngHstEntity.syncState.eq("DONE")) - .and(mapSheetMngHstEntity.useInference.eq("USE"))) + .where(whereBuilder) .fetch(); } @@ -519,6 +556,13 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport .fetch(); } + /** + * 변화탐지 실행 가능 비교년도 조회 + * + * @param mngYyyy + * @param mapIds + * @return + */ @Override public List findByHstMapSheetCompareList(int mngYyyy, List mapIds) { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/scene/MapInkx5kRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/scene/MapInkx5kRepositoryCustom.java index 8abf7e6d..ee971c5c 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/scene/MapInkx5kRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/scene/MapInkx5kRepositoryCustom.java @@ -22,7 +22,4 @@ public interface MapInkx5kRepositoryCustom { Page getSceneListByPage( CommonUseStatus useInference, String searchVal, searchReq searchReq); - - List findByMapSheetNumInAndUseInference( - List mapSheetNums, CommonUseStatus use); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/scene/MapInkx5kRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/scene/MapInkx5kRepositoryImpl.java index f7903d79..11d1badb 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/scene/MapInkx5kRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/scene/MapInkx5kRepositoryImpl.java @@ -129,19 +129,6 @@ public class MapInkx5kRepositoryImpl extends QuerydslRepositorySupport return new PageImpl<>(content, pageable, count); } - @Override - public List findByMapSheetNumInAndUseInference( - List mapSheetNums, CommonUseStatus use) { - if (mapSheetNums == null || mapSheetNums.isEmpty()) { - return List.of(); - } - - return queryFactory - .selectFrom(mapInkx5kEntity) - .where(mapInkx5kEntity.mapidcdNo.in(mapSheetNums), mapInkx5kEntity.useInference.eq(use)) - .fetch(); - } - private BooleanExpression searchUseInference(CommonUseStatus useInference) { if (Objects.isNull(useInference)) { return null;