Merge pull request '영상관리 추가 수정' (#66) from feat/dev_251201 into develop
Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/66
This commit is contained in:
@@ -152,55 +152,97 @@ public class FIleChecker {
|
|||||||
File file = new File(filePath);
|
File file = new File(filePath);
|
||||||
|
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
|
System.err.println("파일이 존재하지 않습니다: " + filePath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasDriver = false;
|
boolean hasDriver = false;
|
||||||
|
|
||||||
|
// 운영체제 감지
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
boolean isWindows = osName.contains("win");
|
||||||
|
boolean isMac = osName.contains("mac");
|
||||||
|
boolean isUnix = osName.contains("nix") || osName.contains("nux") || osName.contains("aix");
|
||||||
|
|
||||||
|
// gdalinfo 경로 찾기 (일반적인 설치 경로 우선 확인)
|
||||||
|
String gdalinfoPath = findGdalinfoPath();
|
||||||
|
if (gdalinfoPath == null) {
|
||||||
|
System.err.println("gdalinfo 명령어를 찾을 수 없습니다. GDAL이 설치되어 있는지 확인하세요.");
|
||||||
|
System.err.println("macOS: brew install gdal");
|
||||||
|
System.err.println("Ubuntu/Debian: sudo apt-get install gdal-bin");
|
||||||
|
System.err.println("CentOS/RHEL: sudo yum install gdal");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
List<String> command = new ArrayList<>();
|
List<String> command = new ArrayList<>();
|
||||||
// 윈도우용
|
|
||||||
|
|
||||||
command.add("cmd.exe"); // 윈도우 명령 프롬프트 실행
|
if (isWindows) {
|
||||||
command.add("/c"); // 명령어를 수행하고 종료한다는 옵션
|
// 윈도우용
|
||||||
command.add("gdalinfo");
|
command.add("cmd.exe");
|
||||||
command.add(filePath);
|
command.add("/c");
|
||||||
command.add("|");
|
command.add(gdalinfoPath + " \"" + filePath + "\" | findstr /i Geo");
|
||||||
command.add("findstr");
|
} else if (isMac || isUnix) {
|
||||||
command.add("/i");
|
// 리눅스, 맥용
|
||||||
command.add("Geo");
|
command.add("sh");
|
||||||
|
command.add("-c");
|
||||||
/*
|
command.add(gdalinfoPath + " \"" + filePath + "\" | grep -i Geo");
|
||||||
command.add("sh"); // 리눅스,맥 명령 프롬프트 실행
|
} else {
|
||||||
command.add("-c"); // 명령어를 수행하고 종료한다는 옵션
|
System.err.println("지원하지 않는 운영체제: " + osName);
|
||||||
command.add("gdalinfo");
|
return false;
|
||||||
command.add(filePath);
|
}
|
||||||
command.add("|");
|
|
||||||
command.add("grep");
|
|
||||||
command.add("-i");
|
|
||||||
command.add("Geo");
|
|
||||||
*/
|
|
||||||
|
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||||
processBuilder.redirectErrorStream(true);
|
processBuilder.redirectErrorStream(true);
|
||||||
|
|
||||||
|
Process process = null;
|
||||||
|
BufferedReader reader = null;
|
||||||
try {
|
try {
|
||||||
Process process = processBuilder.start();
|
System.out.println("gdalinfo 명령어 실행 시작: " + filePath);
|
||||||
|
process = processBuilder.start();
|
||||||
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
System.out.println("line == " + line);
|
System.out.println("gdalinfo 출력: " + line);
|
||||||
if (line.contains("Driver: GTiff/GeoTIFF")) {
|
if (line.contains("Driver: GTiff/GeoTIFF")) {
|
||||||
hasDriver = true;
|
hasDriver = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
process.waitFor();
|
int exitCode = process.waitFor();
|
||||||
|
System.out.println("gdalinfo 종료 코드: " + exitCode);
|
||||||
|
|
||||||
} catch (Exception e) {
|
// 프로세스가 정상 종료되지 않았고 Driver를 찾지 못한 경우
|
||||||
|
if (exitCode != 0 && !hasDriver) {
|
||||||
|
System.err.println("gdalinfo 명령 실행 실패. Exit code: " + exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("gdalinfo 실행 중 I/O 오류 발생: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.err.println("gdalinfo 실행 중 인터럽트 발생: " + e.getMessage());
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("gdalinfo 실행 중 예상치 못한 오류 발생: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
// 리소스 정리
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("BufferedReader 종료 중 오류: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process != null) {
|
||||||
|
process.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasDriver;
|
return hasDriver;
|
||||||
@@ -349,4 +391,54 @@ public class FIleChecker {
|
|||||||
return nameComparator;
|
return nameComparator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdalinfo 실행 파일 경로를 찾습니다.
|
||||||
|
*
|
||||||
|
* @return gdalinfo 경로 (찾지 못하면 null)
|
||||||
|
*/
|
||||||
|
private static String findGdalinfoPath() {
|
||||||
|
// 일반적인 설치 경로 확인
|
||||||
|
String[] possiblePaths = {
|
||||||
|
"/usr/local/bin/gdalinfo", // Homebrew (macOS)
|
||||||
|
"/opt/homebrew/bin/gdalinfo", // Homebrew (Apple Silicon macOS)
|
||||||
|
"/usr/bin/gdalinfo", // Linux
|
||||||
|
"gdalinfo" // PATH에 있는 경우
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String path : possiblePaths) {
|
||||||
|
if (isCommandAvailable(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 명령어가 사용 가능한지 확인합니다.
|
||||||
|
*
|
||||||
|
* @param command 명령어 경로
|
||||||
|
* @return 사용 가능 여부
|
||||||
|
*/
|
||||||
|
private static boolean isCommandAvailable(String command) {
|
||||||
|
try {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(command, "--version");
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
Process process = pb.start();
|
||||||
|
|
||||||
|
// 프로세스 완료 대기 (최대 5초)
|
||||||
|
boolean finished = process.waitFor(5, java.util.concurrent.TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
if (!finished) {
|
||||||
|
process.destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 종료 코드가 0이면 정상 (일부 명령어는 --version에서 다른 코드 반환할 수 있음)
|
||||||
|
return process.exitValue() == 0 || process.exitValue() == 1;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
|||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
import org.springframework.web.client.HttpServerErrorException;
|
import org.springframework.web.client.HttpServerErrorException;
|
||||||
|
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Order(value = 1)
|
@Order(value = 1)
|
||||||
@@ -396,7 +397,8 @@ public class GlobalExceptionHandler {
|
|||||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
@ExceptionHandler(Exception.class)
|
@ExceptionHandler(Exception.class)
|
||||||
public ApiResponseDto<String> handlerException(Exception e, HttpServletRequest request) {
|
public ApiResponseDto<String> handlerException(Exception e, HttpServletRequest request) {
|
||||||
log.warn("[Exception] resource :{} ", e.getMessage());
|
log.error("[Exception] resource: {}, message: {}", request.getRequestURI(), e.getMessage());
|
||||||
|
log.error("Exception stacktrace: ", e);
|
||||||
|
|
||||||
String codeName = "INTERNAL_SERVER_ERROR";
|
String codeName = "INTERNAL_SERVER_ERROR";
|
||||||
ErrorLogEntity errorLog =
|
ErrorLogEntity errorLog =
|
||||||
@@ -504,4 +506,22 @@ public class GlobalExceptionHandler {
|
|||||||
|
|
||||||
// return new ResponseEntity<>(body, status);
|
// return new ResponseEntity<>(body, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
|
||||||
|
@ExceptionHandler(MaxUploadSizeExceededException.class)
|
||||||
|
public ApiResponseDto<String> handleMaxUploadSizeExceeded(
|
||||||
|
MaxUploadSizeExceededException e, HttpServletRequest request) {
|
||||||
|
log.warn("[MaxUploadSizeExceededException] resource :{} ", e.getMessage());
|
||||||
|
ApiResponseCode code = ApiResponseCode.PAYLOAD_TOO_LARGE;
|
||||||
|
ErrorLogEntity errorLog =
|
||||||
|
saveErrorLogData(
|
||||||
|
request,
|
||||||
|
code,
|
||||||
|
HttpStatus.PAYLOAD_TOO_LARGE,
|
||||||
|
ErrorLogDto.LogErrorLevel.WARNING,
|
||||||
|
e.getStackTrace());
|
||||||
|
|
||||||
|
return ApiResponseDto.createException(
|
||||||
|
code, code.getText(), HttpStatus.PAYLOAD_TOO_LARGE, errorLog.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ public class ApiResponseDto<T> {
|
|||||||
"You can only reset your password within 24 hours from when the email was sent.\n"
|
"You can only reset your password within 24 hours from when the email was sent.\n"
|
||||||
+ "To reset your password again, please submit a new request through \"Forgot"
|
+ "To reset your password again, please submit a new request through \"Forgot"
|
||||||
+ " Password.\""),
|
+ " Password.\""),
|
||||||
|
PAYLOAD_TOO_LARGE("업로드 용량 제한을 초과했습니다."),
|
||||||
;
|
;
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|||||||
@@ -40,12 +40,28 @@ public class MapSheetMngApiController {
|
|||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||||
})
|
})
|
||||||
@PostMapping("/mng-list")
|
@PostMapping("/mng-list")
|
||||||
public ApiResponseDto<Page<MapSheetMngDto.MngDto>> findMapSheetMngList(
|
public ApiResponseDto<List<MapSheetMngDto.MngDto>> findMapSheetMngList() {
|
||||||
@RequestBody MapSheetMngDto.MngSearchReq searchReq) {
|
|
||||||
|
|
||||||
System.out.println("kkkkkkkkkkkkkkkkkkkkkkkkk");
|
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngList());
|
||||||
|
}
|
||||||
|
|
||||||
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngList(searchReq));
|
@Operation(summary = "영상데이터관리 상세", description = "영상데이터관리 상세")
|
||||||
|
@ApiResponses(
|
||||||
|
value = {
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "200",
|
||||||
|
description = "조회 성공",
|
||||||
|
content =
|
||||||
|
@Content(
|
||||||
|
mediaType = "application/json",
|
||||||
|
schema = @Schema(implementation = CommonCodeDto.Basic.class))),
|
||||||
|
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
|
||||||
|
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||||
|
})
|
||||||
|
@GetMapping("/mng")
|
||||||
|
public ApiResponseDto<MapSheetMngDto.MngDto> findMapSheetMng(@RequestParam int mngYyyy) {
|
||||||
|
|
||||||
|
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMng(mngYyyy));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "영상관리 > 데이터 등록", description = "영상관리 > 데이터 등록")
|
@Operation(summary = "영상관리 > 데이터 등록", description = "영상관리 > 데이터 등록")
|
||||||
@@ -71,40 +87,9 @@ public class MapSheetMngApiController {
|
|||||||
/**
|
/**
|
||||||
* 오류데이터 목록 조회
|
* 오류데이터 목록 조회
|
||||||
*
|
*
|
||||||
* <p>도엽파일 동기화 시 발생한 오류 데이터를 조회합니다.
|
* @param searchReq
|
||||||
*
|
* @return
|
||||||
* <p>오류 타입:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>NOFILE - 파일없음
|
|
||||||
* <li>NOTPAIR - 페어없음 (tif/tfw 중 하나만 존재)
|
|
||||||
* <li>DUPLICATE - 중복 (동일한 파일이 여러개 존재)
|
|
||||||
* <li>SIZEERROR - size 0 (파일 크기가 0)
|
|
||||||
* <li>TYPEERROR - 형식오류 (tfw 검증 실패 또는 GeoTIFF 검증 실패)
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <p>sync_check_strt_dttm과 sync_check_end_dttm은 동일한 값으로 설정됩니다.
|
|
||||||
*
|
|
||||||
* @param searchReq 검색 조건 (년도, 검색어, syncStateFilter 등)
|
|
||||||
* @return 오류 데이터 목록
|
|
||||||
*/
|
*/
|
||||||
@Operation(
|
|
||||||
summary = "오류데이터 목록 조회",
|
|
||||||
description =
|
|
||||||
"도엽파일 동기화 시 발생한 오류 데이터를 조회합니다. "
|
|
||||||
+ "오류 타입: NOFILE(파일없음), NOTPAIR(페어없음), DUPLICATE(중복), SIZEERROR(size 0), TYPEERROR(형식오류)")
|
|
||||||
@ApiResponses(
|
|
||||||
value = {
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "200",
|
|
||||||
description = "조회 성공",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = "application/json",
|
|
||||||
schema = @Schema(implementation = MapSheetMngDto.ErrorDataDto.class))),
|
|
||||||
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
|
|
||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
|
||||||
})
|
|
||||||
@PostMapping("/error-list")
|
@PostMapping("/error-list")
|
||||||
public ApiResponseDto<Page<MapSheetMngDto.ErrorDataDto>> findMapSheetErrorList(
|
public ApiResponseDto<Page<MapSheetMngDto.ErrorDataDto>> findMapSheetErrorList(
|
||||||
@RequestBody @Valid MapSheetMngDto.ErrorSearchReq searchReq) {
|
@RequestBody @Valid MapSheetMngDto.ErrorSearchReq searchReq) {
|
||||||
|
|||||||
@@ -122,17 +122,18 @@ public class MapSheetMngDto {
|
|||||||
@Schema(description = "정렬", example = "id desc")
|
@Schema(description = "정렬", example = "id desc")
|
||||||
private String sort;
|
private String sort;
|
||||||
|
|
||||||
|
@Schema(description = "오류종류(페어누락:NOTPAIR,중복파일:DUPLICATE,손상파일:FAULT)", example = "NOTPAIR")
|
||||||
|
private String syncState;
|
||||||
|
|
||||||
|
@Schema(description = "처리유형(처리:DONE,미처리:NOTYET)", example = "DONE")
|
||||||
|
private String syncCheckState;
|
||||||
|
|
||||||
@Schema(description = "검색어", example = "부산3959")
|
@Schema(description = "검색어", example = "부산3959")
|
||||||
private String searchValue;
|
private String searchValue;
|
||||||
|
|
||||||
@Schema(description = "년도", example = "2025")
|
@Schema(description = "년도", example = "2025")
|
||||||
private Integer mngYyyy;
|
private Integer mngYyyy;
|
||||||
|
|
||||||
@Schema(
|
|
||||||
description = "동기화 상태 필터 (NOFILE, NOTPAIR, DUPLICATE, SIZEERROR, TYPEERROR)",
|
|
||||||
example = "NOFILE")
|
|
||||||
private String syncStateFilter;
|
|
||||||
|
|
||||||
public Pageable toPageable() {
|
public Pageable toPageable() {
|
||||||
if (sort != null && !sort.isEmpty()) {
|
if (sort != null && !sort.isEmpty()) {
|
||||||
String[] sortParams = sort.split(",");
|
String[] sortParams = sort.split(",");
|
||||||
@@ -149,27 +150,57 @@ public class MapSheetMngDto {
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
|
||||||
public static class ErrorDataDto {
|
public static class ErrorDataDto {
|
||||||
|
// private Integer rowNum;
|
||||||
private Long hstUid;
|
private Long hstUid;
|
||||||
private Integer rowNum;
|
|
||||||
private String map50kName;
|
private String map50kName;
|
||||||
private String map5kName;
|
private String map5kName;
|
||||||
|
private String mapSrcName;
|
||||||
private Integer mapCodeSrc;
|
private Integer mapCodeSrc;
|
||||||
private String createdDttm;
|
@JsonFormatDttm private ZonedDateTime createdDttm;
|
||||||
private DataState dataState;
|
|
||||||
|
|
||||||
@Schema(description = "동기화 상태 (NOFILE, NOTPAIR, DUPLICATE, SIZEERROR, TYPEERROR)")
|
|
||||||
private String syncState;
|
private String syncState;
|
||||||
|
|
||||||
@Schema(description = "동기화 체크 상태")
|
|
||||||
private String syncCheckState;
|
private String syncCheckState;
|
||||||
|
|
||||||
@Schema(description = "동기화 체크 시작 시간")
|
// private Long fileUid;
|
||||||
private java.time.LocalDateTime syncCheckStrtDttm;
|
private String tfwFileName;
|
||||||
|
private String tifFileName;
|
||||||
|
|
||||||
@Schema(description = "동기화 체크 종료 시간")
|
// private List<MngFIleDto> fileArray;
|
||||||
private java.time.LocalDateTime syncCheckEndDttm;
|
|
||||||
|
public ErrorDataDto(
|
||||||
|
Long hstUid,
|
||||||
|
String map50kName,
|
||||||
|
String map5kName,
|
||||||
|
String mapSrcName,
|
||||||
|
Integer mapCodeSrc,
|
||||||
|
ZonedDateTime createdDttm,
|
||||||
|
String syncState,
|
||||||
|
String syncCheckState,
|
||||||
|
String tfwFileName,
|
||||||
|
String tifFileName) {
|
||||||
|
this.hstUid = hstUid;
|
||||||
|
this.map50kName = map50kName;
|
||||||
|
this.map5kName = map5kName;
|
||||||
|
this.mapSrcName = mapSrcName;
|
||||||
|
this.mapCodeSrc = mapCodeSrc;
|
||||||
|
this.createdDttm = createdDttm;
|
||||||
|
this.syncState = syncState;
|
||||||
|
this.syncCheckState = syncCheckState;
|
||||||
|
this.tfwFileName = tfwFileName;
|
||||||
|
this.tifFileName = tifFileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schema(name = "MngFIleDto", description = "관리파일정보")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class MngFIleDto {
|
||||||
|
private Long fileUid;
|
||||||
|
private String filePath;
|
||||||
|
private String fileName;
|
||||||
|
private Long fileSize;
|
||||||
|
private String fileState;
|
||||||
|
private Long hstUid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(name = "DmlReturn", description = "영상관리 DML 수행 후 리턴")
|
@Schema(name = "DmlReturn", description = "영상관리 DML 수행 후 리턴")
|
||||||
@@ -241,27 +272,4 @@ public class MapSheetMngDto {
|
|||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum SyncErrorState implements EnumType {
|
|
||||||
NOFILE("파일없음"),
|
|
||||||
NOTPAIR("페어없음"),
|
|
||||||
DUPLICATE("중복"),
|
|
||||||
SIZEERROR("size 0"),
|
|
||||||
TYPEERROR("형식오류"),
|
|
||||||
DONE("정상");
|
|
||||||
|
|
||||||
private final String desc;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return name();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getText() {
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,13 @@ import java.util.Set;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@@ -318,11 +320,34 @@ public class MapSheetMngFileCheckerService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public String uploadFile(MultipartFile file, String targetPath, boolean overwrite, Long hstUid) {
|
public String uploadFile(MultipartFile file, String targetPath, boolean overwrite, Long hstUid) {
|
||||||
try {
|
try {
|
||||||
|
// 파일 유효성 검증
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
throw new ValidationException("업로드 파일이 비어있습니다.");
|
||||||
|
}
|
||||||
|
if (file.getOriginalFilename() == null || file.getOriginalFilename().isEmpty()) {
|
||||||
|
throw new ValidationException("파일명이 유효하지 않습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
Path path = Paths.get(targetPath);
|
Path path = Paths.get(targetPath);
|
||||||
if (Files.isDirectory(path)) {
|
|
||||||
|
// targetPath가 존재하지 않으면 파일 경로로 가정하고 부모 디렉토리 생성
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
// 경로가 확장자로 끝나면 파일로 간주
|
||||||
|
if (targetPath.matches(".*\\.[a-zA-Z]{3,4}$")) {
|
||||||
|
if (path.getParent() != null) {
|
||||||
|
Files.createDirectories(path.getParent());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 확장자가 없으면 디렉토리로 간주
|
||||||
|
Files.createDirectories(path);
|
||||||
|
path = path.resolve(file.getOriginalFilename());
|
||||||
|
}
|
||||||
|
} else if (Files.isDirectory(path)) {
|
||||||
path = path.resolve(file.getOriginalFilename());
|
path = path.resolve(file.getOriginalFilename());
|
||||||
}
|
}
|
||||||
if (path.getParent() != null) {
|
|
||||||
|
// 최종 파일의 부모 디렉토리 생성
|
||||||
|
if (path.getParent() != null && !Files.exists(path.getParent())) {
|
||||||
Files.createDirectories(path.getParent());
|
Files.createDirectories(path.getParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,8 +428,13 @@ public class MapSheetMngFileCheckerService {
|
|||||||
saveUploadMeta(path, hstUid);
|
saveUploadMeta(path, hstUid);
|
||||||
|
|
||||||
return "업로드 성공";
|
return "업로드 성공";
|
||||||
|
} catch (ValidationException | DuplicateFileException e) {
|
||||||
|
// 비즈니스 예외는 그대로 던짐
|
||||||
|
throw e;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage());
|
throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("파일 업로드 처리 중 오류 발생: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,8 +446,45 @@ public class MapSheetMngFileCheckerService {
|
|||||||
boolean overwrite,
|
boolean overwrite,
|
||||||
Long hstUid) {
|
Long hstUid) {
|
||||||
try {
|
try {
|
||||||
|
log.info(
|
||||||
|
"uploadPair 시작 - targetPath: {}, overwrite: {}, hstUid: {}",
|
||||||
|
targetPath,
|
||||||
|
overwrite,
|
||||||
|
hstUid);
|
||||||
|
|
||||||
|
// 파일 유효성 검증
|
||||||
|
if (tfwFile == null || tfwFile.isEmpty()) {
|
||||||
|
throw new ValidationException("TFW 파일이 비어있습니다.");
|
||||||
|
}
|
||||||
|
if (tifFile == null || tifFile.isEmpty()) {
|
||||||
|
throw new ValidationException("TIF 파일이 비어있습니다.");
|
||||||
|
}
|
||||||
|
if (tfwFile.getOriginalFilename() == null || tfwFile.getOriginalFilename().isEmpty()) {
|
||||||
|
throw new ValidationException("TFW 파일명이 유효하지 않습니다.");
|
||||||
|
}
|
||||||
|
if (tifFile.getOriginalFilename() == null || tifFile.getOriginalFilename().isEmpty()) {
|
||||||
|
throw new ValidationException("TIF 파일명이 유효하지 않습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
"파일명 - TFW: {}, TIF: {}", tfwFile.getOriginalFilename(), tifFile.getOriginalFilename());
|
||||||
|
|
||||||
Path basePath = Paths.get(targetPath);
|
Path basePath = Paths.get(targetPath);
|
||||||
|
|
||||||
|
// targetPath가 존재하지 않으면 디렉토리로 생성
|
||||||
|
if (!Files.exists(basePath)) {
|
||||||
|
log.info("대상 경로가 존재하지 않아 디렉토리 생성: {}", basePath);
|
||||||
|
Files.createDirectories(basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 파일인 경우 부모 디렉토리를 basePath로 사용
|
||||||
|
if (Files.isRegularFile(basePath)) {
|
||||||
|
log.info("대상 경로가 파일이므로 부모 디렉토리 사용");
|
||||||
|
basePath = basePath.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
if (Files.isDirectory(basePath)) {
|
if (Files.isDirectory(basePath)) {
|
||||||
|
log.info("디렉토리 확인됨: {}", basePath);
|
||||||
// 디렉토리인 경우 파일명 기준으로 경로 생성
|
// 디렉토리인 경우 파일명 기준으로 경로 생성
|
||||||
Path tfwPath = basePath.resolve(tfwFile.getOriginalFilename());
|
Path tfwPath = basePath.resolve(tfwFile.getOriginalFilename());
|
||||||
Path tifPath = basePath.resolve(tifFile.getOriginalFilename());
|
Path tifPath = basePath.resolve(tifFile.getOriginalFilename());
|
||||||
@@ -427,9 +494,9 @@ public class MapSheetMngFileCheckerService {
|
|||||||
if (!tfwBase.equalsIgnoreCase(tifBase)) {
|
if (!tfwBase.equalsIgnoreCase(tifBase)) {
|
||||||
throw new ValidationException("TFW/TIF 파일명이 동일한 베이스가 아닙니다.");
|
throw new ValidationException("TFW/TIF 파일명이 동일한 베이스가 아닙니다.");
|
||||||
}
|
}
|
||||||
// 디렉토리 생성
|
// 디렉토리는 이미 생성되었으므로 추가 생성 불필요
|
||||||
if (tfwPath.getParent() != null) Files.createDirectories(tfwPath.getParent());
|
// if (tfwPath.getParent() != null) Files.createDirectories(tfwPath.getParent());
|
||||||
if (tifPath.getParent() != null) Files.createDirectories(tifPath.getParent());
|
// if (tifPath.getParent() != null) Files.createDirectories(tifPath.getParent());
|
||||||
|
|
||||||
// DB 중복 체크 및 overwrite 처리 (각 파일별)
|
// DB 중복 체크 및 overwrite 처리 (각 파일별)
|
||||||
String parentPathStr = basePath.toString();
|
String parentPathStr = basePath.toString();
|
||||||
@@ -450,32 +517,51 @@ public class MapSheetMngFileCheckerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 파일 저장
|
// 파일 저장
|
||||||
|
log.info("파일 저장 시작 - TFW: {}, TIF: {}", tfwPath, tifPath);
|
||||||
tfwFile.transferTo(tfwPath.toFile());
|
tfwFile.transferTo(tfwPath.toFile());
|
||||||
tifFile.transferTo(tifPath.toFile());
|
tifFile.transferTo(tifPath.toFile());
|
||||||
|
log.info("파일 저장 완료");
|
||||||
|
|
||||||
// 검증
|
// 검증
|
||||||
|
log.info("TFW 파일 검증 시작: {}", tfwPath);
|
||||||
boolean tfwOk = FIleChecker.checkTfw(tfwPath.toString());
|
boolean tfwOk = FIleChecker.checkTfw(tfwPath.toString());
|
||||||
if (!tfwOk) {
|
if (!tfwOk) {
|
||||||
|
log.warn("TFW 파일 검증 실패: {}", tfwName);
|
||||||
Files.deleteIfExists(tfwPath);
|
Files.deleteIfExists(tfwPath);
|
||||||
Files.deleteIfExists(tifPath);
|
Files.deleteIfExists(tifPath);
|
||||||
throw new ValidationException("유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + tfwName);
|
throw new ValidationException("유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + tfwName);
|
||||||
}
|
}
|
||||||
|
log.info("TFW 파일 검증 성공");
|
||||||
|
|
||||||
|
log.info("TIF 파일 검증 시작: {}", tifPath);
|
||||||
boolean isValidTif = FIleChecker.cmmndGdalInfo(tifPath.toString());
|
boolean isValidTif = FIleChecker.cmmndGdalInfo(tifPath.toString());
|
||||||
if (!isValidTif) {
|
if (!isValidTif) {
|
||||||
|
log.warn("TIF 파일 검증 실패: {}", tifName);
|
||||||
Files.deleteIfExists(tfwPath);
|
Files.deleteIfExists(tfwPath);
|
||||||
Files.deleteIfExists(tifPath);
|
Files.deleteIfExists(tifPath);
|
||||||
throw new ValidationException("유효하지 않은 TIF 파일입니다 (GDAL 검증 실패): " + tifName);
|
throw new ValidationException("유효하지 않은 TIF 파일입니다 (GDAL 검증 실패): " + tifName);
|
||||||
}
|
}
|
||||||
|
log.info("TIF 파일 검증 성공");
|
||||||
|
|
||||||
// 메타 저장 (두 파일 각각 저장)
|
// 메타 저장 (두 파일 각각 저장)
|
||||||
|
log.info("메타 데이터 저장 시작");
|
||||||
saveUploadMeta(tfwPath, hstUid);
|
saveUploadMeta(tfwPath, hstUid);
|
||||||
saveUploadMeta(tifPath, hstUid);
|
saveUploadMeta(tifPath, hstUid);
|
||||||
|
log.info("메타 데이터 저장 완료");
|
||||||
return "TFW/TIF 페어 업로드 성공";
|
return "TFW/TIF 페어 업로드 성공";
|
||||||
} else {
|
} else {
|
||||||
throw new ValidationException("targetPath는 디렉토리여야 합니다.");
|
throw new ValidationException("targetPath는 디렉토리여야 합니다.");
|
||||||
}
|
}
|
||||||
|
} catch (ValidationException | DuplicateFileException e) {
|
||||||
|
// 비즈니스 예외는 그대로 던짐
|
||||||
|
log.warn("업로드 비즈니스 예외 발생: {}", e.getMessage());
|
||||||
|
throw e;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage());
|
log.error("파일 I/O 처리 실패: {}", e.getMessage(), e);
|
||||||
|
throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("파일 업로드 처리 중 예상치 못한 오류 발생: {}", e.getMessage(), e);
|
||||||
|
throw new IllegalArgumentException("파일 업로드 처리 중 오류 발생: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,15 +212,19 @@ public class MapSheetMngService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MapSheetMngDto.MngDto> findMapSheetMngList() {
|
||||||
|
return mapSheetMngCoreService.findMapSheetMngList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) {
|
||||||
|
return mapSheetMngCoreService.findMapSheetMng(mngYyyy);
|
||||||
|
}
|
||||||
|
|
||||||
public Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList(
|
public Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList(
|
||||||
MapSheetMngDto.@Valid ErrorSearchReq searchReq) {
|
MapSheetMngDto.@Valid ErrorSearchReq searchReq) {
|
||||||
return mapSheetMngCoreService.findMapSheetErrorList(searchReq);
|
return mapSheetMngCoreService.findMapSheetErrorList(searchReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<MapSheetMngDto.MngDto> findMapSheetMngList(MapSheetMngDto.MngSearchReq searchReq) {
|
|
||||||
return mapSheetMngCoreService.findMapSheetMngList(searchReq);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public MapSheetMngDto.DmlReturn mngDataSave(MapSheetMngDto.AddReq AddReq) {
|
public MapSheetMngDto.DmlReturn mngDataSave(MapSheetMngDto.AddReq AddReq) {
|
||||||
return mapSheetMngCoreService.mngDataSave(AddReq);
|
return mapSheetMngCoreService.mngDataSave(AddReq);
|
||||||
|
|||||||
@@ -37,9 +37,12 @@ public class MapSheetMngCoreService {
|
|||||||
return mapSheetMngRepository.findMapSheetErrorList(searchReq);
|
return mapSheetMngRepository.findMapSheetErrorList(searchReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<MapSheetMngDto.MngDto> findMapSheetMngList(
|
public List<MapSheetMngDto.MngDto> findMapSheetMngList() {
|
||||||
MapSheetMngDto.@Valid MngSearchReq searchReq) {
|
return mapSheetMngRepository.findMapSheetMngList();
|
||||||
return mapSheetMngRepository.findMapSheetMngList(searchReq);
|
}
|
||||||
|
|
||||||
|
public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) {
|
||||||
|
return mapSheetMngRepository.findMapSheetMng(mngYyyy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapSheetMngDto.DmlReturn uploadProcess(@Valid List<Long> hstUidList) {
|
public MapSheetMngDto.DmlReturn uploadProcess(@Valid List<Long> hstUidList) {
|
||||||
|
|||||||
@@ -3,12 +3,15 @@ package com.kamco.cd.kamcoback.postgres.repository.mapsheet;
|
|||||||
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
|
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
|
||||||
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
|
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
|
|
||||||
public interface MapSheetMngRepositoryCustom {
|
public interface MapSheetMngRepositoryCustom {
|
||||||
|
|
||||||
Page<MapSheetMngDto.MngDto> findMapSheetMngList(MapSheetMngDto.MngSearchReq searchReq);
|
List<MapSheetMngDto.MngDto> findMapSheetMngList();
|
||||||
|
|
||||||
|
MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy);
|
||||||
|
|
||||||
Optional<MapSheetMngHstEntity> findMapSheetMngHstInfo(Long hstUid);
|
Optional<MapSheetMngHstEntity> findMapSheetMngHstInfo(Long hstUid);
|
||||||
|
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<MapSheetMngDto.MngDto> findMapSheetMngList(MapSheetMngDto.MngSearchReq searchReq) {
|
public List<MapSheetMngDto.MngDto> findMapSheetMngList() {
|
||||||
|
|
||||||
Pageable pageable = searchReq.toPageable();
|
// Pageable pageable = searchReq.toPageable();
|
||||||
BooleanBuilder whereBuilder = new BooleanBuilder();
|
BooleanBuilder whereBuilder = new BooleanBuilder();
|
||||||
|
|
||||||
if (searchReq.getMngYyyy() != null) {
|
// if (searchReq.getMngYyyy() != null) {
|
||||||
whereBuilder.and(mapSheetMngEntity.mngYyyy.eq(searchReq.getMngYyyy()));
|
// whereBuilder.and(mapSheetMngEntity.mngYyyy.eq(searchReq.getMngYyyy()));
|
||||||
}
|
// }
|
||||||
|
|
||||||
NumberExpression<Long> totalCount = mapSheetMngHstEntity.count().as("syncTotCnt");
|
NumberExpression<Long> totalCount = mapSheetMngHstEntity.count().as("syncTotCnt");
|
||||||
|
|
||||||
@@ -151,9 +151,9 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
.leftJoin(mapSheetMngHstEntity)
|
.leftJoin(mapSheetMngHstEntity)
|
||||||
.on(mapSheetMngEntity.mngYyyy.eq(mapSheetMngHstEntity.mngYyyy))
|
.on(mapSheetMngEntity.mngYyyy.eq(mapSheetMngHstEntity.mngYyyy))
|
||||||
.where(whereBuilder)
|
.where(whereBuilder)
|
||||||
.offset(pageable.getOffset())
|
// .offset(pageable.getOffset())
|
||||||
.limit(pageable.getPageSize())
|
// .limit(pageable.getPageSize())
|
||||||
.orderBy(mapSheetMngEntity.createdDttm.desc())
|
.orderBy(mapSheetMngEntity.mngYyyy.desc())
|
||||||
.groupBy(mapSheetMngEntity.mngYyyy)
|
.groupBy(mapSheetMngEntity.mngYyyy)
|
||||||
.fetch();
|
.fetch();
|
||||||
|
|
||||||
@@ -164,7 +164,107 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
.where(whereBuilder)
|
.where(whereBuilder)
|
||||||
.fetchOne();
|
.fetchOne();
|
||||||
|
|
||||||
return new PageImpl<>(foundContent, pageable, countQuery);
|
return foundContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) {
|
||||||
|
|
||||||
|
BooleanBuilder whereBuilder = new BooleanBuilder();
|
||||||
|
whereBuilder.and(mapSheetMngEntity.mngYyyy.eq(mngYyyy));
|
||||||
|
|
||||||
|
MapSheetMngDto.MngDto foundContent =
|
||||||
|
queryFactory
|
||||||
|
.select(
|
||||||
|
Projections.constructor(
|
||||||
|
MapSheetMngDto.MngDto.class,
|
||||||
|
Expressions.numberTemplate(
|
||||||
|
Integer.class,
|
||||||
|
"row_number() over(order by {0} desc)",
|
||||||
|
mapSheetMngEntity.createdDttm),
|
||||||
|
mapSheetMngEntity.mngYyyy,
|
||||||
|
mapSheetMngEntity.mngState,
|
||||||
|
mapSheetMngEntity.syncState,
|
||||||
|
mapSheetMngEntity.syncCheckState,
|
||||||
|
mapSheetMngHstEntity.count(),
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(mapSheetMngHstEntity.dataState.eq("DONE"))
|
||||||
|
.then(1L)
|
||||||
|
.otherwise(0L)
|
||||||
|
.sum()
|
||||||
|
.as("syncStateDoneCnt"),
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(mapSheetMngHstEntity.syncState.ne("NOTYET"))
|
||||||
|
.then(1L)
|
||||||
|
.otherwise(0L)
|
||||||
|
.sum(),
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(
|
||||||
|
mapSheetMngHstEntity
|
||||||
|
.syncState
|
||||||
|
.eq("NOFILE")
|
||||||
|
.or(mapSheetMngHstEntity.syncState.eq("NOTPAIR")))
|
||||||
|
.then(1L)
|
||||||
|
.otherwise(0L)
|
||||||
|
.sum(),
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(
|
||||||
|
mapSheetMngHstEntity
|
||||||
|
.syncCheckState
|
||||||
|
.eq("DONE")
|
||||||
|
.and(
|
||||||
|
mapSheetMngHstEntity
|
||||||
|
.syncState
|
||||||
|
.eq("NOFILE")
|
||||||
|
.or(mapSheetMngHstEntity.syncState.eq("NOTPAIR"))))
|
||||||
|
.then(1L)
|
||||||
|
.otherwise(0L)
|
||||||
|
.sum(),
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(mapSheetMngHstEntity.syncState.eq("DUPLICATE"))
|
||||||
|
.then(1L)
|
||||||
|
.otherwise(0L)
|
||||||
|
.sum(),
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(
|
||||||
|
mapSheetMngHstEntity
|
||||||
|
.syncCheckState
|
||||||
|
.eq("DONE")
|
||||||
|
.and(mapSheetMngHstEntity.syncState.eq("DUPLICATE")))
|
||||||
|
.then(1L)
|
||||||
|
.otherwise(0L)
|
||||||
|
.sum(),
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(
|
||||||
|
mapSheetMngHstEntity
|
||||||
|
.syncState
|
||||||
|
.eq("TYPEERROR")
|
||||||
|
.or(mapSheetMngHstEntity.syncState.eq("SIZEERROR")))
|
||||||
|
.then(1L)
|
||||||
|
.otherwise(0L)
|
||||||
|
.sum(),
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(
|
||||||
|
mapSheetMngHstEntity
|
||||||
|
.syncCheckState
|
||||||
|
.eq("DONE")
|
||||||
|
.and(
|
||||||
|
mapSheetMngHstEntity
|
||||||
|
.syncState
|
||||||
|
.eq("TYPEERROR")
|
||||||
|
.or(mapSheetMngHstEntity.syncState.eq("SIZEERROR"))))
|
||||||
|
.then(1L)
|
||||||
|
.otherwise(0L)
|
||||||
|
.sum(),
|
||||||
|
mapSheetMngEntity.createdDttm,
|
||||||
|
mapSheetMngHstEntity.syncEndDttm.max()))
|
||||||
|
.from(mapSheetMngEntity)
|
||||||
|
.leftJoin(mapSheetMngHstEntity)
|
||||||
|
.on(mapSheetMngEntity.mngYyyy.eq(mapSheetMngHstEntity.mngYyyy))
|
||||||
|
.where(whereBuilder)
|
||||||
|
.groupBy(mapSheetMngEntity.mngYyyy)
|
||||||
|
.fetchOne();
|
||||||
|
|
||||||
|
return foundContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -172,28 +272,50 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
MapSheetMngDto.@Valid ErrorSearchReq searchReq) {
|
MapSheetMngDto.@Valid ErrorSearchReq searchReq) {
|
||||||
|
|
||||||
Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize());
|
Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize());
|
||||||
|
|
||||||
BooleanBuilder whereBuilder = new BooleanBuilder();
|
BooleanBuilder whereBuilder = new BooleanBuilder();
|
||||||
whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(searchReq.getMngYyyy()));
|
|
||||||
|
|
||||||
// syncStateFilter 조건 추가
|
whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(searchReq.getMngYyyy()));
|
||||||
if (searchReq.getSyncStateFilter() != null && !searchReq.getSyncStateFilter().isEmpty()) {
|
whereBuilder.and(
|
||||||
whereBuilder.and(mapSheetMngHstEntity.syncState.eq(searchReq.getSyncStateFilter()));
|
mapSheetMngHstEntity.syncState.ne("DONE").and(mapSheetMngHstEntity.syncState.ne("NOTYET")));
|
||||||
} else {
|
|
||||||
// 기본: 오류 상태만 조회 (NOFILE, NOTPAIR, DUPLICATE, SIZEERROR, TYPEERROR)
|
if (searchReq.getSyncState() != null && !searchReq.getSyncState().isEmpty()) {
|
||||||
whereBuilder.and(
|
if (searchReq.getSyncState().equals("NOTPAIR")) {
|
||||||
mapSheetMngHstEntity
|
whereBuilder.and(
|
||||||
.syncState
|
mapSheetMngHstEntity
|
||||||
.eq("NOFILE")
|
.syncState
|
||||||
.or(mapSheetMngHstEntity.syncState.eq("NOTPAIR"))
|
.eq("NOTPAIR")
|
||||||
.or(mapSheetMngHstEntity.syncState.eq("DUPLICATE"))
|
.or(mapSheetMngHstEntity.syncState.eq("NOFILE")));
|
||||||
.or(mapSheetMngHstEntity.syncState.eq("SIZEERROR"))
|
} else if (searchReq.getSyncState().equals("FAULT")) {
|
||||||
.or(mapSheetMngHstEntity.syncState.eq("TYPEERROR")));
|
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()) {
|
if (searchReq.getSearchValue() != null && !searchReq.getSearchValue().isEmpty()) {
|
||||||
whereBuilder.and(mapSheetErrorSearchValue(searchReq));
|
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() + "%")));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MapSheetMngDto.ErrorDataDto> foundContent =
|
List<MapSheetMngDto.ErrorDataDto> foundContent =
|
||||||
@@ -202,30 +324,38 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
Projections.constructor(
|
Projections.constructor(
|
||||||
MapSheetMngDto.ErrorDataDto.class,
|
MapSheetMngDto.ErrorDataDto.class,
|
||||||
mapSheetMngHstEntity.hstUid,
|
mapSheetMngHstEntity.hstUid,
|
||||||
rowNum(),
|
|
||||||
Expressions.stringTemplate(
|
Expressions.stringTemplate(
|
||||||
"concat({0}, {1})",
|
"concat({0},substring({1}, 0, 6))",
|
||||||
mapSheetMngHstEntity.mapSheetName, mapInkx50kEntity.mapidcdNo),
|
mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum)
|
||||||
|
.as("map50kName"),
|
||||||
Expressions.stringTemplate(
|
Expressions.stringTemplate(
|
||||||
"concat({0}, substring({1}, {2}, {3}))",
|
"concat({0},substring({1}, 6, 8))",
|
||||||
mapSheetMngHstEntity.mapSheetName, mapSheetMngHstEntity.mapSheetNum, 6, 8),
|
mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum)
|
||||||
mapSheetMngHstEntity.mapSheetCodeSrc,
|
.as("map5kName"),
|
||||||
Expressions.stringTemplate(
|
Expressions.stringTemplate(
|
||||||
"to_char({0}, 'YYYY-MM-DD')", mapSheetMngHstEntity.createdDate),
|
"concat({0},substring({1}, 6, 8))",
|
||||||
mapSheetMngHstEntity.dataState,
|
mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum)
|
||||||
|
.as("mapSrcName"),
|
||||||
|
mapInkx5kEntity.fid,
|
||||||
|
mapSheetMngHstEntity.createdDate,
|
||||||
mapSheetMngHstEntity.syncState,
|
mapSheetMngHstEntity.syncState,
|
||||||
mapSheetMngHstEntity.syncCheckState,
|
mapSheetMngHstEntity.syncCheckState,
|
||||||
mapSheetMngHstEntity.syncCheckStrtDttm,
|
Expressions.stringTemplate(
|
||||||
mapSheetMngHstEntity.syncCheckEndDttm))
|
"MAX(CASE WHEN {0} = 'tfw' THEN {1} END)",
|
||||||
|
mapSheetMngFileEntity.fileExt, mapSheetMngFileEntity.fileName),
|
||||||
|
Expressions.stringTemplate(
|
||||||
|
"MAX(CASE WHEN {0} = 'tif' THEN {1} END)",
|
||||||
|
mapSheetMngFileEntity.fileExt, mapSheetMngFileEntity.fileName)))
|
||||||
.from(mapSheetMngHstEntity)
|
.from(mapSheetMngHstEntity)
|
||||||
.innerJoin(mapInkx5kEntity)
|
.innerJoin(mapInkx5kEntity)
|
||||||
.on(mapSheetMngHstEntity.mapSheetCode.eq(mapInkx5kEntity.fid))
|
.on(mapSheetMngHstEntity.mapSheetNum.eq(mapInkx5kEntity.mapidcdNo))
|
||||||
.leftJoin(mapInkx50kEntity)
|
.leftJoin(mapSheetMngFileEntity)
|
||||||
.on(mapInkx5kEntity.fidK50.eq(mapInkx50kEntity.fid.longValue()))
|
.on(mapSheetMngHstEntity.hstUid.eq(mapSheetMngFileEntity.hstUid))
|
||||||
.where(whereBuilder)
|
.where(whereBuilder)
|
||||||
|
.groupBy(mapSheetMngHstEntity.hstUid, mapInkx5kEntity.fid, mapInkx5kEntity.mapidNm)
|
||||||
|
.orderBy(mapSheetMngHstEntity.createdDate.desc())
|
||||||
.offset(pageable.getOffset())
|
.offset(pageable.getOffset())
|
||||||
.limit(pageable.getPageSize())
|
.limit(pageable.getPageSize())
|
||||||
.orderBy(mapSheetMngHstEntity.createdDate.desc())
|
|
||||||
.fetch();
|
.fetch();
|
||||||
|
|
||||||
Long countQuery =
|
Long countQuery =
|
||||||
|
|||||||
@@ -38,13 +38,14 @@ spring:
|
|||||||
servlet:
|
servlet:
|
||||||
multipart:
|
multipart:
|
||||||
enabled: true
|
enabled: true
|
||||||
max-file-size: 1024MB
|
max-file-size: 4GB
|
||||||
max-request-size: 2048MB
|
max-request-size: 4GB
|
||||||
file-size-threshold: 10MB
|
file-size-threshold: 10MB
|
||||||
|
|
||||||
server:
|
server:
|
||||||
tomcat:
|
tomcat:
|
||||||
max-swallow-size: 2097152000 # 약 2GB
|
max-swallow-size: 4GB
|
||||||
|
max-http-form-post-size: 4GB
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
|
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
|
||||||
@@ -73,4 +74,3 @@ mapsheet:
|
|||||||
upload:
|
upload:
|
||||||
skipGdalValidation: true
|
skipGdalValidation: true
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,18 @@ spring:
|
|||||||
port: 6379
|
port: 6379
|
||||||
password: 1234
|
password: 1234
|
||||||
|
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
enabled: true
|
||||||
|
max-file-size: 4GB
|
||||||
|
max-request-size: 4GB
|
||||||
|
file-size-threshold: 10MB
|
||||||
|
|
||||||
|
server:
|
||||||
|
tomcat:
|
||||||
|
max-swallow-size: 4GB
|
||||||
|
max-http-form-post-size: 4GB
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
|
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
|
||||||
access-token-validity-in-ms: 86400000 # 1일
|
access-token-validity-in-ms: 86400000 # 1일
|
||||||
@@ -41,7 +53,3 @@ token:
|
|||||||
springdoc:
|
springdoc:
|
||||||
swagger-ui:
|
swagger-ui:
|
||||||
persist-authorization: true # 스웨거 새로고침해도 토큰 유지, 로컬스토리지에 저장
|
persist-authorization: true # 스웨거 새로고침해도 토큰 유지, 로컬스토리지에 저장
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ spring:
|
|||||||
application:
|
application:
|
||||||
name: kamco-change-detection-api
|
name: kamco-change-detection-api
|
||||||
profiles:
|
profiles:
|
||||||
active: dev # 사용할 프로파일 지정 (ex. dev, prod, test)
|
active: local # 사용할 프로파일 지정 (ex. dev, prod, test)
|
||||||
|
|
||||||
datasource:
|
datasource:
|
||||||
driver-class-name: org.postgresql.Driver
|
driver-class-name: org.postgresql.Driver
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,110 @@
|
|||||||
|
package com.kamco.cd.kamcoback.common.utils;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||||
|
import org.junit.jupiter.api.condition.OS;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
class FIleCheckerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOsDetection() {
|
||||||
|
// 운영체제 감지 테스트
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
System.out.println("현재 운영체제: " + osName);
|
||||||
|
|
||||||
|
boolean isWindows = osName.contains("win");
|
||||||
|
boolean isMac = osName.contains("mac");
|
||||||
|
boolean isUnix = osName.contains("nix") || osName.contains("nux") || osName.contains("aix");
|
||||||
|
|
||||||
|
// 최소한 하나의 OS는 감지되어야 함
|
||||||
|
assertTrue(isWindows || isMac || isUnix, "지원되는 운영체제를 감지해야 합니다");
|
||||||
|
|
||||||
|
System.out.println("Windows: " + isWindows);
|
||||||
|
System.out.println("Mac: " + isMac);
|
||||||
|
System.out.println("Unix/Linux: " + isUnix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCheckTfw_ValidFile(@TempDir File tempDir) throws IOException {
|
||||||
|
// 임시 유효한 TFW 파일 생성
|
||||||
|
File tfwFile = new File(tempDir, "test.tfw");
|
||||||
|
try (FileWriter writer = new FileWriter(tfwFile)) {
|
||||||
|
writer.write("0.25\n"); // pixel size x
|
||||||
|
writer.write("0.0\n"); // rotation x
|
||||||
|
writer.write("0.0\n"); // rotation y
|
||||||
|
writer.write("-0.25\n"); // pixel size y
|
||||||
|
writer.write("127.5\n"); // upper left x
|
||||||
|
writer.write("37.5\n"); // upper left y
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = FIleChecker.checkTfw(tfwFile.getAbsolutePath());
|
||||||
|
assertTrue(result, "유효한 TFW 파일은 true를 반환해야 합니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCheckTfw_InvalidFile(@TempDir File tempDir) throws IOException {
|
||||||
|
// 잘못된 TFW 파일 생성 (5줄만)
|
||||||
|
File tfwFile = new File(tempDir, "invalid.tfw");
|
||||||
|
try (FileWriter writer = new FileWriter(tfwFile)) {
|
||||||
|
writer.write("0.25\n");
|
||||||
|
writer.write("0.0\n");
|
||||||
|
writer.write("0.0\n");
|
||||||
|
writer.write("-0.25\n");
|
||||||
|
writer.write("127.5\n");
|
||||||
|
// 6번째 줄 누락
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = FIleChecker.checkTfw(tfwFile.getAbsolutePath());
|
||||||
|
assertFalse(result, "잘못된 TFW 파일은 false를 반환해야 합니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCheckTfw_NonExistentFile() {
|
||||||
|
boolean result = FIleChecker.checkTfw("/non/existent/path/file.tfw");
|
||||||
|
assertFalse(result, "존재하지 않는 파일은 false를 반환해야 합니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCmmndGdalInfo_MethodExists() throws NoSuchMethodException {
|
||||||
|
// cmmndGdalInfo 메서드가 존재하는지 확인
|
||||||
|
assertNotNull(FIleChecker.class.getMethod("cmmndGdalInfo", String.class));
|
||||||
|
System.out.println("cmmndGdalInfo 메서드 존재 확인");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCmmndGdalInfo_NonExistentFile() {
|
||||||
|
// 존재하지 않는 파일은 false를 반환해야 함
|
||||||
|
boolean result = FIleChecker.cmmndGdalInfo("/non/existent/path/file.tif");
|
||||||
|
assertFalse(result, "존재하지 않는 파일은 false를 반환해야 합니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnabledOnOs(OS.WINDOWS)
|
||||||
|
void testWindowsCommand() {
|
||||||
|
System.out.println("Windows OS 감지됨 - GDAL 명령어 형식 확인");
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
assertTrue(osName.contains("win"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnabledOnOs(OS.MAC)
|
||||||
|
void testMacCommand() {
|
||||||
|
System.out.println("Mac OS 감지됨 - GDAL 명령어 형식 확인");
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
assertTrue(osName.contains("mac"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnabledOnOs(OS.LINUX)
|
||||||
|
void testLinuxCommand() {
|
||||||
|
System.out.println("Linux OS 감지됨 - GDAL 명령어 형식 확인");
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
assertTrue(osName.contains("nux") || osName.contains("nix"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user