From 41f7afca968d2469329d4171af0e627fcc65c0c7 Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 12 Dec 2025 17:18:14 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/CustomAuthenticationProvider.java | 9 +-- .../cd/kamcoback/common/enums/StatusType.java | 4 +- .../common/utils/CommonStringUtils.java | 18 ++++++ .../config/GlobalExceptionHandler.java | 31 ++++++++--- .../cd/kamcoback/config/SecurityConfig.java | 2 + .../MapSheetMngFileCheckerApiController.java | 3 +- .../mapsheet/service/MapSheetMngService.java | 1 - .../kamcoback/members/AdminApiController.java | 21 ------- .../cd/kamcoback/members/AuthController.java | 7 ++- .../members/MembersApiController.java | 5 -- .../cd/kamcoback/members/dto/MembersDto.java | 29 +++------- .../members/exception/MemberException.java | 1 + .../members/service/AdminService.java | 26 +++++---- .../members/service/MembersService.java | 16 +----- .../postgres/core/MembersCoreService.java | 51 +++++++++-------- .../postgres/entity/MemberEntity.java | 2 +- .../mapsheet/MapSheetMngRepositoryImpl.java | 55 ++++++++++--------- .../members/MembersRepositoryCustom.java | 4 ++ .../members/MembersRepositoryImpl.java | 31 +++++++++++ src/main/resources/application-local.yml | 8 ++- 20 files changed, 179 insertions(+), 145 deletions(-) create mode 100644 src/main/java/com/kamco/cd/kamcoback/common/utils/CommonStringUtils.java diff --git a/src/main/java/com/kamco/cd/kamcoback/auth/CustomAuthenticationProvider.java b/src/main/java/com/kamco/cd/kamcoback/auth/CustomAuthenticationProvider.java index 757c0a52..98601d76 100644 --- a/src/main/java/com/kamco/cd/kamcoback/auth/CustomAuthenticationProvider.java +++ b/src/main/java/com/kamco/cd/kamcoback/auth/CustomAuthenticationProvider.java @@ -30,8 +30,8 @@ public class CustomAuthenticationProvider implements AuthenticationProvider { .findByUserId(username) .orElseThrow(() -> new CustomApiException(AuthErrorCode.LOGIN_ID_NOT_FOUND)); - // 삭제 상태 - if (member.getStatus().equals(StatusType.DELETED.getId())) { + // 미사용 상태 + if (member.getStatus().equals(StatusType.INACTIVE.getId())) { throw new CustomApiException(AuthErrorCode.LOGIN_ID_NOT_FOUND); } @@ -39,15 +39,12 @@ public class CustomAuthenticationProvider implements AuthenticationProvider { if (!BCrypt.checkpw(rawPassword, member.getPassword())) { // 실패 카운트 저장 int cnt = member.getLoginFailCount() + 1; - if (cnt >= 5) { - member.setStatus(StatusType.INACTIVE.getId()); - } member.setLoginFailCount(cnt); membersRepository.save(member); throw new CustomApiException(AuthErrorCode.LOGIN_PASSWORD_MISMATCH); } - // 패스워드 실패 횟수 체크 + // 로그인 실패 체크 if (member.getLoginFailCount() >= 5) { throw new CustomApiException(AuthErrorCode.LOGIN_PASSWORD_EXCEEDED); } diff --git a/src/main/java/com/kamco/cd/kamcoback/common/enums/StatusType.java b/src/main/java/com/kamco/cd/kamcoback/common/enums/StatusType.java index 0da73338..029e85ae 100644 --- a/src/main/java/com/kamco/cd/kamcoback/common/enums/StatusType.java +++ b/src/main/java/com/kamco/cd/kamcoback/common/enums/StatusType.java @@ -8,8 +8,8 @@ import lombok.Getter; @AllArgsConstructor public enum StatusType implements EnumType { ACTIVE("활성"), - INACTIVE("비활성"), - DELETED("삭제"); + INACTIVE("미사용"), + PENDING("보류"); private final String desc; diff --git a/src/main/java/com/kamco/cd/kamcoback/common/utils/CommonStringUtils.java b/src/main/java/com/kamco/cd/kamcoback/common/utils/CommonStringUtils.java new file mode 100644 index 00000000..a1db89a3 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/utils/CommonStringUtils.java @@ -0,0 +1,18 @@ +package com.kamco.cd.kamcoback.common.utils; + +import java.util.regex.Pattern; + +public class CommonStringUtils { + + /** + * 영문, 숫자, 특수문자를 모두 포함하여 8~20자 이내의 비밀번호 + * + * @param password + * @return + */ + public static boolean isValidPassword(String password) { + String passwordPattern = + "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-\\[\\]{};':\"\\\\|,.<>/?]).{8,20}$"; + return Pattern.matches(passwordPattern, password); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/config/GlobalExceptionHandler.java b/src/main/java/com/kamco/cd/kamcoback/config/GlobalExceptionHandler.java index bc7cdb74..a28000f2 100644 --- a/src/main/java/com/kamco/cd/kamcoback/config/GlobalExceptionHandler.java +++ b/src/main/java/com/kamco/cd/kamcoback/config/GlobalExceptionHandler.java @@ -435,23 +435,38 @@ public class GlobalExceptionHandler { } @ExceptionHandler(CustomApiException.class) - public ResponseEntity> handleCustomApiException( + public ApiResponseDto handleCustomApiException( CustomApiException e, HttpServletRequest request) { log.warn("[CustomApiException] resource : {}", e.getMessage()); String codeName = e.getCodeName(); HttpStatus status = e.getStatus(); - String message = e.getMessage() == null ? ApiResponseCode.getMessage(codeName) : e.getMessage(); - - ApiResponseCode apiCode = ApiResponseCode.getCode(codeName); + // String message = e.getMessage() == null ? ApiResponseCode.getMessage(codeName) : + // e.getMessage(); + // + // ApiResponseCode apiCode = ApiResponseCode.getCode(codeName); + // + // ErrorLogEntity errorLog = + // saveErrorLogData( + // request, apiCode, status, ErrorLogDto.LogErrorLevel.WARNING, e.getStackTrace()); + // + // ApiResponseDto body = + // ApiResponseDto.createException(apiCode, message, status, errorLog.getId()); ErrorLogEntity errorLog = saveErrorLogData( - request, apiCode, status, ErrorLogDto.LogErrorLevel.WARNING, e.getStackTrace()); + request, + ApiResponseCode.getCode(codeName), + HttpStatus.valueOf(status.value()), + ErrorLogDto.LogErrorLevel.WARNING, + e.getStackTrace()); - ApiResponseDto body = - ApiResponseDto.createException(apiCode, message, status, errorLog.getId()); + return ApiResponseDto.createException( + ApiResponseCode.getCode(codeName), + ApiResponseCode.getMessage(codeName), + HttpStatus.valueOf(status.value()), + errorLog.getId()); - return new ResponseEntity<>(body, status); + // return new ResponseEntity<>(body, status); } } diff --git a/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java b/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java index eca8fcf7..9bd0fb25 100644 --- a/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java +++ b/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java @@ -54,6 +54,8 @@ public class SecurityConfig { // ADMIN, REVIEWER 접근 .requestMatchers("/api/test/review") .hasAnyRole("ADMIN", "REVIEWER") + .requestMatchers("/error") + .permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**") .permitAll() // preflight 허용 .requestMatchers( 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 e702e106..554cd6a4 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java @@ -74,8 +74,7 @@ public class MapSheetMngFileCheckerApiController { @Operation(summary = "파일 업로드", description = "파일 업로드 및 TIF 검증") @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ApiResponseDto uploadFile( - @RequestPart("file") MultipartFile file, - @RequestParam("targetPath") String targetPath) { + @RequestPart("file") MultipartFile file, @RequestParam("targetPath") String targetPath) { return ApiResponseDto.createOK(mapSheetMngFileCheckerService.uploadFile(file, targetPath)); } diff --git a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java index 40145c2e..e6daa08f 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java @@ -29,7 +29,6 @@ import org.apache.commons.io.FilenameUtils; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; @Service @RequiredArgsConstructor diff --git a/src/main/java/com/kamco/cd/kamcoback/members/AdminApiController.java b/src/main/java/com/kamco/cd/kamcoback/members/AdminApiController.java index 33afc12c..44f1acb5 100644 --- a/src/main/java/com/kamco/cd/kamcoback/members/AdminApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/members/AdminApiController.java @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -87,24 +86,4 @@ public class AdminApiController { adminService.updateMembers(uuid, updateReq); return ApiResponseDto.createOK(UUID.randomUUID()); } - - @Operation(summary = "관리자 계정 미사용 처리", description = "관리자 계정 미사용 처리") - @ApiResponses( - value = { - @ApiResponse( - responseCode = "201", - description = "관리자 계정 미사용 처리", - content = - @Content( - mediaType = "application/json", - schema = @Schema(implementation = UUID.class))), - @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), - @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), - @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) - }) - @DeleteMapping("/delete/{uuid}") - public ApiResponseDto deleteAccount(@PathVariable UUID uuid) { - adminService.deleteAccount(uuid); - return ApiResponseDto.createOK(uuid); - } } diff --git a/src/main/java/com/kamco/cd/kamcoback/members/AuthController.java b/src/main/java/com/kamco/cd/kamcoback/members/AuthController.java index 5487bc2d..a708238e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/members/AuthController.java +++ b/src/main/java/com/kamco/cd/kamcoback/members/AuthController.java @@ -113,7 +113,7 @@ public class AuthController { MembersDto.Member member = new MembersDto.Member(); // 비활성 상태면 임시패스워드를 비교함 - if (StatusType.INACTIVE.getId().equals(status)) { + if (StatusType.PENDING.getId().equals(status)) { if (!authService.isTempPasswordValid(request)) { throw new CustomApiException(AuthErrorCode.LOGIN_PASSWORD_MISMATCH); } @@ -124,8 +124,9 @@ public class AuthController { request.getUsername(), request.getPassword())); } - // INACTIVE 비활성 상태(새로운 패스워드 입력 해야함), DELETED 탈퇴 - if (!StatusType.ACTIVE.getId().equals(status)) { + // PENDING 비활성 상태(새로운 패스워드 입력 해야함) + if (StatusType.PENDING.getId().equals(status)) { + member.setEmployeeNo(request.getUsername()); return ApiResponseDto.ok(new TokenResponse(status, null, null, member)); } diff --git a/src/main/java/com/kamco/cd/kamcoback/members/MembersApiController.java b/src/main/java/com/kamco/cd/kamcoback/members/MembersApiController.java index 2d6a850b..2dc47e98 100644 --- a/src/main/java/com/kamco/cd/kamcoback/members/MembersApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/members/MembersApiController.java @@ -15,7 +15,6 @@ import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; import org.springframework.data.domain.Page; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -70,10 +69,6 @@ public class MembersApiController { @PatchMapping("/{memberId}/password") public ApiResponseDto resetPassword( @PathVariable String memberId, @RequestBody @Valid MembersDto.InitReq initReq) { - - authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(memberId, initReq.getTempPassword())); - membersService.resetPassword(memberId, initReq); return ApiResponseDto.createOK(memberId); } diff --git a/src/main/java/com/kamco/cd/kamcoback/members/dto/MembersDto.java b/src/main/java/com/kamco/cd/kamcoback/members/dto/MembersDto.java index de69c6e3..ab2a7908 100644 --- a/src/main/java/com/kamco/cd/kamcoback/members/dto/MembersDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/members/dto/MembersDto.java @@ -108,32 +108,26 @@ public class MembersDto { @Schema(description = "관리자 유형", example = "ADMIN") @NotBlank - @EnumValid(enumClass = RoleType.class, message = "userRole은 ADMIN, LABELER, REVIEWER만 가능합니다.") + @EnumValid(enumClass = RoleType.class, message = "userRole은 ADMIN, LABELER, REVIEWER 만 가능합니다.") private String userRole; + @Schema(description = "사번", example = "K20251212001") + @Size(max = 50) + private String employeeNo; + @Schema(description = "이름", example = "홍길동") @NotBlank @Size(min = 2, max = 100) private String name; - @Schema(description = "ID", example = "gildong") - @NotBlank - @Size(min = 2, max = 50) - private String userId; - @Schema(description = "임시 비밀번호", example = "q!w@e#r4") private String tempPassword; - @Schema(description = "사번", example = "123456") - private String employeeNo; - - public AddReq( - String userRole, String name, String userId, String tempPassword, String employeeNo) { + public AddReq(String userRole, String employeeNo, String name, String tempPassword) { this.userRole = userRole; - this.name = name; - this.userId = userId; - this.tempPassword = tempPassword; this.employeeNo = employeeNo; + this.name = name; + this.tempPassword = tempPassword; } } @@ -141,10 +135,6 @@ public class MembersDto { @Setter public static class UpdateReq { - @Schema(description = "사번, 패스워드 변경시 필수 값", example = "11111") - @Size(max = 50) - private String employeeNo; - @Schema(description = "이름", example = "홍길동") @Size(min = 2, max = 100) private String name; @@ -157,8 +147,7 @@ public class MembersDto { @EnumValid(enumClass = StatusType.class, message = "status는 ACTIVE, INACTIVE, DELETED 만 가능합니다.") private String status; - public UpdateReq(String employeeNo, String name, String tempPassword, String status) { - this.employeeNo = employeeNo; + public UpdateReq(String name, String tempPassword, String status) { this.name = name; this.tempPassword = tempPassword; this.status = status; diff --git a/src/main/java/com/kamco/cd/kamcoback/members/exception/MemberException.java b/src/main/java/com/kamco/cd/kamcoback/members/exception/MemberException.java index 33d2e8da..60490c4e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/members/exception/MemberException.java +++ b/src/main/java/com/kamco/cd/kamcoback/members/exception/MemberException.java @@ -11,6 +11,7 @@ public class MemberException { public enum Field { USER_ID, + EMPLOYEE_NO, DEFAULT } diff --git a/src/main/java/com/kamco/cd/kamcoback/members/service/AdminService.java b/src/main/java/com/kamco/cd/kamcoback/members/service/AdminService.java index dd6f5621..72d73b5e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/members/service/AdminService.java +++ b/src/main/java/com/kamco/cd/kamcoback/members/service/AdminService.java @@ -1,9 +1,13 @@ package com.kamco.cd.kamcoback.members.service; +import com.kamco.cd.kamcoback.common.enums.StatusType; +import com.kamco.cd.kamcoback.common.exception.CustomApiException; +import com.kamco.cd.kamcoback.common.utils.CommonStringUtils; import com.kamco.cd.kamcoback.members.dto.MembersDto; import com.kamco.cd.kamcoback.postgres.core.MembersCoreService; import java.util.UUID; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,6 +26,10 @@ public class AdminService { */ @Transactional public Long saveMember(MembersDto.AddReq addReq) { + if (!CommonStringUtils.isValidPassword(addReq.getTempPassword())) { + throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST); + } + return membersCoreService.saveMembers(addReq); } @@ -33,16 +41,12 @@ public class AdminService { */ @Transactional public void updateMembers(UUID uuid, MembersDto.UpdateReq updateReq) { - membersCoreService.updateMembers(uuid, updateReq); - } - - /** - * 관리자 계정 미사용 처리 - * - * @param uuid - */ - @Transactional - public void deleteAccount(UUID uuid) { - membersCoreService.deleteAccount(uuid); + if (StatusType.INACTIVE.getId().equals(updateReq.getStatus())) { + // 미사용 처리 + membersCoreService.deleteMember(uuid); + } else { + // 수정 + membersCoreService.updateMembers(uuid, updateReq); + } } } diff --git a/src/main/java/com/kamco/cd/kamcoback/members/service/MembersService.java b/src/main/java/com/kamco/cd/kamcoback/members/service/MembersService.java index c9611d6d..538ad387 100644 --- a/src/main/java/com/kamco/cd/kamcoback/members/service/MembersService.java +++ b/src/main/java/com/kamco/cd/kamcoback/members/service/MembersService.java @@ -1,10 +1,10 @@ package com.kamco.cd.kamcoback.members.service; import com.kamco.cd.kamcoback.common.exception.CustomApiException; +import com.kamco.cd.kamcoback.common.utils.CommonStringUtils; import com.kamco.cd.kamcoback.members.dto.MembersDto; import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic; import com.kamco.cd.kamcoback.postgres.core.MembersCoreService; -import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; @@ -37,21 +37,9 @@ public class MembersService { @Transactional public void resetPassword(String id, MembersDto.InitReq initReq) { - if (!isValidPassword(initReq.getPassword())) { + if (!CommonStringUtils.isValidPassword(initReq.getPassword())) { throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST); } membersCoreService.resetPassword(id, initReq); } - - /** - * 영문, 숫자, 특수문자를 모두 포함하여 8~20자 이내의 비밀번호 - * - * @param password - * @return - */ - private boolean isValidPassword(String password) { - String passwordPattern = - "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-\\[\\]{};':\"\\\\|,.<>/?]).{8,20}$"; - return Pattern.matches(passwordPattern, password); - } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MembersCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MembersCoreService.java index b03ce3ed..e3904478 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MembersCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MembersCoreService.java @@ -2,6 +2,9 @@ package com.kamco.cd.kamcoback.postgres.core; import com.kamco.cd.kamcoback.auth.BCryptSaltGenerator; import com.kamco.cd.kamcoback.common.enums.StatusType; +import com.kamco.cd.kamcoback.common.enums.error.AuthErrorCode; +import com.kamco.cd.kamcoback.common.exception.CustomApiException; +import com.kamco.cd.kamcoback.common.utils.CommonStringUtils; import com.kamco.cd.kamcoback.common.utils.UserUtil; import com.kamco.cd.kamcoback.members.dto.MembersDto; import com.kamco.cd.kamcoback.members.dto.MembersDto.AddReq; @@ -18,6 +21,7 @@ import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.mindrot.jbcrypt.BCrypt; import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service @@ -34,8 +38,8 @@ public class MembersCoreService { * @return */ public Long saveMembers(AddReq addReq) { - if (membersRepository.existsByUserId(addReq.getUserId())) { - throw new DuplicateMemberException(Field.USER_ID, addReq.getUserId()); + if (membersRepository.existsByEmployeeNo(addReq.getEmployeeNo())) { + throw new DuplicateMemberException(Field.EMPLOYEE_NO, addReq.getEmployeeNo()); } // salt 생성, 사번이 salt @@ -44,7 +48,7 @@ public class MembersCoreService { String hashedPassword = BCrypt.hashpw(addReq.getTempPassword(), salt); MemberEntity memberEntity = new MemberEntity(); - memberEntity.setUserId(addReq.getUserId()); + memberEntity.setUserId(addReq.getEmployeeNo()); memberEntity.setUserRole(addReq.getUserRole()); memberEntity.setTempPassword(addReq.getTempPassword().trim()); // 임시 패스워드는 암호화 하지 않음 memberEntity.setPassword(hashedPassword); @@ -71,41 +75,35 @@ public class MembersCoreService { // 임시 패스워드는 암호화 하지 않음 if (StringUtils.isNotBlank(updateReq.getTempPassword())) { - // 임시 패스워드가 기존과 다르면 패스워드 변경으로 처리함 - // 상태 INACTIVE로 변경하여 사용자가 로그인할때 패스워드 변경하게함 - // 패스워드 리셋이므로 로그인 실패카운트 초기화처리함 + /** + * 임시 패스워드가 기존과 다르면 패스워드 변경으로 처리함 상태 PENDING 으로 변경하여 사용자가 로그인할때 패스워드 변경하게함 패스워드 리셋이므로 로그인 + * 실패카운트 초기화처리함 + */ if (!memberEntity.getTempPassword().equals(updateReq.getTempPassword().trim())) { - memberEntity.setStatus(StatusType.INACTIVE.getId()); + // 패스워드 유효성 검사 + if (!CommonStringUtils.isValidPassword(updateReq.getTempPassword())) { + throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST); + } + + memberEntity.setStatus(StatusType.PENDING.getId()); memberEntity.setLoginFailCount(0); } memberEntity.setTempPassword(updateReq.getTempPassword().trim()); } - - if (StringUtils.isNotBlank(updateReq.getEmployeeNo())) { - memberEntity.setEmployeeNo(updateReq.getEmployeeNo()); - } - - if (StringUtils.isNotBlank(updateReq.getStatus())) { - memberEntity.setStatus(updateReq.getStatus()); - } - memberEntity.setUpdtrUid(userUtil.getId()); membersRepository.save(memberEntity); } /** - * 관리자 계정 삭제 처리 + * 미사용 처리 * * @param uuid */ - public void deleteAccount(UUID uuid) { + public void deleteMember(UUID uuid) { MemberEntity memberEntity = - membersRepository.findByUUID(uuid).orElseThrow(() -> new MemberNotFoundException()); - - memberEntity.setStatus(StatusType.DELETED.getId()); - memberEntity.setUpdatedDttm(ZonedDateTime.now()); - memberEntity.setUpdtrUid(userUtil.getId()); + membersRepository.findByUUID(uuid).orElseThrow(MemberNotFoundException::new); + memberEntity.setStatus(StatusType.INACTIVE.getId()); membersRepository.save(memberEntity); } @@ -116,7 +114,12 @@ public class MembersCoreService { */ public void resetPassword(String id, MembersDto.InitReq initReq) { MemberEntity memberEntity = - membersRepository.findByUserId(id).orElseThrow(() -> new MemberNotFoundException()); + membersRepository.findByEmployeeNo(id).orElseThrow(() -> new MemberNotFoundException()); + + // 임시 패스워드 확인 + if (!memberEntity.getTempPassword().equals(initReq.getTempPassword())) { + throw new CustomApiException(AuthErrorCode.LOGIN_PASSWORD_MISMATCH); + } String salt = BCryptSaltGenerator.generateSaltWithEmployeeNo(memberEntity.getEmployeeNo().trim()); diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MemberEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MemberEntity.java index 19f7e4d3..79e1c1da 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MemberEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MemberEntity.java @@ -64,7 +64,7 @@ public class MemberEntity { @Size(max = 20) @ColumnDefault("'INACTIVE'") @Column(name = "status", length = 20) - private String status = StatusType.INACTIVE.getId(); + private String status = StatusType.PENDING.getId(); @NotNull @ColumnDefault("now()") 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 ddc1c4cb..c50ae12c 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 @@ -170,48 +170,53 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport @Override public void deleteByMngYyyyMngAll(int mngYyyy) { - long deletedFileCount = queryFactory - .delete(mapSheetMngFileEntity) - .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedFileCount = + queryFactory + .delete(mapSheetMngFileEntity) + .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy)) + .execute(); - long deletedHisCount = queryFactory - .delete(mapSheetMngHstEntity) - .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedHisCount = + queryFactory + .delete(mapSheetMngHstEntity) + .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy)) + .execute(); - long deletedMngCount = queryFactory - .delete(mapSheetMngEntity) - .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedMngCount = + queryFactory + .delete(mapSheetMngEntity) + .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) + .execute(); } - @Override public void deleteByMngYyyyMng(int mngYyyy) { - long deletedMngCount = queryFactory - .delete(mapSheetMngEntity) - .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedMngCount = + queryFactory + .delete(mapSheetMngEntity) + .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) + .execute(); } @Override public void deleteByMngYyyyMngHst(int mngYyyy) { - long deletedHisCount = queryFactory - .delete(mapSheetMngHstEntity) - .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedHisCount = + queryFactory + .delete(mapSheetMngHstEntity) + .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy)) + .execute(); } @Override public void deleteByMngYyyyMngFile(int mngYyyy) { - long deletedFileCount = queryFactory - .delete(mapSheetMngFileEntity) - .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedFileCount = + queryFactory + .delete(mapSheetMngFileEntity) + .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy)) + .execute(); } @Override diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/members/MembersRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/members/MembersRepositoryCustom.java index bcd23223..b43bf938 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/members/MembersRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/members/MembersRepositoryCustom.java @@ -11,6 +11,10 @@ public interface MembersRepositoryCustom { boolean existsByUserId(String userId); + boolean existsByEmployeeNo(String employeeNo); + + Optional findByEmployeeNo(String employeeNo); + Optional findByUserId(String userId); Optional findByUUID(UUID uuid); diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/members/MembersRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/members/MembersRepositoryImpl.java index 989bdd7d..7c562e89 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/members/MembersRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/members/MembersRepositoryImpl.java @@ -40,6 +40,22 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom { != null; } + /** + * 사용자 사번 조회 + * + * @param employeeNo + * @return + */ + @Override + public boolean existsByEmployeeNo(String employeeNo) { + return queryFactory + .selectOne() + .from(memberEntity) + .where(memberEntity.employeeNo.eq(employeeNo)) + .fetchFirst() + != null; + } + /** * 사용자 조회 user id * @@ -52,6 +68,21 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom { queryFactory.selectFrom(memberEntity).where(memberEntity.userId.eq(userId)).fetchOne()); } + /** + * 사용자 조회 employeed no + * + * @param employeeNo + * @return + */ + @Override + public Optional findByEmployeeNo(String employeeNo) { + return Optional.ofNullable( + queryFactory + .selectFrom(memberEntity) + .where(memberEntity.employeeNo.eq(employeeNo)) + .fetchOne()); + } + /** * 회원정보 목록 조회 * diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 7b211531..8a691ff3 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -15,8 +15,8 @@ spring: format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성) datasource: - #url: jdbc:postgresql://192.168.2.127:15432/kamco_cds - url: jdbc:postgresql://localhost:15432/kamco_cds + url: jdbc:postgresql://192.168.2.127:15432/kamco_cds + #url: jdbc:postgresql://localhost:15432/kamco_cds username: kamco_cds password: kamco_cds_Q!W@E#R$ hikari: @@ -29,6 +29,10 @@ spring: port: 6379 password: 1234 +logging: + level: + org.springframework.security: DEBUG + jwt: secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b" access-token-validity-in-ms: 86400000 # 1일 From c4ec9e0138c07158626172c6de047b42588ad67b Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Fri, 12 Dec 2025 18:05:03 +0900 Subject: [PATCH 2/5] =?UTF-8?q?SearchReq=20->=20Animal=20=EB=B0=94?= =?UTF-8?q?=EB=9D=BC=EB=B3=B4=EB=8D=98=20=EA=B2=83=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cd/kamcoback/code/dto/CommonCodeDto.java | 30 +++++++++++++++++++ .../postgres/core/CommonCodeCoreService.java | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/code/dto/CommonCodeDto.java b/src/main/java/com/kamco/cd/kamcoback/code/dto/CommonCodeDto.java index 963d6e90..e735108a 100644 --- a/src/main/java/com/kamco/cd/kamcoback/code/dto/CommonCodeDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/code/dto/CommonCodeDto.java @@ -15,6 +15,9 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; public class CommonCodeDto { @@ -132,6 +135,33 @@ public class CommonCodeDto { } } + @Schema(name = "SearchReq", description = "검색 요청") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class SearchReq { + + // 검색 조건 + private String name; + + // 페이징 파라미터 + private int page = 0; + private int size = 20; + private String sort; + + public Pageable toPageable() { + if (sort != null && !sort.isEmpty()) { + String[] sortParams = sort.split(","); + String property = sortParams[0]; + Sort.Direction direction = + sortParams.length > 1 ? Sort.Direction.fromString(sortParams[1]) : Sort.Direction.ASC; + return PageRequest.of(page, size, Sort.by(direction, property)); + } + return PageRequest.of(page, size); + } + } + @Getter public static class Clazzes { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/CommonCodeCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/CommonCodeCoreService.java index 259b885c..2e23ceb0 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/CommonCodeCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/CommonCodeCoreService.java @@ -7,7 +7,7 @@ import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ApiResponseCode; import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ResponseObj; import com.kamco.cd.kamcoback.postgres.entity.CommonCodeEntity; import com.kamco.cd.kamcoback.postgres.repository.code.CommonCodeRepository; -import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.SearchReq; +import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.SearchReq; import jakarta.persistence.EntityNotFoundException; import java.util.List; import java.util.Optional; From 81108870888883d7161863761d18988c58750870 Mon Sep 17 00:00:00 2001 From: DanielLee <198891672+sanghyeonhd@users.noreply.github.com> Date: Fri, 12 Dec 2025 18:52:51 +0900 Subject: [PATCH 3/5] File Management API Upload ALL Success --- .../auth/JwtAuthenticationFilter.java | 3 +- .../exception/DuplicateFileException.java | 7 + .../common/exception/ValidationException.java | 7 + .../config/GlobalExceptionHandler.java | 75 ++++++-- .../cd/kamcoback/config/SecurityConfig.java | 43 +++-- .../config/api/ApiResponseAdvice.java | 36 ++-- .../MapSheetMngFileCheckerApiController.java | 16 +- .../MapSheetMngFileCheckerService.java | 163 ++++++++++++++++-- .../mapsheet/service/MapSheetMngService.java | 1 - .../entity/MapSheetMngFileEntity.java | 12 +- .../postgres/entity/MapSheetMngHstEntity.java | 2 +- .../mapsheet/MapSheetMngFileRepository.java | 6 + .../mapsheet/MapSheetMngRepositoryImpl.java | 55 +++--- src/main/resources/application-dev.yml | 22 +++ src/main/resources/application-local.yml | 20 +++ src/main/resources/application.yml | 2 +- test_data/fake.tif | 1 + 17 files changed, 368 insertions(+), 103 deletions(-) create mode 100644 src/main/java/com/kamco/cd/kamcoback/common/exception/DuplicateFileException.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/common/exception/ValidationException.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngFileRepository.java create mode 100644 test_data/fake.tif diff --git a/src/main/java/com/kamco/cd/kamcoback/auth/JwtAuthenticationFilter.java b/src/main/java/com/kamco/cd/kamcoback/auth/JwtAuthenticationFilter.java index 6b1b84f1..e572e16e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/auth/JwtAuthenticationFilter.java +++ b/src/main/java/com/kamco/cd/kamcoback/auth/JwtAuthenticationFilter.java @@ -43,9 +43,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { String path = request.getServletPath(); - // 여기에 JWT 필터를 타지 않게 할 URL 패턴들 작성 + // JWT 필터를 타지 않게 할 URL 패턴들 return path.startsWith("/api/auth/signin") || path.startsWith("/api/auth/refresh"); - // 필요하면 "/api/auth/logout" 도 추가 } private String resolveToken(HttpServletRequest request) { diff --git a/src/main/java/com/kamco/cd/kamcoback/common/exception/DuplicateFileException.java b/src/main/java/com/kamco/cd/kamcoback/common/exception/DuplicateFileException.java new file mode 100644 index 00000000..8ee3b671 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/exception/DuplicateFileException.java @@ -0,0 +1,7 @@ +package com.kamco.cd.kamcoback.common.exception; + +public class DuplicateFileException extends RuntimeException { + public DuplicateFileException(String message) { + super(message); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/common/exception/ValidationException.java b/src/main/java/com/kamco/cd/kamcoback/common/exception/ValidationException.java new file mode 100644 index 00000000..f9df75b7 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/exception/ValidationException.java @@ -0,0 +1,7 @@ +package com.kamco.cd.kamcoback.common.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/config/GlobalExceptionHandler.java b/src/main/java/com/kamco/cd/kamcoback/config/GlobalExceptionHandler.java index bc7cdb74..701a863f 100644 --- a/src/main/java/com/kamco/cd/kamcoback/config/GlobalExceptionHandler.java +++ b/src/main/java/com/kamco/cd/kamcoback/config/GlobalExceptionHandler.java @@ -2,6 +2,8 @@ package com.kamco.cd.kamcoback.config; import com.kamco.cd.kamcoback.auth.CustomUserDetails; import com.kamco.cd.kamcoback.common.exception.CustomApiException; +import com.kamco.cd.kamcoback.common.exception.DuplicateFileException; +import com.kamco.cd.kamcoback.common.exception.ValidationException; import com.kamco.cd.kamcoback.config.api.ApiLogFunction; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ApiResponseCode; @@ -42,6 +44,60 @@ public class GlobalExceptionHandler { this.errorLogRepository = errorLogRepository; } + @ResponseStatus(HttpStatus.CONFLICT) + @ExceptionHandler(DuplicateFileException.class) + public ApiResponseDto handleDuplicateFileException( + DuplicateFileException e, HttpServletRequest request) { + log.warn("[DuplicateFileException] resource :{} ", e.getMessage()); + ApiResponseCode code = ApiResponseCode.CONFLICT; + ErrorLogEntity errorLog = + saveErrorLogData( + request, + code, + HttpStatus.CONFLICT, + ErrorLogDto.LogErrorLevel.WARNING, + e.getStackTrace()); + + return ApiResponseDto.createException( + code, e.getMessage(), HttpStatus.CONFLICT, errorLog.getId()); + } + + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) + @ExceptionHandler(ValidationException.class) + public ApiResponseDto handleValidationException( + ValidationException e, HttpServletRequest request) { + log.warn("[ValidationException] resource :{} ", e.getMessage()); + ApiResponseCode code = ApiResponseCode.UNPROCESSABLE_ENTITY; + ErrorLogEntity errorLog = + saveErrorLogData( + request, + code, + HttpStatus.UNPROCESSABLE_ENTITY, + ErrorLogDto.LogErrorLevel.WARNING, + e.getStackTrace()); + + return ApiResponseDto.createException( + code, e.getMessage(), HttpStatus.UNPROCESSABLE_ENTITY, errorLog.getId()); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(IllegalArgumentException.class) + public ApiResponseDto handleIllegalArgumentException( + IllegalArgumentException e, HttpServletRequest request) { + log.warn("[IllegalArgumentException] resource :{} ", e.getMessage()); + ApiResponseCode code = ApiResponseCode.BAD_REQUEST; + ErrorLogEntity errorLog = + saveErrorLogData( + request, + code, + HttpStatus.BAD_REQUEST, + ErrorLogDto.LogErrorLevel.WARNING, + e.getStackTrace()); + + return ApiResponseDto.createException( + code, e.getMessage(), HttpStatus.BAD_REQUEST, errorLog.getId()); + } + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) @ExceptionHandler(EntityNotFoundException.class) public ApiResponseDto handlerEntityNotFoundException( @@ -105,26 +161,7 @@ public class GlobalExceptionHandler { errorLog.getId()); } - @ResponseStatus(HttpStatus.BAD_REQUEST) - @ExceptionHandler(IllegalArgumentException.class) - public ApiResponseDto handlerIllegalArgumentException( - IllegalArgumentException e, HttpServletRequest request) { - log.warn("[handlerIllegalArgumentException] resource :{} ", e.getMessage()); - String codeName = "BAD_REQUEST"; - ErrorLogEntity errorLog = - saveErrorLogData( - request, - ApiResponseCode.getCode(codeName), - HttpStatus.valueOf(codeName), - ErrorLogDto.LogErrorLevel.WARNING, - e.getStackTrace()); - return ApiResponseDto.createException( - ApiResponseCode.getCode(codeName), - ApiResponseCode.getMessage(codeName), - HttpStatus.valueOf(codeName), - errorLog.getId()); - } @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) @ExceptionHandler(DataIntegrityViolationException.class) diff --git a/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java b/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java index eca8fcf7..a1cebc5d 100644 --- a/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java +++ b/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java @@ -11,9 +11,13 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.firewall.HttpFirewall; +import org.springframework.security.web.firewall.StrictHttpFirewall; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @@ -30,19 +34,23 @@ public class SecurityConfig { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.cors(cors -> cors.configurationSource(corsConfigurationSource())) - .csrf(csrf -> csrf.disable()) // CSRF 보안 기능 비활성화 - .sessionManagement( - sm -> - sm.sessionCreationPolicy( - SessionCreationPolicy.STATELESS)) // 서버 세션 만들지 않음, 요청은 JWT 인증 - .formLogin(form -> form.disable()) // react에서 로그인 요청 관리 - .httpBasic(basic -> basic.disable()) // 기본 basic 인증 비활성화 JWT 인증사용 - .logout(logout -> logout.disable()) // 기본 로그아웃 비활성화 JWT는 서버 상태가 없으므로 로그아웃 처리 필요 없음 - .authenticationProvider( - customAuthenticationProvider) // 로그인 패스워드 비교방식 스프링 기본 Provider 사용안함 커스텀 사용 + .csrf(csrf -> csrf.disable()) + .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .formLogin(form -> form.disable()) + .httpBasic(basic -> basic.disable()) + .logout(logout -> logout.disable()) + .authenticationProvider(customAuthenticationProvider) .authorizeHttpRequests( auth -> auth + // 맵시트 영역 전체 허용 (우선순위 최상단) + .requestMatchers("/api/mapsheet/**") + .permitAll() + + // 업로드 명시적 허용 + .requestMatchers(HttpMethod.POST, "/api/mapsheet/upload") + .permitAll() + // ADMIN만 접근 .requestMatchers("/api/test/admin") .hasRole("ADMIN") @@ -98,4 +106,19 @@ public class SecurityConfig { source.registerCorsConfiguration("/**", config); // CORS 정책을 등록 return source; } + + @Bean + public HttpFirewall httpFirewall() { + StrictHttpFirewall firewall = new StrictHttpFirewall(); + firewall.setAllowUrlEncodedSlash(true); + firewall.setAllowUrlEncodedDoubleSlash(true); + firewall.setAllowUrlEncodedPercent(true); + firewall.setAllowSemicolon(true); + return firewall; + } + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return (web) -> web.ignoring().requestMatchers(new AntPathRequestMatcher("/api/mapsheet/**")); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/config/api/ApiResponseAdvice.java b/src/main/java/com/kamco/cd/kamcoback/config/api/ApiResponseAdvice.java index e6d1f12f..034eccf3 100644 --- a/src/main/java/com/kamco/cd/kamcoback/config/api/ApiResponseAdvice.java +++ b/src/main/java/com/kamco/cd/kamcoback/config/api/ApiResponseAdvice.java @@ -56,37 +56,38 @@ public class ApiResponseAdvice implements ResponseBodyAdvice { ServerHttpResponse response) { HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); - ContentCachingRequestWrapper contentWrapper = (ContentCachingRequestWrapper) servletRequest; + ContentCachingRequestWrapper contentWrapper = null; + if (servletRequest instanceof ContentCachingRequestWrapper wrapper) { + contentWrapper = wrapper; + } if (body instanceof ApiResponseDto apiResponse) { - // ApiResponseDto에 설정된 httpStatus를 실제 HTTP 응답에 적용 response.setStatusCode(apiResponse.getHttpStatus()); String ip = ApiLogFunction.getClientIp(servletRequest); - Long userid = null; - /** - * servletRequest.getUserPrincipal() instanceof UsernamePasswordAuthenticationToken auth 이 요청이 - * JWT 인증을 통과한 요청인가? 그리고 Spring Security Authentication 객체가 - * UsernamePasswordAuthenticationToken 타입인가? 체크 - */ - /** - * auth.getPrincipal() instanceof CustomUserDetails customUserDetails principal 안에 들어있는 객체가 내가 - * 만든 CustomUserDetails 타입인가? 체크 - */ if (servletRequest.getUserPrincipal() instanceof UsernamePasswordAuthenticationToken auth && auth.getPrincipal() instanceof CustomUserDetails customUserDetails) { - - // audit 에는 long 타입 user_id가 들어가지만 토큰 sub은 uuid여서 user_id 가져오기 userid = customUserDetails.getMember().getId(); } - String requestBody = ApiLogFunction.getRequestBody(servletRequest, contentWrapper); - requestBody = maskSensitiveFields(requestBody); // 로그 저장전에 중요정보 마스킹 + String requestBody; + // 멀티파트 요청은 바디 로깅을 생략 (파일 바이너리로 인한 문제 예방) + MediaType reqContentType = null; + try { + String ct = servletRequest.getContentType(); + reqContentType = ct != null ? MediaType.valueOf(ct) : null; + } catch (Exception ignored) { + } + if (reqContentType != null && MediaType.MULTIPART_FORM_DATA.includes(reqContentType)) { + requestBody = "(multipart omitted)"; + } else { + requestBody = ApiLogFunction.getRequestBody(servletRequest, contentWrapper); + requestBody = maskSensitiveFields(requestBody); + } List list = menuService.getFindAll(); - List result = list.stream() .map( @@ -111,7 +112,6 @@ public class ApiResponseAdvice implements ResponseBodyAdvice { servletRequest.getRequestURI(), requestBody, apiResponse.getErrorLogUid()); - // tb_audit_log 테이블 저장 auditLogRepository.save(log); } 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 e702e106..ee87c0eb 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java @@ -14,13 +14,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @@ -74,9 +75,12 @@ public class MapSheetMngFileCheckerApiController { @Operation(summary = "파일 업로드", description = "파일 업로드 및 TIF 검증") @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ApiResponseDto uploadFile( - @RequestPart("file") MultipartFile file, - @RequestParam("targetPath") String targetPath) { - return ApiResponseDto.createOK(mapSheetMngFileCheckerService.uploadFile(file, targetPath)); + @RequestParam("file") MultipartFile file, + @RequestParam("targetPath") String targetPath, + @RequestParam(name = "overwrite", required = false, defaultValue = "true") + boolean overwrite) { + return ApiResponseDto.createOK( + mapSheetMngFileCheckerService.uploadFile(file, targetPath, overwrite)); } @Operation(summary = "파일 삭제", description = "중복 파일 등 파일 삭제") @@ -126,4 +130,8 @@ public class MapSheetMngFileCheckerApiController { */ + @PostMapping("/upload-test") + public String uploadTest(@RequestParam("name") String name) { + return "RECV:" + name; + } } 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 fc38a92f..7b8cfd08 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 @@ -2,6 +2,8 @@ package com.kamco.cd.kamcoback.mapsheet.service; import static java.lang.String.CASE_INSENSITIVE_ORDER; +import com.kamco.cd.kamcoback.common.exception.DuplicateFileException; +import com.kamco.cd.kamcoback.common.exception.ValidationException; import com.kamco.cd.kamcoback.common.utils.FIleChecker; import com.kamco.cd.kamcoback.common.utils.NameValidator; import com.kamco.cd.kamcoback.config.FileConfig; @@ -14,6 +16,8 @@ import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.SrchFilesDto; import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.SrchFoldersDto; import com.kamco.cd.kamcoback.mapsheet.dto.ImageryDto; import com.kamco.cd.kamcoback.postgres.core.MapSheetMngFileCheckerCoreService; +import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngFileEntity; +import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngFileRepository; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -31,6 +35,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -42,6 +47,10 @@ public class MapSheetMngFileCheckerService { private final MapSheetMngFileCheckerCoreService mapSheetMngFileCheckerCoreService; private final FileConfig fileConfig; + private final MapSheetMngFileRepository mapSheetMngFileRepository; + + @Value("${mapsheet.upload.skipGdalValidation:false}") + private boolean skipGdalValidation; public FoldersDto getFolderAll(SrchFoldersDto srchDto) { @@ -308,41 +317,145 @@ public class MapSheetMngFileCheckerService { } @Transactional - public String uploadFile(MultipartFile file, String targetPath) { + public String uploadFile(MultipartFile file, String targetPath, boolean overwrite) { try { Path path = Paths.get(targetPath); - // If targetPath is a directory, append the original filename if (Files.isDirectory(path)) { path = path.resolve(file.getOriginalFilename()); - } else if (Files.notExists(path) && Files.isDirectory(path.getParent())) { - // If path doesn't exist but parent is a directory, assume it's the full path - // (No change needed) } - - // Ensure parent directory exists if (path.getParent() != null) { Files.createDirectories(path.getParent()); } - // Save file + String filename = path.getFileName().toString(); + String ext = FilenameUtils.getExtension(filename).toLowerCase(); + String baseName = FilenameUtils.getBaseName(filename); + Path tfwPath = + path.getParent() == null + ? Paths.get(baseName + ".tfw") + : path.getParent().resolve(baseName + ".tfw"); + Path tifPath = + path.getParent() == null + ? Paths.get(baseName + ".tif") + : path.getParent().resolve(baseName + ".tif"); + + // 이미 존재하는 경우 처리 + if (Files.exists(path) && !overwrite) { + throw new DuplicateFileException("동일한 파일이 이미 존재합니다: " + path.getFileName()); + } + + // 업로드 파일 저장(덮어쓰기 허용 시 replace) file.transferTo(path.toFile()); - // Check TIF using Gdal functionality - String ext = FilenameUtils.getExtension(path.getFileName().toString()); - if ("tif".equalsIgnoreCase(ext) || "tiff".equalsIgnoreCase(ext)) { - boolean isValid = FIleChecker.cmmndGdalInfo(path.toString()); - if (!isValid) { - Files.delete(path); // Delete invalid file - throw new RuntimeException("유효하지 않은 TIF 파일입니다 (Gdal 검증 실패)."); + if ("tfw".equals(ext)) { + // TFW 검증 + boolean tfwOk = FIleChecker.checkTfw(path.toString()); + if (!tfwOk) { + Files.deleteIfExists(path); + throw new ValidationException( + "유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + path.getFileName()); } + // 안내: 같은 베이스의 TIF가 없으면 추후 TIF 업로드 필요 + if (!Files.exists(tifPath)) { + // DB 메타 저장은 진행 (향후 쌍 검증 위해) + saveUploadMeta(path); + return "TFW 업로드 성공 (매칭되는 TIF가 아직 없습니다)."; + } + // TIF가 존재하면 쌍 요건 충족 + saveUploadMeta(path); + return "TFW 업로드 성공"; } + if ("tif".equals(ext) || "tiff".equals(ext)) { + // GDAL 검증 (플래그에 따라 스킵) + if (!skipGdalValidation) { + boolean isValidTif = FIleChecker.cmmndGdalInfo(path.toString()); + if (!isValidTif) { + Files.deleteIfExists(path); + throw new ValidationException("유효하지 않은 TIF 파일입니다 (GDAL 검증 실패): " + path.getFileName()); + } + } + // TFW 존재/검증 + if (!Files.exists(tfwPath)) { + Files.deleteIfExists(path); + throw new ValidationException("TFW 파일이 존재하지 않습니다: " + tfwPath.getFileName()); + } + boolean tfwOk = FIleChecker.checkTfw(tfwPath.toString()); + if (!tfwOk) { + Files.deleteIfExists(path); + throw new ValidationException( + "유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + tfwPath.getFileName()); + } + saveUploadMeta(path); + return skipGdalValidation ? "TIF 업로드 성공(GDAL 검증 스킵)" : "TIF 업로드 성공"; + } + + // 기타 확장자: 저장만 하고 메타 기록 + saveUploadMeta(path); + return "업로드 성공"; } catch (IOException e) { - throw new RuntimeException("파일 업로드 실패: " + e.getMessage()); + throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage()); } } + private void saveUploadMeta(Path savedPath) { + String fullPath = savedPath.toAbsolutePath().toString(); + String fileName = savedPath.getFileName().toString(); + String ext = FilenameUtils.getExtension(fileName); + + // 연도(mng_yyyy) 추출: 경로 내의 연도 폴더명을 찾음 (예: .../original-images/2022/2022_25cm/...) + Integer mngYyyy = extractYearFromPath(fullPath); + + // 도엽번호(map_sheet_num) 추정: 파일명 내 숫자 연속 부분을 추출해 정수화 + String mapSheetNum = extractMapSheetNumFromFileName(fileName); + + // ref_map_sheet_num: 1000으로 나눈 값(파일명 규칙에 따라 추정) + String refMapSheetNum = null; + if (mapSheetNum != null && !mapSheetNum.isEmpty()) { + try { + long num = Long.parseLong(mapSheetNum); + refMapSheetNum = String.valueOf(num / 1000); + } catch (NumberFormatException ignored) { + } + } + + MapSheetMngFileEntity entity = new MapSheetMngFileEntity(); + entity.setMngYyyy(mngYyyy); + entity.setMapSheetNum(mapSheetNum); + entity.setRefMapSheetNum(refMapSheetNum); + entity.setFilePath(savedPath.getParent() != null ? savedPath.getParent().toString() : ""); + entity.setFileName(fileName); + entity.setFileExt(ext); + + mapSheetMngFileRepository.save(entity); + } + + private Integer extractYearFromPath(String fullPath) { + // 경로에서 4자리 연도를 찾아 가장 근접한 값을 사용 + // 예시 경로: /Users/.../original-images/2022/2022_25cm/1/34602 + String[] parts = fullPath.split("/"); + for (String p : parts) { + if (p.matches("\\d{4}")) { + try { + return Integer.parseInt(p); + } catch (NumberFormatException ignored) { + } + } + } + return null; + } + + private String extractMapSheetNumFromFileName(String fileName) { + // 파일명에서 연속된 숫자를 최대한 찾아 사용 (예: 34602027.tif -> 34602027) + String base = FilenameUtils.getBaseName(fileName); + String digits = base.replaceAll("[^0-9]", ""); + if (!digits.isEmpty()) { + return digits; + } + return null; + } + @Transactional public Boolean deleteFile(String filePath) { try { @@ -352,4 +465,22 @@ public class MapSheetMngFileCheckerService { throw new RuntimeException("파일 삭제 실패: " + e.getMessage()); } } + + @Transactional(readOnly = true) + public List findRecentFiles(int limit) { + // 간단히 전체를 불러 정렬/제한 (운영에선 Page 요청으로 변경 권장) + List all = new ArrayList<>(); + mapSheetMngFileRepository.findAll().forEach(all::add); + all.sort( + (a, b) -> { + // fileUid 기준 내림차순 + long av = a.getFileUid() == null ? 0L : a.getFileUid(); + long bv = b.getFileUid() == null ? 0L : b.getFileUid(); + return Long.compare(bv, av); + }); + if (all.size() > limit) { + return all.subList(0, limit); + } + return all; + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java index 40145c2e..e6daa08f 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java @@ -29,7 +29,6 @@ import org.apache.commons.io.FilenameUtils; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; @Service @RequiredArgsConstructor diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetMngFileEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetMngFileEntity.java index e3a1e930..df8757e6 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetMngFileEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetMngFileEntity.java @@ -28,10 +28,10 @@ public class MapSheetMngFileEntity { @NotNull @Column(name = "map_sheet_num", nullable = false) - private Integer mapSheetNum; + private String mapSheetNum; @Column(name = "ref_map_sheet_num") - private Integer refMapSheetNum; + private String refMapSheetNum; @Size(max = 255) @Column(name = "file_path") @@ -45,9 +45,9 @@ public class MapSheetMngFileEntity { @Column(name = "file_ext", length = 20) private String fileExt; - @Column(name = "mng_uid") - private Long mngUid; + // @Column(name = "mng_uid") + // private Long mngUid; - @Column(name = "hst_uid") - private Long hstUid; + // @Column(name = "hst_uid") + // private Long hstUid; } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetMngHstEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetMngHstEntity.java index 332fde9b..50c85e18 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetMngHstEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetMngHstEntity.java @@ -51,7 +51,7 @@ public class MapSheetMngHstEntity extends CommonDateEntity { private String mapSheetPath; @Column(name = "ref_map_sheet_num") - private Long refMapSheetNum; + private String refMapSheetNum; @Column(name = "created_uid") private Long createdUid; 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 new file mode 100644 index 00000000..212ea91c --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngFileRepository.java @@ -0,0 +1,6 @@ +package com.kamco.cd.kamcoback.postgres.repository.mapsheet; + +import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngFileEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MapSheetMngFileRepository extends JpaRepository {} 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 ddc1c4cb..c50ae12c 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 @@ -170,48 +170,53 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport @Override public void deleteByMngYyyyMngAll(int mngYyyy) { - long deletedFileCount = queryFactory - .delete(mapSheetMngFileEntity) - .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedFileCount = + queryFactory + .delete(mapSheetMngFileEntity) + .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy)) + .execute(); - long deletedHisCount = queryFactory - .delete(mapSheetMngHstEntity) - .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedHisCount = + queryFactory + .delete(mapSheetMngHstEntity) + .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy)) + .execute(); - long deletedMngCount = queryFactory - .delete(mapSheetMngEntity) - .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedMngCount = + queryFactory + .delete(mapSheetMngEntity) + .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) + .execute(); } - @Override public void deleteByMngYyyyMng(int mngYyyy) { - long deletedMngCount = queryFactory - .delete(mapSheetMngEntity) - .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedMngCount = + queryFactory + .delete(mapSheetMngEntity) + .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) + .execute(); } @Override public void deleteByMngYyyyMngHst(int mngYyyy) { - long deletedHisCount = queryFactory - .delete(mapSheetMngHstEntity) - .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedHisCount = + queryFactory + .delete(mapSheetMngHstEntity) + .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy)) + .execute(); } @Override public void deleteByMngYyyyMngFile(int mngYyyy) { - long deletedFileCount = queryFactory - .delete(mapSheetMngFileEntity) - .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy)) - .execute(); + long deletedFileCount = + queryFactory + .delete(mapSheetMngFileEntity) + .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy)) + .execute(); } @Override diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 2013e5fb..a06fba7a 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -35,6 +35,16 @@ spring: port: 6379 password: kamco + servlet: + multipart: + enabled: true + max-file-size: 1024MB + max-request-size: 2048MB + file-size-threshold: 10MB + +server: + tomcat: + max-swallow-size: 2097152000 # 약 2GB jwt: secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b" @@ -47,3 +57,15 @@ token: refresh-cookie-name: kamco-dev # 개발용 쿠키 이름 refresh-cookie-secure: false # 로컬 http 테스트면 false +logging: + level: + org: + springframework: + security: DEBUG + org.springframework.security: DEBUG + +mapsheet: + upload: + skipGdalValidation: true + + diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 7b211531..a35ad602 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -29,6 +29,17 @@ spring: port: 6379 password: 1234 + servlet: + multipart: + enabled: true + max-file-size: 1024MB + max-request-size: 2048MB + file-size-threshold: 10MB + +server: + tomcat: + max-swallow-size: 2097152000 # 약 2GB + jwt: secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b" access-token-validity-in-ms: 86400000 # 1일 @@ -38,4 +49,13 @@ token: refresh-cookie-name: kamco-local # 개발용 쿠키 이름 refresh-cookie-secure: false # 로컬 http 테스트면 false +logging: + level: + org: + springframework: + security: DEBUG + org.springframework.security: DEBUG +mapsheet: + upload: + skipGdalValidation: true diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f8cb1814..289b414c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -41,6 +41,7 @@ logging: web: DEBUG security: DEBUG root: INFO + org.springframework.security: DEBUG # actuator management: health: @@ -77,4 +78,3 @@ geojson: - tar.gz - tgz max-file-size: 104857600 # 100MB - diff --git a/test_data/fake.tif b/test_data/fake.tif new file mode 100644 index 00000000..97513f08 --- /dev/null +++ b/test_data/fake.tif @@ -0,0 +1 @@ +fake tif content From 7761357bad0019c8d420eb9c1d3cb9263be4c38f Mon Sep 17 00:00:00 2001 From: DanielLee <198891672+sanghyeonhd@users.noreply.github.com> Date: Fri, 12 Dec 2025 18:53:25 +0900 Subject: [PATCH 4/5] File Management API Upload ALL Success --- test_data/fake.tif | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test_data/fake.tif diff --git a/test_data/fake.tif b/test_data/fake.tif deleted file mode 100644 index 97513f08..00000000 --- a/test_data/fake.tif +++ /dev/null @@ -1 +0,0 @@ -fake tif content From 03ce680a7f02163c0acc74af7d5647cb5e0ec00c Mon Sep 17 00:00:00 2001 From: DanielLee <198891672+sanghyeonhd@users.noreply.github.com> Date: Fri, 12 Dec 2025 18:57:38 +0900 Subject: [PATCH 5/5] Build error fix --- .../mapsheet/MapSheetMngFileCheckerApiController.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 e702e106..eca12743 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngFileCheckerApiController.java @@ -75,8 +75,9 @@ public class MapSheetMngFileCheckerApiController { @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ApiResponseDto uploadFile( @RequestPart("file") MultipartFile file, - @RequestParam("targetPath") String targetPath) { - return ApiResponseDto.createOK(mapSheetMngFileCheckerService.uploadFile(file, targetPath)); + @RequestParam("targetPath") String targetPath, + @RequestParam(value = "overwrite", required = false, defaultValue = "false") boolean overwrite) { + return ApiResponseDto.createOK(mapSheetMngFileCheckerService.uploadFile(file, targetPath, overwrite)); } @Operation(summary = "파일 삭제", description = "중복 파일 등 파일 삭제")