Skip to content

Commit d58dff3

Browse files
committed
feat: #83 악기 매물 삭제 API 구현
1 parent 72a286b commit d58dff3

14 files changed

+609
-80
lines changed

src/main/java/com/ajou/hertz/common/exception/constant/CustomExceptionType.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public enum CustomExceptionType {
5151
/**
5252
* 악기 관련 예외
5353
*/
54-
INSTRUMENT_NOT_FOUND_BY_ID(2600, "일치하는 매물 정보를 찾을 수 없습니다.")
55-
;
54+
INSTRUMENT_NOT_FOUND_BY_ID(2600, "일치하는 매물 정보를 찾을 수 없습니다."),
55+
INSTRUMENT_DELETE_PERMISSION_DENIED(2601, "악기 매물을 삭제할 권한이 없습니다. 매물은 판매자 본인만 삭제할 수 있습니다.");
5656

5757
private final Integer code;
5858
private final String message;

src/main/java/com/ajou/hertz/domain/instrument/controller/InstrumentController.java

+22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.springframework.http.MediaType;
1010
import org.springframework.http.ResponseEntity;
1111
import org.springframework.security.core.annotation.AuthenticationPrincipal;
12+
import org.springframework.web.bind.annotation.DeleteMapping;
1213
import org.springframework.web.bind.annotation.GetMapping;
1314
import org.springframework.web.bind.annotation.ModelAttribute;
1415
import org.springframework.web.bind.annotation.PathVariable;
@@ -52,6 +53,9 @@
5253

5354
import io.swagger.v3.oas.annotations.Operation;
5455
import io.swagger.v3.oas.annotations.Parameter;
56+
import io.swagger.v3.oas.annotations.media.Content;
57+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
58+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
5559
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
5660
import io.swagger.v3.oas.annotations.tags.Tag;
5761
import jakarta.validation.Valid;
@@ -411,4 +415,22 @@ public ResponseEntity<AudioEquipmentResponse> createNewAudioEquipmentV1(
411415
.created(URI.create("/instruments/" + audioEquipment.getId()))
412416
.body(InstrumentMapper.toAudioEquipmentResponse(audioEquipment));
413417
}
418+
419+
@Operation(
420+
summary = "악기 매물 삭제",
421+
description = "악기 매물을 삭제합니다. 매물 삭제는 판매자만 할 수 있습니다.",
422+
security = @SecurityRequirement(name = "access-token")
423+
)
424+
@ApiResponses({
425+
@ApiResponse(responseCode = "204"),
426+
@ApiResponse(responseCode = "403", description = "[2601] 악기를 삭제하려는 유저가 판매자가 아닌 경우", content = @Content)
427+
})
428+
@DeleteMapping(value = "/{instrumentId}", headers = API_VERSION_HEADER_NAME + "=" + 1)
429+
public ResponseEntity<Void> deleteInstrumentV1(
430+
@AuthenticationPrincipal UserPrincipal userPrincipal,
431+
@Parameter(description = "Id of instrument", example = "2") @PathVariable Long instrumentId
432+
) {
433+
instrumentCommandService.deleteInstrumentById(userPrincipal.getUserId(), instrumentId);
434+
return ResponseEntity.noContent().build();
435+
}
414436
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.ajou.hertz.domain.instrument.exception;
2+
3+
import com.ajou.hertz.common.exception.ForbiddenException;
4+
import com.ajou.hertz.common.exception.constant.CustomExceptionType;
5+
6+
public class InstrumentDeletePermissionDeniedException extends ForbiddenException {
7+
8+
public InstrumentDeletePermissionDeniedException() {
9+
super(CustomExceptionType.INSTRUMENT_DELETE_PERMISSION_DENIED);
10+
}
11+
}

src/main/java/com/ajou/hertz/domain/instrument/repository/InstrumentHashtagRepository.java

+3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
import org.springframework.data.jpa.repository.JpaRepository;
44

5+
import com.ajou.hertz.domain.instrument.entity.Instrument;
56
import com.ajou.hertz.domain.instrument.entity.InstrumentHashtag;
67

78
public interface InstrumentHashtagRepository extends JpaRepository<InstrumentHashtag, Long> {
9+
10+
void deleteAllByInstrument(Instrument instrument);
811
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package com.ajou.hertz.domain.instrument.repository;
22

3+
import java.util.List;
4+
35
import org.springframework.data.jpa.repository.JpaRepository;
46

57
import com.ajou.hertz.domain.instrument.entity.InstrumentImage;
68

79
public interface InstrumentImageRepository extends JpaRepository<InstrumentImage, Long> {
10+
11+
List<InstrumentImage> findAllByInstrument_Id(Long instrumentId);
812
}

src/main/java/com/ajou/hertz/domain/instrument/service/InstrumentCommandService.java

+21-16
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.springframework.transaction.annotation.Transactional;
77
import org.springframework.web.multipart.MultipartFile;
88

9-
import com.ajou.hertz.common.file.service.FileService;
109
import com.ajou.hertz.domain.instrument.dto.AcousticAndClassicGuitarDto;
1110
import com.ajou.hertz.domain.instrument.dto.AmplifierDto;
1211
import com.ajou.hertz.domain.instrument.dto.AudioEquipmentDto;
@@ -29,9 +28,9 @@
2928
import com.ajou.hertz.domain.instrument.entity.Instrument;
3029
import com.ajou.hertz.domain.instrument.entity.InstrumentHashtag;
3130
import com.ajou.hertz.domain.instrument.entity.InstrumentImage;
31+
import com.ajou.hertz.domain.instrument.exception.InstrumentDeletePermissionDeniedException;
3232
import com.ajou.hertz.domain.instrument.mapper.InstrumentMapper;
3333
import com.ajou.hertz.domain.instrument.repository.InstrumentHashtagRepository;
34-
import com.ajou.hertz.domain.instrument.repository.InstrumentImageRepository;
3534
import com.ajou.hertz.domain.instrument.repository.InstrumentRepository;
3635
import com.ajou.hertz.domain.instrument.strategy.AcousticAndClassicGuitarCreationStrategy;
3736
import com.ajou.hertz.domain.instrument.strategy.AmplifierCreationStrategy;
@@ -50,13 +49,11 @@
5049
@Service
5150
public class InstrumentCommandService {
5251

53-
private static final String INSTRUMENT_IMAGE_UPLOAD_PATH = "instrument-image/";
54-
5552
private final UserQueryService userQueryService;
56-
private final FileService fileService;
53+
private final InstrumentQueryService instrumentQueryService;
54+
private final InstrumentImageCommandService instrumentImageCommandService;
5755
private final InstrumentRepository instrumentRepository;
5856
private final InstrumentHashtagRepository instrumentHashtagRepository;
59-
private final InstrumentImageRepository instrumentImageRepository;
6057

6158
/**
6259
* 신규 일렉 기타 매물을 생성 및 저장한다.
@@ -137,6 +134,23 @@ public AudioEquipmentDto createNewAudioEquipment(Long sellerId, CreateNewAudioEq
137134
return InstrumentMapper.toAmplifierDto(audioEquipment);
138135
}
139136

137+
/**
138+
* 악기 매물을 삭제한다.
139+
*
140+
* @param userId 악기 매물을 삭제하려는 유저의 id
141+
* @param instrumentId 삭제할 악기 매물의 id
142+
* @throws InstrumentDeletePermissionDeniedException 악기를 삭제하려는 유저가 판매자가 아닌 경우
143+
*/
144+
public void deleteInstrumentById(Long userId, Long instrumentId) {
145+
Instrument instrument = instrumentQueryService.getInstrumentById(instrumentId);
146+
if (!userId.equals(instrument.getSeller().getId())) {
147+
throw new InstrumentDeletePermissionDeniedException();
148+
}
149+
instrumentImageCommandService.deleteAllByInstrumentId(instrumentId);
150+
instrumentHashtagRepository.deleteAllByInstrument(instrument);
151+
instrumentRepository.delete(instrument);
152+
}
153+
140154
/**
141155
* 신규 악기 매물을 생성하여 저장한다.
142156
*
@@ -164,16 +178,7 @@ private <T extends Instrument, U extends CreateNewInstrumentRequest<T>> T create
164178
* @param images image list
165179
*/
166180
private void registerInstrumentImages(Instrument instrument, List<MultipartFile> images) {
167-
List<InstrumentImage> instrumentImages = fileService
168-
.uploadFiles(images, INSTRUMENT_IMAGE_UPLOAD_PATH)
169-
.stream()
170-
.map(fileDto -> InstrumentImage.create(
171-
instrument,
172-
fileDto.getOriginalName(),
173-
fileDto.getStoredName(),
174-
fileDto.getUrl()
175-
)).toList();
176-
List<InstrumentImage> savedInstrumentImages = instrumentImageRepository.saveAll(instrumentImages);
181+
List<InstrumentImage> savedInstrumentImages = instrumentImageCommandService.saveImages(instrument, images);
177182
instrument.getImages().addAll(savedInstrumentImages);
178183
}
179184

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.ajou.hertz.domain.instrument.service;
2+
3+
import java.util.List;
4+
5+
import org.springframework.stereotype.Service;
6+
import org.springframework.web.multipart.MultipartFile;
7+
8+
import com.ajou.hertz.common.file.service.FileService;
9+
import com.ajou.hertz.domain.instrument.entity.Instrument;
10+
import com.ajou.hertz.domain.instrument.entity.InstrumentImage;
11+
import com.ajou.hertz.domain.instrument.repository.InstrumentImageRepository;
12+
13+
import jakarta.transaction.Transactional;
14+
import lombok.RequiredArgsConstructor;
15+
16+
@RequiredArgsConstructor
17+
@Transactional
18+
@Service
19+
public class InstrumentImageCommandService {
20+
21+
private static final String INSTRUMENT_IMAGE_UPLOAD_PATH = "instrument-image/";
22+
23+
private final InstrumentImageQueryService instrumentImageQueryService;
24+
private final FileService fileService;
25+
private final InstrumentImageRepository instrumentImageRepository;
26+
27+
/**
28+
* 전달받은 multipart files로 instrument image entity를 생성 후 저장한다.
29+
*
30+
* @param instrument 이미지의 대상이 되는 instrument entity
31+
* @param images 저장하고자 하는 image
32+
* @return 저장된 instrument images
33+
*/
34+
public List<InstrumentImage> saveImages(Instrument instrument, Iterable<MultipartFile> images) {
35+
List<InstrumentImage> instrumentImages = fileService
36+
.uploadFiles(images, INSTRUMENT_IMAGE_UPLOAD_PATH)
37+
.stream()
38+
.map(fileDto -> InstrumentImage.create(
39+
instrument,
40+
fileDto.getOriginalName(),
41+
fileDto.getStoredName(),
42+
fileDto.getUrl()
43+
)).toList();
44+
return instrumentImageRepository.saveAll(instrumentImages);
45+
}
46+
47+
/**
48+
* 특정 악기 매물의 모든 이미지를 삭제한다.
49+
*
50+
* @param instrumentId 이미지를 전체 삭제할 악기 매물의 id
51+
*/
52+
public void deleteAllByInstrumentId(Long instrumentId) {
53+
List<InstrumentImage> instrumentImages = instrumentImageQueryService.findAllByInstrumentId(instrumentId);
54+
fileService.deleteAll(
55+
instrumentImages.stream()
56+
.map(InstrumentImage::getStoredName)
57+
.toList()
58+
);
59+
instrumentImageRepository.deleteAll(instrumentImages);
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.ajou.hertz.domain.instrument.service;
2+
3+
import java.util.List;
4+
5+
import org.springframework.stereotype.Service;
6+
import org.springframework.transaction.annotation.Transactional;
7+
8+
import com.ajou.hertz.domain.instrument.entity.InstrumentImage;
9+
import com.ajou.hertz.domain.instrument.repository.InstrumentImageRepository;
10+
11+
import lombok.RequiredArgsConstructor;
12+
13+
@RequiredArgsConstructor
14+
@Transactional(readOnly = true)
15+
@Service
16+
public class InstrumentImageQueryService {
17+
18+
private final InstrumentImageRepository instrumentImageRepository;
19+
20+
/**
21+
* 악기 id를 전달받고, 해당하는 악기의 이미지를 전부 조회한다.
22+
*
23+
* @param instrumentId 이미지를 조회하고자 하는 악기의 id
24+
* @return 조회된 Instrument image entity list
25+
*/
26+
public List<InstrumentImage> findAllByInstrumentId(Long instrumentId) {
27+
return instrumentImageRepository.findAllByInstrument_Id(instrumentId);
28+
}
29+
}

src/main/java/com/ajou/hertz/domain/instrument/service/InstrumentQueryService.java

+73-5
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,50 @@ public class InstrumentQueryService {
3333

3434
private final InstrumentRepository instrumentRepository;
3535

36+
/**
37+
* Id로 악기를 조회한다.
38+
*
39+
* @param id 조회하고자 하는 Instrument entity의 id
40+
* @return 조회된 Instrument entity
41+
*/
42+
public Instrument getInstrumentById(Long id) {
43+
return instrumentRepository.findById(id)
44+
.orElseThrow(() -> new InstrumentNotFoundByIdException(id));
45+
}
46+
47+
/**
48+
* Id로 악기를 조회한다.
49+
*
50+
* @param id 조회하고자 하는 악기의 id
51+
* @return 조회된 Instrument dto
52+
*/
3653
public InstrumentDto getInstrumentDtoById(Long id) {
3754
Instrument instrument = getInstrumentById(id);
3855
return InstrumentMapper.toDto(instrument);
3956
}
4057

58+
/**
59+
* 악기 목록을 조회한다.
60+
*
61+
* @param page 페이지 번호
62+
* @param pageSize 페이지 크기
63+
* @param sort 정렬 기준
64+
* @return 조회된 악기 dto 목록
65+
*/
4166
public Page<InstrumentDto> findInstrumentDtos(int page, int pageSize, InstrumentSortOption sort) {
4267
return instrumentRepository
4368
.findAll(PageRequest.of(page, pageSize, sort.toSort()))
4469
.map(InstrumentMapper::toDto);
4570
}
4671

72+
/**
73+
* 일렉 기타 목록을 조회한다.
74+
*
75+
* @param page 페이지 번호
76+
* @param pageSize 페이지 크기
77+
* @param sort 정렬 기준
78+
* @return 조회된 일렉 기타 dto 목록
79+
*/
4780
public Page<ElectricGuitarDto> findElectricGuitarDtos(
4881
int page,
4982
int pageSize,
@@ -55,6 +88,14 @@ public Page<ElectricGuitarDto> findElectricGuitarDtos(
5588
.map(InstrumentMapper::toElectricGuitarDto);
5689
}
5790

91+
/**
92+
* 베이스 기타 목록을 조회한다.
93+
*
94+
* @param page 페이지 번호
95+
* @param pageSize 페이지 크기
96+
* @param sort 정렬 기준
97+
* @return 조회된 베이스 기타 dto 목록
98+
*/
5899
public Page<BassGuitarDto> findBassGuitarDtos(
59100
int page,
60101
int pageSize,
@@ -66,6 +107,14 @@ public Page<BassGuitarDto> findBassGuitarDtos(
66107
.map(InstrumentMapper::toBassGuitarDto);
67108
}
68109

110+
/**
111+
* 어쿠스틱&클래식 기타 목록을 조회한다.
112+
*
113+
* @param page 페이지 번호
114+
* @param pageSize 페이지 크기
115+
* @param sort 정렬 기준
116+
* @return 조회된 어쿠스틱&클래식 기타 dto 목록
117+
*/
69118
public Page<AcousticAndClassicGuitarDto> findAcousticAndClassicGuitarDtos(
70119
int page,
71120
int pageSize,
@@ -77,6 +126,14 @@ public Page<AcousticAndClassicGuitarDto> findAcousticAndClassicGuitarDtos(
77126
.map(InstrumentMapper::toAcousticAndClassicGuitarDto);
78127
}
79128

129+
/**
130+
* 이펙터 목록을 조회한다.
131+
*
132+
* @param page 페이지 번호
133+
* @param pageSize 페이지 크기
134+
* @param sort 정렬 기준
135+
* @return 조회된 이펙터 dto 목록
136+
*/
80137
public Page<EffectorDto> findEffectorDtos(
81138
int page,
82139
int pageSize,
@@ -88,6 +145,14 @@ public Page<EffectorDto> findEffectorDtos(
88145
.map(InstrumentMapper::toEffectorDto);
89146
}
90147

148+
/**
149+
* 앰프 목록을 조회한다.
150+
*
151+
* @param page 페이지 번호
152+
* @param pageSize 페이지 크기
153+
* @param sort 정렬 기준
154+
* @return 조회된 앰프 dto 목록
155+
*/
91156
public Page<AmplifierDto> findAmplifierDtos(
92157
int page,
93158
int pageSize,
@@ -99,6 +164,14 @@ public Page<AmplifierDto> findAmplifierDtos(
99164
.map(InstrumentMapper::toAmplifierDto);
100165
}
101166

167+
/**
168+
* 음향 장비 목록을 조회한다.
169+
*
170+
* @param page 페이지 번호
171+
* @param pageSize 페이지 크기
172+
* @param sort 정렬 기준
173+
* @return 조회된 음향 장비 dto 목록
174+
*/
102175
public Page<AudioEquipmentDto> findAudioEquipmentDtos(
103176
int page,
104177
int pageSize,
@@ -109,9 +182,4 @@ public Page<AudioEquipmentDto> findAudioEquipmentDtos(
109182
.findAudioEquipments(page, pageSize, sort, filterConditions)
110183
.map(InstrumentMapper::toAmplifierDto);
111184
}
112-
113-
private Instrument getInstrumentById(Long id) {
114-
return instrumentRepository.findById(id)
115-
.orElseThrow(() -> new InstrumentNotFoundByIdException(id));
116-
}
117185
}

0 commit comments

Comments
 (0)