diff --git a/src/main/java/com/ajou/hertz/domain/instrument/audio_equipment/dto/request/AudioEquipmentUpdateRequest.java b/src/main/java/com/ajou/hertz/domain/instrument/audio_equipment/dto/request/AudioEquipmentUpdateRequest.java new file mode 100644 index 0000000..7c8cf29 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/audio_equipment/dto/request/AudioEquipmentUpdateRequest.java @@ -0,0 +1,45 @@ +package com.ajou.hertz.domain.instrument.audio_equipment.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.audio_equipment.constant.AudioEquipmentType; +import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.dto.request.InstrumentUpdateRequest; + +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 AudioEquipmentUpdateRequest extends InstrumentUpdateRequest { + + @NotNull + private AudioEquipmentType type; + + private AudioEquipmentUpdateRequest( + @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 AudioEquipmentType type + ) { + super(title, progressStatus, tradeAddress, qualityStatus, price, hasAnomaly, description, + deletedImageIds, newImages, deletedHashtagIds, newHashtags); + this.type = type; + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/audio_equipment/entity/AudioEquipment.java b/src/main/java/com/ajou/hertz/domain/instrument/audio_equipment/entity/AudioEquipment.java index c6ca285..8ee8d6e 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/audio_equipment/entity/AudioEquipment.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/audio_equipment/entity/AudioEquipment.java @@ -2,7 +2,9 @@ import com.ajou.hertz.common.entity.Address; import com.ajou.hertz.domain.instrument.audio_equipment.constant.AudioEquipmentType; +import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentUpdateRequest; 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; @@ -57,4 +59,10 @@ public static AudioEquipment create( qualityStatus, price, hasAnomaly, description, type ); } + + public void update(InstrumentUpdateRequest updateRequest) { + AudioEquipmentUpdateRequest audioEquipmentUpdateRequest = (AudioEquipmentUpdateRequest)updateRequest; + super.update(updateRequest); + this.type = audioEquipmentUpdateRequest.getType(); + } } 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 5a93779..d485ccf 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 @@ -13,6 +13,7 @@ import com.ajou.hertz.domain.instrument.amplifier.dto.response.AmplifierResponse; import com.ajou.hertz.domain.instrument.audio_equipment.dto.AudioEquipmentDto; import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentFilterConditions; +import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentUpdateRequest; import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.CreateNewAudioEquipmentRequest; import com.ajou.hertz.domain.instrument.audio_equipment.dto.response.AudioEquipmentResponse; import com.ajou.hertz.domain.instrument.bass_guitar.dto.BassGuitarDto; @@ -555,6 +556,29 @@ public EffectorResponse updateEffectorV1( return InstrumentMapper.toEffectorResponse(effectorDto); } + @Operation( + summary = "음향장비 매물 수정", + description = """ +

음향장비 매물 정보를 수정합니다. +

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

변경하고자 하는 매물 정보만 request body에 담아 요청하면 됩니다. 요청 시 보내지 않은 필드는 수정하지 않습니다. + """, + security = @SecurityRequirement(name = "access-token") + ) + @PatchMapping("/audio-equipments/{audioEquipmentsId}") + public AudioEquipmentResponse updateAudioEquipmentsV1( + @AuthenticationPrincipal UserPrincipal userPrincipal, + @Parameter(description = "수정하고자 하는 악기 매물의 id", example = "2") @PathVariable Long audioEquipmentsId, + @ParameterObject @ModelAttribute @Valid AudioEquipmentUpdateRequest updateRequest + ) { + AudioEquipmentDto audioEquipmentDto = instrumentCommandService.updateAudioEquipment( + userPrincipal.getUserId(), + audioEquipmentsId, + updateRequest + ); + return InstrumentMapper.toAudioEquipmentResponse(audioEquipmentDto); + } + @Operation( summary = "악기 매물 삭제", description = "악기 매물을 삭제합니다. 매물 삭제는 판매자만 할 수 있습니다.", 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 34c4810..66eae0c 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 @@ -1,5 +1,11 @@ package com.ajou.hertz.domain.instrument.service; +import java.util.Collection; +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import com.ajou.hertz.domain.instrument.acoustic_and_classic_guitar.dto.AcousticAndClassicGuitarDto; import com.ajou.hertz.domain.instrument.acoustic_and_classic_guitar.dto.request.AcousticAndClassicGuitarUpdateRequest; import com.ajou.hertz.domain.instrument.acoustic_and_classic_guitar.dto.request.CreateNewAcousticAndClassicGuitarRequest; @@ -11,6 +17,7 @@ import com.ajou.hertz.domain.instrument.amplifier.entity.Amplifier; import com.ajou.hertz.domain.instrument.amplifier.strategy.AmplifierCreationStrategy; import com.ajou.hertz.domain.instrument.audio_equipment.dto.AudioEquipmentDto; +import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentUpdateRequest; import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.CreateNewAudioEquipmentRequest; import com.ajou.hertz.domain.instrument.audio_equipment.entity.AudioEquipment; import com.ajou.hertz.domain.instrument.audio_equipment.strategy.AudioEquipmentCreationStrategy; @@ -42,12 +49,8 @@ import com.ajou.hertz.domain.instrument.strategy.InstrumentCreationStrategy; import com.ajou.hertz.domain.user.entity.User; import com.ajou.hertz.domain.user.service.UserQueryService; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.Collection; -import java.util.List; +import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Transactional @@ -298,6 +301,22 @@ public EffectorDto updateEffector( return (EffectorDto)updateInstrument(userId, effectorId, updateRequest); } + /** + * 음향장비 매물 정보를 수정한다. + * + * @param userId 수정하고자 하는 유저의 id. 악기 판매자와 동일해야 한다. + * @param audioEquipmentsId 수정할 일렉 기타의 id + * @param updateRequest 수정하고자 하는 정보 + * @return 수정된 일렉 기타 매물 정보 + */ + public AudioEquipmentDto updateAudioEquipment( + Long userId, + Long audioEquipmentsId, + AudioEquipmentUpdateRequest updateRequest + ) { + return (AudioEquipmentDto)updateInstrument(userId, audioEquipmentsId, updateRequest); + } + /** * 판매자가 sellerId와 일치하는 모든 악기 매물 데이터에서 판매자를 제거한다. (회원 탈퇴 시 사용) * @@ -305,7 +324,7 @@ public EffectorDto updateEffector( */ public void removeSellerFromInstruments(Long sellerId) { instrumentRepository.findAllBySellerId(sellerId) - .forEach(Instrument::removeSeller); + .forEach(Instrument::removeSeller); } /** 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 fd71268..9f1dbe4 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 @@ -47,6 +47,7 @@ import com.ajou.hertz.domain.instrument.audio_equipment.constant.AudioEquipmentType; import com.ajou.hertz.domain.instrument.audio_equipment.dto.AudioEquipmentDto; import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentFilterConditions; +import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentUpdateRequest; import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.CreateNewAudioEquipmentRequest; import com.ajou.hertz.domain.instrument.bass_guitar.constant.BassGuitarBrand; import com.ajou.hertz.domain.instrument.bass_guitar.constant.BassGuitarPickUp; @@ -833,6 +834,45 @@ public InstrumentControllerTest(MockMvc mvc) { verifyEveryMocksShouldHaveNoMoreInteractions(); } + @Test + void 수정할_음향장비_정보가_주어지고_주어진_정보로_매물_정보를_수정한다() throws Exception { + // given + long userId = 1L; + long audioEquipmentsId = 2L; + AudioEquipmentUpdateRequest updateRequest = createAudioEquipmentUpdateRequest(); + AudioEquipmentDto expectedResult = createAudioEquipmentDto(audioEquipmentsId, userId); + given(instrumentCommandService.updateAudioEquipment( + eq(userId), + eq(audioEquipmentsId), + any(AudioEquipmentUpdateRequest.class) + )).willReturn(expectedResult); + + // when & then + mvc.perform( + multipart("/api/instruments/audio-equipments/{audioEquipmentsId}", audioEquipmentsId) + .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())) + .with(user(createTestUser(userId))) + .with(request -> { + request.setMethod("PATCH"); + return request; + }) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(expectedResult.getId())); + then(instrumentCommandService) + .should() + .updateAudioEquipment(eq(userId), eq(audioEquipmentsId), + any(AudioEquipmentUpdateRequest.class)); + verifyEveryMocksShouldHaveNoMoreInteractions(); + } + @Test void 악기_id가_주어지고_해당하는_악기_매물을_삭제한다() throws Exception { // given @@ -1232,4 +1272,21 @@ private EffectorUpdateRequest createEffectorUpdateRequest() throws Exception { ); } + private AudioEquipmentUpdateRequest createAudioEquipmentUpdateRequest() throws Exception { + return ReflectionUtils.createAudioEquipmentUpdateRequest( + "Test AudioEquipment", + InstrumentProgressStatus.SOLD_OUT, + createAddressRequest(), + (short)3, + 550000, + true, + "description", + null, + null, + null, + null, + AudioEquipmentType.AUDIO_EQUIPMENT + ); + } + } 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 c4be6da..94c3da3 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 @@ -39,6 +39,7 @@ import com.ajou.hertz.domain.instrument.amplifier.entity.Amplifier; import com.ajou.hertz.domain.instrument.audio_equipment.constant.AudioEquipmentType; import com.ajou.hertz.domain.instrument.audio_equipment.dto.AudioEquipmentDto; +import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentUpdateRequest; import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.CreateNewAudioEquipmentRequest; import com.ajou.hertz.domain.instrument.audio_equipment.entity.AudioEquipment; import com.ajou.hertz.domain.instrument.bass_guitar.constant.BassGuitarBrand; @@ -456,6 +457,36 @@ class InstrumentCommandServiceTest { assertThat(result.getImages()).hasSize(newImages.size()); } + @Test + void 추가할_이미지들이_주어지고_음향장비_정보를_수정하면_새로운_악기_이미지들이_추가된다() throws Exception { + // given + long userId = 1L; + long instrumentId = 2L; + AudioEquipment audioEquipment = createAudioEquipment(instrumentId, createUser(userId)); + List newImages = List.of( + createMultipartFile(), + createMultipartFile() + ); + List newInstrumentImages = List.of( + createInstrumentImage(3L, audioEquipment), + createInstrumentImage(4L, audioEquipment) + ); + AudioEquipmentUpdateRequest updateRequest = createAudioEquipmentUpdateRequest(List.of(), newImages, null, null); + given(instrumentQueryService.getInstrumentById(instrumentId)) + .willReturn(audioEquipment); + given(instrumentImageCommandService.saveImages(audioEquipment, updateRequest.getNewImages())) + .willReturn(newInstrumentImages); + + // when + AudioEquipmentDto result = sut.updateAudioEquipment(userId, instrumentId, updateRequest); + + // then + then(instrumentQueryService).should().getInstrumentById(instrumentId); + then(instrumentImageCommandService).should().saveImages(audioEquipment, updateRequest.getNewImages()); + verifyEveryMocksShouldHaveNoMoreInteractions(); + assertThat(result.getImages()).hasSize(newImages.size()); + } + @Test void 삭제할_해시태그들의_id_리스트가_주어지고_매물_정보를_수정하면_해시태그가_삭제된다() throws Exception { long userId = 1L; @@ -880,4 +911,17 @@ private EffectorUpdateRequest createEffectorUpdateRequest( null, null ); } + + private AudioEquipmentUpdateRequest createAudioEquipmentUpdateRequest( + @Nullable List deletedImageIds, + @Nullable List newImages, + @Nullable List deletedHashtagIds, + @Nullable List newHashtags + ) throws Exception { + return ReflectionUtils.createAudioEquipmentUpdateRequest( + null, null, null, null, null, null, null, + deletedImageIds, newImages, deletedHashtagIds, newHashtags, + null + ); + } } diff --git a/src/test/java/com/ajou/hertz/util/ReflectionUtils.java b/src/test/java/com/ajou/hertz/util/ReflectionUtils.java index 1b7cecc..f5f9584 100644 --- a/src/test/java/com/ajou/hertz/util/ReflectionUtils.java +++ b/src/test/java/com/ajou/hertz/util/ReflectionUtils.java @@ -47,6 +47,7 @@ import com.ajou.hertz.domain.instrument.audio_equipment.constant.AudioEquipmentType; import com.ajou.hertz.domain.instrument.audio_equipment.dto.AudioEquipmentDto; import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentFilterConditions; +import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.AudioEquipmentUpdateRequest; import com.ajou.hertz.domain.instrument.audio_equipment.dto.request.CreateNewAudioEquipmentRequest; import com.ajou.hertz.domain.instrument.audio_equipment.entity.AudioEquipment; import com.ajou.hertz.domain.instrument.bass_guitar.constant.BassGuitarBrand; @@ -1139,6 +1140,42 @@ public static EffectorUpdateRequest createEffectorUpdateRequest( ); } + public static AudioEquipmentUpdateRequest createAudioEquipmentUpdateRequest( + @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 AudioEquipmentType type + ) throws Exception { + Constructor constructor = AudioEquipmentUpdateRequest.class.getDeclaredConstructor( + String.class, InstrumentProgressStatus.class, AddressRequest.class, Short.class, + Integer.class, Boolean.class, String.class, List.class, List.class, List.class, List.class, + AudioEquipmentType.class + ); + constructor.setAccessible(true); + return constructor.newInstance( + title, + progressStatus, + tradeAddress, + qualityStatus, + price, + hasAnomaly, + description, + deletedImageIds, + newImages, + deletedHashtagIds, + newHashtags, + type + ); + } + public static SendUserAuthCodeRequest createSendUserAuthCodeRequest(String phoneNumber) throws Exception { Constructor constructor = SendUserAuthCodeRequest.class.getDeclaredConstructor(String.class);