diff --git a/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java b/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java index 854f7a04..2647cbd8 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java @@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.mapsheet; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto; import com.kamco.cd.kamcoback.code.service.CommonCodeService; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; +import com.kamco.cd.kamcoback.mapsheet.dto.FileDto; import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.FilesDto; import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.FoldersDto; import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.SrchFilesDto; @@ -16,6 +17,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -24,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import java.util.List; + @Tag(name = "영상 관리", description = "영상 관리 API") @RestController @RequiredArgsConstructor @@ -110,6 +114,27 @@ public class MapSheetMngFileCheckerApiController { mapSheetMngFileCheckerService.deleteDuplicate(filePath, fileName)); } + // 중복 페어 조회: 특정 경로에서 같은 베이스명의 tif/tfw 후보를 반환 + @Operation(summary = "중복 페어 조회", description = "경로와 베이스명으로 중복된 tif/tfw 후보 조회") + @PostMapping("/duplicates") + public ApiResponseDto> findDuplicates( + @RequestParam("targetPath") String targetPath, + @RequestParam("baseName") String baseName) { + return ApiResponseDto.createOK( + mapSheetMngFileCheckerService.findDuplicatePair(targetPath, baseName)); + } + + // 선택 삭제: ID 목록으로 중복 항목 삭제 + public static class DeleteDuplicatesReq { + public List ids; + } + + @Operation(summary = "중복 파일 선택 삭제", description = "중복 후보 중 선택한 항목만 삭제") + @DeleteMapping("/duplicates") + public ApiResponseDto> deleteDuplicates(@RequestBody DeleteDuplicatesReq req) { + return ApiResponseDto.createOK(mapSheetMngFileCheckerService.deleteDuplicatesByIds(req.ids)); + } + /* @Operation(summary = "지정폴더(하위폴더포함) 파일목록 조회", description = "지정폴더(하위폴더포함) 파일목록 조회") @ApiResponses( diff --git a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngFileCheckerService.java b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngFileCheckerService.java index 7c175d70..1d9162c3 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngFileCheckerService.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngFileCheckerService.java @@ -25,11 +25,13 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; import java.text.SimpleDateFormat; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -319,12 +321,20 @@ public class MapSheetMngFileCheckerService { @Transactional public String uploadFile(MultipartFile file, String targetPath, boolean overwrite, Long hstUid) { + ZonedDateTime startAt = ZonedDateTime.now(); + if (hstUid != null) { + mapSheetMngFileCheckerCoreService.updateHstSyncCheckStart(hstUid); + } try { // 파일 유효성 검증 if (file == null || file.isEmpty()) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "SIZEERROR"); throw new ValidationException("업로드 파일이 비어있습니다."); } if (file.getOriginalFilename() == null || file.getOriginalFilename().isEmpty()) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException("파일명이 유효하지 않습니다."); } @@ -367,10 +377,11 @@ public class MapSheetMngFileCheckerService { String parentPathStr = path.getParent() != null ? path.getParent().toString() : ""; boolean dbExists = mapSheetMngFileRepository.existsByFileNameAndFilePath(filename, parentPathStr); - // boolean fileExists = Files.exists(path); // 파일 시스템 존재 여부는 체크하지 않음 (DB 기준) // 이미 존재하는 경우 처리 (DB에만 있는 경우 체크) if (!overwrite && dbExists) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "DUPLICATE"); throw new DuplicateFileException("동일한 파일이 이미 존재합니다 (DB): " + path.getFileName()); } @@ -382,22 +393,31 @@ public class MapSheetMngFileCheckerService { // 업로드 파일 저장(덮어쓰기 허용 시 replace) file.transferTo(path.toFile()); + if (file.getSize() == 0) { + Files.deleteIfExists(path); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "SIZEERROR"); + throw new ValidationException("유효하지 않은 파일입니다 (크기 0): " + path.getFileName()); + } + if ("tfw".equals(ext)) { - // TFW 검증 boolean tfwOk = FIleChecker.checkTfw(path.toString()); if (!tfwOk) { Files.deleteIfExists(path); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException( "유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + path.getFileName()); } - // 안내: 같은 베이스의 TIF가 없으면 추후 TIF 업로드 필요 if (!Files.exists(tifPath)) { - // DB 메타 저장은 진행 (향후 쌍 검증 위해) + // TIF가 없으면 페어 없음 saveUploadMeta(path, hstUid); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "NOTPAIR"); return "TFW 업로드 성공 (매칭되는 TIF가 아직 없습니다)."; } - // TIF가 존재하면 쌍 요건 충족 saveUploadMeta(path, hstUid); + if (hstUid != null) mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "DONE"); return "TFW 업로드 성공"; } @@ -406,34 +426,46 @@ public class MapSheetMngFileCheckerService { boolean isValidTif = FIleChecker.cmmndGdalInfo(path.toString()); if (!isValidTif) { Files.deleteIfExists(path); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException("유효하지 않은 TIF 파일입니다 (GDAL 검증 실패): " + path.getFileName()); } // TFW 존재/검증 if (!Files.exists(tfwPath)) { Files.deleteIfExists(path); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "NOTPAIR"); throw new ValidationException("TFW 파일이 존재하지 않습니다: " + tfwPath.getFileName()); } boolean tfwOk = FIleChecker.checkTfw(tfwPath.toString()); if (!tfwOk) { Files.deleteIfExists(path); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException( "유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + tfwPath.getFileName()); } saveUploadMeta(path, hstUid); + if (hstUid != null) mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "DONE"); return "TIF 업로드 성공"; } // 기타 확장자: 저장만 하고 메타 기록 saveUploadMeta(path, hstUid); + if (hstUid != null) mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "DONE"); return "업로드 성공"; } catch (ValidationException | DuplicateFileException e) { // 비즈니스 예외는 그대로 던짐 throw e; } catch (IOException e) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage(), e); } catch (Exception e) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new IllegalArgumentException("파일 업로드 처리 중 오류 발생: " + e.getMessage(), e); } } @@ -445,6 +477,9 @@ public class MapSheetMngFileCheckerService { String targetPath, boolean overwrite, Long hstUid) { + if (hstUid != null) { + mapSheetMngFileCheckerCoreService.updateHstSyncCheckStart(hstUid); + } try { log.info( "uploadPair 시작 - targetPath: {}, overwrite: {}, hstUid: {}", @@ -454,15 +489,23 @@ public class MapSheetMngFileCheckerService { // 파일 유효성 검증 if (tfwFile == null || tfwFile.isEmpty()) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "NOFILE"); throw new ValidationException("TFW 파일이 비어있습니다."); } if (tifFile == null || tifFile.isEmpty()) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "NOFILE"); throw new ValidationException("TIF 파일이 비어있습니다."); } if (tfwFile.getOriginalFilename() == null || tfwFile.getOriginalFilename().isEmpty()) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException("TFW 파일명이 유효하지 않습니다."); } if (tifFile.getOriginalFilename() == null || tifFile.getOriginalFilename().isEmpty()) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException("TIF 파일명이 유효하지 않습니다."); } @@ -492,13 +535,11 @@ public class MapSheetMngFileCheckerService { String tfwBase = FilenameUtils.getBaseName(tfwPath.getFileName().toString()); String tifBase = FilenameUtils.getBaseName(tifPath.getFileName().toString()); if (!tfwBase.equalsIgnoreCase(tifBase)) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "NOTPAIR"); throw new ValidationException("TFW/TIF 파일명이 동일한 베이스가 아닙니다."); } - // 디렉토리는 이미 생성되었으므로 추가 생성 불필요 - // if (tfwPath.getParent() != null) Files.createDirectories(tfwPath.getParent()); - // if (tifPath.getParent() != null) Files.createDirectories(tifPath.getParent()); - // DB 중복 체크 및 overwrite 처리 (각 파일별) String parentPathStr = basePath.toString(); String tfwName = tfwPath.getFileName().toString(); String tifName = tifPath.getFileName().toString(); @@ -507,6 +548,8 @@ public class MapSheetMngFileCheckerService { boolean tifDbExists = mapSheetMngFileRepository.existsByFileNameAndFilePath(tifName, parentPathStr); if (!overwrite && (tfwDbExists || tifDbExists)) { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "DUPLICATE"); throw new DuplicateFileException("동일한 파일이 이미 존재합니다 (DB): " + tfwName + ", " + tifName); } if (overwrite) { @@ -522,6 +565,15 @@ public class MapSheetMngFileCheckerService { tifFile.transferTo(tifPath.toFile()); log.info("파일 저장 완료"); + // 0 바이트 체크 + if (Files.size(tfwPath) == 0 || Files.size(tifPath) == 0) { + Files.deleteIfExists(tfwPath); + Files.deleteIfExists(tifPath); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "SIZEERROR"); + throw new ValidationException("업로드 파일 크기가 0입니다."); + } + // 검증 log.info("TFW 파일 검증 시작: {}", tfwPath); boolean tfwOk = FIleChecker.checkTfw(tfwPath.toString()); @@ -529,6 +581,8 @@ public class MapSheetMngFileCheckerService { log.warn("TFW 파일 검증 실패: {}", tfwName); Files.deleteIfExists(tfwPath); Files.deleteIfExists(tifPath); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException("유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + tfwName); } log.info("TFW 파일 검증 성공"); @@ -539,6 +593,8 @@ public class MapSheetMngFileCheckerService { log.warn("TIF 파일 검증 실패: {}", tifName); Files.deleteIfExists(tfwPath); Files.deleteIfExists(tifPath); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException("유효하지 않은 TIF 파일입니다 (GDAL 검증 실패): " + tifName); } log.info("TIF 파일 검증 성공"); @@ -548,8 +604,11 @@ public class MapSheetMngFileCheckerService { saveUploadMeta(tfwPath, hstUid); saveUploadMeta(tifPath, hstUid); log.info("메타 데이터 저장 완료"); + if (hstUid != null) mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "DONE"); return "TFW/TIF 페어 업로드 성공"; } else { + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new ValidationException("targetPath는 디렉토리여야 합니다."); } } catch (ValidationException | DuplicateFileException e) { @@ -558,13 +617,62 @@ public class MapSheetMngFileCheckerService { throw e; } catch (IOException e) { log.error("파일 I/O 처리 실패: {}", e.getMessage(), e); - throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage(), e); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); + throw new IllegalArgumentException("파일 업로드 처리 중 오류 발생: " + e.getMessage(), e); } catch (Exception e) { log.error("파일 업로드 처리 중 예상치 못한 오류 발생: {}", e.getMessage(), e); + if (hstUid != null) + mapSheetMngFileCheckerCoreService.updateHstSyncCheckEnd(hstUid, "TYPEERROR"); throw new IllegalArgumentException("파일 업로드 처리 중 오류 발생: " + e.getMessage(), e); } } + // 중복 페어 조회: 경로와 베이스명으로 tif/tfw 후보 반환 + @Transactional(readOnly = true) + public List findDuplicatePair(String targetPath, String baseName) { + String tfwName = baseName + ".tfw"; + String tifName = baseName + ".tif"; + List names = List.of(tfwName, tifName, baseName + ".tiff"); + List entities = + mapSheetMngFileRepository.findByFilePathAndFileNameIn(targetPath, names); + return entities.stream() + .map( + e -> + new FileDto.Basic( + e.getFileName(), + Paths.get(e.getFilePath()).getFileName().toString(), + e.getFilePath(), + Paths.get(e.getFilePath(), e.getFileName()).toString(), + e.getFileExt(), + e.getFileSize() == null ? 0L : e.getFileSize(), + "")) + .collect(Collectors.toList()); + } + + // 선택 삭제: ID 목록으로 삭제 수행하고 삭제된 ID 리스트 반환 + @Transactional + public List deleteDuplicatesByIds(List ids) { + if (ids == null || ids.isEmpty()) { + return List.of(); + } + // 존재 확인 및 안전 삭제 + List toDelete = mapSheetMngFileRepository.findAllById(ids); + List realIds = toDelete.stream().map(MapSheetMngFileEntity::getFileUid).collect(Collectors.toList()); + mapSheetMngFileRepository.deleteAllById(realIds); + // 실제 파일도 제거 시도(실패 시 로그만) + toDelete.forEach( + e -> { + try { + Path p = Paths.get(e.getFilePath(), e.getFileName()); + Files.deleteIfExists(p); + } catch (Exception ex) { + log.warn("파일 삭제 실패: {}", ex.getMessage()); + } + }); + return realIds; + } + private void saveUploadMeta(Path savedPath, Long hstUid) { String fullPath = savedPath.toAbsolutePath().toString(); String fileName = savedPath.getFileName().toString(); diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngFileCheckerCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngFileCheckerCoreService.java index 57b7e4db..305115d0 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngFileCheckerCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngFileCheckerCoreService.java @@ -5,6 +5,7 @@ import com.kamco.cd.kamcoback.mapsheet.dto.ImageryDto; import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity; import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngFileCheckerRepository; import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngRepository; +import java.time.ZonedDateTime; import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; @@ -58,4 +59,12 @@ public class MapSheetMngFileCheckerCoreService { public Optional findHstByUid(Long hstUid) { return mapSheetMngRepository.findMapSheetMngHstInfo(hstUid); } + + public void updateHstSyncCheckStart(Long hstUid) { + mapSheetMngRepository.updateHstSyncCheck(hstUid, null, ZonedDateTime.now(), null); + } + + public void updateHstSyncCheckEnd(Long hstUid, String state) { + mapSheetMngRepository.updateHstSyncCheck(hstUid, state, null, ZonedDateTime.now()); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngFileRepository.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngFileRepository.java index add097f2..1bca8a20 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngFileRepository.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngFileRepository.java @@ -1,10 +1,14 @@ package com.kamco.cd.kamcoback.postgres.repository.mapsheet; import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngFileEntity; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; public interface MapSheetMngFileRepository extends JpaRepository { boolean existsByFileNameAndFilePath(String fileName, String filePath); void deleteByFileNameAndFilePath(String fileName, String filePath); + + // 추가: 특정 경로에서 파일명 목록으로 중복 조회 + List findByFilePathAndFileNameIn(String filePath, List fileNames); } 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 f6348a66..adbeae1a 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 @@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.postgres.repository.mapsheet; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity; import jakarta.validation.Valid; +import java.time.ZonedDateTime; import java.util.Optional; import org.springframework.data.domain.Page; @@ -26,4 +27,7 @@ public interface MapSheetMngRepositoryCustom { MapSheetMngDto.@Valid ErrorSearchReq searchReq); void updateHstFileSizes(Long hstUid, long tifSizeBytes, long tfwSizeBytes, long totalSizeBytes); + + // 동기화 점검 상태/시간 업데이트 (state/start/end 각각 null이면 기존값 유지) + void updateHstSyncCheck(Long hstUid, String state, ZonedDateTime start, ZonedDateTime end); } 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 c447594a..7e026ed7 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 @@ -9,7 +9,6 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity.mapSh import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity; import com.querydsl.core.BooleanBuilder; -import com.querydsl.core.Tuple; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.CaseBuilder; @@ -20,10 +19,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.validation.Valid; -import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import org.hibernate.query.Query; @@ -176,133 +172,62 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport MapSheetMngDto.@Valid ErrorSearchReq searchReq) { Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize()); + BooleanBuilder whereBuilder = new BooleanBuilder(); - whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(searchReq.getMngYyyy())); - whereBuilder.and( - mapSheetMngHstEntity.syncState.ne("DONE").and(mapSheetMngHstEntity.syncState.ne("NOTYET"))); - if (searchReq.getSyncState() != null && !searchReq.getSyncState().isEmpty()) { - if (searchReq.getSyncState().equals("NOTPAIR")) { - whereBuilder.and( - mapSheetMngHstEntity - .syncState - .eq("NOTPAIR") - .or(mapSheetMngHstEntity.syncState.eq("NOFILE"))); - } else if (searchReq.getSyncState().equals("FAULT")) { - whereBuilder.and( - mapSheetMngHstEntity - .syncState - .eq("SIZEERROR") - .or(mapSheetMngHstEntity.syncState.eq("TYPEERROR"))); - } else { - whereBuilder.and(mapSheetMngHstEntity.syncState.eq(searchReq.getSyncState())); - } - } - - if (searchReq.getSyncCheckState() != null && !searchReq.getSyncCheckState().isEmpty()) { - whereBuilder.and(mapSheetMngHstEntity.syncCheckState.eq(searchReq.getSyncCheckState())); - } - - if (searchReq.getSearchValue() != null && !searchReq.getSearchValue().isEmpty()) { + // syncStateFilter 조건 추가 + if (searchReq.getSyncStateFilter() != null && !searchReq.getSyncStateFilter().isEmpty()) { + whereBuilder.and(mapSheetMngHstEntity.syncState.eq(searchReq.getSyncStateFilter())); + } else { + // 기본: 오류 상태만 조회 (NOFILE, NOTPAIR, DUPLICATE, SIZEERROR, TYPEERROR) whereBuilder.and( mapSheetMngHstEntity - .mapSheetNum - .eq(searchReq.getSearchValue()) - .or(mapSheetMngHstEntity.refMapSheetNum.eq(searchReq.getSearchValue())) - .or( - Expressions.stringTemplate( - "concat({0},substring({1}, 0, 6))", - mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum) - .likeIgnoreCase(searchReq.getSearchValue())) - .or( - Expressions.stringTemplate( - "concat({0},substring({1}, 6, 8))", - mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum) - .likeIgnoreCase(searchReq.getSearchValue()))); + .syncState + .eq("NOFILE") + .or(mapSheetMngHstEntity.syncState.eq("NOTPAIR")) + .or(mapSheetMngHstEntity.syncState.eq("DUPLICATE")) + .or(mapSheetMngHstEntity.syncState.eq("SIZEERROR")) + .or(mapSheetMngHstEntity.syncState.eq("TYPEERROR"))); } - List tuples = + // 검색어 조건 추가 + if (searchReq.getSearchValue() != null && !searchReq.getSearchValue().isEmpty()) { + whereBuilder.and(mapSheetErrorSearchValue(searchReq)); + } + + List foundContent = queryFactory .select( - mapSheetMngHstEntity.hstUid, - mapSheetMngHstEntity.mapSheetName, - mapInkx5kEntity.mapidNm, - mapSheetMngHstEntity.mapSheetNum, - mapSheetMngHstEntity.mapSheetCodeSrc, - mapSheetMngHstEntity.createdDate, - mapSheetMngHstEntity.syncState, - mapSheetMngHstEntity.syncCheckState, - mapSheetMngFileEntity.fileUid, - mapSheetMngFileEntity.filePath, - mapSheetMngFileEntity.fileName, - mapSheetMngFileEntity.fileSize, - mapSheetMngFileEntity.fileState) + Projections.constructor( + MapSheetMngDto.ErrorDataDto.class, + mapSheetMngHstEntity.hstUid, + rowNum(), + Expressions.stringTemplate( + "concat({0}, {1})", + mapSheetMngHstEntity.mapSheetName, mapInkx50kEntity.mapidcdNo), + Expressions.stringTemplate( + "concat({0}, substring({1}, {2}, {3}))", + mapSheetMngHstEntity.mapSheetName, mapSheetMngHstEntity.mapSheetNum, 6, 8), + mapSheetMngHstEntity.mapSheetCodeSrc, + Expressions.stringTemplate( + "to_char({0}, 'YYYY-MM-DD')", mapSheetMngHstEntity.createdDate), + mapSheetMngHstEntity.dataState, + mapSheetMngHstEntity.syncState, + mapSheetMngHstEntity.syncCheckState, + mapSheetMngHstEntity.syncCheckStrtDttm, + mapSheetMngHstEntity.syncCheckEndDttm)) .from(mapSheetMngHstEntity) .innerJoin(mapInkx5kEntity) - .on(mapSheetMngHstEntity.mapSheetNum.eq(mapInkx5kEntity.mapidcdNo)) - .leftJoin(mapSheetMngFileEntity) - .on(mapSheetMngFileEntity.hstUid.eq(mapSheetMngHstEntity.hstUid)) + .on(mapSheetMngHstEntity.mapSheetCode.eq(mapInkx5kEntity.fid)) + .leftJoin(mapInkx50kEntity) + .on(mapInkx5kEntity.fidK50.eq(mapInkx50kEntity.fid.longValue())) .where(whereBuilder) - .orderBy(mapSheetMngHstEntity.createdDate.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) + .orderBy(mapSheetMngHstEntity.createdDate.desc()) .fetch(); - Map resultMap = new LinkedHashMap<>(); - - for (Tuple t : tuples) { - - Long hstUid = t.get(mapSheetMngHstEntity.hstUid); - - MapSheetMngDto.ErrorDataDto dto = - resultMap.computeIfAbsent( - hstUid, - id -> { - String map50kName = - t.get(mapSheetMngHstEntity.mapSheetName) - + t.get(mapSheetMngHstEntity.mapSheetNum).substring(0, 5); - - String map5kName = - t.get(mapSheetMngHstEntity.mapSheetName) - + t.get(mapSheetMngHstEntity.mapSheetNum).substring(5, 8); - - String mapSrcName = - t.get(mapSheetMngHstEntity.mapSheetName) - + t.get(mapSheetMngHstEntity.mapSheetNum).substring(5, 8); - - MapSheetMngDto.ErrorDataDto newDto = - new MapSheetMngDto.ErrorDataDto( - id, - map50kName, - map5kName, - mapSrcName, - t.get(mapSheetMngHstEntity.mapSheetCodeSrc), - t.get(mapSheetMngHstEntity.createdDate), - t.get(mapSheetMngHstEntity.syncState), - t.get(mapSheetMngHstEntity.syncCheckState)); - - newDto.setFileArray(new ArrayList<>()); - return newDto; - }); - - // 파일 정보가 있는 경우만 추가 - Long fileUid = t.get(mapSheetMngFileEntity.fileUid); - if (fileUid != null) { - MapSheetMngDto.MngFIleDto fileDto = new MapSheetMngDto.MngFIleDto(); - fileDto.setFileUid(fileUid); - fileDto.setFilePath(t.get(mapSheetMngFileEntity.filePath)); - fileDto.setFileName(t.get(mapSheetMngFileEntity.fileName)); - fileDto.setFileSize(t.get(mapSheetMngFileEntity.fileSize)); - fileDto.setFileState(t.get(mapSheetMngFileEntity.fileState)); - fileDto.setHstUid(hstUid); - - dto.getFileArray().add(fileDto); - } - } - - List foundContent = new ArrayList<>(resultMap.values()); - Long countQuery = queryFactory .select(mapSheetMngHstEntity.hstUid.count()) diff --git a/src/main/resources/db/migration/dump-kamco_cds-202512122050.tar b/src/main/resources/db/migration/dump-kamco_cds-202512161836.tar similarity index 89% rename from src/main/resources/db/migration/dump-kamco_cds-202512122050.tar rename to src/main/resources/db/migration/dump-kamco_cds-202512161836.tar index 5fb52862..a6ac6730 100644 Binary files a/src/main/resources/db/migration/dump-kamco_cds-202512122050.tar and b/src/main/resources/db/migration/dump-kamco_cds-202512161836.tar differ