Skip to content

Commit

Permalink
[release] 배포합니다. (#135)
Browse files Browse the repository at this point in the history
* style [#56] 엔터 추가

* feat [#56] 콘서트 정보 조회 API 구현

* refactor [#62] 매개변수가 하나인 함수 이름을 from으로 변경

* refactor [#62] 작성된 예외 처리 클래스로 변경

* hotfix [#69] ArtistResolver에서 같은 객체를 계속 탐색하는 것을 수정

* fix [#76] ArtistResolver의 load 함수에서 target이 리스트 타입인 경우 탐색 가능하도록 수정

* style [#76] print 함수 제거

* hotfix [#81] ArtistResolver 수동으로 매핑하는 방법으로 변경

* style [#81] 주석 추가

* feat [#78] 임시 페스티벌 생성 API 구현

* feat [#57] ArtistFavoriteController 구현

* feat [#57] UserFavoriteFacade 구현

* feat [#57] ArtistFavoriteService, Repository 구현

* feat [#57] 아티스트 목록 최대 3개만 조회하도록 제한

* refactor [#57] 메서드 of->from 변경

* refactor [#57] 인자로 user이 아닌 userId 불러오도록 변경

* style [#57] 필요없는 코드 삭제

* fix [#57] 메서드 of->from 변경

* feat [#67] 페스티벌 정보 조회 API 구현 (#84)

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* chore [#67] DB 쿼리 값 확인을 위해 p6spy 의존성 추가

* feat [#67] 값 전달을 위해 DTO 생성

* refactor [#67] 값 타입 변경을 위해 엔티티 파일 수정

* fix [#67] artistIds 아이디 수집 변수에 Artist Id 값을 추가하는 것으로 변경

* feat [#85] 아티스트 검색 API 구현

* refactor [#87] 콘서트 정보 조회 API 변경에 따른 수정

* refactor [#89] 함수명을 명확히 수정, 조회 함수에 트랜잭션 readonly = true 추가

* feat [#89] 아티스트 좋아요 추가 API 구현

* refactor [#89] addArtistFavorite에서 artistId 최소값 제거

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 구현 (#95)

* feat [#91] 콘서트 좋아요 추가 API 추가

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 추가

* style [#92] 함수 위치 재배치

* feat [#92] 콘서트 좋아요 삭제 API 추가 (#97)

* feat [#92] 콘서트 좋아요 삭제 API 추가

* refactor [#92] 유저 서비스에 트랜잭션 추가 및 에러 응답 Unauthorized 설정 및 중복된 함수 제거

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 구현 (#95)

* feat [#91] 콘서트 좋아요 추가 API 추가

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 추가

* style [#92] 함수 위치 재배치

* feat [#92] 콘서트 좋아요 삭제 API 추가

* refactor [#92] 유저 서비스에 트랜잭션 추가 및 에러 응답 Unauthorized 설정 및 중복된 함수 제거

* feat [#92] 아티스트 접기/펼치기 기준 값 상수 추가

* refactor [#92] 아티스트 접기/펼치기 기준 값 상수 사용하도록 변경

* feat [#98] CORS 허용 도메인 추가

* refactor [#100] DateConvertor static 클래스로 변경

* feat [#83] 타임테이블에 등록된 페스티벌, 날짜 조회 (#96)

* feat [#67] 페스티벌 정보 조회 API 구현 (#84)

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* chore [#67] DB 쿼리 값 확인을 위해 p6spy 의존성 추가

* feat [#67] 값 전달을 위해 DTO 생성

* refactor [#67] 값 타입 변경을 위해 엔티티 파일 수정

* fix [#67] artistIds 아이디 수집 변수에 Artist Id 값을 추가하는 것으로 변경

* feat [#83] UserTimetable 컨트롤러 구현

* feat [#83] UserTimetableResponse api 응답값 구현

* feat [#83] UserTimetableFacade 구현

* feat [#83] UserTimetable Dto 구현

* feat [#83] UserTimetable service 구현

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 구현 (#95)

* feat [#91] 콘서트 좋아요 추가 API 추가

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 추가

* style [#92] 함수 위치 재배치

* feat [#92] 콘서트 좋아요 삭제 API 추가 (#97)

* feat [#92] 콘서트 좋아요 삭제 API 추가

* refactor [#92] 유저 서비스에 트랜잭션 추가 및 에러 응답 Unauthorized 설정 및 중복된 함수 제거

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 구현 (#95)

* feat [#91] 콘서트 좋아요 추가 API 추가

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 추가

* style [#92] 함수 위치 재배치

* feat [#92] 콘서트 좋아요 삭제 API 추가

* refactor [#92] 유저 서비스에 트랜잭션 추가 및 에러 응답 Unauthorized 설정 및 중복된 함수 제거

* feat [#92] 아티스트 접기/펼치기 기준 값 상수 추가

* refactor [#92] 아티스트 접기/펼치기 기준 값 상수 사용하도록 변경

* feat [#98] CORS 허용 도메인 추가

* refactor [#100] DateConvertor static 클래스로 변경

* feat [#67] 페스티벌 정보 조회 API 구현 (#84)

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* feat [#67] 페스티벌 정보 조회 API 응답 DTO 생성

* feat [#67] 페스티벌 정보 조회 API 구현

* chore [#67] DB 쿼리 값 확인을 위해 p6spy 의존성 추가

* feat [#67] 값 전달을 위해 DTO 생성

* refactor [#67] 값 타입 변경을 위해 엔티티 파일 수정

* fix [#67] artistIds 아이디 수집 변수에 Artist Id 값을 추가하는 것으로 변경

* feat [#83] UserTimetable 컨트롤러 구현

* feat [#83] UserTimetableResponse api 응답값 구현

* feat [#83] UserTimetableFacade 구현

* feat [#83] UserTimetable Dto 구현

* feat [#83] UserTimetable service 구현

* 작업중

* feat [#83] 날짜 포맷팅 설정 추가

* feat [#83] s3핸들러 설정 추가

* style [#83] 오타 수정

---------

Co-authored-by: chyun <[email protected]>

* refactor [#105] 날짜 매핑 방법 변경

* refactor [#105] 날짜 매핑 방법 변경

* feat [#107] existsById 반환 값 boolean으로 변경 및 타임테이블 페스티벌 삭제 API 구현

* feat [#109] 공연(콘서트, 페스티벌) 정보 조회 날짜가 지나지 않았는지 검증 추가

* refactor [#109] 공연 좋아요 여부 조회 로직 분리

* refactor [#109] 공연, 아티스트 좋아요 여부 조회 로직 분리

* refactor [#109] 공연, 아티스트, 유저 존재 여부 조회 로직 분리

* style [#109] 주석 추가

* refactor [#109] 엔티티 존재 여부 메소드 분리

* fix [#109] 지난 페스티벌을 제외하고 타임테이블에 추가된 페스티벌을 조회하는 기능 추가

* style [#109] 불필요한 import 제거

* feat [#111] 타임테이블에 페스티벌 추가 API 구현 (#112)

* feat [#111] 타임테이블 페스티벌 추가 요청값 생성

* feat [#111] 요청값 전달 DTO 작성

* feat [#111] 타임테이블에 페스티벌 추가 API 구현

* feat [#111] 타임테이블 에러 응답 메세지 작성

* refactor [#111] 에러 응답 메세지 @Getter 사용으로 변경

* refactor [#111] 현재 페스티벌과 추가할 페스티벌 변수 위치 변경

* refactor [#111] 사용하지않는 함수 제거

* refactor [#111] 에러 메시지에 @Getter 사용 및 Enum 타입 생성자에 private 접근지정자 제거

* feat [#109] 공연(콘서트, 페스티벌) 정보 조회 날짜가 지나지 않았는지 검증 추가

* refactor [#109] 공연 좋아요 여부 조회 로직 분리

* refactor [#109] 공연, 아티스트 좋아요 여부 조회 로직 분리

* refactor [#109] 공연, 아티스트, 유저 존재 여부 조회 로직 분리

* style [#109] 주석 추가

* refactor [#109] 엔티티 존재 여부 메소드 분리

* fix [#109] 지난 페스티벌을 제외하고 타임테이블에 추가된 페스티벌을 조회하는 기능 추가

* style [#109] 불필요한 import 제거

* feat [#111] 타임테이블 페스티벌 추가 요청값 생성

* feat [#111] 요청값 전달 DTO 작성

* feat [#111] 타임테이블에 페스티벌 추가 API 구현

* feat [#111] 타임테이블 에러 응답 메세지 작성

* refactor [#111] 에러 응답 메세지 @Getter 사용으로 변경

* refactor [#111] 현재 페스티벌과 추가할 페스티벌 변수 위치 변경

* refactor [#111] 사용하지않는 함수 제거

* refactor [#111] 에러 메시지에 @Getter 사용 및 Enum 타입 생성자에 private 접근지정자 제거

* fix [#111] TimetableFestivalRepository에 findByUserId 함수 추가

* refactor [#111] 타임테이블에 더 이상 추가할 수 없을 때 에러 메세지 변경

* fix [#114] SpotifyAPIHandler 예외 처리 수정

* refactor [#113] 메세지 파일 롬복 어노테이션 사용하도록 변경

* feat [#113] 파사드 계층 관심 공연 DTO 작성

* feat [#113] 공연 타입 상수 파일 작성

* feat [#113] 작업 중간 저장

* feat [#113] 관심 공연 리스트 조회 API 구현

* feat [#117] 아티스트 좋아요 추가 API에 아티스트 아이디가 존재하는지 검증하는 로직 추가

* feat [#117] ConfetiException 글로벌 핸들러 추가

* feat [#117] 아티스트 아이디가 잘못되었을 때도 Not Found로 변경

* fix [#121] 토큰 재발급 로직 추가

* fix [#119] 타임테이블에 페스티벌 추가 시 선택 여부 값 추가

* refactor [#119] ERD 구조 변경에 따른 엔티티 파일 수정

* refactor [#119] 페스티벌 조회 성능 최적화

* refactor [#119] 패치 조인 쿼리 수정

* fix [#119] 타임테이블에 페스티벌 추가/삭제 시 선택 여부 값 추가/삭제 및 ERD 수정에 따른 엔티티 변경

* fix [#127] SpotifyAPIHandler 리프레시 토큰 재발급 로직 재구성 및 마지막 발매 일자 가져오는 방법 변경

* style [#127] 사용하지 않는 변수 제거

* feat [#41] PerformanceController, Response 구현

* feat [#41] PerformanceFacade 구현

* feat [#41] PerformanceDTO 구현

* feat [#41] PerformanceService, Repository 구현

* refactor [#41] 코드리뷰 반영

* feat [#126] 커서 정보 담을 DTO 작성

* feat [#126] 추가할 페스티벌 응답 Response 작성

* feat [#126] 커서 베이스 페이징에 사용할 데이터 DTO 생성

* feat [#126] 커서 베이스 페이징 쿼리 작성

* feat [#126] 커서 베이스 페이징 응답 DTO 생성

* feat [#126] 커서 베이스 페이징 데이터 규격 유틸 작성

* feat [#126] 예정된 페스티벌 목록 조회 API 구현

* refactor [#126] API 명세서에 따라 사이즈 값을 서버에서 설정하도록 변경

* refactor [#126] 커서 대상 페스티벌 조회

---------

Co-authored-by: ivoryeee <[email protected]>
Co-authored-by: Ivoryeee <[email protected]>
  • Loading branch information
3 people authored Jan 22, 2025
1 parent 7f135b0 commit 5eb2787
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,17 @@ public class UserFavoriteController {
private final S3FileHandler s3FileHandler;

@PostMapping("/festivals/{festivalId}")
public ResponseEntity<BaseResponse<?>> postFavoriteFestival(@RequestHeader("Authorization") Long userId, @PathVariable
@Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) {
public ResponseEntity<BaseResponse<?>> postFavoriteFestival(
@RequestHeader("Authorization") Long userId,
@PathVariable(name = "festivalId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) {
userFavoriteFacade.addFestivalFavorite(userId, festivalId);
return ApiResponseUtil.success(SuccessMessage.SUCCESS);
}

@DeleteMapping("/festivals/{festivalId}")
public ResponseEntity<BaseResponse<?>> deleteFavoriteFestival(@RequestHeader("Authorization") Long userId, @PathVariable
@Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) {
public ResponseEntity<BaseResponse<?>> deleteFavoriteFestival(
@RequestHeader("Authorization") Long userId,
@PathVariable(name = "festivalId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) {
userFavoriteFacade.removeFestivalFavorite(userId, festivalId);
return ApiResponseUtil.success(SuccessMessage.SUCCESS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.sopt.confeti.api.user.dto.request.AddTimetableFestivalRequest;
import org.sopt.confeti.api.user.dto.response.TimetablesToAddResponse;
import org.sopt.confeti.api.user.dto.response.UserTimetableDetailResponse;
import org.sopt.confeti.api.user.facade.UserTimetableFacade;
import org.sopt.confeti.api.user.facade.dto.request.AddTimetableFestivalDTO;
import org.sopt.confeti.api.user.facade.dto.response.TimetableToAddDTO;
import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO;
import org.sopt.confeti.global.common.BaseResponse;
import org.sopt.confeti.global.common.CursorPage;
import org.sopt.confeti.global.message.SuccessMessage;
import org.sopt.confeti.global.util.ApiResponseUtil;
import org.sopt.confeti.global.util.S3FileHandler;
Expand All @@ -20,13 +23,15 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated
@RequiredArgsConstructor
@RequestMapping("/user/timetables/festivals")
public class UserTimetableController {

private final UserTimetableFacade userTimetableFacade;
private final S3FileHandler s3FileHandler;

Expand All @@ -36,6 +41,15 @@ public ResponseEntity<BaseResponse<?>> getTimetablesListAndDate(@RequestHeader("
return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserTimetableDetailResponse.of(userTimetableDTO, s3FileHandler));
}

@GetMapping("/add")
public ResponseEntity<BaseResponse<?>> getTimetablesToAdd(
@RequestHeader("Authorization") long userId,
@RequestParam(name = "cursor", required = false) Long cursor
) {
CursorPage<TimetableToAddDTO> timetablesToAdd = userTimetableFacade.getTimetablesToAdd(userId, cursor);
return ApiResponseUtil.success(SuccessMessage.SUCCESS, TimetablesToAddResponse.of(timetablesToAdd, s3FileHandler));
}

@PostMapping
public ResponseEntity<BaseResponse<?>> addTimetableFestival(
@RequestHeader("Authorization") long userId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.sopt.confeti.api.user.dto.response;

import org.sopt.confeti.api.user.facade.dto.response.TimetableToAddDTO;
import org.sopt.confeti.global.util.S3FileHandler;

public record TimetablesToAddFestivalResponse(
long festivalId,
String posterUrl,
String title
) {
public static TimetablesToAddFestivalResponse of(final TimetableToAddDTO timetableToAddDTO, final S3FileHandler s3FileHandler) {
return new TimetablesToAddFestivalResponse (
timetableToAddDTO.festivalId(),
s3FileHandler.getFileUrl(timetableToAddDTO.posterPath()),
timetableToAddDTO.title()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.sopt.confeti.api.user.dto.response;

import java.util.List;
import org.sopt.confeti.api.user.facade.dto.response.TimetableToAddDTO;
import org.sopt.confeti.global.common.CursorPage;
import org.sopt.confeti.global.util.S3FileHandler;

public record TimetablesToAddResponse(
long nextCursor,
List<TimetablesToAddFestivalResponse> festivals
) {
private static final long DEFAULT_NEXT_CURSOR = -1L;

public static TimetablesToAddResponse of(final CursorPage<TimetableToAddDTO> cursorPage, final S3FileHandler s3FileHandler) {
Long nextCursor = DEFAULT_NEXT_CURSOR;

if (!cursorPage.isLast()) {
nextCursor = cursorPage.getNextCursor().festivalId();
}

return new TimetablesToAddResponse(
nextCursor,
cursorPage.getItems().stream()
.map(timetableToAddDTO -> TimetablesToAddFestivalResponse.of(timetableToAddDTO, s3FileHandler))
.toList()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
import org.sopt.confeti.annotation.Facade;
import org.sopt.confeti.api.user.facade.dto.request.AddTimetableFestivalArtiestDTO;
import org.sopt.confeti.api.user.facade.dto.request.AddTimetableFestivalDTO;
import org.sopt.confeti.api.user.facade.dto.response.TimetableToAddDTO;
import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO;
import org.sopt.confeti.domain.festival.Festival;
import org.sopt.confeti.domain.festival.application.FestivalService;
import org.sopt.confeti.domain.festival.application.dto.FestivalCursorDTO;
import org.sopt.confeti.domain.timetablefestival.TimetableFestival;
import org.sopt.confeti.domain.timetablefestival.application.TimetableFestivalService;
import org.sopt.confeti.domain.user.User;
import org.sopt.confeti.domain.user.application.UserService;
import org.sopt.confeti.global.common.CursorPage;
import org.sopt.confeti.global.exception.ConflictException;
import org.sopt.confeti.global.exception.NotFoundException;
import org.sopt.confeti.global.exception.UnauthorizedException;
Expand All @@ -25,6 +28,8 @@
public class UserTimetableFacade {

private static final int TIMETABLE_FESTIVAL_COUNT_MAXIMUM = 3;
private static final int NEXT_CURSOR_SIZE = 1;
private static final int TIMETABLE_FESTIVALS_TO_ADD_SIZE = 6 + NEXT_CURSOR_SIZE;

private final UserService userService;
private final TimetableFestivalService timetableFestivalService;
Expand Down Expand Up @@ -106,5 +111,37 @@ protected void validateExistTimetableFestival(final long userId, final long fest
throw new NotFoundException(ErrorMessage.NOT_FOUND);
}
}

@Transactional(readOnly = true)
public CursorPage<TimetableToAddDTO> getTimetablesToAdd(final long userId, final Long cursor) {
if (cursor == null) {
List<Festival> festivals = festivalService.findFestivalsUsingInitCursor(userId, TIMETABLE_FESTIVALS_TO_ADD_SIZE);
return CursorPage.of(
festivals.stream()
.map(TimetableToAddDTO::from)
.toList(),
TIMETABLE_FESTIVALS_TO_ADD_SIZE
);
}

// 커서 값 조회
FestivalCursorDTO festivalCursorDTO = getFestivalCursor(userId, cursor);

List<Festival> festivals = festivalService.findFestivalsUsingCursor(userId, festivalCursorDTO.cursorTitle(), festivalCursorDTO.cursorIsFavorite(), TIMETABLE_FESTIVALS_TO_ADD_SIZE);
return CursorPage.of(
festivals.stream()
.map(TimetableToAddDTO::from)
.toList(),
TIMETABLE_FESTIVALS_TO_ADD_SIZE
);
}

@Transactional(readOnly = true)
public FestivalCursorDTO getFestivalCursor(final long userId, final long cursor) {
return festivalService.findFestivalCursor(userId, cursor)
.orElseThrow(
() -> new NotFoundException(ErrorMessage.NOT_FOUND)
);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.sopt.confeti.api.user.facade.dto.response;

import org.sopt.confeti.domain.festival.Festival;

public record TimetableToAddDTO(
long festivalId,
String posterPath,
String title
) {
public static TimetableToAddDTO from(final Festival festival) {
return new TimetableToAddDTO(
festival.getId(),
festival.getFestivalPosterPath(),
festival.getFestivalTitle()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package org.sopt.confeti.domain.festival.application;

import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO;
import org.sopt.confeti.domain.festival.Festival;
import org.sopt.confeti.domain.festival.application.dto.FestivalCursorDTO;
import org.sopt.confeti.domain.festival.infra.repository.FestivalRepository;
import org.sopt.confeti.global.exception.NotFoundException;
import org.sopt.confeti.global.message.ErrorMessage;
import org.sopt.confeti.global.util.artistsearcher.ArtistResolver;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -18,6 +23,9 @@ public class FestivalService {
private final FestivalRepository festivalRepository;
private final ArtistResolver artistResolver;

private static final int INIT_PAGE = 0;
private static final String FESTIVAL_TITLE_COLUMN_NAME = "festivalTitle";

@Transactional(readOnly = true)
public Festival findById(Long festivalId) {
Festival festival = festivalRepository.findById(festivalId)
Expand Down Expand Up @@ -50,4 +58,42 @@ public boolean existsById(final long festivalId) {
public List<Festival> findFestivalsByIdIn(final List<Long> festivalIds) {
return festivalRepository.findFestivalsByIdIn(festivalIds);
}

@Transactional(readOnly = true)
public List<Festival> findFestivalsUsingInitCursor(final long userId, final int size) {
return festivalRepository.findFestivalsUsingInitCursor(
userId,
getPageRequestWithSort(size, getFestivalSort())
);
}

private PageRequest getPageRequestWithSort(final int size, final Sort sort) {
return PageRequest.of(INIT_PAGE, size, sort);
}

private Sort getFestivalSort() {
return Sort.by(
Order.asc(FESTIVAL_TITLE_COLUMN_NAME)
);
}

@Transactional(readOnly = true)
public List<Festival> findFestivalsUsingCursor(
final long userId,
final String cursorTitle,
final boolean cursorIsFavorite,
final int size
) {
return festivalRepository.findFestivalsUsingCursor(
userId,
cursorTitle,
cursorIsFavorite,
getPageRequestWithSort(size, getFestivalSort())
);
}

@Transactional(readOnly = true)
public Optional<FestivalCursorDTO> findFestivalCursor(final long userId, final long festivalId) {
return festivalRepository.findFestivalCursor(userId, festivalId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.sopt.confeti.domain.festival.application.dto;

public record FestivalCursorDTO(
String cursorTitle,
boolean cursorIsFavorite
) {
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,74 @@
package org.sopt.confeti.domain.festival.infra.repository;

import java.util.List;
import java.util.Optional;
import org.sopt.confeti.domain.festival.Festival;
import org.sopt.confeti.domain.festival.application.dto.FestivalCursorDTO;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface FestivalRepository extends JpaRepository<Festival, Long> {
List<Festival> findFestivalsByIdIn(final @Param("festivalIds") List<Long> festivalIds);

@Query(value =
"SELECT f" +
" FROM Festival f" +
" LEFT JOIN FestivalFavorite ff" +
" ON f.id = ff.festival.id AND ff.user.id = :userId" +
" WHERE f.festivalEndAt >= CURRENT_DATE AND f.id NOT IN (" +
" SELECT tf.id" +
" FROM TimetableFestival tf" +
" INNER JOIN tf.user u" +
" WHERE u.id = :userId" +
" )" +
" ORDER BY CASE WHEN ff.id IS NULL THEN 0 ELSE 1 END DESC"
)
List<Festival> findFestivalsUsingInitCursor(
final @Param("userId") long userId,
final PageRequest page
);

@Query(value =
"SELECT f" +
" FROM Festival f" +
" LEFT JOIN FestivalFavorite ff" +
" ON f.id = ff.festival.id AND ff.user.id = :userId" +
" WHERE f.festivalEndAt >= CURRENT_DATE AND f.id NOT IN (" +
" SELECT tf.id" +
" FROM TimetableFestival tf" +
" INNER JOIN tf.user u" +
" WHERE u.id = :userId" +
" ) AND " +
" (" +
" (" +
" ((:cursorIsFavorite = true AND ff.id IS NOT NULL) OR (:cursorIsFavorite = false AND ff.id IS NULL)) AND (:cursorTitle <= f.festivalTitle)" +
" ) OR (" +
" :cursorIsFavorite = true AND ff.id IS NULL" +
" ) " +
" )" +
" ORDER BY CASE WHEN ff.id IS NULL THEN 0 ELSE 1 END DESC"
)
List<Festival> findFestivalsUsingCursor(
final @Param("userId") long userId,
final @Param("cursorTitle") String cursorTitle,
final @Param("cursorIsFavorite") boolean cursorIsFavorite,
final PageRequest page
);

@Query(value =
"SELECT new org.sopt.confeti.domain.festival.application.dto.FestivalCursorDTO(" +
" f.festivalTitle," +
" CASE WHEN ff.id IS NULL THEN false ELSE true END" +
" )" +
" FROM Festival f" +
" LEFT JOIN FestivalFavorite ff" +
" ON f.id = ff.festival.id AND ff.user.id = :userId" +
" WHERE f.id = :festivalId"
)
Optional<FestivalCursorDTO> findFestivalCursor(
final @Param("userId") long userId,
final @Param("festivalId") long festivalId
);
}
32 changes: 32 additions & 0 deletions src/main/java/org/sopt/confeti/global/common/CursorPage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.sopt.confeti.global.common;

import java.util.List;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class CursorPage<T> {

private final List<T> itemsWithNextCursor;
private final int size;

public static <T> CursorPage<T> of(final List<T> itemsWithNextCursor, final int size) {
return new CursorPage<>(itemsWithNextCursor, size);
}

public boolean isLast() {
return itemsWithNextCursor.size() < size;
}

public List<T> getItems() {
if (isLast()) {
return itemsWithNextCursor;
}

return itemsWithNextCursor.subList(0, size - 1);
}

public T getNextCursor() {
return itemsWithNextCursor.get(size - 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ public enum ErrorMessage {

private final HttpStatus httpStatus;
private final String message;

}

0 comments on commit 5eb2787

Please sign in to comment.