diff --git a/src/main/java/com/ajou/hertz/domain/instrument/amplifier/entity/Amplifier.java b/src/main/java/com/ajou/hertz/domain/instrument/amplifier/entity/Amplifier.java index 6fb8fe5..d674949 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/amplifier/entity/Amplifier.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/amplifier/entity/Amplifier.java @@ -4,7 +4,9 @@ import com.ajou.hertz.domain.instrument.amplifier.constant.AmplifierBrand; import com.ajou.hertz.domain.instrument.amplifier.constant.AmplifierType; import com.ajou.hertz.domain.instrument.amplifier.constant.AmplifierUsage; +import com.ajou.hertz.domain.instrument.amplifier.dto.request.AmplifierUpdateRequest; import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.dto.request.InstrumentUpdateRequest; import com.ajou.hertz.domain.instrument.entity.Instrument; import com.ajou.hertz.domain.user.entity.User; @@ -73,4 +75,12 @@ public static Amplifier create( price, hasAnomaly, description, type, brand, usage ); } + + public void update(InstrumentUpdateRequest updateRequest) { + AmplifierUpdateRequest amplifierUpdateRequest = (AmplifierUpdateRequest)updateRequest; + super.update(updateRequest); + this.type = amplifierUpdateRequest.getType(); + this.brand = amplifierUpdateRequest.getBrand(); + this.usage = amplifierUpdateRequest.getUsage(); + } } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentController.java b/src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentController.java index 8d69d43..851271d 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentController.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentController.java @@ -45,6 +45,7 @@ import com.ajou.hertz.domain.instrument.effector.dto.EffectorDto; import com.ajou.hertz.domain.instrument.effector.dto.request.CreateNewEffectorRequest; import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorFilterConditions; +import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorUpdateRequest; import com.ajou.hertz.domain.instrument.effector.dto.response.EffectorResponse; import com.ajou.hertz.domain.instrument.electric_guitar.dto.ElectricGuitarDto; import com.ajou.hertz.domain.instrument.electric_guitar.dto.request.CreateNewElectricGuitarRequest; @@ -517,6 +518,29 @@ public AmplifierResponse updateAmplifiersV1( return InstrumentMapper.toAmplifierResponse(amplifierDto); } + @Operation( + summary = "이펙터 기타 매물 수정", + description = """ +

이펙터 매물 정보를 수정합니다. +

요청 시 multipart/form-data content-type으로 요쳥해야 합니다. +

변경하고자 하는 매물 정보만 request body에 담아 요청하면 됩니다. 요청 시 보내지 않은 필드는 수정하지 않습니다. + """, + security = @SecurityRequirement(name = "access-token") + ) + @PatchMapping("/effectors/{effectorId}") + public EffectorResponse updateEffectorV1( + @AuthenticationPrincipal UserPrincipal userPrincipal, + @Parameter(description = "수정하고자 하는 악기 매물의 id", example = "2") @PathVariable Long effectorId, + @ParameterObject @ModelAttribute @Valid EffectorUpdateRequest updateRequest + ) { + EffectorDto effectorDto = instrumentCommandService.updateEffector( + userPrincipal.getUserId(), + effectorId, + updateRequest + ); + return InstrumentMapper.toEffectorResponse(effectorDto); + } + @Operation( summary = "악기 매물 삭제", description = "악기 매물을 삭제합니다. 매물 삭제는 판매자만 할 수 있습니다.", diff --git a/src/main/java/com/ajou/hertz/domain/instrument/effector/dto/request/EffectorUpdateRequest.java b/src/main/java/com/ajou/hertz/domain/instrument/effector/dto/request/EffectorUpdateRequest.java new file mode 100644 index 0000000..ed7624e --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/effector/dto/request/EffectorUpdateRequest.java @@ -0,0 +1,51 @@ +package com.ajou.hertz.domain.instrument.effector.dto.request; + +import java.util.List; + +import org.springframework.lang.Nullable; +import org.springframework.web.multipart.MultipartFile; + +import com.ajou.hertz.common.dto.request.AddressRequest; +import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.dto.request.InstrumentUpdateRequest; +import com.ajou.hertz.domain.instrument.effector.constant.EffectorFeature; +import com.ajou.hertz.domain.instrument.effector.constant.EffectorType; + +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Setter +@Getter +public class EffectorUpdateRequest extends InstrumentUpdateRequest { + + @NotNull + private EffectorType type; + + @NotNull + private EffectorFeature feature; + + private EffectorUpdateRequest( + @Nullable String title, + @Nullable InstrumentProgressStatus progressStatus, + @Nullable AddressRequest tradeAddress, + @Nullable Short qualityStatus, + @Nullable Integer price, + @Nullable Boolean hasAnomaly, + @Nullable String description, + @Nullable List deletedImageIds, + @Nullable List newImages, + @Nullable List deletedHashtagIds, + @Nullable List newHashtags, + @Nullable EffectorType type, + @Nullable EffectorFeature feature + ) { + super(title, progressStatus, tradeAddress, qualityStatus, price, hasAnomaly, description, + deletedImageIds, newImages, deletedHashtagIds, newHashtags); + this.type = type; + this.feature = feature; + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/effector/entity/Effector.java b/src/main/java/com/ajou/hertz/domain/instrument/effector/entity/Effector.java index 9f87d7b..5dbdba7 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/effector/entity/Effector.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/effector/entity/Effector.java @@ -1,9 +1,11 @@ package com.ajou.hertz.domain.instrument.effector.entity; import com.ajou.hertz.common.entity.Address; +import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.dto.request.InstrumentUpdateRequest; import com.ajou.hertz.domain.instrument.effector.constant.EffectorFeature; import com.ajou.hertz.domain.instrument.effector.constant.EffectorType; -import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorUpdateRequest; import com.ajou.hertz.domain.instrument.entity.Instrument; import com.ajou.hertz.domain.user.entity.User; @@ -65,4 +67,11 @@ public static Effector create( price, hasAnomaly, description, type, feature ); } + + public void update(InstrumentUpdateRequest updateRequest) { + EffectorUpdateRequest effectorUpdateRequest = (EffectorUpdateRequest)updateRequest; + super.update(updateRequest); + this.type = effectorUpdateRequest.getType(); + this.feature = effectorUpdateRequest.getFeature(); + } } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/service/InstrumentCommandService.java b/src/main/java/com/ajou/hertz/domain/instrument/service/InstrumentCommandService.java index f9ee636..69677c4 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/service/InstrumentCommandService.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/service/InstrumentCommandService.java @@ -29,6 +29,7 @@ import com.ajou.hertz.domain.instrument.dto.request.InstrumentUpdateRequest; import com.ajou.hertz.domain.instrument.effector.dto.EffectorDto; import com.ajou.hertz.domain.instrument.effector.dto.request.CreateNewEffectorRequest; +import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorUpdateRequest; import com.ajou.hertz.domain.instrument.effector.entity.Effector; import com.ajou.hertz.domain.instrument.effector.strategy.EffectorCreationStrategy; import com.ajou.hertz.domain.instrument.electric_guitar.dto.ElectricGuitarDto; @@ -266,6 +267,22 @@ public AmplifierDto updateAmplifier( return (AmplifierDto)updateInstrument(userId, amplifierId, updateRequest); } + /** + * 이펙터 매물 정보를 수정한다. + * + * @param userId 수정하고자 하는 유저의 id. 악기 판매자와 동일해야 한다. + * @param effectorId 수정할 일렉 기타의 id + * @param updateRequest 수정하고자 하는 정보 + * @return 수정된 일렉 기타 매물 정보 + */ + public EffectorDto updateEffector( + Long userId, + Long effectorId, + EffectorUpdateRequest updateRequest + ) { + return (EffectorDto)updateInstrument(userId, effectorId, updateRequest); + } + /** * 악기 매물을 삭제한다. * diff --git a/src/test/java/com/ajou/hertz/unit/domain/instrument/controller/InstrumentControllerTest.java b/src/test/java/com/ajou/hertz/unit/domain/instrument/controller/InstrumentControllerTest.java index 933ae46..fd71268 100644 --- a/src/test/java/com/ajou/hertz/unit/domain/instrument/controller/InstrumentControllerTest.java +++ b/src/test/java/com/ajou/hertz/unit/domain/instrument/controller/InstrumentControllerTest.java @@ -67,6 +67,7 @@ import com.ajou.hertz.domain.instrument.effector.dto.EffectorDto; import com.ajou.hertz.domain.instrument.effector.dto.request.CreateNewEffectorRequest; import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorFilterConditions; +import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorUpdateRequest; import com.ajou.hertz.domain.instrument.electric_guitar.constant.ElectricGuitarBrand; import com.ajou.hertz.domain.instrument.electric_guitar.constant.ElectricGuitarModel; import com.ajou.hertz.domain.instrument.electric_guitar.dto.ElectricGuitarDto; @@ -774,7 +775,6 @@ public InstrumentControllerTest(MockMvc mvc) { .param("price", String.valueOf(updateRequest.getPrice())) .param("hasAnomaly", String.valueOf(updateRequest.getHasAnomaly())) .param("description", updateRequest.getDescription()) - .param("brand", String.valueOf(updateRequest.getBrand())) .param("type", String.valueOf(updateRequest.getType())) .param("brand", String.valueOf(updateRequest.getBrand())) .param("usage", String.valueOf(updateRequest.getUsage())) @@ -793,6 +793,46 @@ public InstrumentControllerTest(MockMvc mvc) { verifyEveryMocksShouldHaveNoMoreInteractions(); } + @Test + void 수정할_이펙터_정보가_주어지고_주어진_정보로_매물_정보를_수정한다() throws Exception { + // given + long userId = 1L; + long effectorId = 2L; + EffectorUpdateRequest updateRequest = createEffectorUpdateRequest(); + EffectorDto expectedResult = createEffectorDto(effectorId, userId); + given(instrumentCommandService.updateEffector( + eq(userId), + eq(effectorId), + any(EffectorUpdateRequest.class) + )).willReturn(expectedResult); + + // when & then + mvc.perform( + multipart("/api/instruments/effectors/{effectorId}", effectorId) + .header(API_VERSION_HEADER_NAME, 1) + .param("title", updateRequest.getTitle()) + .param("progressStatus", String.valueOf(updateRequest.getProgressStatus())) + .param("qualityStatus", String.valueOf(updateRequest.getQualityStatus())) + .param("price", String.valueOf(updateRequest.getPrice())) + .param("hasAnomaly", String.valueOf(updateRequest.getHasAnomaly())) + .param("description", updateRequest.getDescription()) + .param("type", String.valueOf(updateRequest.getType())) + .param("feature", String.valueOf(updateRequest.getFeature())) + .with(user(createTestUser(userId))) + .with(request -> { + request.setMethod("PATCH"); + return request; + }) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(expectedResult.getId())); + then(instrumentCommandService) + .should() + .updateEffector(eq(userId), eq(effectorId), + any(EffectorUpdateRequest.class)); + verifyEveryMocksShouldHaveNoMoreInteractions(); + } + @Test void 악기_id가_주어지고_해당하는_악기_매물을_삭제한다() throws Exception { // given @@ -1174,4 +1214,22 @@ private AmplifierUpdateRequest createAmplifierUpdateRequest() throws Exception { ); } + private EffectorUpdateRequest createEffectorUpdateRequest() throws Exception { + return ReflectionUtils.createEffectorUpdateRequest( + "Test Effector", + InstrumentProgressStatus.SOLD_OUT, + createAddressRequest(), + (short)3, + 550000, + true, + "description", + null, + null, + null, + null, + EffectorType.GUITAR, + EffectorFeature.GUITAR_WAH + ); + } + } diff --git a/src/test/java/com/ajou/hertz/unit/domain/instrument/service/InstrumentCommandServiceTest.java b/src/test/java/com/ajou/hertz/unit/domain/instrument/service/InstrumentCommandServiceTest.java index e3aee2b..c4be6da 100644 --- a/src/test/java/com/ajou/hertz/unit/domain/instrument/service/InstrumentCommandServiceTest.java +++ b/src/test/java/com/ajou/hertz/unit/domain/instrument/service/InstrumentCommandServiceTest.java @@ -53,6 +53,7 @@ import com.ajou.hertz.domain.instrument.effector.constant.EffectorType; import com.ajou.hertz.domain.instrument.effector.dto.EffectorDto; import com.ajou.hertz.domain.instrument.effector.dto.request.CreateNewEffectorRequest; +import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorUpdateRequest; import com.ajou.hertz.domain.instrument.effector.entity.Effector; import com.ajou.hertz.domain.instrument.electric_guitar.constant.ElectricGuitarBrand; import com.ajou.hertz.domain.instrument.electric_guitar.constant.ElectricGuitarModel; @@ -425,6 +426,36 @@ class InstrumentCommandServiceTest { assertThat(result.getImages()).hasSize(newImages.size()); } + @Test + void 추가할_이미지들이_주어지고_이펙터_정보를_수정하면_새로운_악기_이미지들이_추가된다() throws Exception { + // given + long userId = 1L; + long instrumentId = 2L; + Effector effector = createEffector(instrumentId, createUser(userId)); + List newImages = List.of( + createMultipartFile(), + createMultipartFile() + ); + List newInstrumentImages = List.of( + createInstrumentImage(3L, effector), + createInstrumentImage(4L, effector) + ); + EffectorUpdateRequest updateRequest = createEffectorUpdateRequest(List.of(), newImages, null, null); + given(instrumentQueryService.getInstrumentById(instrumentId)) + .willReturn(effector); + given(instrumentImageCommandService.saveImages(effector, updateRequest.getNewImages())) + .willReturn(newInstrumentImages); + + // when + EffectorDto result = sut.updateEffector(userId, instrumentId, updateRequest); + + // then + then(instrumentQueryService).should().getInstrumentById(instrumentId); + then(instrumentImageCommandService).should().saveImages(effector, updateRequest.getNewImages()); + verifyEveryMocksShouldHaveNoMoreInteractions(); + assertThat(result.getImages()).hasSize(newImages.size()); + } + @Test void 삭제할_해시태그들의_id_리스트가_주어지고_매물_정보를_수정하면_해시태그가_삭제된다() throws Exception { long userId = 1L; @@ -836,4 +867,17 @@ private AmplifierUpdateRequest createAmplifierUpdateRequest( null, null, null ); } + + private EffectorUpdateRequest createEffectorUpdateRequest( + @Nullable List deletedImageIds, + @Nullable List newImages, + @Nullable List deletedHashtagIds, + @Nullable List newHashtags + ) throws Exception { + return ReflectionUtils.createEffectorUpdateRequest( + null, null, null, null, null, null, null, + deletedImageIds, newImages, deletedHashtagIds, newHashtags, + null, null + ); + } } diff --git a/src/test/java/com/ajou/hertz/util/ReflectionUtils.java b/src/test/java/com/ajou/hertz/util/ReflectionUtils.java index dac8c14..1b7cecc 100644 --- a/src/test/java/com/ajou/hertz/util/ReflectionUtils.java +++ b/src/test/java/com/ajou/hertz/util/ReflectionUtils.java @@ -65,6 +65,7 @@ import com.ajou.hertz.domain.instrument.effector.dto.EffectorDto; import com.ajou.hertz.domain.instrument.effector.dto.request.CreateNewEffectorRequest; import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorFilterConditions; +import com.ajou.hertz.domain.instrument.effector.dto.request.EffectorUpdateRequest; import com.ajou.hertz.domain.instrument.effector.entity.Effector; import com.ajou.hertz.domain.instrument.electric_guitar.constant.ElectricGuitarBrand; import com.ajou.hertz.domain.instrument.electric_guitar.constant.ElectricGuitarModel; @@ -1100,6 +1101,44 @@ public static AmplifierUpdateRequest createAmplifierUpdateRequest( ); } + public static EffectorUpdateRequest createEffectorUpdateRequest( + @Nullable String title, + @Nullable InstrumentProgressStatus progressStatus, + @Nullable AddressRequest tradeAddress, + @Nullable Short qualityStatus, + @Nullable Integer price, + @Nullable Boolean hasAnomaly, + @Nullable String description, + @Nullable List deletedImageIds, + @Nullable List newImages, + @Nullable List deletedHashtagIds, + @Nullable List newHashtags, + @Nullable EffectorType type, + @Nullable EffectorFeature feature + ) throws Exception { + Constructor constructor = EffectorUpdateRequest.class.getDeclaredConstructor( + String.class, InstrumentProgressStatus.class, AddressRequest.class, Short.class, + Integer.class, Boolean.class, String.class, List.class, List.class, List.class, List.class, + EffectorType.class, EffectorFeature.class + ); + constructor.setAccessible(true); + return constructor.newInstance( + title, + progressStatus, + tradeAddress, + qualityStatus, + price, + hasAnomaly, + description, + deletedImageIds, + newImages, + deletedHashtagIds, + newHashtags, + type, + feature + ); + } + public static SendUserAuthCodeRequest createSendUserAuthCodeRequest(String phoneNumber) throws Exception { Constructor constructor = SendUserAuthCodeRequest.class.getDeclaredConstructor(String.class);