Skip to content

Commit

Permalink
Merge pull request #61 from YAPP-Github/feature/MAFOO-101
Browse files Browse the repository at this point in the history
[MAFOO-101] feat: 사진 순서 관련 API 기능 추가
  • Loading branch information
gmkim20713 authored Aug 24, 2024
2 parents 587ddaa + 7c08b08 commit e324505
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 23 deletions.
19 changes: 18 additions & 1 deletion photo-service/src/main/java/kr/mafoo/photo/api/PhotoApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import kr.mafoo.photo.controller.dto.request.PhotoCreateRequest;
import kr.mafoo.photo.controller.dto.request.PhotoBulkUpdateAlbumIdRequest;
import kr.mafoo.photo.controller.dto.request.PhotoUpdateAlbumIdRequest;
import kr.mafoo.photo.controller.dto.request.PhotoUpdateDisplayIndexRequest;
import kr.mafoo.photo.controller.dto.response.PhotoResponse;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
Expand Down Expand Up @@ -57,7 +58,7 @@ Mono<PhotoResponse> updatePhotoAlbum(
@RequestBody
PhotoUpdateAlbumIdRequest request
);

@Operation(summary = "사진 앨범 n건 수정", description = "사진 여러 개를 다른 앨범으로 이동시킵니다.")
@PatchMapping("/bulk/album")
Flux<PhotoResponse> updatePhotoBulkAlbum(
Expand All @@ -69,6 +70,22 @@ Flux<PhotoResponse> updatePhotoBulkAlbum(
PhotoBulkUpdateAlbumIdRequest request
);

@Operation(summary = "사진 표시 순서 변경", description = "사진의 표시 순서를 변경합니다.")
@PatchMapping("/{photoId}/display-index")
Mono<PhotoResponse> updatePhotoDisplayIndex(
@RequestMemberId
String memberId,

@ULID
@Parameter(description = "사진 ID", example = "test_photo_id")
@PathVariable
String photoId,

@Valid
@RequestBody
PhotoUpdateDisplayIndexRequest request
);

@Operation(summary = "사진 삭제", description = "사진을 삭제합니다.")
@DeleteMapping("{photoId}")
Mono<Void> deletePhoto(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import kr.mafoo.photo.controller.dto.request.PhotoCreateRequest;
import kr.mafoo.photo.controller.dto.request.PhotoBulkUpdateAlbumIdRequest;
import kr.mafoo.photo.controller.dto.request.PhotoUpdateAlbumIdRequest;
import kr.mafoo.photo.controller.dto.request.PhotoUpdateDisplayIndexRequest;
import kr.mafoo.photo.controller.dto.response.PhotoResponse;
import kr.mafoo.photo.service.PhotoService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;


@RequiredArgsConstructor
@RestController
public class PhotoController implements PhotoApi {
Expand Down Expand Up @@ -48,7 +48,7 @@ public Mono<PhotoResponse> updatePhotoAlbum(
.updatePhotoAlbumId(photoId, request.albumId(), memberId)
.map(PhotoResponse::fromEntity);
}

@Override
public Flux<PhotoResponse> updatePhotoBulkAlbum(
String memberId,
Expand All @@ -59,6 +59,16 @@ public Flux<PhotoResponse> updatePhotoBulkAlbum(
.map(PhotoResponse::fromEntity);
}

@Override
public Mono<PhotoResponse> updatePhotoDisplayIndex(
String memberId,
String photoId,
PhotoUpdateDisplayIndexRequest request
) {
return photoService
.updatePhotoDisplayIndex(photoId, request.newDisplayIndex(), memberId)
.map(PhotoResponse::fromEntity);
}

@Override
public Mono<Void> deletePhoto(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package kr.mafoo.photo.controller.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

@Schema(description = "사진 인덱스 수정 요청")
public record PhotoUpdateDisplayIndexRequest(
@NotNull
@Schema(description = "사진 표시 인덱스", example = "photo_display_index")
Integer newDisplayIndex
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public class PhotoEntity implements Persistable<String> {
@Column("album_id")
private String albumId;

@Column("display_index")
private Integer displayIndex;

@CreatedDate
@Column("created_at")
private LocalDateTime createdAt;
Expand Down Expand Up @@ -76,6 +79,11 @@ public PhotoEntity updateAlbumId(String albumId) {
return this;
}

public PhotoEntity updateDisplayIndex(Integer displayIndex) {
this.displayIndex = displayIndex;
return this;
}

public static PhotoEntity newPhoto(String photoId, String photoUrl, BrandType brandType, String ownerMemberId) {
PhotoEntity photo = new PhotoEntity();
photo.photoId = photoId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ public enum ErrorCode {
ALBUM_NOT_FOUND("AE0001", "앨범을 찾을 수 없습니다"),
ALBUM_DISPLAY_INDEX_IS_SAME("AE0002", "옮기려는 대상 앨범 인덱스가 같습니다"),
PHOTO_NOT_FOUND("PE0001", "사진을 찾을 수 없습니다"),
PHOTO_BRAND_NOT_EXISTS("PE002", "사진 브랜드가 존재하지 않습니다"),
PHOTO_QR_URL_EXPIRED("PE003", "사진 저장을 위한 QR URL이 만료되었습니다"),
PHOTO_BRAND_NOT_EXISTS("PE0002", "사진 브랜드가 존재하지 않습니다"),
PHOTO_QR_URL_EXPIRED("PE0003", "사진 저장을 위한 QR URL이 만료되었습니다"),
PHOTO_DISPLAY_INDEX_IS_SAME("PE0004", "옮기려는 대상 사진 인덱스가 같습니다"),
PHOTO_DISPLAY_INDEX_NOT_VALID("PE0005", "옮기려는 대상 사진 인덱스가 유효하지 않습니다"),

;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kr.mafoo.photo.exception;

public class PhotoDisplayIndexIsSameException extends DomainException {
public PhotoDisplayIndexIsSameException() {
super(ErrorCode.PHOTO_DISPLAY_INDEX_IS_SAME);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kr.mafoo.photo.exception;

public class PhotoDisplayIndexNotValidException extends DomainException {
public PhotoDisplayIndexNotValidException() {
super(ErrorCode.PHOTO_DISPLAY_INDEX_NOT_VALID);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
package kr.mafoo.photo.repository;

import kr.mafoo.photo.domain.PhotoEntity;
import org.springframework.data.r2dbc.repository.Modifying;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface PhotoRepository extends R2dbcRepository<PhotoEntity, String> {
Flux<PhotoEntity> findAllByAlbumId(String ownerAlbumId);
Flux<PhotoEntity> findAllByAlbumIdOrderByDisplayIndexDesc(String ownerAlbumId);

@Modifying
@Query("UPDATE photo SET display_index = display_index - 1 WHERE album_id = :albumId AND display_index > :startIndex")
Mono<Void> popDisplayIndexGreaterThan(String albumId, int startIndex);

@Modifying
@Query("UPDATE photo SET display_index = display_index - 1 WHERE album_id = :albumId AND display_index BETWEEN :startIndex AND :endIndex")
Mono<Void> popDisplayIndexBetween(String albumId, int startIndex, int endIndex);

@Modifying
@Query("UPDATE photo SET display_index = display_index + 1 WHERE album_id = :albumId AND display_index BETWEEN :startIndex AND :endIndex")
Mono<Void> pushDisplayIndexBetween(String albumId, int startIndex, int endIndex);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import kr.mafoo.photo.domain.PhotoEntity;
import kr.mafoo.photo.exception.AlbumNotFoundException;
import kr.mafoo.photo.exception.PhotoDisplayIndexIsSameException;
import kr.mafoo.photo.exception.PhotoDisplayIndexNotValidException;
import kr.mafoo.photo.exception.PhotoNotFoundException;
import kr.mafoo.photo.repository.AlbumRepository;
import kr.mafoo.photo.repository.PhotoRepository;
Expand Down Expand Up @@ -45,7 +47,7 @@ public Flux<PhotoEntity> findAllByAlbumId(String albumId, String requestMemberId
// 내 앨범이 아니면 그냥 없는 앨범 처리
return Mono.error(new AlbumNotFoundException());
} else {
return photoRepository.findAllByAlbumId(albumId);
return photoRepository.findAllByAlbumIdOrderByDisplayIndexDesc(albumId);
}
});
}
Expand All @@ -60,8 +62,9 @@ public Mono<Void> deletePhotoById(String photoId, String requestMemberId) {
// 내 사진이 아니면 그냥 없는 사진 처리
return Mono.error(new PhotoNotFoundException());
} else {
return photoRepository.deleteById(photoId)
.then(albumService.decreaseAlbumPhotoCount(photoEntity.getAlbumId(), requestMemberId));
return albumService.decreaseAlbumPhotoCount(photoEntity.getAlbumId(), requestMemberId)
.then(photoRepository.popDisplayIndexGreaterThan(photoEntity.getAlbumId(), photoEntity.getDisplayIndex()))
.then(photoRepository.deleteById(photoId));
}
});
}
Expand All @@ -88,22 +91,64 @@ public Mono<PhotoEntity> updatePhotoAlbumId(String photoId, String albumId, Stri
if (!photoEntity.getOwnerMemberId().equals(requestMemberId)) {
// 내 사진이 아니면 그냥 없는 사진 처리
return Mono.error(new PhotoNotFoundException());
} else {
return albumRepository
.findById(albumId)
.switchIfEmpty(Mono.error(new AlbumNotFoundException()))
.flatMap(albumEntity -> {
if (!albumEntity.getOwnerMemberId().equals(requestMemberId)) {
// 내 앨범이 아니면 그냥 없는 앨범 처리
return Mono.error(new AlbumNotFoundException());
} else {
return albumService.decreaseAlbumPhotoCount(photoEntity.getAlbumId(), requestMemberId)
.then(photoRepository.popDisplayIndexGreaterThan(photoEntity.getAlbumId(), photoEntity.getDisplayIndex()))
.then(albumService.increaseAlbumPhotoCount(albumId, requestMemberId))
.then(photoRepository.save(
photoEntity
.updateAlbumId(albumId)
.updateDisplayIndex(albumEntity.getPhotoCount())
));
}
});
}

return albumRepository
.findById(albumId)
.switchIfEmpty(Mono.error(new AlbumNotFoundException()))
.flatMap(albumEntity -> {
if (!albumEntity.getOwnerMemberId().equals(requestMemberId)) {
// 내 앨범이 아니면 그냥 없는 앨범 처리
return Mono.error(new AlbumNotFoundException());
}

return albumService.decreaseAlbumPhotoCount(photoEntity.getAlbumId(), requestMemberId)
.then(albumService.increaseAlbumPhotoCount(albumId, requestMemberId))
.then(photoRepository.save(photoEntity.updateAlbumId(albumId)));
});
});
}

@Transactional
public Mono<PhotoEntity> updatePhotoDisplayIndex(String photoId, Integer newIndex, String requestMemberId) {
return photoRepository
.findById(photoId)
.switchIfEmpty(Mono.error(new PhotoNotFoundException()))
.flatMap(photoEntity -> albumRepository
.findById(photoEntity.getAlbumId())
.switchIfEmpty(Mono.error(new AlbumNotFoundException()))
.flatMap(albumEntity -> {

int targetIndex = albumEntity.getPhotoCount() - newIndex - 1;

if (!albumEntity.getOwnerMemberId().equals(requestMemberId)) {
return Mono.error(new AlbumNotFoundException());
}

if (photoEntity.getDisplayIndex().equals(targetIndex)) {
return Mono.error(new PhotoDisplayIndexIsSameException());
}

if (targetIndex < 0 || targetIndex >= albumEntity.getPhotoCount()) {
return Mono.error(new PhotoDisplayIndexNotValidException());
}

if (photoEntity.getDisplayIndex() < targetIndex) {
return photoRepository
.popDisplayIndexBetween(photoEntity.getAlbumId(), photoEntity.getDisplayIndex() + 1, targetIndex)
.then(photoRepository.save(photoEntity.updateDisplayIndex(targetIndex)));
} else {
return photoRepository
.pushDisplayIndexBetween(photoEntity.getAlbumId(), targetIndex, photoEntity.getDisplayIndex() - 1)
.then(photoRepository.save(photoEntity.updateDisplayIndex(targetIndex)));
}

}));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
ALTER TABLE `photo`
ADD `display_index` INTEGER COMMENT '표시순' AFTER `album_id`;

CREATE INDEX `idx_photo_display_index` ON `photo`(`display_index`);

WITH RankedPhotos AS (
SELECT
id,
owner_member_id,
album_id,
CASE
WHEN album_id IS NOT NULL THEN ROW_NUMBER() OVER (PARTITION BY owner_member_id, album_id ORDER BY created_at) - 1
ELSE NULL
END AS new_index
FROM photo
)
UPDATE photo
JOIN RankedPhotos
ON photo.id = RankedPhotos.id
SET photo.display_index = RankedPhotos.new_index;

0 comments on commit e324505

Please sign in to comment.