diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/application/ImageService.java b/backend/ddang/src/main/java/com/ddang/ddang/image/application/ImageService.java index e871a2936..bb64c5dca 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/application/ImageService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/application/ImageService.java @@ -3,7 +3,7 @@ import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; -import com.ddang.ddang.image.infrastructure.persistence.JpaImageRepository; +import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; @@ -22,7 +22,7 @@ public class ImageService { @Value("${image.store.dir}") private String imageStoreDir; - private final JpaImageRepository imageRepository; + private final JpaProfileImageRepository imageRepository; private final JpaAuctionImageRepository auctionImageRepository; public Resource readProfileImage(final Long id) throws MalformedURLException { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepository.java similarity index 66% rename from backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaImageRepository.java rename to backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepository.java index f04bb2f15..e1a218310 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaImageRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepository.java @@ -3,5 +3,5 @@ import com.ddang.ddang.image.domain.ProfileImage; import org.springframework.data.jpa.repository.JpaRepository; -public interface JpaImageRepository extends JpaRepository { +public interface JpaProfileImageRepository extends JpaRepository { } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/application/UserService.java b/backend/ddang/src/main/java/com/ddang/ddang/user/application/UserService.java index db5947839..8a296ad52 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/application/UserService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/application/UserService.java @@ -30,10 +30,19 @@ public ReadUserDto readById(final Long userId) { public ReadUserDto updateById(final Long userId, final UpdateUserDto userDto) { final User user = userRepository.findByIdAndDeletedIsFalse(userId) .orElseThrow(() -> new UserNotFoundException("사용자 정보를 사용할 수 없습니다.")); - final StoreImageDto storeImageDto = imageProcessor.storeImageFile(userDto.profileImage()); - user.update(userDto.name(), storeImageDto.toProfileImageEntity()); + updateUserByReqeust(userDto, user); return ReadUserDto.from(user); } + + private void updateUserByReqeust(final UpdateUserDto userDto, final User user) { + if (userDto.profileImage() != null) { + final StoreImageDto storeImageDto = imageProcessor.storeImageFile(userDto.profileImage()); + user.updateProfileImage(storeImageDto.toProfileImageEntity()); + } + if (userDto.name() != null) { + user.updateName(userDto.name()); + } + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/User.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/User.java index 25c12ba9c..0e02c571e 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/User.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/User.java @@ -62,9 +62,12 @@ private User( this.oauthId = oauthId; } - public void update(final String name, final ProfileImage profileProfileImage) { + public void updateName(final String name) { this.name = name; - this.profileImage = profileProfileImage; + } + + public void updateProfileImage(final ProfileImage profileImage) { + this.profileImage = profileImage; } public void withdrawal() { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/UserController.java b/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/UserController.java index 352fd739d..a47bcdf31 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/UserController.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/UserController.java @@ -35,13 +35,15 @@ public ResponseEntity readById(@AuthenticateUser final Authent @PatchMapping public ResponseEntity updateById( @AuthenticateUser final AuthenticationUserInfo userInfo, - @RequestPart @Valid final UpdateUserRequest request, - @RequestPart final MultipartFile profileImage + @RequestPart(required = false) @Valid final UpdateUserRequest request, + @RequestPart(required = false) final MultipartFile profileImage ) { - final ReadUserDto readUserDto = userService.updateById( - userInfo.userId(), - UpdateUserDto.of(request, profileImage) - ); + UpdateUserDto updateUserDto = null; + if (request != null) { + updateUserDto = UpdateUserDto.of(request, profileImage); + } + + final ReadUserDto readUserDto = userService.updateById(userInfo.userId(), updateUserDto); final ReadUserResponse response = ReadUserResponse.from(readUserDto); return ResponseEntity.ok(response); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/application/ProfileImageServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/image/application/ProfileImageServiceTest.java index cb206bcf5..91d6ecd41 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/image/application/ProfileImageServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/application/ProfileImageServiceTest.java @@ -4,7 +4,7 @@ import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; -import com.ddang.ddang.image.infrastructure.persistence.JpaImageRepository; +import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -27,7 +27,7 @@ class ProfileImageServiceTest { ImageService imageService; @Autowired - JpaImageRepository imageRepository; + JpaProfileImageRepository imageRepository; @Autowired JpaAuctionImageRepository auctionImageRepository; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionProfileImageRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionImageRepositoryTest.java similarity index 97% rename from backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionProfileImageRepositoryTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionImageRepositoryTest.java index a513e5bd1..59f4d4dd4 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionProfileImageRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionImageRepositoryTest.java @@ -19,7 +19,7 @@ @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") @Import(QuerydslConfiguration.class) -class JpaAuctionProfileImageRepositoryTest { +class JpaAuctionImageRepositoryTest { @PersistenceContext EntityManager em; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepositoryTest.java index fd6a66446..40de0afa7 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepositoryTest.java @@ -27,7 +27,7 @@ class JpaProfileImageRepositoryTest { EntityManager em; @Autowired - JpaImageRepository imageRepository; + JpaProfileImageRepository imageRepository; @Test void 지정한_아이디에_해당하는_이미지를_조회한다() { diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/application/UserServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/application/UserServiceTest.java index c2424a5d2..7af8b11ee 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/application/UserServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/application/UserServiceTest.java @@ -107,6 +107,68 @@ class UserServiceTest { }); } + @Test + void 사용자_정보를_수정시_이름만_수정한다() { + // given + final User user = User.builder() + .name("사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(4.7d) + .oauthId("12345") + .build(); + + userRepository.save(user); + + final UpdateUserDto updateUserDto = new UpdateUserDto("updateName", null); + + // when + userService.updateById(user.getId(), updateUserDto); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(user.getName()).isEqualTo("updateName"); + softAssertions.assertThat(user.getProfileImage().getImage().getStoreName()).isEqualTo("store.png"); + softAssertions.assertThat(user.getReliability()).isEqualTo(4.7d); + softAssertions.assertThat(user.getOauthId()).isEqualTo("12345"); + }); + } + + @Test + void 사용자_정보를_수정시_이미지만_수정한다() { + // given + final User user = User.builder() + .name("사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(4.7d) + .oauthId("12345") + .build(); + + userRepository.save(user); + + final StoreImageDto storeImageDto = new StoreImageDto("newUpload.png", "newStore.png"); + given(imageProcessor.storeImageFile(any())).willReturn(storeImageDto); + + final MockMultipartFile updateImage = new MockMultipartFile( + "updateImage.png", + "updateImage.png", + MediaType.IMAGE_PNG.toString(), + new byte[]{1} + ); + + final UpdateUserDto updateUserDto = new UpdateUserDto(null, updateImage); + + // when + userService.updateById(user.getId(), updateUserDto); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(user.getName()).isEqualTo("사용자"); + softAssertions.assertThat(user.getProfileImage().getImage().getStoreName()).isEqualTo("newStore.png"); + softAssertions.assertThat(user.getReliability()).isEqualTo(4.7d); + softAssertions.assertThat(user.getOauthId()).isEqualTo("12345"); + }); + } + @Test void 사용자_정보_수정시_존재하지_않는_사용자라면_예외가_발생한다() { // given diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserTest.java index e0b0305eb..e575cfa45 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserTest.java @@ -13,7 +13,7 @@ class UserTest { @Test - void 회원_정보를_수정한다() { + void 회원_정보의_이름을_수정한다() { // given final User user = User.builder() .name("kakao12345") @@ -22,12 +22,32 @@ class UserTest { .build(); // when - user.update("newName", new ProfileImage("newUpload.png", "newStore.png")); + user.updateName("newName"); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(user.getName()).isEqualTo("newName"); - softAssertions.assertThat(user.getProfileImage()).isEqualTo(new ProfileImage("newUpload.png", "newStore.png")); + softAssertions.assertThat(user.getProfileImage()).isEqualTo(new ProfileImage("upload.png", "store.png")); + softAssertions.assertThat(user.getReliability()).isEqualTo(5.0d); + }); + } + + @Test + void 회원_정보의_이미지를_수정한다() { + // given + final User user = User.builder() + .name("kakao12345") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(5.0d) + .build(); + + // when + user.updateProfileImage(new ProfileImage("updateUpload.png", "updateStore.png")); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(user.getName()).isEqualTo("kakao12345"); + softAssertions.assertThat(user.getProfileImage()).isEqualTo(new ProfileImage("updateUpload.png", "updateStore.png")); softAssertions.assertThat(user.getReliability()).isEqualTo(5.0d); }); } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserControllerTest.java index 9087cc5b3..5f3afbe40 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserControllerTest.java @@ -172,7 +172,7 @@ void setUp(@Autowired RestDocumentationContextProvider provider) { } @Test - void 사용자_정보를_수정한다() throws Exception { + void 사용자_정보를_모두_수정한다() throws Exception { // given final MockMultipartFile profileImage = new MockMultipartFile( "profileImage", @@ -218,14 +218,84 @@ void setUp(@Autowired RestDocumentationContextProvider provider) { ), responseFields( fieldWithPath("name").type(JsonFieldType.STRING).description("사용자 닉네임"), - fieldWithPath("profileImage").type(JsonFieldType.STRING) - .description("사용자 프로필 이미지"), + fieldWithPath("profileImage").type(JsonFieldType.STRING).description("사용자 프로필 이미지"), fieldWithPath("reliability").type(JsonFieldType.NUMBER).description("사용자 신뢰도") ) ) ); } + @Test + void 사용자_정보를_이름만_수정한다() throws Exception { + // given + final MockMultipartFile profileImage = new MockMultipartFile( + "profileImage", + (byte[]) null + ); + final UpdateUserRequest updateUserRequest = new UpdateUserRequest("updateName"); + final MockMultipartFile request = new MockMultipartFile( + "request", + "request", + MediaType.APPLICATION_JSON_VALUE, + objectMapper.writeValueAsBytes(updateUserRequest) + ); + + final ReadUserDto readUserDto = new ReadUserDto(1L, "사용자1", 1L, 4.6d, "12345", false); + final PrivateClaims privateClaims = new PrivateClaims(1L); + + given(userService.updateById(anyLong(), any())).willReturn(readUserDto); + given(mockTokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(privateClaims)); + + // when & then + mockMvc.perform(multipart(HttpMethod.PATCH, "/users") + .file(request) + .file(profileImage) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + ) + .andExpectAll( + status().isOk(), + jsonPath("$.name", is(readUserDto.name())), + jsonPath("$.profileImage").exists(), + jsonPath("$.reliability", is(readUserDto.reliability())) + ); + } + + @Test + void 사용자_정보를_이미지만_수정한다() throws Exception { + // given + final MockMultipartFile profileImage = new MockMultipartFile( + "profileImage", + "image.png", + MediaType.IMAGE_PNG_VALUE, + new byte[]{1} + ); + final MockMultipartFile request = new MockMultipartFile( + "request", + (byte[]) null + ); + + final ReadUserDto readUserDto = new ReadUserDto(1L, "사용자1", 1L, 4.6d, "12345", false); + final PrivateClaims privateClaims = new PrivateClaims(1L); + + given(userService.updateById(anyLong(), any())).willReturn(readUserDto); + given(mockTokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(privateClaims)); + + // when & then + mockMvc.perform(multipart(HttpMethod.PATCH, "/users") + .file(request) + .file(profileImage) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") + ) + .andExpectAll( + status().isOk(), + jsonPath("$.name", is(readUserDto.name())), + jsonPath("$.profileImage").exists(), + jsonPath("$.reliability", is(readUserDto.reliability())) + ); + } + @Test void 존재하지_않는_사용자_정보_조회시_404를_반환한다() throws Exception { // given