diff --git a/src/main/java/EatPic/spring/domain/card/service/CardServiceImpl.java b/src/main/java/EatPic/spring/domain/card/service/CardServiceImpl.java index ae59a60..1be96ab 100644 --- a/src/main/java/EatPic/spring/domain/card/service/CardServiceImpl.java +++ b/src/main/java/EatPic/spring/domain/card/service/CardServiceImpl.java @@ -72,10 +72,10 @@ public class CardServiceImpl implements CardService { private void connectHashtagsToCard(Card card, List hashtags, User user) { if (hashtags == null || hashtags.isEmpty()) return; for (String hashtagName : hashtags) { - // ① 이미 존재하는 해시태그 찾기 + // 이미 존재하는 해시태그 찾기 Hashtag hashtag = hashtagRepository.findByHashtagName(hashtagName) .orElseGet(() -> { - // ② 없으면 새로 생성 & 저장 + // 없으면 새로 생성 & 저장 Hashtag newHashtag = Hashtag.builder() .hashtagName(hashtagName) .user(user) @@ -83,7 +83,7 @@ private void connectHashtagsToCard(Card card, List hashtags, User user) return hashtagRepository.save(newHashtag); }); - // ③ CardHashtag 저장 + // CardHashtag 저장 CardHashtag cardHashtag = CardHashtag.builder() .card(card) .hashtag(hashtag) @@ -101,8 +101,6 @@ private void connectHashtagsToCard(Card card, List hashtags, User user) public CardResponse.CreateCardResponse createNewCard(CardCreateRequest.CreateCardRequest request, User user, MultipartFile cardImageFile) { Long userId = user.getId(); - // 아직 유저 관련 처리 안했음 - //User user = userRepository.findUserById(userId); // 오늘 날짜 00:00부터 23:59:59까지 범위 계산 LocalDate today = LocalDate.now(); @@ -134,8 +132,6 @@ public CardResponse.CreateCardResponse createNewCard(CardCreateRequest.CreateCar // S3 업로드 시 예외 발생 가능성 있음, try-catch로 처리 cardImageUrl = s3Manager.uploadFile(keyName, cardImageFile); } catch (Exception e) { - log.error("S3 파일 업로드 실패", e); - // 적절한 커스텀 예외 또는 공통 예외로 감싸서 던짐 throw new GeneralException(ErrorStatus.FILE_UPLOAD_FAILED); } } diff --git a/src/main/java/EatPic/spring/domain/user/controller/UserRestController.java b/src/main/java/EatPic/spring/domain/user/controller/UserRestController.java index 8056b07..25b1908 100644 --- a/src/main/java/EatPic/spring/domain/user/controller/UserRestController.java +++ b/src/main/java/EatPic/spring/domain/user/controller/UserRestController.java @@ -1,13 +1,17 @@ package EatPic.spring.domain.user.controller; import EatPic.spring.domain.user.dto.response.UserResponseDTO; +import EatPic.spring.domain.user.entity.User; import EatPic.spring.domain.user.service.UserService; import EatPic.spring.global.common.ApiResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; @RestController @RequiredArgsConstructor @@ -54,4 +58,14 @@ public ApiResponse unfollowUser(HttpServl return ApiResponse.onSuccess(userService.unfollowUser(request,userId)); } + @PatchMapping(value = "/setting/{userId}/profile-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "프로필 이미지 수정", description = "마이페이지에서 본인의 프로필 이미지를 수정합니다.") + public ApiResponse updateUserProfileImage( + HttpServletRequest request, + @RequestPart(value = "profileImage") MultipartFile profileImage) { + + UserResponseDTO.ProfileDto updatedProfile = userService.updateUserProfileImage(request, profileImage, userService.getLoginUser(request)); + return ApiResponse.onSuccess(updatedProfile); + } + } diff --git a/src/main/java/EatPic/spring/domain/user/entity/User.java b/src/main/java/EatPic/spring/domain/user/entity/User.java index 21154d8..f7c8f12 100644 --- a/src/main/java/EatPic/spring/domain/user/entity/User.java +++ b/src/main/java/EatPic/spring/domain/user/entity/User.java @@ -88,5 +88,10 @@ public void updateRefreshToken(String refreshToken) { public void updateLastNotificationCheckAt(LocalDateTime time) { this.lastNotificationCheckAt = time; } + + public void setProfileImageUrl(String profileImageUrl) { + this.profileImageUrl = profileImageUrl; + } + } diff --git a/src/main/java/EatPic/spring/domain/user/service/UserService.java b/src/main/java/EatPic/spring/domain/user/service/UserService.java index 30af2c7..a517402 100644 --- a/src/main/java/EatPic/spring/domain/user/service/UserService.java +++ b/src/main/java/EatPic/spring/domain/user/service/UserService.java @@ -8,6 +8,7 @@ import EatPic.spring.domain.user.dto.response.SignupResponseDTO; import EatPic.spring.domain.user.entity.User; import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.multipart.MultipartFile; public interface UserService { // UserCommandService @@ -25,6 +26,8 @@ public interface UserService { UserResponseDTO.UserActionResponseDto followUser(HttpServletRequest request, Long targetUserId); UserResponseDTO.UserActionResponseDto unfollowUser(HttpServletRequest request, Long targetUserId); User getLoginUser(HttpServletRequest request); + + UserResponseDTO.ProfileDto updateUserProfileImage(HttpServletRequest request, MultipartFile profileImage, User user); } diff --git a/src/main/java/EatPic/spring/domain/user/service/UserServiceImpl.java b/src/main/java/EatPic/spring/domain/user/service/UserServiceImpl.java index 44ad5df..98668c1 100644 --- a/src/main/java/EatPic/spring/domain/user/service/UserServiceImpl.java +++ b/src/main/java/EatPic/spring/domain/user/service/UserServiceImpl.java @@ -15,6 +15,7 @@ import EatPic.spring.domain.user.repository.UserRepository; import EatPic.spring.domain.user.repository.UserBlockRepository; import EatPic.spring.global.common.code.status.ErrorStatus; +import EatPic.spring.global.common.exception.GeneralException; import EatPic.spring.global.common.exception.handler.ExceptionHandler; import EatPic.spring.global.config.jwt.JwtTokenProvider; import jakarta.servlet.http.HttpServletRequest; @@ -26,8 +27,11 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import EatPic.spring.global.aws.s3.*; import java.util.Collections; +import java.util.UUID; import static EatPic.spring.global.common.code.status.ErrorStatus.*; @@ -42,6 +46,9 @@ public class UserServiceImpl implements UserService{ private final PasswordEncoder passwordEncoder; private final JwtTokenProvider jwtTokenProvider; + // s3 설정 + private final AmazonS3Manager s3Manager; + // 회원가입 public SignupResponseDTO signup(SignupRequestDTO request) { // 이메일 중복 검사 @@ -220,4 +227,33 @@ public UserResponseDTO.UserActionResponseDto followUser(HttpServletRequest reque return UserConverter.toUserActionResponseDto(follow); } + + @Override + public UserResponseDTO.ProfileDto updateUserProfileImage(HttpServletRequest request, MultipartFile profileImage,User user) { + + String profileImageUrl = null; + if (profileImage != null && !profileImage.isEmpty()) { + String uuid = UUID.randomUUID().toString(); + String keyName = "userProfiles/" + uuid + "_" + profileImage.getOriginalFilename(); + + try { + profileImageUrl = s3Manager.uploadFile(keyName, profileImage); + } catch (Exception e) { + throw new GeneralException(ErrorStatus.FILE_UPLOAD_FAILED); + } + + // 프로필 이미지 URL 업데이트 + user.setProfileImageUrl(profileImageUrl); + + // 유저 정보 DB 저장 + userRepository.save(user); + } + + // 업데이트 결과를 DTO로 반환 + return UserResponseDTO.ProfileDto.builder() + .userId(user.getId()) + .profileImageUrl(profileImageUrl) // 새 URL + .build(); + } + } \ No newline at end of file