diff --git a/nect-api/src/main/java/com/nect/api/domain/mypage/controller/MypageController.java b/nect-api/src/main/java/com/nect/api/domain/mypage/controller/MypageController.java index bc62608..1797bcb 100644 --- a/nect-api/src/main/java/com/nect/api/domain/mypage/controller/MypageController.java +++ b/nect-api/src/main/java/com/nect/api/domain/mypage/controller/MypageController.java @@ -24,10 +24,15 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; @RestController @RequestMapping("/api/v1/mypage") @@ -61,11 +66,22 @@ public ApiResponse getProfile( public ApiResponse updateProfile( @AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody ProfileSettingsRequestDto request - ) { + ) throws IOException { mypageService.updateProfile(userDetails.getUserId(), request); return ApiResponse.ok(); } + @PatchMapping(value = "/profile/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ApiResponse updateProfileMultipart( + @AuthenticationPrincipal UserDetailsImpl userDetails, + @RequestPart("request") ProfileSettingsRequestDto request, + @RequestParam(required = false) MultiValueMap files + ) throws IOException { + Map projectHistoryImages = extractProjectHistoryImages(files); + mypageService.updateProfile(userDetails.getUserId(), request, projectHistoryImages); + return ApiResponse.ok(); + } + /** * 프로젝트 조회 */ @@ -114,6 +130,48 @@ public ApiResponse editField(@PathVariable Long projectId, @RequestParam(" return ApiResponse.ok(); } + private Map extractProjectHistoryImages(MultiValueMap files) { + if (files == null || files.isEmpty()) { + return Collections.emptyMap(); + } + + Map result = new HashMap<>(); + for (Map.Entry> entry : files.entrySet()) { + Long projectHistoryId = parseProjectHistoryImageKey(entry.getKey()); + if (projectHistoryId == null) { + continue; + } + + List fileList = entry.getValue(); + if (fileList == null || fileList.isEmpty()) { + continue; + } + + MultipartFile file = fileList.get(0); + if (file != null && !file.isEmpty()) { + result.put(projectHistoryId, file); + } + } + + return result; + } + + private Long parseProjectHistoryImageKey(String key) { + if (key == null) { + return null; + } + String prefix = "projectHistoryImages["; + if (!key.startsWith(prefix) || !key.endsWith("]")) { + return null; + } + String idPart = key.substring(prefix.length(), key.length() - 1); + try { + return Long.parseLong(idPart); + } catch (NumberFormatException e) { + return null; + } + } + diff --git a/nect-api/src/main/java/com/nect/api/domain/mypage/service/MypageService.java b/nect-api/src/main/java/com/nect/api/domain/mypage/service/MypageService.java index b727ae6..e65bb3a 100644 --- a/nect-api/src/main/java/com/nect/api/domain/mypage/service/MypageService.java +++ b/nect-api/src/main/java/com/nect/api/domain/mypage/service/MypageService.java @@ -13,7 +13,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -77,7 +79,7 @@ public ProfileSettingsResponseDto getProfile(Long userId) { .map(projectHistory -> new ProjectHistoryDto( projectHistory.getUserProjectHistoryId(), projectHistory.getProjectName(), - projectHistory.getProjectImage(), + toPresignedProjectImage(projectHistory.getProjectImage()), projectHistory.getProjectDescription(), projectHistory.getStartYearMonth(), projectHistory.getEndYearMonth() @@ -147,7 +149,13 @@ public ProfileSettingsResponseDto getProfile(Long userId) { } @Transactional - public void updateProfile(Long userId, ProfileSettingsRequestDto request) { + public void updateProfile(Long userId, ProfileSettingsRequestDto request) throws IOException { + updateProfile(userId, request, Collections.emptyMap()); + } + + @Transactional + public void updateProfile(Long userId, ProfileSettingsRequestDto request, + Map projectHistoryImages) throws IOException { User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException("사용자를 찾을 수 없습니다.")); @@ -234,16 +242,18 @@ public void updateProfile(Long userId, ProfileSettingsRequestDto request) { if (request.projectHistories() != null) { userProjectHistoryRepository.deleteByUserUserId(userId); if (!request.projectHistories().isEmpty()) { - List newProjectHistories = request.projectHistories().stream() - .map(projectHistoryDto -> UserProjectHistory.builder() - .user(user) - .projectName(projectHistoryDto.projectName()) - .projectImage(projectHistoryDto.projectImage()) - .projectDescription(projectHistoryDto.projectDescription()) - .startYearMonth(projectHistoryDto.startYearMonth()) - .endYearMonth(projectHistoryDto.endYearMonth()) - .build()) - .collect(Collectors.toList()); + List newProjectHistories = new ArrayList<>(); + for (ProjectHistoryDto projectHistoryDto : request.projectHistories()) { + UserProjectHistory history = UserProjectHistory.builder() + .user(user) + .projectName(projectHistoryDto.projectName()) + .projectImage(resolveProjectHistoryImage(projectHistoryDto, projectHistoryImages)) + .projectDescription(projectHistoryDto.projectDescription()) + .startYearMonth(projectHistoryDto.startYearMonth()) + .endYearMonth(projectHistoryDto.endYearMonth()) + .build(); + newProjectHistories.add(history); + } userProjectHistoryRepository.saveAll(newProjectHistories); } } @@ -283,5 +293,26 @@ private UserStatus parseUserStatus(String userStatusStr) { } } + private String resolveProjectHistoryImage(ProjectHistoryDto dto, + Map projectHistoryImages) throws IOException { + if (projectHistoryImages == null || dto == null || dto.userProjectHistoryId() == null) { + return dto == null ? null : dto.projectImage(); + } + MultipartFile file = projectHistoryImages.get(dto.userProjectHistoryId()); + if (file == null || file.isEmpty()) { + return dto.projectImage(); + } + return s3Service.uploadFile(file); + } + + private String toPresignedProjectImage(String projectImage) { + if (projectImage == null || projectImage.isBlank()) { + return null; + } + if (projectImage.startsWith("http://") || projectImage.startsWith("https://")) { + return projectImage; + } + return s3Service.getPresignedGetUrl(projectImage); + } -} \ No newline at end of file +}