Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

악기 매물 삭제 API 구현 #84

Merged
merged 2 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public enum CustomExceptionType {
/**
* 악기 관련 예외
*/
INSTRUMENT_NOT_FOUND_BY_ID(2600, "일치하는 매물 정보를 찾을 수 없습니다.")
;
INSTRUMENT_NOT_FOUND_BY_ID(2600, "일치하는 매물 정보를 찾을 수 없습니다."),
INSTRUMENT_DELETE_PERMISSION_DENIED(2601, "악기 매물을 삭제할 권한이 없습니다. 매물은 판매자 본인만 삭제할 수 있습니다.");

private final Integer code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

Expand All @@ -15,6 +16,7 @@
import com.ajou.hertz.common.properties.AWSProperties;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;

Expand Down Expand Up @@ -56,8 +58,16 @@ public FileDto uploadFile(MultipartFile multipartFile, String uploadPath) {
}

// TODO: 추후 서버 성능 테스트와 함께 @Async 사용한 비동기 로직으로 변경 검토

/**
* 파일(MultipartFile)들을 전달받아 모두 s3 bucket에 업로드한다.
*
* @param multipartFiles 업로드할 파일들
* @param uploadPath 파일들을 업로드할 경로
* @return AWS S3에 저장된 파일들의 정보가 담긴 dto list
*/
@Override
public List<FileDto> uploadFiles(List<MultipartFile> multipartFiles, String uploadPath) {
public List<FileDto> uploadFiles(Iterable<MultipartFile> multipartFiles, String uploadPath) {
List<FileDto> result = new ArrayList<>();
multipartFiles.forEach(multipartFile -> {
FileDto fileUploaded = uploadFile(multipartFile, uploadPath);
Expand All @@ -66,11 +76,23 @@ public List<FileDto> uploadFiles(List<MultipartFile> multipartFiles, String uplo
return result;
}

/**
* S3 bucket에서 파일들을 삭제한다.
*
* @param storedFileNames 삭제할 파일들의 이름 목록 (keys of bucket object)
*/
@Override
public void deleteAll(Collection<String> storedFileNames) {
storedFileNames.forEach(storedFileName -> s3Client.deleteObject(
new DeleteObjectRequest(awsProperties.s3().bucketName(), storedFileName)
));
}

/**
* S3 Bucket에 업로드할 고유한 파일 이름을 생성한다.
*
* @param originalFilename 파일의 원래 이름.
* @param uploadPath 업로드 경로
* @param uploadPath 업로드 경로
* @return 생성된 고유한 파일 이름
*/
private String createFileNameToStore(String originalFilename, String uploadPath) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ajou.hertz.common.file.service;

import java.util.Collection;
import java.util.List;

import org.springframework.web.multipart.MultipartFile;
Expand All @@ -24,5 +25,12 @@ public interface FileService {
* @param uploadPath 파일들을 업로드할 경로
* @return 저장된 파일들의 정보가 담긴 dto list
*/
List<FileDto> uploadFiles(List<MultipartFile> multipartFiles, String uploadPath);
List<FileDto> uploadFiles(Iterable<MultipartFile> multipartFiles, String uploadPath);

/**
* 파일들을 삭제한다.
*
* @param storedFileNames 삭제할 파일들의 이름 목록
*/
void deleteAll(Collection<String> storedFileNames);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
Expand Down Expand Up @@ -52,6 +53,9 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand Down Expand Up @@ -411,4 +415,22 @@ public ResponseEntity<AudioEquipmentResponse> createNewAudioEquipmentV1(
.created(URI.create("/instruments/" + audioEquipment.getId()))
.body(InstrumentMapper.toAudioEquipmentResponse(audioEquipment));
}

@Operation(
summary = "악기 매물 삭제",
description = "악기 매물을 삭제합니다. 매물 삭제는 판매자만 할 수 있습니다.",
security = @SecurityRequirement(name = "access-token")
)
@ApiResponses({
@ApiResponse(responseCode = "204"),
@ApiResponse(responseCode = "403", description = "[2601] 악기를 삭제하려는 유저가 판매자가 아닌 경우", content = @Content)
})
@DeleteMapping(value = "/{instrumentId}", headers = API_VERSION_HEADER_NAME + "=" + 1)
public ResponseEntity<Void> deleteInstrumentV1(
@AuthenticationPrincipal UserPrincipal userPrincipal,
@Parameter(description = "Id of instrument", example = "2") @PathVariable Long instrumentId
) {
instrumentCommandService.deleteInstrumentById(userPrincipal.getUserId(), instrumentId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ajou.hertz.domain.instrument.exception;

import com.ajou.hertz.common.exception.ForbiddenException;
import com.ajou.hertz.common.exception.constant.CustomExceptionType;

public class InstrumentDeletePermissionDeniedException extends ForbiddenException {

public InstrumentDeletePermissionDeniedException() {
super(CustomExceptionType.INSTRUMENT_DELETE_PERMISSION_DENIED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

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

import com.ajou.hertz.domain.instrument.entity.Instrument;
import com.ajou.hertz.domain.instrument.entity.InstrumentHashtag;

public interface InstrumentHashtagRepository extends JpaRepository<InstrumentHashtag, Long> {

void deleteAllByInstrument(Instrument instrument);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.ajou.hertz.domain.instrument.repository;

import java.util.List;

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

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

public interface InstrumentImageRepository extends JpaRepository<InstrumentImage, Long> {

List<InstrumentImage> findAllByInstrument_Id(Long instrumentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.ajou.hertz.common.file.service.FileService;
import com.ajou.hertz.domain.instrument.dto.AcousticAndClassicGuitarDto;
import com.ajou.hertz.domain.instrument.dto.AmplifierDto;
import com.ajou.hertz.domain.instrument.dto.AudioEquipmentDto;
Expand All @@ -29,9 +28,9 @@
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.exception.InstrumentDeletePermissionDeniedException;
import com.ajou.hertz.domain.instrument.mapper.InstrumentMapper;
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.strategy.AcousticAndClassicGuitarCreationStrategy;
import com.ajou.hertz.domain.instrument.strategy.AmplifierCreationStrategy;
Expand All @@ -50,13 +49,11 @@
@Service
public class InstrumentCommandService {

private static final String INSTRUMENT_IMAGE_UPLOAD_PATH = "instrument-image/";

private final UserQueryService userQueryService;
private final FileService fileService;
private final InstrumentQueryService instrumentQueryService;
private final InstrumentImageCommandService instrumentImageCommandService;
private final InstrumentRepository instrumentRepository;
private final InstrumentHashtagRepository instrumentHashtagRepository;
private final InstrumentImageRepository instrumentImageRepository;

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

/**
* 악기 매물을 삭제한다.
*
* @param userId 악기 매물을 삭제하려는 유저의 id
* @param instrumentId 삭제할 악기 매물의 id
* @throws InstrumentDeletePermissionDeniedException 악기를 삭제하려는 유저가 판매자가 아닌 경우
*/
public void deleteInstrumentById(Long userId, Long instrumentId) {
Instrument instrument = instrumentQueryService.getInstrumentById(instrumentId);
if (!userId.equals(instrument.getSeller().getId())) {
throw new InstrumentDeletePermissionDeniedException();
}
instrumentImageCommandService.deleteAllByInstrumentId(instrumentId);
instrumentHashtagRepository.deleteAllByInstrument(instrument);
instrumentRepository.delete(instrument);
}

/**
* 신규 악기 매물을 생성하여 저장한다.
*
Expand Down Expand Up @@ -164,16 +178,7 @@ private <T extends Instrument, U extends CreateNewInstrumentRequest<T>> T create
* @param images image list
*/
private void registerInstrumentImages(Instrument instrument, List<MultipartFile> images) {
List<InstrumentImage> instrumentImages = fileService
.uploadFiles(images, INSTRUMENT_IMAGE_UPLOAD_PATH)
.stream()
.map(fileDto -> InstrumentImage.create(
instrument,
fileDto.getOriginalName(),
fileDto.getStoredName(),
fileDto.getUrl()
)).toList();
List<InstrumentImage> savedInstrumentImages = instrumentImageRepository.saveAll(instrumentImages);
List<InstrumentImage> savedInstrumentImages = instrumentImageCommandService.saveImages(instrument, images);
instrument.getImages().addAll(savedInstrumentImages);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.ajou.hertz.domain.instrument.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.ajou.hertz.common.file.service.FileService;
import com.ajou.hertz.domain.instrument.entity.Instrument;
import com.ajou.hertz.domain.instrument.entity.InstrumentImage;
import com.ajou.hertz.domain.instrument.repository.InstrumentImageRepository;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Transactional
@Service
public class InstrumentImageCommandService {

private static final String INSTRUMENT_IMAGE_UPLOAD_PATH = "instrument-image/";

private final InstrumentImageQueryService instrumentImageQueryService;
private final FileService fileService;
private final InstrumentImageRepository instrumentImageRepository;

/**
* 전달받은 multipart files로 instrument image entity를 생성 후 저장한다.
*
* @param instrument 이미지의 대상이 되는 instrument entity
* @param images 저장하고자 하는 image
* @return 저장된 instrument images
*/
public List<InstrumentImage> saveImages(Instrument instrument, Iterable<MultipartFile> images) {
List<InstrumentImage> instrumentImages = fileService
.uploadFiles(images, INSTRUMENT_IMAGE_UPLOAD_PATH)
.stream()
.map(fileDto -> InstrumentImage.create(
instrument,
fileDto.getOriginalName(),
fileDto.getStoredName(),
fileDto.getUrl()
)).toList();
return instrumentImageRepository.saveAll(instrumentImages);
}

/**
* 특정 악기 매물의 모든 이미지를 삭제한다.
*
* @param instrumentId 이미지를 전체 삭제할 악기 매물의 id
*/
public void deleteAllByInstrumentId(Long instrumentId) {
List<InstrumentImage> instrumentImages = instrumentImageQueryService.findAllByInstrumentId(instrumentId);
fileService.deleteAll(
instrumentImages.stream()
.map(InstrumentImage::getStoredName)
.toList()
);
instrumentImageRepository.deleteAll(instrumentImages);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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.domain.instrument.entity.InstrumentImage;
import com.ajou.hertz.domain.instrument.repository.InstrumentImageRepository;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class InstrumentImageQueryService {

private final InstrumentImageRepository instrumentImageRepository;

/**
* 악기 id를 전달받고, 해당하는 악기의 이미지를 전부 조회한다.
*
* @param instrumentId 이미지를 조회하고자 하는 악기의 id
* @return 조회된 Instrument image entity list
*/
public List<InstrumentImage> findAllByInstrumentId(Long instrumentId) {
return instrumentImageRepository.findAllByInstrument_Id(instrumentId);
}
}
Loading
Loading