From 04b44b380dbe90235d0c50d2198590812d34f304 Mon Sep 17 00:00:00 2001 From: Wo-ogie Date: Tue, 27 Feb 2024 02:47:22 +0900 Subject: [PATCH 1/6] =?UTF-8?q?refactor:=20#50=20=EC=95=85=EA=B8=B0=20enti?= =?UTF-8?q?ty=EC=97=90=20=EC=96=91=EB=B0=A9=ED=96=A5=20=EC=97=B0=EA=B4=80?= =?UTF-8?q?=EA=B4=80=EA=B3=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/instrument/entity/Instrument.java | 30 ++++++++++++++++-- .../instrument/entity/InstrumentHashtags.java | 29 +++++++++++++++++ .../instrument/entity/InstrumentImages.java | 31 +++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentHashtags.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentImages.java diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/Instrument.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/Instrument.java index 4f98e4b..cd8c569 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/Instrument.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/Instrument.java @@ -20,11 +20,9 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -@AllArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @DiscriminatorColumn(name = "category") @@ -62,4 +60,32 @@ public abstract class Instrument extends TimeTrackedBaseEntity { @Column(length = 1000, nullable = false) private String description; + + @Embedded + private InstrumentImages images = new InstrumentImages(); + + @Embedded + private InstrumentHashtags hashtags = new InstrumentHashtags(); + + protected Instrument( + Long id, + User seller, + String title, + InstrumentProgressStatus progressStatus, + Address tradeAddress, + Short qualityStatus, + Integer price, + Boolean hasAnomaly, + String description + ) { + this.id = id; + this.seller = seller; + this.title = title; + this.progressStatus = progressStatus; + this.tradeAddress = tradeAddress; + this.qualityStatus = qualityStatus; + this.price = price; + this.hasAnomaly = hasAnomaly; + this.description = description; + } } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentHashtags.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentHashtags.java new file mode 100644 index 0000000..ec00fa4 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentHashtags.java @@ -0,0 +1,29 @@ +package com.ajou.hertz.domain.instrument.entity; + +import java.util.LinkedList; +import java.util.List; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.OneToMany; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Embeddable +public class InstrumentHashtags { + + @OneToMany(mappedBy = "instrument") + private List content = new LinkedList<>(); + + public void addAll(List hashtags) { + content.addAll(hashtags); + } + + public List toStrings() { + return content.stream() + .map(InstrumentHashtag::getContent) + .toList(); + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentImages.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentImages.java new file mode 100644 index 0000000..0403cfc --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentImages.java @@ -0,0 +1,31 @@ +package com.ajou.hertz.domain.instrument.entity; + +import java.util.LinkedList; +import java.util.List; + +import com.ajou.hertz.domain.instrument.dto.InstrumentImageDto; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.OneToMany; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Embeddable +public class InstrumentImages { + + @OneToMany(mappedBy = "instrument") + private List content = new LinkedList<>(); + + public void addAll(List images) { + content.addAll(images); + } + + public List toDtos() { + return content.stream() + .map(InstrumentImageDto::from) + .toList(); + } +} From c7443914649f2fae16ef58769e462c0d2769e576 Mon Sep 17 00:00:00 2001 From: Wo-ogie Date: Tue, 27 Feb 2024 02:49:47 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor:=20#50=20=EC=95=85=EA=B8=B0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20entity=EB=93=A4=EC=9D=98=20static=20create?= =?UTF-8?q?=20method=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 매물 등록 시 사용자가 매물의 판매 진행 상태를 설정할 수 있다는 것을 뒤늦게 알고 수정하였음. --- .../domain/instrument/entity/AcousticAndClassGuitar.java | 5 +++-- .../com/ajou/hertz/domain/instrument/entity/Amplifier.java | 5 +++-- .../ajou/hertz/domain/instrument/entity/AudioEquipment.java | 3 ++- .../com/ajou/hertz/domain/instrument/entity/BassGuitar.java | 5 +++-- .../com/ajou/hertz/domain/instrument/entity/Effector.java | 5 +++-- .../ajou/hertz/domain/instrument/entity/ElectricGuitar.java | 5 +++-- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/AcousticAndClassGuitar.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/AcousticAndClassGuitar.java index aaab2b3..7703d48 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/AcousticAndClassGuitar.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/AcousticAndClassGuitar.java @@ -64,6 +64,7 @@ private AcousticAndClassGuitar( public static AcousticAndClassGuitar create( User seller, String title, + InstrumentProgressStatus progressStatus, Address tradeAddress, Short qualityStatus, Integer price, @@ -75,8 +76,8 @@ public static AcousticAndClassGuitar create( AcousticAndClassGuitarPickUp pickUp ) { return new AcousticAndClassGuitar( - null, seller, title, InstrumentProgressStatus.SELLING, tradeAddress, - qualityStatus, price, hasAnomaly, description, brand, model, wood, pickUp + null, seller, title, progressStatus, tradeAddress, qualityStatus, + price, hasAnomaly, description, brand, model, wood, pickUp ); } } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/Amplifier.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/Amplifier.java index ebaf785..6dfb204 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/Amplifier.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/Amplifier.java @@ -57,6 +57,7 @@ private Amplifier( public static Amplifier create( User seller, String title, + InstrumentProgressStatus progressStatus, Address tradeAddress, Short qualityStatus, Integer price, @@ -67,8 +68,8 @@ public static Amplifier create( AmplifierUsage usage ) { return new Amplifier( - null, seller, title, InstrumentProgressStatus.SELLING, tradeAddress, - qualityStatus, price, hasAnomaly, description, type, brand, usage + null, seller, title, progressStatus, tradeAddress, qualityStatus, + price, hasAnomaly, description, type, brand, usage ); } } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/AudioEquipment.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/AudioEquipment.java index 368e2bb..3420bad 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/AudioEquipment.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/AudioEquipment.java @@ -43,6 +43,7 @@ private AudioEquipment( public static AudioEquipment create( User seller, String title, + InstrumentProgressStatus progressStatus, Address tradeAddress, Short qualityStatus, Integer price, @@ -51,7 +52,7 @@ public static AudioEquipment create( AudioEquipmentType type ) { return new AudioEquipment( - null, seller, title, InstrumentProgressStatus.SELLING, tradeAddress, + null, seller, title, progressStatus, tradeAddress, qualityStatus, price, hasAnomaly, description, type ); } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/BassGuitar.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/BassGuitar.java index b0207d7..17004a8 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/BassGuitar.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/BassGuitar.java @@ -64,6 +64,7 @@ private BassGuitar( public static BassGuitar create( User seller, String title, + InstrumentProgressStatus progressStatus, Address tradeAddress, Short qualityStatus, Integer price, @@ -75,8 +76,8 @@ public static BassGuitar create( GuitarColor color ) { return new BassGuitar( - null, seller, title, InstrumentProgressStatus.SELLING, tradeAddress, - qualityStatus, price, hasAnomaly, description, brand, pickUp, preAmplifier, color + null, seller, title, progressStatus, tradeAddress, qualityStatus, + price, hasAnomaly, description, brand, pickUp, preAmplifier, color ); } } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/Effector.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/Effector.java index d0915c1..d3136c0 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/Effector.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/Effector.java @@ -50,6 +50,7 @@ private Effector( public static Effector create( User seller, String title, + InstrumentProgressStatus progressStatus, Address tradeAddress, Short qualityStatus, Integer price, @@ -59,8 +60,8 @@ public static Effector create( EffectorFeature feature ) { return new Effector( - null, seller, title, InstrumentProgressStatus.SELLING, tradeAddress, - qualityStatus, price, hasAnomaly, description, type, feature + null, seller, title, progressStatus, tradeAddress, qualityStatus, + price, hasAnomaly, description, type, feature ); } } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/ElectricGuitar.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/ElectricGuitar.java index 0fe47af..c7fad3d 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/ElectricGuitar.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/ElectricGuitar.java @@ -62,6 +62,7 @@ private ElectricGuitar( public static ElectricGuitar create( User seller, String title, + InstrumentProgressStatus progressStatus, Address tradeAddress, Short qualityStatus, Integer price, @@ -73,8 +74,8 @@ public static ElectricGuitar create( GuitarColor color ) { return new ElectricGuitar( - null, seller, title, InstrumentProgressStatus.SELLING, tradeAddress, - qualityStatus, price, hasAnomaly, description, brand, model, productionYear, color + null, seller, title, progressStatus, tradeAddress, qualityStatus, + price, hasAnomaly, description, brand, model, productionYear, color ); } } From 1835641a5fce0ec6e7506555e5d123657f6e5b9e Mon Sep 17 00:00:00 2001 From: Wo-ogie Date: Tue, 27 Feb 2024 02:50:25 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20#50=20=EC=95=85=EA=B8=B0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20entity=EC=97=90=20=EB=8C=80=ED=95=9C=20rep?= =?UTF-8?q?ository=20layer=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/InstrumentHashtagRepository.java | 8 ++++++++ .../instrument/repository/InstrumentImageRepository.java | 8 ++++++++ .../instrument/repository/InstrumentRepository.java | 8 ++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentHashtagRepository.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentImageRepository.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentRepository.java diff --git a/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentHashtagRepository.java b/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentHashtagRepository.java new file mode 100644 index 0000000..4370c42 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentHashtagRepository.java @@ -0,0 +1,8 @@ +package com.ajou.hertz.domain.instrument.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.ajou.hertz.domain.instrument.entity.InstrumentHashtag; + +public interface InstrumentHashtagRepository extends JpaRepository { +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentImageRepository.java b/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentImageRepository.java new file mode 100644 index 0000000..6880280 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentImageRepository.java @@ -0,0 +1,8 @@ +package com.ajou.hertz.domain.instrument.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.ajou.hertz.domain.instrument.entity.InstrumentImage; + +public interface InstrumentImageRepository extends JpaRepository { +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentRepository.java b/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentRepository.java new file mode 100644 index 0000000..e9ecb88 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentRepository.java @@ -0,0 +1,8 @@ +package com.ajou.hertz.domain.instrument.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.ajou.hertz.domain.instrument.entity.Instrument; + +public interface InstrumentRepository extends JpaRepository { +} From 7c96ebee56f2f0b33c9fec173df6cb870cf46c36 Mon Sep 17 00:00:00 2001 From: Wo-ogie Date: Tue, 27 Feb 2024 03:52:19 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20#50=20=EC=9D=BC=EB=A0=89=EA=B8=B0?= =?UTF-8?q?=ED=83=80=20=EB=A7=A4=EB=AC=BC=20=EC=83=9D=EC=84=B1=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ajou/hertz/common/dto/AddressDto.java | 22 ++ .../common/dto/request/AddressRequest.java | 34 +++ .../common/dto/response/AddressResponse.java | 28 +++ .../com/ajou/hertz/common/entity/Address.java | 2 +- .../controller/InstrumentControllerV1.java | 59 +++++ .../instrument/dto/ElectricGuitarDto.java | 58 +++++ .../instrument/dto/InstrumentImageDto.java | 26 ++ .../CreateNewElectricGuitarRequest.java | 100 ++++++++ .../dto/response/ElectricGuitarResponse.java | 90 +++++++ .../dto/response/InstrumentImageResponse.java | 32 +++ .../instrument/entity/InstrumentHashtag.java | 4 + .../instrument/entity/InstrumentImage.java | 18 +- .../service/InstrumentCommandService.java | 67 ++++++ .../domain/user/service/UserQueryService.java | 2 +- .../InstrumentControllerV1Test.java | 206 ++++++++++++++++ .../service/InstrumentCommandServiceTest.java | 227 ++++++++++++++++++ 16 files changed, 971 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/ajou/hertz/common/dto/AddressDto.java create mode 100644 src/main/java/com/ajou/hertz/common/dto/request/AddressRequest.java create mode 100644 src/main/java/com/ajou/hertz/common/dto/response/AddressResponse.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentControllerV1.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/dto/ElectricGuitarDto.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/dto/InstrumentImageDto.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/dto/request/CreateNewElectricGuitarRequest.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/dto/response/ElectricGuitarResponse.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/dto/response/InstrumentImageResponse.java create mode 100644 src/main/java/com/ajou/hertz/domain/instrument/service/InstrumentCommandService.java create mode 100644 src/test/java/com/ajou/hertz/unit/domain/instrument/controller/InstrumentControllerV1Test.java create mode 100644 src/test/java/com/ajou/hertz/unit/domain/instrument/service/InstrumentCommandServiceTest.java diff --git a/src/main/java/com/ajou/hertz/common/dto/AddressDto.java b/src/main/java/com/ajou/hertz/common/dto/AddressDto.java new file mode 100644 index 0000000..a332842 --- /dev/null +++ b/src/main/java/com/ajou/hertz/common/dto/AddressDto.java @@ -0,0 +1,22 @@ +package com.ajou.hertz.common.dto; + +import com.ajou.hertz.common.entity.Address; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class AddressDto { + + private String sido; + private String sgg; + private String emd; + + public static AddressDto from(Address address) { + return new AddressDto(address.getSido(), address.getSgg(), address.getEmd()); + } +} diff --git a/src/main/java/com/ajou/hertz/common/dto/request/AddressRequest.java b/src/main/java/com/ajou/hertz/common/dto/request/AddressRequest.java new file mode 100644 index 0000000..8deb815 --- /dev/null +++ b/src/main/java/com/ajou/hertz/common/dto/request/AddressRequest.java @@ -0,0 +1,34 @@ +package com.ajou.hertz.common.dto.request; + +import com.ajou.hertz.common.entity.Address; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Setter +@Getter +public class AddressRequest { + + @Schema(description = "시/도", example = "서울특별시") + @NotBlank + private String sido; + + @Schema(description = "시/군/구", example = "강남구") + @NotBlank + private String sgg; + + @Schema(description = "읍/면/동", example = "청담동") + @NotBlank + private String emd; + + public Address toEntity() { + return new Address(sido, sgg, emd); + } +} diff --git a/src/main/java/com/ajou/hertz/common/dto/response/AddressResponse.java b/src/main/java/com/ajou/hertz/common/dto/response/AddressResponse.java new file mode 100644 index 0000000..802af5b --- /dev/null +++ b/src/main/java/com/ajou/hertz/common/dto/response/AddressResponse.java @@ -0,0 +1,28 @@ +package com.ajou.hertz.common.dto.response; + +import com.ajou.hertz.common.dto.AddressDto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class AddressResponse { + + @Schema(description = "시/도", example = "서울특별시") + private String sido; + + @Schema(description = "시/군/구", example = "강남구") + private String sgg; + + @Schema(description = "읍/면/동", example = "청담동") + private String emd; + + public static AddressResponse from(AddressDto addressDto) { + return new AddressResponse(addressDto.getSido(), addressDto.getSgg(), addressDto.getEmd()); + } +} diff --git a/src/main/java/com/ajou/hertz/common/entity/Address.java b/src/main/java/com/ajou/hertz/common/entity/Address.java index 0a8f2a5..3dd515d 100644 --- a/src/main/java/com/ajou/hertz/common/entity/Address.java +++ b/src/main/java/com/ajou/hertz/common/entity/Address.java @@ -9,7 +9,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; -@AllArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Embeddable diff --git a/src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentControllerV1.java b/src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentControllerV1.java new file mode 100644 index 0000000..53afdfe --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentControllerV1.java @@ -0,0 +1,59 @@ +package com.ajou.hertz.domain.instrument.controller; + +import static com.ajou.hertz.common.constant.GlobalConstants.*; + +import java.net.URI; + +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ajou.hertz.common.auth.UserPrincipal; +import com.ajou.hertz.domain.instrument.dto.ElectricGuitarDto; +import com.ajou.hertz.domain.instrument.dto.request.CreateNewElectricGuitarRequest; +import com.ajou.hertz.domain.instrument.dto.response.ElectricGuitarResponse; +import com.ajou.hertz.domain.instrument.service.InstrumentCommandService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@Tag(name = "중고 악기 관련 API") +@RequiredArgsConstructor +@RequestMapping("/v1/instruments") +@RestController +public class InstrumentControllerV1 { + + private final InstrumentCommandService instrumentCommandService; + + @Operation( + summary = "일렉기타 매물 등록", + description = """ +

일렉기타 매물을 등록합니다. +

요청 시 multipart/form-data content-type으로 요쳥해야 합니다. + """ + ) + @PostMapping( + value = "/electric-guitars", + headers = API_MINOR_VERSION_HEADER_NAME + "=" + 1, + consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity createNewInstrumentV1_1( + @AuthenticationPrincipal UserPrincipal userPrincipal, + @ParameterObject @ModelAttribute @Valid CreateNewElectricGuitarRequest createNewElectricGuitarRequest + ) { + ElectricGuitarDto electricGuitar = instrumentCommandService.createNewElectricGuitar( + userPrincipal.getUserId(), + createNewElectricGuitarRequest + ); + return ResponseEntity + .created(URI.create("/instruments/" + electricGuitar.getId())) + .body(ElectricGuitarResponse.from(electricGuitar)); + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/dto/ElectricGuitarDto.java b/src/main/java/com/ajou/hertz/domain/instrument/dto/ElectricGuitarDto.java new file mode 100644 index 0000000..542f656 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/dto/ElectricGuitarDto.java @@ -0,0 +1,58 @@ +package com.ajou.hertz.domain.instrument.dto; + +import java.util.List; + +import com.ajou.hertz.common.dto.AddressDto; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarBrand; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarModel; +import com.ajou.hertz.domain.instrument.constant.GuitarColor; +import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.entity.ElectricGuitar; +import com.ajou.hertz.domain.user.dto.UserDto; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class ElectricGuitarDto { + + private Long id; + private UserDto seller; + private String title; + private InstrumentProgressStatus progressStatus; + private AddressDto tradeAddress; + private Short qualityStatus; + private Integer price; + private Boolean hasAnomaly; + private String description; + private ElectricGuitarBrand brand; + private ElectricGuitarModel model; + private Short productionYear; + private GuitarColor color; + private List images; + private List hashtags; + + public static ElectricGuitarDto from(ElectricGuitar electricGuitar) { + return new ElectricGuitarDto( + electricGuitar.getId(), + UserDto.from(electricGuitar.getSeller()), + electricGuitar.getTitle(), + electricGuitar.getProgressStatus(), + AddressDto.from(electricGuitar.getTradeAddress()), + electricGuitar.getQualityStatus(), + electricGuitar.getPrice(), + electricGuitar.getHasAnomaly(), + electricGuitar.getDescription(), + electricGuitar.getBrand(), + electricGuitar.getModel(), + electricGuitar.getProductionYear(), + electricGuitar.getColor(), + electricGuitar.getImages().toDtos(), + electricGuitar.getHashtags().toStrings() + ); + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/dto/InstrumentImageDto.java b/src/main/java/com/ajou/hertz/domain/instrument/dto/InstrumentImageDto.java new file mode 100644 index 0000000..5543f97 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/dto/InstrumentImageDto.java @@ -0,0 +1,26 @@ +package com.ajou.hertz.domain.instrument.dto; + +import com.ajou.hertz.domain.instrument.entity.InstrumentImage; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class InstrumentImageDto { + + private Long id; + private String name; + private String url; + + public static InstrumentImageDto from(InstrumentImage instrumentImage) { + return new InstrumentImageDto( + instrumentImage.getId(), + instrumentImage.getOriginalName(), + instrumentImage.getUrl() + ); + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/dto/request/CreateNewElectricGuitarRequest.java b/src/main/java/com/ajou/hertz/domain/instrument/dto/request/CreateNewElectricGuitarRequest.java new file mode 100644 index 0000000..d440afc --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/dto/request/CreateNewElectricGuitarRequest.java @@ -0,0 +1,100 @@ +package com.ajou.hertz.domain.instrument.dto.request; + +import java.util.List; + +import org.hibernate.validator.constraints.Length; +import org.springframework.web.multipart.MultipartFile; + +import com.ajou.hertz.common.dto.request.AddressRequest; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarBrand; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarModel; +import com.ajou.hertz.domain.instrument.constant.GuitarColor; +import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.entity.ElectricGuitar; +import com.ajou.hertz.domain.user.entity.User; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Setter // for multipart/form-data with @ModelAttribute +@Getter +public class CreateNewElectricGuitarRequest { + + @Schema(description = "제목", example = "펜더 로드원 텔레캐스터") + @NotBlank + private String title; + + @Schema(description = "악기 사진") + @NotNull + @Size(min = 4, max = 7) + private List images; + + @NotNull + private InstrumentProgressStatus progressStatus; + + @Schema(description = "거래 장소") + @NotNull + private AddressRequest tradeAddress; + + @Schema(description = "악기 상태. 1~5 단계로 구성됩니다.", example = "3") + @NotNull + @Min(1) + @Max(5) + private Short qualityStatus; + + @Schema(description = "가격", example = "527000") + @NotNull + private Integer price; + + @Schema(description = "특이사항 유무", example = "true") + @NotNull + private Boolean hasAnomaly; + + @Schema(description = "특이사항 및 상세 설명. 내용이 없을 경우에는 빈 문자열로 요청하면 됩니다.", example = "14년 시리얼 펜더 로드원 50 텔레입니다. 기존 ...") + @NotNull + private String description; + + @NotNull + private ElectricGuitarBrand brand; + + @NotNull + private ElectricGuitarModel model; + + @Schema(description = "생상연도", example = "2014") + @NotNull + private Short productionYear; + + @NotNull + private GuitarColor color; + + @Schema(description = "해시태그(각 해시태그마다 최대 10글자)", example = "[\"펜더\", \"Fender\"]") + private List<@NotBlank @Length(max = 10) String> hashtags; + + public ElectricGuitar toEntity(User seller) { + return ElectricGuitar.create( + seller, + title, + progressStatus, + tradeAddress.toEntity(), + qualityStatus, + price, + hasAnomaly, + description, + brand, + model, + productionYear, + color + ); + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/dto/response/ElectricGuitarResponse.java b/src/main/java/com/ajou/hertz/domain/instrument/dto/response/ElectricGuitarResponse.java new file mode 100644 index 0000000..a776aa7 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/dto/response/ElectricGuitarResponse.java @@ -0,0 +1,90 @@ +package com.ajou.hertz.domain.instrument.dto.response; + +import java.util.List; + +import com.ajou.hertz.common.dto.response.AddressResponse; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarBrand; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarModel; +import com.ajou.hertz.domain.instrument.constant.GuitarColor; +import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.dto.ElectricGuitarDto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class ElectricGuitarResponse { + + @Schema(description = "Id of instrument(electric guitar)", example = "2") + private Long id; + + @Schema(description = "Id of seller", example = "1") + private Long sellerId; + + @Schema(description = "제목", example = "펜더 로드원 텔레캐스터") + private String title; + + @Schema(description = "매물 진행 상태") + private InstrumentProgressStatus progressStatus; + + @Schema(description = "거래 장소") + private AddressResponse tradeAddress; + + @Schema(description = "악기 상태. 1~5의 값을 갖습니다.", example = "3") + private Short qualityStatus; + + @Schema(description = "가격", example = "527000") + private Integer price; + + @Schema(description = "특이사항 유무", example = "true") + private Boolean hasAnomaly; + + @Schema(description = "특이사항 및 상세 설명. 내용이 없을 경우에는 빈 문자열로 요청하면 됩니다.", example = "14년 시리얼 펜더 로드원 50 텔레입니다. 기존 ...") + private String description; + + @Schema(description = "브랜드") + private ElectricGuitarBrand brand; + + @Schema(description = "모델") + private ElectricGuitarModel model; + + @Schema(description = "생상연도", example = "2014") + private Short productionYear; + + @Schema(description = "색상") + private GuitarColor color; + + @Schema(description = "악기 이미지") + private List images; + + @Schema(description = "해시태그", example = "[\"펜더\", \"Fender\"]") + private List hashtags; + + public static ElectricGuitarResponse from(ElectricGuitarDto electricGuitarDto) { + return new ElectricGuitarResponse( + electricGuitarDto.getId(), + electricGuitarDto.getSeller().getId(), + electricGuitarDto.getTitle(), + electricGuitarDto.getProgressStatus(), + AddressResponse.from(electricGuitarDto.getTradeAddress()), + electricGuitarDto.getQualityStatus(), + electricGuitarDto.getPrice(), + electricGuitarDto.getHasAnomaly(), + electricGuitarDto.getDescription(), + electricGuitarDto.getBrand(), + electricGuitarDto.getModel(), + electricGuitarDto.getProductionYear(), + electricGuitarDto.getColor(), + electricGuitarDto.getImages() + .stream() + .map(InstrumentImageResponse::from) + .toList(), + electricGuitarDto.getHashtags() + ); + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/dto/response/InstrumentImageResponse.java b/src/main/java/com/ajou/hertz/domain/instrument/dto/response/InstrumentImageResponse.java new file mode 100644 index 0000000..8c7172d --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/dto/response/InstrumentImageResponse.java @@ -0,0 +1,32 @@ +package com.ajou.hertz.domain.instrument.dto.response; + +import com.ajou.hertz.domain.instrument.dto.InstrumentImageDto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class InstrumentImageResponse { + + @Schema(description = "Id of instrument image", example = "3") + private Long id; + + @Schema(description = "이미지 이름", example = "fender-guitar.jpg") + private String name; + + @Schema(description = "이미지 url", example = "https://instrument-image-url") + private String url; + + public static InstrumentImageResponse from(InstrumentImageDto instrumentImageDto) { + return new InstrumentImageResponse( + instrumentImageDto.getId(), + instrumentImageDto.getName(), + instrumentImageDto.getUrl() + ); + } +} diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentHashtag.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentHashtag.java index 1c9bcc8..fe81fd9 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentHashtag.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentHashtag.java @@ -32,4 +32,8 @@ public class InstrumentHashtag extends BaseEntity { @Column(length = 10, nullable = false) private String content; + + public static InstrumentHashtag create(Instrument instrument, String content) { + return new InstrumentHashtag(null, instrument, content); + } } diff --git a/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentImage.java b/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentImage.java index a90a75f..825ed2c 100644 --- a/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentImage.java +++ b/src/main/java/com/ajou/hertz/domain/instrument/entity/InstrumentImage.java @@ -11,11 +11,9 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -@AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Entity @@ -29,4 +27,20 @@ public class InstrumentImage extends FileEntity { @JoinColumn(name = "instrument_id", nullable = false) @ManyToOne(fetch = FetchType.LAZY) private Instrument instrument; + + private InstrumentImage(Long id, Instrument instrument, String originalName, String storedName, String url) { + super(originalName, storedName, url); + this.id = id; + this.instrument = instrument; + } + + public static InstrumentImage create(Instrument instrument, String originalName, String storedName, String url) { + return new InstrumentImage( + null, + instrument, + originalName, + storedName, + url + ); + } } 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 new file mode 100644 index 0000000..ca74785 --- /dev/null +++ b/src/main/java/com/ajou/hertz/domain/instrument/service/InstrumentCommandService.java @@ -0,0 +1,67 @@ +package com.ajou.hertz.domain.instrument.service; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ajou.hertz.common.file.service.FileService; +import com.ajou.hertz.domain.instrument.dto.ElectricGuitarDto; +import com.ajou.hertz.domain.instrument.dto.request.CreateNewElectricGuitarRequest; +import com.ajou.hertz.domain.instrument.entity.ElectricGuitar; +import com.ajou.hertz.domain.instrument.entity.InstrumentHashtag; +import com.ajou.hertz.domain.instrument.entity.InstrumentImage; +import com.ajou.hertz.domain.instrument.repository.InstrumentHashtagRepository; +import com.ajou.hertz.domain.instrument.repository.InstrumentImageRepository; +import com.ajou.hertz.domain.instrument.repository.InstrumentRepository; +import com.ajou.hertz.domain.user.entity.User; +import com.ajou.hertz.domain.user.service.UserQueryService; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Transactional +@Service +public class InstrumentCommandService { + + private static final String INSTRUMENT_IMAGE_UPLOAD_PATH = "instrument-image/"; + + private final UserQueryService userQueryService; + private final FileService fileService; + private final InstrumentRepository instrumentRepository; + private final InstrumentHashtagRepository instrumentHashtagRepository; + private final InstrumentImageRepository instrumentImageRepository; + + public ElectricGuitarDto createNewElectricGuitar( + Long sellerId, + CreateNewElectricGuitarRequest createNewElectricGuitarRequest + ) { + // 악기(Instrument) 매물 등록 + User seller = userQueryService.getById(sellerId); + ElectricGuitar electricGuitar = instrumentRepository.save(createNewElectricGuitarRequest.toEntity(seller)); + + // 악기 이미지 등록 + List instrumentImages = fileService + .uploadFiles(createNewElectricGuitarRequest.getImages(), INSTRUMENT_IMAGE_UPLOAD_PATH) + .stream() + .map(fileDto -> InstrumentImage.create( + electricGuitar, + fileDto.getOriginalName(), + fileDto.getStoredName(), + fileDto.getUrl() + )).toList(); + List savedInstrumentImages = instrumentImageRepository.saveAll(instrumentImages); + electricGuitar.getImages().addAll(savedInstrumentImages); + + // 악기 해시태그 등록 + List hashtags = createNewElectricGuitarRequest + .getHashtags() + .stream() + .map(hashtagContent -> InstrumentHashtag.create(electricGuitar, hashtagContent)) + .toList(); + List savedInstrumentHashtags = instrumentHashtagRepository.saveAll(hashtags); + electricGuitar.getHashtags().addAll(savedInstrumentHashtags); + + return ElectricGuitarDto.from(electricGuitar); + } +} diff --git a/src/main/java/com/ajou/hertz/domain/user/service/UserQueryService.java b/src/main/java/com/ajou/hertz/domain/user/service/UserQueryService.java index a512eaf..9186d82 100644 --- a/src/main/java/com/ajou/hertz/domain/user/service/UserQueryService.java +++ b/src/main/java/com/ajou/hertz/domain/user/service/UserQueryService.java @@ -28,7 +28,7 @@ public class UserQueryService { * @return 조회한 user entity * @throws UserNotFoundByIdException 일치하는 유저를 찾지 못한 경우 */ - private User getById(Long id) { + public User getById(Long id) { return userRepository.findById(id).orElseThrow(() -> new UserNotFoundByIdException(id)); } diff --git a/src/test/java/com/ajou/hertz/unit/domain/instrument/controller/InstrumentControllerV1Test.java b/src/test/java/com/ajou/hertz/unit/domain/instrument/controller/InstrumentControllerV1Test.java new file mode 100644 index 0000000..43b48a5 --- /dev/null +++ b/src/test/java/com/ajou/hertz/unit/domain/instrument/controller/InstrumentControllerV1Test.java @@ -0,0 +1,206 @@ +package com.ajou.hertz.unit.domain.instrument.controller; + +import static com.ajou.hertz.common.constant.GlobalConstants.*; +import static org.mockito.BDDMockito.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.lang.reflect.Constructor; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.test.web.servlet.MockMvc; + +import com.ajou.hertz.common.auth.UserPrincipal; +import com.ajou.hertz.common.dto.AddressDto; +import com.ajou.hertz.common.dto.request.AddressRequest; +import com.ajou.hertz.config.ControllerTestConfig; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarBrand; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarModel; +import com.ajou.hertz.domain.instrument.constant.GuitarColor; +import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.controller.InstrumentControllerV1; +import com.ajou.hertz.domain.instrument.dto.ElectricGuitarDto; +import com.ajou.hertz.domain.instrument.dto.request.CreateNewElectricGuitarRequest; +import com.ajou.hertz.domain.instrument.service.InstrumentCommandService; +import com.ajou.hertz.domain.user.constant.Gender; +import com.ajou.hertz.domain.user.constant.RoleType; +import com.ajou.hertz.domain.user.dto.UserDto; + +@DisplayName("[Unit] Controller - Instrument(V1)") +@Import(ControllerTestConfig.class) +@WebMvcTest(controllers = InstrumentControllerV1.class) +class InstrumentControllerV1Test { + + @MockBean + private InstrumentCommandService instrumentCommandService; + + private final MockMvc mvc; + + @Autowired + public InstrumentControllerV1Test(MockMvc mvc) { + this.mvc = mvc; + } + + @Test + void 새로_등록할_일렉기타의_정보가_주어지면_일렉기타_매물을_등록한다() throws Exception { + // given + long sellerId = 1L; + CreateNewElectricGuitarRequest electricGuitarRequest = createElectricGuitarRequest(); + ElectricGuitarDto expectedResult = createElectricGuitarDto(2L, sellerId); + given(instrumentCommandService.createNewElectricGuitar( + eq(sellerId), any(CreateNewElectricGuitarRequest.class) + )).willReturn(expectedResult); + + // when & then + mvc.perform( + multipart("/v1/instruments/electric-guitars") + .file("images[0]", electricGuitarRequest.getImages().get(0).getBytes()) + .file("images[1]", electricGuitarRequest.getImages().get(1).getBytes()) + .file("images[2]", electricGuitarRequest.getImages().get(2).getBytes()) + .file("images[3]", electricGuitarRequest.getImages().get(3).getBytes()) + .header(API_MINOR_VERSION_HEADER_NAME, 1) + .param("title", electricGuitarRequest.getTitle()) + .param("progressStatus", electricGuitarRequest.getProgressStatus().name()) + .param("tradeAddress.sido", electricGuitarRequest.getTradeAddress().getSido()) + .param("tradeAddress.sgg", electricGuitarRequest.getTradeAddress().getSgg()) + .param("tradeAddress.emd", electricGuitarRequest.getTradeAddress().getEmd()) + .param("qualityStatus", String.valueOf(electricGuitarRequest.getQualityStatus())) + .param("price", String.valueOf(electricGuitarRequest.getPrice())) + .param("hasAnomaly", String.valueOf(electricGuitarRequest.getHasAnomaly())) + .param("description", electricGuitarRequest.getDescription()) + .param("brand", electricGuitarRequest.getBrand().name()) + .param("model", electricGuitarRequest.getModel().name()) + .param("productionYear", String.valueOf(electricGuitarRequest.getProductionYear())) + .param("color", electricGuitarRequest.getColor().name()) + .with(user(createTestUser(sellerId))) + ) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.id").value(expectedResult.getId())) + .andExpect(jsonPath("$.sellerId").value(sellerId)) + .andExpect(jsonPath("$.images").isArray()) + .andExpect(jsonPath("$.images.size()").value(expectedResult.getImages().size())) + .andExpect(jsonPath("$.hashtags").isArray()) + .andExpect(jsonPath("$.hashtags.size()").value(expectedResult.getHashtags().size())); + then(instrumentCommandService).should().createNewElectricGuitar( + eq(sellerId), any(CreateNewElectricGuitarRequest.class) + ); + verifyEveryMocksShouldHaveNoMoreInteractions(); + } + + private void verifyEveryMocksShouldHaveNoMoreInteractions() { + then(instrumentCommandService).shouldHaveNoMoreInteractions(); + } + + private UserDetails createTestUser(Long userId) throws Exception { + return new UserPrincipal(createUserDto(userId)); + } + + private MockMultipartFile createMultipartFile() { + return new MockMultipartFile( + "image", + "originalFilename", + MediaType.IMAGE_PNG_VALUE, + "content".getBytes() + ); + } + + private UserDto createUserDto(long id) throws Exception { + Constructor userResponseConstructor = UserDto.class.getDeclaredConstructor( + Long.class, Set.class, String.class, String.class, String.class, + String.class, LocalDate.class, Gender.class, String.class, String.class, + LocalDateTime.class + ); + userResponseConstructor.setAccessible(true); + return userResponseConstructor.newInstance( + id, + Set.of(RoleType.USER), + "test@mail.com", + "$2a$abc123", + "kakao-user-id", + "https://user-default-profile-image", + LocalDate.of(2024, 1, 1), + Gender.ETC, + "01012345678", + "https://contack-link", + LocalDateTime.of(2024, 1, 1, 0, 0) + ); + } + + private AddressDto createAddressDto() throws Exception { + Constructor addressDtoConstructor = AddressDto.class.getDeclaredConstructor( + String.class, String.class, String.class + ); + addressDtoConstructor.setAccessible(true); + return addressDtoConstructor.newInstance("서울특별시", "강남구", "청담동"); + } + + private ElectricGuitarDto createElectricGuitarDto(long id, long sellerId) throws Exception { + Constructor electricGuitarDtoConstructor = ElectricGuitarDto.class.getDeclaredConstructor( + Long.class, UserDto.class, String.class, InstrumentProgressStatus.class, AddressDto.class, Short.class, + Integer.class, Boolean.class, String.class, ElectricGuitarBrand.class, ElectricGuitarModel.class, + Short.class, GuitarColor.class, List.class, List.class + ); + electricGuitarDtoConstructor.setAccessible(true); + return electricGuitarDtoConstructor.newInstance( + id, + createUserDto(sellerId), + "Test electric guitar", + InstrumentProgressStatus.SELLING, + createAddressDto(), + (short)3, + 550000, + true, + "description", + ElectricGuitarBrand.FENDER_USA, + ElectricGuitarModel.TELECASTER, + (short)2014, + GuitarColor.RED, + List.of(), + List.of() + ); + } + + private AddressRequest createAddressRequest() throws Exception { + Constructor addressRequestConstructor = AddressRequest.class.getDeclaredConstructor( + String.class, String.class, String.class + ); + addressRequestConstructor.setAccessible(true); + return addressRequestConstructor.newInstance("서울특별시", "강남구", "청담동"); + } + + private CreateNewElectricGuitarRequest createElectricGuitarRequest() throws Exception { + Constructor createNewElectricGuitarRequestConstructor = CreateNewElectricGuitarRequest.class + .getDeclaredConstructor(String.class, List.class, InstrumentProgressStatus.class, AddressRequest.class, + Short.class, Integer.class, Boolean.class, String.class, ElectricGuitarBrand.class, + ElectricGuitarModel.class, Short.class, GuitarColor.class, List.class); + createNewElectricGuitarRequestConstructor.setAccessible(true); + return createNewElectricGuitarRequestConstructor.newInstance( + "Test electric guitar", + List.of(createMultipartFile(), createMultipartFile(), createMultipartFile(), createMultipartFile()), + InstrumentProgressStatus.SELLING, + createAddressRequest(), + (short)3, + 550000, + true, + "description", + ElectricGuitarBrand.FENDER_USA, + ElectricGuitarModel.TELECASTER, + (short)2014, + GuitarColor.RED, + List.of("Fender", "Guitar") + ); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..7cec72d --- /dev/null +++ b/src/test/java/com/ajou/hertz/unit/domain/instrument/service/InstrumentCommandServiceTest.java @@ -0,0 +1,227 @@ +package com.ajou.hertz.unit.domain.instrument.service; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.lang.reflect.Constructor; +import java.time.LocalDate; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; + +import com.ajou.hertz.common.dto.request.AddressRequest; +import com.ajou.hertz.common.entity.Address; +import com.ajou.hertz.common.file.dto.FileDto; +import com.ajou.hertz.common.file.service.FileService; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarBrand; +import com.ajou.hertz.domain.instrument.constant.ElectricGuitarModel; +import com.ajou.hertz.domain.instrument.constant.GuitarColor; +import com.ajou.hertz.domain.instrument.constant.InstrumentProgressStatus; +import com.ajou.hertz.domain.instrument.dto.ElectricGuitarDto; +import com.ajou.hertz.domain.instrument.dto.request.CreateNewElectricGuitarRequest; +import com.ajou.hertz.domain.instrument.entity.ElectricGuitar; +import com.ajou.hertz.domain.instrument.entity.Instrument; +import com.ajou.hertz.domain.instrument.entity.InstrumentHashtag; +import com.ajou.hertz.domain.instrument.entity.InstrumentImage; +import com.ajou.hertz.domain.instrument.repository.InstrumentHashtagRepository; +import com.ajou.hertz.domain.instrument.repository.InstrumentImageRepository; +import com.ajou.hertz.domain.instrument.repository.InstrumentRepository; +import com.ajou.hertz.domain.instrument.service.InstrumentCommandService; +import com.ajou.hertz.domain.user.constant.Gender; +import com.ajou.hertz.domain.user.constant.RoleType; +import com.ajou.hertz.domain.user.entity.User; +import com.ajou.hertz.domain.user.service.UserQueryService; + +@DisplayName("[Unit] Service(Command) - Instrument") +@ExtendWith(MockitoExtension.class) +class InstrumentCommandServiceTest { + + @InjectMocks + private InstrumentCommandService sut; + + @Mock + private UserQueryService userQueryService; + + @Mock + private FileService fileService; + + @Mock + private InstrumentRepository instrumentRepository; + + @Mock + private InstrumentHashtagRepository instrumentHashtagRepository; + + @Mock + private InstrumentImageRepository instrumentImageRepository; + + @Test + void 새로_등록할_일렉기타의_정보가_주어지면_일렉기타_매물을_등록한다() throws Exception { + // given + long sellerId = 1L; + CreateNewElectricGuitarRequest electricGuitarRequest = createElectricGuitarRequest(); + User seller = createUser(1L); + ElectricGuitar electricGuitar = createElectricGuitar(2L, seller); + List instrumentImages = List.of(createInstrumentImage(3L, electricGuitar)); + List instrumentHashtags = List.of(createInstrumentHashtag(4L, electricGuitar)); + List.of(); + given(userQueryService.getById(sellerId)).willReturn(seller); + given(instrumentRepository.save(any(Instrument.class))).willReturn(electricGuitar); + given(fileService.uploadFiles(eq(electricGuitarRequest.getImages()), anyString())) + .willReturn(List.of(createFileDto())); + given(instrumentImageRepository.saveAll(ArgumentMatchers.>any())) + .willReturn(instrumentImages); + given(instrumentHashtagRepository.saveAll(ArgumentMatchers.>any())) + .willReturn(instrumentHashtags); + + // when + ElectricGuitarDto result = sut.createNewElectricGuitar(sellerId, electricGuitarRequest); + + // then + then(userQueryService).should().getById(sellerId); + then(instrumentRepository).should().save(any(Instrument.class)); + then(fileService).should().uploadFiles(eq(electricGuitarRequest.getImages()), anyString()); + then(instrumentImageRepository).should().saveAll(ArgumentMatchers.>any()); + then(instrumentHashtagRepository).should().saveAll(ArgumentMatchers.>any()); + verifyEveryMocksShouldHaveNoMoreInteractions(); + assertThat(result) + .hasFieldOrPropertyWithValue("id", electricGuitar.getId()) + .hasFieldOrPropertyWithValue("seller.id", seller.getId()); + assertThat(result.getImages()).hasSize(instrumentImages.size()); + assertThat(result.getHashtags()).hasSize(instrumentHashtags.size()); + } + + private void verifyEveryMocksShouldHaveNoMoreInteractions() { + then(userQueryService).shouldHaveNoMoreInteractions(); + then(fileService).shouldHaveNoMoreInteractions(); + then(instrumentRepository).shouldHaveNoMoreInteractions(); + then(instrumentImageRepository).shouldHaveNoMoreInteractions(); + then(instrumentHashtagRepository).shouldHaveNoMoreInteractions(); + } + + private MockMultipartFile createMultipartFile() { + return new MockMultipartFile( + "image", + "originalFilename", + MediaType.IMAGE_PNG_VALUE, + "content".getBytes() + ); + } + + private User createUser(Long id) throws Exception { + Constructor userConstructor = User.class.getDeclaredConstructor( + Long.class, Set.class, String.class, String.class, String.class, + String.class, LocalDate.class, Gender.class, String.class, String.class + ); + userConstructor.setAccessible(true); + return userConstructor.newInstance( + id, + Set.of(RoleType.USER), + "test@mail.com", + "password", + "12345", + "https://user-default-profile-image-url", + LocalDate.of(2024, 1, 1), + Gender.ETC, + "01012345678", + null + ); + } + + private Address createAddress() { + return new Address("서울특별시", "강남구", "청담동"); + } + + private InstrumentImage createInstrumentImage(Long id, Instrument instrument) throws Exception { + Constructor instrumentImageConstructor = InstrumentImage.class.getDeclaredConstructor( + Long.class, Instrument.class, String.class, String.class, String.class + ); + instrumentImageConstructor.setAccessible(true); + return instrumentImageConstructor.newInstance( + id, + instrument, + "original-name", + "stored-name", + "https://instrument-image-url" + ); + } + + private InstrumentHashtag createInstrumentHashtag(Long id, Instrument instrument) throws Exception { + Constructor instrumentHashtagConstructor = InstrumentHashtag.class.getDeclaredConstructor( + Long.class, Instrument.class, String.class + ); + instrumentHashtagConstructor.setAccessible(true); + return instrumentHashtagConstructor.newInstance(id, instrument, "content"); + } + + private ElectricGuitar createElectricGuitar(Long id, User seller) throws Exception { + Constructor electricGuitarConstructor = ElectricGuitar.class.getDeclaredConstructor( + Long.class, User.class, String.class, InstrumentProgressStatus.class, Address.class, Short.class, + Integer.class, Boolean.class, String.class, ElectricGuitarBrand.class, ElectricGuitarModel.class, + Short.class, GuitarColor.class + ); + electricGuitarConstructor.setAccessible(true); + return electricGuitarConstructor.newInstance( + id, + seller, + "Test electric guitar", + InstrumentProgressStatus.SELLING, + createAddress(), + (short)3, + 550000, + true, + "description", + ElectricGuitarBrand.FENDER_USA, + ElectricGuitarModel.TELECASTER, + (short)2014, + GuitarColor.RED + ); + } + + private FileDto createFileDto() throws Exception { + Constructor fileDtoConstructor = FileDto.class.getDeclaredConstructor( + String.class, String.class, String.class + ); + fileDtoConstructor.setAccessible(true); + return fileDtoConstructor.newInstance("original-name", "stored-name", "https://file-url"); + } + + private AddressRequest createAddressRequest() throws Exception { + Constructor addressRequestConstructor = AddressRequest.class.getDeclaredConstructor( + String.class, String.class, String.class + ); + addressRequestConstructor.setAccessible(true); + return addressRequestConstructor.newInstance("서울특별시", "강남구", "청담동"); + } + + private CreateNewElectricGuitarRequest createElectricGuitarRequest() throws Exception { + Constructor createNewElectricGuitarRequestConstructor = CreateNewElectricGuitarRequest.class + .getDeclaredConstructor(String.class, List.class, InstrumentProgressStatus.class, AddressRequest.class, + Short.class, Integer.class, Boolean.class, String.class, ElectricGuitarBrand.class, + ElectricGuitarModel.class, Short.class, GuitarColor.class, List.class); + createNewElectricGuitarRequestConstructor.setAccessible(true); + return createNewElectricGuitarRequestConstructor.newInstance( + "Test electric guitar", + List.of(createMultipartFile()), + InstrumentProgressStatus.SELLING, + createAddressRequest(), + (short)3, + 550000, + true, + "description", + ElectricGuitarBrand.FENDER_USA, + ElectricGuitarModel.TELECASTER, + (short)2014, + GuitarColor.RED, + List.of("Fender", "Guitar") + ); + } +} From 4d12b6b3832dd099e4d6614b198efad43c37fc06 Mon Sep 17 00:00:00 2001 From: Wo-ogie Date: Tue, 27 Feb 2024 03:52:50 +0900 Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20#50=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20enum=20class=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../instrument/constant/InstrumentCategory.java | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 src/main/java/com/ajou/hertz/domain/instrument/constant/InstrumentCategory.java diff --git a/src/main/java/com/ajou/hertz/domain/instrument/constant/InstrumentCategory.java b/src/main/java/com/ajou/hertz/domain/instrument/constant/InstrumentCategory.java deleted file mode 100644 index daa73e2..0000000 --- a/src/main/java/com/ajou/hertz/domain/instrument/constant/InstrumentCategory.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.ajou.hertz.domain.instrument.constant; - -public enum InstrumentCategory { - - ELECTRIC_GUITAR, - BASS_GUITAR, - ACOUSTIC_AND_CLASSIC_GUITAR, - AMPLIFIER, - EFFECTOR, - AUDIO_EQUIPMENT -} From 09bf01069cd6ed15a049b781612c87d5cd6de854 Mon Sep 17 00:00:00 2001 From: Wo-ogie Date: Tue, 27 Feb 2024 03:54:04 +0900 Subject: [PATCH 6/6] =?UTF-8?q?chore:=20#50=20tomcat=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=ED=81=AC=EA=B8=B0=20=EC=A0=9C=ED=95=9C=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 888a534..748afc5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -16,8 +16,8 @@ spring.datasource.username=${DB_USERNAME} spring.datasource.password=${DB_PASSWORD} spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.servlet.multipart.max-request-size=50MB -spring.servlet.multipart.max-file-size=5MB +spring.servlet.multipart.max-request-size=70MB +spring.servlet.multipart.max-file-size=10MB spring.sql.init.mode=never