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/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
-}
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/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
);
}
}
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/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/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/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/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();
+ }
+}
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 {
+}
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/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
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")
+ );
+ }
+}