Skip to content

Commit

Permalink
Merge pull request #57 from Na-o-man/feat/#54/photo-download-api
Browse files Browse the repository at this point in the history
[FEAT] 사진 다운로드 API 구현
  • Loading branch information
jjeongdong authored Aug 4, 2024
2 parents 2b3a076 + f7ebea3 commit bb56e5d
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@
import com.umc.naoman.global.result.ResultResponse;
import com.umc.naoman.global.security.annotation.LoginMember;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand Down Expand Up @@ -57,8 +61,8 @@ public ResultResponse<PhotoResponse.PhotoUploadInfo> uploadPhotoList(@Valid @Req

@GetMapping("/all")
@Operation(summary = "특정 공유그룹의 전체 사진 조회 API", description = "특정 공유 그룹의 전체 사진을 조회하는 API입니다.")
public ResultResponse<PhotoResponse.PagedPhotoInfo> getAllPhotoListByShareGroup(@RequestParam Long shareGroupId, @LoginMember Member member,
@PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
public ResultResponse<PhotoResponse.PagedPhotoInfo> getAllPhotoListByShareGroup(@RequestParam Long shareGroupId, @PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable,
@LoginMember Member member) {
Page<Photo> allPhotoListByShareGroup = photoService.getAllPhotoList(shareGroupId, member, pageable);
return ResultResponse.of(RETRIEVE_PHOTO, photoConverter.toPhotoListInfo(allPhotoListByShareGroup));
}
Expand All @@ -70,4 +74,12 @@ public ResultResponse<PhotoResponse.PhotoDeleteInfo> deletePhotoList(@Valid @Req
List<Photo> photoList = photoService.deletePhotoList(request, member);
return ResultResponse.of(DELETE_PHOTO, photoConverter.toPhotoDeleteInfo(photoList));
}

@GetMapping("/download")
@Operation(summary = "사진 다운로드 API", description = "여러장의 사진을 다운로드할 주소를 받는 API입니다. 해당 공유그룹에 속해있는 회원만 다운로드 요청할 수 있습니다.")
public ResultResponse<PhotoResponse.PhotoDownloadUrlListInfo> getPhotoDownloadUrlList(@RequestParam List<Long> photoIdList, @RequestParam Long shareGroupId,
@LoginMember Member member) {
PhotoResponse.PhotoDownloadUrlListInfo photoDownloadUrlList = photoService.getPhotoDownloadUrlList(photoIdList, shareGroupId, member);
return ResultResponse.of(DOWNLOAD_PHOTO, photoDownloadUrlList);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,13 @@ public PhotoResponse.PhotoDeleteInfo toPhotoDeleteInfo(List<Photo> photoList) {
.build();
}

public PhotoResponse.PhotoDownloadUrlListInfo toPhotoDownloadUrlListInfo(List<Photo> photoList) {
List<String> photoDownloadUrlList = photoList.stream()
.map(Photo::getUrl)
.collect(Collectors.toList());

return PhotoResponse.PhotoDownloadUrlListInfo.builder()
.photoDownloadUrlList(photoDownloadUrlList)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;

import java.time.LocalDateTime;
import java.util.List;
Expand Down Expand Up @@ -68,4 +70,11 @@ public static class PhotoDeleteInfo {
private List<Long> photoIdList;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class PhotoDownloadUrlListInfo {
private List<String> photoDownloadUrlList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
public interface PhotoRepository extends JpaRepository<Photo, Long> {
Page<Photo> findAllByShareGroupId(Long shareGroupId, Pageable pageable);
List<Photo> findByIdInAndShareGroupId(List<Long> photoIdList, Long shareGroupId);
List<Photo> findByIdIn(List<Long> photoIdList);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.umc.naoman.domain.photo.dto.PhotoRequest;
import com.umc.naoman.domain.photo.dto.PhotoResponse;
import com.umc.naoman.domain.photo.entity.Photo;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

Expand All @@ -18,4 +19,6 @@ public interface PhotoService {
Page<Photo> getAllPhotoList(Long shareGroupId, Member member, Pageable pageable);

List<Photo> deletePhotoList(PhotoRequest.PhotoDeletedRequest request, Member member);

PhotoResponse.PhotoDownloadUrlListInfo getPhotoDownloadUrlList(List<Long> photoIdList, Long shareGroupId, Member member);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import com.umc.naoman.domain.photo.entity.Photo;
import com.umc.naoman.domain.photo.repository.PhotoRepository;
import com.umc.naoman.domain.shareGroup.entity.ShareGroup;
import com.umc.naoman.domain.shareGroup.repository.ProfileRepository;
import com.umc.naoman.domain.shareGroup.service.ShareGroupService;
import com.umc.naoman.global.error.BusinessException;
import io.awspring.cloud.s3.S3Template;
Expand Down Expand Up @@ -42,7 +41,6 @@ public class PhotoServiceImpl implements PhotoService {
private final PhotoRepository photoRepository;
private final ShareGroupService shareGroupService;
private final PhotoConverter photoConverter;
private final ProfileRepository profileRepository;

@Value("${spring.cloud.aws.s3.bucket}")
private String bucketName;
Expand All @@ -57,7 +55,7 @@ public class PhotoServiceImpl implements PhotoService {
@Override
@Transactional
public List<PhotoResponse.PreSignedUrlInfo> getPreSignedUrlList(PhotoRequest.PreSignedUrlRequest request, Member member) {
shareGroupService.findProfile(request.getShareGroupId(), member.getId());
validateShareGroupAndProfile(request.getShareGroupId(), member);

return request.getPhotoNameList().stream()
.map(this::getPreSignedUrl)
Expand Down Expand Up @@ -112,8 +110,8 @@ private String generateFileAccessUrl(String fileName) {
@Override
@Transactional
public PhotoResponse.PhotoUploadInfo uploadPhotoList(PhotoRequest.PhotoUploadRequest request, Member member) {
shareGroupService.findProfile(member.getId(), request.getShareGroupId());
ShareGroup shareGroup = shareGroupService.findShareGroup(request.getShareGroupId());
shareGroupService.findProfile(request.getShareGroupId(), member.getId());
int uploadCount = 0;

for (String photoUrl : request.getPhotoUrlList()) {
Expand Down Expand Up @@ -156,15 +154,15 @@ public Page<Photo> getAllPhotoList(Long shareGroupId, Member member, Pageable pa
@Override
@Transactional
public List<Photo> deletePhotoList(PhotoRequest.PhotoDeletedRequest request, Member member) {
// 멤버가 해당 공유 그룹에 대한 권한이 있는지 확인
shareGroupService.findProfile(member.getId(), request.getShareGroupId());
validateShareGroupAndProfile(request.getShareGroupId(), member);

// 요청된 사진 ID 목록과 공유 그룹 ID를 기반으로 사진 목록 조회
List<Photo> photoList = photoRepository.findByIdInAndShareGroupId(request.getPhotoIdList(), request.getShareGroupId());

// 사진 목록 크기 검증
if (photoList.size() != request.getPhotoIdList().size()) {
throw new BusinessException(PHOTO_NOT_FOUND); // 요청한 사진이 일부 또는 전부 없을 경우 예외 발생
// 요청한 사진이 일부 또는 전부 없을 경우 예외 발생
throw new BusinessException(PHOTO_NOT_FOUND);
}

// 각 사진에 대해 S3에서 객체 삭제 및 데이터베이스에서 삭제
Expand All @@ -185,4 +183,24 @@ private void deletePhoto(Photo photo) {
photoRepository.delete(photo);
}

@Override
@Transactional(readOnly = true)
public PhotoResponse.PhotoDownloadUrlListInfo getPhotoDownloadUrlList(List<Long> photoIdList, Long shareGroupId, Member member) {
validateShareGroupAndProfile(shareGroupId, member);
List<Photo> photoList = photoRepository.findByIdIn(photoIdList);

if (photoList.size() != photoIdList.size()) {
// 요청한 사진이 일부 또는 전부 없을 경우 예외 발생
throw new BusinessException(PHOTO_NOT_FOUND);
}

return photoConverter.toPhotoDownloadUrlListInfo(photoList);
}

private void validateShareGroupAndProfile(Long shareGroupId, Member member) {
// 해당 공유 그룹이 존재하는지 확인
shareGroupService.findShareGroup(shareGroupId);
// 멤버가 해당 공유 그룹에 속해있는지 확인
shareGroupService.findProfile(shareGroupId, member.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public enum S3ErrorCode implements ErrorCode {
UNAUTHORIZED_GET(403, "ES3000", "사진을 조회할 권한이 없습니다."),
UNAUTHORIZED_DELETE(403, "ES3000", "사진을 삭제할 권한이 없습니다."),
UNAUTHORIZED_UPLOAD(403, "ES3000", "사진을 업로드할 권한이 없습니다."),
PHOTO_NOT_FOUND(404, "ES3005", "요청한 사진이 존재하지 않습니다."),
PHOTO_NOT_FOUND(404, "ES3000", "요청한 사진이 존재하지 않습니다."),
FAILED_DOWNLOAD_PHOTO(500, "ES3000", "사진을 다운로드하는 도중 문제가 발생하였습니다.")

;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ public static <T> ResultResponse<T> of(ResultCode resultCode) {
.data(null)
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public enum PhotoResultCode implements ResultCode {
CREATE_PRESIGNED_URL(200, "SP000", "성공적으로 Presigned URL을 요청하였습니다."),
UPLOAD_PHOTO(200, "SP000", "성공적으로 이미지를 업로드하였습니다."),
RETRIEVE_PHOTO(200, "SP000", "성공적으로 이미지를 조회하였습니다."),
DELETE_PHOTO(200, "SP000", "성공적으로 이미지를 삭제하였습니다.")
DELETE_PHOTO(200, "SP000", "성공적으로 이미지를 삭제하였습니다."),
DOWNLOAD_PHOTO(200, "SP000", "성공적으로 이미지를 디운로드하였습니다.")

;
private final int status;
Expand Down

0 comments on commit bb56e5d

Please sign in to comment.