Merge remote-tracking branch 'origin/feat/dev_251201' into feat/dev_251201

# Conflicts:
#	src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngApiController.java
#	src/main/java/com/kamco/cd/kamcoback/mapsheet/dto/MapSheetMngDto.java
#	src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java
This commit is contained in:
Moon
2025-12-16 18:40:44 +09:00
17 changed files with 389 additions and 58 deletions

View File

@@ -27,7 +27,7 @@ public class CustomAuthenticationProvider implements AuthenticationProvider {
// 유저 조회 // 유저 조회
MemberEntity member = MemberEntity member =
membersRepository membersRepository
.findByUserId(username) .findByEmployeeNo(username)
.orElseThrow(() -> new CustomApiException(AuthErrorCode.LOGIN_ID_NOT_FOUND)); .orElseThrow(() -> new CustomApiException(AuthErrorCode.LOGIN_ID_NOT_FOUND));
// 미사용 상태 // 미사용 상태

View File

@@ -7,9 +7,9 @@ import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum StatusType implements EnumType { public enum StatusType implements EnumType {
ACTIVE("활성"), ACTIVE("사용"),
INACTIVE("미사용"), INACTIVE("미사용"),
PENDING("보류"); PENDING("계정등록");
private final String desc; private final String desc;

View File

@@ -14,7 +14,7 @@ public class CommonStringUtils {
*/ */
public static boolean isValidPassword(String password) { public static boolean isValidPassword(String password) {
String passwordPattern = String passwordPattern =
"^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-\\[\\]{};':\"\\\\|,.<>/?]).{8,20}$"; "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-\\[\\]{};':\"\\\\|,.<>/?=]).{8,20}$";
return Pattern.matches(passwordPattern, password); return Pattern.matches(passwordPattern, password);
} }

View File

@@ -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;
}
}
} }

View File

@@ -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());
}
} }

View File

@@ -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;

View File

@@ -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);
} }
} }

View File

@@ -46,7 +46,8 @@ public class MembersDto {
ZonedDateTime createdDttm, ZonedDateTime createdDttm,
ZonedDateTime firstLoginDttm, ZonedDateTime firstLoginDttm,
ZonedDateTime lastLoginDttm, ZonedDateTime lastLoginDttm,
ZonedDateTime statusChgDttm) { ZonedDateTime statusChgDttm,
Boolean pwdResetYn) {
this.id = id; this.id = id;
this.uuid = uuid; this.uuid = uuid;
this.userRole = userRole; this.userRole = userRole;
@@ -54,7 +55,7 @@ public class MembersDto {
this.name = name; this.name = name;
this.employeeNo = employeeNo; this.employeeNo = employeeNo;
this.status = status; this.status = status;
this.statusName = getStatusName(status); this.statusName = getStatusName(status, pwdResetYn);
this.createdDttm = createdDttm; this.createdDttm = createdDttm;
this.firstLoginDttm = firstLoginDttm; this.firstLoginDttm = firstLoginDttm;
this.lastLoginDttm = lastLoginDttm; this.lastLoginDttm = lastLoginDttm;
@@ -66,8 +67,12 @@ public class MembersDto {
return type.getText(); return type.getText();
} }
private String getStatusName(String status) { private String getStatusName(String status, Boolean pwdResetYn) {
StatusType type = Enums.fromId(StatusType.class, status); StatusType type = Enums.fromId(StatusType.class, status);
pwdResetYn = pwdResetYn != null && pwdResetYn;
if (type.equals(StatusType.PENDING) && pwdResetYn) {
type = StatusType.ACTIVE;
}
return type.getText(); return type.getText();
} }
} }

View File

@@ -11,7 +11,7 @@ import lombok.ToString;
@ToString(exclude = "password") @ToString(exclude = "password")
public class SignInRequest { public class SignInRequest {
@Schema(description = "사용자 ID", example = "admin2") @Schema(description = "사용자 ID", example = "1234567")
private String username; private String username;
@Schema(description = "비밀번호", example = "Admin2!@#") @Schema(description = "비밀번호", example = "Admin2!@#")

View File

@@ -142,7 +142,7 @@ public class MembersCoreService {
public String getUserStatus(SignInRequest request) { public String getUserStatus(SignInRequest request) {
MemberEntity memberEntity = MemberEntity memberEntity =
membersRepository membersRepository
.findByUserId(request.getUsername()) .findByEmployeeNo(request.getUsername())
.orElseThrow(MemberNotFoundException::new); .orElseThrow(MemberNotFoundException::new);
return memberEntity.getStatus(); return memberEntity.getStatus();
} }

View File

@@ -125,7 +125,8 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom {
memberEntity.createdDttm, memberEntity.createdDttm,
memberEntity.firstLoginDttm, memberEntity.firstLoginDttm,
memberEntity.lastLoginDttm, memberEntity.lastLoginDttm,
memberEntity.statusChgDttm)) memberEntity.statusChgDttm,
memberEntity.pwdResetYn))
.from(memberEntity) .from(memberEntity)
.where(builder) .where(builder)
.offset(pageable.getOffset()) .offset(pageable.getOffset())

View File

@@ -108,14 +108,19 @@ public class MapSheetMngFileJobRepositoryImpl extends QuerydslRepositorySupport
public void mngHstDataSyncStateUpdate(MapSheetMngDto.MngHstDto updateReq) { public void mngHstDataSyncStateUpdate(MapSheetMngDto.MngHstDto updateReq) {
ZonedDateTime now = ZonedDateTime.now();
if (updateReq.getDataState().equals("DONE")) { if (updateReq.getDataState().equals("DONE")) {
long updateCount = long updateCount =
queryFactory queryFactory
.update(mapSheetMngHstEntity) .update(mapSheetMngHstEntity)
.set(mapSheetMngHstEntity.dataState, updateReq.getDataState()) .set(mapSheetMngHstEntity.dataState, updateReq.getDataState())
.set(mapSheetMngHstEntity.dataStateDttm, ZonedDateTime.now()) .set(mapSheetMngHstEntity.dataStateDttm, now)
.set(mapSheetMngHstEntity.syncState, updateReq.getSyncState()) .set(mapSheetMngHstEntity.syncState, updateReq.getSyncState())
.set(mapSheetMngHstEntity.syncEndDttm, ZonedDateTime.now()) .set(mapSheetMngHstEntity.syncEndDttm, now)
.set(mapSheetMngHstEntity.syncCheckState, "DONE")
.set(mapSheetMngHstEntity.syncCheckStrtDttm, now)
.set(mapSheetMngHstEntity.syncCheckEndDttm, now)
.where(mapSheetMngHstEntity.hstUid.eq(updateReq.getHstUid())) .where(mapSheetMngHstEntity.hstUid.eq(updateReq.getHstUid()))
.execute(); .execute();
} else { } else {
@@ -123,10 +128,13 @@ public class MapSheetMngFileJobRepositoryImpl extends QuerydslRepositorySupport
queryFactory queryFactory
.update(mapSheetMngHstEntity) .update(mapSheetMngHstEntity)
.set(mapSheetMngHstEntity.dataState, updateReq.getDataState()) .set(mapSheetMngHstEntity.dataState, updateReq.getDataState())
.set(mapSheetMngHstEntity.dataStateDttm, ZonedDateTime.now()) .set(mapSheetMngHstEntity.dataStateDttm, now)
.set(mapSheetMngHstEntity.syncState, updateReq.getSyncState()) .set(mapSheetMngHstEntity.syncState, updateReq.getSyncState())
.set(mapSheetMngHstEntity.syncStrtDttm, ZonedDateTime.now()) .set(mapSheetMngHstEntity.syncStrtDttm, now)
.set(mapSheetMngHstEntity.syncEndDttm, ZonedDateTime.now()) .set(mapSheetMngHstEntity.syncEndDttm, now)
.set(mapSheetMngHstEntity.syncCheckState, "PROCESSING")
.set(mapSheetMngHstEntity.syncCheckStrtDttm, now)
.set(mapSheetMngHstEntity.syncCheckEndDttm, now)
.where(mapSheetMngHstEntity.hstUid.eq(updateReq.getHstUid())) .where(mapSheetMngHstEntity.hstUid.eq(updateReq.getHstUid()))
.execute(); .execute();
} }

View File

@@ -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

View File

@@ -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 # 스웨거 새로고침해도 토큰 유지, 로컬스토리지에 저장

View File

@@ -1,7 +1,7 @@
spring: spring:
config: config:
activate: activate:
on-profile: dev on-profile: prod
jpa: jpa:
show-sql: false show-sql: false

View File

@@ -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

View File

@@ -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"));
}
}