From 43cc655326548b85d8b8e8d6312be1d7ea5c4578 Mon Sep 17 00:00:00 2001 From: chyun Date: Fri, 17 Jan 2025 01:19:04 +0900 Subject: [PATCH 01/96] =?UTF-8?q?style=20[#56]=20=EC=97=94=ED=84=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/api/user/controller/UserFavoriteController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index 45f0656..b19770f 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -30,4 +30,5 @@ public ResponseEntity> deleteFavoriteFestival(@RequestHeader("Au @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) { userFavoriteFacade.delete(userId, festivalId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); - }} + } +} From 8b1da5aecc0b8efbbce6327c9c5aa06f8c07d614 Mon Sep 17 00:00:00 2001 From: chyun Date: Fri, 17 Jan 2025 03:02:50 +0900 Subject: [PATCH 02/96] =?UTF-8?q?feat=20[#56]=20=EC=BD=98=EC=84=9C?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PerformanceController.java | 33 ++++++++++++ .../performance/dto/request}/.gitkeep | 0 .../response/ConcertDetailArtistResponse.java | 17 +++++++ .../response/ConcertDetailInfoResponse.java | 43 ++++++++++++++++ .../dto/response/ConcertDetailResponse.java | 18 +++++++ .../performance/facade/PerformanceFacade.java | 23 +++++++++ .../performance/facade/dto/request/.gitkeep | 0 .../facade/dto/response/ConcertArtistDTO.java | 23 +++++++++ .../facade/dto/response/ConcertDetailDTO.java | 51 +++++++++++++++++++ .../sopt/confeti/api/performance/vo/.gitkeep | 0 .../sopt/confeti/domain/concert/Concert.java | 1 + .../concert/application/ConcertService.java | 29 +++++++++++ 12 files changed, 238 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java rename src/main/java/org/sopt/confeti/{global/exception => api/performance/dto/request}/.gitkeep (100%) create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailArtistResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/request/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertArtistDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/vo/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java new file mode 100644 index 0000000..1112fd5 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -0,0 +1,33 @@ +package org.sopt.confeti.api.performance.controller; + +import jakarta.validation.constraints.Min; +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.api.performance.dto.response.ConcertDetailResponse; +import org.sopt.confeti.api.performance.facade.PerformanceFacade; +import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; +import org.sopt.confeti.global.common.BaseResponse; +import org.sopt.confeti.global.message.SuccessMessage; +import org.sopt.confeti.global.util.ApiResponseUtil; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@Validated +@RequestMapping("/performances") +public class PerformanceController { + + private final PerformanceFacade performanceFacade; + + @GetMapping("/concerts/{concertId}") + public ResponseEntity> getConcertInfo(@RequestHeader("Authorization") Long userId, + @PathVariable @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long concertId) { + ConcertDetailDTO concertDetailDTO = performanceFacade.getConcertDetailInfo(concertId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, ConcertDetailResponse.from(concertDetailDTO)); + } +} diff --git a/src/main/java/org/sopt/confeti/global/exception/.gitkeep b/src/main/java/org/sopt/confeti/api/performance/dto/request/.gitkeep similarity index 100% rename from src/main/java/org/sopt/confeti/global/exception/.gitkeep rename to src/main/java/org/sopt/confeti/api/performance/dto/request/.gitkeep diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailArtistResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailArtistResponse.java new file mode 100644 index 0000000..e1ef98a --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailArtistResponse.java @@ -0,0 +1,17 @@ +package org.sopt.confeti.api.performance.dto.response; + +import org.sopt.confeti.api.performance.facade.dto.response.ConcertArtistDTO; + +public record ConcertDetailArtistResponse( + String artistId, + String name, + String profileUrl +) { + public static ConcertDetailArtistResponse of(final ConcertArtistDTO concertArtistDTO) { + return new ConcertDetailArtistResponse( + concertArtistDTO.artistId(), + concertArtistDTO.name(), + concertArtistDTO.profileUrl() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java new file mode 100644 index 0000000..6613e7a --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java @@ -0,0 +1,43 @@ +package org.sopt.confeti.api.performance.dto.response; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; + +public record ConcertDetailInfoResponse( + long concertId, + String posterUrl, + String posterBgUrl, + String title, + String subtitle, + LocalDate startAt, + LocalDate endAt, + String area, + LocalDateTime reserveAt, + String reservationUrl, + String time, + String ageRating, + String reservationOffice, + String price, + String infoImgUrl +) { + public static ConcertDetailInfoResponse of(final ConcertDetailDTO concertDetailDTO) { + return new ConcertDetailInfoResponse( + concertDetailDTO.concertId(), + concertDetailDTO.posterUrl(), + concertDetailDTO.posterBgUrl(), + concertDetailDTO.title(), + concertDetailDTO.subtitle(), + concertDetailDTO.startAt(), + concertDetailDTO.endAt(), + concertDetailDTO.area(), + concertDetailDTO.reserveAt(), + concertDetailDTO.reservationUrl(), + concertDetailDTO.time(), + concertDetailDTO.ageRating(), + concertDetailDTO.reservationOffice(), + concertDetailDTO.price(), + concertDetailDTO.infoImgUrl() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java new file mode 100644 index 0000000..cfb593f --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java @@ -0,0 +1,18 @@ +package org.sopt.confeti.api.performance.dto.response; + +import java.util.List; +import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; + +public record ConcertDetailResponse( + ConcertDetailInfoResponse concert, + List concertArtists +) { + public static ConcertDetailResponse from(final ConcertDetailDTO concertDetailDTO) { + return new ConcertDetailResponse( + ConcertDetailInfoResponse.of(concertDetailDTO), + concertDetailDTO.artists().stream() + .map(ConcertDetailArtistResponse::of) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java new file mode 100644 index 0000000..0345a3c --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -0,0 +1,23 @@ +package org.sopt.confeti.api.performance.facade; + +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.annotation.Facade; +import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.domain.concert.application.ConcertService; +import org.sopt.confeti.global.util.S3FileHandler; +import org.springframework.transaction.annotation.Transactional; + +@Facade +@RequiredArgsConstructor +public class PerformanceFacade { + + private final ConcertService concertService; + private final S3FileHandler s3FileHandler; + + @Transactional + public ConcertDetailDTO getConcertDetailInfo(final long concertId) { + Concert concert = concertService.getConcertDetailByConcertId(concertId); + return ConcertDetailDTO.of(concert, s3FileHandler); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/.gitkeep b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertArtistDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertArtistDTO.java new file mode 100644 index 0000000..4f736cc --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertArtistDTO.java @@ -0,0 +1,23 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.time.LocalDate; +import org.sopt.confeti.domain.concertartist.ConcertArtist; +import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; + +public record ConcertArtistDTO( + String artistId, + String name, + String profileUrl, + LocalDate latestReleaseAt +) { + public static ConcertArtistDTO of(final ConcertArtist concertArtist) { + ConfetiArtist confetiArtist = concertArtist.getArtist(); + + return new ConcertArtistDTO( + confetiArtist.getArtistId(), + confetiArtist.getName(), + confetiArtist.getProfileUrl(), + confetiArtist.getLatestReleaseAt() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java new file mode 100644 index 0000000..26e9af7 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java @@ -0,0 +1,51 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.global.util.S3FileHandler; + +public record ConcertDetailDTO( + long concertId, + String title, + String subtitle, + LocalDate startAt, + LocalDate endAt, + String area, + String posterUrl, + String posterBgUrl, + String infoImgUrl, + String concertReservationBgUrl, + LocalDateTime reserveAt, + String reservationUrl, + String reservationOffice, + String ageRating, + String time, + String price, + List artists +) { + public static ConcertDetailDTO of(final Concert concert, final S3FileHandler s3FileHandler) { + return new ConcertDetailDTO( + concert.getId(), + concert.getConcertTitle(), + concert.getConcertSubtitle(), + concert.getConcertStartAt(), + concert.getConcertEndAt(), + concert.getConcertArea(), + s3FileHandler.getFileUrl(concert.getConcertPosterPath()), + s3FileHandler.getFileUrl(concert.getConcertPosterBgPath()), + s3FileHandler.getFileUrl(concert.getConcertInfoImgPath()), + s3FileHandler.getFileUrl(concert.getConcertReservationBgPath()), + concert.getReserveAt(), + concert.getReservationUrl(), + concert.getReservationOffice(), + concert.getAgeRating(), + concert.getTime(), + concert.getPrice(), + concert.getArtists().stream() + .map(ConcertArtistDTO::of) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/vo/.gitkeep b/src/main/java/org/sopt/confeti/api/performance/vo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/org/sopt/confeti/domain/concert/Concert.java b/src/main/java/org/sopt/confeti/domain/concert/Concert.java index ce0fdad..0dfe79d 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/Concert.java +++ b/src/main/java/org/sopt/confeti/domain/concert/Concert.java @@ -10,6 +10,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; @Entity @Table(name="concerts") diff --git a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java new file mode 100644 index 0000000..2ce824d --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java @@ -0,0 +1,29 @@ +package org.sopt.confeti.domain.concert.application; + +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.domain.concert.infra.repository.ConcertRepository; +import org.sopt.confeti.global.exception.ConfetiException; +import org.sopt.confeti.global.message.ErrorMessage; +import org.sopt.confeti.global.util.artistsearcher.ArtistResolver; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ConcertService { + + private final ConcertRepository concertRepository; + private final ArtistResolver artistResolver; + + public Concert getConcertDetailByConcertId(final long concertId) { + Concert concert = concertRepository.findById(concertId) + .orElseThrow( + () -> new ConfetiException(ErrorMessage.NOT_FOUND) + ); + + artistResolver.load(concert); + + return concert; + } +} From 5e3a02ea032ded9e15de80182c42cb3577526ccd Mon Sep 17 00:00:00 2001 From: chyun Date: Fri, 17 Jan 2025 03:37:03 +0900 Subject: [PATCH 03/96] =?UTF-8?q?refactor=20[#62]=20=EB=A7=A4=EA=B0=9C?= =?UTF-8?q?=EB=B3=80=EC=88=98=EA=B0=80=20=ED=95=98=EB=82=98=EC=9D=B8=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=9D=B4=EB=A6=84=EC=9D=84=20from?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../performance/dto/response/ConcertDetailArtistResponse.java | 2 +- .../performance/dto/response/ConcertDetailInfoResponse.java | 2 +- .../api/performance/dto/response/ConcertDetailResponse.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailArtistResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailArtistResponse.java index e1ef98a..efd1e6d 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailArtistResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailArtistResponse.java @@ -7,7 +7,7 @@ public record ConcertDetailArtistResponse( String name, String profileUrl ) { - public static ConcertDetailArtistResponse of(final ConcertArtistDTO concertArtistDTO) { + public static ConcertDetailArtistResponse from(final ConcertArtistDTO concertArtistDTO) { return new ConcertDetailArtistResponse( concertArtistDTO.artistId(), concertArtistDTO.name(), diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java index 6613e7a..236e6b9 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java @@ -21,7 +21,7 @@ public record ConcertDetailInfoResponse( String price, String infoImgUrl ) { - public static ConcertDetailInfoResponse of(final ConcertDetailDTO concertDetailDTO) { + public static ConcertDetailInfoResponse from(final ConcertDetailDTO concertDetailDTO) { return new ConcertDetailInfoResponse( concertDetailDTO.concertId(), concertDetailDTO.posterUrl(), diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java index cfb593f..4d3fe64 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java @@ -9,9 +9,9 @@ public record ConcertDetailResponse( ) { public static ConcertDetailResponse from(final ConcertDetailDTO concertDetailDTO) { return new ConcertDetailResponse( - ConcertDetailInfoResponse.of(concertDetailDTO), + ConcertDetailInfoResponse.from(concertDetailDTO), concertDetailDTO.artists().stream() - .map(ConcertDetailArtistResponse::of) + .map(ConcertDetailArtistResponse::from) .toList() ); } From 64002260034d39874f887de3e87106377a5fbfc6 Mon Sep 17 00:00:00 2001 From: chyun Date: Fri, 17 Jan 2025 04:03:29 +0900 Subject: [PATCH 04/96] =?UTF-8?q?refactor=20[#62]=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EB=90=9C=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/domain/concert/application/ConcertService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java index 2ce824d..59fbf81 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java +++ b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java @@ -5,6 +5,7 @@ import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.infra.repository.ConcertRepository; import org.sopt.confeti.global.exception.ConfetiException; +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.stereotype.Service; @@ -19,7 +20,7 @@ public class ConcertService { public Concert getConcertDetailByConcertId(final long concertId) { Concert concert = concertRepository.findById(concertId) .orElseThrow( - () -> new ConfetiException(ErrorMessage.NOT_FOUND) + () -> new NotFoundException(ErrorMessage.NOT_FOUND) ); artistResolver.load(concert); From ca20c79b1d6d965e64e0e9fc4d9fad1a14f7a70f Mon Sep 17 00:00:00 2001 From: chyun Date: Fri, 17 Jan 2025 04:41:20 +0900 Subject: [PATCH 05/96] =?UTF-8?q?hotfix=20[#69]=20ArtistResolver=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B0=99=EC=9D=80=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20?= =?UTF-8?q?=EA=B3=84=EC=86=8D=20=ED=83=90=EC=83=89=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B2=83=EC=9D=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PerformanceController.java | 2 +- .../util/artistsearcher/ArtistResolver.java | 56 +++++++++++++------ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index 1112fd5..6161dbc 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -26,7 +26,7 @@ public class PerformanceController { @GetMapping("/concerts/{concertId}") public ResponseEntity> getConcertInfo(@RequestHeader("Authorization") Long userId, - @PathVariable @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long concertId) { + @PathVariable("concertId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long concertId) { ConcertDetailDTO concertDetailDTO = performanceFacade.getConcertDetailInfo(concertId); return ApiResponseUtil.success(SuccessMessage.SUCCESS, ConcertDetailResponse.from(concertDetailDTO)); } diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java index 063ab63..169eecb 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java @@ -33,31 +33,47 @@ public class ArtistResolver { private final ConcurrentHashMap, IntegrateFunction> collectByTypeMapper = new ConcurrentHashMap, IntegrateFunction>() {{ put(List.class, args -> { - if (!(args[0] instanceof List) || !(args[1] instanceof List) || !(args[2] instanceof HashMap)) { + if (!(args[0] instanceof List)) { // TODO: // 예외 처리 추가 throw new RuntimeException(); } - collectByListType((List) args[0], (List) args[1], (HashMap) args[2]); + collectByListType((List) args[0]); return null; }); }}; + private List artistIds; + private HashMap artistMapper; + private HashMap objectTrack; + private final SpotifyAPIHandler spotifyAPIHandler; + private void prologue() { + artistIds = new ArrayList<>(); + artistMapper = new HashMap<>(); + objectTrack = new HashMap<>(); + } + + private void epilogue() { + artistIds.clear(); + artistMapper.clear(); + objectTrack.clear(); + } + // Spotify API를 사용해 아티스트를 로드하는 엔트리 포인트 public void load(final Object target) { - List artistIds = new ArrayList<>(); - HashMap artistMapper = new HashMap<>(); - collect(target, artistIds, artistMapper); + prologue(); + collect(target); List confetiArtists = searchByArtistIds(artistIds); - injection(artistMapper, confetiArtists); + injection(confetiArtists); + epilogue(); } - private void injection(final HashMap artistMapper, final List confetiArtists) { + private void injection(final List confetiArtists) { confetiArtists.forEach((confetiArtist -> { ConfetiArtist mappedConfetiArtist = artistMapper.get(confetiArtist.getArtistId()); mappedConfetiArtist.setName(confetiArtist.getName()); @@ -70,7 +86,15 @@ private List searchByArtistIds(final List artistIds) { } // 리플렉션을 사용해 타겟 오브젝트를 재귀적으로 순회하며 아티스트 아이디를 수집하는 함수 - private void collect(final Object target, final List artistIds, final HashMap artistMapper) { + private void collect(final Object target) { + // 이미 탐색한 객체인 경우 + if (objectTrack.containsKey(target)) { + return; + } + + // 오브젝트 트랙에 현재 탐색 대상 객체를 추가 + objectTrack.put(target, true); + // 현재 오브젝트가 ConfetiArtist 타입인 경우 if (isConfetiArtistClass(target)) { ConfetiArtist artist = (ConfetiArtist) target; @@ -93,7 +117,7 @@ private void collect(final Object target, final List artistIds, final Ha } try { - collectByType(target, field, artistIds, artistMapper); + collectByType(target, field); } catch (IllegalAccessException e) { throw new RuntimeException(e); } @@ -101,27 +125,23 @@ private void collect(final Object target, final List artistIds, final Ha }); } - private void collectByType(final Object target, final Field field, final List artistIds, final HashMap artistMapper) throws IllegalAccessException { + private void collectByType(final Object target, final Field field) throws IllegalAccessException { Class fieldType = field.getType(); setAccessibleIfPrivate(field); try { collectByTypeMapper.get(fieldType).apply( - field.get(target), - artistIds, - artistMapper + field.get(target) ); } catch (NullPointerException e) { collect( - field.get(target), - artistIds, - artistMapper + field.get(target) ); } } - private void collectByListType(final List objects, final List artistIds, final HashMap artistMapper) { - objects.forEach((obj) -> collect(obj, artistIds, artistMapper)); + private void collectByListType(final List objects) { + objects.forEach(this::collect); } private void setAccessibleIfPrivate(final Field field) { From a6f3164f06549d8eade770bf233fadba6f246044 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 00:12:53 +0900 Subject: [PATCH 06/96] =?UTF-8?q?fix=20[#76]=20ArtistResolver=EC=9D=98=20l?= =?UTF-8?q?oad=20=ED=95=A8=EC=88=98=EC=97=90=EC=84=9C=20target=EC=9D=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=83=80=EC=9E=85=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=ED=83=90=EC=83=89=20=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/artistsearcher/ArtistResolver.java | 58 ++++++++++++------- .../artistsearcher/SpotifyAPIHandler.java | 5 ++ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java index 169eecb..0b48760 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java @@ -16,6 +16,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Resolver; import org.sopt.confeti.global.util.IntegrateFunction; +import org.springframework.aop.support.AopUtils; @Resolver @RequiredArgsConstructor(access = AccessLevel.PROTECTED) @@ -32,6 +33,16 @@ public class ArtistResolver { ); private final ConcurrentHashMap, IntegrateFunction> collectByTypeMapper = new ConcurrentHashMap, IntegrateFunction>() {{ + put(ArrayList.class, args -> { + if (!(args[0] instanceof List)) { + // TODO: + // 예외 처리 추가 + throw new RuntimeException(); + } + + collectByListType((List) args[0]); + return null; + }); put(List.class, args -> { if (!(args[0] instanceof List)) { // TODO: @@ -86,9 +97,13 @@ private List searchByArtistIds(final List artistIds) { } // 리플렉션을 사용해 타겟 오브젝트를 재귀적으로 순회하며 아티스트 아이디를 수집하는 함수 + // TODO: 추후에 메소드 분리 리펙토링 예정 private void collect(final Object target) { + if (target !=null) { + System.out.println(target); + } // 이미 탐색한 객체인 경우 - if (objectTrack.containsKey(target)) { + if (target == null || objectTrack.containsKey(target)) { return; } @@ -102,6 +117,12 @@ private void collect(final Object target) { artistIds.add(artist.getArtistId()); artistMapper.put(artist.getArtistId(), artist); return; + } else if (collectByTypeMapper.containsKey(target.getClass())) { + // 현재 오브젝트가 정해진 처리 방법이 필요한 타입인 경우 (현재는 List) + collectByTypeMapper.get(target.getClass()).apply( + target + ); + return; } // 필드에 있는 객체 목록 확인 @@ -116,8 +137,18 @@ private void collect(final Object target) { return; } + setAccessibleIfPrivateOrProtected(field); + try { - collectByType(target, field); + if (collectByTypeMapper.containsKey(field.getType())) { + collectByTypeMapper.get(field.getType()).apply( + field.get(target) + ); + + return; + } + + collect(field.get(target)); } catch (IllegalAccessException e) { throw new RuntimeException(e); } @@ -125,27 +156,14 @@ private void collect(final Object target) { }); } - private void collectByType(final Object target, final Field field) throws IllegalAccessException { - Class fieldType = field.getType(); - setAccessibleIfPrivate(field); - - try { - collectByTypeMapper.get(fieldType).apply( - field.get(target) - ); - } catch (NullPointerException e) { - collect( - field.get(target) - ); - } - } - private void collectByListType(final List objects) { objects.forEach(this::collect); } - private void setAccessibleIfPrivate(final Field field) { - if (Modifier.isPrivate(field.getModifiers())) { + private void setAccessibleIfPrivateOrProtected(final Field field) { + if ( + Modifier.isPrivate(field.getModifiers()) || Modifier.isProtected(field.getModifiers()) + ) { field.setAccessible(ACCESS_ALLOW); } } @@ -169,7 +187,7 @@ private boolean isConfetiArtistClass(final Object object) { private ConfetiArtist extractConfetiArtist(final Object target, final Field field) throws RuntimeException { try { - setAccessibleIfPrivate(field); + setAccessibleIfPrivateOrProtected(field); return (ConfetiArtist) field.get(target); } catch (IllegalAccessException e) { throw new RuntimeException(); diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java index 306b486..b417b8e 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java @@ -3,6 +3,7 @@ import com.neovisionaries.i18n.CountryCode; import java.io.IOException; import java.time.LocalDate; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; @@ -62,6 +63,10 @@ public Optional findArtistsByKeyword(final String keyword) { } public List findArtistsByArtistIds(final List artistIds) { + if (artistIds.isEmpty()) { + return new ArrayList<>(); + } + try { Artist[] artists = spotifyApi.getSeveralArtists( artistIds.toArray(new String[0]) From dc27e72a1121978f0e8fa98b3ece627adcf6bd4b Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 00:22:01 +0900 Subject: [PATCH 07/96] =?UTF-8?q?style=20[#76]=20print=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/global/util/artistsearcher/ArtistResolver.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java index 0b48760..849be76 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java @@ -99,9 +99,6 @@ private List searchByArtistIds(final List artistIds) { // 리플렉션을 사용해 타겟 오브젝트를 재귀적으로 순회하며 아티스트 아이디를 수집하는 함수 // TODO: 추후에 메소드 분리 리펙토링 예정 private void collect(final Object target) { - if (target !=null) { - System.out.println(target); - } // 이미 탐색한 객체인 경우 if (target == null || objectTrack.containsKey(target)) { return; From e800100f5cdfe8d3ed03c8461ef0d2b10fd75a6f Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 03:58:44 +0900 Subject: [PATCH 08/96] =?UTF-8?q?hotfix=20[#81]=20ArtistResolver=20?= =?UTF-8?q?=EC=88=98=EB=8F=99=EC=9C=BC=EB=A1=9C=20=EB=A7=A4=ED=95=91?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=B0=A9=EB=B2=95=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/domain/festival/Festival.java | 2 +- .../util/artistsearcher/ArtistResolver.java | 192 +++++++----------- 2 files changed, 72 insertions(+), 122 deletions(-) diff --git a/src/main/java/org/sopt/confeti/domain/festival/Festival.java b/src/main/java/org/sopt/confeti/domain/festival/Festival.java index ea808f2..d2ce407 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/Festival.java +++ b/src/main/java/org/sopt/confeti/domain/festival/Festival.java @@ -72,7 +72,7 @@ public class Festival { private String price; @OneToMany(mappedBy = "festival", cascade = CascadeType.ALL, orphanRemoval = true) - private List dates= new ArrayList<>(); + private List dates = new ArrayList<>(); @OneToMany(mappedBy = "festival", cascade = CascadeType.REMOVE) private List festivalFavorites = new ArrayList<>(); diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java index 849be76..728bc43 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java @@ -1,76 +1,54 @@ package org.sopt.confeti.global.util.artistsearcher; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Resolver; +import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.domain.festival.Festival; +import org.sopt.confeti.domain.festivaldate.FestivalDate; import org.sopt.confeti.global.util.IntegrateFunction; -import org.springframework.aop.support.AopUtils; @Resolver @RequiredArgsConstructor(access = AccessLevel.PROTECTED) public class ArtistResolver { - private static final boolean ACCESS_ALLOW = true; - - private final Set> noCustomReferenceTypes = new LinkedHashSet>( - Arrays.asList( - Integer.class, Long.class, Float.class, Double.class, - LocalDate.class, LocalDateTime.class, LocalTime.class, - String.class - ) - ); - private final ConcurrentHashMap, IntegrateFunction> collectByTypeMapper = new ConcurrentHashMap, IntegrateFunction>() {{ - put(ArrayList.class, args -> { - if (!(args[0] instanceof List)) { - // TODO: - // 예외 처리 추가 - throw new RuntimeException(); - } - - collectByListType((List) args[0]); + put(ArtistFavorite.class, args -> { + collectArtistFavorite(args[0]); + return null; + }); + put(Concert.class, args -> { + collectConcert(args[0]); + return null; + }); + put(Festival.class, args -> { + collectFestival(args[0]); return null; }); - put(List.class, args -> { - if (!(args[0] instanceof List)) { - // TODO: - // 예외 처리 추가 - throw new RuntimeException(); - } - - collectByListType((List) args[0]); + put(FestivalDate.class, args -> { + collectFestivalDate(args[0]); return null; }); }}; private List artistIds; private HashMap artistMapper; - private HashMap objectTrack; private final SpotifyAPIHandler spotifyAPIHandler; private void prologue() { artistIds = new ArrayList<>(); artistMapper = new HashMap<>(); - objectTrack = new HashMap<>(); } private void epilogue() { artistIds.clear(); artistMapper.clear(); - objectTrack.clear(); } // Spotify API를 사용해 아티스트를 로드하는 엔트리 포인트 @@ -84,111 +62,83 @@ public void load(final Object target) { epilogue(); } - private void injection(final List confetiArtists) { - confetiArtists.forEach((confetiArtist -> { - ConfetiArtist mappedConfetiArtist = artistMapper.get(confetiArtist.getArtistId()); - mappedConfetiArtist.setName(confetiArtist.getName()); - mappedConfetiArtist.setProfileUrl(confetiArtist.getProfileUrl()); - })); - } - - private List searchByArtistIds(final List artistIds) { - return spotifyAPIHandler.findArtistsByArtistIds(artistIds); - } - - // 리플렉션을 사용해 타겟 오브젝트를 재귀적으로 순회하며 아티스트 아이디를 수집하는 함수 - // TODO: 추후에 메소드 분리 리펙토링 예정 private void collect(final Object target) { - // 이미 탐색한 객체인 경우 - if (target == null || objectTrack.containsKey(target)) { + if (target == null) { return; } - // 오브젝트 트랙에 현재 탐색 대상 객체를 추가 - objectTrack.put(target, true); + if (isListType(target)) { + List objects = (List) target; - // 현재 오브젝트가 ConfetiArtist 타입인 경우 - if (isConfetiArtistClass(target)) { - ConfetiArtist artist = (ConfetiArtist) target; + objects.forEach(object -> { + if (collectByTypeMapper.containsKey(object.getClass())) { + collectByTypeMapper.get(object.getClass()) + .apply(object); + } + }); - artistIds.add(artist.getArtistId()); - artistMapper.put(artist.getArtistId(), artist); - return; - } else if (collectByTypeMapper.containsKey(target.getClass())) { - // 현재 오브젝트가 정해진 처리 방법이 필요한 타입인 경우 (현재는 List) - collectByTypeMapper.get(target.getClass()).apply( - target - ); return; } - // 필드에 있는 객체 목록 확인 - Class targetClass = target.getClass(); - Arrays.stream(targetClass.getDeclaredFields()).forEach((Field field) -> { - if (isReferenceType(field) && isNoCustomReferenceType(field)) { - if (isConfetiArtistClass(field)) { - ConfetiArtist artist = extractConfetiArtist(target, field); - - artistIds.add(artist.getArtistId()); - artistMapper.put(artist.getArtistId(), artist); - return; - } - - setAccessibleIfPrivateOrProtected(field); - - try { - if (collectByTypeMapper.containsKey(field.getType())) { - collectByTypeMapper.get(field.getType()).apply( - field.get(target) - ); - - return; - } - - collect(field.get(target)); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - }); + if (collectByTypeMapper.containsKey(target.getClass())) { + collectByTypeMapper.get(target.getClass()).apply(target); + } } - private void collectByListType(final List objects) { - objects.forEach(this::collect); + private boolean isListType(final Object target) { + return target.getClass() == ArrayList.class; } - private void setAccessibleIfPrivateOrProtected(final Field field) { - if ( - Modifier.isPrivate(field.getModifiers()) || Modifier.isProtected(field.getModifiers()) - ) { - field.setAccessible(ACCESS_ALLOW); - } + private void collectArtistFavorite(final Object target) { + ArtistFavorite artistFavorite = (ArtistFavorite) target; + ConfetiArtist confetiArtist = artistFavorite.getArtist(); + artistIds.add(confetiArtist.getArtistId()); + artistMapper.put(confetiArtist.getArtistId(), artistFavorite.getArtist()); } - private boolean isNoCustomReferenceType(final Field field) { - return !noCustomReferenceTypes.contains(field.getType()); + private void collectConcert(final Object target) { + Concert concert = (Concert) target; + concert.getArtists().forEach(artist -> { + artistIds.add(artist.getArtist().getArtistId()); + artistMapper.put( + artist.getArtist().getArtistId(), + artist.getArtist() + ); + }); } - private boolean isReferenceType(final Field field) { - return !field.getType().isPrimitive(); + private void collectFestival(final Object target) { + Festival festival = (Festival) target; + festival.getDates().stream() + .flatMap(date -> date.getStages().stream()) + .flatMap(stage -> stage.getTimes().stream()) + .flatMap(time -> time.getArtists().stream()) + .forEach(artist -> { + artistIds.add(artist.getArtist().getName()); + artistMapper.put(artist.getArtist().getArtistId(), artist.getArtist()); + }); } - private boolean isConfetiArtistClass(final Field field) { - return field.getType().isAssignableFrom(ConfetiArtist.class); + private void collectFestivalDate(final Object target) { + FestivalDate festivalDate = (FestivalDate) target; + festivalDate.getStages().stream() + .flatMap(stage -> stage.getTimes().stream()) + .flatMap(time -> time.getArtists().stream()) + .forEach(artist -> { + artistIds.add(artist.getArtist().getName()); + artistMapper.put(artist.getArtist().getArtistId(), artist.getArtist()); + }); } - private boolean isConfetiArtistClass(final Object object) { - return object.getClass().isAssignableFrom(ConfetiArtist.class); + private void injection(final List confetiArtists) { + confetiArtists.forEach((confetiArtist -> { + ConfetiArtist mappedConfetiArtist = artistMapper.get(confetiArtist.getArtistId()); + mappedConfetiArtist.setName(confetiArtist.getName()); + mappedConfetiArtist.setProfileUrl(confetiArtist.getProfileUrl()); + })); } - private ConfetiArtist extractConfetiArtist(final Object target, final Field field) - throws RuntimeException { - try { - setAccessibleIfPrivateOrProtected(field); - return (ConfetiArtist) field.get(target); - } catch (IllegalAccessException e) { - throw new RuntimeException(); - } - + private List searchByArtistIds(final List artistIds) { + return spotifyAPIHandler.findArtistsByArtistIds(artistIds); } } From 4a6ea571b37cc92317cdc83054d8e18763d80086 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 04:05:08 +0900 Subject: [PATCH 09/96] =?UTF-8?q?style=20[#81]=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/global/util/artistsearcher/ArtistResolver.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java index 728bc43..f92149c 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java @@ -17,6 +17,7 @@ @RequiredArgsConstructor(access = AccessLevel.PROTECTED) public class ArtistResolver { + // 기존에 조회할 클래스를 매핑 private final ConcurrentHashMap, IntegrateFunction> collectByTypeMapper = new ConcurrentHashMap, IntegrateFunction>() {{ put(ArtistFavorite.class, args -> { collectArtistFavorite(args[0]); @@ -67,10 +68,12 @@ private void collect(final Object target) { return; } + // 주어진 타겟이 리스트일 경우 if (isListType(target)) { List objects = (List) target; objects.forEach(object -> { + // Mapper에 등록된 클래스 타입인 경우 if (collectByTypeMapper.containsKey(object.getClass())) { collectByTypeMapper.get(object.getClass()) .apply(object); @@ -80,6 +83,7 @@ private void collect(final Object target) { return; } + // Mapper에 등록된 클래스 타입인 경우 if (collectByTypeMapper.containsKey(target.getClass())) { collectByTypeMapper.get(target.getClass()).apply(target); } From 732b22d80f297b1f0976ed358d8f688b5997e714 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 02:43:57 +0900 Subject: [PATCH 10/96] =?UTF-8?q?feat=20[#78]=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=20=EC=83=9D=EC=84=B1=20API?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PerformanceController.java | 11 ++++ .../api/performance/dto/request/.gitkeep | 0 .../request/CreateFestivalArtistRequest.java | 6 ++ .../request/CreateFestivalDateRequest.java | 12 ++++ .../dto/request/CreateFestivalRequest.java | 26 ++++++++ .../request/CreateFestivalStageRequest.java | 10 +++ .../request/CreateFestivalTimeRequest.java | 12 ++++ .../performance/facade/PerformanceFacade.java | 8 +++ .../performance/facade/dto/request/.gitkeep | 0 .../dto/request/CreateFestivalArtistDTO.java | 13 ++++ .../facade/dto/request/CreateFestivalDTO.java | 50 +++++++++++++++ .../dto/request/CreateFestivalDateDTO.java | 22 +++++++ .../dto/request/CreateFestivalStageDTO.java | 20 ++++++ .../dto/request/CreateFestivalTimeDTO.java | 23 +++++++ .../domain/artistfavorite/ArtistFavorite.java | 2 +- .../confeti/domain/festival/Festival.java | 62 ++++++++++++++++++- .../festival/application/FestivalService.java | 7 +++ .../domain/festivalartist/FestivalArtist.java | 17 +++++ .../domain/festivaldate/FestivalDate.java | 32 ++++++++++ .../domain/festivalstage/FestivalStage.java | 27 ++++++++ .../domain/festivaltime/FestivalTime.java | 33 +++++++++- .../util/artistsearcher/ConfetiArtist.java | 8 ++- 22 files changed, 395 insertions(+), 6 deletions(-) delete mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/request/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalArtistRequest.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalStageRequest.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java delete mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/request/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalArtistDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDateDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalStageDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalTimeDTO.java diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index 6161dbc..20e4742 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -2,8 +2,10 @@ import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; +import org.sopt.confeti.api.performance.dto.request.CreateFestivalRequest; import org.sopt.confeti.api.performance.dto.response.ConcertDetailResponse; import org.sopt.confeti.api.performance.facade.PerformanceFacade; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; @@ -12,6 +14,8 @@ import org.springframework.validation.annotation.Validated; 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.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -30,4 +34,11 @@ public ResponseEntity> getConcertInfo(@RequestHeader("Authorizat ConcertDetailDTO concertDetailDTO = performanceFacade.getConcertDetailInfo(concertId); return ApiResponseUtil.success(SuccessMessage.SUCCESS, ConcertDetailResponse.from(concertDetailDTO)); } + + @PostMapping("/festivals") + public ResponseEntity> createConcert(@RequestBody CreateFestivalRequest createFestivalRequest) { + performanceFacade.createFestival(CreateFestivalDTO.from(createFestivalRequest)); + + return ApiResponseUtil.success(SuccessMessage.SUCCESS); + } } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/.gitkeep b/src/main/java/org/sopt/confeti/api/performance/dto/request/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalArtistRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalArtistRequest.java new file mode 100644 index 0000000..3956c92 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalArtistRequest.java @@ -0,0 +1,6 @@ +package org.sopt.confeti.api.performance.dto.request; + +public record CreateFestivalArtistRequest( + String artistId +) { +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java new file mode 100644 index 0000000..a9b77d8 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java @@ -0,0 +1,12 @@ +package org.sopt.confeti.api.performance.dto.request; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +public record CreateFestivalDateRequest( + LocalDate festivalAt, + LocalTime ticketOpenAt, + List festivalStages +) { +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java new file mode 100644 index 0000000..e60c364 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java @@ -0,0 +1,26 @@ +package org.sopt.confeti.api.performance.dto.request; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +public record CreateFestivalRequest( + String festivalTitle, + String festivalSubtitle, + LocalDate festivalStartAt, + LocalDate festivalEndAt, + String festivalArea, + String festivalPosterPath, + String festivalPosterBgPath, + String festivalInfoImgPath, + String festivalReservationBgPath, + String festivalLogoPath, + LocalDate reserveAt, + String reservationUrl, + String reservationOffice, + String ageRating, + String time, + String price, + List festivalDates +) { +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalStageRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalStageRequest.java new file mode 100644 index 0000000..60819ca --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalStageRequest.java @@ -0,0 +1,10 @@ +package org.sopt.confeti.api.performance.dto.request; + +import java.util.List; + +public record CreateFestivalStageRequest( + String name, + int orders, + List festivalTimes +) { +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java new file mode 100644 index 0000000..59f5fbc --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java @@ -0,0 +1,12 @@ +package org.sopt.confeti.api.performance.dto.request; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +public record CreateFestivalTimeRequest( + LocalTime startAt, + LocalTime endAt, + List festivalArtists +) { +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index 0345a3c..e63e588 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -2,9 +2,11 @@ import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.application.ConcertService; +import org.sopt.confeti.domain.festival.application.FestivalService; import org.sopt.confeti.global.util.S3FileHandler; import org.springframework.transaction.annotation.Transactional; @@ -13,6 +15,7 @@ public class PerformanceFacade { private final ConcertService concertService; + private final FestivalService festivalService; private final S3FileHandler s3FileHandler; @Transactional @@ -20,4 +23,9 @@ public ConcertDetailDTO getConcertDetailInfo(final long concertId) { Concert concert = concertService.getConcertDetailByConcertId(concertId); return ConcertDetailDTO.of(concert, s3FileHandler); } + + @Transactional + public void createFestival(final CreateFestivalDTO createFestivalDTO) { + festivalService.create(createFestivalDTO); + } } diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/.gitkeep b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalArtistDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalArtistDTO.java new file mode 100644 index 0000000..0f300b6 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalArtistDTO.java @@ -0,0 +1,13 @@ +package org.sopt.confeti.api.performance.facade.dto.request; + +import org.sopt.confeti.api.performance.dto.request.CreateFestivalArtistRequest; + +public record CreateFestivalArtistDTO( + String artistId +) { + public static CreateFestivalArtistDTO from(final CreateFestivalArtistRequest createFestivalArtistRequest) { + return new CreateFestivalArtistDTO( + createFestivalArtistRequest.artistId() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java new file mode 100644 index 0000000..1428bc1 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java @@ -0,0 +1,50 @@ +package org.sopt.confeti.api.performance.facade.dto.request; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import org.sopt.confeti.api.performance.dto.request.CreateFestivalRequest; + +public record CreateFestivalDTO( + String festivalTitle, + String festivalSubtitle, + LocalDate festivalStartAt, + LocalDate festivalEndAt, + String festivalArea, + String festivalPosterPath, + String festivalPosterBgPath, + String festivalInfoImgPath, + String festivalReservationBgPath, + String festivalLogoPath, + LocalDate reserveAt, + String reservationUrl, + String reservationOffice, + String ageRating, + String time, + String price, + List dates +) { + public static CreateFestivalDTO from(final CreateFestivalRequest createFestivalRequest) { + return new CreateFestivalDTO( + createFestivalRequest.festivalTitle(), + createFestivalRequest.festivalSubtitle(), + createFestivalRequest.festivalStartAt(), + createFestivalRequest.festivalEndAt(), + createFestivalRequest.festivalArea(), + createFestivalRequest.festivalPosterPath(), + createFestivalRequest.festivalPosterBgPath(), + createFestivalRequest.festivalInfoImgPath(), + createFestivalRequest.festivalReservationBgPath(), + createFestivalRequest.festivalLogoPath(), + createFestivalRequest.reserveAt(), + createFestivalRequest.reservationUrl(), + createFestivalRequest.reservationOffice(), + createFestivalRequest.ageRating(), + createFestivalRequest.time(), + createFestivalRequest.price(), + createFestivalRequest.festivalDates().stream() + .map(CreateFestivalDateDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDateDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDateDTO.java new file mode 100644 index 0000000..3d3b067 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDateDTO.java @@ -0,0 +1,22 @@ +package org.sopt.confeti.api.performance.facade.dto.request; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import org.sopt.confeti.api.performance.dto.request.CreateFestivalDateRequest; + +public record CreateFestivalDateDTO( + LocalDate festivalAt, + LocalTime openAt, + List stages +) { + public static CreateFestivalDateDTO from(final CreateFestivalDateRequest createFestivalDateRequest) { + return new CreateFestivalDateDTO( + createFestivalDateRequest.festivalAt(), + createFestivalDateRequest.ticketOpenAt(), + createFestivalDateRequest.festivalStages().stream() + .map(CreateFestivalStageDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalStageDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalStageDTO.java new file mode 100644 index 0000000..9f75890 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalStageDTO.java @@ -0,0 +1,20 @@ +package org.sopt.confeti.api.performance.facade.dto.request; + +import java.util.List; +import org.sopt.confeti.api.performance.dto.request.CreateFestivalStageRequest; + +public record CreateFestivalStageDTO( + String name, + int orders, + List times +) { + public static CreateFestivalStageDTO from(final CreateFestivalStageRequest createFestivalStageRequest) { + return new CreateFestivalStageDTO( + createFestivalStageRequest.name(), + createFestivalStageRequest.orders(), + createFestivalStageRequest.festivalTimes().stream() + .map(CreateFestivalTimeDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalTimeDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalTimeDTO.java new file mode 100644 index 0000000..b193907 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalTimeDTO.java @@ -0,0 +1,23 @@ +package org.sopt.confeti.api.performance.facade.dto.request; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import org.sopt.confeti.api.performance.dto.request.CreateFestivalTimeRequest; + +public record CreateFestivalTimeDTO( + LocalTime startAt, + LocalTime endAt, + List artists +) { + public static CreateFestivalTimeDTO from(final CreateFestivalTimeRequest createFestivalTimeRequest) { + return new CreateFestivalTimeDTO( + createFestivalTimeRequest.startAt(), + createFestivalTimeRequest.endAt(), + createFestivalTimeRequest.festivalArtists().stream() + .map(CreateFestivalArtistDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/ArtistFavorite.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/ArtistFavorite.java index be22df6..1b35b9f 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/ArtistFavorite.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/ArtistFavorite.java @@ -29,7 +29,7 @@ public class ArtistFavorite { @Builder private ArtistFavorite(User user, String artistId) { this.user = user; - this.artist = new ConfetiArtist(artistId); + this.artist = ConfetiArtist.from(artistId); } public static ArtistFavorite create(User user, String artistId) { diff --git a/src/main/java/org/sopt/confeti/domain/festival/Festival.java b/src/main/java/org/sopt/confeti/domain/festival/Festival.java index d2ce407..e95b7ab 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/Festival.java +++ b/src/main/java/org/sopt/confeti/domain/festival/Festival.java @@ -2,8 +2,10 @@ import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.domain.festivaldate.FestivalDate; import org.sopt.confeti.domain.festivalfavorite.FestivalFavorite; import org.sopt.confeti.domain.timetablefestival.TimetableFestival; @@ -12,6 +14,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import org.sopt.confeti.domain.user.User; @Entity @Table(name="festivals") @@ -54,7 +57,7 @@ public class Festival { private String festivalLogoPath; @Column(nullable = false) - private LocalDateTime reserveAt; + private LocalDate reserveAt; @Column(length = 250, nullable = false) private String reservationUrl; @@ -79,5 +82,62 @@ public class Festival { @OneToMany(mappedBy = "festival", cascade = CascadeType.REMOVE) private List timetableFestivals = new ArrayList<>(); + + @Builder + public Festival(String festivalTitle, String festivalSubtitle, LocalDate festivalStartAt, + LocalDate festivalEndAt, String festivalArea, String festivalPosterPath, + String festivalPosterBgPath, + String festivalInfoImgPath, String festivalReservationBgPath, String festivalLogoPath, + LocalDate reserveAt, String reservationUrl, String reservationOffice, String ageRating, + String time, + String price, List dates) { + this.festivalTitle = festivalTitle; + this.festivalSubtitle = festivalSubtitle; + this.festivalStartAt = festivalStartAt; + this.festivalEndAt = festivalEndAt; + this.festivalArea = festivalArea; + this.festivalPosterPath = festivalPosterPath; + this.festivalPosterBgPath = festivalPosterBgPath; + this.festivalInfoImgPath = festivalInfoImgPath; + this.festivalReservationBgPath = festivalReservationBgPath; + this.festivalLogoPath = festivalLogoPath; + this.reserveAt = reserveAt; + this.reservationUrl = reservationUrl; + this.reservationOffice = reservationOffice; + this.ageRating = ageRating; + this.time = time; + this.price = price; + this.dates = dates; + + this.dates.forEach(date -> { + date.setFestival(this); + }); + } + + public static Festival create(CreateFestivalDTO createFestivalDTO) { + return Festival.builder() + .festivalTitle(createFestivalDTO.festivalTitle()) + .festivalSubtitle(createFestivalDTO.festivalSubtitle()) + .festivalStartAt(createFestivalDTO.festivalStartAt()) + .festivalEndAt(createFestivalDTO.festivalEndAt()) + .festivalArea(createFestivalDTO.festivalArea()) + .festivalPosterPath(createFestivalDTO.festivalPosterPath()) + .festivalPosterBgPath(createFestivalDTO.festivalPosterBgPath()) + .festivalInfoImgPath(createFestivalDTO.festivalInfoImgPath()) + .festivalReservationBgPath(createFestivalDTO.festivalReservationBgPath()) + .festivalLogoPath(createFestivalDTO.festivalLogoPath()) + .reserveAt(createFestivalDTO.reserveAt()) + .reservationUrl(createFestivalDTO.reservationUrl()) + .reservationOffice(createFestivalDTO.reservationOffice()) + .ageRating(createFestivalDTO.ageRating()) + .time(createFestivalDTO.time()) + .price(createFestivalDTO.price()) + .dates( + createFestivalDTO.dates().stream() + .map(FestivalDate::create) + .toList() + ) + .build(); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index 1f8dcb5..7f81a2a 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -1,6 +1,7 @@ package org.sopt.confeti.domain.festival.application; 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.infra.repository.FestivalRepository; import org.sopt.confeti.global.exception.NotFoundException; @@ -17,4 +18,10 @@ public Festival findById(Long festivalId) { .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND)); return festival; } + + public void create(final CreateFestivalDTO createFestivalDTO) { + festivalRepository.save( + Festival.create(createFestivalDTO) + ); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festivalartist/FestivalArtist.java b/src/main/java/org/sopt/confeti/domain/festivalartist/FestivalArtist.java index dd71f88..65039f5 100644 --- a/src/main/java/org/sopt/confeti/domain/festivalartist/FestivalArtist.java +++ b/src/main/java/org/sopt/confeti/domain/festivalartist/FestivalArtist.java @@ -2,8 +2,11 @@ import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalArtistDTO; import org.sopt.confeti.domain.festivalstage.FestivalStage; import org.sopt.confeti.domain.festivaltime.FestivalTime; import org.sopt.confeti.domain.usertimetable.UserTimetable; @@ -26,7 +29,21 @@ public class FestivalArtist { @Embedded private ConfetiArtist artist; + @Setter @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="festival_time_id", nullable=false) private FestivalTime festivalTime; + + @Builder + public FestivalArtist(ConfetiArtist artist) { + this.artist = artist; + } + + public static FestivalArtist create(final CreateFestivalArtistDTO createFestivalArtistDTO) { + return FestivalArtist.builder() + .artist( + ConfetiArtist.from(createFestivalArtistDTO.artistId()) + ) + .build(); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festivaldate/FestivalDate.java b/src/main/java/org/sopt/confeti/domain/festivaldate/FestivalDate.java index 1c6e897..166530d 100644 --- a/src/main/java/org/sopt/confeti/domain/festivaldate/FestivalDate.java +++ b/src/main/java/org/sopt/confeti/domain/festivaldate/FestivalDate.java @@ -1,9 +1,14 @@ package org.sopt.confeti.domain.festivaldate; import jakarta.persistence.*; +import java.time.LocalDateTime; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDateDTO; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festivalstage.FestivalStage; @@ -22,6 +27,7 @@ public class FestivalDate { @Column(name="festival_date_id") private Long id; + @Setter @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "festival_id", nullable = false) private Festival festival; @@ -34,4 +40,30 @@ public class FestivalDate { @OneToMany(mappedBy = "festivalDate", cascade = CascadeType.ALL, orphanRemoval = true) private List stages = new ArrayList<>(); + + @Builder + public FestivalDate(LocalDate festivalAt, LocalTime openAt, + List stages) { + this.festivalAt = festivalAt; + this.openAt = openAt; + this.stages = stages; + + this.stages.forEach(stage -> { + stage.setFestivalDate(this); + }); + } + + + public static FestivalDate create(CreateFestivalDateDTO createFestivalDateDTO) { + return FestivalDate.builder() + .festivalAt(createFestivalDateDTO.festivalAt()) + .openAt(createFestivalDateDTO.openAt()) + .stages( + createFestivalDateDTO.stages().stream() + .map(FestivalStage::create) + .toList() + ) + .build(); + } + } diff --git a/src/main/java/org/sopt/confeti/domain/festivalstage/FestivalStage.java b/src/main/java/org/sopt/confeti/domain/festivalstage/FestivalStage.java index 50308f9..d0ed543 100644 --- a/src/main/java/org/sopt/confeti/domain/festivalstage/FestivalStage.java +++ b/src/main/java/org/sopt/confeti/domain/festivalstage/FestivalStage.java @@ -2,8 +2,11 @@ import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalStageDTO; import org.sopt.confeti.domain.festivalartist.FestivalArtist; import org.sopt.confeti.domain.festivaldate.FestivalDate; import org.sopt.confeti.domain.festivaltime.FestivalTime; @@ -21,6 +24,7 @@ public class FestivalStage { @Column(name="festival_stage_id") private Long id; + @Setter @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="festival_date_id", nullable = false) private FestivalDate festivalDate; @@ -33,4 +37,27 @@ public class FestivalStage { @OneToMany(mappedBy = "festivalStage", cascade = CascadeType.ALL, orphanRemoval = true) private List times = new ArrayList<>(); + + @Builder + public FestivalStage(String name, int order, List times) { + this.name = name; + this.order = order; + this.times = times; + + this.times.forEach(time -> { + time.setFestivalStage(this); + }); + } + + public static FestivalStage create(final CreateFestivalStageDTO createFestivalStageDTO) { + return FestivalStage.builder() + .name(createFestivalStageDTO.name()) + .order(createFestivalStageDTO.orders()) + .times( + createFestivalStageDTO.times().stream() + .map(FestivalTime::create) + .toList() + ) + .build(); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festivaltime/FestivalTime.java b/src/main/java/org/sopt/confeti/domain/festivaltime/FestivalTime.java index 78949a3..8cbfcc8 100644 --- a/src/main/java/org/sopt/confeti/domain/festivaltime/FestivalTime.java +++ b/src/main/java/org/sopt/confeti/domain/festivaltime/FestivalTime.java @@ -1,9 +1,14 @@ package org.sopt.confeti.domain.festivaltime; import jakarta.persistence.*; +import java.time.LocalDate; +import java.time.LocalTime; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalTimeDTO; import org.sopt.confeti.domain.festivalartist.FestivalArtist; import org.sopt.confeti.domain.festivalstage.FestivalStage; import org.sopt.confeti.domain.usertimetable.UserTimetable; @@ -22,19 +27,43 @@ public class FestivalTime { @Column(name="festival_time_id") private Long id; + @Setter @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="festival_stage_id", nullable = false) private FestivalStage festivalStage; @Column(nullable = false) - private LocalDateTime startAt; + private LocalTime startAt; @Column(nullable = false) - private LocalDateTime endAt; + private LocalTime endAt; @OneToMany(mappedBy = "festivalTime", cascade = CascadeType.REMOVE) private List timetables = new ArrayList<>(); @OneToMany(mappedBy = "festivalTime", cascade = CascadeType.ALL, orphanRemoval = true) private List artists = new ArrayList<>(); + + @Builder + public FestivalTime(LocalTime startAt, LocalTime endAt, List artists) { + this.startAt = startAt; + this.endAt = endAt; + this.artists = artists; + + this.artists.forEach(artist -> { + artist.setFestivalTime(this); + }); + } + + public static FestivalTime create(final CreateFestivalTimeDTO createFestivalTimeDTO) { + return FestivalTime.builder() + .startAt(createFestivalTimeDTO.startAt()) + .endAt(createFestivalTimeDTO.endAt()) + .artists( + createFestivalTimeDTO.artists().stream() + .map(FestivalArtist::create) + .toList() + ) + .build(); + } } diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ConfetiArtist.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ConfetiArtist.java index 975f118..1e9c41e 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ConfetiArtist.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ConfetiArtist.java @@ -33,6 +33,10 @@ public class ConfetiArtist { @Transient private LocalDate latestReleaseAt; + private ConfetiArtist(String artistId) { + this.artistId = artistId; + } + public static ConfetiArtist toConfetiArtist(final Artist artist) { Optional image = Arrays.stream(artist.getImages()) .min(Comparator.comparingInt(Image::getHeight)); @@ -57,7 +61,7 @@ public static ConfetiArtist toConfetiArtist(final Artist artist, final LocalDate ); } - public ConfetiArtist(String artistId) { - this.artistId = artistId; + public static ConfetiArtist from(final String artistId) { + return new ConfetiArtist(artistId); } } From d866c78152d782a162f3e7904023b4c779645cf2 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Sat, 18 Jan 2025 01:23:51 +0900 Subject: [PATCH 11/96] =?UTF-8?q?feat=20[#57]=20ArtistFavoriteController?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserFavoriteController.java | 8 ++++++++ .../dto/response/UserFavoriteListResponse.java | 17 +++++++++++++++++ .../user/dto/response/UserFavoriteResponse.java | 16 ++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteListResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index b19770f..4c4cdec 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -2,7 +2,9 @@ import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; +import org.sopt.confeti.api.user.dto.response.UserFavoriteResponse; import org.sopt.confeti.api.user.facade.UserFavoriteFacade; +import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; import org.sopt.confeti.global.util.ApiResponseUtil; @@ -31,4 +33,10 @@ public ResponseEntity> deleteFavoriteFestival(@RequestHeader("Au userFavoriteFacade.delete(userId, festivalId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } + + @GetMapping("/artists") + public ResponseEntity> getFavoriteArtists(@RequestHeader("Authorization") Long userId) { + UserFavoriteArtistDTO userFavoriteArtistDTO = userFavoriteFacade.getArtistList(userId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserFavoriteResponse.of(userFavoriteArtistDTO.artists())); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteListResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteListResponse.java new file mode 100644 index 0000000..500d575 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteListResponse.java @@ -0,0 +1,17 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistListDTO; + +public record UserFavoriteListResponse ( + String artistId, + String name, + String profileUrl +) { + public static UserFavoriteListResponse from(final UserFavoriteArtistListDTO artistListDTO) { + return new UserFavoriteListResponse( + artistListDTO.artistId(), + artistListDTO.name(), + artistListDTO.profileUrl() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java new file mode 100644 index 0000000..9a3c9c4 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java @@ -0,0 +1,16 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistListDTO; + +import java.util.List; + +public record UserFavoriteResponse(List artists) { + public static UserFavoriteResponse of(final List artistListDTO) { + return new UserFavoriteResponse( + artistListDTO.stream() + .map(UserFavoriteListResponse::from) + .toList() + ); + } +} + From d7c8a3c5a821ed49e96b675fc51e142633ac166a Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Sat, 18 Jan 2025 01:24:16 +0900 Subject: [PATCH 12/96] =?UTF-8?q?feat=20[#57]=20UserFavoriteFacade=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserFavoriteFacade.java | 13 +++++++++++++ .../dto/response/UserFavoriteArtistDTO.java | 15 +++++++++++++++ .../response/UserFavoriteArtistListDTO.java | 19 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistListDTO.java diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index e5d9eb4..d7aee02 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -2,6 +2,9 @@ import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; +import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; +import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; +import org.sopt.confeti.domain.artistfavorite.application.ArtistFavoriteService; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; @@ -9,6 +12,8 @@ import org.sopt.confeti.domain.user.application.UserService; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Facade @RequiredArgsConstructor public class UserFavoriteFacade { @@ -16,6 +21,7 @@ public class UserFavoriteFacade { private final UserService userService; private final FestivalService festivalService; private final FestivalFavoriteService festivalFavoriteService; + private final ArtistFavoriteService artistFavoriteService; @Transactional public void save(long userId, long festivalId) { @@ -30,4 +36,11 @@ public void delete(long userId, long festivalId) { Festival festival = festivalService.findById(festivalId); festivalFavoriteService.delete(user, festival); } + + @Transactional + public UserFavoriteArtistDTO getArtistList(long userId) { + User user = userService.findById(userId); + List artists = artistFavoriteService.getArtistList(user); + return UserFavoriteArtistDTO.of(artists); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistDTO.java new file mode 100644 index 0000000..bb6377a --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistDTO.java @@ -0,0 +1,15 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; + +import java.util.List; + +public record UserFavoriteArtistDTO (List artists) { + public static UserFavoriteArtistDTO of(final List artists) { + return new UserFavoriteArtistDTO( + artists.stream() + .map(UserFavoriteArtistListDTO::from) + .toList() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistListDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistListDTO.java new file mode 100644 index 0000000..56b38f6 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistListDTO.java @@ -0,0 +1,19 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; + +public record UserFavoriteArtistListDTO ( + String artistId, + String name, + String profileUrl +){ + public static UserFavoriteArtistListDTO from(final ArtistFavorite artistFavorite){ + return new UserFavoriteArtistListDTO( + artistFavorite.getArtist().getArtistId(), + artistFavorite.getArtist().getName(), + artistFavorite.getArtist().getProfileUrl() + ); + } +} + + From 894fdfd5c8d1e9597ba92d9dc056a12e55af8726 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Sat, 18 Jan 2025 01:24:39 +0900 Subject: [PATCH 13/96] =?UTF-8?q?feat=20[#57]=20ArtistFavoriteService,=20R?= =?UTF-8?q?epository=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ArtistFavoriteService.java | 26 +++++++++++++++++++ .../repository/ArtistFavoriteRepository.java | 3 +++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java new file mode 100644 index 0000000..3a5da90 --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java @@ -0,0 +1,26 @@ +package org.sopt.confeti.domain.artistfavorite.application; + +import lombok.AllArgsConstructor; +import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; +import org.sopt.confeti.domain.artistfavorite.infra.repository.ArtistFavoriteRepository; +import org.sopt.confeti.domain.user.User; +import org.sopt.confeti.global.util.artistsearcher.ArtistResolver; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@AllArgsConstructor +public class ArtistFavoriteService { + ArtistFavoriteRepository artistFavoriteRepository; + private final ArtistResolver artistResolver; + + @Transactional(readOnly = true) + public List getArtistList(User user) { + List artistList = artistFavoriteRepository.findAllByUserId(user.getId()); + artistResolver.load(artistList); + + return artistList; + } +} diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java index d12ad88..5ab3fe2 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java @@ -3,5 +3,8 @@ import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface ArtistFavoriteRepository extends JpaRepository { + List findAllByUserId(Long userId); } From 86b232a2f224f8aacfb3820e0e46ff752589bf20 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Sat, 18 Jan 2025 02:39:22 +0900 Subject: [PATCH 14/96] =?UTF-8?q?feat=20[#57]=20=EC=95=84=ED=8B=B0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=20=EC=B5=9C=EB=8C=80=20?= =?UTF-8?q?3=EA=B0=9C=EB=A7=8C=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artistfavorite/application/ArtistFavoriteService.java | 2 +- .../infra/repository/ArtistFavoriteRepository.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java index 3a5da90..1a3dd75 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java @@ -18,7 +18,7 @@ public class ArtistFavoriteService { @Transactional(readOnly = true) public List getArtistList(User user) { - List artistList = artistFavoriteRepository.findAllByUserId(user.getId()); + List artistList = artistFavoriteRepository.findTop3ByUserIdOrderByRand(user.getId()); artistResolver.load(artistList); return artistList; diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java index 5ab3fe2..888cf75 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java @@ -2,9 +2,12 @@ import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; public interface ArtistFavoriteRepository extends JpaRepository { - List findAllByUserId(Long userId); + @Query(value = "select * from artist_favorites where user_id = :userId order by rand() limit 3", nativeQuery = true) + List findTop3ByUserIdOrderByRand(@Param("userId") Long userId); } From 26748e1e403ee104b6ac3e50de999566abdc89c0 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Sat, 18 Jan 2025 03:29:29 +0900 Subject: [PATCH 15/96] =?UTF-8?q?refactor=20[#57]=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20of->from=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/confeti/api/user/facade/UserFavoriteFacade.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index d7aee02..24fe852 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -40,7 +40,8 @@ public void delete(long userId, long festivalId) { @Transactional public UserFavoriteArtistDTO getArtistList(long userId) { User user = userService.findById(userId); - List artists = artistFavoriteService.getArtistList(user); - return UserFavoriteArtistDTO.of(artists); + userService.existsById(userId); + List artists = artistFavoriteService.getArtistList(userId); + return UserFavoriteArtistDTO.from(artists); } } From 58b6857d1c680648289ef7344482756da5ce9205 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Sat, 18 Jan 2025 03:30:16 +0900 Subject: [PATCH 16/96] =?UTF-8?q?refactor=20[#57]=20=EC=9D=B8=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=20user=EC=9D=B4=20=EC=95=84=EB=8B=8C=20userId=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/facade/dto/response/UserFavoriteArtistDTO.java | 2 +- .../artistfavorite/application/ArtistFavoriteService.java | 4 ++-- .../sopt/confeti/domain/user/application/UserService.java | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistDTO.java index bb6377a..a3c0f8d 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistDTO.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoriteArtistDTO.java @@ -5,7 +5,7 @@ import java.util.List; public record UserFavoriteArtistDTO (List artists) { - public static UserFavoriteArtistDTO of(final List artists) { + public static UserFavoriteArtistDTO from(final List artists) { return new UserFavoriteArtistDTO( artists.stream() .map(UserFavoriteArtistListDTO::from) diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java index 1a3dd75..c196125 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java @@ -17,8 +17,8 @@ public class ArtistFavoriteService { private final ArtistResolver artistResolver; @Transactional(readOnly = true) - public List getArtistList(User user) { - List artistList = artistFavoriteRepository.findTop3ByUserIdOrderByRand(user.getId()); + public List getArtistList(Long userId) { + List artistList = artistFavoriteRepository.findTop3ByUserIdOrderByRand(userId); artistResolver.load(artistList); return artistList; diff --git a/src/main/java/org/sopt/confeti/domain/user/application/UserService.java b/src/main/java/org/sopt/confeti/domain/user/application/UserService.java index 35fee75..7d30903 100644 --- a/src/main/java/org/sopt/confeti/domain/user/application/UserService.java +++ b/src/main/java/org/sopt/confeti/domain/user/application/UserService.java @@ -23,4 +23,11 @@ public User getUserInfo(Long userId) { .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND)); return user; } + + public void existsById(Long userId) { + final boolean isExistUser = userRepository.existsById(userId); + if(!isExistUser) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } } From e07aa1a1ba38577c7d6257550129a9cfa644d534 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Sat, 18 Jan 2025 04:27:45 +0900 Subject: [PATCH 17/96] =?UTF-8?q?style=20[#57]=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/confeti/api/user/facade/UserFavoriteFacade.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index 24fe852..d382a98 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -39,7 +39,6 @@ public void delete(long userId, long festivalId) { @Transactional public UserFavoriteArtistDTO getArtistList(long userId) { - User user = userService.findById(userId); userService.existsById(userId); List artists = artistFavoriteService.getArtistList(userId); return UserFavoriteArtistDTO.from(artists); From c391ec443dc440221afeb705b413173a804e3575 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Sat, 18 Jan 2025 04:34:28 +0900 Subject: [PATCH 18/96] =?UTF-8?q?fix=20[#57]=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20of->from=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/api/user/controller/UserFavoriteController.java | 2 +- .../confeti/api/user/dto/response/UserFavoriteResponse.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index 4c4cdec..1f3d4cd 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -37,6 +37,6 @@ public ResponseEntity> deleteFavoriteFestival(@RequestHeader("Au @GetMapping("/artists") public ResponseEntity> getFavoriteArtists(@RequestHeader("Authorization") Long userId) { UserFavoriteArtistDTO userFavoriteArtistDTO = userFavoriteFacade.getArtistList(userId); - return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserFavoriteResponse.of(userFavoriteArtistDTO.artists())); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserFavoriteResponse.from(userFavoriteArtistDTO.artists())); } } diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java index 9a3c9c4..ad4cc54 100644 --- a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java @@ -5,7 +5,7 @@ import java.util.List; public record UserFavoriteResponse(List artists) { - public static UserFavoriteResponse of(final List artistListDTO) { + public static UserFavoriteResponse from(final List artistListDTO) { return new UserFavoriteResponse( artistListDTO.stream() .map(UserFavoriteListResponse::from) From 5f73308da1222a84e7f1459542e09ceea779cb3e Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 17:00:34 +0900 Subject: [PATCH 19/96] =?UTF-8?q?feat=20[#67]=20=ED=8E=98=EC=8A=A4?= =?UTF-8?q?=ED=8B=B0=EB=B2=8C=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 값을 추가하는 것으로 변경 --- build.gradle | 4 ++ .../controller/PerformanceController.java | 9 +++ .../FestivalDetailArtistResponse.java | 17 ++++++ .../response/FestivalDetailDateResponse.java | 29 ++++++++++ .../response/FestivalDetailInfoResponse.java | 45 +++++++++++++++ .../dto/response/FestivalDetailResponse.java | 23 ++++++++ .../performance/facade/PerformanceFacade.java | 18 +++++- .../dto/response/FestivalDetailArtistDTO.java | 22 ++++++++ .../dto/response/FestivalDetailDTO.java | 55 +++++++++++++++++++ .../dto/response/FestivalDetailDateDTO.java | 24 ++++++++ .../dto/response/FestivalDetailStageDTO.java | 22 ++++++++ .../dto/response/FestivalDetailTimeDTO.java | 24 ++++++++ .../festival/application/FestivalService.java | 11 ++++ .../domain/festivalartist/FestivalArtist.java | 2 +- .../application/FestivalFavoriteService.java | 4 ++ .../FestivalFavoriteRepository.java | 2 + .../confeti/global/message/ErrorMessage.java | 2 +- .../util/artistsearcher/ArtistResolver.java | 5 +- 18 files changed, 312 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailArtistResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailDateResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailArtistDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDateDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailStageDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailTimeDTO.java diff --git a/build.gradle b/build.gradle index 0963ce0..f1eb350 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' + // psy + implementation 'p6spy:p6spy:3.9.1' + implementation 'com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.0' + // spotify api implementation 'se.michaelthelin.spotify:spotify-web-api-java:8.4.1' diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index 20e4742..4593579 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -4,9 +4,11 @@ import lombok.RequiredArgsConstructor; import org.sopt.confeti.api.performance.dto.request.CreateFestivalRequest; import org.sopt.confeti.api.performance.dto.response.ConcertDetailResponse; +import org.sopt.confeti.api.performance.dto.response.FestivalDetailResponse; import org.sopt.confeti.api.performance.facade.PerformanceFacade; import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; +import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; import org.sopt.confeti.global.util.ApiResponseUtil; @@ -35,6 +37,13 @@ public ResponseEntity> getConcertInfo(@RequestHeader("Authorizat return ApiResponseUtil.success(SuccessMessage.SUCCESS, ConcertDetailResponse.from(concertDetailDTO)); } + @GetMapping("/festivals/{festivalId}") + public ResponseEntity> getFestivalInfo(@RequestHeader(name = "Authorization", required = false) Long userId, + @PathVariable("festivalId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) { + FestivalDetailDTO festivalDetailDTO = performanceFacade.getFestivalDetailInfo(userId, festivalId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, FestivalDetailResponse.from(festivalDetailDTO)); + } + @PostMapping("/festivals") public ResponseEntity> createConcert(@RequestBody CreateFestivalRequest createFestivalRequest) { performanceFacade.createFestival(CreateFestivalDTO.from(createFestivalRequest)); diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailArtistResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailArtistResponse.java new file mode 100644 index 0000000..59cbdfa --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailArtistResponse.java @@ -0,0 +1,17 @@ +package org.sopt.confeti.api.performance.dto.response; + +import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailArtistDTO; + +public record FestivalDetailArtistResponse( + String artistId, + String name, + String profileUrl +) { + public static FestivalDetailArtistResponse from(final FestivalDetailArtistDTO artist) { + return new FestivalDetailArtistResponse( + artist.artistId(), + artist.name(), + artist.profileUrl() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailDateResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailDateResponse.java new file mode 100644 index 0000000..be33adf --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailDateResponse.java @@ -0,0 +1,29 @@ +package org.sopt.confeti.api.performance.dto.response; + +import java.util.List; +import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDateDTO; + +public record FestivalDetailDateResponse( + long festivalDateId, + String festivalAt, + boolean isOpen, + List artists +) { + private static final int OPEN_CRITERIA = 4; + private static final String FESTIVAL_AT_PREFIX = "Day "; + + public static FestivalDetailDateResponse of(final FestivalDetailDateDTO festivalDate, final int order) { + List artists = festivalDate.stages().stream() + .flatMap(date -> date.times().stream()) + .flatMap(time -> time.artists().stream()) + .map(FestivalDetailArtistResponse::from) + .toList(); + + return new FestivalDetailDateResponse( + festivalDate.festivalDateId(), + FESTIVAL_AT_PREFIX + order, + artists.size() > OPEN_CRITERIA, + artists + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java new file mode 100644 index 0000000..28b220a --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java @@ -0,0 +1,45 @@ +package org.sopt.confeti.api.performance.dto.response; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; + +public record FestivalDetailInfoResponse( + long festivalId, + String posterUrl, + String posterBgUrl, + String title, + String subtitle, + LocalDate startAt, + LocalDate endAt, + String area, + LocalDate reserveAt, + String reservationUrl, + String time, + String ageRating, + String reservationOffice, + String price, + String infoImgUrl, + boolean isFavorite +) { + public static FestivalDetailInfoResponse from(final FestivalDetailDTO festival) { + return new FestivalDetailInfoResponse( + festival.festivalId(), + festival.festivalPosterUrl(), + festival.festivalPosterBgUrl(), + festival.festivalTitle(), + festival.festivalSubtitle(), + festival.festivalStartAt(), + festival.festivalEndAt(), + festival.festivalArea(), + festival.reserveAt(), + festival.reservationUrl(), + festival.time(), + festival.ageRating(), + festival.reservationOffice(), + festival.price(), + festival.festivalInfoImgUrl(), + festival.isFavorite() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailResponse.java new file mode 100644 index 0000000..7336386 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailResponse.java @@ -0,0 +1,23 @@ +package org.sopt.confeti.api.performance.dto.response; + +import java.util.List; +import java.util.stream.IntStream; +import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; + +public record FestivalDetailResponse( + FestivalDetailInfoResponse festival, + List festivalDates +) { + private static final int SPACE_BETWEEN_DATE_AND_IDX = 1; + public static FestivalDetailResponse from(final FestivalDetailDTO festival) { + return new FestivalDetailResponse( + FestivalDetailInfoResponse.from(festival), + IntStream.range(0, festival.dates().size()) + .mapToObj(idx -> + FestivalDetailDateResponse.of( + festival.dates().get(idx), idx + SPACE_BETWEEN_DATE_AND_IDX) + ) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index e63e588..1ce7677 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -4,9 +4,12 @@ import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; +import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.application.ConcertService; +import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; +import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; import org.sopt.confeti.global.util.S3FileHandler; import org.springframework.transaction.annotation.Transactional; @@ -16,9 +19,10 @@ public class PerformanceFacade { private final ConcertService concertService; private final FestivalService festivalService; + private final FestivalFavoriteService festivalFavoriteService; private final S3FileHandler s3FileHandler; - @Transactional + @Transactional(readOnly = true) public ConcertDetailDTO getConcertDetailInfo(final long concertId) { Concert concert = concertService.getConcertDetailByConcertId(concertId); return ConcertDetailDTO.of(concert, s3FileHandler); @@ -28,4 +32,16 @@ public ConcertDetailDTO getConcertDetailInfo(final long concertId) { public void createFestival(final CreateFestivalDTO createFestivalDTO) { festivalService.create(createFestivalDTO); } + + @Transactional(readOnly = true) + public FestivalDetailDTO getFestivalDetailInfo(final Long userId, final long festivalId) { + boolean isFavorite = false; + + if (userId != null) { + isFavorite = festivalFavoriteService.isFavorite(userId, festivalId); + } + + Festival festival = festivalService.getFestivalDetailByFestivalId(festivalId); + return FestivalDetailDTO.of(festival, isFavorite, s3FileHandler); + } } diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailArtistDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailArtistDTO.java new file mode 100644 index 0000000..2a50b99 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailArtistDTO.java @@ -0,0 +1,22 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.time.LocalDate; +import org.sopt.confeti.domain.festivalartist.FestivalArtist; +import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; + +public record FestivalDetailArtistDTO( + String artistId, + String name, + String profileUrl, + LocalDate latestReleaseAt +) { + public static FestivalDetailArtistDTO from(final FestivalArtist festivalArtist) { + ConfetiArtist confetiArtist = festivalArtist.getArtist(); + return new FestivalDetailArtistDTO( + confetiArtist.getArtistId(), + confetiArtist.getName(), + confetiArtist.getProfileUrl(), + confetiArtist.getLatestReleaseAt() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDTO.java new file mode 100644 index 0000000..d241bdd --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDTO.java @@ -0,0 +1,55 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import org.sopt.confeti.domain.festival.Festival; +import org.sopt.confeti.global.util.S3FileHandler; + +public record FestivalDetailDTO( + long festivalId, + String festivalTitle, + String festivalSubtitle, + LocalDate festivalStartAt, + LocalDate festivalEndAt, + String festivalArea, + String festivalPosterUrl, + String festivalPosterBgUrl, + String festivalInfoImgUrl, + String festivalReservationBgUrl, + String festivalLogoUrl, + LocalDate reserveAt, + String reservationUrl, + String reservationOffice, + String ageRating, + String time, + String price, + boolean isFavorite, + List dates +) { + public static FestivalDetailDTO of(final Festival festival, boolean isFavorite, final S3FileHandler s3FileHandler) { + return new FestivalDetailDTO( + festival.getId(), + festival.getFestivalTitle(), + festival.getFestivalSubtitle(), + festival.getFestivalStartAt(), + festival.getFestivalEndAt(), + festival.getFestivalArea(), + s3FileHandler.getFileUrl(festival.getFestivalPosterPath()), + s3FileHandler.getFileUrl(festival.getFestivalPosterBgPath()), + s3FileHandler.getFileUrl(festival.getFestivalInfoImgPath()), + s3FileHandler.getFileUrl(festival.getFestivalReservationBgPath()), + s3FileHandler.getFileUrl(festival.getFestivalLogoPath()), + festival.getReserveAt(), + festival.getReservationUrl(), + festival.getReservationOffice(), + festival.getAgeRating(), + festival.getTime(), + festival.getPrice(), + isFavorite, + festival.getDates().stream() + .map(FestivalDetailDateDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDateDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDateDTO.java new file mode 100644 index 0000000..65ece6d --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDateDTO.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import org.sopt.confeti.domain.festivaldate.FestivalDate; + +public record FestivalDetailDateDTO( + long festivalDateId, + LocalDate festivalAt, + LocalTime openAt, + List stages +) { + public static FestivalDetailDateDTO from(final FestivalDate festivalDate) { + return new FestivalDetailDateDTO( + festivalDate.getId(), + festivalDate.getFestivalAt(), + festivalDate.getOpenAt(), + festivalDate.getStages().stream() + .map(FestivalDetailStageDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailStageDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailStageDTO.java new file mode 100644 index 0000000..1827961 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailStageDTO.java @@ -0,0 +1,22 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.util.List; +import org.sopt.confeti.domain.festivalstage.FestivalStage; + +public record FestivalDetailStageDTO( + long festivalStageId, + String name, + int order, + List times +) { + public static FestivalDetailStageDTO from(final FestivalStage festivalStage) { + return new FestivalDetailStageDTO( + festivalStage.getId(), + festivalStage.getName(), + festivalStage.getOrder(), + festivalStage.getTimes().stream() + .map(FestivalDetailTimeDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailTimeDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailTimeDTO.java new file mode 100644 index 0000000..86242fe --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailTimeDTO.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import org.sopt.confeti.domain.festivaltime.FestivalTime; + +public record FestivalDetailTimeDTO( + long festivalTimeId, + LocalTime startAt, + LocalTime endAt, + List artists +) { + public static FestivalDetailTimeDTO from(final FestivalTime festivalTime) { + return new FestivalDetailTimeDTO( + festivalTime.getId(), + festivalTime.getStartAt(), + festivalTime.getEndAt(), + festivalTime.getArtists().stream() + .map(FestivalDetailArtistDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index 7f81a2a..feff146 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -6,12 +6,15 @@ 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.stereotype.Service; @Service @RequiredArgsConstructor public class FestivalService { + private final FestivalRepository festivalRepository; + private final ArtistResolver artistResolver; public Festival findById(Long festivalId) { Festival festival = festivalRepository.findById(festivalId) @@ -19,6 +22,14 @@ public Festival findById(Long festivalId) { return festival; } + public Festival getFestivalDetailByFestivalId(final long festivalId) { + Festival festival = festivalRepository.findById(festivalId) + .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND)); + artistResolver.load(festival); + + return festival; + } + public void create(final CreateFestivalDTO createFestivalDTO) { festivalRepository.save( Festival.create(createFestivalDTO) diff --git a/src/main/java/org/sopt/confeti/domain/festivalartist/FestivalArtist.java b/src/main/java/org/sopt/confeti/domain/festivalartist/FestivalArtist.java index 65039f5..6d9234e 100644 --- a/src/main/java/org/sopt/confeti/domain/festivalartist/FestivalArtist.java +++ b/src/main/java/org/sopt/confeti/domain/festivalartist/FestivalArtist.java @@ -17,7 +17,7 @@ import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; @Entity -@Table(name="festival_artist") +@Table(name="festival_artists") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class FestivalArtist { diff --git a/src/main/java/org/sopt/confeti/domain/festivalfavorite/application/FestivalFavoriteService.java b/src/main/java/org/sopt/confeti/domain/festivalfavorite/application/FestivalFavoriteService.java index 4003264..157c8f7 100644 --- a/src/main/java/org/sopt/confeti/domain/festivalfavorite/application/FestivalFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/festivalfavorite/application/FestivalFavoriteService.java @@ -31,4 +31,8 @@ public void delete(User user, Festival festival) { festivalFavoriteRepository.delete(festivalFavorite); } + + public boolean isFavorite(final long userId, final long festivalId) { + return festivalFavoriteRepository.existsByUserIdAndFestivalId(userId, festivalId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festivalfavorite/infra/repository/FestivalFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/festivalfavorite/infra/repository/FestivalFavoriteRepository.java index c0fa65f..aa738d4 100644 --- a/src/main/java/org/sopt/confeti/domain/festivalfavorite/infra/repository/FestivalFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festivalfavorite/infra/repository/FestivalFavoriteRepository.java @@ -9,4 +9,6 @@ public interface FestivalFavoriteRepository extends JpaRepository { Optional findByUserIdAndFestivalId(long userId, long festivalId); + + boolean existsByUserIdAndFestivalId(long userId, long festivalId); } diff --git a/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java b/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java index 1aec2cb..eed3881 100644 --- a/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java +++ b/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java @@ -28,7 +28,7 @@ public enum ErrorMessage { private final HttpStatus httpStatus; private final String message; - private ErrorMessage(HttpStatus httpStatus, String message) { + ErrorMessage(HttpStatus httpStatus, String message) { this.httpStatus = httpStatus; this.message = message; } diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java index f92149c..77eb958 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ArtistResolver.java @@ -82,7 +82,6 @@ private void collect(final Object target) { return; } - // Mapper에 등록된 클래스 타입인 경우 if (collectByTypeMapper.containsKey(target.getClass())) { collectByTypeMapper.get(target.getClass()).apply(target); @@ -118,7 +117,7 @@ private void collectFestival(final Object target) { .flatMap(stage -> stage.getTimes().stream()) .flatMap(time -> time.getArtists().stream()) .forEach(artist -> { - artistIds.add(artist.getArtist().getName()); + artistIds.add(artist.getArtist().getArtistId()); artistMapper.put(artist.getArtist().getArtistId(), artist.getArtist()); }); } @@ -129,7 +128,7 @@ private void collectFestivalDate(final Object target) { .flatMap(stage -> stage.getTimes().stream()) .flatMap(time -> time.getArtists().stream()) .forEach(artist -> { - artistIds.add(artist.getArtist().getName()); + artistIds.add(artist.getArtist().getArtistId()); artistMapper.put(artist.getArtist().getArtistId(), artist.getArtist()); }); } From 3de20b48256909ebde8a819cbe8b0be581a0ab53 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 16:29:01 +0900 Subject: [PATCH 20/96] =?UTF-8?q?feat=20[#85]=20=EC=95=84=ED=8B=B0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B2=80=EC=83=89=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artist/controller/ArtistController.java | 33 ++++++++++++++++++ .../confeti/api/artist/dto/request/.gitkeep | 0 .../dto/response/SearchArtistResponse.java | 13 +++++++ .../response/SearchArtistSingleResponse.java | 24 +++++++++++++ .../api/artist/facade/ArtistFacade.java | 34 +++++++++++++++++++ .../facade/dto/response/SearchArtistDTO.java | 26 ++++++++++++++ .../org/sopt/confeti/api/artist/vo/.gitkeep | 0 .../application/ArtistFavoriteService.java | 5 +++ .../repository/ArtistFavoriteRepository.java | 2 ++ .../util/artistsearcher/ConfetiArtist.java | 4 +++ 10 files changed, 141 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/artist/controller/ArtistController.java create mode 100644 src/main/java/org/sopt/confeti/api/artist/dto/request/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistSingleResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/artist/facade/ArtistFacade.java create mode 100644 src/main/java/org/sopt/confeti/api/artist/facade/dto/response/SearchArtistDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/artist/vo/.gitkeep diff --git a/src/main/java/org/sopt/confeti/api/artist/controller/ArtistController.java b/src/main/java/org/sopt/confeti/api/artist/controller/ArtistController.java new file mode 100644 index 0000000..82d8946 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/artist/controller/ArtistController.java @@ -0,0 +1,33 @@ +package org.sopt.confeti.api.artist.controller; + +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.api.artist.dto.response.SearchArtistResponse; +import org.sopt.confeti.api.artist.facade.ArtistFacade; +import org.sopt.confeti.api.artist.facade.dto.response.SearchArtistDTO; +import org.sopt.confeti.api.user.facade.UserInfoFacade; +import org.sopt.confeti.global.common.BaseResponse; +import org.sopt.confeti.global.message.SuccessMessage; +import org.sopt.confeti.global.util.ApiResponseUtil; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +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 +@RequiredArgsConstructor +@RequestMapping("/artists") +public class ArtistController { + + private final ArtistFacade artistFacade; + + @GetMapping + public ResponseEntity> search( + @RequestHeader(name = "Authorization", required = false) Long userId, + @RequestParam(name = "search") String keyword + ) { + SearchArtistDTO artist = artistFacade.searchByKeyword(userId, keyword); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, SearchArtistResponse.from(artist)); + } +} diff --git a/src/main/java/org/sopt/confeti/api/artist/dto/request/.gitkeep b/src/main/java/org/sopt/confeti/api/artist/dto/request/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistResponse.java b/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistResponse.java new file mode 100644 index 0000000..f2d7ee3 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistResponse.java @@ -0,0 +1,13 @@ +package org.sopt.confeti.api.artist.dto.response; + +import org.sopt.confeti.api.artist.facade.dto.response.SearchArtistDTO; + +public record SearchArtistResponse( + SearchArtistSingleResponse artist +) { + public static SearchArtistResponse from(final SearchArtistDTO searchArtistDTO) { + return new SearchArtistResponse( + SearchArtistSingleResponse.from(searchArtistDTO) + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistSingleResponse.java b/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistSingleResponse.java new file mode 100644 index 0000000..9ab6e7f --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistSingleResponse.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.api.artist.dto.response; + +import java.time.LocalDate; +import org.sopt.confeti.api.artist.facade.dto.response.SearchArtistDTO; + +public record SearchArtistSingleResponse( + String artistId, + String name, + String profileUrl, + LocalDate latestReleaseAt, + boolean isFavorite, + boolean isMultipleArtists +) { + public static SearchArtistSingleResponse from(final SearchArtistDTO searchArtistDTO) { + return new SearchArtistSingleResponse( + searchArtistDTO.artistId(), + searchArtistDTO.name(), + searchArtistDTO.profileUrl(), + searchArtistDTO.latestReleaseAt(), + searchArtistDTO.isFavorite(), + searchArtistDTO.isMultipleArtists() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/artist/facade/ArtistFacade.java b/src/main/java/org/sopt/confeti/api/artist/facade/ArtistFacade.java new file mode 100644 index 0000000..3cc2c60 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/artist/facade/ArtistFacade.java @@ -0,0 +1,34 @@ +package org.sopt.confeti.api.artist.facade; + +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.annotation.Facade; +import org.sopt.confeti.api.artist.facade.dto.response.SearchArtistDTO; +import org.sopt.confeti.domain.artistfavorite.application.ArtistFavoriteService; +import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; +import org.sopt.confeti.global.util.artistsearcher.SpotifyAPIHandler; +import org.springframework.transaction.annotation.Transactional; + +@Facade +@RequiredArgsConstructor +public class ArtistFacade { + + private final ArtistFavoriteService artistFavoriteService; + private final SpotifyAPIHandler spotifyAPIHandler; + + @Transactional(readOnly = true) + public SearchArtistDTO searchByKeyword(final Long userId, final String keyword) { + Optional confetiArtist = spotifyAPIHandler.findArtistsByKeyword(keyword); + + boolean isFavorite = false; + + if (confetiArtist.isPresent() && userId != null) { + isFavorite = artistFavoriteService.isFavorite(userId, confetiArtist.get().getArtistId()); + } + + return SearchArtistDTO.from( + confetiArtist.orElse(ConfetiArtist.empty()), + isFavorite + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/artist/facade/dto/response/SearchArtistDTO.java b/src/main/java/org/sopt/confeti/api/artist/facade/dto/response/SearchArtistDTO.java new file mode 100644 index 0000000..d3e2187 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/artist/facade/dto/response/SearchArtistDTO.java @@ -0,0 +1,26 @@ +package org.sopt.confeti.api.artist.facade.dto.response; + +import java.time.LocalDate; +import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; + +public record SearchArtistDTO( + String artistId, + String name, + String profileUrl, + LocalDate latestReleaseAt, + boolean isFavorite, + boolean isMultipleArtists +) { + private static final boolean fixedIsMultipleArtists = false; + + public static SearchArtistDTO from(final ConfetiArtist confetiArtist, final boolean isFavorite) { + return new SearchArtistDTO( + confetiArtist.getArtistId(), + confetiArtist.getName(), + confetiArtist.getProfileUrl(), + confetiArtist.getLatestReleaseAt(), + isFavorite, + fixedIsMultipleArtists + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/artist/vo/.gitkeep b/src/main/java/org/sopt/confeti/api/artist/vo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java index c196125..7851eb6 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java @@ -23,4 +23,9 @@ public List getArtistList(Long userId) { return artistList; } + + @Transactional(readOnly = true) + public boolean isFavorite(final long userId, final String artistId) { + return artistFavoriteRepository.existsByUserIdAndArtist_ArtistId(userId, artistId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java index 888cf75..8227ef1 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java @@ -10,4 +10,6 @@ public interface ArtistFavoriteRepository extends JpaRepository { @Query(value = "select * from artist_favorites where user_id = :userId order by rand() limit 3", nativeQuery = true) List findTop3ByUserIdOrderByRand(@Param("userId") Long userId); + + boolean existsByUserIdAndArtist_ArtistId(long userId, String artistId); } diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ConfetiArtist.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ConfetiArtist.java index 1e9c41e..6a7f804 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/ConfetiArtist.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/ConfetiArtist.java @@ -64,4 +64,8 @@ public static ConfetiArtist toConfetiArtist(final Artist artist, final LocalDate public static ConfetiArtist from(final String artistId) { return new ConfetiArtist(artistId); } + + public static ConfetiArtist empty() { + return new ConfetiArtist(); + } } From 722fbc8e76a2a63d69a227631cb7ff4d29e1f76f Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 16:46:38 +0900 Subject: [PATCH 21/96] =?UTF-8?q?refactor=20[#87]=20=EC=BD=98=EC=84=9C?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PerformanceController.java | 10 +++++++--- .../response/ConcertDetailInfoResponse.java | 9 +++++---- .../dto/response/ConcertDetailResponse.java | 17 ++++++++++++----- .../performance/facade/PerformanceFacade.java | 2 +- .../facade/dto/response/ConcertDetailDTO.java | 19 +++++++++---------- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index 4593579..abd4936 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -12,6 +12,7 @@ import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; import org.sopt.confeti.global.util.ApiResponseUtil; +import org.sopt.confeti.global.util.S3FileHandler; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -29,12 +30,15 @@ public class PerformanceController { private final PerformanceFacade performanceFacade; + private final S3FileHandler s3FileHandler; @GetMapping("/concerts/{concertId}") - public ResponseEntity> getConcertInfo(@RequestHeader("Authorization") Long userId, - @PathVariable("concertId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long concertId) { + public ResponseEntity> getConcertInfo( + @RequestHeader(name = "Authorization", required = false) Long userId, + @PathVariable("concertId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long concertId + ) { ConcertDetailDTO concertDetailDTO = performanceFacade.getConcertDetailInfo(concertId); - return ApiResponseUtil.success(SuccessMessage.SUCCESS, ConcertDetailResponse.from(concertDetailDTO)); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, ConcertDetailResponse.of(concertDetailDTO, s3FileHandler)); } @GetMapping("/festivals/{festivalId}") diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java index 236e6b9..e6da7a9 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java @@ -3,6 +3,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; +import org.sopt.confeti.global.util.S3FileHandler; public record ConcertDetailInfoResponse( long concertId, @@ -21,11 +22,11 @@ public record ConcertDetailInfoResponse( String price, String infoImgUrl ) { - public static ConcertDetailInfoResponse from(final ConcertDetailDTO concertDetailDTO) { + public static ConcertDetailInfoResponse of(final ConcertDetailDTO concertDetailDTO, final S3FileHandler s3FileHandler) { return new ConcertDetailInfoResponse( concertDetailDTO.concertId(), - concertDetailDTO.posterUrl(), - concertDetailDTO.posterBgUrl(), + s3FileHandler.getFileUrl(concertDetailDTO.posterPath()), + s3FileHandler.getFileUrl(concertDetailDTO.posterBgPath()), concertDetailDTO.title(), concertDetailDTO.subtitle(), concertDetailDTO.startAt(), @@ -37,7 +38,7 @@ public static ConcertDetailInfoResponse from(final ConcertDetailDTO concertDetai concertDetailDTO.ageRating(), concertDetailDTO.reservationOffice(), concertDetailDTO.price(), - concertDetailDTO.infoImgUrl() + s3FileHandler.getFileUrl(concertDetailDTO.infoImgPath()) ); } } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java index 4d3fe64..8abdd30 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java @@ -2,17 +2,24 @@ import java.util.List; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; +import org.sopt.confeti.global.util.S3FileHandler; public record ConcertDetailResponse( ConcertDetailInfoResponse concert, + boolean isOpen, List concertArtists ) { - public static ConcertDetailResponse from(final ConcertDetailDTO concertDetailDTO) { + private static final int OPEN_CRITERIA = 4; + + public static ConcertDetailResponse of(final ConcertDetailDTO concertDetailDTO, final S3FileHandler s3FileHandler) { + List concertArtists = concertDetailDTO.artists().stream() + .map(ConcertDetailArtistResponse::from) + .toList(); + return new ConcertDetailResponse( - ConcertDetailInfoResponse.from(concertDetailDTO), - concertDetailDTO.artists().stream() - .map(ConcertDetailArtistResponse::from) - .toList() + ConcertDetailInfoResponse.of(concertDetailDTO, s3FileHandler), + concertArtists.size() > OPEN_CRITERIA, + concertArtists ); } } diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index 1ce7677..6f66399 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -25,7 +25,7 @@ public class PerformanceFacade { @Transactional(readOnly = true) public ConcertDetailDTO getConcertDetailInfo(final long concertId) { Concert concert = concertService.getConcertDetailByConcertId(concertId); - return ConcertDetailDTO.of(concert, s3FileHandler); + return ConcertDetailDTO.from(concert); } @Transactional diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java index 26e9af7..a2b2412 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java @@ -4,7 +4,6 @@ import java.time.LocalDateTime; import java.util.List; import org.sopt.confeti.domain.concert.Concert; -import org.sopt.confeti.global.util.S3FileHandler; public record ConcertDetailDTO( long concertId, @@ -13,10 +12,10 @@ public record ConcertDetailDTO( LocalDate startAt, LocalDate endAt, String area, - String posterUrl, - String posterBgUrl, - String infoImgUrl, - String concertReservationBgUrl, + String posterPath, + String posterBgPath, + String infoImgPath, + String concertReservationBgPath, LocalDateTime reserveAt, String reservationUrl, String reservationOffice, @@ -25,7 +24,7 @@ public record ConcertDetailDTO( String price, List artists ) { - public static ConcertDetailDTO of(final Concert concert, final S3FileHandler s3FileHandler) { + public static ConcertDetailDTO from(final Concert concert) { return new ConcertDetailDTO( concert.getId(), concert.getConcertTitle(), @@ -33,10 +32,10 @@ public static ConcertDetailDTO of(final Concert concert, final S3FileHandler s3F concert.getConcertStartAt(), concert.getConcertEndAt(), concert.getConcertArea(), - s3FileHandler.getFileUrl(concert.getConcertPosterPath()), - s3FileHandler.getFileUrl(concert.getConcertPosterBgPath()), - s3FileHandler.getFileUrl(concert.getConcertInfoImgPath()), - s3FileHandler.getFileUrl(concert.getConcertReservationBgPath()), + concert.getConcertPosterPath(), + concert.getConcertPosterBgPath(), + concert.getConcertInfoImgPath(), + concert.getConcertReservationBgPath(), concert.getReserveAt(), concert.getReservationUrl(), concert.getReservationOffice(), From 0d10105cd1ea71db095417254e81377e54f73e46 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 17:45:25 +0900 Subject: [PATCH 22/96] =?UTF-8?q?refactor=20[#89]=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=AA=85=EC=9D=84=20=EB=AA=85=ED=99=95=ED=9E=88=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=A1=B0=ED=9A=8C=20=ED=95=A8=EC=88=98=EC=97=90?= =?UTF-8?q?=20=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20readonly=20=3D=20true?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/confeti/api/user/facade/UserFavoriteFacade.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index d382a98..3d77eea 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -24,20 +24,20 @@ public class UserFavoriteFacade { private final ArtistFavoriteService artistFavoriteService; @Transactional - public void save(long userId, long festivalId) { + public void addFestivalFavorite(long userId, long festivalId) { User user = userService.findById(userId); Festival festival = festivalService.findById(festivalId); festivalFavoriteService.save(user, festival); } @Transactional - public void delete(long userId, long festivalId) { + public void removeFestivalFavorite(long userId, long festivalId) { User user = userService.findById(userId); Festival festival = festivalService.findById(festivalId); festivalFavoriteService.delete(user, festival); } - @Transactional + @Transactional(readOnly = true) public UserFavoriteArtistDTO getArtistList(long userId) { userService.existsById(userId); List artists = artistFavoriteService.getArtistList(userId); From 99dd3ed0a626dc19e15caaa3b0830ead5145aed2 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 18:10:46 +0900 Subject: [PATCH 23/96] =?UTF-8?q?feat=20[#89]=20=EC=95=84=ED=8B=B0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/controller/UserFavoriteController.java | 13 +++++++++++-- .../confeti/api/user/facade/UserFavoriteFacade.java | 13 +++++++++++++ .../application/ArtistFavoriteService.java | 8 ++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index 1f3d4cd..81dce3a 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -23,14 +23,14 @@ public class UserFavoriteController { @PostMapping("/festivals/{festivalId}") public ResponseEntity> postFavoriteFestival(@RequestHeader("Authorization") Long userId, @PathVariable @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) { - userFavoriteFacade.save(userId, festivalId); + userFavoriteFacade.addFestivalFavorite(userId, festivalId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } @DeleteMapping("/festivals/{festivalId}") public ResponseEntity> deleteFavoriteFestival(@RequestHeader("Authorization") Long userId, @PathVariable @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) { - userFavoriteFacade.delete(userId, festivalId); + userFavoriteFacade.removeFestivalFavorite(userId, festivalId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } @@ -39,4 +39,13 @@ public ResponseEntity> getFavoriteArtists(@RequestHeader("Author UserFavoriteArtistDTO userFavoriteArtistDTO = userFavoriteFacade.getArtistList(userId); return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserFavoriteResponse.from(userFavoriteArtistDTO.artists())); } + + @PostMapping("/artists/{artistId}") + public ResponseEntity> addArtistFavorite( + @RequestHeader("Authorization") Long userId, + @PathVariable(name = "artistId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") String artistId + ) { + userFavoriteFacade.addArtistFavorite(userId, artistId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index 3d77eea..fdf68ab 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -10,6 +10,8 @@ import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; import org.sopt.confeti.domain.user.User; import org.sopt.confeti.domain.user.application.UserService; +import org.sopt.confeti.global.exception.ConflictException; +import org.sopt.confeti.global.message.ErrorMessage; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -43,4 +45,15 @@ public UserFavoriteArtistDTO getArtistList(long userId) { List artists = artistFavoriteService.getArtistList(userId); return UserFavoriteArtistDTO.from(artists); } + + @Transactional + public void addArtistFavorite(final long userId, final String artistId) { + User user = userService.findById(userId); + + if (artistFavoriteService.isFavorite(userId, artistId)) { + throw new ConflictException(ErrorMessage.CONFLICT); + } + + artistFavoriteService.addFavorite(user, artistId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java index 7851eb6..17ec5ef 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java @@ -5,6 +5,7 @@ import org.sopt.confeti.domain.artistfavorite.infra.repository.ArtistFavoriteRepository; import org.sopt.confeti.domain.user.User; import org.sopt.confeti.global.util.artistsearcher.ArtistResolver; +import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -28,4 +29,11 @@ public List getArtistList(Long userId) { public boolean isFavorite(final long userId, final String artistId) { return artistFavoriteRepository.existsByUserIdAndArtist_ArtistId(userId, artistId); } + + @Transactional + public void addFavorite(final User user, final String artistId) { + artistFavoriteRepository.save( + ArtistFavorite.create(user, artistId) + ); + } } From df131d99cdb570f4393f71fe8f6f85d3b12b4771 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 18:38:43 +0900 Subject: [PATCH 24/96] =?UTF-8?q?refactor=20[#89]=20addArtistFavorite?= =?UTF-8?q?=EC=97=90=EC=84=9C=20artistId=20=EC=B5=9C=EC=86=8C=EA=B0=92=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/api/user/controller/UserFavoriteController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index 81dce3a..7793cf2 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -43,7 +43,7 @@ public ResponseEntity> getFavoriteArtists(@RequestHeader("Author @PostMapping("/artists/{artistId}") public ResponseEntity> addArtistFavorite( @RequestHeader("Authorization") Long userId, - @PathVariable(name = "artistId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") String artistId + @PathVariable(name = "artistId") String artistId ) { userFavoriteFacade.addArtistFavorite(userId, artistId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); From 2e82b9a2ef0ab595de7b36d497666916e68bdaee Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 18:36:36 +0900 Subject: [PATCH 25/96] =?UTF-8?q?feat=20[#90]=20=EC=95=84=ED=8B=B0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/controller/UserFavoriteController.java | 9 +++++++++ .../confeti/api/user/facade/UserFavoriteFacade.java | 12 ++++++++++++ .../application/ArtistFavoriteService.java | 5 +++++ .../infra/repository/ArtistFavoriteRepository.java | 2 ++ 4 files changed, 28 insertions(+) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index 7793cf2..1b09379 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -48,4 +48,13 @@ public ResponseEntity> addArtistFavorite( userFavoriteFacade.addArtistFavorite(userId, artistId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } + + @DeleteMapping("/artists/{artistId}") + public ResponseEntity> removeArtistFavorite( + @RequestHeader("Authorization") Long userId, + @PathVariable(name = "artistId") String artistId + ) { + userFavoriteFacade.removeArtistFavorite(userId, artistId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index fdf68ab..319af46 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -1,5 +1,6 @@ package org.sopt.confeti.api.user.facade; +import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; @@ -56,4 +57,15 @@ public void addArtistFavorite(final long userId, final String artistId) { artistFavoriteService.addFavorite(user, artistId); } + + @Transactional + public void removeArtistFavorite(final long userId, final String artistId) { + userService.existsById(userId); + + if (!artistFavoriteService.isFavorite(userId, artistId)) { + throw new ConflictException(ErrorMessage.CONFLICT); + } + + artistFavoriteService.removeFavorite(userId, artistId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java index 17ec5ef..ab62bfa 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java @@ -36,4 +36,9 @@ public void addFavorite(final User user, final String artistId) { ArtistFavorite.create(user, artistId) ); } + + @Transactional + public void removeFavorite(final long userId, final String artistId) { + artistFavoriteRepository.deleteByUserIdAndArtist_ArtistId(userId, artistId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java index 8227ef1..60e099f 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java @@ -12,4 +12,6 @@ public interface ArtistFavoriteRepository extends JpaRepository findTop3ByUserIdOrderByRand(@Param("userId") Long userId); boolean existsByUserIdAndArtist_ArtistId(long userId, String artistId); + + void deleteByUserIdAndArtist_ArtistId(final long userId, final String artistId); } From ef6d627654ade8912ce978ff0590de38be2db02b Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 18:44:00 +0900 Subject: [PATCH 26/96] =?UTF-8?q?refactor=20[#90]=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=EB=A5=BC=20=EB=88=84=EB=A5=B4=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=95=98=EC=9D=84=20=EB=95=8C=20NOT=20FOUND=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/confeti/api/user/facade/UserFavoriteFacade.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index 319af46..db390cf 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -12,6 +12,7 @@ import org.sopt.confeti.domain.user.User; import org.sopt.confeti.domain.user.application.UserService; import org.sopt.confeti.global.exception.ConflictException; +import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.message.ErrorMessage; import org.springframework.transaction.annotation.Transactional; @@ -63,7 +64,7 @@ public void removeArtistFavorite(final long userId, final String artistId) { userService.existsById(userId); if (!artistFavoriteService.isFavorite(userId, artistId)) { - throw new ConflictException(ErrorMessage.CONFLICT); + throw new NotFoundException(ErrorMessage.NOT_FOUND); } artistFavoriteService.removeFavorite(userId, artistId); From 86b463a76171e40cffadb106b4e059a19607ff86 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 19:43:35 +0900 Subject: [PATCH 27/96] =?UTF-8?q?feat=20[#91]=20=EC=BD=98=EC=84=9C?= =?UTF-8?q?=ED=8A=B8=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat [#91] 콘서트 좋아요 추가 API 추가 * feat [#90] 아티스트 좋아요 삭제 API 구현 * refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경 * feat [#91] 콘서트 좋아요 추가 API 추가 * style [#92] 함수 위치 재배치 --- .../controller/UserFavoriteController.java | 9 +++++++ .../api/user/facade/UserFavoriteFacade.java | 17 ++++++++++++ .../concert/application/ConcertService.java | 7 +++++ .../application/ConcertFavoriteService.java | 26 +++++++++++++++++++ .../repository/ConcertFavoriteRepository.java | 1 + 5 files changed, 60 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index 1b09379..c36f9fa 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -57,4 +57,13 @@ public ResponseEntity> removeArtistFavorite( userFavoriteFacade.removeArtistFavorite(userId, artistId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } + + @PostMapping("/concerts/{concertId}") + public ResponseEntity> addConcertFavorite( + @RequestHeader("Authorization") Long userId, + @PathVariable(name = "concertId") Long concertId + ) { + userFavoriteFacade.addConcertFavorite(userId, concertId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index db390cf..3fa087c 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -6,6 +6,9 @@ import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; import org.sopt.confeti.domain.artistfavorite.application.ArtistFavoriteService; +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.domain.concert.application.ConcertService; +import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; @@ -26,6 +29,8 @@ public class UserFavoriteFacade { private final FestivalService festivalService; private final FestivalFavoriteService festivalFavoriteService; private final ArtistFavoriteService artistFavoriteService; + private final ConcertFavoriteService concertFavoriteService; + private final ConcertService concertService; @Transactional public void addFestivalFavorite(long userId, long festivalId) { @@ -69,4 +74,16 @@ public void removeArtistFavorite(final long userId, final String artistId) { artistFavoriteService.removeFavorite(userId, artistId); } + + @Transactional + public void addConcertFavorite(final long userId, final long concertId) { + User user = userService.findById(userId); + Concert concert = concertService.findById(concertId); + + if (concertFavoriteService.isFavorite(userId, concertId)) { + throw new ConflictException(ErrorMessage.CONFLICT); + } + + concertFavoriteService.addFavorite(user, concert); + } } diff --git a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java index 59fbf81..e6f06fc 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java +++ b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java @@ -27,4 +27,11 @@ public Concert getConcertDetailByConcertId(final long concertId) { return concert; } + + public Concert findById(final long concertId) { + return concertRepository.findById(concertId) + .orElseThrow( + () -> new NotFoundException(ErrorMessage.NOT_FOUND) + ); + } } diff --git a/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java b/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java new file mode 100644 index 0000000..60da6e4 --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java @@ -0,0 +1,26 @@ +package org.sopt.confeti.domain.concertfavorite.application; + +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.domain.concertfavorite.ConcertFavorite; +import org.sopt.confeti.domain.concertfavorite.infra.repository.ConcertFavoriteRepository; +import org.sopt.confeti.domain.user.User; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ConcertFavoriteService { + + private final ConcertFavoriteRepository concertFavoriteRepository; + + + public boolean isFavorite(final long userId, final long concertId) { + return concertFavoriteRepository.existsByUserIdAndConcertId(userId, concertId); + } + + public void addFavorite(final User user, final Concert concert) { + concertFavoriteRepository.save( + ConcertFavorite.create(user, concert) + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java index 22a7a5a..1506481 100644 --- a/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java @@ -4,4 +4,5 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface ConcertFavoriteRepository extends JpaRepository { + boolean existsByUserIdAndConcertId(final long userId, final long concertId); } From 0e0150f25c525dd204c7546f3f6f446d9c555b41 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 21:15:41 +0900 Subject: [PATCH 28/96] =?UTF-8?q?feat=20[#92]=20=EC=BD=98=EC=84=9C?= =?UTF-8?q?=ED=8A=B8=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?API=20=EC=B6=94=EA=B0=80=20(#97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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] 아티스트 접기/펼치기 기준 값 상수 사용하도록 변경 --- .../dto/response/ConcertDetailResponse.java | 5 ++--- .../dto/response/FestivalDetailDateResponse.java | 4 ++-- .../user/controller/UserFavoriteController.java | 9 +++++++++ .../api/user/facade/UserFavoriteFacade.java | 15 ++++++++++++++- .../confeti/api/user/facade/UserInfoFacade.java | 2 +- .../concert/application/ConcertService.java | 12 ++++++++++-- .../application/ConcertFavoriteService.java | 9 ++++++++- .../repository/ConcertFavoriteRepository.java | 1 + .../domain/user/application/UserService.java | 14 +++++++------- .../global/common/constant/ArtistConstant.java | 9 +++++++++ 10 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 src/main/java/org/sopt/confeti/global/common/constant/ArtistConstant.java diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java index 8abdd30..9baf639 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailResponse.java @@ -2,6 +2,7 @@ import java.util.List; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; +import org.sopt.confeti.global.common.constant.ArtistConstant; import org.sopt.confeti.global.util.S3FileHandler; public record ConcertDetailResponse( @@ -9,8 +10,6 @@ public record ConcertDetailResponse( boolean isOpen, List concertArtists ) { - private static final int OPEN_CRITERIA = 4; - public static ConcertDetailResponse of(final ConcertDetailDTO concertDetailDTO, final S3FileHandler s3FileHandler) { List concertArtists = concertDetailDTO.artists().stream() .map(ConcertDetailArtistResponse::from) @@ -18,7 +17,7 @@ public static ConcertDetailResponse of(final ConcertDetailDTO concertDetailDTO, return new ConcertDetailResponse( ConcertDetailInfoResponse.of(concertDetailDTO, s3FileHandler), - concertArtists.size() > OPEN_CRITERIA, + concertArtists.size() > ArtistConstant.ARTIST_BOX_OPEN_CRITERIA, concertArtists ); } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailDateResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailDateResponse.java index be33adf..021fc7e 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailDateResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailDateResponse.java @@ -2,6 +2,7 @@ import java.util.List; import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDateDTO; +import org.sopt.confeti.global.common.constant.ArtistConstant; public record FestivalDetailDateResponse( long festivalDateId, @@ -9,7 +10,6 @@ public record FestivalDetailDateResponse( boolean isOpen, List artists ) { - private static final int OPEN_CRITERIA = 4; private static final String FESTIVAL_AT_PREFIX = "Day "; public static FestivalDetailDateResponse of(final FestivalDetailDateDTO festivalDate, final int order) { @@ -22,7 +22,7 @@ public static FestivalDetailDateResponse of(final FestivalDetailDateDTO festival return new FestivalDetailDateResponse( festivalDate.festivalDateId(), FESTIVAL_AT_PREFIX + order, - artists.size() > OPEN_CRITERIA, + artists.size() > ArtistConstant.ARTIST_BOX_OPEN_CRITERIA, artists ); } diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index c36f9fa..e834b91 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -66,4 +66,13 @@ public ResponseEntity> addConcertFavorite( userFavoriteFacade.addConcertFavorite(userId, concertId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } + + @DeleteMapping("/concerts/{concertId}") + public ResponseEntity> removeConcertFavorite( + @RequestHeader("Authorization") Long userId, + @PathVariable(name = "concertId") Long concertId + ) { + userFavoriteFacade.removeConcertFavorite(userId, concertId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index 3fa087c..07df51b 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -1,11 +1,12 @@ package org.sopt.confeti.api.user.facade; -import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; import org.sopt.confeti.domain.artistfavorite.application.ArtistFavoriteService; +import org.sopt.confeti.domain.concert.application.ConcertService; +import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.application.ConcertService; import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService; @@ -86,4 +87,16 @@ public void addConcertFavorite(final long userId, final long concertId) { concertFavoriteService.addFavorite(user, concert); } + + @Transactional + public void removeConcertFavorite(final long userId, final long concertId) { + userService.existsById(userId); + concertService.existsById(concertId); + + if (!concertFavoriteService.isFavorite(userId, concertId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + + concertFavoriteService.removeFavorite(userId, concertId); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserInfoFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserInfoFacade.java index 82c7a64..00775c3 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserInfoFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserInfoFacade.java @@ -14,7 +14,7 @@ public class UserInfoFacade { @Transactional public UserInfoDTO getUserInfo(Long userId) { - User user = userService.getUserInfo(userId); + User user = userService.findById(userId); return UserInfoDTO.from(user); } } diff --git a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java index e6f06fc..947f2e5 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java +++ b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java @@ -1,14 +1,13 @@ package org.sopt.confeti.domain.concert.application; -import java.util.Optional; import lombok.RequiredArgsConstructor; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.infra.repository.ConcertRepository; -import org.sopt.confeti.global.exception.ConfetiException; 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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -17,6 +16,7 @@ public class ConcertService { private final ConcertRepository concertRepository; private final ArtistResolver artistResolver; + @Transactional(readOnly = true) public Concert getConcertDetailByConcertId(final long concertId) { Concert concert = concertRepository.findById(concertId) .orElseThrow( @@ -28,6 +28,14 @@ public Concert getConcertDetailByConcertId(final long concertId) { return concert; } + @Transactional(readOnly = true) + public void existsById(long concertId) { + if (!concertRepository.existsById(concertId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } + + @Transactional(readOnly = true) public Concert findById(final long concertId) { return concertRepository.findById(concertId) .orElseThrow( diff --git a/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java b/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java index 60da6e4..a2bc3b2 100644 --- a/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java @@ -6,6 +6,7 @@ import org.sopt.confeti.domain.concertfavorite.infra.repository.ConcertFavoriteRepository; import org.sopt.confeti.domain.user.User; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -13,14 +14,20 @@ public class ConcertFavoriteService { private final ConcertFavoriteRepository concertFavoriteRepository; - + @Transactional(readOnly = true) public boolean isFavorite(final long userId, final long concertId) { return concertFavoriteRepository.existsByUserIdAndConcertId(userId, concertId); } + @Transactional public void addFavorite(final User user, final Concert concert) { concertFavoriteRepository.save( ConcertFavorite.create(user, concert) ); } + + @Transactional + public void removeFavorite(final long userId, final long concertId) { + concertFavoriteRepository.deleteByUserIdAndConcertId(userId, concertId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java index 1506481..fe68c07 100644 --- a/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java @@ -5,4 +5,5 @@ public interface ConcertFavoriteRepository extends JpaRepository { boolean existsByUserIdAndConcertId(final long userId, final long concertId); + void deleteByUserIdAndConcertId(final long userId, final long concertId); } diff --git a/src/main/java/org/sopt/confeti/domain/user/application/UserService.java b/src/main/java/org/sopt/confeti/domain/user/application/UserService.java index 7d30903..8c216dd 100644 --- a/src/main/java/org/sopt/confeti/domain/user/application/UserService.java +++ b/src/main/java/org/sopt/confeti/domain/user/application/UserService.java @@ -4,26 +4,26 @@ import org.sopt.confeti.domain.user.User; import org.sopt.confeti.domain.user.infra.repository.UserRepository; import org.sopt.confeti.global.exception.NotFoundException; +import org.sopt.confeti.global.exception.UnauthorizedException; import org.sopt.confeti.global.message.ErrorMessage; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; + @Transactional(readOnly = true) public User findById(Long userId) { User user = userRepository.findById(userId) - .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND)); - return user; - } - - public User getUserInfo(Long userId) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND)); + .orElseThrow( + () -> new UnauthorizedException(ErrorMessage.UNAUTHORIZED) + ); return user; } + @Transactional(readOnly = true) public void existsById(Long userId) { final boolean isExistUser = userRepository.existsById(userId); if(!isExistUser) { diff --git a/src/main/java/org/sopt/confeti/global/common/constant/ArtistConstant.java b/src/main/java/org/sopt/confeti/global/common/constant/ArtistConstant.java new file mode 100644 index 0000000..2e51e6b --- /dev/null +++ b/src/main/java/org/sopt/confeti/global/common/constant/ArtistConstant.java @@ -0,0 +1,9 @@ +package org.sopt.confeti.global.common.constant; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ArtistConstant { + public static final int ARTIST_BOX_OPEN_CRITERIA = 4; +} From be1103daf676204fadfdad52ac88715c26b0c910 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 21:18:20 +0900 Subject: [PATCH 29/96] =?UTF-8?q?feat=20[#98]=20CORS=20=ED=97=88=EC=9A=A9?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/sopt/confeti/global/config/CorsConfig.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/config/CorsConfig.java b/src/main/java/org/sopt/confeti/global/config/CorsConfig.java index 68c761a..8bfb93d 100644 --- a/src/main/java/org/sopt/confeti/global/config/CorsConfig.java +++ b/src/main/java/org/sopt/confeti/global/config/CorsConfig.java @@ -16,8 +16,10 @@ public static CorsConfigurationSource corsConfigurationSource() { //리소스를 허용할 URL 지정 ArrayList allowedOriginPatterns = new ArrayList<>(); - allowedOriginPatterns.add("http://localhost:3000"); - allowedOriginPatterns.add("http://127.0.0.1:3000"); + allowedOriginPatterns.add("http://localhost:5173"); + allowedOriginPatterns.add("http://localhost:5174"); + allowedOriginPatterns.add("https://35-appjam-web-confeti.vercel.app/"); + allowedOriginPatterns.add("https://confeti.co.kr"); allowedOriginPatterns.add("https://confeti.xyz"); allowedOriginPatterns.add("https://api.confeti.xyz"); configuration.setAllowedOrigins(allowedOriginPatterns); From 0645753e060f8bb87e9512d7cb15e6ab9f6d82e1 Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 21:42:29 +0900 Subject: [PATCH 30/96] =?UTF-8?q?refactor=20[#100]=20DateConvertor=20stati?= =?UTF-8?q?c=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/confeti/annotation/Convertor.java | 15 ------------ .../confeti/global/util/DateConvertor.java | 24 +++++++++---------- 2 files changed, 11 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/org/sopt/confeti/annotation/Convertor.java diff --git a/src/main/java/org/sopt/confeti/annotation/Convertor.java b/src/main/java/org/sopt/confeti/annotation/Convertor.java deleted file mode 100644 index a40106e..0000000 --- a/src/main/java/org/sopt/confeti/annotation/Convertor.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sopt.confeti.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import org.springframework.stereotype.Component; - -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Component -public @interface Convertor { -} diff --git a/src/main/java/org/sopt/confeti/global/util/DateConvertor.java b/src/main/java/org/sopt/confeti/global/util/DateConvertor.java index a165680..b02ef70 100644 --- a/src/main/java/org/sopt/confeti/global/util/DateConvertor.java +++ b/src/main/java/org/sopt/confeti/global/util/DateConvertor.java @@ -4,41 +4,39 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; -import org.sopt.confeti.annotation.Convertor; -@Convertor public class DateConvertor { - private final DateTimeFormatter spotifyLocalDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - private final DateTimeFormatter localDateFormat = DateTimeFormatter.ofPattern("yyyy.MM.dd"); - private final DateTimeFormatter localTimeFormat = DateTimeFormatter.ofPattern("HH:mm:ss"); - private final DateTimeFormatter localDateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + private static final DateTimeFormatter spotifyLocalDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + private static final DateTimeFormatter localDateFormat = DateTimeFormatter.ofPattern("yyyy.MM.dd"); + private static final DateTimeFormatter localTimeFormat = DateTimeFormatter.ofPattern("HH:mm:ss"); + private static final DateTimeFormatter localDateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); - public String convert(final LocalDate localDate) { + public static String convert(final LocalDate localDate) { return localDate.format(localDateFormat); } - public String convert(final LocalTime localTime) { + public static String convert(final LocalTime localTime) { return localTime.format(localTimeFormat); } - public String convert(final LocalDateTime localDateTime) { + public static String convert(final LocalDateTime localDateTime) { return localDateTime.format(localDateTimeFormat); } - public LocalDate convertToLocalDate(final String localDate) { + public static LocalDate convertToLocalDate(final String localDate) { return LocalDate.parse(localDate, localDateFormat); } - public LocalTime convertToLocalTime(final String localTime) { + public static LocalTime convertToLocalTime(final String localTime) { return LocalTime.parse(localTime, localTimeFormat); } - public LocalDateTime convertToLocalDateTime(final String localDateTime) { + public static LocalDateTime convertToLocalDateTime(final String localDateTime) { return LocalDateTime.parse(localDateTime, localDateTimeFormat); } - public LocalDate convertToSpotifyLocalDate(final String spotifyLocalDate) { + public static LocalDate convertToSpotifyLocalDate(final String spotifyLocalDate) { return LocalDate.parse(spotifyLocalDate, spotifyLocalDateFormat); } } From dd39674adb21af9e8d290d122f01d1f83fb6a402 Mon Sep 17 00:00:00 2001 From: Ivoryeee <105477246+Ivoryeee@users.noreply.github.com> Date: Sat, 18 Jan 2025 22:35:54 +0900 Subject: [PATCH 31/96] =?UTF-8?q?feat=20[#83]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=EB=90=9C=20=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C,=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=20=EC=A1=B0=ED=9A=8C=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- .../controller/UserTimetableController.java | 29 +++++++++++++++++ .../UserTimetableDetailDatesResponse.java | 18 +++++++++++ .../UserTimetableDetailFestivalResponse.java | 24 ++++++++++++++ .../response/UserTimetableDetailResponse.java | 18 +++++++++++ .../api/user/facade/UserTimetableFacade.java | 31 +++++++++++++++++++ .../facade/dto/response/UserTimetableDTO.java | 17 ++++++++++ .../dto/response/UserTimetableDatesDTO.java | 13 ++++++++ .../response/UserTimetableFestivalDTO.java | 24 ++++++++++++++ .../application/TimetableFestivalService.java | 18 +++++++++++ .../TimetableFestivalRepository.java | 3 ++ .../artistsearcher/SpotifyAPIHandler.java | 4 +-- 11 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailDatesResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailFestivalResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableDatesDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalDTO.java create mode 100644 src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java new file mode 100644 index 0000000..eb7aa49 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java @@ -0,0 +1,29 @@ +package org.sopt.confeti.api.user.controller; + +import lombok.RequiredArgsConstructor; +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.response.UserTimetableDTO; +import org.sopt.confeti.global.common.BaseResponse; +import org.sopt.confeti.global.message.SuccessMessage; +import org.sopt.confeti.global.util.ApiResponseUtil; +import org.sopt.confeti.global.util.S3FileHandler; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/user/timetables/festivals") +public class UserTimetableController { + private final UserTimetableFacade userTimetableFacade; + private final S3FileHandler s3FileHandler; + + @GetMapping + public ResponseEntity> getTimetablesListAndDate(@RequestHeader("Authorization") long userId) { + UserTimetableDTO userTimetableDTO = userTimetableFacade.getTimetablesListAndDate(userId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserTimetableDetailResponse.of(userTimetableDTO, s3FileHandler)); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailDatesResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailDatesResponse.java new file mode 100644 index 0000000..38ac212 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailDatesResponse.java @@ -0,0 +1,18 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDatesDTO; +import org.sopt.confeti.global.util.DateConvertor; + +import java.time.LocalDate; + +public record UserTimetableDetailDatesResponse( + long festivalDateId, + String festivalAt +) { + public static UserTimetableDetailDatesResponse from(UserTimetableDatesDTO userTimetableDatesDTO) { + return new UserTimetableDetailDatesResponse( + userTimetableDatesDTO.festivalDateId(), + DateConvertor.convert(userTimetableDatesDTO.festivalAt()) + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailFestivalResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailFestivalResponse.java new file mode 100644 index 0000000..6d41bfb --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailFestivalResponse.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableFestivalDTO; +import org.sopt.confeti.global.util.S3FileHandler; + +import java.util.List; + +public record UserTimetableDetailFestivalResponse( + long festivalId, + String title, + String logoUrl, + List festivalDates +) { + public static UserTimetableDetailFestivalResponse of(UserTimetableFestivalDTO userTimetableFestivalDTO, S3FileHandler s3FileHandler) { + return new UserTimetableDetailFestivalResponse( + userTimetableFestivalDTO.festivalId(), + userTimetableFestivalDTO.title(), + s3FileHandler.getFileUrl(userTimetableFestivalDTO.logoPath()), + userTimetableFestivalDTO.festivalDates().stream() + .map(UserTimetableDetailDatesResponse::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailResponse.java new file mode 100644 index 0000000..3c882b7 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableDetailResponse.java @@ -0,0 +1,18 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO; +import org.sopt.confeti.global.util.S3FileHandler; + +import java.util.List; + +public record UserTimetableDetailResponse( + List festivals +) { + public static UserTimetableDetailResponse of(UserTimetableDTO userTimeTableDTO, S3FileHandler s3FileHandler) { + return new UserTimetableDetailResponse( + userTimeTableDTO.festivals().stream() + .map(timetableFestivalDTO -> UserTimetableDetailFestivalResponse.of(timetableFestivalDTO, s3FileHandler)) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java new file mode 100644 index 0000000..497deee --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -0,0 +1,31 @@ +package org.sopt.confeti.api.user.facade; + +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.annotation.Facade; +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO; +import org.sopt.confeti.domain.festival.application.FestivalService; +import org.sopt.confeti.domain.timetablefestival.TimetableFestival; +import org.sopt.confeti.domain.timetablefestival.application.TimetableFestivalService; +import org.sopt.confeti.domain.user.application.UserService; +import org.sopt.confeti.global.util.S3FileHandler; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.List; + +@Facade +@RequiredArgsConstructor +public class UserTimetableFacade { + + private final UserService userService; + private final TimetableFestivalService timetableFestivalService; + + @Transactional(readOnly = true) + public UserTimetableDTO getTimetablesListAndDate(long userId) { + userService.existsById(userId); + List festivalList = timetableFestivalService.getFetivalList(userId); + return UserTimetableDTO.from(festivalList); + } +} + diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableDTO.java new file mode 100644 index 0000000..fdf03f9 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableDTO.java @@ -0,0 +1,17 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.timetablefestival.TimetableFestival; + +import java.util.List; + +public record UserTimetableDTO ( + List festivals +){ + public static UserTimetableDTO from(List userTimetable) { + return new UserTimetableDTO( + userTimetable.stream() + .map(UserTimetableFestivalDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableDatesDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableDatesDTO.java new file mode 100644 index 0000000..fa49f8f --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableDatesDTO.java @@ -0,0 +1,13 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.festivaldate.FestivalDate; +import java.time.LocalDate; + +public record UserTimetableDatesDTO(long festivalDateId, LocalDate festivalAt) { + public static UserTimetableDatesDTO from(FestivalDate festivalDate) { + return new UserTimetableDatesDTO( + festivalDate.getId(), + festivalDate.getFestivalAt() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalDTO.java new file mode 100644 index 0000000..a547313 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalDTO.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.timetablefestival.TimetableFestival; + +import java.util.List; + +public record UserTimetableFestivalDTO( + long festivalId, + String title, + String logoPath, + List festivalDates +) { + public static UserTimetableFestivalDTO from(TimetableFestival timetableFestival) { + return new UserTimetableFestivalDTO( + timetableFestival.getFestival().getId(), + timetableFestival.getFestival().getFestivalTitle(), + timetableFestival.getFestival().getFestivalLogoPath(), + timetableFestival.getFestival().getDates().stream() + .map(UserTimetableDatesDTO::from) + .toList() + ); + } + +} diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java new file mode 100644 index 0000000..fd2032a --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java @@ -0,0 +1,18 @@ +package org.sopt.confeti.domain.timetablefestival.application; + +import lombok.AllArgsConstructor; +import org.sopt.confeti.domain.timetablefestival.TimetableFestival; +import org.sopt.confeti.domain.timetablefestival.infra.repository.TimetableFestivalRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@AllArgsConstructor +public class TimetableFestivalService { + private final TimetableFestivalRepository timetableFestivalRepository; + + public List getFetivalList(long userId){ + return timetableFestivalRepository.findByUserId(userId); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java index 5da25a1..4e8b32a 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java @@ -3,5 +3,8 @@ import org.sopt.confeti.domain.timetablefestival.TimetableFestival; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface TimetableFestivalRepository extends JpaRepository { + List findByUserId(Long userId); } diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java index b417b8e..475d6b7 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java @@ -41,8 +41,6 @@ public class SpotifyAPIHandler { private SpotifyApi spotifyApi; - private final DateConvertor dateConvertor; - public void init() { createSpotifyApi(); generateAccessToken(); @@ -110,7 +108,7 @@ private LocalDate findLatestReleaseAt(final String keyword, final Artist artist) ) .max(Comparator.comparing(AlbumSimplified::getReleaseDate)) .map( - albumSimplified -> dateConvertor.convertToSpotifyLocalDate(albumSimplified.getReleaseDate()) + albumSimplified -> DateConvertor.convertToSpotifyLocalDate(albumSimplified.getReleaseDate()) ) .get(); } From c569d4b73349b1b9703918d025eca4d78596a98f Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 22:53:59 +0900 Subject: [PATCH 32/96] =?UTF-8?q?refactor=20[#105]=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EB=A7=A4=ED=95=91=20=EB=B0=A9=EB=B2=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/SearchArtistSingleResponse.java | 5 +++-- .../dto/request/CreateFestivalDateRequest.java | 3 +++ .../dto/request/CreateFestivalRequest.java | 5 +++++ .../dto/request/CreateFestivalTimeRequest.java | 3 +++ .../dto/response/ConcertDetailInfoResponse.java | 13 +++++++------ .../dto/response/FestivalDetailInfoResponse.java | 13 +++++++------ .../api/user/dto/response/UserFavoriteResponse.java | 4 +++- .../api/user/dto/response/UserInfoResponse.java | 6 +++++- .../org/sopt/confeti/global/util/DateConvertor.java | 12 ++++++++++++ 9 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistSingleResponse.java b/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistSingleResponse.java index 9ab6e7f..b53d258 100644 --- a/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistSingleResponse.java +++ b/src/main/java/org/sopt/confeti/api/artist/dto/response/SearchArtistSingleResponse.java @@ -2,12 +2,13 @@ import java.time.LocalDate; import org.sopt.confeti.api.artist.facade.dto.response.SearchArtistDTO; +import org.sopt.confeti.global.util.DateConvertor; public record SearchArtistSingleResponse( String artistId, String name, String profileUrl, - LocalDate latestReleaseAt, + String latestReleaseAt, boolean isFavorite, boolean isMultipleArtists ) { @@ -16,7 +17,7 @@ public static SearchArtistSingleResponse from(final SearchArtistDTO searchArtist searchArtistDTO.artistId(), searchArtistDTO.name(), searchArtistDTO.profileUrl(), - searchArtistDTO.latestReleaseAt(), + DateConvertor.convert(searchArtistDTO.latestReleaseAt()), searchArtistDTO.isFavorite(), searchArtistDTO.isMultipleArtists() ); diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java index a9b77d8..141b7ac 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java @@ -3,9 +3,12 @@ import java.time.LocalDate; import java.time.LocalTime; import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; public record CreateFestivalDateRequest( + @DateTimeFormat(pattern = "yyyy.MM.dd") LocalDate festivalAt, + @DateTimeFormat(pattern = "HH:mm:ss") LocalTime ticketOpenAt, List festivalStages ) { diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java index e60c364..13968e2 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java @@ -3,11 +3,15 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; +import org.sopt.confeti.global.util.DateConvertor; +import org.springframework.format.annotation.DateTimeFormat; public record CreateFestivalRequest( String festivalTitle, String festivalSubtitle, + @DateTimeFormat(pattern = "yyyy.MM.dd") LocalDate festivalStartAt, + @DateTimeFormat(pattern = "yyyy.MM.dd") LocalDate festivalEndAt, String festivalArea, String festivalPosterPath, @@ -15,6 +19,7 @@ public record CreateFestivalRequest( String festivalInfoImgPath, String festivalReservationBgPath, String festivalLogoPath, + @DateTimeFormat(pattern = "yyyy.MM.dd") LocalDate reserveAt, String reservationUrl, String reservationOffice, diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java index 59f5fbc..76340ad 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java @@ -3,9 +3,12 @@ import java.time.LocalDate; import java.time.LocalTime; import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; public record CreateFestivalTimeRequest( + @DateTimeFormat(pattern = "HH:mm:ss") LocalTime startAt, + @DateTimeFormat(pattern = "HH:mm:ss") LocalTime endAt, List festivalArtists ) { diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java index e6da7a9..4743bb3 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java @@ -3,6 +3,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; +import org.sopt.confeti.global.util.DateConvertor; import org.sopt.confeti.global.util.S3FileHandler; public record ConcertDetailInfoResponse( @@ -11,10 +12,10 @@ public record ConcertDetailInfoResponse( String posterBgUrl, String title, String subtitle, - LocalDate startAt, - LocalDate endAt, + String startAt, + String endAt, String area, - LocalDateTime reserveAt, + String reserveAt, String reservationUrl, String time, String ageRating, @@ -29,10 +30,10 @@ public static ConcertDetailInfoResponse of(final ConcertDetailDTO concertDetailD s3FileHandler.getFileUrl(concertDetailDTO.posterBgPath()), concertDetailDTO.title(), concertDetailDTO.subtitle(), - concertDetailDTO.startAt(), - concertDetailDTO.endAt(), + DateConvertor.convert(concertDetailDTO.startAt()), + DateConvertor.convert(concertDetailDTO.endAt()), concertDetailDTO.area(), - concertDetailDTO.reserveAt(), + DateConvertor.convert(concertDetailDTO.reserveAt()), concertDetailDTO.reservationUrl(), concertDetailDTO.time(), concertDetailDTO.ageRating(), diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java index 28b220a..8331584 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java @@ -3,6 +3,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; +import org.sopt.confeti.global.util.DateConvertor; public record FestivalDetailInfoResponse( long festivalId, @@ -10,10 +11,10 @@ public record FestivalDetailInfoResponse( String posterBgUrl, String title, String subtitle, - LocalDate startAt, - LocalDate endAt, + String startAt, + String endAt, String area, - LocalDate reserveAt, + String reserveAt, String reservationUrl, String time, String ageRating, @@ -29,10 +30,10 @@ public static FestivalDetailInfoResponse from(final FestivalDetailDTO festival) festival.festivalPosterBgUrl(), festival.festivalTitle(), festival.festivalSubtitle(), - festival.festivalStartAt(), - festival.festivalEndAt(), + DateConvertor.convert(festival.festivalStartAt()), + DateConvertor.convert(festival.festivalEndAt()), festival.festivalArea(), - festival.reserveAt(), + DateConvertor.convert(festival.reserveAt()), festival.reservationUrl(), festival.time(), festival.ageRating(), diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java index ad4cc54..1cafe8a 100644 --- a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoriteResponse.java @@ -4,7 +4,9 @@ import java.util.List; -public record UserFavoriteResponse(List artists) { +public record UserFavoriteResponse( + List artists +) { public static UserFavoriteResponse from(final List artistListDTO) { return new UserFavoriteResponse( artistListDTO.stream() diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserInfoResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserInfoResponse.java index 4ae8b5d..7c87db1 100644 --- a/src/main/java/org/sopt/confeti/api/user/dto/response/UserInfoResponse.java +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserInfoResponse.java @@ -2,7 +2,11 @@ import org.sopt.confeti.api.user.facade.dto.response.UserInfoDTO; -public record UserInfoResponse (Long userId, String profileUrl, String username){ +public record UserInfoResponse ( + Long userId, + String profileUrl, + String username +) { public static UserInfoResponse from (UserInfoDTO userInfoDTO) { return new UserInfoResponse(userInfoDTO.userId(), userInfoDTO.profileUrl(), userInfoDTO.username()); } diff --git a/src/main/java/org/sopt/confeti/global/util/DateConvertor.java b/src/main/java/org/sopt/confeti/global/util/DateConvertor.java index b02ef70..b8a3297 100644 --- a/src/main/java/org/sopt/confeti/global/util/DateConvertor.java +++ b/src/main/java/org/sopt/confeti/global/util/DateConvertor.java @@ -13,14 +13,26 @@ public class DateConvertor { private static final DateTimeFormatter localDateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); public static String convert(final LocalDate localDate) { + if (localDate == null) { + return null; + } + return localDate.format(localDateFormat); } public static String convert(final LocalTime localTime) { + if (localTime == null) { + return null; + } + return localTime.format(localTimeFormat); } public static String convert(final LocalDateTime localDateTime) { + if (localDateTime == null) { + return null; + } + return localDateTime.format(localDateTimeFormat); } From 059628913e88d8349c151958cdfc86c71d797b4a Mon Sep 17 00:00:00 2001 From: chyun Date: Sat, 18 Jan 2025 23:11:15 +0900 Subject: [PATCH 33/96] =?UTF-8?q?refactor=20[#105]=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EB=A7=A4=ED=95=91=20=EB=B0=A9=EB=B2=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CreateFestivalDateRequest.java | 6 +++--- .../performance/dto/request/CreateFestivalRequest.java | 10 ++++------ .../dto/request/CreateFestivalTimeRequest.java | 7 +++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java index 141b7ac..cf43667 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java @@ -1,14 +1,14 @@ package org.sopt.confeti.api.performance.dto.request; +import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; -import org.springframework.format.annotation.DateTimeFormat; public record CreateFestivalDateRequest( - @DateTimeFormat(pattern = "yyyy.MM.dd") + @JsonFormat(pattern = "yyyy.MM.dd") LocalDate festivalAt, - @DateTimeFormat(pattern = "HH:mm:ss") + @JsonFormat(pattern = "HH:mm:ss") LocalTime ticketOpenAt, List festivalStages ) { diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java index 13968e2..30f5fbd 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java @@ -1,17 +1,15 @@ package org.sopt.confeti.api.performance.dto.request; +import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; -import org.sopt.confeti.global.util.DateConvertor; -import org.springframework.format.annotation.DateTimeFormat; public record CreateFestivalRequest( String festivalTitle, String festivalSubtitle, - @DateTimeFormat(pattern = "yyyy.MM.dd") + @JsonFormat(pattern = "yyyy.MM.dd") LocalDate festivalStartAt, - @DateTimeFormat(pattern = "yyyy.MM.dd") + @JsonFormat(pattern = "yyyy.MM.dd") LocalDate festivalEndAt, String festivalArea, String festivalPosterPath, @@ -19,7 +17,7 @@ public record CreateFestivalRequest( String festivalInfoImgPath, String festivalReservationBgPath, String festivalLogoPath, - @DateTimeFormat(pattern = "yyyy.MM.dd") + @JsonFormat(pattern = "yyyy.MM.dd") LocalDate reserveAt, String reservationUrl, String reservationOffice, diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java index 76340ad..8214628 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java @@ -1,14 +1,13 @@ package org.sopt.confeti.api.performance.dto.request; -import java.time.LocalDate; +import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalTime; import java.util.List; -import org.springframework.format.annotation.DateTimeFormat; public record CreateFestivalTimeRequest( - @DateTimeFormat(pattern = "HH:mm:ss") + @JsonFormat(pattern = "HH:mm:ss") LocalTime startAt, - @DateTimeFormat(pattern = "HH:mm:ss") + @JsonFormat(pattern = "HH:mm:ss") LocalTime endAt, List festivalArtists ) { From cc18958fa24b0a2c2a439d17ba751e7c422c4d9c Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 00:35:30 +0900 Subject: [PATCH 34/96] =?UTF-8?q?feat=20[#107]=20existsById=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20=EA=B0=92=20boolean=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=ED=83=80=EC=9E=84=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserTimetableController.java | 14 +++++++++++ .../api/user/facade/UserFavoriteFacade.java | 18 +++++++------- .../api/user/facade/UserTimetableFacade.java | 24 +++++++++++++++---- .../concert/application/ConcertService.java | 6 ++--- .../festival/application/FestivalService.java | 4 ++++ .../application/TimetableFestivalService.java | 12 ++++++++++ .../TimetableFestivalRepository.java | 4 ++++ .../domain/user/application/UserService.java | 7 ++---- 8 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java index eb7aa49..cde71df 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java @@ -1,5 +1,6 @@ package org.sopt.confeti.api.user.controller; +import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; import org.sopt.confeti.api.user.dto.response.UserTimetableDetailResponse; import org.sopt.confeti.api.user.facade.UserTimetableFacade; @@ -9,12 +10,16 @@ import org.sopt.confeti.global.util.ApiResponseUtil; import org.sopt.confeti.global.util.S3FileHandler; import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +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.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController +@Validated @RequiredArgsConstructor @RequestMapping("/user/timetables/festivals") public class UserTimetableController { @@ -26,4 +31,13 @@ public ResponseEntity> getTimetablesListAndDate(@RequestHeader(" UserTimetableDTO userTimetableDTO = userTimetableFacade.getTimetablesListAndDate(userId); return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserTimetableDetailResponse.of(userTimetableDTO, s3FileHandler)); } + + @DeleteMapping("/{festivalId}") + public ResponseEntity> removeTimetableFestival( + @RequestHeader("Authorization") long userId, + @PathVariable(name = "festivalId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") long festivalId + ) { + userTimetableFacade.removeTimetableFestival(userId, festivalId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index 07df51b..ec8f891 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -49,7 +49,10 @@ public void removeFestivalFavorite(long userId, long festivalId) { @Transactional(readOnly = true) public UserFavoriteArtistDTO getArtistList(long userId) { - userService.existsById(userId); + if (!userService.existsById(userId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + List artists = artistFavoriteService.getArtistList(userId); return UserFavoriteArtistDTO.from(artists); } @@ -67,9 +70,9 @@ public void addArtistFavorite(final long userId, final String artistId) { @Transactional public void removeArtistFavorite(final long userId, final String artistId) { - userService.existsById(userId); - - if (!artistFavoriteService.isFavorite(userId, artistId)) { + if ( + !userService.existsById(userId) || !artistFavoriteService.isFavorite(userId, artistId) + ) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } @@ -90,10 +93,9 @@ public void addConcertFavorite(final long userId, final long concertId) { @Transactional public void removeConcertFavorite(final long userId, final long concertId) { - userService.existsById(userId); - concertService.existsById(concertId); - - if (!concertFavoriteService.isFavorite(userId, concertId)) { + if ( + !userService.existsById(userId) || !concertService.existsById(concertId) || !concertFavoriteService.isFavorite(userId, concertId) + ) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 497deee..2b1e330 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -1,6 +1,5 @@ package org.sopt.confeti.api.user.facade; -import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO; @@ -8,10 +7,10 @@ import org.sopt.confeti.domain.timetablefestival.TimetableFestival; import org.sopt.confeti.domain.timetablefestival.application.TimetableFestivalService; import org.sopt.confeti.domain.user.application.UserService; -import org.sopt.confeti.global.util.S3FileHandler; +import org.sopt.confeti.global.exception.NotFoundException; +import org.sopt.confeti.global.message.ErrorMessage; import org.springframework.transaction.annotation.Transactional; -import java.util.HashMap; import java.util.List; @Facade @@ -20,12 +19,29 @@ public class UserTimetableFacade { private final UserService userService; private final TimetableFestivalService timetableFestivalService; + private final FestivalService festivalService; @Transactional(readOnly = true) public UserTimetableDTO getTimetablesListAndDate(long userId) { - userService.existsById(userId); + if (!userService.existsById(userId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + List festivalList = timetableFestivalService.getFetivalList(userId); return UserTimetableDTO.from(festivalList); } + + @Transactional + public void removeTimetableFestival(final long userId, final long festivalId) { + if ( + !userService.existsById(userId) || + !festivalService.existsById(festivalId) || + !timetableFestivalService.existsByUserIdAndFestivalId(userId, festivalId) + ) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + + timetableFestivalService.removeTimetableFestival(userId, festivalId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java index 947f2e5..b081f01 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java +++ b/src/main/java/org/sopt/confeti/domain/concert/application/ConcertService.java @@ -29,10 +29,8 @@ public Concert getConcertDetailByConcertId(final long concertId) { } @Transactional(readOnly = true) - public void existsById(long concertId) { - if (!concertRepository.existsById(concertId)) { - throw new NotFoundException(ErrorMessage.NOT_FOUND); - } + public boolean existsById(long concertId) { + return concertRepository.existsById(concertId); } @Transactional(readOnly = true) diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index feff146..c178409 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -35,4 +35,8 @@ public void create(final CreateFestivalDTO createFestivalDTO) { Festival.create(createFestivalDTO) ); } + + public boolean existsById(final long festivalId) { + return festivalRepository.existsById(festivalId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java index fd2032a..d048d96 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java @@ -6,13 +6,25 @@ import org.springframework.stereotype.Service; import java.util.List; +import org.springframework.transaction.annotation.Transactional; @Service @AllArgsConstructor public class TimetableFestivalService { private final TimetableFestivalRepository timetableFestivalRepository; + @Transactional(readOnly = true) public List getFetivalList(long userId){ return timetableFestivalRepository.findByUserId(userId); } + + @Transactional(readOnly = true) + public boolean existsByUserIdAndFestivalId(final long userId, final long festivalId) { + return timetableFestivalRepository.existsByUserIdAndFestivalId(userId, festivalId); + } + + @Transactional + public void removeTimetableFestival(final long userId, final long festivalId) { + timetableFestivalRepository.deleteByUserIdAndFestivalId(userId, festivalId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java index 4e8b32a..10dbc4c 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java @@ -7,4 +7,8 @@ public interface TimetableFestivalRepository extends JpaRepository { List findByUserId(Long userId); + + boolean existsByUserIdAndFestivalId(final long userId, final long festivalId); + + void deleteByUserIdAndFestivalId(final long userId, final long festivalId); } diff --git a/src/main/java/org/sopt/confeti/domain/user/application/UserService.java b/src/main/java/org/sopt/confeti/domain/user/application/UserService.java index 8c216dd..b1de383 100644 --- a/src/main/java/org/sopt/confeti/domain/user/application/UserService.java +++ b/src/main/java/org/sopt/confeti/domain/user/application/UserService.java @@ -24,10 +24,7 @@ public User findById(Long userId) { } @Transactional(readOnly = true) - public void existsById(Long userId) { - final boolean isExistUser = userRepository.existsById(userId); - if(!isExistUser) { - throw new NotFoundException(ErrorMessage.NOT_FOUND); - } + public boolean existsById(Long userId) { + return userRepository.existsById(userId); } } From f17fca6b138ed2cc095716edb33bea9e6a2f7876 Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 01:02:51 +0900 Subject: [PATCH 35/96] =?UTF-8?q?feat=20[#109]=20=EA=B3=B5=EC=97=B0(?= =?UTF-8?q?=EC=BD=98=EC=84=9C=ED=8A=B8,=20=ED=8E=98=EC=8A=A4=ED=8B=B0?= =?UTF-8?q?=EB=B2=8C)=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=EA=B0=80=20=EC=A7=80=EB=82=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=95=98=EB=8A=94=EC=A7=80=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PerformanceController.java | 2 +- .../performance/facade/PerformanceFacade.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index abd4936..79bdf0d 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -35,7 +35,7 @@ public class PerformanceController { @GetMapping("/concerts/{concertId}") public ResponseEntity> getConcertInfo( @RequestHeader(name = "Authorization", required = false) Long userId, - @PathVariable("concertId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long concertId + @PathVariable("concertId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") long concertId ) { ConcertDetailDTO concertDetailDTO = performanceFacade.getConcertDetailInfo(concertId); return ApiResponseUtil.success(SuccessMessage.SUCCESS, ConcertDetailResponse.of(concertDetailDTO, s3FileHandler)); diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index 6f66399..1d4efd9 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -1,5 +1,6 @@ package org.sopt.confeti.api.performance.facade; +import java.time.LocalDate; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; @@ -10,6 +11,8 @@ import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; +import org.sopt.confeti.global.exception.NotFoundException; +import org.sopt.confeti.global.message.ErrorMessage; import org.sopt.confeti.global.util.S3FileHandler; import org.springframework.transaction.annotation.Transactional; @@ -25,9 +28,18 @@ public class PerformanceFacade { @Transactional(readOnly = true) public ConcertDetailDTO getConcertDetailInfo(final long concertId) { Concert concert = concertService.getConcertDetailByConcertId(concertId); + validateConcertNotPassed(concert); + return ConcertDetailDTO.from(concert); } + @Transactional(readOnly = true) + protected void validateConcertNotPassed(final Concert concert) { + if (LocalDate.now().isAfter(concert.getConcertEndAt())) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } + @Transactional public void createFestival(final CreateFestivalDTO createFestivalDTO) { festivalService.create(createFestivalDTO); @@ -42,6 +54,15 @@ public FestivalDetailDTO getFestivalDetailInfo(final Long userId, final long fes } Festival festival = festivalService.getFestivalDetailByFestivalId(festivalId); + validateFestivalNotPassed(festival); + return FestivalDetailDTO.of(festival, isFavorite, s3FileHandler); } + + @Transactional(readOnly = true) + protected void validateFestivalNotPassed(final Festival festival) { + if (LocalDate.now().isAfter(festival.getFestivalEndAt())) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } } From ea8c3941d10d7fcbbebbcab4c9ddb00353f0c2cd Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 01:05:32 +0900 Subject: [PATCH 36/96] =?UTF-8?q?refactor=20[#109]=20=EA=B3=B5=EC=97=B0=20?= =?UTF-8?q?=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=97=AC=EB=B6=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/performance/facade/PerformanceFacade.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index 1d4efd9..ff8ef5a 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -47,11 +47,7 @@ public void createFestival(final CreateFestivalDTO createFestivalDTO) { @Transactional(readOnly = true) public FestivalDetailDTO getFestivalDetailInfo(final Long userId, final long festivalId) { - boolean isFavorite = false; - - if (userId != null) { - isFavorite = festivalFavoriteService.isFavorite(userId, festivalId); - } + boolean isFavorite = getIsFavorite(userId, festivalId); Festival festival = festivalService.getFestivalDetailByFestivalId(festivalId); validateFestivalNotPassed(festival); @@ -59,6 +55,15 @@ public FestivalDetailDTO getFestivalDetailInfo(final Long userId, final long fes return FestivalDetailDTO.of(festival, isFavorite, s3FileHandler); } + @Transactional(readOnly = true) + protected boolean getIsFavorite(final Long userid, final long festivalId) { + if (userid != null) { + return festivalFavoriteService.isFavorite(userid, festivalId); + } + + return false; + } + @Transactional(readOnly = true) protected void validateFestivalNotPassed(final Festival festival) { if (LocalDate.now().isAfter(festival.getFestivalEndAt())) { From adb0d7810dd8003d3aac9dbb565cec02559c62c4 Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 01:26:07 +0900 Subject: [PATCH 37/96] =?UTF-8?q?refactor=20[#109]=20=EA=B3=B5=EC=97=B0,?= =?UTF-8?q?=20=EC=95=84=ED=8B=B0=EC=8A=A4=ED=8A=B8=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=20=EC=97=AC=EB=B6=80=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserFavoriteFacade.java | 82 +++++++++++++++---- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index ec8f891..2d8c42d 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -37,6 +37,8 @@ public class UserFavoriteFacade { public void addFestivalFavorite(long userId, long festivalId) { User user = userService.findById(userId); Festival festival = festivalService.findById(festivalId); + validateNotExistFestivalFavorite(userId, festivalId); + festivalFavoriteService.save(user, festival); } @@ -44,14 +46,28 @@ public void addFestivalFavorite(long userId, long festivalId) { public void removeFestivalFavorite(long userId, long festivalId) { User user = userService.findById(userId); Festival festival = festivalService.findById(festivalId); + validateExistFestivalFavorite(userId, festivalId); + festivalFavoriteService.delete(user, festival); } @Transactional(readOnly = true) - public UserFavoriteArtistDTO getArtistList(long userId) { - if (!userService.existsById(userId)) { + protected void validateExistFestivalFavorite(final long userId, final long festivalId) { + if (!festivalFavoriteService.isFavorite(userId, festivalId)) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } + } + + @Transactional(readOnly = true) + protected void validateNotExistFestivalFavorite(final long userId, final long festivalId) { + if (festivalFavoriteService.isFavorite(userId, festivalId)) { + throw new ConflictException(ErrorMessage.CONFLICT); + } + } + + @Transactional(readOnly = true) + public UserFavoriteArtistDTO getArtistList(long userId) { + validateExistUser(userId); List artists = artistFavoriteService.getArtistList(userId); return UserFavoriteArtistDTO.from(artists); @@ -60,23 +76,31 @@ public UserFavoriteArtistDTO getArtistList(long userId) { @Transactional public void addArtistFavorite(final long userId, final String artistId) { User user = userService.findById(userId); - - if (artistFavoriteService.isFavorite(userId, artistId)) { - throw new ConflictException(ErrorMessage.CONFLICT); - } + validateNotExistArtistFavorite(userId, artistId); artistFavoriteService.addFavorite(user, artistId); } @Transactional public void removeArtistFavorite(final long userId, final String artistId) { - if ( - !userService.existsById(userId) || !artistFavoriteService.isFavorite(userId, artistId) - ) { + validateExistUser(userId); + validateExistArtistFavorite(userId, artistId); + + artistFavoriteService.removeFavorite(userId, artistId); + } + + @Transactional(readOnly = true) + protected void validateExistArtistFavorite(final long userId, final String artistId) { + if (!artistFavoriteService.isFavorite(userId, artistId)) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } + } - artistFavoriteService.removeFavorite(userId, artistId); + @Transactional(readOnly = true) + protected void validateNotExistArtistFavorite(final long userId, final String artistId) { + if (artistFavoriteService.isFavorite(userId, artistId)) { + throw new ConflictException(ErrorMessage.CONFLICT); + } } @Transactional @@ -84,21 +108,45 @@ public void addConcertFavorite(final long userId, final long concertId) { User user = userService.findById(userId); Concert concert = concertService.findById(concertId); - if (concertFavoriteService.isFavorite(userId, concertId)) { - throw new ConflictException(ErrorMessage.CONFLICT); - } + validateExistConcertFavorite(userId, concertId); concertFavoriteService.addFavorite(user, concert); } @Transactional public void removeConcertFavorite(final long userId, final long concertId) { - if ( - !userService.existsById(userId) || !concertService.existsById(concertId) || !concertFavoriteService.isFavorite(userId, concertId) - ) { + validateExistUser(userId); + validateExistConcert(concertId); + validateExistConcertFavorite(userId, concertId); + + concertFavoriteService.removeFavorite(userId, concertId); + } + + @Transactional(readOnly = true) + protected void validateExistConcertFavorite(final long userId, final long concertId) { + if (!concertFavoriteService.isFavorite(userId, concertId)) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } + } - concertFavoriteService.removeFavorite(userId, concertId); + @Transactional(readOnly = true) + protected void validateExistUser(final long userId) { + if (!userService.existsById(userId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } + + @Transactional(readOnly = true) + protected void validateExistConcert(final long concertId) { + if (!concertService.existsById(concertId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } + + @Transactional(readOnly = true) + protected void validateExistFestival(final long festivalId) { + if (!festivalService.existsById(festivalId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } } } From 9b528afafa36d1c7ca4bac848562d07586558db4 Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 01:33:55 +0900 Subject: [PATCH 38/96] =?UTF-8?q?refactor=20[#109]=20=EA=B3=B5=EC=97=B0,?= =?UTF-8?q?=20=EC=95=84=ED=8B=B0=EC=8A=A4=ED=8A=B8,=20=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=20=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserFavoriteFacade.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index 2d8c42d..d0c05ec 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -1,5 +1,6 @@ package org.sopt.confeti.api.user.facade; +import java.time.LocalDate; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; @@ -46,11 +47,20 @@ public void addFestivalFavorite(long userId, long festivalId) { public void removeFestivalFavorite(long userId, long festivalId) { User user = userService.findById(userId); Festival festival = festivalService.findById(festivalId); + validateExistUser(userId); + validateExistFestival(festivalId); validateExistFestivalFavorite(userId, festivalId); festivalFavoriteService.delete(user, festival); } + @Transactional(readOnly = true) + protected void validateExistFestival(final long festivalId) { + if (!festivalService.existsById(festivalId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } + @Transactional(readOnly = true) protected void validateExistFestivalFavorite(final long userId, final long festivalId) { if (!festivalFavoriteService.isFavorite(userId, festivalId)) { @@ -142,11 +152,4 @@ protected void validateExistConcert(final long concertId) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } } - - @Transactional(readOnly = true) - protected void validateExistFestival(final long festivalId) { - if (!festivalService.existsById(festivalId)) { - throw new NotFoundException(ErrorMessage.NOT_FOUND); - } - } } From 054bc7de44d3401001a4eb61952ebd07fb66c3b9 Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 01:37:05 +0900 Subject: [PATCH 39/96] =?UTF-8?q?style=20[#109]=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/confeti/api/user/facade/UserFavoriteFacade.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index d0c05ec..cf1090e 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -45,10 +45,9 @@ public void addFestivalFavorite(long userId, long festivalId) { @Transactional public void removeFestivalFavorite(long userId, long festivalId) { + // TODO: 페스티벌 좋아요 삭제 시 엔티티 값을 사용하지 않으므로 아이디 값으로 삭제하도록 리펙토링 예정 User user = userService.findById(userId); Festival festival = festivalService.findById(festivalId); - validateExistUser(userId); - validateExistFestival(festivalId); validateExistFestivalFavorite(userId, festivalId); festivalFavoriteService.delete(user, festival); From e6c17a6e1ef567d644e0444d5f0bbcf04d77677f Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 02:28:34 +0900 Subject: [PATCH 40/96] =?UTF-8?q?refactor=20[#109]=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserTimetableFacade.java | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 2b1e330..2172140 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -23,9 +23,7 @@ public class UserTimetableFacade { @Transactional(readOnly = true) public UserTimetableDTO getTimetablesListAndDate(long userId) { - if (!userService.existsById(userId)) { - throw new NotFoundException(ErrorMessage.NOT_FOUND); - } + validateExistUser(userId); List festivalList = timetableFestivalService.getFetivalList(userId); return UserTimetableDTO.from(festivalList); @@ -33,15 +31,32 @@ public UserTimetableDTO getTimetablesListAndDate(long userId) { @Transactional public void removeTimetableFestival(final long userId, final long festivalId) { - if ( - !userService.existsById(userId) || - !festivalService.existsById(festivalId) || - !timetableFestivalService.existsByUserIdAndFestivalId(userId, festivalId) - ) { + validateExistUser(userId); + validateExistFestival(festivalId); + validateExistTimetableFestival(userId, festivalId); + + timetableFestivalService.removeTimetableFestival(userId, festivalId); + } + + @Transactional(readOnly = true) + protected void validateExistUser(final long userId) { + if (!userService.existsById(userId)) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } + } - timetableFestivalService.removeTimetableFestival(userId, festivalId); + @Transactional(readOnly = true) + protected void validateExistFestival(final long festivalId) { + if (!festivalService.existsById(festivalId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } + + @Transactional(readOnly = true) + protected void validateExistTimetableFestival(final long userId, final long festivalId) { + if (!timetableFestivalService.existsByUserIdAndFestivalId(userId, festivalId)) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } } } From 16d149fa6ff6ac29c535e61d372bc6a16386cae5 Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 02:53:07 +0900 Subject: [PATCH 41/96] =?UTF-8?q?fix=20[#109]=20=EC=A7=80=EB=82=9C=20?= =?UTF-8?q?=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=EC=9D=84=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8=ED=95=98=EA=B3=A0=20=ED=83=80=EC=9E=84=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=97=90=20=EC=B6=94=EA=B0=80=EB=90=9C=20?= =?UTF-8?q?=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=EC=9D=84=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/TimetableFestivalService.java | 2 +- .../infra/repository/TimetableFestivalRepository.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java index d048d96..db464ee 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java @@ -15,7 +15,7 @@ public class TimetableFestivalService { @Transactional(readOnly = true) public List getFetivalList(long userId){ - return timetableFestivalRepository.findByUserId(userId); + return timetableFestivalRepository.findByUserIdWhereEndAtLENow(userId); } @Transactional(readOnly = true) diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java index 10dbc4c..f24761f 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java @@ -1,12 +1,16 @@ package org.sopt.confeti.domain.timetablefestival.infra.repository; +import java.time.LocalDate; import org.sopt.confeti.domain.timetablefestival.TimetableFestival; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface TimetableFestivalRepository extends JpaRepository { - List findByUserId(Long userId); + @Query("select tf from TimetableFestival tf join fetch tf.festival f where tf.user.id = :userId and f.festivalEndAt <= CURRENT_DATE") + List findByUserIdWhereEndAtLENow(@Param("userId") Long userId); boolean existsByUserIdAndFestivalId(final long userId, final long festivalId); From 4b4e224e817d741c7987391c9c016a62ffcf508a Mon Sep 17 00:00:00 2001 From: chyun Date: Sun, 19 Jan 2025 03:06:35 +0900 Subject: [PATCH 42/96] =?UTF-8?q?style=20[#109]=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/repository/TimetableFestivalRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java index f24761f..04d517e 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java @@ -1,6 +1,5 @@ package org.sopt.confeti.domain.timetablefestival.infra.repository; -import java.time.LocalDate; import org.sopt.confeti.domain.timetablefestival.TimetableFestival; import org.springframework.data.jpa.repository.JpaRepository; From 162a545ff53ad983031e953e6a16271ce47483a7 Mon Sep 17 00:00:00 2001 From: chyun Date: Mon, 20 Jan 2025 17:13:23 +0900 Subject: [PATCH 43/96] =?UTF-8?q?feat=20[#111]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=ED=8E=98=EC=8A=A4?= =?UTF-8?q?=ED=8B=B0=EB=B2=8C=20=EC=B6=94=EA=B0=80=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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] 타임테이블에 더 이상 추가할 수 없을 때 에러 메세지 변경 --- .../controller/UserTimetableController.java | 13 +++++ .../confeti/api/user/dto/request/.gitkeep | 0 .../AddTimetableFestivalArtistRequest.java | 6 +++ .../request/AddTimetableFestivalRequest.java | 8 ++++ .../api/user/facade/UserTimetableFacade.java | 48 ++++++++++++++++++- .../AddTimetableFestivalArtiestDTO.java | 13 +++++ .../dto/request/AddTimetableFestivalDTO.java | 16 +++++++ .../festival/application/FestivalService.java | 11 +++++ .../infra/repository/FestivalRepository.java | 2 + .../application/TimetableFestivalService.java | 17 +++++++ .../TimetableFestivalRepository.java | 4 ++ .../confeti/global/message/ErrorMessage.java | 12 ++--- .../global/message/SuccessMessage.java | 11 ++--- 13 files changed, 143 insertions(+), 18 deletions(-) delete mode 100644 src/main/java/org/sopt/confeti/api/user/dto/request/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/request/AddTimetableFestivalArtistRequest.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/request/AddTimetableFestivalRequest.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/request/AddTimetableFestivalArtiestDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/request/AddTimetableFestivalDTO.java diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java index cde71df..7e6cbd6 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java @@ -2,8 +2,10 @@ 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.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.UserTimetableDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; @@ -14,6 +16,8 @@ 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.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -32,6 +36,15 @@ public ResponseEntity> getTimetablesListAndDate(@RequestHeader(" return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserTimetableDetailResponse.of(userTimetableDTO, s3FileHandler)); } + @PostMapping + public ResponseEntity> addTimetableFestival( + @RequestHeader("Authorization") long userId, + @RequestBody AddTimetableFestivalRequest addTimetableFestivalRequest + ) { + userTimetableFacade.addTimetableFestivals(userId, AddTimetableFestivalDTO.from(addTimetableFestivalRequest)); + return ApiResponseUtil.success(SuccessMessage.SUCCESS); + } + @DeleteMapping("/{festivalId}") public ResponseEntity> removeTimetableFestival( @RequestHeader("Authorization") long userId, diff --git a/src/main/java/org/sopt/confeti/api/user/dto/request/.gitkeep b/src/main/java/org/sopt/confeti/api/user/dto/request/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/org/sopt/confeti/api/user/dto/request/AddTimetableFestivalArtistRequest.java b/src/main/java/org/sopt/confeti/api/user/dto/request/AddTimetableFestivalArtistRequest.java new file mode 100644 index 0000000..8df4e0d --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/request/AddTimetableFestivalArtistRequest.java @@ -0,0 +1,6 @@ +package org.sopt.confeti.api.user.dto.request; + +public record AddTimetableFestivalArtistRequest( + long festivalId +) { +} diff --git a/src/main/java/org/sopt/confeti/api/user/dto/request/AddTimetableFestivalRequest.java b/src/main/java/org/sopt/confeti/api/user/dto/request/AddTimetableFestivalRequest.java new file mode 100644 index 0000000..76c5497 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/request/AddTimetableFestivalRequest.java @@ -0,0 +1,8 @@ +package org.sopt.confeti.api.user.dto.request; + +import java.util.List; + +public record AddTimetableFestivalRequest( + List festivals +) { +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 2172140..088cd28 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -1,13 +1,20 @@ package org.sopt.confeti.api.user.facade; +import java.util.function.Predicate; import lombok.RequiredArgsConstructor; 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.UserTimetableDTO; +import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; 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.exception.ConflictException; import org.sopt.confeti.global.exception.NotFoundException; +import org.sopt.confeti.global.exception.UnauthorizedException; import org.sopt.confeti.global.message.ErrorMessage; import org.springframework.transaction.annotation.Transactional; @@ -17,6 +24,8 @@ @RequiredArgsConstructor public class UserTimetableFacade { + private static final int TIMETABLE_FESTIVAL_COUNT_MAXIMUM = 3; + private final UserService userService; private final TimetableFestivalService timetableFestivalService; private final FestivalService festivalService; @@ -38,10 +47,40 @@ public void removeTimetableFestival(final long userId, final long festivalId) { timetableFestivalService.removeTimetableFestival(userId, festivalId); } + @Transactional + public void addTimetableFestivals(final long userId, final AddTimetableFestivalDTO from) { + User user = userService.findById(userId); + List addFestivals = festivalService.findByIdIn( + from.festivals().stream() + .distinct() + .map(AddTimetableFestivalArtiestDTO::festivalId) + .toList() + ); + List currentFestivals = timetableFestivalService.findByUserId(userId).stream() + .map(TimetableFestival::getFestival) + .toList(); + + validateDuplicateTimetableFestival(currentFestivals, addFestivals); + validateCountTimetableFestival(currentFestivals.size(), addFestivals.size()); + + timetableFestivalService.addTimetableFestivals(user, addFestivals); + } + + @Transactional + protected void validateDuplicateTimetableFestival(final List currentFestivals, final List addFestivals) { + if ( + currentFestivals.stream() + .anyMatch(currentFestival -> addFestivals.stream() + .anyMatch(Predicate.isEqual(currentFestival))) + ) { + throw new ConflictException(ErrorMessage.CONFLICT); + } + } + @Transactional(readOnly = true) protected void validateExistUser(final long userId) { if (!userService.existsById(userId)) { - throw new NotFoundException(ErrorMessage.NOT_FOUND); + throw new UnauthorizedException(ErrorMessage.UNAUTHORIZED); } } @@ -52,6 +91,13 @@ protected void validateExistFestival(final long festivalId) { } } + @Transactional(readOnly = true) + protected void validateCountTimetableFestival(final long currentCount, final int addCount) { + if (currentCount + addCount > TIMETABLE_FESTIVAL_COUNT_MAXIMUM) { + throw new ConflictException(ErrorMessage.TIMETABLE_FESTIVAL_IS_FULL); + } + } + @Transactional(readOnly = true) protected void validateExistTimetableFestival(final long userId, final long festivalId) { if (!timetableFestivalService.existsByUserIdAndFestivalId(userId, festivalId)) { diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/request/AddTimetableFestivalArtiestDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/request/AddTimetableFestivalArtiestDTO.java new file mode 100644 index 0000000..7c80256 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/request/AddTimetableFestivalArtiestDTO.java @@ -0,0 +1,13 @@ +package org.sopt.confeti.api.user.facade.dto.request; + +import org.sopt.confeti.api.user.dto.request.AddTimetableFestivalArtistRequest; + +public record AddTimetableFestivalArtiestDTO( + long festivalId +) { + public static AddTimetableFestivalArtiestDTO from(final AddTimetableFestivalArtistRequest addTimetableFestivalArtistRequest) { + return new AddTimetableFestivalArtiestDTO( + addTimetableFestivalArtistRequest.festivalId() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/request/AddTimetableFestivalDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/request/AddTimetableFestivalDTO.java new file mode 100644 index 0000000..35144ed --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/request/AddTimetableFestivalDTO.java @@ -0,0 +1,16 @@ +package org.sopt.confeti.api.user.facade.dto.request; + +import java.util.List; +import org.sopt.confeti.api.user.dto.request.AddTimetableFestivalRequest; + +public record AddTimetableFestivalDTO( + List festivals +) { + public static AddTimetableFestivalDTO from(final AddTimetableFestivalRequest addTimetableFestivalRequest) { + return new AddTimetableFestivalDTO( + addTimetableFestivalRequest.festivals().stream() + .map(AddTimetableFestivalArtiestDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index c178409..cbbb712 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -1,5 +1,6 @@ package org.sopt.confeti.domain.festival.application; +import java.util.List; import lombok.RequiredArgsConstructor; import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.domain.festival.Festival; @@ -8,6 +9,7 @@ import org.sopt.confeti.global.message.ErrorMessage; import org.sopt.confeti.global.util.artistsearcher.ArtistResolver; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -16,12 +18,14 @@ public class FestivalService { private final FestivalRepository festivalRepository; private final ArtistResolver artistResolver; + @Transactional(readOnly = true) public Festival findById(Long festivalId) { Festival festival = festivalRepository.findById(festivalId) .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND)); return festival; } + @Transactional(readOnly = true) public Festival getFestivalDetailByFestivalId(final long festivalId) { Festival festival = festivalRepository.findById(festivalId) .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND)); @@ -30,13 +34,20 @@ public Festival getFestivalDetailByFestivalId(final long festivalId) { return festival; } + @Transactional public void create(final CreateFestivalDTO createFestivalDTO) { festivalRepository.save( Festival.create(createFestivalDTO) ); } + @Transactional(readOnly = true) public boolean existsById(final long festivalId) { return festivalRepository.existsById(festivalId); } + + @Transactional + public List findByIdIn(final List festivalIds) { + return festivalRepository.findByIdIn(festivalIds); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java index b2005a4..de1e4ef 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java @@ -1,7 +1,9 @@ package org.sopt.confeti.domain.festival.infra.repository; +import java.util.List; import org.sopt.confeti.domain.festival.Festival; import org.springframework.data.jpa.repository.JpaRepository; public interface FestivalRepository extends JpaRepository { + List findByIdIn(final List festivalIds); } diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java index db464ee..baf10f0 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/application/TimetableFestivalService.java @@ -1,8 +1,11 @@ package org.sopt.confeti.domain.timetablefestival.application; import lombok.AllArgsConstructor; +import org.sopt.confeti.api.user.facade.dto.request.AddTimetableFestivalDTO; +import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.timetablefestival.TimetableFestival; import org.sopt.confeti.domain.timetablefestival.infra.repository.TimetableFestivalRepository; +import org.sopt.confeti.domain.user.User; import org.springframework.stereotype.Service; import java.util.List; @@ -27,4 +30,18 @@ public boolean existsByUserIdAndFestivalId(final long userId, final long festiva public void removeTimetableFestival(final long userId, final long festivalId) { timetableFestivalRepository.deleteByUserIdAndFestivalId(userId, festivalId); } + + @Transactional + public void addTimetableFestivals(final User user, final List festivals) { + timetableFestivalRepository.saveAll( + festivals.stream() + .map(festival -> TimetableFestival.create(user, festival)) + .toList() + ); + } + + @Transactional(readOnly = true) + public List findByUserId(final long userId) { + return timetableFestivalRepository.findByUserId(userId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java index 04d517e..b31c531 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java @@ -11,7 +11,11 @@ public interface TimetableFestivalRepository extends JpaRepository findByUserIdWhereEndAtLENow(@Param("userId") Long userId); + List findByUserId(final long userId); + boolean existsByUserIdAndFestivalId(final long userId, final long festivalId); void deleteByUserIdAndFestivalId(final long userId, final long festivalId); + + int countByUserId(final long userId); } diff --git a/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java b/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java index eed3881..fd71a63 100644 --- a/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java +++ b/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java @@ -1,7 +1,9 @@ package org.sopt.confeti.global.message; +import lombok.Getter; import org.springframework.http.HttpStatus; +@Getter public enum ErrorMessage { /* 400 Bad Request */ BAD_REQUEST(HttpStatus.BAD_REQUEST, "요청 형식이 올바르지 않습니다."), @@ -21,6 +23,7 @@ public enum ErrorMessage { /* 409 Conflict */ CONFLICT(HttpStatus.CONFLICT, "이미 존재하는 리소스입니다."), + TIMETABLE_FESTIVAL_IS_FULL(HttpStatus.CONFLICT, "더 이상 등록할 수 없습니다."), /* 500 Internal Server Error*/ INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."),; @@ -32,13 +35,4 @@ public enum ErrorMessage { this.httpStatus = httpStatus; this.message = message; } - - public HttpStatus getHttpStatus() { - return httpStatus; - } - - public String getMessage() { - return message; - } - } diff --git a/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java b/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java index cc023d5..254fe23 100644 --- a/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java +++ b/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java @@ -1,23 +1,18 @@ package org.sopt.confeti.global.message; +import lombok.Getter; import org.springframework.http.HttpStatus; +@Getter public enum SuccessMessage { SUCCESS(HttpStatus.OK, "요청이 성공했습니다."),; private final HttpStatus httpStatus; private final String message; - private SuccessMessage(HttpStatus httpStatus, String message) { + SuccessMessage(HttpStatus httpStatus, String message) { this.httpStatus = httpStatus; this.message = message; } - public HttpStatus getHttpStatus() { - return httpStatus; - } - - public String getMessage() { - return message; - } } From 234cdfbfbb4cc3de654ad5937040ad1affb227fb Mon Sep 17 00:00:00 2001 From: chyun Date: Mon, 20 Jan 2025 20:03:52 +0900 Subject: [PATCH 44/96] =?UTF-8?q?fix=20[#114]=20SpotifyAPIHandler=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artistsearcher/SpotifyAPIHandler.java | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java index 475d6b7..890737a 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java @@ -5,19 +5,23 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.Optional; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.apache.hc.core5.http.ParseException; import org.sopt.confeti.annotation.Handler; -import org.sopt.confeti.global.exception.NotFoundException; +import org.sopt.confeti.global.exception.ConfetiException; import org.sopt.confeti.global.message.ErrorMessage; import org.sopt.confeti.global.util.DateConvertor; import org.springframework.beans.factory.annotation.Value; import se.michaelthelin.spotify.SpotifyApi; import se.michaelthelin.spotify.exceptions.SpotifyWebApiException; +import se.michaelthelin.spotify.exceptions.detailed.BadRequestException; +import se.michaelthelin.spotify.exceptions.detailed.NotFoundException; import se.michaelthelin.spotify.model_objects.credentials.ClientCredentials; import se.michaelthelin.spotify.model_objects.specification.AlbumSimplified; import se.michaelthelin.spotify.model_objects.specification.Artist; @@ -60,23 +64,50 @@ public Optional findArtistsByKeyword(final String keyword) { ); } + public Optional findArtistByArtistId(final String artistId) { + if (artistId == null || artistId.isBlank()) { + return Optional.empty(); + } + + try { + Artist artist = spotifyApi.getArtist(artistId) + .build() + .execute(); + + return Optional.of( + ConfetiArtist.toConfetiArtist(artist) + ); + } catch (NotFoundException e) { + return Optional.empty(); + } catch (BadRequestException e) { + throw new ConfetiException(ErrorMessage.BAD_REQUEST); + } catch (IOException | ParseException | SpotifyWebApiException e) { + throw new RuntimeException(e); + } + } + public List findArtistsByArtistIds(final List artistIds) { if (artistIds.isEmpty()) { - return new ArrayList<>(); + return Collections.emptyList(); } try { Artist[] artists = spotifyApi.getSeveralArtists( - artistIds.toArray(new String[0]) + artistIds.stream() + .filter(Objects::nonNull) + .toArray(String[]::new) ) .build() .execute(); return Arrays.stream(artists) + .filter(Objects::nonNull) .map(ConfetiArtist::toConfetiArtist) .toList(); + } catch (BadRequestException e) { + throw new ConfetiException(ErrorMessage.BAD_REQUEST); } catch (IOException | ParseException | SpotifyWebApiException e) { - throw new NotFoundException(ErrorMessage.NOT_FOUND); + throw new RuntimeException(e); } } @@ -92,7 +123,7 @@ private Optional searchArtistByKeyword(final String keyword) { return Arrays.stream(artists.getItems()) .findFirst(); } catch (IOException | ParseException | SpotifyWebApiException e) { - throw new NotFoundException(ErrorMessage.NOT_FOUND); + throw new ConfetiException(ErrorMessage.BAD_REQUEST); } } From 167bed35c905601ea97b3d96ee4243b43670f077 Mon Sep 17 00:00:00 2001 From: chyun Date: Mon, 20 Jan 2025 16:30:11 +0900 Subject: [PATCH 45/96] =?UTF-8?q?refactor=20[#113]=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=ED=8C=8C=EC=9D=BC=20=EB=A1=AC=EB=B3=B5=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/confeti/global/message/ErrorMessage.java | 8 +++----- .../org/sopt/confeti/global/message/SuccessMessage.java | 8 ++------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java b/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java index fd71a63..8d3c70b 100644 --- a/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java +++ b/src/main/java/org/sopt/confeti/global/message/ErrorMessage.java @@ -1,9 +1,11 @@ package org.sopt.confeti.global.message; -import lombok.Getter; import org.springframework.http.HttpStatus; +import lombok.AllArgsConstructor; +import lombok.Getter; @Getter +@AllArgsConstructor public enum ErrorMessage { /* 400 Bad Request */ BAD_REQUEST(HttpStatus.BAD_REQUEST, "요청 형식이 올바르지 않습니다."), @@ -31,8 +33,4 @@ public enum ErrorMessage { private final HttpStatus httpStatus; private final String message; - ErrorMessage(HttpStatus httpStatus, String message) { - this.httpStatus = httpStatus; - this.message = message; - } } diff --git a/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java b/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java index 254fe23..333f241 100644 --- a/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java +++ b/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java @@ -1,18 +1,14 @@ package org.sopt.confeti.global.message; +import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.http.HttpStatus; @Getter +@AllArgsConstructor public enum SuccessMessage { SUCCESS(HttpStatus.OK, "요청이 성공했습니다."),; private final HttpStatus httpStatus; private final String message; - - SuccessMessage(HttpStatus httpStatus, String message) { - this.httpStatus = httpStatus; - this.message = message; - } - } From 762a682890f65e502ebe74519d637f3d5d2767b6 Mon Sep 17 00:00:00 2001 From: chyun Date: Mon, 20 Jan 2025 16:30:42 +0900 Subject: [PATCH 46/96] =?UTF-8?q?feat=20[#113]=20=ED=8C=8C=EC=82=AC?= =?UTF-8?q?=EB=93=9C=20=EA=B3=84=EC=B8=B5=20=EA=B4=80=EC=8B=AC=20=EA=B3=B5?= =?UTF-8?q?=EC=97=B0=20DTO=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/UserFavoritePerformanceDTO.java | 11 +++++++++++ .../dto/response/UserFavoritePerformancesDTO.java | 8 ++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformancesDTO.java diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java new file mode 100644 index 0000000..655363e --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java @@ -0,0 +1,11 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.global.common.constant.PerformanceType; + +public record UserFavoritePerformanceDTO( + long performanceId, + PerformanceType type, + String title, + String posterPath +) { +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformancesDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformancesDTO.java new file mode 100644 index 0000000..f902a3c --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformancesDTO.java @@ -0,0 +1,8 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import java.util.List; + +public record UserFavoritePerformancesDTO( + List performances +) { +} From 61671d75a73bf775df16bb14bb336755cca98d19 Mon Sep 17 00:00:00 2001 From: chyun Date: Mon, 20 Jan 2025 16:30:53 +0900 Subject: [PATCH 47/96] =?UTF-8?q?feat=20[#113]=20=EA=B3=B5=EC=97=B0=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=83=81=EC=88=98=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/constant/PerformanceType.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/global/common/constant/PerformanceType.java diff --git a/src/main/java/org/sopt/confeti/global/common/constant/PerformanceType.java b/src/main/java/org/sopt/confeti/global/common/constant/PerformanceType.java new file mode 100644 index 0000000..d700fbb --- /dev/null +++ b/src/main/java/org/sopt/confeti/global/common/constant/PerformanceType.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.global.common.constant; + +import java.util.Arrays; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.sopt.confeti.global.exception.ConfetiException; +import org.sopt.confeti.global.message.ErrorMessage; + +@Getter +@AllArgsConstructor +public enum PerformanceType { + CONCERT("concert"), FESTIVAL("festival"); + + private final String type; + + public static PerformanceType convert(final String input) { + return Arrays.stream(PerformanceType.values()) + .filter(performanceType -> performanceType.getType().equals(input)) + .findFirst() + .orElseThrow( + () -> new ConfetiException(ErrorMessage.BAD_REQUEST) + ); + } +} From 5b29d6b6396cca42b079f685344e9522a0820eb6 Mon Sep 17 00:00:00 2001 From: chyun Date: Mon, 20 Jan 2025 19:53:56 +0900 Subject: [PATCH 48/96] =?UTF-8?q?feat=20[#113]=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?=EC=A4=91=EA=B0=84=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserFavoriteController.java | 9 +++++ .../api/user/facade/UserFavoriteFacade.java | 8 +++++ .../user/application/dto/response/.gitkeep | 0 .../view/performance/PerformanceDTO.java | 24 +++++++++++++ .../application/PerformanceService.java | 20 +++++++++++ .../application/dto/request/.gitkeep | 0 .../application/dto/response/.gitkeep | 0 .../repository/PerformanceRepository.java | 34 +++++++++++++++++++ 8 files changed, 95 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/domain/user/application/dto/response/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index e834b91..c52abe7 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -5,6 +5,7 @@ import org.sopt.confeti.api.user.dto.response.UserFavoriteResponse; import org.sopt.confeti.api.user.facade.UserFavoriteFacade; import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; +import org.sopt.confeti.api.user.facade.dto.response.UserFavoritePerformancesDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; import org.sopt.confeti.global.util.ApiResponseUtil; @@ -75,4 +76,12 @@ public ResponseEntity> removeConcertFavorite( userFavoriteFacade.removeConcertFavorite(userId, concertId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } + + @GetMapping("/performances/preview") + public ResponseEntity> getFavoritePerformances( + @RequestHeader("Authorization") long userId + ) { + UserFavoritePerformancesDTO userFavoritePerformancesDTO = userFavoriteFacade.getFavoritePerformances(userId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, ); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index cf1090e..3fa1fd6 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; +import org.sopt.confeti.api.user.facade.dto.response.UserFavoritePerformancesDTO; import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; import org.sopt.confeti.domain.artistfavorite.application.ArtistFavoriteService; import org.sopt.confeti.domain.concert.application.ConcertService; @@ -131,6 +132,13 @@ public void removeConcertFavorite(final long userId, final long concertId) { concertFavoriteService.removeFavorite(userId, concertId); } + @Transactional(readOnly = true) + public UserFavoritePerformancesDTO getFavoritePerformances(final long userId) { + validateExistUser(userId); + + List<> + } + @Transactional(readOnly = true) protected void validateExistConcertFavorite(final long userId, final long concertId) { if (!concertFavoriteService.isFavorite(userId, concertId)) { diff --git a/src/main/java/org/sopt/confeti/domain/user/application/dto/response/.gitkeep b/src/main/java/org/sopt/confeti/domain/user/application/dto/response/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java new file mode 100644 index 0000000..10610e4 --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.domain.view.performance; + +import org.sopt.confeti.global.common.constant.PerformanceType; + +public record PerformanceDTO( + long performanceId, + PerformanceType type, + String title, + String posterPath +) { + public PerformanceDTO( + final long performanceId, + final String type, + final String title, + final String posterPath + ) { + this( + performanceId, + PerformanceType.convert(type), + title, + posterPath + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java new file mode 100644 index 0000000..5c89e2c --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java @@ -0,0 +1,20 @@ +package org.sopt.confeti.domain.view.performance.application; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.domain.view.performance.PerformanceDTO; +import org.sopt.confeti.domain.view.performance.infra.repository.PerformanceRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class PerformanceService { + + private final PerformanceRepository performanceRepository; + + @Transactional(readOnly = true) + public List getFavoritePerformancesPreview() { + + } +} diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/.gitkeep b/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/.gitkeep b/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java new file mode 100644 index 0000000..b5c546d --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java @@ -0,0 +1,34 @@ +package org.sopt.confeti.domain.view.performance.infra.repository; + +import jakarta.persistence.EntityManager; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.domain.view.performance.PerformanceDTO; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class PerformanceRepository { + + private static final int PREVIEW_FAVORITE_PERFORMANCE_COUNT = 3; + + private final EntityManager em; + + public List findFavoritePerformancesPreview() { + return em.createQuery( + "select new org.sopt.confeti.domain.view.performance.PerformanceDTO(p.id, p.)" + + " from (" + + " select c.id id, \"concert\" type, c.concertTitle title, c.concertPosterPath posterPath, c.concertStartAt startAt" + + " from Concert c" + + " where c.concertEndAt >= CURRENT DATE" + + " union" + + " select f.id id, \"festival\" type, f.concertTitle title, f.concertPosterPath posterPath, f.festivalStartAt startAt" + + " from Festival f" + + " where f.concertEndAt >= CURRENT DATE" + + " order by start_at" + + " limit :previewCount" + + ") p" + ) + .setParameter("previewCount", PREVIEW_FAVORITE_PERFORMANCE_COUNT) + } +} From 59eafecdf43cc7b5f395983f148faab73b46ebb7 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 00:12:44 +0900 Subject: [PATCH 49/96] =?UTF-8?q?feat=20[#113]=20=EA=B4=80=EC=8B=AC=20?= =?UTF-8?q?=EA=B3=B5=EC=97=B0=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserFavoriteController.java | 5 +- .../UserFavoritePerformanceResponse.java | 21 ++++++++ .../UserFavoritePerformancesResponse.java | 18 +++++++ .../api/user/facade/UserFavoriteFacade.java | 16 ++---- .../response/UserFavoritePerformanceDTO.java | 9 ++++ .../response/UserFavoritePerformancesDTO.java | 8 +++ .../view/performance/PerformanceDTO.java | 5 +- .../application/PerformanceService.java | 4 +- .../repository/PerformanceRepository.java | 52 ++++++++++++------- 9 files changed, 104 insertions(+), 34 deletions(-) create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformanceResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformancesResponse.java diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index c52abe7..dee5b4f 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -2,6 +2,7 @@ import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; +import org.sopt.confeti.api.user.dto.response.UserFavoritePerformancesResponse; import org.sopt.confeti.api.user.dto.response.UserFavoriteResponse; import org.sopt.confeti.api.user.facade.UserFavoriteFacade; import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; @@ -9,6 +10,7 @@ import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; import org.sopt.confeti.global.util.ApiResponseUtil; +import org.sopt.confeti.global.util.S3FileHandler; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -20,6 +22,7 @@ public class UserFavoriteController { private final UserFavoriteFacade userFavoriteFacade; + private final S3FileHandler s3FileHandler; @PostMapping("/festivals/{festivalId}") public ResponseEntity> postFavoriteFestival(@RequestHeader("Authorization") Long userId, @PathVariable @@ -82,6 +85,6 @@ public ResponseEntity> getFavoritePerformances( @RequestHeader("Authorization") long userId ) { UserFavoritePerformancesDTO userFavoritePerformancesDTO = userFavoriteFacade.getFavoritePerformances(userId); - return ApiResponseUtil.success(SuccessMessage.SUCCESS, ); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserFavoritePerformancesResponse.of(userFavoritePerformancesDTO, s3FileHandler)); } } diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformanceResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformanceResponse.java new file mode 100644 index 0000000..051922e --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformanceResponse.java @@ -0,0 +1,21 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserFavoritePerformanceDTO; +import org.sopt.confeti.global.util.S3FileHandler; + +public record UserFavoritePerformanceResponse( + long performanceId, + String type, + String title, + String posterUrl +) { + public static UserFavoritePerformanceResponse of(final UserFavoritePerformanceDTO performanceDTO, final + S3FileHandler s3FileHandler) { + return new UserFavoritePerformanceResponse( + performanceDTO.performanceId(), + performanceDTO.type().getType(), + performanceDTO.title(), + s3FileHandler.getFileUrl(performanceDTO.posterPath()) + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformancesResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformancesResponse.java new file mode 100644 index 0000000..bb88f47 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformancesResponse.java @@ -0,0 +1,18 @@ +package org.sopt.confeti.api.user.dto.response; + +import java.util.List; +import org.sopt.confeti.api.user.facade.dto.response.UserFavoritePerformancesDTO; +import org.sopt.confeti.global.util.S3FileHandler; + +public record UserFavoritePerformancesResponse( + List performances +) { + public static UserFavoritePerformancesResponse of(final UserFavoritePerformancesDTO performancesDTO, final + S3FileHandler s3FileHandler) { + return new UserFavoritePerformancesResponse( + performancesDTO.performances().stream() + .map(performanceDTO -> UserFavoritePerformanceResponse.of(performanceDTO, s3FileHandler)) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index 3fa1fd6..afaf535 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -1,6 +1,5 @@ package org.sopt.confeti.api.user.facade; -import java.time.LocalDate; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; @@ -10,13 +9,13 @@ import org.sopt.confeti.domain.concert.application.ConcertService; import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService; import org.sopt.confeti.domain.concert.Concert; -import org.sopt.confeti.domain.concert.application.ConcertService; -import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; import org.sopt.confeti.domain.user.User; import org.sopt.confeti.domain.user.application.UserService; +import org.sopt.confeti.domain.view.performance.PerformanceDTO; +import org.sopt.confeti.domain.view.performance.application.PerformanceService; import org.sopt.confeti.global.exception.ConflictException; import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.message.ErrorMessage; @@ -34,6 +33,7 @@ public class UserFavoriteFacade { private final ArtistFavoriteService artistFavoriteService; private final ConcertFavoriteService concertFavoriteService; private final ConcertService concertService; + private final PerformanceService performanceService; @Transactional public void addFestivalFavorite(long userId, long festivalId) { @@ -54,13 +54,6 @@ public void removeFestivalFavorite(long userId, long festivalId) { festivalFavoriteService.delete(user, festival); } - @Transactional(readOnly = true) - protected void validateExistFestival(final long festivalId) { - if (!festivalService.existsById(festivalId)) { - throw new NotFoundException(ErrorMessage.NOT_FOUND); - } - } - @Transactional(readOnly = true) protected void validateExistFestivalFavorite(final long userId, final long festivalId) { if (!festivalFavoriteService.isFavorite(userId, festivalId)) { @@ -136,7 +129,8 @@ public void removeConcertFavorite(final long userId, final long concertId) { public UserFavoritePerformancesDTO getFavoritePerformances(final long userId) { validateExistUser(userId); - List<> + List performances = performanceService.getFavoritePerformancesPreview(userId); + return UserFavoritePerformancesDTO.from(performances); } @Transactional(readOnly = true) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java index 655363e..c04b325 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java @@ -1,5 +1,6 @@ package org.sopt.confeti.api.user.facade.dto.response; +import org.sopt.confeti.domain.view.performance.PerformanceDTO; import org.sopt.confeti.global.common.constant.PerformanceType; public record UserFavoritePerformanceDTO( @@ -8,4 +9,12 @@ public record UserFavoritePerformanceDTO( String title, String posterPath ) { + public static UserFavoritePerformanceDTO from(final PerformanceDTO performance) { + return new UserFavoritePerformanceDTO( + performance.performanceId(), + performance.type(), + performance.title(), + performance.posterPath() + ); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformancesDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformancesDTO.java index f902a3c..9432190 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformancesDTO.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformancesDTO.java @@ -1,8 +1,16 @@ package org.sopt.confeti.api.user.facade.dto.response; import java.util.List; +import org.sopt.confeti.domain.view.performance.PerformanceDTO; public record UserFavoritePerformancesDTO( List performances ) { + public static UserFavoritePerformancesDTO from(final List performances) { + return new UserFavoritePerformancesDTO( + performances.stream() + .map(UserFavoritePerformanceDTO::from) + .toList() + ); + } } diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java index 10610e4..80ba2c7 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java @@ -1,5 +1,6 @@ package org.sopt.confeti.domain.view.performance; +import java.time.LocalDate; import org.sopt.confeti.global.common.constant.PerformanceType; public record PerformanceDTO( @@ -8,13 +9,13 @@ public record PerformanceDTO( String title, String posterPath ) { - public PerformanceDTO( + public static PerformanceDTO of( final long performanceId, final String type, final String title, final String posterPath ) { - this( + return new PerformanceDTO( performanceId, PerformanceType.convert(type), title, diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java index 5c89e2c..e64904e 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java @@ -14,7 +14,7 @@ public class PerformanceService { private final PerformanceRepository performanceRepository; @Transactional(readOnly = true) - public List getFavoritePerformancesPreview() { - + public List getFavoritePerformancesPreview(final long userId) { + return performanceRepository.findFavoritePerformancesPreview(userId); } } diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java index b5c546d..d81de8c 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java @@ -1,34 +1,50 @@ package org.sopt.confeti.domain.view.performance.infra.repository; import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import java.util.List; import lombok.RequiredArgsConstructor; import org.sopt.confeti.domain.view.performance.PerformanceDTO; +import org.sopt.confeti.global.common.constant.PerformanceType; import org.springframework.stereotype.Repository; @Repository @RequiredArgsConstructor public class PerformanceRepository { - private static final int PREVIEW_FAVORITE_PERFORMANCE_COUNT = 3; - private final EntityManager em; - public List findFavoritePerformancesPreview() { - return em.createQuery( - "select new org.sopt.confeti.domain.view.performance.PerformanceDTO(p.id, p.)" + - " from (" + - " select c.id id, \"concert\" type, c.concertTitle title, c.concertPosterPath posterPath, c.concertStartAt startAt" + - " from Concert c" + - " where c.concertEndAt >= CURRENT DATE" + - " union" + - " select f.id id, \"festival\" type, f.concertTitle title, f.concertPosterPath posterPath, f.festivalStartAt startAt" + - " from Festival f" + - " where f.concertEndAt >= CURRENT DATE" + - " order by start_at" + - " limit :previewCount" + - ") p" - ) - .setParameter("previewCount", PREVIEW_FAVORITE_PERFORMANCE_COUNT) + int PREVIEW_FAVORITE_PERFORMANCE_COUNT = 3; + + public List findFavoritePerformancesPreview(final long userId) { + String sql = + "SELECT c.concert_id performanceId, :concertType type, c.concert_title title, c.concert_poster_path posterPath, c.concert_start_at startAt" + + " FROM concert_favorites cf INNER JOIN concerts c ON cf.concert_id = c.concert_id" + + " WHERE cf.user_id = :userId AND c.concert_end_at >= CURRENT_DATE" + + " UNION" + + " SELECT f.festival_id performanceId, :festivalType type, f.festival_title title, f.festival_poster_path posterPath, f.festival_start_at startAt" + + " FROM festival_favorites ff INNER JOIN festivals f ON ff.festival_id = f.festival_id" + + " WHERE ff.user_id = :userId AND f.festival_end_at >= CURRENT_DATE" + + " ORDER BY startAt ASC" + + " LIMIT :performancePreviewCount"; + + Query query = em.createNativeQuery(sql); + query.setParameter("userId", userId); + query.setParameter("concertType", PerformanceType.CONCERT.getType()); + query.setParameter("festivalType", PerformanceType.FESTIVAL.getType()); + query.setParameter("performancePreviewCount", PREVIEW_FAVORITE_PERFORMANCE_COUNT); + + return convertToPerformanceDTOs(query.getResultList()); + } + + private List convertToPerformanceDTOs(final List results) { + return results.stream() + .map(result -> PerformanceDTO.of( + ((Number) result[0]).longValue(), + (String) result[1], + (String) result[2], + (String) result[3] + )) + .toList(); } } From a06984f1bab4e44b95132735bf3950b0378f83be Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 00:48:13 +0900 Subject: [PATCH 50/96] =?UTF-8?q?feat=20[#117]=20=EC=95=84=ED=8B=B0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20API=EC=97=90=20=EC=95=84=ED=8B=B0=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=95=84=EC=9D=B4=EB=94=94=EA=B0=80=20=EC=A1=B4=EC=9E=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=EC=A7=80=20=EA=B2=80=EC=A6=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/controller/UserFavoriteController.java | 2 +- .../confeti/api/user/facade/UserFavoriteFacade.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index dee5b4f..0e3cf96 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -46,7 +46,7 @@ public ResponseEntity> getFavoriteArtists(@RequestHeader("Author @PostMapping("/artists/{artistId}") public ResponseEntity> addArtistFavorite( - @RequestHeader("Authorization") Long userId, + @RequestHeader("Authorization") long userId, @PathVariable(name = "artistId") String artistId ) { userFavoriteFacade.addArtistFavorite(userId, artistId); diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index afaf535..1efc0bd 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -1,5 +1,6 @@ package org.sopt.confeti.api.user.facade; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO; @@ -19,6 +20,8 @@ import org.sopt.confeti.global.exception.ConflictException; import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.message.ErrorMessage; +import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; +import org.sopt.confeti.global.util.artistsearcher.SpotifyAPIHandler; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -33,6 +36,7 @@ public class UserFavoriteFacade { private final ArtistFavoriteService artistFavoriteService; private final ConcertFavoriteService concertFavoriteService; private final ConcertService concertService; + private final SpotifyAPIHandler spotifyAPIHandler; private final PerformanceService performanceService; @Transactional @@ -79,6 +83,7 @@ public UserFavoriteArtistDTO getArtistList(long userId) { @Transactional public void addArtistFavorite(final long userId, final String artistId) { User user = userService.findById(userId); + validateExistArtist(artistId); validateNotExistArtistFavorite(userId, artistId); artistFavoriteService.addFavorite(user, artistId); @@ -92,6 +97,13 @@ public void removeArtistFavorite(final long userId, final String artistId) { artistFavoriteService.removeFavorite(userId, artistId); } + private void validateExistArtist(final String artistId) { + Optional artist = spotifyAPIHandler.findArtistByArtistId(artistId); + if (artist.isEmpty()) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + } + @Transactional(readOnly = true) protected void validateExistArtistFavorite(final long userId, final String artistId) { if (!artistFavoriteService.isFavorite(userId, artistId)) { From 3361de4897d53a3361d6caa0f23e6303502f3763 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 00:56:58 +0900 Subject: [PATCH 51/96] =?UTF-8?q?feat=20[#117]=20ConfetiException=20?= =?UTF-8?q?=EA=B8=80=EB=A1=9C=EB=B2=8C=20=ED=95=B8=EB=93=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/global/exception/GlobalExceptionHandler.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/sopt/confeti/global/exception/GlobalExceptionHandler.java b/src/main/java/org/sopt/confeti/global/exception/GlobalExceptionHandler.java index 3a6b123..5ca6fa7 100644 --- a/src/main/java/org/sopt/confeti/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/org/sopt/confeti/global/exception/GlobalExceptionHandler.java @@ -39,4 +39,9 @@ public ResponseEntity> handleHttpRequestMethodNotSupportedExcept public ResponseEntity> handleConflictException(ConflictException e) { return ApiResponseUtil.failure(ErrorMessage.CONFLICT); } + + @ExceptionHandler(ConfetiException.class) + public ResponseEntity> handleConfetiException(ConfetiException e) { + return ApiResponseUtil.failure(e.getErrorMessage()); + } } From 578a246136aa4548cbee5401d9464cac7e877176 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 01:01:52 +0900 Subject: [PATCH 52/96] =?UTF-8?q?feat=20[#117]=20=EC=95=84=ED=8B=B0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=95=84=EC=9D=B4=EB=94=94=EA=B0=80=20?= =?UTF-8?q?=EC=9E=98=EB=AA=BB=EB=90=98=EC=97=88=EC=9D=84=20=EB=95=8C?= =?UTF-8?q?=EB=8F=84=20Not=20Found=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/global/util/artistsearcher/SpotifyAPIHandler.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java index 890737a..10e6c7a 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java @@ -77,10 +77,8 @@ public Optional findArtistByArtistId(final String artistId) { return Optional.of( ConfetiArtist.toConfetiArtist(artist) ); - } catch (NotFoundException e) { + } catch (NotFoundException | BadRequestException e) { return Optional.empty(); - } catch (BadRequestException e) { - throw new ConfetiException(ErrorMessage.BAD_REQUEST); } catch (IOException | ParseException | SpotifyWebApiException e) { throw new RuntimeException(e); } From d9d3a07e17ba620e303de4517446e3eeca08ceb7 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 04:08:51 +0900 Subject: [PATCH 53/96] =?UTF-8?q?fix=20[#121]=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artistsearcher/SpotifyAPIHandler.java | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java index 10e6c7a..09198e8 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java @@ -3,7 +3,6 @@ import com.neovisionaries.i18n.CountryCode; import java.io.IOException; import java.time.LocalDate; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -22,10 +21,13 @@ import se.michaelthelin.spotify.exceptions.SpotifyWebApiException; import se.michaelthelin.spotify.exceptions.detailed.BadRequestException; import se.michaelthelin.spotify.exceptions.detailed.NotFoundException; +import se.michaelthelin.spotify.exceptions.detailed.UnauthorizedException; +import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials; import se.michaelthelin.spotify.model_objects.credentials.ClientCredentials; import se.michaelthelin.spotify.model_objects.specification.AlbumSimplified; import se.michaelthelin.spotify.model_objects.specification.Artist; import se.michaelthelin.spotify.model_objects.specification.Paging; +import se.michaelthelin.spotify.requests.authorization.authorization_code.AuthorizationCodeRefreshRequest; import se.michaelthelin.spotify.requests.authorization.client_credentials.ClientCredentialsRequest; @Handler @@ -77,6 +79,9 @@ public Optional findArtistByArtistId(final String artistId) { return Optional.of( ConfetiArtist.toConfetiArtist(artist) ); + } catch (UnauthorizedException e) { + refreshToken(); + return findArtistByArtistId(artistId); } catch (NotFoundException | BadRequestException e) { return Optional.empty(); } catch (IOException | ParseException | SpotifyWebApiException e) { @@ -102,6 +107,9 @@ public List findArtistsByArtistIds(final List artistIds) .filter(Objects::nonNull) .map(ConfetiArtist::toConfetiArtist) .toList(); + } catch (UnauthorizedException e) { + refreshToken(); + return findArtistsByArtistIds(artistIds); } catch (BadRequestException e) { throw new ConfetiException(ErrorMessage.BAD_REQUEST); } catch (IOException | ParseException | SpotifyWebApiException e) { @@ -120,15 +128,18 @@ private Optional searchArtistByKeyword(final String keyword) { return Arrays.stream(artists.getItems()) .findFirst(); + } catch (UnauthorizedException e) { + refreshToken(); + return searchArtistByKeyword(keyword); } catch (IOException | ParseException | SpotifyWebApiException e) { throw new ConfetiException(ErrorMessage.BAD_REQUEST); } } private LocalDate findLatestReleaseAt(final String keyword, final Artist artist) { - Paging albums = searchAlbumByKeyword(keyword); + Optional> albums = searchAlbumByKeyword(keyword); - return Arrays.stream(albums.getItems()) + return albums.map(albumSimplifiedPaging -> Arrays.stream(albumSimplifiedPaging.getItems()) .filter((searchedAlbum) -> Arrays.stream(searchedAlbum.getArtists()) .anyMatch((searchedArtist) -> @@ -139,17 +150,24 @@ private LocalDate findLatestReleaseAt(final String keyword, final Artist artist) .map( albumSimplified -> DateConvertor.convertToSpotifyLocalDate(albumSimplified.getReleaseDate()) ) - .get(); + .get()) + .orElse(null); + } - private Paging searchAlbumByKeyword(final String keyword) { + private Optional> searchAlbumByKeyword(final String keyword) { try { - return spotifyApi.searchAlbums(keyword) + return Optional.of(spotifyApi.searchAlbums(keyword) .market(CountryCode.KR) .limit(ALBUM_LIMIT) .offset(ALBUM_OFFSET) .build() - .execute(); + .execute()); + } catch (UnauthorizedException e) { + refreshToken(); + return searchAlbumByKeyword(keyword); + } catch (NotFoundException | BadRequestException e) { + return Optional.empty(); } catch (IOException | ParseException | SpotifyWebApiException e) { throw new RuntimeException(e); } @@ -174,4 +192,16 @@ private void generateAccessToken() { } } + public void refreshToken() { + try { + AuthorizationCodeRefreshRequest refreshRequest = spotifyApi.authorizationCodeRefresh().build(); + AuthorizationCodeCredentials credentials = refreshRequest.execute(); + + spotifyApi.setAccessToken(credentials.getAccessToken()); + + } catch (IOException | SpotifyWebApiException | ParseException e) { + throw new ConfetiException(ErrorMessage.INTERNAL_SERVER_ERROR); + } + } + } From a833bdcfcefacfa8c45753072ef301dfd3423a55 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 02:00:03 +0900 Subject: [PATCH 54/96] =?UTF-8?q?fix=20[#119]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=ED=8E=98=EC=8A=A4?= =?UTF-8?q?=ED=8B=B0=EB=B2=8C=20=EC=B6=94=EA=B0=80=20=EC=8B=9C=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EC=97=AC=EB=B6=80=20=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserTimetableFacade.java | 8 ++++++ .../TimetableFestivalRepository.java | 2 -- .../application/UserTimetableService.java | 28 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 088cd28..0debcfb 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -12,6 +12,7 @@ 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.domain.usertimetable.application.UserTimetableService; import org.sopt.confeti.global.exception.ConflictException; import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.exception.UnauthorizedException; @@ -28,6 +29,7 @@ public class UserTimetableFacade { private final UserService userService; private final TimetableFestivalService timetableFestivalService; + private final UserTimetableService userTimetableService; private final FestivalService festivalService; @Transactional(readOnly = true) @@ -64,6 +66,12 @@ public void addTimetableFestivals(final long userId, final AddTimetableFestivalD validateCountTimetableFestival(currentFestivals.size(), addFestivals.size()); timetableFestivalService.addTimetableFestivals(user, addFestivals); + userTimetableService.addUserTimetables(user, addFestivals.stream() + .flatMap(festival -> festival.getDates().stream()) + .flatMap(festivalDate -> festivalDate.getStages().stream()) + .flatMap(festivalStage -> festivalStage.getTimes().stream()) + .toList() + ); } @Transactional diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java index b31c531..f007421 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java @@ -16,6 +16,4 @@ public interface TimetableFestivalRepository extends JpaRepository festivalTimes) { + userTimetableRepository.saveAll( + festivalTimes.stream() + .map(festivalTime -> UserTimetable.create(user, festivalTime, DEFAULT_IS_SELECTED)) + .toList() + ); + } +} From 882cdc8d94004cfda55a6397090d2adb709da653 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 02:41:27 +0900 Subject: [PATCH 55/96] =?UTF-8?q?refactor=20[#119]=20ERD=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=ED=8C=8C=EC=9D=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/timetablefestival/TimetableFestival.java | 12 ++++++++++++ .../java/org/sopt/confeti/domain/user/User.java | 3 --- .../confeti/domain/usertimetable/UserTimetable.java | 13 +++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/TimetableFestival.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/TimetableFestival.java index 6838959..7f32cde 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/TimetableFestival.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/TimetableFestival.java @@ -1,18 +1,21 @@ package org.sopt.confeti.domain.timetablefestival; import jakarta.persistence.*; +import java.util.List; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.user.User; +import org.sopt.confeti.domain.usertimetable.UserTimetable; @Entity @Table(name="timetable_festivals") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class TimetableFestival { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "timetable_festival_id") @@ -26,10 +29,19 @@ public class TimetableFestival { @JoinColumn(name = "festival_id") private Festival festival; + @OneToMany(mappedBy = "timetableFestival", cascade = CascadeType.ALL) + private List userTimetables; + @Builder public TimetableFestival(User user, Festival festival) { this.user = user; this.festival = festival; + + this.userTimetables = festival.getDates().stream() + .flatMap(festivalDate -> festivalDate.getStages().stream()) + .flatMap(festivalStage -> festivalStage.getTimes().stream()) + .map(festivalTime -> UserTimetable.create(this, festivalTime, false)) + .toList(); } public static TimetableFestival create(User user, Festival festival) { diff --git a/src/main/java/org/sopt/confeti/domain/user/User.java b/src/main/java/org/sopt/confeti/domain/user/User.java index 480f229..8695549 100644 --- a/src/main/java/org/sopt/confeti/domain/user/User.java +++ b/src/main/java/org/sopt/confeti/domain/user/User.java @@ -30,9 +30,6 @@ public class User { @Column(length=250, nullable = false) private String profilePath; - @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE, orphanRemoval = true ) - private List timetables = new ArrayList<>(); - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true ) private List artistFavorites = new ArrayList<>(); diff --git a/src/main/java/org/sopt/confeti/domain/usertimetable/UserTimetable.java b/src/main/java/org/sopt/confeti/domain/usertimetable/UserTimetable.java index 2552e67..e51e8cc 100644 --- a/src/main/java/org/sopt/confeti/domain/usertimetable/UserTimetable.java +++ b/src/main/java/org/sopt/confeti/domain/usertimetable/UserTimetable.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.sopt.confeti.domain.festivaltime.FestivalTime; +import org.sopt.confeti.domain.timetablefestival.TimetableFestival; import org.sopt.confeti.domain.user.User; @Entity @@ -19,8 +20,8 @@ public class UserTimetable { private Long id; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name="user_id") - private User user; + @JoinColumn(name="timetable_festival_id") + private TimetableFestival timetableFestival; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="festival_time_id") @@ -30,15 +31,15 @@ public class UserTimetable { private boolean isSelected; @Builder - public UserTimetable(User user, FestivalTime festivalTime, boolean isSelected) { - this.user = user; + public UserTimetable(TimetableFestival timetableFestival, FestivalTime festivalTime, boolean isSelected) { + this.timetableFestival = timetableFestival; this.festivalTime = festivalTime; this.isSelected = isSelected; } - public static UserTimetable create(User user, FestivalTime festivalTime, boolean isSelected) { + public static UserTimetable create(TimetableFestival timetableFestival, FestivalTime festivalTime, boolean isSelected) { return UserTimetable.builder() - .user(user) + .timetableFestival(timetableFestival) .festivalTime(festivalTime) .isSelected(isSelected) .build(); From 17fd48b82646eab3820917f442e887f0b9de2763 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 03:00:28 +0900 Subject: [PATCH 56/96] =?UTF-8?q?refactor=20[#119]=20=ED=8E=98=EC=8A=A4?= =?UTF-8?q?=ED=8B=B0=EB=B2=8C=20=EC=A1=B0=ED=9A=8C=20=EC=84=B1=EB=8A=A5=20?= =?UTF-8?q?=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserTimetableFacade.java | 22 ++++++++----------- .../festival/application/FestivalService.java | 4 ++-- .../infra/repository/FestivalRepository.java | 14 +++++++++++- .../domain/user/application/UserService.java | 8 +++++++ .../user/infra/repository/UserRepository.java | 11 ++++++++++ 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 0debcfb..c53995f 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -51,27 +51,23 @@ public void removeTimetableFestival(final long userId, final long festivalId) { @Transactional public void addTimetableFestivals(final long userId, final AddTimetableFestivalDTO from) { - User user = userService.findById(userId); - List addFestivals = festivalService.findByIdIn( + User user = userService.findUserTimetablesById(userId); + List addFestivals = festivalService.findAllByIdIn( from.festivals().stream() .distinct() .map(AddTimetableFestivalArtiestDTO::festivalId) .toList() ); - List currentFestivals = timetableFestivalService.findByUserId(userId).stream() - .map(TimetableFestival::getFestival) - .toList(); - validateDuplicateTimetableFestival(currentFestivals, addFestivals); - validateCountTimetableFestival(currentFestivals.size(), addFestivals.size()); + validateDuplicateTimetableFestival( + user.getTimetableFestivals().stream() + .map(TimetableFestival::getFestival) + .toList(), + addFestivals + ); + validateCountTimetableFestival(user.getTimetableFestivals().size(), addFestivals.size()); timetableFestivalService.addTimetableFestivals(user, addFestivals); - userTimetableService.addUserTimetables(user, addFestivals.stream() - .flatMap(festival -> festival.getDates().stream()) - .flatMap(festivalDate -> festivalDate.getStages().stream()) - .flatMap(festivalStage -> festivalStage.getTimes().stream()) - .toList() - ); } @Transactional diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index cbbb712..ef42c51 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -47,7 +47,7 @@ public boolean existsById(final long festivalId) { } @Transactional - public List findByIdIn(final List festivalIds) { - return festivalRepository.findByIdIn(festivalIds); + public List findAllByIdIn(final List festivalIds) { + return festivalRepository.findAllByIdIn(festivalIds); } } diff --git a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java index de1e4ef..9559c5b 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java @@ -3,7 +3,19 @@ import java.util.List; import org.sopt.confeti.domain.festival.Festival; 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 { - List findByIdIn(final List festivalIds); + + @Query(value = + "SELECT DISTINCT f" + + " FROM Festival f" + + " JOIN FETCH f.dates fd" + + " JOIN FETCH fd.stages fs" + + " JOIN FETCH fs.times ft" + + " JOIN FETCH ft.artists fa" + + " WHERE f.id IN :festivalIds" + ) + List findAllByIdIn(final @Param("festivalIds") List festivalIds); } diff --git a/src/main/java/org/sopt/confeti/domain/user/application/UserService.java b/src/main/java/org/sopt/confeti/domain/user/application/UserService.java index b1de383..38b9c67 100644 --- a/src/main/java/org/sopt/confeti/domain/user/application/UserService.java +++ b/src/main/java/org/sopt/confeti/domain/user/application/UserService.java @@ -27,4 +27,12 @@ public User findById(Long userId) { public boolean existsById(Long userId) { return userRepository.existsById(userId); } + + @Transactional(readOnly = true) + public User findUserTimetablesById(final long userId) { + return userRepository.findUserTimetablesById(userId) + .orElseThrow( + () -> new NotFoundException(ErrorMessage.NOT_FOUND) + ); + } } diff --git a/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java b/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java index c215450..2d282d0 100644 --- a/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java +++ b/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java @@ -1,7 +1,18 @@ package org.sopt.confeti.domain.user.infra.repository; +import java.util.Optional; import org.sopt.confeti.domain.user.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface UserRepository extends JpaRepository { + @Query(value = + "SELECT DISTINCT u" + + " FROM User u" + + " JOIN FETCH u.timetableFestivals tf" + + " JOIN FETCH tf.userTimetables ut" + + " WHERE u.id = :userId" + ) + Optional findUserTimetablesById(final @Param("userId") long userId); } From 4437a1ee8ff9c4cdffb1169cdfcb8349b68e9ecd Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 03:50:50 +0900 Subject: [PATCH 57/96] =?UTF-8?q?refactor=20[#119]=20=ED=8C=A8=EC=B9=98=20?= =?UTF-8?q?=EC=A1=B0=EC=9D=B8=20=EC=BF=BC=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserTimetableFacade.java | 2 -- .../user/infra/repository/UserRepository.java | 2 +- .../application/UserTimetableService.java | 28 ------------------- 3 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index c53995f..723f1fb 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -12,7 +12,6 @@ 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.domain.usertimetable.application.UserTimetableService; import org.sopt.confeti.global.exception.ConflictException; import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.exception.UnauthorizedException; @@ -29,7 +28,6 @@ public class UserTimetableFacade { private final UserService userService; private final TimetableFestivalService timetableFestivalService; - private final UserTimetableService userTimetableService; private final FestivalService festivalService; @Transactional(readOnly = true) diff --git a/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java b/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java index 2d282d0..9338ae2 100644 --- a/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java +++ b/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java @@ -8,7 +8,7 @@ public interface UserRepository extends JpaRepository { @Query(value = - "SELECT DISTINCT u" + + "SELECT u" + " FROM User u" + " JOIN FETCH u.timetableFestivals tf" + " JOIN FETCH tf.userTimetables ut" + diff --git a/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java b/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java deleted file mode 100644 index eb82542..0000000 --- a/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.sopt.confeti.domain.usertimetable.application; - -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.sopt.confeti.domain.festivaltime.FestivalTime; -import org.sopt.confeti.domain.user.User; -import org.sopt.confeti.domain.usertimetable.UserTimetable; -import org.sopt.confeti.domain.usertimetable.infra.repository.UserTimetableRepository; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -public class UserTimetableService { - - private static final boolean DEFAULT_IS_SELECTED = false; - - private final UserTimetableRepository userTimetableRepository; - - @Transactional - public void addUserTimetables(final User user, final List festivalTimes) { - userTimetableRepository.saveAll( - festivalTimes.stream() - .map(festivalTime -> UserTimetable.create(user, festivalTime, DEFAULT_IS_SELECTED)) - .toList() - ); - } -} From 82affdf91f0bf8258c36ce1cd6bff6c2a60cc8cc Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 04:54:37 +0900 Subject: [PATCH 58/96] =?UTF-8?q?fix=20[#119]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=ED=8E=98=EC=8A=A4?= =?UTF-8?q?=ED=8B=B0=EB=B2=8C=20=EC=B6=94=EA=B0=80/=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=8B=9C=20=EC=84=A0=ED=83=9D=20=EC=97=AC=EB=B6=80=20=EA=B0=92?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80/=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20ERD=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/api/user/facade/UserTimetableFacade.java | 2 +- .../domain/festival/application/FestivalService.java | 4 ++-- .../infra/repository/FestivalRepository.java | 12 +----------- .../domain/user/infra/repository/UserRepository.java | 5 ++--- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 723f1fb..3412aa8 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -50,7 +50,7 @@ public void removeTimetableFestival(final long userId, final long festivalId) { @Transactional public void addTimetableFestivals(final long userId, final AddTimetableFestivalDTO from) { User user = userService.findUserTimetablesById(userId); - List addFestivals = festivalService.findAllByIdIn( + List addFestivals = festivalService.findFestivalsByIdIn( from.festivals().stream() .distinct() .map(AddTimetableFestivalArtiestDTO::festivalId) diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index ef42c51..77e8bed 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -47,7 +47,7 @@ public boolean existsById(final long festivalId) { } @Transactional - public List findAllByIdIn(final List festivalIds) { - return festivalRepository.findAllByIdIn(festivalIds); + public List findFestivalsByIdIn(final List festivalIds) { + return festivalRepository.findFestivalsByIdIn(festivalIds); } } diff --git a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java index 9559c5b..228b8ff 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java @@ -7,15 +7,5 @@ import org.springframework.data.repository.query.Param; public interface FestivalRepository extends JpaRepository { - - @Query(value = - "SELECT DISTINCT f" + - " FROM Festival f" + - " JOIN FETCH f.dates fd" + - " JOIN FETCH fd.stages fs" + - " JOIN FETCH fs.times ft" + - " JOIN FETCH ft.artists fa" + - " WHERE f.id IN :festivalIds" - ) - List findAllByIdIn(final @Param("festivalIds") List festivalIds); + List findFestivalsByIdIn(final @Param("festivalIds") List festivalIds); } diff --git a/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java b/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java index 9338ae2..4e88299 100644 --- a/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java +++ b/src/main/java/org/sopt/confeti/domain/user/infra/repository/UserRepository.java @@ -8,10 +8,9 @@ public interface UserRepository extends JpaRepository { @Query(value = - "SELECT u" + + "SELECT DISTINCT u" + " FROM User u" + - " JOIN FETCH u.timetableFestivals tf" + - " JOIN FETCH tf.userTimetables ut" + + " LEFT JOIN FETCH u.timetableFestivals tf" + " WHERE u.id = :userId" ) Optional findUserTimetablesById(final @Param("userId") long userId); From b27f38fbc1eec2d45a5396e121bece30afab2346 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 16:25:38 +0900 Subject: [PATCH 59/96] =?UTF-8?q?fix=20[#127]=20SpotifyAPIHandler=20?= =?UTF-8?q?=EB=A6=AC=ED=94=84=EB=A0=88=EC=8B=9C=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EB=A1=9C=EC=A7=81=20=EC=9E=AC?= =?UTF-8?q?=EA=B5=AC=EC=84=B1=20=EB=B0=8F=20=EB=A7=88=EC=A7=80=EB=A7=89=20?= =?UTF-8?q?=EB=B0=9C=EB=A7=A4=20=EC=9D=BC=EC=9E=90=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=A4=EB=8A=94=20=EB=B0=A9=EB=B2=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artistsearcher/SpotifyAPIHandler.java | 196 ++++++++++-------- 1 file changed, 108 insertions(+), 88 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java index 09198e8..48c6b0f 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java @@ -1,7 +1,6 @@ package org.sopt.confeti.global.util.artistsearcher; import com.neovisionaries.i18n.CountryCode; -import java.io.IOException; import java.time.LocalDate; import java.util.Arrays; import java.util.Collections; @@ -9,9 +8,9 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.Callable; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; -import org.apache.hc.core5.http.ParseException; import org.sopt.confeti.annotation.Handler; import org.sopt.confeti.global.exception.ConfetiException; import org.sopt.confeti.global.message.ErrorMessage; @@ -22,13 +21,10 @@ import se.michaelthelin.spotify.exceptions.detailed.BadRequestException; import se.michaelthelin.spotify.exceptions.detailed.NotFoundException; import se.michaelthelin.spotify.exceptions.detailed.UnauthorizedException; -import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials; import se.michaelthelin.spotify.model_objects.credentials.ClientCredentials; import se.michaelthelin.spotify.model_objects.specification.AlbumSimplified; import se.michaelthelin.spotify.model_objects.specification.Artist; import se.michaelthelin.spotify.model_objects.specification.Paging; -import se.michaelthelin.spotify.requests.authorization.authorization_code.AuthorizationCodeRefreshRequest; -import se.michaelthelin.spotify.requests.authorization.client_credentials.ClientCredentialsRequest; @Handler @RequiredArgsConstructor(access = AccessLevel.PROTECTED) @@ -36,8 +32,12 @@ public class SpotifyAPIHandler { private static final int ARTIST_LIMIT = 1; private static final int ARTIST_OFFSET = 0; - private static final int ALBUM_LIMIT = 10; + private static final int ALBUM_LIMIT = 1; private static final int ALBUM_OFFSET = 0; + private static final int REFRESH_TRIAL = 5; + private static final int REFRESH_INIT_VALUE = 0; + + private static final String TOKEN_EXPIRED_MESSAGE = "The access token expired"; @Value("${spotify.credentials.client-id}") private String clientId; @@ -47,9 +47,13 @@ public class SpotifyAPIHandler { private SpotifyApi spotifyApi; + private int refreshCount; + public void init() { createSpotifyApi(); generateAccessToken(); + + refreshCount = REFRESH_INIT_VALUE; } public Optional findArtistsByKeyword(final String keyword) { @@ -62,7 +66,7 @@ public Optional findArtistsByKeyword(final String keyword) { Artist artist = searchedArtist.get(); return Optional.of( - ConfetiArtist.toConfetiArtist(artist, findLatestReleaseAt(keyword, artist)) + ConfetiArtist.toConfetiArtist(artist, findLatestReleaseAt(artist.getId())) ); } @@ -72,20 +76,23 @@ public Optional findArtistByArtistId(final String artistId) { } try { - Artist artist = spotifyApi.getArtist(artistId) - .build() - .execute(); - - return Optional.of( - ConfetiArtist.toConfetiArtist(artist) - ); - } catch (UnauthorizedException e) { - refreshToken(); - return findArtistByArtistId(artistId); - } catch (NotFoundException | BadRequestException e) { + generateAccessTokenIfExpired(); + + return executeWithTokenRefresh(() -> { + Artist artist = spotifyApi.getArtist(artistId) + .build() + .execute(); + + return Optional.of( + ConfetiArtist.toConfetiArtist(artist) + ); + }); + } catch (NotFoundException e) { return Optional.empty(); - } catch (IOException | ParseException | SpotifyWebApiException e) { - throw new RuntimeException(e); + } catch (BadRequestException e) { + throw new ConfetiException(ErrorMessage.BAD_REQUEST); + } catch (Exception e) { + throw new ConfetiException(ErrorMessage.INTERNAL_SERVER_ERROR); } } @@ -95,81 +102,79 @@ public List findArtistsByArtistIds(final List artistIds) } try { - Artist[] artists = spotifyApi.getSeveralArtists( - artistIds.stream() - .filter(Objects::nonNull) - .toArray(String[]::new) - ) - .build() - .execute(); - - return Arrays.stream(artists) - .filter(Objects::nonNull) - .map(ConfetiArtist::toConfetiArtist) - .toList(); - } catch (UnauthorizedException e) { - refreshToken(); - return findArtistsByArtistIds(artistIds); + generateAccessTokenIfExpired(); + + return executeWithTokenRefresh(() -> { + Artist[] artists = spotifyApi.getSeveralArtists( + artistIds.stream() + .filter(Objects::nonNull) + .toArray(String[]::new) + ) + .build() + .execute(); + + return Arrays.stream(artists) + .filter(Objects::nonNull) + .map(ConfetiArtist::toConfetiArtist) + .toList(); + }); } catch (BadRequestException e) { throw new ConfetiException(ErrorMessage.BAD_REQUEST); - } catch (IOException | ParseException | SpotifyWebApiException e) { - throw new RuntimeException(e); + } catch (Exception e) { + throw new ConfetiException(ErrorMessage.INTERNAL_SERVER_ERROR); } } private Optional searchArtistByKeyword(final String keyword) { try { - Paging artists = spotifyApi.searchArtists(keyword) - .market(CountryCode.KR) - .limit(ARTIST_LIMIT) - .offset(ARTIST_OFFSET) - .build() - .execute(); - - return Arrays.stream(artists.getItems()) - .findFirst(); - } catch (UnauthorizedException e) { - refreshToken(); - return searchArtistByKeyword(keyword); - } catch (IOException | ParseException | SpotifyWebApiException e) { - throw new ConfetiException(ErrorMessage.BAD_REQUEST); + generateAccessTokenIfExpired(); + + return executeWithTokenRefresh(() -> { + Paging artists = spotifyApi.searchArtists(keyword) + .market(CountryCode.KR) + .limit(ARTIST_LIMIT) + .offset(ARTIST_OFFSET) + .build() + .execute(); + + return Arrays.stream(artists.getItems()) + .findFirst(); + }); + } catch (Exception e) { + throw new ConfetiException(ErrorMessage.INTERNAL_SERVER_ERROR); } } - private LocalDate findLatestReleaseAt(final String keyword, final Artist artist) { - Optional> albums = searchAlbumByKeyword(keyword); - - return albums.map(albumSimplifiedPaging -> Arrays.stream(albumSimplifiedPaging.getItems()) - .filter((searchedAlbum) -> - Arrays.stream(searchedAlbum.getArtists()) - .anyMatch((searchedArtist) -> - searchedArtist.getId().equals(artist.getId()) - ) - ) - .max(Comparator.comparing(AlbumSimplified::getReleaseDate)) - .map( - albumSimplified -> DateConvertor.convertToSpotifyLocalDate(albumSimplified.getReleaseDate()) - ) - .get()) - .orElse(null); + private LocalDate findLatestReleaseAt(final String artistId) { + Optional> searchedAlbums = searchAlbumByArtistId(artistId); + + if (searchedAlbums.isEmpty()) { + return null; + } + + Paging albums = searchedAlbums.get(); + return Arrays.stream(albums.getItems()) + .map(albumItem -> DateConvertor.convertToSpotifyLocalDate(albumItem.getReleaseDate())) + .findFirst() + .orElseGet(null); } - private Optional> searchAlbumByKeyword(final String keyword) { + private Optional> searchAlbumByArtistId(final String artistId) { try { - return Optional.of(spotifyApi.searchAlbums(keyword) + generateAccessTokenIfExpired(); + + return executeWithTokenRefresh(() -> + Optional.of(spotifyApi.getArtistsAlbums(artistId) .market(CountryCode.KR) .limit(ALBUM_LIMIT) .offset(ALBUM_OFFSET) .build() - .execute()); - } catch (UnauthorizedException e) { - refreshToken(); - return searchAlbumByKeyword(keyword); + .execute())); } catch (NotFoundException | BadRequestException e) { return Optional.empty(); - } catch (IOException | ParseException | SpotifyWebApiException e) { - throw new RuntimeException(e); + } catch (Exception e) { + throw new ConfetiException(ErrorMessage.INTERNAL_SERVER_ERROR); } } @@ -181,27 +186,42 @@ private void createSpotifyApi() { } private void generateAccessToken() { - ClientCredentialsRequest clientCredentialsRequest = spotifyApi.clientCredentials().build(); - try { - final ClientCredentials clientCredentials = clientCredentialsRequest.execute(); + final ClientCredentials clientCredentials = spotifyApi.clientCredentials() + .build() + .execute(); spotifyApi.setAccessToken(clientCredentials.getAccessToken()); - } catch (IOException | ParseException | SpotifyWebApiException e) { - throw new RuntimeException(e); + refreshCount = REFRESH_INIT_VALUE; + + } catch (Exception e) { + throw new ConfetiException(ErrorMessage.INTERNAL_SERVER_ERROR); } } - public void refreshToken() { - try { - AuthorizationCodeRefreshRequest refreshRequest = spotifyApi.authorizationCodeRefresh().build(); - AuthorizationCodeCredentials credentials = refreshRequest.execute(); - - spotifyApi.setAccessToken(credentials.getAccessToken()); + private void generateAccessTokenIfExpired() { + if (spotifyApi.getAccessToken() == null) { + generateAccessToken(); + } + } - } catch (IOException | SpotifyWebApiException | ParseException e) { - throw new ConfetiException(ErrorMessage.INTERNAL_SERVER_ERROR); + // 토큰 재발급을 위한 실행 메서드 + private T executeWithTokenRefresh(Callable action) throws Exception { + try { + T retVal = action.call(); + refreshCount = REFRESH_INIT_VALUE; + return retVal; + } catch (UnauthorizedException e) { + if (isTokenExpired(e) && refreshCount < REFRESH_TRIAL) { + generateAccessToken(); + return action.call(); // 새 토큰으로 재시도 + } + throw e; } } + // 토큰 만료 여부 확인 + private boolean isTokenExpired(SpotifyWebApiException e) { + return e instanceof UnauthorizedException; + } } From 7027d80a306915f645bac5cdd9c8f4e380aea70b Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 16:42:51 +0900 Subject: [PATCH 60/96] =?UTF-8?q?style=20[#127]=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/global/util/artistsearcher/SpotifyAPIHandler.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java index 48c6b0f..243ab87 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java @@ -37,8 +37,6 @@ public class SpotifyAPIHandler { private static final int REFRESH_TRIAL = 5; private static final int REFRESH_INIT_VALUE = 0; - private static final String TOKEN_EXPIRED_MESSAGE = "The access token expired"; - @Value("${spotify.credentials.client-id}") private String clientId; From 3b91cccb6e36ac9284a490fac8326feaf230d37b Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Tue, 21 Jan 2025 08:27:52 +0900 Subject: [PATCH 61/96] =?UTF-8?q?feat=20[#41]=20PerformanceController,=20R?= =?UTF-8?q?esponse=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PerformanceController.java | 9 +++++++ .../PerformanceReservationDetailResponse.java | 25 +++++++++++++++++++ .../PerformanceReservationResponse.java | 21 ++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationDetailResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationResponse.java diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index 79bdf0d..f6d15af 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -9,6 +9,8 @@ import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; +import org.sopt.confeti.api.performance.dto.response.PerformanceReservationResponse; +import org.sopt.confeti.api.performance.facade.dto.response.PerformanceReservationDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; import org.sopt.confeti.global.util.ApiResponseUtil; @@ -54,4 +56,11 @@ public ResponseEntity> createConcert(@RequestBody CreateFestival return ApiResponseUtil.success(SuccessMessage.SUCCESS); } + + @GetMapping("/reservation") + public ResponseEntity> getPerformReservationInfo(@RequestHeader(name = "Authorization", required = false) Long userId + ) { + PerformanceReservationDTO performanceReservationDTO = performanceFacade.getPerformReservationInfo(userId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, PerformanceReservationResponse.of(performanceReservationDTO, s3FileHandler)); + } } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationDetailResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationDetailResponse.java new file mode 100644 index 0000000..dfde279 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationDetailResponse.java @@ -0,0 +1,25 @@ +package org.sopt.confeti.api.performance.dto.response; + +import org.sopt.confeti.api.performance.facade.dto.response.PerformanceReservationDetailDTO; +import org.sopt.confeti.global.common.constant.PerformanceType; +import org.sopt.confeti.global.util.S3FileHandler; + +public record PerformanceReservationDetailResponse( + long index, + long performanceId, + PerformanceType type, + String subtitle, + String reserveAt, + String reservationBgUrl +){ + public static PerformanceReservationDetailResponse of(PerformanceReservationDetailDTO performanceReservation, S3FileHandler s3FileHandler) { + return new PerformanceReservationDetailResponse( + performanceReservation.index(), + performanceReservation.performanceId(), + performanceReservation.type(), + performanceReservation.subtitle(), + performanceReservation.reserveAt(), + s3FileHandler.getFileUrl(performanceReservation.reservationBgUrl()) + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationResponse.java new file mode 100644 index 0000000..6531f7f --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationResponse.java @@ -0,0 +1,21 @@ +package org.sopt.confeti.api.performance.dto.response; + +import org.sopt.confeti.api.performance.facade.dto.response.PerformanceReservationDTO; +import org.sopt.confeti.global.util.S3FileHandler; + +import java.util.List; + +public record PerformanceReservationResponse( + Integer performanceCount, + List performances +){ + public static PerformanceReservationResponse of(final PerformanceReservationDTO performanceReservation, + final S3FileHandler s3FileHandler) { + return new PerformanceReservationResponse( + performanceReservation.performanceReservation().size(), + performanceReservation.performanceReservation().stream() + .map(performanceReserve -> PerformanceReservationDetailResponse.of(performanceReserve, s3FileHandler)) + .toList() + ); + } +} From 4e3278001c8d412df1d531ad4e68723d2c239353 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Tue, 21 Jan 2025 08:28:01 +0900 Subject: [PATCH 62/96] =?UTF-8?q?feat=20[#41]=20PerformanceFacade=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/performance/facade/PerformanceFacade.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index ff8ef5a..eb784b3 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -1,16 +1,22 @@ package org.sopt.confeti.api.performance.facade; import java.time.LocalDate; +import java.util.List; + import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; +import org.sopt.confeti.api.performance.facade.dto.response.PerformanceReservationDTO; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.application.ConcertService; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; +import org.sopt.confeti.domain.user.application.UserService; +import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; +import org.sopt.confeti.domain.view.performance.application.PerformanceService; import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.message.ErrorMessage; import org.sopt.confeti.global.util.S3FileHandler; @@ -22,8 +28,10 @@ public class PerformanceFacade { private final ConcertService concertService; private final FestivalService festivalService; + private final UserService userService; private final FestivalFavoriteService festivalFavoriteService; private final S3FileHandler s3FileHandler; + private final PerformanceService performanceService; @Transactional(readOnly = true) public ConcertDetailDTO getConcertDetailInfo(final long concertId) { @@ -70,4 +78,10 @@ protected void validateFestivalNotPassed(final Festival festival) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } } + + @Transactional(readOnly = true) + public PerformanceReservationDTO getPerformReservationInfo(final Long userId){ + List performanceReserve=performanceService.getFavoritePerformancesReservation(userId); + return PerformanceReservationDTO.from(performanceReserve); + } } From 548008f8fe67c75c1872dcc7ea5572fac7ba7dcb Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Tue, 21 Jan 2025 08:28:10 +0900 Subject: [PATCH 63/96] =?UTF-8?q?feat=20[#41]=20PerformanceDTO=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/PerformanceReservationDTO.java | 18 +++++++++++ .../PerformanceReservationDetailDTO.java | 24 +++++++++++++++ .../performance/PerformanceTicketDTO.java | 30 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDetailDTO.java create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/PerformanceTicketDTO.java diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDTO.java new file mode 100644 index 0000000..92575a1 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDTO.java @@ -0,0 +1,18 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; + +import java.util.List; + +public record PerformanceReservationDTO( + List performanceReservation +) { + public static PerformanceReservationDTO from (List performanceTickets){ + return new PerformanceReservationDTO( + performanceTickets.stream() + .map(PerformanceReservationDetailDTO::from) + .toList() + ); + } + +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDetailDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDetailDTO.java new file mode 100644 index 0000000..d577b58 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDetailDTO.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; +import org.sopt.confeti.global.common.constant.PerformanceType; + +public record PerformanceReservationDetailDTO( + long index, + long performanceId, + PerformanceType type, + String subtitle, + String reserveAt, + String reservationBgUrl +) { + public static PerformanceReservationDetailDTO from(PerformanceTicketDTO performanceTicketDTO) { + return new PerformanceReservationDetailDTO( + performanceTicketDTO.index(), + performanceTicketDTO.performanceId(), + performanceTicketDTO.type(), + performanceTicketDTO.subtitle(), + performanceTicketDTO.reserveAt(), + performanceTicketDTO.reservationBgUrl() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceTicketDTO.java b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceTicketDTO.java new file mode 100644 index 0000000..7972505 --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceTicketDTO.java @@ -0,0 +1,30 @@ +package org.sopt.confeti.domain.view.performance; + +import org.sopt.confeti.global.common.constant.PerformanceType; + +public record PerformanceTicketDTO( + long index, + long performanceId, + PerformanceType type, + String subtitle, + String reserveAt, + String reservationBgUrl +) { + public static PerformanceTicketDTO of( + final long index, + final long performanceId, + final String type, + final String subtitle, + final String reserveAt, + final String reservationBgUrl + ) { + return new PerformanceTicketDTO( + index, + performanceId, + PerformanceType.convert(type), + subtitle, + reserveAt, + reservationBgUrl + ); + } +} \ No newline at end of file From b9e29c23a12a270a9d6ab95aacc5c28fd9a90f4c Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Tue, 21 Jan 2025 08:28:16 +0900 Subject: [PATCH 64/96] =?UTF-8?q?feat=20[#41]=20PerformanceService,=20Repo?= =?UTF-8?q?sitory=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/PerformanceService.java | 8 +- .../repository/PerformanceRepository.java | 118 ++++++++++++++++-- 2 files changed, 115 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java index e64904e..5b59feb 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java @@ -3,6 +3,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.sopt.confeti.domain.view.performance.PerformanceDTO; +import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; import org.sopt.confeti.domain.view.performance.infra.repository.PerformanceRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,4 +18,9 @@ public class PerformanceService { public List getFavoritePerformancesPreview(final long userId) { return performanceRepository.findFavoritePerformancesPreview(userId); } -} + + @Transactional(readOnly = true) + public List getFavoritePerformancesReservation(final Long userId) { + return performanceRepository.findFavoritePerformancesReservation(userId); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java index d81de8c..5de28fd 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java @@ -2,9 +2,12 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; + +import java.sql.Timestamp; import java.util.List; import lombok.RequiredArgsConstructor; import org.sopt.confeti.domain.view.performance.PerformanceDTO; +import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; import org.sopt.confeti.global.common.constant.PerformanceType; import org.springframework.stereotype.Repository; @@ -15,18 +18,19 @@ public class PerformanceRepository { private final EntityManager em; int PREVIEW_FAVORITE_PERFORMANCE_COUNT = 3; + int RESERVE_FAVORITE_PERFORMANCE_COUNT = 5; - public List findFavoritePerformancesPreview(final long userId) { + public List findFavoritePerformancesPreview(final Long userId) { String sql = "SELECT c.concert_id performanceId, :concertType type, c.concert_title title, c.concert_poster_path posterPath, c.concert_start_at startAt" + - " FROM concert_favorites cf INNER JOIN concerts c ON cf.concert_id = c.concert_id" + - " WHERE cf.user_id = :userId AND c.concert_end_at >= CURRENT_DATE" + - " UNION" + - " SELECT f.festival_id performanceId, :festivalType type, f.festival_title title, f.festival_poster_path posterPath, f.festival_start_at startAt" + - " FROM festival_favorites ff INNER JOIN festivals f ON ff.festival_id = f.festival_id" + - " WHERE ff.user_id = :userId AND f.festival_end_at >= CURRENT_DATE" + - " ORDER BY startAt ASC" + - " LIMIT :performancePreviewCount"; + " FROM concert_favorites cf INNER JOIN concerts c ON cf.concert_id = c.concert_id" + + " WHERE cf.user_id = :userId AND c.concert_end_at >= CURRENT_DATE" + + " UNION" + + " SELECT f.festival_id performanceId, :festivalType type, f.festival_title title, f.festival_poster_path posterPath, f.festival_start_at startAt" + + " FROM festival_favorites ff INNER JOIN festivals f ON ff.festival_id = f.festival_id" + + " WHERE ff.user_id = :userId AND f.festival_end_at >= CURRENT_DATE" + + " ORDER BY startAt ASC" + + " LIMIT :performancePreviewCount"; Query query = em.createNativeQuery(sql); query.setParameter("userId", userId); @@ -47,4 +51,98 @@ private List convertToPerformanceDTOs(final List resul )) .toList(); } -} + + public List findFavoritePerformancesReservation(final Long userId) { + String sql; + boolean isUserIdValid = userId != null && userId > 0; + + if (!isUserIdValid) { + sql = getAllPerformancesQuery(); + } else { + boolean hasFavorites = checkUserHasFavorites(userId); + sql = hasFavorites ? getFavoritePerformancesQuery() : getAllPerformancesQuery(); + } + + Query query = em.createNativeQuery(sql) + .setParameter("concertType", PerformanceType.CONCERT.getType()) + .setParameter("festivalType", PerformanceType.FESTIVAL.getType()) + .setParameter("performanceReservationCount", RESERVE_FAVORITE_PERFORMANCE_COUNT); + + if (isUserIdValid && sql.contains(":userId")) { + query.setParameter("userId", userId); + } + + List results = query.getResultList(); + return convertToPerformanceTicketDTOs(results); + } + + private boolean checkUserHasFavorites(Long userId) { + String checkFavoritesQuery = """ + SELECT COUNT(*) > 0 + FROM ( + SELECT 1 FROM concert_favorites cf + JOIN concerts c ON cf.concert_id = c.concert_id + WHERE cf.user_id = ?1 AND c.reserve_at >= CURRENT_DATE + UNION ALL + SELECT 1 FROM festival_favorites ff + JOIN festivals f ON ff.festival_id = f.festival_id + WHERE ff.user_id = ?1 AND f.reserve_at >= CURRENT_DATE + LIMIT 1 + ) AS favorites + """; + + Query checkQuery = em.createNativeQuery(checkFavoritesQuery) + .setParameter(1, userId); + + return ((Number) checkQuery.getSingleResult()).intValue() > 0; + } + + private String getAllPerformancesQuery() { + return """ + SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url + FROM ( + SELECT c.concert_id AS performance_id, :concertType AS type, c.concert_subtitle AS subtitle, c.reserve_at, c.concert_reservation_bg_path AS reservation_bg_url + FROM concerts c + WHERE c.reserve_at >= CURRENT_DATE + UNION ALL + SELECT f.festival_id, :festivalType, f.festival_subtitle, f.reserve_at, f.festival_reservation_bg_path + FROM festivals f + WHERE f.reserve_at >= CURRENT_DATE + ) AS all_performances + ORDER BY reserve_at ASC + LIMIT :performanceReservationCount + """; + } + + private String getFavoritePerformancesQuery() { + return """ + SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url + FROM ( + SELECT c.concert_id AS performance_id, :concertType AS type, c.concert_subtitle AS subtitle, c.reserve_at, c.concert_reservation_bg_path AS reservation_bg_url + FROM concert_favorites cf + JOIN concerts c ON cf.concert_id = c.concert_id + WHERE cf.user_id = :userId AND c.reserve_at >= CURRENT_DATE + UNION ALL + SELECT f.festival_id, :festivalType, f.festival_subtitle, f.reserve_at, f.festival_reservation_bg_path + FROM festival_favorites ff + JOIN festivals f ON ff.festival_id = f.festival_id + WHERE ff.user_id = :userId AND f.reserve_at >= CURRENT_DATE + ) AS favorite_performances + ORDER BY reserve_at ASC + LIMIT :performanceReservationCount + """; + } + + private List convertToPerformanceTicketDTOs(List results) { + return results.stream() + .map(row -> PerformanceTicketDTO.of( + ((Number) row[0]).longValue(), + ((Number) row[1]).longValue(), + (String) row[2], + (String) row[3], + row[4] != null ? ((Timestamp) row[4]).toString() : null, + (String) row[5] + )) + .toList(); + } +} \ No newline at end of file From e25993a87bdd3fcf9607e3c1d1703a0839a6231a Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Tue, 21 Jan 2025 18:22:38 +0900 Subject: [PATCH 65/96] =?UTF-8?q?refactor=20[#41]=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../performance/facade/PerformanceFacade.java | 13 ++- .../application/ConcertFavoriteService.java | 5 ++ .../repository/ConcertFavoriteRepository.java | 1 + .../application/FestivalFavoriteService.java | 4 + .../FestivalFavoriteRepository.java | 1 + .../application/PerformanceService.java | 5 ++ .../repository/PerformanceRepository.java | 88 +++++++------------ 7 files changed, 58 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index eb784b3..6a827fd 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -11,6 +11,7 @@ import org.sopt.confeti.api.performance.facade.dto.response.PerformanceReservationDTO; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.application.ConcertService; +import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; @@ -32,6 +33,7 @@ public class PerformanceFacade { private final FestivalFavoriteService festivalFavoriteService; private final S3FileHandler s3FileHandler; private final PerformanceService performanceService; + private final ConcertFavoriteService concertFavoriteService; @Transactional(readOnly = true) public ConcertDetailDTO getConcertDetailInfo(final long concertId) { @@ -81,7 +83,16 @@ protected void validateFestivalNotPassed(final Festival festival) { @Transactional(readOnly = true) public PerformanceReservationDTO getPerformReservationInfo(final Long userId){ - List performanceReserve=performanceService.getFavoritePerformancesReservation(userId); + boolean isUserExist = userId != null && userService.existsById(userId); + boolean isCFExist = isUserExist && concertFavoriteService.existsByUserId(userId); + boolean isFFExist = isUserExist && festivalFavoriteService.existsByUserId(userId); + + if (isCFExist || isFFExist) { + List performanceReserve=performanceService.getFavoritePerformancesReservation(userId); + return PerformanceReservationDTO.from(performanceReserve); + } + + List performanceReserve=performanceService.getPerformancesReservation(); return PerformanceReservationDTO.from(performanceReserve); } } diff --git a/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java b/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java index a2bc3b2..5c37bde 100644 --- a/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/concertfavorite/application/ConcertFavoriteService.java @@ -30,4 +30,9 @@ public void addFavorite(final User user, final Concert concert) { public void removeFavorite(final long userId, final long concertId) { concertFavoriteRepository.deleteByUserIdAndConcertId(userId, concertId); } + + @Transactional + public boolean existsByUserId(final Long userId){ + return concertFavoriteRepository.existsByUserId(userId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java index fe68c07..d484a4c 100644 --- a/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/concertfavorite/infra/repository/ConcertFavoriteRepository.java @@ -6,4 +6,5 @@ public interface ConcertFavoriteRepository extends JpaRepository { boolean existsByUserIdAndConcertId(final long userId, final long concertId); void deleteByUserIdAndConcertId(final long userId, final long concertId); + boolean existsByUserId(final Long userId); } diff --git a/src/main/java/org/sopt/confeti/domain/festivalfavorite/application/FestivalFavoriteService.java b/src/main/java/org/sopt/confeti/domain/festivalfavorite/application/FestivalFavoriteService.java index 157c8f7..66900ed 100644 --- a/src/main/java/org/sopt/confeti/domain/festivalfavorite/application/FestivalFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/festivalfavorite/application/FestivalFavoriteService.java @@ -35,4 +35,8 @@ public void delete(User user, Festival festival) { public boolean isFavorite(final long userId, final long festivalId) { return festivalFavoriteRepository.existsByUserIdAndFestivalId(userId, festivalId); } + + public boolean existsByUserId(final Long userId) { + return festivalFavoriteRepository.existsByUserId(userId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festivalfavorite/infra/repository/FestivalFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/festivalfavorite/infra/repository/FestivalFavoriteRepository.java index aa738d4..d442612 100644 --- a/src/main/java/org/sopt/confeti/domain/festivalfavorite/infra/repository/FestivalFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festivalfavorite/infra/repository/FestivalFavoriteRepository.java @@ -11,4 +11,5 @@ public interface FestivalFavoriteRepository extends JpaRepository findByUserIdAndFestivalId(long userId, long festivalId); boolean existsByUserIdAndFestivalId(long userId, long festivalId); + boolean existsByUserId(Long userId); } diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java index 5b59feb..0147105 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java @@ -23,4 +23,9 @@ public List getFavoritePerformancesPreview(final long userId) { public List getFavoritePerformancesReservation(final Long userId) { return performanceRepository.findFavoritePerformancesReservation(userId); } + + @Transactional(readOnly = true) + public List getPerformancesReservation() { + return performanceRepository.findPerformancesReservation(); + } } \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java index 5de28fd..89f59fb 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java @@ -17,8 +17,8 @@ public class PerformanceRepository { private final EntityManager em; - int PREVIEW_FAVORITE_PERFORMANCE_COUNT = 3; - int RESERVE_FAVORITE_PERFORMANCE_COUNT = 5; + private static final int PREVIEW_FAVORITE_PERFORMANCE_COUNT = 3; + private static final int RESERVE_FAVORITE_PERFORMANCE_COUNT = 5; public List findFavoritePerformancesPreview(final Long userId) { String sql = @@ -53,84 +53,56 @@ private List convertToPerformanceDTOs(final List resul } public List findFavoritePerformancesReservation(final Long userId) { - String sql; - boolean isUserIdValid = userId != null && userId > 0; - - if (!isUserIdValid) { - sql = getAllPerformancesQuery(); - } else { - boolean hasFavorites = checkUserHasFavorites(userId); - sql = hasFavorites ? getFavoritePerformancesQuery() : getAllPerformancesQuery(); - } + String sql = """ + SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url + FROM ( + SELECT c.concert_id performance_id, :concertType type, c.concert_subtitle subtitle, c.reserve_at, c.concert_reservation_bg_path reservation_bg_url + FROM concert_favorites cf + JOIN concerts c ON cf.concert_id = c.concert_id + WHERE cf.user_id = :userId AND c.reserve_at >= CURRENT_DATE + UNION ALL + SELECT f.festival_id performance_id, :festivalType type, f.festival_subtitle subtitle, f.reserve_at, f.festival_reservation_bg_path reservation_bg_url + FROM festival_favorites ff + JOIN festivals f ON ff.festival_id = f.festival_id + WHERE ff.user_id = :userId AND f.reserve_at >= CURRENT_DATE + ) AS favorite_performances + ORDER BY reserve_at ASC + LIMIT :performanceReservationCount + """;; Query query = em.createNativeQuery(sql) + .setParameter("userId", userId) .setParameter("concertType", PerformanceType.CONCERT.getType()) .setParameter("festivalType", PerformanceType.FESTIVAL.getType()) .setParameter("performanceReservationCount", RESERVE_FAVORITE_PERFORMANCE_COUNT); - if (isUserIdValid && sql.contains(":userId")) { - query.setParameter("userId", userId); - } - List results = query.getResultList(); return convertToPerformanceTicketDTOs(results); } - private boolean checkUserHasFavorites(Long userId) { - String checkFavoritesQuery = """ - SELECT COUNT(*) > 0 - FROM ( - SELECT 1 FROM concert_favorites cf - JOIN concerts c ON cf.concert_id = c.concert_id - WHERE cf.user_id = ?1 AND c.reserve_at >= CURRENT_DATE - UNION ALL - SELECT 1 FROM festival_favorites ff - JOIN festivals f ON ff.festival_id = f.festival_id - WHERE ff.user_id = ?1 AND f.reserve_at >= CURRENT_DATE - LIMIT 1 - ) AS favorites - """; - - Query checkQuery = em.createNativeQuery(checkFavoritesQuery) - .setParameter(1, userId); - - return ((Number) checkQuery.getSingleResult()).intValue() > 0; - } - - private String getAllPerformancesQuery() { - return """ + public List findPerformancesReservation() { + String sql = """ SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url FROM ( - SELECT c.concert_id AS performance_id, :concertType AS type, c.concert_subtitle AS subtitle, c.reserve_at, c.concert_reservation_bg_path AS reservation_bg_url + SELECT c.concert_id performance_id, :concertType type, c.concert_subtitle subtitle, c.reserve_at, c.concert_reservation_bg_path reservation_bg_url FROM concerts c WHERE c.reserve_at >= CURRENT_DATE UNION ALL - SELECT f.festival_id, :festivalType, f.festival_subtitle, f.reserve_at, f.festival_reservation_bg_path + SELECT f.festival_id performance_id, :festivalType type, f.festival_subtitle subtitle, f.reserve_at, f.festival_reservation_bg_path reservation_bg_url FROM festivals f WHERE f.reserve_at >= CURRENT_DATE ) AS all_performances ORDER BY reserve_at ASC LIMIT :performanceReservationCount """; - } - private String getFavoritePerformancesQuery() { - return """ - SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url - FROM ( - SELECT c.concert_id AS performance_id, :concertType AS type, c.concert_subtitle AS subtitle, c.reserve_at, c.concert_reservation_bg_path AS reservation_bg_url - FROM concert_favorites cf - JOIN concerts c ON cf.concert_id = c.concert_id - WHERE cf.user_id = :userId AND c.reserve_at >= CURRENT_DATE - UNION ALL - SELECT f.festival_id, :festivalType, f.festival_subtitle, f.reserve_at, f.festival_reservation_bg_path - FROM festival_favorites ff - JOIN festivals f ON ff.festival_id = f.festival_id - WHERE ff.user_id = :userId AND f.reserve_at >= CURRENT_DATE - ) AS favorite_performances - ORDER BY reserve_at ASC - LIMIT :performanceReservationCount - """; + Query query = em.createNativeQuery(sql) + .setParameter("concertType", PerformanceType.CONCERT.getType()) + .setParameter("festivalType", PerformanceType.FESTIVAL.getType()) + .setParameter("performanceReservationCount", RESERVE_FAVORITE_PERFORMANCE_COUNT); + + List results = query.getResultList(); + return convertToPerformanceTicketDTOs(results); } private List convertToPerformanceTicketDTOs(List results) { From 651a5196eda546fe5ad7d6a9bc9aec0e35d66a62 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 17:01:48 +0900 Subject: [PATCH 66/96] =?UTF-8?q?feat=20[#126]=20=EC=BB=A4=EC=84=9C=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EB=8B=B4=EC=9D=84=20DTO=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/global/common/CursorPage.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/global/common/CursorPage.java diff --git a/src/main/java/org/sopt/confeti/global/common/CursorPage.java b/src/main/java/org/sopt/confeti/global/common/CursorPage.java new file mode 100644 index 0000000..80471e9 --- /dev/null +++ b/src/main/java/org/sopt/confeti/global/common/CursorPage.java @@ -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 { + + private final List itemsWithNextCursor; + private final int size; + + public static CursorPage of(final List itemsWithNextCursor, final int size) { + return new CursorPage<>(itemsWithNextCursor, size); + } + + public boolean isLast() { + return itemsWithNextCursor.size() <= size; + } + + public List getItems() { + if (isLast()) { + return itemsWithNextCursor; + } + + return itemsWithNextCursor.subList(0, size); + } + + public T getNextCursor() { + return itemsWithNextCursor.get(size - 1); + } +} From 56885e69aa058ef4d80579646fdb71c11c0aa546 Mon Sep 17 00:00:00 2001 From: chyun Date: Tue, 21 Jan 2025 17:02:12 +0900 Subject: [PATCH 67/96] =?UTF-8?q?feat=20[#126]=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=A0=20=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20Response=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/TimetablesToAddFestivalResponse.java | 8 ++++++++ .../api/user/dto/response/TimetablesToAddResponse.java | 9 +++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddFestivalResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddResponse.java diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddFestivalResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddFestivalResponse.java new file mode 100644 index 0000000..ff84b3f --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddFestivalResponse.java @@ -0,0 +1,8 @@ +package org.sopt.confeti.api.user.dto.response; + +public record TimetablesToAddFestivalResponse( + long festivalId, + String posterUrl, + String title +) { +} diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddResponse.java new file mode 100644 index 0000000..929cb3e --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddResponse.java @@ -0,0 +1,9 @@ +package org.sopt.confeti.api.user.dto.response; + +import java.util.List; + +public record TimetablesToAddResponse( + long nextCursor, + List festivals +) { +} From 10dcbaf40581c71b2233802a051bc0a4d191034c Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 06:21:17 +0900 Subject: [PATCH 68/96] =?UTF-8?q?feat=20[#126]=20=EC=BB=A4=EC=84=9C=20?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=ED=8E=98=EC=9D=B4=EC=A7=95?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=ED=95=A0=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20DTO=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facade/dto/response/TimetableToAddDTO.java | 17 +++++++++++++++++ .../application/dto/FestivalCursorDTO.java | 7 +++++++ 2 files changed, 24 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/TimetableToAddDTO.java create mode 100644 src/main/java/org/sopt/confeti/domain/festival/application/dto/FestivalCursorDTO.java diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/TimetableToAddDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/TimetableToAddDTO.java new file mode 100644 index 0000000..b0af061 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/TimetableToAddDTO.java @@ -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() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/dto/FestivalCursorDTO.java b/src/main/java/org/sopt/confeti/domain/festival/application/dto/FestivalCursorDTO.java new file mode 100644 index 0000000..1f21812 --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/festival/application/dto/FestivalCursorDTO.java @@ -0,0 +1,7 @@ +package org.sopt.confeti.domain.festival.application.dto; + +public record FestivalCursorDTO( + String cursorTitle, + boolean cursorIsFavorite +) { +} From c3e571f5f70fee404a0b09821417e856707e7278 Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 06:21:36 +0900 Subject: [PATCH 69/96] =?UTF-8?q?feat=20[#126]=20=EC=BB=A4=EC=84=9C=20?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/repository/FestivalRepository.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java index 228b8ff..2ca8ae6 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java @@ -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 { List findFestivalsByIdIn(final @Param("festivalIds") List 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 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 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 findFestivalCursor( + final @Param("userId") long userId, + final @Param("festivalId") long festivalId + ); } From b7658fe0a2231a7fed97c4ede12411b942ed32cf Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 06:21:54 +0900 Subject: [PATCH 70/96] =?UTF-8?q?feat=20[#126]=20=EC=BB=A4=EC=84=9C=20?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20DTO=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimetablesToAddFestivalResponse.java | 10 ++++++++++ .../dto/response/TimetablesToAddResponse.java | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddFestivalResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddFestivalResponse.java index ff84b3f..f397230 100644 --- a/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddFestivalResponse.java +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddFestivalResponse.java @@ -1,8 +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() + ); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddResponse.java index 929cb3e..abc2b16 100644 --- a/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddResponse.java +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/TimetablesToAddResponse.java @@ -1,9 +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 festivals ) { + private static final long DEFAULT_NEXT_CURSOR = -1L; + + public static TimetablesToAddResponse of(final CursorPage 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() + ); + } } From 7d41badab4e62af9e2de1c85cf1026b540b2f88f Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 06:22:54 +0900 Subject: [PATCH 71/96] =?UTF-8?q?feat=20[#126]=20=EC=BB=A4=EC=84=9C=20?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B7=9C=EA=B2=A9=20=EC=9C=A0?= =?UTF-8?q?=ED=8B=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/sopt/confeti/global/common/CursorPage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/global/common/CursorPage.java b/src/main/java/org/sopt/confeti/global/common/CursorPage.java index 80471e9..46efc0d 100644 --- a/src/main/java/org/sopt/confeti/global/common/CursorPage.java +++ b/src/main/java/org/sopt/confeti/global/common/CursorPage.java @@ -15,7 +15,7 @@ public static CursorPage of(final List itemsWithNextCursor, final int } public boolean isLast() { - return itemsWithNextCursor.size() <= size; + return itemsWithNextCursor.size() < size; } public List getItems() { @@ -23,7 +23,7 @@ public List getItems() { return itemsWithNextCursor; } - return itemsWithNextCursor.subList(0, size); + return itemsWithNextCursor.subList(0, size - 1); } public T getNextCursor() { From a32de3dd1a9819ee89d43430a7c665f6858c37e2 Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 06:23:48 +0900 Subject: [PATCH 72/96] =?UTF-8?q?feat=20[#126]=20=EC=98=88=EC=A0=95?= =?UTF-8?q?=EB=90=9C=20=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserFavoriteController.java | 10 ++-- .../controller/UserTimetableController.java | 17 +++++++ .../api/user/facade/UserTimetableFacade.java | 32 ++++++++++++ .../festival/application/FestivalService.java | 50 +++++++++++++++++++ 4 files changed, 105 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java index 0e3cf96..693889a 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserFavoriteController.java @@ -25,15 +25,17 @@ public class UserFavoriteController { private final S3FileHandler s3FileHandler; @PostMapping("/festivals/{festivalId}") - public ResponseEntity> postFavoriteFestival(@RequestHeader("Authorization") Long userId, @PathVariable - @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) { + public ResponseEntity> 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> deleteFavoriteFestival(@RequestHeader("Authorization") Long userId, @PathVariable - @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) { + public ResponseEntity> deleteFavoriteFestival( + @RequestHeader("Authorization") Long userId, + @PathVariable(name = "festivalId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") Long festivalId) { userFavoriteFacade.removeFestivalFavorite(userId, festivalId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java index 7e6cbd6..84a406e 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java @@ -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; @@ -20,6 +23,7 @@ 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 @@ -27,6 +31,9 @@ @RequiredArgsConstructor @RequestMapping("/user/timetables/festivals") public class UserTimetableController { + private static final String DEFAULT_PAGING_SIZE = "3"; + private static final int NEXT_CURSOR_SIZE = 1; + private final UserTimetableFacade userTimetableFacade; private final S3FileHandler s3FileHandler; @@ -36,6 +43,16 @@ public ResponseEntity> getTimetablesListAndDate(@RequestHeader(" return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserTimetableDetailResponse.of(userTimetableDTO, s3FileHandler)); } + @GetMapping("/add") + public ResponseEntity> getTimetablesToAdd( + @RequestHeader("Authorization") long userId, + @RequestParam(name = "cursor", required = false) Long cursor, + @RequestParam(name = "size", required = false, defaultValue = DEFAULT_PAGING_SIZE) int size + ) { + CursorPage timetablesToAdd = userTimetableFacade.getTimetablesToAdd(userId, cursor, size + NEXT_CURSOR_SIZE); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, TimetablesToAddResponse.of(timetablesToAdd, s3FileHandler)); + } + @PostMapping public ResponseEntity> addTimetableFestival( @RequestHeader("Authorization") long userId, diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 3412aa8..8acafad 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -1,21 +1,26 @@ package org.sopt.confeti.api.user.facade; +import java.util.Optional; import java.util.function.Predicate; import lombok.RequiredArgsConstructor; 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; import org.sopt.confeti.global.message.ErrorMessage; +import org.springframework.data.domain.PageRequest; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -106,5 +111,32 @@ protected void validateExistTimetableFestival(final long userId, final long fest throw new NotFoundException(ErrorMessage.NOT_FOUND); } } + + @Transactional(readOnly = true) + public CursorPage getTimetablesToAdd(final long userId, final Long cursor, final int size) { + if (cursor == null) { + List festivals = festivalService.findFestivalsUsingInitCursor(userId, size); + return CursorPage.of( + festivals.stream() + .map(TimetableToAddDTO::from) + .toList(), + size + ); + } + + // 커서 값 조회 + FestivalCursorDTO festivalCursorDTO = festivalService.findFestivalCursor(userId, cursor) + .orElseThrow( + () -> new NotFoundException(ErrorMessage.NOT_FOUND) + ); + + List festivals = festivalService.findFestivalsUsingCursor(userId, festivalCursorDTO.cursorTitle(), festivalCursorDTO.cursorIsFavorite(), size); + return CursorPage.of( + festivals.stream() + .map(TimetableToAddDTO::from) + .toList(), + size + ); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index 77e8bed..6af4abe 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -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; @@ -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) @@ -50,4 +58,46 @@ public boolean existsById(final long festivalId) { public List findFestivalsByIdIn(final List festivalIds) { return festivalRepository.findFestivalsByIdIn(festivalIds); } + + + @Transactional(readOnly = true) + public List 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 PageRequest getPageRequest(final int size) { + return PageRequest.of(INIT_PAGE, size); + } + + private Sort getFestivalSort() { + return Sort.by( + Order.asc(FESTIVAL_TITLE_COLUMN_NAME) + ); + } + + @Transactional(readOnly = true) + public List findFestivalsUsingCursor( + final long userId, + final String cursorTitle, + final boolean cursorIsFavorite, + final int size + ) { + return festivalRepository.findFestivalsUsingCursor( + userId, + cursorTitle, + cursorIsFavorite, + getPageRequestWithSort(size, getFestivalSort()) + ); + } + + public Optional findFestivalCursor(final long userId, final long festivalId) { + return festivalRepository.findFestivalCursor(userId, festivalId); + } } From 44855e4babb3efb0e25066b9c8f23ae129e0b968 Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 15:44:15 +0900 Subject: [PATCH 73/96] =?UTF-8?q?refactor=20[#126]=20API=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=EC=84=9C=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EC=A6=88=20=EA=B0=92=EC=9D=84=20=EC=84=9C=EB=B2=84?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=84=A4=EC=A0=95=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserTimetableController.java | 7 ++----- .../api/user/facade/UserTimetableFacade.java | 14 +++++++------- .../festival/application/FestivalService.java | 5 +---- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java index 84a406e..edd5b5d 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java @@ -31,8 +31,6 @@ @RequiredArgsConstructor @RequestMapping("/user/timetables/festivals") public class UserTimetableController { - private static final String DEFAULT_PAGING_SIZE = "3"; - private static final int NEXT_CURSOR_SIZE = 1; private final UserTimetableFacade userTimetableFacade; private final S3FileHandler s3FileHandler; @@ -46,10 +44,9 @@ public ResponseEntity> getTimetablesListAndDate(@RequestHeader(" @GetMapping("/add") public ResponseEntity> getTimetablesToAdd( @RequestHeader("Authorization") long userId, - @RequestParam(name = "cursor", required = false) Long cursor, - @RequestParam(name = "size", required = false, defaultValue = DEFAULT_PAGING_SIZE) int size + @RequestParam(name = "cursor", required = false) Long cursor ) { - CursorPage timetablesToAdd = userTimetableFacade.getTimetablesToAdd(userId, cursor, size + NEXT_CURSOR_SIZE); + CursorPage timetablesToAdd = userTimetableFacade.getTimetablesToAdd(userId, cursor); return ApiResponseUtil.success(SuccessMessage.SUCCESS, TimetablesToAddResponse.of(timetablesToAdd, s3FileHandler)); } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 8acafad..af10384 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -1,6 +1,5 @@ package org.sopt.confeti.api.user.facade; -import java.util.Optional; import java.util.function.Predicate; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; @@ -20,7 +19,6 @@ import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.exception.UnauthorizedException; import org.sopt.confeti.global.message.ErrorMessage; -import org.springframework.data.domain.PageRequest; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -30,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; @@ -113,14 +113,14 @@ protected void validateExistTimetableFestival(final long userId, final long fest } @Transactional(readOnly = true) - public CursorPage getTimetablesToAdd(final long userId, final Long cursor, final int size) { + public CursorPage getTimetablesToAdd(final long userId, final Long cursor) { if (cursor == null) { - List festivals = festivalService.findFestivalsUsingInitCursor(userId, size); + List festivals = festivalService.findFestivalsUsingInitCursor(userId, TIMETABLE_FESTIVALS_TO_ADD_SIZE); return CursorPage.of( festivals.stream() .map(TimetableToAddDTO::from) .toList(), - size + TIMETABLE_FESTIVALS_TO_ADD_SIZE ); } @@ -130,12 +130,12 @@ public CursorPage getTimetablesToAdd(final long userId, final () -> new NotFoundException(ErrorMessage.NOT_FOUND) ); - List festivals = festivalService.findFestivalsUsingCursor(userId, festivalCursorDTO.cursorTitle(), festivalCursorDTO.cursorIsFavorite(), size); + List festivals = festivalService.findFestivalsUsingCursor(userId, festivalCursorDTO.cursorTitle(), festivalCursorDTO.cursorIsFavorite(), TIMETABLE_FESTIVALS_TO_ADD_SIZE); return CursorPage.of( festivals.stream() .map(TimetableToAddDTO::from) .toList(), - size + TIMETABLE_FESTIVALS_TO_ADD_SIZE ); } } diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index 6af4abe..7b215dc 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -72,10 +72,6 @@ private PageRequest getPageRequestWithSort(final int size, final Sort sort) { return PageRequest.of(INIT_PAGE, size, sort); } - private PageRequest getPageRequest(final int size) { - return PageRequest.of(INIT_PAGE, size); - } - private Sort getFestivalSort() { return Sort.by( Order.asc(FESTIVAL_TITLE_COLUMN_NAME) @@ -97,6 +93,7 @@ public List findFestivalsUsingCursor( ); } + @Transactional(readOnly = true) public Optional findFestivalCursor(final long userId, final long festivalId) { return festivalRepository.findFestivalCursor(userId, festivalId); } From 45a5148f0172f02d893cedf5e50b16a24035c26b Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 15:46:12 +0900 Subject: [PATCH 74/96] =?UTF-8?q?refactor=20[#126]=20=EC=BB=A4=EC=84=9C=20?= =?UTF-8?q?=EB=8C=80=EC=83=81=20=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserTimetableFacade.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index af10384..622ddc3 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -125,10 +125,7 @@ public CursorPage getTimetablesToAdd(final long userId, final } // 커서 값 조회 - FestivalCursorDTO festivalCursorDTO = festivalService.findFestivalCursor(userId, cursor) - .orElseThrow( - () -> new NotFoundException(ErrorMessage.NOT_FOUND) - ); + FestivalCursorDTO festivalCursorDTO = getFestivalCursor(userId, cursor); List festivals = festivalService.findFestivalsUsingCursor(userId, festivalCursorDTO.cursorTitle(), festivalCursorDTO.cursorIsFavorite(), TIMETABLE_FESTIVALS_TO_ADD_SIZE); return CursorPage.of( @@ -138,5 +135,13 @@ public CursorPage getTimetablesToAdd(final long userId, final 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) + ); + } } From 21e58bd18770cfdb4c5618bfbdea316fa1579c4b Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 16:38:37 +0900 Subject: [PATCH 75/96] =?UTF-8?q?fix=20[#137]=20CORS=20=ED=97=88=EC=9A=A9?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=EC=97=90=20=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=20www=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/sopt/confeti/global/config/CorsConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/sopt/confeti/global/config/CorsConfig.java b/src/main/java/org/sopt/confeti/global/config/CorsConfig.java index 8bfb93d..6aa90a1 100644 --- a/src/main/java/org/sopt/confeti/global/config/CorsConfig.java +++ b/src/main/java/org/sopt/confeti/global/config/CorsConfig.java @@ -20,6 +20,7 @@ public static CorsConfigurationSource corsConfigurationSource() { allowedOriginPatterns.add("http://localhost:5174"); allowedOriginPatterns.add("https://35-appjam-web-confeti.vercel.app/"); allowedOriginPatterns.add("https://confeti.co.kr"); + allowedOriginPatterns.add("https://www.confeti.co.kr"); allowedOriginPatterns.add("https://confeti.xyz"); allowedOriginPatterns.add("https://api.confeti.xyz"); configuration.setAllowedOrigins(allowedOriginPatterns); From 1c2a63b56cf25743b40557551a9c829f04bac483 Mon Sep 17 00:00:00 2001 From: Ivoryeee <105477246+Ivoryeee@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:52:28 +0900 Subject: [PATCH 76/96] =?UTF-8?q?feat=20[#104]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=EB=90=9C=20=EC=8B=9C=EA=B0=84=ED=91=9C=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?Api=20=EA=B5=AC=ED=98=84=20(#132)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat [#104] UserTimetableController 및 응답값 구현 * feat [#104] UserTimetableFacade 및 DTO 구현 * feat [#104] FestivalDateService 구현 * feat [#104] FestivalDateRepository 구현 * feat [#126] 커서 정보 담을 DTO 작성 * feat [#126] 추가할 페스티벌 응답 Response 작성 * feat [#126] 커서 베이스 페이징에 사용할 데이터 DTO 생성 * feat [#126] 커서 베이스 페이징 쿼리 작성 * feat [#126] 커서 베이스 페이징 응답 DTO 생성 * feat [#126] 커서 베이스 페이징 데이터 규격 유틸 작성 * feat [#126] 예정된 페스티벌 목록 조회 API 구현 * refactor [#126] API 명세서에 따라 사이즈 값을 서버에서 설정하도록 변경 * refactor [#126] 커서 대상 페스티벌 조회 * feat [#104] UserTimetableFacade 및 DTO 구현 * refactor [#104] 원시타입으로 수정 * refactor [#104] Optional 추가 * style [#104] 리베이스 중 중복된 코드 삭제 --------- Co-authored-by: chyun --- .../controller/UserTimetableController.java | 11 +++++++ .../UserTimetableFestivalArtistResponse.java | 16 ++++++++++ .../UserTimetableFestivalResponse.java | 22 +++++++++++++ .../UserTimetableFestivalStageResponse.java | 21 +++++++++++++ .../UserTimetableFestivalTimeResponse.java | 26 ++++++++++++++++ .../api/user/facade/UserTimetableFacade.java | 28 +++++++++++++++++ .../UserTimetableFestivalArtistDTO.java | 13 ++++++++ .../UserTimetableFestivalBasicDTO.java | 18 +++++++++++ .../UserTimetableFestivalStageDTO.java | 23 ++++++++++++++ .../UserTimetableFestivalTimeDTO.java | 31 +++++++++++++++++++ .../application/FestivalDateService.java | 27 ++++++++++++++++ .../repository/FestivalDateRepository.java | 17 ++++++++++ 12 files changed, 253 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalArtistResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalStageResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalTimeResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalArtistDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalBasicDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalStageDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalTimeDTO.java create mode 100644 src/main/java/org/sopt/confeti/domain/festivaldate/application/FestivalDateService.java diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java index edd5b5d..6bd7d77 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java @@ -5,10 +5,12 @@ 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.dto.response.UserTimetableFestivalResponse; 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.api.user.facade.dto.response.UserTimetableFestivalBasicDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.common.CursorPage; import org.sopt.confeti.global.message.SuccessMessage; @@ -67,4 +69,13 @@ public ResponseEntity> removeTimetableFestival( userTimetableFacade.removeTimetableFestival(userId, festivalId); return ApiResponseUtil.success(SuccessMessage.SUCCESS); } + + @GetMapping("/{festivalDateId}") + public ResponseEntity> getTimetableFestival( + @RequestHeader("Authorization") long userId, + @PathVariable(name = "festivalDateId") @Min(value=0, message="요청 형식이 올바르지 않습니다.") long festivalDateId + ){ + UserTimetableFestivalBasicDTO response = userTimetableFacade.getTimetableInfo(userId, festivalDateId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserTimetableFestivalResponse.from(response)); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalArtistResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalArtistResponse.java new file mode 100644 index 0000000..8686d95 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalArtistResponse.java @@ -0,0 +1,16 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableFestivalArtistDTO; + +public record UserTimetableFestivalArtistResponse ( + String artistId, + String artistName +){ + public static UserTimetableFestivalArtistResponse from(UserTimetableFestivalArtistDTO artist) { + return new UserTimetableFestivalArtistResponse( + artist.artistId(), + artist.artistName() + ); + } +} + diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalResponse.java new file mode 100644 index 0000000..3af39de --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalResponse.java @@ -0,0 +1,22 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableFestivalBasicDTO; +import org.sopt.confeti.global.util.DateConvertor; + +import java.util.List; + +public record UserTimetableFestivalResponse ( + String ticketOpenAt, + int stageCount, + List stages +){ + public static UserTimetableFestivalResponse from(UserTimetableFestivalBasicDTO timetableFestival) { + return new UserTimetableFestivalResponse( + DateConvertor.convert(timetableFestival.ticketOpenAt()), + timetableFestival.stages().size(), + timetableFestival.stages().stream() + .map(UserTimetableFestivalStageResponse::from) + .toList() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalStageResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalStageResponse.java new file mode 100644 index 0000000..ce13741 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalStageResponse.java @@ -0,0 +1,21 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableFestivalStageDTO; + +import java.util.List; + +public record UserTimetableFestivalStageResponse( + int stageOrder, + String stageName, + List festivalTimes +){ + public static UserTimetableFestivalStageResponse from(UserTimetableFestivalStageDTO festivalStage) { + return new UserTimetableFestivalStageResponse( + festivalStage.stageOrder(), + festivalStage.stageName(), + festivalStage.festivalTimes().stream() + .map(UserTimetableFestivalTimeResponse::from) + .toList() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalTimeResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalTimeResponse.java new file mode 100644 index 0000000..d60cd36 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalTimeResponse.java @@ -0,0 +1,26 @@ +package org.sopt.confeti.api.user.dto.response; + +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableFestivalTimeDTO; +import org.sopt.confeti.global.util.DateConvertor; + +import java.util.List; + +public record UserTimetableFestivalTimeResponse( + Long festivalTimeId, + String startAt, + String endAt, + Boolean isSelected, + List artists +) { + public static UserTimetableFestivalTimeResponse from(UserTimetableFestivalTimeDTO festivalTime) { + return new UserTimetableFestivalTimeResponse( + festivalTime.festivalTimeId(), + DateConvertor.convert(festivalTime.startAt()), + DateConvertor.convert(festivalTime.endAt()), + festivalTime.isSelected(), + festivalTime.artists().stream() + .map(UserTimetableFestivalArtistResponse::from) + .toList() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 622ddc3..93f2cf6 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -1,5 +1,6 @@ package org.sopt.confeti.api.user.facade; +import java.util.Optional; import java.util.function.Predicate; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; @@ -7,9 +8,14 @@ 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.api.user.facade.dto.response.UserTimetableFestivalBasicDTO; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.domain.festival.application.FestivalService; +import org.sopt.confeti.domain.festivaldate.FestivalDate; +import org.sopt.confeti.domain.festivaldate.application.FestivalDateService; import org.sopt.confeti.domain.festival.application.dto.FestivalCursorDTO; +import org.sopt.confeti.domain.festivaldate.FestivalDate; +import org.sopt.confeti.domain.festivaldate.application.FestivalDateService; import org.sopt.confeti.domain.timetablefestival.TimetableFestival; import org.sopt.confeti.domain.timetablefestival.application.TimetableFestivalService; import org.sopt.confeti.domain.user.User; @@ -19,6 +25,9 @@ import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.exception.UnauthorizedException; import org.sopt.confeti.global.message.ErrorMessage; +import org.sopt.confeti.global.util.artistsearcher.ArtistResolver; +import org.sopt.confeti.global.util.artistsearcher.ArtistResolver; +import org.springframework.data.domain.PageRequest; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -34,6 +43,8 @@ public class UserTimetableFacade { private final UserService userService; private final TimetableFestivalService timetableFestivalService; private final FestivalService festivalService; + private final FestivalDateService festivalDateService; + private final ArtistResolver artistResolver; @Transactional(readOnly = true) public UserTimetableDTO getTimetablesListAndDate(long userId) { @@ -136,6 +147,23 @@ public CursorPage getTimetablesToAdd(final long userId, final ); } + @Transactional(readOnly = true) + public UserTimetableFestivalBasicDTO getTimetableInfo(final long userId, final long festivalDateId) { + validateUserExists(userId); + + FestivalDate festivalDate = festivalDateService.findFestivalDateId(userId, festivalDateId); + artistResolver.load(festivalDate); + + return UserTimetableFestivalBasicDTO.from(festivalDate); + } + + @Transactional(readOnly = true) + protected void validateUserExists(final long userId) { + if (!userService.existsById(userId)) { + throw new UnauthorizedException(ErrorMessage.UNAUTHORIZED); + } + } + @Transactional(readOnly = true) public FestivalCursorDTO getFestivalCursor(final long userId, final long cursor) { return festivalService.findFestivalCursor(userId, cursor) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalArtistDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalArtistDTO.java new file mode 100644 index 0000000..17e0045 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalArtistDTO.java @@ -0,0 +1,13 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.festivalartist.FestivalArtist; + +public record UserTimetableFestivalArtistDTO (String artistId, String artistName +){ + public static UserTimetableFestivalArtistDTO from(FestivalArtist festivalArtist) { + return new UserTimetableFestivalArtistDTO( + festivalArtist.getArtist().getArtistId(), + festivalArtist.getArtist().getName() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalBasicDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalBasicDTO.java new file mode 100644 index 0000000..acfac35 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalBasicDTO.java @@ -0,0 +1,18 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.festivaldate.FestivalDate; + +import java.time.LocalTime; +import java.util.List; + +public record UserTimetableFestivalBasicDTO(LocalTime ticketOpenAt, List stages) { + public static UserTimetableFestivalBasicDTO from(FestivalDate festivalDate) { + return new UserTimetableFestivalBasicDTO( + festivalDate.getOpenAt(), + festivalDate.getStages() + .stream() + .map(UserTimetableFestivalStageDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalStageDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalStageDTO.java new file mode 100644 index 0000000..b9ab5af --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalStageDTO.java @@ -0,0 +1,23 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.festivalstage.FestivalStage; + +import java.util.List; + +public record UserTimetableFestivalStageDTO ( + int stageOrder, + String stageName, + List festivalTimes +){ + public static UserTimetableFestivalStageDTO from(FestivalStage festivalStage + ) { + return new UserTimetableFestivalStageDTO( + festivalStage.getOrder(), + festivalStage.getName(), + festivalStage.getTimes() + .stream() + .map(UserTimetableFestivalTimeDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalTimeDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalTimeDTO.java new file mode 100644 index 0000000..9d4affd --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalTimeDTO.java @@ -0,0 +1,31 @@ +package org.sopt.confeti.api.user.facade.dto.response; + +import org.sopt.confeti.domain.festivaltime.FestivalTime; +import org.sopt.confeti.domain.usertimetable.UserTimetable; + +import java.time.LocalTime; +import java.util.List; + + +public record UserTimetableFestivalTimeDTO ( + long festivalTimeId, + LocalTime startAt, + LocalTime endAt, + boolean isSelected, + List artists +){ + public static UserTimetableFestivalTimeDTO from(FestivalTime festivalTime) { + return new UserTimetableFestivalTimeDTO( + festivalTime.getId(), + festivalTime.getStartAt(), + festivalTime.getEndAt(), + festivalTime.getTimetables() + .stream() + .anyMatch(UserTimetable::isSelected), + festivalTime.getArtists() + .stream() + .map(UserTimetableFestivalArtistDTO::from) + .toList() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/domain/festivaldate/application/FestivalDateService.java b/src/main/java/org/sopt/confeti/domain/festivaldate/application/FestivalDateService.java new file mode 100644 index 0000000..17e6993 --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/festivaldate/application/FestivalDateService.java @@ -0,0 +1,27 @@ +package org.sopt.confeti.domain.festivaldate.application; + +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.domain.festivaldate.FestivalDate; +import org.sopt.confeti.domain.festivaldate.infra.repository.FestivalDateRepository; +import org.sopt.confeti.global.exception.NotFoundException; +import org.sopt.confeti.global.message.ErrorMessage; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + + +@Service +@RequiredArgsConstructor +public class FestivalDateService { + + private final FestivalDateRepository festivalDateRepository; + + @Transactional(readOnly = true) + public FestivalDate findFestivalDateId(final long userId, final long festivalDateId) { + return festivalDateRepository.findByFestivalDateId(userId, festivalDateId) + .orElseThrow( + () -> new NotFoundException(ErrorMessage.NOT_FOUND) + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/festivaldate/infra/repository/FestivalDateRepository.java b/src/main/java/org/sopt/confeti/domain/festivaldate/infra/repository/FestivalDateRepository.java index 213504e..3cf6479 100644 --- a/src/main/java/org/sopt/confeti/domain/festivaldate/infra/repository/FestivalDateRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festivaldate/infra/repository/FestivalDateRepository.java @@ -2,6 +2,23 @@ import org.sopt.confeti.domain.festivaldate.FestivalDate; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.Optional; public interface FestivalDateRepository extends JpaRepository { + @Query("SELECT f " + + " FROM FestivalDate f " + + " JOIN f.stages s " + + " JOIN s.times t " + + " JOIN t.artists a " + + " JOIN t.timetables ut " + + " JOIN ut.timetableFestival tf " + + " WHERE tf.user.id = :userId AND f.id = :festivalDateId" + ) + + Optional findByFestivalDateId( + @Param("userId") long userId, + @Param("festivalDateId") long festivalDateId); } From 28bf0879eb471d6fce61663dbfe9909f22b3463b Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 21:15:55 +0900 Subject: [PATCH 77/96] =?UTF-8?q?fix=20[#140]=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EA=B0=92=20=EB=B3=80=EA=B2=BD=ED=95=B4=EC=84=9C=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/repository/TimetableFestivalRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java index f007421..25b3a52 100644 --- a/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/timetablefestival/infra/repository/TimetableFestivalRepository.java @@ -8,7 +8,7 @@ import org.springframework.data.repository.query.Param; public interface TimetableFestivalRepository extends JpaRepository { - @Query("select tf from TimetableFestival tf join fetch tf.festival f where tf.user.id = :userId and f.festivalEndAt <= CURRENT_DATE") + @Query("select tf from TimetableFestival tf join fetch tf.festival f where tf.user.id = :userId and f.festivalEndAt >= CURRENT_DATE") List findByUserIdWhereEndAtLENow(@Param("userId") Long userId); List findByUserId(final long userId); From 8c724becbf7cbb8fd637b2f5f8cc052a906d7663 Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 23:56:35 +0900 Subject: [PATCH 78/96] =?UTF-8?q?fix=20[#145]=20=ED=8E=98=EC=8A=A4?= =?UTF-8?q?=ED=8B=B0=EB=B2=8C=20=EC=83=9D=EC=84=B1=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CreateFestivalArtistRequest.java | 3 +++ .../dto/request/CreateFestivalDateRequest.java | 4 ++++ .../dto/request/CreateFestivalRequest.java | 16 ++++++++++++++++ .../dto/request/CreateFestivalStageRequest.java | 2 ++ .../dto/request/CreateFestivalTimeRequest.java | 4 ++++ 5 files changed, 29 insertions(+) diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalArtistRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalArtistRequest.java index 3956c92..d43477b 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalArtistRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalArtistRequest.java @@ -1,6 +1,9 @@ package org.sopt.confeti.api.performance.dto.request; +import com.fasterxml.jackson.annotation.JsonProperty; + public record CreateFestivalArtistRequest( + @JsonProperty(value = "artist_id") String artistId ) { } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java index cf43667..62c05ba 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java @@ -1,15 +1,19 @@ package org.sopt.confeti.api.performance.dto.request; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; public record CreateFestivalDateRequest( @JsonFormat(pattern = "yyyy.MM.dd") + @JsonProperty(value = "festival_at") LocalDate festivalAt, @JsonFormat(pattern = "HH:mm:ss") + @JsonProperty(value = "ticket_open_at") LocalTime ticketOpenAt, + @JsonProperty(value = "festival_stages") List festivalStages ) { } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java index 30f5fbd..56f3447 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java @@ -1,29 +1,45 @@ package org.sopt.confeti.api.performance.dto.request; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; import java.time.LocalDate; import java.util.List; public record CreateFestivalRequest( + @JsonProperty(value = "festival_title") String festivalTitle, + @JsonProperty(value = "festival_subtitle") String festivalSubtitle, @JsonFormat(pattern = "yyyy.MM.dd") + @JsonProperty(value = "festival_start_at") LocalDate festivalStartAt, @JsonFormat(pattern = "yyyy.MM.dd") + @JsonProperty(value = "festival_end_at") LocalDate festivalEndAt, + @JsonProperty(value = "festival_area") String festivalArea, + @JsonProperty(value = "festival_poster_path") String festivalPosterPath, + @JsonProperty(value = "festival_poster_bg_path") String festivalPosterBgPath, + @JsonProperty(value = "festival_info_img_path") String festivalInfoImgPath, + @JsonProperty(value = "festival_reservation_bg_path") String festivalReservationBgPath, + @JsonProperty(value = "festival_logo_path") String festivalLogoPath, @JsonFormat(pattern = "yyyy.MM.dd") + @JsonProperty(value = "reserve_at") LocalDate reserveAt, + @JsonProperty(value = "reservation_url") String reservationUrl, + @JsonProperty(value = "reservation_office") String reservationOffice, + @JsonProperty(value = "age_rating") String ageRating, String time, String price, + @JsonProperty(value = "festival_dates") List festivalDates ) { } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalStageRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalStageRequest.java index 60819ca..b34fdb8 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalStageRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalStageRequest.java @@ -1,10 +1,12 @@ package org.sopt.confeti.api.performance.dto.request; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; public record CreateFestivalStageRequest( String name, int orders, + @JsonProperty(value = "festival_times") List festivalTimes ) { } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java index 8214628..395618a 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java @@ -1,14 +1,18 @@ package org.sopt.confeti.api.performance.dto.request; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; import java.time.LocalTime; import java.util.List; public record CreateFestivalTimeRequest( @JsonFormat(pattern = "HH:mm:ss") + @JsonProperty(value = "start_at") LocalTime startAt, @JsonFormat(pattern = "HH:mm:ss") + @JsonProperty(value = "end_at") LocalTime endAt, + @JsonProperty(value = "festival_artists") List festivalArtists ) { } From f8d00fa8bf7281471a774b96d626c7a6da3f180e Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 23:05:17 +0900 Subject: [PATCH 79/96] =?UTF-8?q?refactor=20[#143]=20=EC=BD=98=EC=84=9C?= =?UTF-8?q?=ED=8A=B8,=20=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EA=B0=92=20=EA=B8=B8=EC=9D=B4=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/sopt/confeti/domain/concert/Concert.java | 6 +++--- .../java/org/sopt/confeti/domain/festival/Festival.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/sopt/confeti/domain/concert/Concert.java b/src/main/java/org/sopt/confeti/domain/concert/Concert.java index 0dfe79d..19f53ad 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/Concert.java +++ b/src/main/java/org/sopt/confeti/domain/concert/Concert.java @@ -34,7 +34,7 @@ public class Concert { @Column(nullable = false) private LocalDate concertEndAt; - @Column(length = 30, nullable = false) + @Column(length = 100, nullable = false) private String concertArea; @Column(length = 250, nullable = false) @@ -55,7 +55,7 @@ public class Concert { @Column(length = 250, nullable = false) private String reservationUrl; - @Column(length = 30, nullable = false) + @Column(length = 50, nullable = false) private String reservationOffice; @Column(length = 30, nullable = false) @@ -64,7 +64,7 @@ public class Concert { @Column(name = "times", length = 30, nullable = false) private String time; - @Column(length = 100, nullable = false) + @Column(length = 200, nullable = false) private String price; @OneToMany(mappedBy = "concert", cascade = CascadeType.REMOVE) diff --git a/src/main/java/org/sopt/confeti/domain/festival/Festival.java b/src/main/java/org/sopt/confeti/domain/festival/Festival.java index e95b7ab..1527622 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/Festival.java +++ b/src/main/java/org/sopt/confeti/domain/festival/Festival.java @@ -38,7 +38,7 @@ public class Festival { @Column(nullable = false) private LocalDate festivalEndAt; - @Column(length = 30, nullable = false) + @Column(length = 100, nullable = false) private String festivalArea; @Column(length = 250, nullable = false) @@ -62,7 +62,7 @@ public class Festival { @Column(length = 250, nullable = false) private String reservationUrl; - @Column(length = 30, nullable = false) + @Column(length = 50, nullable = false) private String reservationOffice; @Column(length = 30, nullable = false) @@ -71,7 +71,7 @@ public class Festival { @Column(name = "times", length = 30, nullable = false) private String time; - @Column(length = 100, nullable = false) + @Column(length = 200, nullable = false) private String price; @OneToMany(mappedBy = "festival", cascade = CascadeType.ALL, orphanRemoval = true) From 1c375da4b93ba6c5bfd6b18398a15193d12a6baf Mon Sep 17 00:00:00 2001 From: chyun Date: Wed, 22 Jan 2025 23:11:43 +0900 Subject: [PATCH 80/96] =?UTF-8?q?refactor=20[#143]=20=EC=BD=98=EC=84=9C?= =?UTF-8?q?=ED=8A=B8,=20=ED=8E=98=EC=8A=A4=ED=8B=B0=EB=B2=8C=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EA=B0=92=20nullable=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/sopt/confeti/domain/concert/Concert.java | 2 +- src/main/java/org/sopt/confeti/domain/festival/Festival.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/domain/concert/Concert.java b/src/main/java/org/sopt/confeti/domain/concert/Concert.java index 19f53ad..5c355c9 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/Concert.java +++ b/src/main/java/org/sopt/confeti/domain/concert/Concert.java @@ -46,7 +46,7 @@ public class Concert { @Column(length = 250, nullable = false) private String concertInfoImgPath; - @Column(length = 250, nullable = false) + @Column(length = 250) // 나중에 nullable = false로 수정 private String concertReservationBgPath; @Column(nullable = false) diff --git a/src/main/java/org/sopt/confeti/domain/festival/Festival.java b/src/main/java/org/sopt/confeti/domain/festival/Festival.java index 1527622..e523be2 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/Festival.java +++ b/src/main/java/org/sopt/confeti/domain/festival/Festival.java @@ -50,7 +50,7 @@ public class Festival { @Column(length = 250, nullable = false) private String festivalInfoImgPath; - @Column(length = 250) + @Column(length = 250) // 나중에 nullable = false로 수정 private String festivalReservationBgPath; @Column(length = 250) From aef93ee72639e417c41ef6cb89fcf370555006b7 Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 01:38:20 +0900 Subject: [PATCH 81/96] =?UTF-8?q?fix=20[#143]=20ERD=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CreateFestivalRequest.java | 7 +- .../response/ConcertDetailInfoResponse.java | 4 +- .../response/FestivalDetailInfoResponse.java | 4 +- .../PerformanceReservationDetailResponse.java | 6 +- .../PerformanceReservationResponse.java | 2 +- .../facade/dto/request/CreateFestivalDTO.java | 6 +- .../facade/dto/response/ConcertDetailDTO.java | 4 +- .../dto/response/FestivalDetailDTO.java | 7 +- .../PerformanceReservationDetailDTO.java | 6 +- .../UserFavoritePerformanceResponse.java | 8 +- .../UserFavoritePerformancesResponse.java | 5 +- .../response/UserFavoritePerformanceDTO.java | 4 +- .../sopt/confeti/domain/concert/Concert.java | 7 +- .../confeti/domain/festival/Festival.java | 15 +-- .../domain/view/performance/Performance.java | 58 +++++++++ .../view/performance/PerformanceDTO.java | 7 +- .../performance/PerformanceTicketDTO.java | 6 +- .../repository/PerformanceRepository.java | 121 +----------------- .../confeti/global/util/DateConvertor.java | 8 ++ 19 files changed, 117 insertions(+), 168 deletions(-) create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/Performance.java diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java index 56f3447..922ffe4 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; public record CreateFestivalRequest( @@ -12,10 +13,10 @@ public record CreateFestivalRequest( String festivalSubtitle, @JsonFormat(pattern = "yyyy.MM.dd") @JsonProperty(value = "festival_start_at") - LocalDate festivalStartAt, + LocalDateTime festivalStartAt, @JsonFormat(pattern = "yyyy.MM.dd") @JsonProperty(value = "festival_end_at") - LocalDate festivalEndAt, + LocalDateTime festivalEndAt, @JsonProperty(value = "festival_area") String festivalArea, @JsonProperty(value = "festival_poster_path") @@ -30,7 +31,7 @@ public record CreateFestivalRequest( String festivalLogoPath, @JsonFormat(pattern = "yyyy.MM.dd") @JsonProperty(value = "reserve_at") - LocalDate reserveAt, + LocalDateTime reserveAt, @JsonProperty(value = "reservation_url") String reservationUrl, @JsonProperty(value = "reservation_office") diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java index 4743bb3..afdfd08 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java @@ -30,8 +30,8 @@ public static ConcertDetailInfoResponse of(final ConcertDetailDTO concertDetailD s3FileHandler.getFileUrl(concertDetailDTO.posterBgPath()), concertDetailDTO.title(), concertDetailDTO.subtitle(), - DateConvertor.convert(concertDetailDTO.startAt()), - DateConvertor.convert(concertDetailDTO.endAt()), + DateConvertor.convertToLocalDate(concertDetailDTO.startAt()), + DateConvertor.convertToLocalDate(concertDetailDTO.endAt()), concertDetailDTO.area(), DateConvertor.convert(concertDetailDTO.reserveAt()), concertDetailDTO.reservationUrl(), diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java index 8331584..9f0a4c4 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/FestivalDetailInfoResponse.java @@ -30,8 +30,8 @@ public static FestivalDetailInfoResponse from(final FestivalDetailDTO festival) festival.festivalPosterBgUrl(), festival.festivalTitle(), festival.festivalSubtitle(), - DateConvertor.convert(festival.festivalStartAt()), - DateConvertor.convert(festival.festivalEndAt()), + DateConvertor.convertToLocalDate(festival.festivalStartAt()), + DateConvertor.convertToLocalDate(festival.festivalEndAt()), festival.festivalArea(), DateConvertor.convert(festival.reserveAt()), festival.reservationUrl(), diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationDetailResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationDetailResponse.java index dfde279..9cfeef1 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationDetailResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationDetailResponse.java @@ -5,8 +5,8 @@ import org.sopt.confeti.global.util.S3FileHandler; public record PerformanceReservationDetailResponse( - long index, - long performanceId, + int index, + long typeId, PerformanceType type, String subtitle, String reserveAt, @@ -15,7 +15,7 @@ public record PerformanceReservationDetailResponse( public static PerformanceReservationDetailResponse of(PerformanceReservationDetailDTO performanceReservation, S3FileHandler s3FileHandler) { return new PerformanceReservationDetailResponse( performanceReservation.index(), - performanceReservation.performanceId(), + performanceReservation.typeId(), performanceReservation.type(), performanceReservation.subtitle(), performanceReservation.reserveAt(), diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationResponse.java index 6531f7f..c1cf0ba 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/PerformanceReservationResponse.java @@ -6,7 +6,7 @@ import java.util.List; public record PerformanceReservationResponse( - Integer performanceCount, + int performanceCount, List performances ){ public static PerformanceReservationResponse of(final PerformanceReservationDTO performanceReservation, diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java index 1428bc1..692f0e8 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java @@ -8,15 +8,15 @@ public record CreateFestivalDTO( String festivalTitle, String festivalSubtitle, - LocalDate festivalStartAt, - LocalDate festivalEndAt, + LocalDateTime festivalStartAt, + LocalDateTime festivalEndAt, String festivalArea, String festivalPosterPath, String festivalPosterBgPath, String festivalInfoImgPath, String festivalReservationBgPath, String festivalLogoPath, - LocalDate reserveAt, + LocalDateTime reserveAt, String reservationUrl, String reservationOffice, String ageRating, diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java index a2b2412..a5e245e 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java @@ -9,8 +9,8 @@ public record ConcertDetailDTO( long concertId, String title, String subtitle, - LocalDate startAt, - LocalDate endAt, + LocalDateTime startAt, + LocalDateTime endAt, String area, String posterPath, String posterBgPath, diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDTO.java index d241bdd..8e7cdeb 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDTO.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/FestivalDetailDTO.java @@ -1,6 +1,5 @@ package org.sopt.confeti.api.performance.facade.dto.response; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import org.sopt.confeti.domain.festival.Festival; @@ -10,15 +9,15 @@ public record FestivalDetailDTO( long festivalId, String festivalTitle, String festivalSubtitle, - LocalDate festivalStartAt, - LocalDate festivalEndAt, + LocalDateTime festivalStartAt, + LocalDateTime festivalEndAt, String festivalArea, String festivalPosterUrl, String festivalPosterBgUrl, String festivalInfoImgUrl, String festivalReservationBgUrl, String festivalLogoUrl, - LocalDate reserveAt, + LocalDateTime reserveAt, String reservationUrl, String reservationOffice, String ageRating, diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDetailDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDetailDTO.java index d577b58..513093f 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDetailDTO.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/PerformanceReservationDetailDTO.java @@ -4,8 +4,8 @@ import org.sopt.confeti.global.common.constant.PerformanceType; public record PerformanceReservationDetailDTO( - long index, - long performanceId, + int index, + long typeId, PerformanceType type, String subtitle, String reserveAt, @@ -14,7 +14,7 @@ public record PerformanceReservationDetailDTO( public static PerformanceReservationDetailDTO from(PerformanceTicketDTO performanceTicketDTO) { return new PerformanceReservationDetailDTO( performanceTicketDTO.index(), - performanceTicketDTO.performanceId(), + performanceTicketDTO.typeId(), performanceTicketDTO.type(), performanceTicketDTO.subtitle(), performanceTicketDTO.reserveAt(), diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformanceResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformanceResponse.java index 051922e..e310001 100644 --- a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformanceResponse.java +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformanceResponse.java @@ -4,15 +4,17 @@ import org.sopt.confeti.global.util.S3FileHandler; public record UserFavoritePerformanceResponse( - long performanceId, + int index, + long typeId, String type, String title, String posterUrl ) { public static UserFavoritePerformanceResponse of(final UserFavoritePerformanceDTO performanceDTO, final - S3FileHandler s3FileHandler) { + S3FileHandler s3FileHandler, final int index) { return new UserFavoritePerformanceResponse( - performanceDTO.performanceId(), + index, + performanceDTO.typeId(), performanceDTO.type().getType(), performanceDTO.title(), s3FileHandler.getFileUrl(performanceDTO.posterPath()) diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformancesResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformancesResponse.java index bb88f47..a26d0f8 100644 --- a/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformancesResponse.java +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserFavoritePerformancesResponse.java @@ -1,6 +1,7 @@ package org.sopt.confeti.api.user.dto.response; import java.util.List; +import java.util.stream.IntStream; import org.sopt.confeti.api.user.facade.dto.response.UserFavoritePerformancesDTO; import org.sopt.confeti.global.util.S3FileHandler; @@ -10,8 +11,8 @@ public record UserFavoritePerformancesResponse( public static UserFavoritePerformancesResponse of(final UserFavoritePerformancesDTO performancesDTO, final S3FileHandler s3FileHandler) { return new UserFavoritePerformancesResponse( - performancesDTO.performances().stream() - .map(performanceDTO -> UserFavoritePerformanceResponse.of(performanceDTO, s3FileHandler)) + IntStream.range(0, performancesDTO.performances().size()) + .mapToObj(i -> UserFavoritePerformanceResponse.of(performancesDTO.performances().get(i), s3FileHandler, i)) .toList() ); } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java index c04b325..45e05b8 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserFavoritePerformanceDTO.java @@ -4,14 +4,14 @@ import org.sopt.confeti.global.common.constant.PerformanceType; public record UserFavoritePerformanceDTO( - long performanceId, + long typeId, PerformanceType type, String title, String posterPath ) { public static UserFavoritePerformanceDTO from(final PerformanceDTO performance) { return new UserFavoritePerformanceDTO( - performance.performanceId(), + performance.typeId(), performance.type(), performance.title(), performance.posterPath() diff --git a/src/main/java/org/sopt/confeti/domain/concert/Concert.java b/src/main/java/org/sopt/confeti/domain/concert/Concert.java index 5c355c9..30dec26 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/Concert.java +++ b/src/main/java/org/sopt/confeti/domain/concert/Concert.java @@ -5,12 +5,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.sopt.confeti.domain.concertartist.ConcertArtist; - -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; @Entity @Table(name="concerts") @@ -29,10 +26,10 @@ public class Concert { private String concertSubtitle; @Column(nullable = false) - private LocalDate concertStartAt; + private LocalDateTime concertStartAt; @Column(nullable = false) - private LocalDate concertEndAt; + private LocalDateTime concertEndAt; @Column(length = 100, nullable = false) private String concertArea; diff --git a/src/main/java/org/sopt/confeti/domain/festival/Festival.java b/src/main/java/org/sopt/confeti/domain/festival/Festival.java index e523be2..0897f3d 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/Festival.java +++ b/src/main/java/org/sopt/confeti/domain/festival/Festival.java @@ -9,12 +9,9 @@ import org.sopt.confeti.domain.festivaldate.FestivalDate; import org.sopt.confeti.domain.festivalfavorite.FestivalFavorite; import org.sopt.confeti.domain.timetablefestival.TimetableFestival; - -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import org.sopt.confeti.domain.user.User; @Entity @Table(name="festivals") @@ -33,10 +30,10 @@ public class Festival { private String festivalSubtitle; @Column(nullable = false) - private LocalDate festivalStartAt; + private LocalDateTime festivalStartAt; @Column(nullable = false) - private LocalDate festivalEndAt; + private LocalDateTime festivalEndAt; @Column(length = 100, nullable = false) private String festivalArea; @@ -57,7 +54,7 @@ public class Festival { private String festivalLogoPath; @Column(nullable = false) - private LocalDate reserveAt; + private LocalDateTime reserveAt; @Column(length = 250, nullable = false) private String reservationUrl; @@ -84,11 +81,11 @@ public class Festival { private List timetableFestivals = new ArrayList<>(); @Builder - public Festival(String festivalTitle, String festivalSubtitle, LocalDate festivalStartAt, - LocalDate festivalEndAt, String festivalArea, String festivalPosterPath, + public Festival(String festivalTitle, String festivalSubtitle, LocalDateTime festivalStartAt, + LocalDateTime festivalEndAt, String festivalArea, String festivalPosterPath, String festivalPosterBgPath, String festivalInfoImgPath, String festivalReservationBgPath, String festivalLogoPath, - LocalDate reserveAt, String reservationUrl, String reservationOffice, String ageRating, + LocalDateTime reserveAt, String reservationUrl, String reservationOffice, String ageRating, String time, String price, List dates) { this.festivalTitle = festivalTitle; diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/Performance.java b/src/main/java/org/sopt/confeti/domain/view/performance/Performance.java new file mode 100644 index 0000000..cb7381c --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/view/performance/Performance.java @@ -0,0 +1,58 @@ +package org.sopt.confeti.domain.view.performance; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.sopt.confeti.global.common.constant.PerformanceType; + +@Entity +@Table(name = "performances") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Performance { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "performance_id") + private Long id; + + @Column(nullable = false) + private long typeId; + + @Column(length = 10, nullable = false) + @Enumerated(value = EnumType.STRING) + private PerformanceType type; + + @Column(length = 100, nullable = false) + private String artistId; + + @Column(length = 100, nullable = false) + private String area; + + @Column(length = 50, nullable = false) + private String title; + + @Column(length = 50, nullable = false) + private String subtitle; + + @Column(nullable = false) + private LocalDateTime startAt; + + @Column(nullable = false) + private LocalDateTime endAt; + + @Column(length = 250, nullable = false) + private String posterPath; + + @Column(length = 250, nullable = false) + private String reservationBgPath; +} diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java index 80ba2c7..45d9cd7 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceDTO.java @@ -1,22 +1,21 @@ package org.sopt.confeti.domain.view.performance; -import java.time.LocalDate; import org.sopt.confeti.global.common.constant.PerformanceType; public record PerformanceDTO( - long performanceId, + long typeId, PerformanceType type, String title, String posterPath ) { public static PerformanceDTO of( - final long performanceId, + final long typeId, final String type, final String title, final String posterPath ) { return new PerformanceDTO( - performanceId, + typeId, PerformanceType.convert(type), title, posterPath diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceTicketDTO.java b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceTicketDTO.java index 7972505..f66d441 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceTicketDTO.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/PerformanceTicketDTO.java @@ -3,15 +3,15 @@ import org.sopt.confeti.global.common.constant.PerformanceType; public record PerformanceTicketDTO( - long index, - long performanceId, + int index, + long typeId, PerformanceType type, String subtitle, String reserveAt, String reservationBgUrl ) { public static PerformanceTicketDTO of( - final long index, + final int index, final long performanceId, final String type, final String subtitle, diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java index 89f59fb..f9964e2 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java @@ -1,120 +1,7 @@ package org.sopt.confeti.domain.view.performance.infra.repository; -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; +import org.sopt.confeti.domain.view.performance.Performance; +import org.springframework.data.jpa.repository.JpaRepository; -import java.sql.Timestamp; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.sopt.confeti.domain.view.performance.PerformanceDTO; -import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; -import org.sopt.confeti.global.common.constant.PerformanceType; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class PerformanceRepository { - - private final EntityManager em; - - private static final int PREVIEW_FAVORITE_PERFORMANCE_COUNT = 3; - private static final int RESERVE_FAVORITE_PERFORMANCE_COUNT = 5; - - public List findFavoritePerformancesPreview(final Long userId) { - String sql = - "SELECT c.concert_id performanceId, :concertType type, c.concert_title title, c.concert_poster_path posterPath, c.concert_start_at startAt" + - " FROM concert_favorites cf INNER JOIN concerts c ON cf.concert_id = c.concert_id" + - " WHERE cf.user_id = :userId AND c.concert_end_at >= CURRENT_DATE" + - " UNION" + - " SELECT f.festival_id performanceId, :festivalType type, f.festival_title title, f.festival_poster_path posterPath, f.festival_start_at startAt" + - " FROM festival_favorites ff INNER JOIN festivals f ON ff.festival_id = f.festival_id" + - " WHERE ff.user_id = :userId AND f.festival_end_at >= CURRENT_DATE" + - " ORDER BY startAt ASC" + - " LIMIT :performancePreviewCount"; - - Query query = em.createNativeQuery(sql); - query.setParameter("userId", userId); - query.setParameter("concertType", PerformanceType.CONCERT.getType()); - query.setParameter("festivalType", PerformanceType.FESTIVAL.getType()); - query.setParameter("performancePreviewCount", PREVIEW_FAVORITE_PERFORMANCE_COUNT); - - return convertToPerformanceDTOs(query.getResultList()); - } - - private List convertToPerformanceDTOs(final List results) { - return results.stream() - .map(result -> PerformanceDTO.of( - ((Number) result[0]).longValue(), - (String) result[1], - (String) result[2], - (String) result[3] - )) - .toList(); - } - - public List findFavoritePerformancesReservation(final Long userId) { - String sql = """ - SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url - FROM ( - SELECT c.concert_id performance_id, :concertType type, c.concert_subtitle subtitle, c.reserve_at, c.concert_reservation_bg_path reservation_bg_url - FROM concert_favorites cf - JOIN concerts c ON cf.concert_id = c.concert_id - WHERE cf.user_id = :userId AND c.reserve_at >= CURRENT_DATE - UNION ALL - SELECT f.festival_id performance_id, :festivalType type, f.festival_subtitle subtitle, f.reserve_at, f.festival_reservation_bg_path reservation_bg_url - FROM festival_favorites ff - JOIN festivals f ON ff.festival_id = f.festival_id - WHERE ff.user_id = :userId AND f.reserve_at >= CURRENT_DATE - ) AS favorite_performances - ORDER BY reserve_at ASC - LIMIT :performanceReservationCount - """;; - - Query query = em.createNativeQuery(sql) - .setParameter("userId", userId) - .setParameter("concertType", PerformanceType.CONCERT.getType()) - .setParameter("festivalType", PerformanceType.FESTIVAL.getType()) - .setParameter("performanceReservationCount", RESERVE_FAVORITE_PERFORMANCE_COUNT); - - List results = query.getResultList(); - return convertToPerformanceTicketDTOs(results); - } - - public List findPerformancesReservation() { - String sql = """ - SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url - FROM ( - SELECT c.concert_id performance_id, :concertType type, c.concert_subtitle subtitle, c.reserve_at, c.concert_reservation_bg_path reservation_bg_url - FROM concerts c - WHERE c.reserve_at >= CURRENT_DATE - UNION ALL - SELECT f.festival_id performance_id, :festivalType type, f.festival_subtitle subtitle, f.reserve_at, f.festival_reservation_bg_path reservation_bg_url - FROM festivals f - WHERE f.reserve_at >= CURRENT_DATE - ) AS all_performances - ORDER BY reserve_at ASC - LIMIT :performanceReservationCount - """; - - Query query = em.createNativeQuery(sql) - .setParameter("concertType", PerformanceType.CONCERT.getType()) - .setParameter("festivalType", PerformanceType.FESTIVAL.getType()) - .setParameter("performanceReservationCount", RESERVE_FAVORITE_PERFORMANCE_COUNT); - - List results = query.getResultList(); - return convertToPerformanceTicketDTOs(results); - } - - private List convertToPerformanceTicketDTOs(List results) { - return results.stream() - .map(row -> PerformanceTicketDTO.of( - ((Number) row[0]).longValue(), - ((Number) row[1]).longValue(), - (String) row[2], - (String) row[3], - row[4] != null ? ((Timestamp) row[4]).toString() : null, - (String) row[5] - )) - .toList(); - } -} \ No newline at end of file +public interface PerformanceRepository extends JpaRepository { +} diff --git a/src/main/java/org/sopt/confeti/global/util/DateConvertor.java b/src/main/java/org/sopt/confeti/global/util/DateConvertor.java index b8a3297..eee2cca 100644 --- a/src/main/java/org/sopt/confeti/global/util/DateConvertor.java +++ b/src/main/java/org/sopt/confeti/global/util/DateConvertor.java @@ -36,6 +36,14 @@ public static String convert(final LocalDateTime localDateTime) { return localDateTime.format(localDateTimeFormat); } + public static String convertToLocalDate(final LocalDateTime localDateTime) { + if (localDateTime == null) { + return null; + } + + return localDateTime.format(localDateFormat); + } + public static LocalDate convertToLocalDate(final String localDate) { return LocalDate.parse(localDate, localDateFormat); } From bfb2bced451a0891e3054a123ff1661884f433db Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 01:39:33 +0900 Subject: [PATCH 82/96] =?UTF-8?q?fix=20[#143]=20=EA=B8=B0=EC=A1=B4=20DTO?= =?UTF-8?q?=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=9C=20Repository=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=84=20DTORepository=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/PerformanceDTORepository.java | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceDTORepository.java diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceDTORepository.java b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceDTORepository.java new file mode 100644 index 0000000..1659ddb --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceDTORepository.java @@ -0,0 +1,120 @@ +package org.sopt.confeti.domain.view.performance.infra.repository; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; + +import java.sql.Timestamp; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.domain.view.performance.PerformanceDTO; +import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; +import org.sopt.confeti.global.common.constant.PerformanceType; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class PerformanceDTORepository { + + private final EntityManager em; + + private static final int PREVIEW_FAVORITE_PERFORMANCE_COUNT = 3; + private static final int RESERVE_FAVORITE_PERFORMANCE_COUNT = 5; + + public List findFavoritePerformancesPreview(final Long userId) { + String sql = + "SELECT c.concert_id performanceId, :concertType type, c.concert_title title, c.concert_poster_path posterPath, c.concert_start_at startAt" + + " FROM concert_favorites cf INNER JOIN concerts c ON cf.concert_id = c.concert_id" + + " WHERE cf.user_id = :userId AND c.concert_end_at >= CURRENT_DATE" + + " UNION" + + " SELECT f.festival_id performanceId, :festivalType type, f.festival_title title, f.festival_poster_path posterPath, f.festival_start_at startAt" + + " FROM festival_favorites ff INNER JOIN festivals f ON ff.festival_id = f.festival_id" + + " WHERE ff.user_id = :userId AND f.festival_end_at >= CURRENT_DATE" + + " ORDER BY startAt ASC" + + " LIMIT :performancePreviewCount"; + + Query query = em.createNativeQuery(sql); + query.setParameter("userId", userId); + query.setParameter("concertType", PerformanceType.CONCERT.getType()); + query.setParameter("festivalType", PerformanceType.FESTIVAL.getType()); + query.setParameter("performancePreviewCount", PREVIEW_FAVORITE_PERFORMANCE_COUNT); + + return convertToPerformanceDTOs(query.getResultList()); + } + + private List convertToPerformanceDTOs(final List results) { + return results.stream() + .map(result -> PerformanceDTO.of( + ((Number) result[0]).longValue(), + (String) result[1], + (String) result[2], + (String) result[3] + )) + .toList(); + } + + public List findFavoritePerformancesReservation(final Long userId) { + String sql = """ + SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url + FROM ( + SELECT c.concert_id performance_id, :concertType type, c.concert_subtitle subtitle, c.reserve_at, c.concert_reservation_bg_path reservation_bg_url + FROM concert_favorites cf + JOIN concerts c ON cf.concert_id = c.concert_id + WHERE cf.user_id = :userId AND c.reserve_at >= CURRENT_DATE + UNION ALL + SELECT f.festival_id performance_id, :festivalType type, f.festival_subtitle subtitle, f.reserve_at, f.festival_reservation_bg_path reservation_bg_url + FROM festival_favorites ff + JOIN festivals f ON ff.festival_id = f.festival_id + WHERE ff.user_id = :userId AND f.reserve_at >= CURRENT_DATE + ) AS favorite_performances + ORDER BY reserve_at ASC + LIMIT :performanceReservationCount + """;; + + Query query = em.createNativeQuery(sql) + .setParameter("userId", userId) + .setParameter("concertType", PerformanceType.CONCERT.getType()) + .setParameter("festivalType", PerformanceType.FESTIVAL.getType()) + .setParameter("performanceReservationCount", RESERVE_FAVORITE_PERFORMANCE_COUNT); + + List results = query.getResultList(); + return convertToPerformanceTicketDTOs(results); + } + + public List findPerformancesReservation() { + String sql = """ + SELECT ROW_NUMBER() OVER (ORDER BY reserve_at ASC) AS ind, performance_id, type, subtitle, reserve_at, reservation_bg_url + FROM ( + SELECT c.concert_id performance_id, :concertType type, c.concert_subtitle subtitle, c.reserve_at, c.concert_reservation_bg_path reservation_bg_url + FROM concerts c + WHERE c.reserve_at >= CURRENT_DATE + UNION ALL + SELECT f.festival_id performance_id, :festivalType type, f.festival_subtitle subtitle, f.reserve_at, f.festival_reservation_bg_path reservation_bg_url + FROM festivals f + WHERE f.reserve_at >= CURRENT_DATE + ) AS all_performances + ORDER BY reserve_at ASC + LIMIT :performanceReservationCount + """; + + Query query = em.createNativeQuery(sql) + .setParameter("concertType", PerformanceType.CONCERT.getType()) + .setParameter("festivalType", PerformanceType.FESTIVAL.getType()) + .setParameter("performanceReservationCount", RESERVE_FAVORITE_PERFORMANCE_COUNT); + + List results = query.getResultList(); + return convertToPerformanceTicketDTOs(results); + } + + private List convertToPerformanceTicketDTOs(List results) { + return results.stream() + .map(row -> PerformanceTicketDTO.of( + ((Number) row[0]).intValue(), + ((Number) row[1]).longValue(), + (String) row[2], + (String) row[3], + row[4] != null ? ((Timestamp) row[4]).toString() : null, + (String) row[5] + )) + .toList(); + } +} \ No newline at end of file From 2af1ea00599d5ae4263d8c905bbfca34c27d0d6f Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 01:46:33 +0900 Subject: [PATCH 83/96] =?UTF-8?q?fix=20[#143]=20=EB=88=84=EB=9D=BD?= =?UTF-8?q?=EB=90=9C=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/api/performance/facade/PerformanceFacade.java | 6 +++--- .../view/performance/application/PerformanceService.java | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index 6a827fd..55d5aed 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -1,6 +1,6 @@ package org.sopt.confeti.api.performance.facade; -import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import lombok.RequiredArgsConstructor; @@ -45,7 +45,7 @@ public ConcertDetailDTO getConcertDetailInfo(final long concertId) { @Transactional(readOnly = true) protected void validateConcertNotPassed(final Concert concert) { - if (LocalDate.now().isAfter(concert.getConcertEndAt())) { + if (LocalDateTime.now().isAfter(concert.getConcertEndAt())) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } } @@ -76,7 +76,7 @@ protected boolean getIsFavorite(final Long userid, final long festivalId) { @Transactional(readOnly = true) protected void validateFestivalNotPassed(final Festival festival) { - if (LocalDate.now().isAfter(festival.getFestivalEndAt())) { + if (LocalDateTime.now().isAfter(festival.getFestivalEndAt())) { throw new NotFoundException(ErrorMessage.NOT_FOUND); } } diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java index 0147105..7dbc481 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.confeti.domain.view.performance.PerformanceDTO; import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; +import org.sopt.confeti.domain.view.performance.infra.repository.PerformanceDTORepository; import org.sopt.confeti.domain.view.performance.infra.repository.PerformanceRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -12,20 +13,21 @@ @RequiredArgsConstructor public class PerformanceService { + private final PerformanceDTORepository performanceDTORepository; private final PerformanceRepository performanceRepository; @Transactional(readOnly = true) public List getFavoritePerformancesPreview(final long userId) { - return performanceRepository.findFavoritePerformancesPreview(userId); + return performanceDTORepository.findFavoritePerformancesPreview(userId); } @Transactional(readOnly = true) public List getFavoritePerformancesReservation(final Long userId) { - return performanceRepository.findFavoritePerformancesReservation(userId); + return performanceDTORepository.findFavoritePerformancesReservation(userId); } @Transactional(readOnly = true) public List getPerformancesReservation() { - return performanceRepository.findPerformancesReservation(); + return performanceDTORepository.findPerformancesReservation(); } } \ No newline at end of file From ac7709424c86a121541fa7c3f5e97d8f0458965e Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 02:40:33 +0900 Subject: [PATCH 84/96] =?UTF-8?q?feat=20[#151]=20CREATED=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/sopt/confeti/global/message/SuccessMessage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java b/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java index 333f241..e584f62 100644 --- a/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java +++ b/src/main/java/org/sopt/confeti/global/message/SuccessMessage.java @@ -7,7 +7,8 @@ @Getter @AllArgsConstructor public enum SuccessMessage { - SUCCESS(HttpStatus.OK, "요청이 성공했습니다."),; + SUCCESS(HttpStatus.OK, "요청이 성공했습니다."), + CREATED(HttpStatus.CREATED, "생성되었습니다."),; private final HttpStatus httpStatus; private final String message; From 4d95fb5e43e649cb58d5995aecbc58f204fba1af Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 03:57:37 +0900 Subject: [PATCH 85/96] =?UTF-8?q?feat=20[#151]=20=ED=8E=98=EC=8A=A4?= =?UTF-8?q?=ED=8B=B0=EB=B2=8C=20=EC=83=9D=EC=84=B1=20API=EC=97=90=20Perfor?= =?UTF-8?q?mances=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=B6=94=EA=B0=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PerformanceController.java | 2 +- .../request/CreateFestivalDateRequest.java | 4 +-- .../dto/request/CreateFestivalRequest.java | 12 +++---- .../request/CreateFestivalTimeRequest.java | 4 +-- .../performance/facade/PerformanceFacade.java | 3 +- .../facade/dto/request/CreateFestivalDTO.java | 7 ++-- .../festival/application/FestivalService.java | 9 +++-- .../domain/view/performance/Performance.java | 36 +++++++++++++++++++ .../application/PerformanceService.java | 15 ++++++++ 9 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index f6d15af..3de1c27 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -54,7 +54,7 @@ public ResponseEntity> getFestivalInfo(@RequestHeader(name = "Au public ResponseEntity> createConcert(@RequestBody CreateFestivalRequest createFestivalRequest) { performanceFacade.createFestival(CreateFestivalDTO.from(createFestivalRequest)); - return ApiResponseUtil.success(SuccessMessage.SUCCESS); + return ApiResponseUtil.success(SuccessMessage.CREATED); } @GetMapping("/reservation") diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java index 62c05ba..44d6201 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalDateRequest.java @@ -7,10 +7,10 @@ import java.util.List; public record CreateFestivalDateRequest( - @JsonFormat(pattern = "yyyy.MM.dd") + @JsonFormat(pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") @JsonProperty(value = "festival_at") LocalDate festivalAt, - @JsonFormat(pattern = "HH:mm:ss") + @JsonFormat(pattern = "HH:mm:ss", timezone = "Asia/Seoul") @JsonProperty(value = "ticket_open_at") LocalTime ticketOpenAt, @JsonProperty(value = "festival_stages") diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java index 922ffe4..7633ed9 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalRequest.java @@ -11,12 +11,12 @@ public record CreateFestivalRequest( String festivalTitle, @JsonProperty(value = "festival_subtitle") String festivalSubtitle, - @JsonFormat(pattern = "yyyy.MM.dd") + @JsonFormat(pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") @JsonProperty(value = "festival_start_at") - LocalDateTime festivalStartAt, - @JsonFormat(pattern = "yyyy.MM.dd") + LocalDate festivalStartAt, + @JsonFormat(pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") @JsonProperty(value = "festival_end_at") - LocalDateTime festivalEndAt, + LocalDate festivalEndAt, @JsonProperty(value = "festival_area") String festivalArea, @JsonProperty(value = "festival_poster_path") @@ -29,9 +29,9 @@ public record CreateFestivalRequest( String festivalReservationBgPath, @JsonProperty(value = "festival_logo_path") String festivalLogoPath, - @JsonFormat(pattern = "yyyy.MM.dd") + @JsonFormat(pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") @JsonProperty(value = "reserve_at") - LocalDateTime reserveAt, + LocalDate reserveAt, @JsonProperty(value = "reservation_url") String reservationUrl, @JsonProperty(value = "reservation_office") diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java index 395618a..e5eef2c 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateFestivalTimeRequest.java @@ -6,10 +6,10 @@ import java.util.List; public record CreateFestivalTimeRequest( - @JsonFormat(pattern = "HH:mm:ss") + @JsonFormat(pattern = "HH:mm:ss", timezone = "Asia/Seoul") @JsonProperty(value = "start_at") LocalTime startAt, - @JsonFormat(pattern = "HH:mm:ss") + @JsonFormat(pattern = "HH:mm:ss", timezone = "Asia/Seoul") @JsonProperty(value = "end_at") LocalTime endAt, @JsonProperty(value = "festival_artists") diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index 55d5aed..a0383ba 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -52,7 +52,8 @@ protected void validateConcertNotPassed(final Concert concert) { @Transactional public void createFestival(final CreateFestivalDTO createFestivalDTO) { - festivalService.create(createFestivalDTO); + Festival festival = festivalService.create(createFestivalDTO); + performanceService.create(festival); } @Transactional(readOnly = true) diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java index 692f0e8..268684a 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateFestivalDTO.java @@ -1,6 +1,5 @@ package org.sopt.confeti.api.performance.facade.dto.request; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import org.sopt.confeti.api.performance.dto.request.CreateFestivalRequest; @@ -28,15 +27,15 @@ public static CreateFestivalDTO from(final CreateFestivalRequest createFestivalR return new CreateFestivalDTO( createFestivalRequest.festivalTitle(), createFestivalRequest.festivalSubtitle(), - createFestivalRequest.festivalStartAt(), - createFestivalRequest.festivalEndAt(), + createFestivalRequest.festivalStartAt().atStartOfDay(), + createFestivalRequest.festivalEndAt().atStartOfDay(), createFestivalRequest.festivalArea(), createFestivalRequest.festivalPosterPath(), createFestivalRequest.festivalPosterBgPath(), createFestivalRequest.festivalInfoImgPath(), createFestivalRequest.festivalReservationBgPath(), createFestivalRequest.festivalLogoPath(), - createFestivalRequest.reserveAt(), + createFestivalRequest.reserveAt().atStartOfDay(), createFestivalRequest.reservationUrl(), createFestivalRequest.reservationOffice(), createFestivalRequest.ageRating(), diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index 7b215dc..ff8a378 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -1,5 +1,6 @@ package org.sopt.confeti.domain.festival.application; +import jakarta.persistence.EntityManager; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -21,7 +22,8 @@ public class FestivalService { private final FestivalRepository festivalRepository; - private final ArtistResolver artistResolver; + private final ArtistResolver artistResolver; + private final EntityManager em; private static final int INIT_PAGE = 0; private static final String FESTIVAL_TITLE_COLUMN_NAME = "festivalTitle"; @@ -43,8 +45,9 @@ public Festival getFestivalDetailByFestivalId(final long festivalId) { } @Transactional - public void create(final CreateFestivalDTO createFestivalDTO) { - festivalRepository.save( + public Festival create(final CreateFestivalDTO createFestivalDTO) { + + return festivalRepository.save( Festival.create(createFestivalDTO) ); } diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/Performance.java b/src/main/java/org/sopt/confeti/domain/view/performance/Performance.java index cb7381c..7657860 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/Performance.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/Performance.java @@ -10,8 +10,12 @@ import jakarta.persistence.Table; import java.time.LocalDateTime; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; +import org.sopt.confeti.domain.festival.Festival; +import org.sopt.confeti.domain.user.User; import org.sopt.confeti.global.common.constant.PerformanceType; @Entity @@ -55,4 +59,36 @@ public class Performance { @Column(length = 250, nullable = false) private String reservationBgPath; + + @Builder + public Performance(long typeId, PerformanceType type, String artistId, String area, String title, + String subtitle, LocalDateTime startAt, LocalDateTime endAt, String posterPath, + String reservationBgPath) { + this.typeId = typeId; + this.type = type; + this.artistId = artistId; + this.area = area; + this.title = title; + this.subtitle = subtitle; + this.startAt = startAt; + this.endAt = endAt; + this.posterPath = posterPath; + this.reservationBgPath = reservationBgPath; + + } + + public static Performance create(final Festival festival, final String artistId) { + return Performance.builder() + .typeId(festival.getId()) + .type(PerformanceType.FESTIVAL) + .artistId(artistId) + .area(festival.getFestivalArea()) + .title(festival.getFestivalTitle()) + .subtitle(festival.getFestivalSubtitle()) + .startAt(festival.getFestivalStartAt()) + .endAt(festival.getFestivalEndAt()) + .posterPath(festival.getFestivalPosterPath()) + .reservationBgPath(festival.getFestivalReservationBgPath()) + .build(); + } } diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java index 7dbc481..2070bd0 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java @@ -2,6 +2,9 @@ import java.util.List; 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.view.performance.Performance; import org.sopt.confeti.domain.view.performance.PerformanceDTO; import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; import org.sopt.confeti.domain.view.performance.infra.repository.PerformanceDTORepository; @@ -30,4 +33,16 @@ public List getFavoritePerformancesReservation(final Long public List getPerformancesReservation() { return performanceDTORepository.findPerformancesReservation(); } + + @Transactional + public void create(final Festival festival) { + performanceRepository.saveAll( + festival.getDates().stream() + .flatMap(festivalDate -> festivalDate.getStages().stream()) + .flatMap(festivalStage -> festivalStage.getTimes().stream()) + .flatMap(festivalTime -> festivalTime.getArtists().stream()) + .map(artist -> Performance.create(festival, artist.getArtist().getArtistId())) + .toList() + ); + } } \ No newline at end of file From b360549d558f8a0ef838097055b3554c4dab9d7b Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 04:12:58 +0900 Subject: [PATCH 86/96] =?UTF-8?q?fix=20[#153]=20=EC=BD=98=EC=84=9C?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20API?= =?UTF-8?q?=EC=97=90=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EA=B0=80=20=EC=A0=84=EB=8B=AC=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EA=B2=83=EC=9D=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PerformanceController.java | 2 +- .../dto/response/ConcertDetailInfoResponse.java | 6 ++++-- .../api/performance/facade/PerformanceFacade.java | 13 +++++++++++-- .../facade/dto/response/ConcertDetailDTO.java | 8 +++++--- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index 3de1c27..de14b0b 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -39,7 +39,7 @@ public ResponseEntity> getConcertInfo( @RequestHeader(name = "Authorization", required = false) Long userId, @PathVariable("concertId") @Min(value = 0, message = "요청 형식이 올바르지 않습니다.") long concertId ) { - ConcertDetailDTO concertDetailDTO = performanceFacade.getConcertDetailInfo(concertId); + ConcertDetailDTO concertDetailDTO = performanceFacade.getConcertDetailInfo(userId, concertId); return ApiResponseUtil.success(SuccessMessage.SUCCESS, ConcertDetailResponse.of(concertDetailDTO, s3FileHandler)); } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java index afdfd08..d2563f8 100644 --- a/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/ConcertDetailInfoResponse.java @@ -21,7 +21,8 @@ public record ConcertDetailInfoResponse( String ageRating, String reservationOffice, String price, - String infoImgUrl + String infoImgUrl, + boolean isFavorite ) { public static ConcertDetailInfoResponse of(final ConcertDetailDTO concertDetailDTO, final S3FileHandler s3FileHandler) { return new ConcertDetailInfoResponse( @@ -39,7 +40,8 @@ public static ConcertDetailInfoResponse of(final ConcertDetailDTO concertDetailD concertDetailDTO.ageRating(), concertDetailDTO.reservationOffice(), concertDetailDTO.price(), - s3FileHandler.getFileUrl(concertDetailDTO.infoImgPath()) + s3FileHandler.getFileUrl(concertDetailDTO.infoImgPath()), + concertDetailDTO.isFavorite() ); } } diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index a0383ba..9393d7d 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -36,11 +36,20 @@ public class PerformanceFacade { private final ConcertFavoriteService concertFavoriteService; @Transactional(readOnly = true) - public ConcertDetailDTO getConcertDetailInfo(final long concertId) { + public ConcertDetailDTO getConcertDetailInfo(final Long userId, final long concertId) { Concert concert = concertService.getConcertDetailByConcertId(concertId); validateConcertNotPassed(concert); - return ConcertDetailDTO.from(concert); + return ConcertDetailDTO.of(concert, getConcertFavorite(userId, concertId)); + } + + @Transactional(readOnly = true) + public boolean getConcertFavorite(final Long userId, final long concertId) { + if (userId == null) { + return false; + } + + return concertFavoriteService.isFavorite(userId, concertId); } @Transactional(readOnly = true) diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java index a5e245e..7be0a6f 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/ConcertDetailDTO.java @@ -22,9 +22,10 @@ public record ConcertDetailDTO( String ageRating, String time, String price, - List artists + List artists, + boolean isFavorite ) { - public static ConcertDetailDTO from(final Concert concert) { + public static ConcertDetailDTO of(final Concert concert, final boolean isFavorite) { return new ConcertDetailDTO( concert.getId(), concert.getConcertTitle(), @@ -44,7 +45,8 @@ public static ConcertDetailDTO from(final Concert concert) { concert.getPrice(), concert.getArtists().stream() .map(ConcertArtistDTO::of) - .toList() + .toList(), + isFavorite ); } } From fdd574920c02745f8931cf70ed428d6256149e78 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Thu, 23 Jan 2025 04:12:45 +0900 Subject: [PATCH 87/96] =?UTF-8?q?feat=20[#144]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=8B=9C=EA=B0=84=ED=91=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20Request=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserTimetableController.java | 22 +++++++++++-------- .../request/PatchTimetableListRequest.java | 10 +++++++++ .../dto/request/PatchTimetableRequest.java | 8 +++++++ 3 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/request/PatchTimetableListRequest.java create mode 100644 src/main/java/org/sopt/confeti/api/user/dto/request/PatchTimetableRequest.java diff --git a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java index 6bd7d77..4e57ad3 100644 --- a/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java +++ b/src/main/java/org/sopt/confeti/api/user/controller/UserTimetableController.java @@ -3,14 +3,17 @@ import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; import org.sopt.confeti.api.user.dto.request.AddTimetableFestivalRequest; +import org.sopt.confeti.api.user.dto.request.PatchTimetableRequest; import org.sopt.confeti.api.user.dto.response.TimetablesToAddResponse; import org.sopt.confeti.api.user.dto.response.UserTimetableDetailResponse; import org.sopt.confeti.api.user.dto.response.UserTimetableFestivalResponse; 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.request.PatchTimetableDTO; import org.sopt.confeti.api.user.facade.dto.response.TimetableToAddDTO; import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO; import org.sopt.confeti.api.user.facade.dto.response.UserTimetableFestivalBasicDTO; +import org.sopt.confeti.api.user.facade.dto.response.UserTimetableFestivalDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.common.CursorPage; import org.sopt.confeti.global.message.SuccessMessage; @@ -18,15 +21,7 @@ import org.sopt.confeti.global.util.S3FileHandler; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; -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.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @Validated @@ -78,4 +73,13 @@ public ResponseEntity> getTimetableFestival( UserTimetableFestivalBasicDTO response = userTimetableFacade.getTimetableInfo(userId, festivalDateId); return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserTimetableFestivalResponse.from(response)); } + + @PatchMapping + public ResponseEntity> updateTimetableFestival( + @RequestHeader("Authorization") long userId, + @RequestBody PatchTimetableRequest patchTimetablerequest + ){ + userTimetableFacade.patchTimetableFestivals(userId, PatchTimetableDTO.from(patchTimetablerequest)); + return ApiResponseUtil.success(SuccessMessage.SUCCESS); + } } diff --git a/src/main/java/org/sopt/confeti/api/user/dto/request/PatchTimetableListRequest.java b/src/main/java/org/sopt/confeti/api/user/dto/request/PatchTimetableListRequest.java new file mode 100644 index 0000000..3182f7d --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/request/PatchTimetableListRequest.java @@ -0,0 +1,10 @@ +package org.sopt.confeti.api.user.dto.request; + +public record PatchTimetableListRequest ( + long userTimetableId, + boolean isSelected +){ + public static PatchTimetableListRequest from(PatchTimetableListRequest request) { + return new PatchTimetableListRequest(request.userTimetableId(), request.isSelected()); + } +} diff --git a/src/main/java/org/sopt/confeti/api/user/dto/request/PatchTimetableRequest.java b/src/main/java/org/sopt/confeti/api/user/dto/request/PatchTimetableRequest.java new file mode 100644 index 0000000..19dd17b --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/dto/request/PatchTimetableRequest.java @@ -0,0 +1,8 @@ +package org.sopt.confeti.api.user.dto.request; + +import java.util.List; + +public record PatchTimetableRequest ( + List userTimetables +){ +} \ No newline at end of file From 88931b494d6d119e2800a64a94fdf04c2136c499 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Thu, 23 Jan 2025 04:12:55 +0900 Subject: [PATCH 88/96] =?UTF-8?q?feat=20[#144]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=8B=9C=EA=B0=84=ED=91=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20DTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facade/dto/request/PatchTimetableDTO.java | 19 +++++++++++++++++++ .../dto/request/PatchTimetableListDTO.java | 16 ++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/request/PatchTimetableDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/user/facade/dto/request/PatchTimetableListDTO.java diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/request/PatchTimetableDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/request/PatchTimetableDTO.java new file mode 100644 index 0000000..471ea8e --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/request/PatchTimetableDTO.java @@ -0,0 +1,19 @@ +package org.sopt.confeti.api.user.facade.dto.request; + +import org.sopt.confeti.api.user.dto.request.PatchTimetableRequest; + +import java.util.List; + +public record PatchTimetableDTO ( + List userTimetables +) { + public static PatchTimetableDTO from(PatchTimetableRequest timetableRequest) { + return new PatchTimetableDTO( + timetableRequest.userTimetables() + .stream() + .map(PatchTimetableListDTO::from) + .toList() + ); + } +} + diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/request/PatchTimetableListDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/request/PatchTimetableListDTO.java new file mode 100644 index 0000000..74d537a --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/request/PatchTimetableListDTO.java @@ -0,0 +1,16 @@ +package org.sopt.confeti.api.user.facade.dto.request; + +import org.sopt.confeti.api.user.dto.request.PatchTimetableListRequest; + +public record PatchTimetableListDTO ( + long userTimetableId, + boolean isSelected +){ + public static PatchTimetableListDTO from(final PatchTimetableListRequest patchTimetableListRequest) { + return new PatchTimetableListDTO( + patchTimetableListRequest.userTimetableId(), + patchTimetableListRequest.isSelected() + ); + } +} + From 8b231fbabd5cfa14a227312242bb37ef45523b43 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Thu, 23 Jan 2025 04:13:08 +0900 Subject: [PATCH 89/96] =?UTF-8?q?feat=20[#144]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=8B=9C=EA=B0=84=ED=91=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20facade=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user/facade/UserTimetableFacade.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 93f2cf6..d9785bf 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -6,6 +6,8 @@ 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.request.PatchTimetableDTO; +import org.sopt.confeti.api.user.facade.dto.request.PatchTimetableListDTO; import org.sopt.confeti.api.user.facade.dto.response.TimetableToAddDTO; import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO; import org.sopt.confeti.api.user.facade.dto.response.UserTimetableFestivalBasicDTO; @@ -14,20 +16,17 @@ import org.sopt.confeti.domain.festivaldate.FestivalDate; import org.sopt.confeti.domain.festivaldate.application.FestivalDateService; import org.sopt.confeti.domain.festival.application.dto.FestivalCursorDTO; -import org.sopt.confeti.domain.festivaldate.FestivalDate; -import org.sopt.confeti.domain.festivaldate.application.FestivalDateService; 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.domain.usertimetable.application.UserTimetableService; 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; import org.sopt.confeti.global.message.ErrorMessage; import org.sopt.confeti.global.util.artistsearcher.ArtistResolver; -import org.sopt.confeti.global.util.artistsearcher.ArtistResolver; -import org.springframework.data.domain.PageRequest; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -45,8 +44,8 @@ public class UserTimetableFacade { private final FestivalService festivalService; private final FestivalDateService festivalDateService; private final ArtistResolver artistResolver; + private final UserTimetableService userTimetableService; - @Transactional(readOnly = true) public UserTimetableDTO getTimetablesListAndDate(long userId) { validateExistUser(userId); @@ -171,5 +170,11 @@ public FestivalCursorDTO getFestivalCursor(final long userId, final long cursor) () -> new NotFoundException(ErrorMessage.NOT_FOUND) ); } + + @Transactional + public void patchTimetableFestivals(final long userId, final PatchTimetableDTO timetableDTO) { + validateUserExists(userId); + userTimetableService.patchTimetableFestival(userId, timetableDTO); + } } From 2d76f65070c7703e25adf7520b52c61088a4823a Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Thu, 23 Jan 2025 04:13:41 +0900 Subject: [PATCH 90/96] =?UTF-8?q?feat=20[#144]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=8B=9C=EA=B0=84=ED=91=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/usertimetable/UserTimetable.java | 5 +++ .../application/UserTimetableService.java | 44 +++++++++++++++++++ .../repository/UserTimetableRepository.java | 7 +++ 3 files changed, 56 insertions(+) create mode 100644 src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java diff --git a/src/main/java/org/sopt/confeti/domain/usertimetable/UserTimetable.java b/src/main/java/org/sopt/confeti/domain/usertimetable/UserTimetable.java index e51e8cc..a4ff5dd 100644 --- a/src/main/java/org/sopt/confeti/domain/usertimetable/UserTimetable.java +++ b/src/main/java/org/sopt/confeti/domain/usertimetable/UserTimetable.java @@ -44,4 +44,9 @@ public static UserTimetable create(TimetableFestival timetableFestival, Festival .isSelected(isSelected) .build(); } + + public void setIsSelected(boolean isSelected) { + this.isSelected = isSelected; + } + } diff --git a/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java b/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java new file mode 100644 index 0000000..d7b5f66 --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java @@ -0,0 +1,44 @@ +package org.sopt.confeti.domain.usertimetable.application; + +import lombok.RequiredArgsConstructor; +import org.sopt.confeti.api.user.facade.dto.request.PatchTimetableDTO; +import org.sopt.confeti.api.user.facade.dto.request.PatchTimetableListDTO; +import org.sopt.confeti.domain.usertimetable.UserTimetable; +import org.sopt.confeti.domain.usertimetable.infra.repository.UserTimetableRepository; +import org.sopt.confeti.global.exception.NotFoundException; +import org.sopt.confeti.global.message.ErrorMessage; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class UserTimetableService { + + private final UserTimetableRepository userTimetableRepository; + + @Transactional + public void patchTimetableFestival(long userId, PatchTimetableDTO timetableDTO) { + List userTimetables = userTimetableRepository.findByUserId(userId); + + if (userTimetables.isEmpty()) { + throw new NotFoundException(ErrorMessage.NOT_FOUND); + } + + Map updateMap = timetableDTO.userTimetables() + .stream() + .collect(Collectors.toMap(PatchTimetableListDTO::userTimetableId, PatchTimetableListDTO::isSelected)); + + for (UserTimetable timetable : userTimetables) { + Boolean isSelected = updateMap.get(timetable.getId()); + if (isSelected != null) { + timetable.setIsSelected(isSelected); + } + } + + userTimetableRepository.saveAll(userTimetables); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/usertimetable/infra/repository/UserTimetableRepository.java b/src/main/java/org/sopt/confeti/domain/usertimetable/infra/repository/UserTimetableRepository.java index 1678858..d2febb9 100644 --- a/src/main/java/org/sopt/confeti/domain/usertimetable/infra/repository/UserTimetableRepository.java +++ b/src/main/java/org/sopt/confeti/domain/usertimetable/infra/repository/UserTimetableRepository.java @@ -2,6 +2,13 @@ import org.sopt.confeti.domain.usertimetable.UserTimetable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; public interface UserTimetableRepository extends JpaRepository { + + @Query("SELECT ut FROM UserTimetable ut WHERE ut.timetableFestival.user.id = :userId") + List findByUserId(@Param("userId") long userId); } From 2ea28322589d8fdeda9e167a20c3b0816add8e45 Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Thu, 23 Jan 2025 04:26:22 +0900 Subject: [PATCH 91/96] =?UTF-8?q?refactor=20[#144]=20saveAll=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/usertimetable/application/UserTimetableService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java b/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java index d7b5f66..f211e68 100644 --- a/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java +++ b/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java @@ -38,7 +38,5 @@ public void patchTimetableFestival(long userId, PatchTimetableDTO timetableDTO) timetable.setIsSelected(isSelected); } } - - userTimetableRepository.saveAll(userTimetables); } } From f72c6e0ef6a5c6c39953e5b88caeb76f74dbc53e Mon Sep 17 00:00:00 2001 From: ivoryeee Date: Thu, 23 Jan 2025 06:11:22 +0900 Subject: [PATCH 92/96] =?UTF-8?q?refactor=20[#156]=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=8B=9C=EA=B0=84=ED=91=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=9D=91=EB=8B=B5=EA=B0=92=20userTimetabl?= =?UTF-8?q?eId=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserTimetableFestivalTimeResponse.java | 4 ++-- .../api/user/facade/UserTimetableFacade.java | 19 +++++++++++++++++-- .../UserTimetableFestivalBasicDTO.java | 6 ++++-- .../UserTimetableFestivalStageDTO.java | 6 ++++-- .../UserTimetableFestivalTimeDTO.java | 11 +++++------ .../application/FestivalDateService.java | 4 ++-- .../repository/FestivalDateRepository.java | 6 +----- .../application/UserTimetableService.java | 5 +++++ .../repository/UserTimetableRepository.java | 9 +++++++++ 9 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalTimeResponse.java b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalTimeResponse.java index d60cd36..941ec94 100644 --- a/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalTimeResponse.java +++ b/src/main/java/org/sopt/confeti/api/user/dto/response/UserTimetableFestivalTimeResponse.java @@ -6,7 +6,7 @@ import java.util.List; public record UserTimetableFestivalTimeResponse( - Long festivalTimeId, + Long userTimetableId, String startAt, String endAt, Boolean isSelected, @@ -14,7 +14,7 @@ public record UserTimetableFestivalTimeResponse( ) { public static UserTimetableFestivalTimeResponse from(UserTimetableFestivalTimeDTO festivalTime) { return new UserTimetableFestivalTimeResponse( - festivalTime.festivalTimeId(), + festivalTime.userTimetableId(), DateConvertor.convert(festivalTime.startAt()), DateConvertor.convert(festivalTime.endAt()), festivalTime.isSelected(), diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index d9785bf..69bfd2c 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -1,6 +1,8 @@ package org.sopt.confeti.api.user.facade; +import java.util.Map; import java.util.Optional; +import java.util.function.Function; import java.util.function.Predicate; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; @@ -16,10 +18,12 @@ import org.sopt.confeti.domain.festivaldate.FestivalDate; import org.sopt.confeti.domain.festivaldate.application.FestivalDateService; import org.sopt.confeti.domain.festival.application.dto.FestivalCursorDTO; +import org.sopt.confeti.domain.festivaltime.FestivalTime; 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.domain.usertimetable.UserTimetable; import org.sopt.confeti.domain.usertimetable.application.UserTimetableService; import org.sopt.confeti.global.common.CursorPage; import org.sopt.confeti.global.exception.ConflictException; @@ -30,6 +34,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.stream.Collectors; @Facade @RequiredArgsConstructor @@ -150,10 +155,20 @@ public CursorPage getTimetablesToAdd(final long userId, final public UserTimetableFestivalBasicDTO getTimetableInfo(final long userId, final long festivalDateId) { validateUserExists(userId); - FestivalDate festivalDate = festivalDateService.findFestivalDateId(userId, festivalDateId); + FestivalDate festivalDate = festivalDateService.findFestivalDateId(festivalDateId); artistResolver.load(festivalDate); - return UserTimetableFestivalBasicDTO.from(festivalDate); + List festivalTimeIds = festivalDate.getStages().stream() + .flatMap(festivalStage -> festivalStage.getTimes().stream()) + .map(FestivalTime::getId) + .toList(); + + List userTimetables = userTimetableService.getUserTimetables(userId, festivalTimeIds); + + Map userTimetableMapper = userTimetables.stream() + .collect(Collectors.toMap(UserTimetable::getId, Function.identity())); + + return UserTimetableFestivalBasicDTO.of(festivalDate, userTimetableMapper); } @Transactional(readOnly = true) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalBasicDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalBasicDTO.java index acfac35..0c3a10a 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalBasicDTO.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalBasicDTO.java @@ -1,17 +1,19 @@ package org.sopt.confeti.api.user.facade.dto.response; import org.sopt.confeti.domain.festivaldate.FestivalDate; +import org.sopt.confeti.domain.usertimetable.UserTimetable; import java.time.LocalTime; import java.util.List; +import java.util.Map; public record UserTimetableFestivalBasicDTO(LocalTime ticketOpenAt, List stages) { - public static UserTimetableFestivalBasicDTO from(FestivalDate festivalDate) { + public static UserTimetableFestivalBasicDTO of(FestivalDate festivalDate, Map userTimetables ) { return new UserTimetableFestivalBasicDTO( festivalDate.getOpenAt(), festivalDate.getStages() .stream() - .map(UserTimetableFestivalStageDTO::from) + .map(stages -> UserTimetableFestivalStageDTO.of(stages, userTimetables)) .toList() ); } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalStageDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalStageDTO.java index b9ab5af..ca131d1 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalStageDTO.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalStageDTO.java @@ -1,22 +1,24 @@ package org.sopt.confeti.api.user.facade.dto.response; import org.sopt.confeti.domain.festivalstage.FestivalStage; +import org.sopt.confeti.domain.usertimetable.UserTimetable; import java.util.List; +import java.util.Map; public record UserTimetableFestivalStageDTO ( int stageOrder, String stageName, List festivalTimes ){ - public static UserTimetableFestivalStageDTO from(FestivalStage festivalStage + public static UserTimetableFestivalStageDTO of(FestivalStage festivalStage, Map userTimetables ) { return new UserTimetableFestivalStageDTO( festivalStage.getOrder(), festivalStage.getName(), festivalStage.getTimes() .stream() - .map(UserTimetableFestivalTimeDTO::from) + .map(time -> UserTimetableFestivalTimeDTO.of(time, userTimetables)) .toList() ); } diff --git a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalTimeDTO.java b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalTimeDTO.java index 9d4affd..a02c2a4 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalTimeDTO.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/dto/response/UserTimetableFestivalTimeDTO.java @@ -5,23 +5,22 @@ import java.time.LocalTime; import java.util.List; +import java.util.Map; public record UserTimetableFestivalTimeDTO ( - long festivalTimeId, + long userTimetableId, LocalTime startAt, LocalTime endAt, boolean isSelected, List artists ){ - public static UserTimetableFestivalTimeDTO from(FestivalTime festivalTime) { + public static UserTimetableFestivalTimeDTO of(FestivalTime festivalTime, Map userTimetables) { return new UserTimetableFestivalTimeDTO( - festivalTime.getId(), + userTimetables.get(festivalTime.getId()).getId(), festivalTime.getStartAt(), festivalTime.getEndAt(), - festivalTime.getTimetables() - .stream() - .anyMatch(UserTimetable::isSelected), + userTimetables.get(festivalTime.getId()).isSelected(), festivalTime.getArtists() .stream() .map(UserTimetableFestivalArtistDTO::from) diff --git a/src/main/java/org/sopt/confeti/domain/festivaldate/application/FestivalDateService.java b/src/main/java/org/sopt/confeti/domain/festivaldate/application/FestivalDateService.java index 17e6993..48bb6dd 100644 --- a/src/main/java/org/sopt/confeti/domain/festivaldate/application/FestivalDateService.java +++ b/src/main/java/org/sopt/confeti/domain/festivaldate/application/FestivalDateService.java @@ -18,8 +18,8 @@ public class FestivalDateService { private final FestivalDateRepository festivalDateRepository; @Transactional(readOnly = true) - public FestivalDate findFestivalDateId(final long userId, final long festivalDateId) { - return festivalDateRepository.findByFestivalDateId(userId, festivalDateId) + public FestivalDate findFestivalDateId(final long festivalDateId) { + return festivalDateRepository.findByFestivalDateId(festivalDateId) .orElseThrow( () -> new NotFoundException(ErrorMessage.NOT_FOUND) ); diff --git a/src/main/java/org/sopt/confeti/domain/festivaldate/infra/repository/FestivalDateRepository.java b/src/main/java/org/sopt/confeti/domain/festivaldate/infra/repository/FestivalDateRepository.java index 3cf6479..180aa60 100644 --- a/src/main/java/org/sopt/confeti/domain/festivaldate/infra/repository/FestivalDateRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festivaldate/infra/repository/FestivalDateRepository.java @@ -13,12 +13,8 @@ public interface FestivalDateRepository extends JpaRepository findByFestivalDateId( - @Param("userId") long userId, @Param("festivalDateId") long festivalDateId); } diff --git a/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java b/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java index f211e68..4b5fac2 100644 --- a/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java +++ b/src/main/java/org/sopt/confeti/domain/usertimetable/application/UserTimetableService.java @@ -39,4 +39,9 @@ public void patchTimetableFestival(long userId, PatchTimetableDTO timetableDTO) } } } + + @Transactional(readOnly = true) + public List getUserTimetables(final long userId, final List festivalTimeIds) { + return userTimetableRepository.findByUserIdAndFestivalTimeIds(userId, festivalTimeIds); + } } diff --git a/src/main/java/org/sopt/confeti/domain/usertimetable/infra/repository/UserTimetableRepository.java b/src/main/java/org/sopt/confeti/domain/usertimetable/infra/repository/UserTimetableRepository.java index d2febb9..92d00eb 100644 --- a/src/main/java/org/sopt/confeti/domain/usertimetable/infra/repository/UserTimetableRepository.java +++ b/src/main/java/org/sopt/confeti/domain/usertimetable/infra/repository/UserTimetableRepository.java @@ -11,4 +11,13 @@ public interface UserTimetableRepository extends JpaRepository findByUserId(@Param("userId") long userId); + + @Query(value = + "SELECT DISTINCT ut" + + " FROM UserTimetable ut" + + " JOIN FETCH ut.timetableFestival tf" + + " WHERE tf.user.id = :userId" + + " AND ut.festivalTime.id IN :festivalTimeIds" + ) + List findByUserIdAndFestivalTimeIds(final long userId, final List festivalTimeIds); } From 1d0c61589e36286b07170118a30d60ad478c4cee Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 15:37:01 +0900 Subject: [PATCH 93/96] =?UTF-8?q?refactor=20[#159]=20=EC=8A=A4=ED=8F=AC?= =?UTF-8?q?=ED=8B=B0=ED=8C=8C=EC=9D=B4=EC=97=90=20=EC=A1=B4=EC=9E=AC?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=95=84=ED=8B=B0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20404=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confeti/api/artist/facade/ArtistFacade.java | 11 ++++------- .../util/artistsearcher/SpotifyAPIHandler.java | 17 ++++++----------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/sopt/confeti/api/artist/facade/ArtistFacade.java b/src/main/java/org/sopt/confeti/api/artist/facade/ArtistFacade.java index 3cc2c60..fb2dfc5 100644 --- a/src/main/java/org/sopt/confeti/api/artist/facade/ArtistFacade.java +++ b/src/main/java/org/sopt/confeti/api/artist/facade/ArtistFacade.java @@ -18,17 +18,14 @@ public class ArtistFacade { @Transactional(readOnly = true) public SearchArtistDTO searchByKeyword(final Long userId, final String keyword) { - Optional confetiArtist = spotifyAPIHandler.findArtistsByKeyword(keyword); + ConfetiArtist confetiArtist = spotifyAPIHandler.findArtistsByKeyword(keyword); boolean isFavorite = false; - if (confetiArtist.isPresent() && userId != null) { - isFavorite = artistFavoriteService.isFavorite(userId, confetiArtist.get().getArtistId()); + if (userId != null) { + isFavorite = artistFavoriteService.isFavorite(userId, confetiArtist.getArtistId()); } - return SearchArtistDTO.from( - confetiArtist.orElse(ConfetiArtist.empty()), - isFavorite - ); + return SearchArtistDTO.from(confetiArtist, isFavorite); } } diff --git a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java index 243ab87..30ae651 100644 --- a/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java +++ b/src/main/java/org/sopt/confeti/global/util/artistsearcher/SpotifyAPIHandler.java @@ -54,18 +54,13 @@ public void init() { refreshCount = REFRESH_INIT_VALUE; } - public Optional findArtistsByKeyword(final String keyword) { - Optional searchedArtist = searchArtistByKeyword(keyword); - - if (searchedArtist.isEmpty()) { - return Optional.empty(); - } - - Artist artist = searchedArtist.get(); + public ConfetiArtist findArtistsByKeyword(final String keyword) { + Artist artist = searchArtistByKeyword(keyword) + .orElseThrow( + () -> new ConfetiException(ErrorMessage.NOT_FOUND) + ); - return Optional.of( - ConfetiArtist.toConfetiArtist(artist, findLatestReleaseAt(artist.getId())) - ); + return ConfetiArtist.toConfetiArtist(artist, findLatestReleaseAt(artist.getId())); } public Optional findArtistByArtistId(final String artistId) { From 12ccd85b5191d26bf35fe9ba045b862ceef5d07c Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 17:05:24 +0900 Subject: [PATCH 94/96] =?UTF-8?q?fix=20[#162]=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=EA=B0=80=20=EC=9E=88=EC=9D=84=20=EB=95=8C=20409=20?= =?UTF-8?q?=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/confeti/api/user/facade/UserFavoriteFacade.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java index 1efc0bd..f4f8985 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserFavoriteFacade.java @@ -123,7 +123,7 @@ public void addConcertFavorite(final long userId, final long concertId) { User user = userService.findById(userId); Concert concert = concertService.findById(concertId); - validateExistConcertFavorite(userId, concertId); + validateNotExistConcertFavorite(userId, concertId); concertFavoriteService.addFavorite(user, concert); } @@ -152,6 +152,13 @@ protected void validateExistConcertFavorite(final long userId, final long concer } } + @Transactional(readOnly = true) + protected void validateNotExistConcertFavorite(final long userId, final long concertId) { + if (concertFavoriteService.isFavorite(userId, concertId)) { + throw new ConflictException(ErrorMessage.CONFLICT); + } + } + @Transactional(readOnly = true) protected void validateExistUser(final long userId) { if (!userService.existsById(userId)) { From 474c52446e99923685230e60ee31d8dcd8895598 Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 17:13:10 +0900 Subject: [PATCH 95/96] =?UTF-8?q?feat=20[#136]=20=EC=B5=9C=EC=8B=A0=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=88=9C=20=EA=B3=B5=EC=97=B0=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20(#1?= =?UTF-8?q?58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat [#136] 최신 공연 정보 DTO 작성 * feat [#136] 최신 공연 조회 함수 추가 * feat [#136] 최신 공연 조회 함수 구현 * feat [#136] 콘서트/페스티벌 아이디와 타입으로 공연 아이디 요청/응답 DTO 작성 * feat [#136] 파사드 계층에서 컨트롤러 계층 최신 공연 값 DTO 작성 * feat [#136] 콘서트/페스티벌 아이디, 공연 타입으로 공연 아이디 조회하는 로직 구현 * style [#136] 사용하지 않는 파일 삭제 * feat [#136] 최신 공연 정보 조회 응답 DTO 작성 * feat [#136] 파사드 계층 최신 공연 정보 조회 데이터 DTO 수정 * feat [#136] 좋아요 정보 없을 때 최신 공연 정보 조회 구현 * feat [#136] 좋아요 정보 있는 때 최신 공연 정보 조회 구현 * refactor [#156] 타임테이블 시간표 조회 응답값 userTimetableId로 수정 * feat [#136] 최신 공연 정보 DTO 작성 * feat [#136] 최신 공연 조회 함수 추가 * feat [#136] 최신 공연 조회 함수 구현 * feat [#136] 콘서트/페스티벌 아이디와 타입으로 공연 아이디 요청/응답 DTO 작성 * feat [#136] 파사드 계층에서 컨트롤러 계층 최신 공연 값 DTO 작성 * feat [#136] 콘서트/페스티벌 아이디, 공연 타입으로 공연 아이디 조회하는 로직 구현 * style [#136] 사용하지 않는 파일 삭제 * feat [#136] 최신 공연 정보 조회 응답 DTO 작성 * feat [#136] 파사드 계층 최신 공연 정보 조회 데이터 DTO 수정 * feat [#136] 좋아요 정보 없을 때 최신 공연 정보 조회 구현 * feat [#136] 좋아요 정보 있는 때 최신 공연 정보 조회 구현 * refactor [#159] 스포티파이에 존재하지 않는 아티스트인 경우 404 응답 처리 * feat [#136] 최신 공연 정보 DTO 작성 * feat [#136] 최신 공연 조회 함수 추가 * feat [#136] 최신 공연 조회 함수 구현 * feat [#136] 콘서트/페스티벌 아이디와 타입으로 공연 아이디 요청/응답 DTO 작성 * feat [#136] 파사드 계층에서 컨트롤러 계층 최신 공연 값 DTO 작성 * feat [#136] 콘서트/페스티벌 아이디, 공연 타입으로 공연 아이디 조회하는 로직 구현 * style [#136] 사용하지 않는 파일 삭제 * feat [#136] 최신 공연 정보 조회 응답 DTO 작성 * feat [#136] 파사드 계층 최신 공연 정보 조회 데이터 DTO 수정 * feat [#136] 좋아요 정보 없을 때 최신 공연 정보 조회 구현 * feat [#136] 좋아요 정보 있는 때 최신 공연 정보 조회 구현 * feat [#136] 최신 공연 조회 함수 구현 * feat [#136] 파사드 계층에서 컨트롤러 계층 최신 공연 값 DTO 작성 * feat [#136] 콘서트/페스티벌 아이디, 공연 타입으로 공연 아이디 조회하는 로직 구현 * feat [#136] 파사드 계층 최신 공연 정보 조회 데이터 DTO 수정 * feat [#136] 좋아요 정보 없을 때 최신 공연 정보 조회 구현 * feat [#136] 좋아요 정보 있는 때 최신 공연 정보 조회 구현 --------- Co-authored-by: ivoryeee --- .../controller/PerformanceController.java | 10 ++++ .../response/RecentPerformanceResponse.java | 27 ++++++++++ .../response/RecentPerformancesResponse.java | 17 ++++++ .../performance/facade/PerformanceFacade.java | 53 +++++++++++++++++++ .../dto/response/RecentPerformanceDTO.java | 53 +++++++++++++++++++ .../dto/response/RecentPerformancesDTO.java | 34 ++++++++++++ .../application/ArtistFavoriteService.java | 5 ++ .../repository/ArtistFavoriteRepository.java | 2 + .../concert/application/ConcertService.java | 26 +++++++++ .../infra/repository/ConcertRepository.java | 5 ++ .../festival/application/FestivalService.java | 26 +++++++-- .../infra/repository/FestivalRepository.java | 3 ++ .../application/PerformanceService.java | 27 +++++++++- .../application/dto/request/.gitkeep | 0 .../dto/request/GetPerformanceIdRequest.java | 24 +++++++++ .../application/dto/response/.gitkeep | 0 .../response/GetPerformanceIdResponse.java | 18 +++++++ .../repository/PerformanceRepository.java | 9 ++++ 18 files changed, 334 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/RecentPerformanceResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/dto/response/RecentPerformancesResponse.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/RecentPerformanceDTO.java create mode 100644 src/main/java/org/sopt/confeti/api/performance/facade/dto/response/RecentPerformancesDTO.java delete mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/GetPerformanceIdRequest.java delete mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/.gitkeep create mode 100644 src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/GetPerformanceIdResponse.java diff --git a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java index de14b0b..13d6124 100644 --- a/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java +++ b/src/main/java/org/sopt/confeti/api/performance/controller/PerformanceController.java @@ -5,12 +5,14 @@ import org.sopt.confeti.api.performance.dto.request.CreateFestivalRequest; import org.sopt.confeti.api.performance.dto.response.ConcertDetailResponse; import org.sopt.confeti.api.performance.dto.response.FestivalDetailResponse; +import org.sopt.confeti.api.performance.dto.response.RecentPerformancesResponse; import org.sopt.confeti.api.performance.facade.PerformanceFacade; import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; import org.sopt.confeti.api.performance.dto.response.PerformanceReservationResponse; import org.sopt.confeti.api.performance.facade.dto.response.PerformanceReservationDTO; +import org.sopt.confeti.api.performance.facade.dto.response.RecentPerformancesDTO; import org.sopt.confeti.global.common.BaseResponse; import org.sopt.confeti.global.message.SuccessMessage; import org.sopt.confeti.global.util.ApiResponseUtil; @@ -63,4 +65,12 @@ public ResponseEntity> getPerformReservationInfo(@RequestHeader( PerformanceReservationDTO performanceReservationDTO = performanceFacade.getPerformReservationInfo(userId); return ApiResponseUtil.success(SuccessMessage.SUCCESS, PerformanceReservationResponse.of(performanceReservationDTO, s3FileHandler)); } + + @GetMapping("/info") + public ResponseEntity> getRecentPerformances( + @RequestHeader(name = "Authorization", required = false) Long userId + ) { + RecentPerformancesDTO recentPerformances = performanceFacade.getRecentPerformances(userId); + return ApiResponseUtil.success(SuccessMessage.SUCCESS, RecentPerformancesResponse.of(recentPerformances, s3FileHandler)); + } } diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/RecentPerformanceResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/RecentPerformanceResponse.java new file mode 100644 index 0000000..20f92cf --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/RecentPerformanceResponse.java @@ -0,0 +1,27 @@ +package org.sopt.confeti.api.performance.dto.response; + +import org.sopt.confeti.api.performance.facade.dto.response.RecentPerformanceDTO; +import org.sopt.confeti.global.util.DateConvertor; +import org.sopt.confeti.global.util.S3FileHandler; + +public record RecentPerformanceResponse( + long performanceId, + long typeId, + String type, + String title, + String subtitle, + String performanceAt, + String posterUrl +) { + public static RecentPerformanceResponse of(final RecentPerformanceDTO recentPerformanceDTO, final S3FileHandler s3FileHandler) { + return new RecentPerformanceResponse( + recentPerformanceDTO.performanceId(), + recentPerformanceDTO.typeId(), + recentPerformanceDTO.type().getType(), + recentPerformanceDTO.title(), + recentPerformanceDTO.subtitle(), + DateConvertor.convertToLocalDate(recentPerformanceDTO.performanceAt()), + s3FileHandler.getFileUrl(recentPerformanceDTO.posterPath()) + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/response/RecentPerformancesResponse.java b/src/main/java/org/sopt/confeti/api/performance/dto/response/RecentPerformancesResponse.java new file mode 100644 index 0000000..ae0d004 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/response/RecentPerformancesResponse.java @@ -0,0 +1,17 @@ +package org.sopt.confeti.api.performance.dto.response; + +import java.util.List; +import org.sopt.confeti.api.performance.facade.dto.response.RecentPerformancesDTO; +import org.sopt.confeti.global.util.S3FileHandler; + +public record RecentPerformancesResponse( + List performances +) { + public static RecentPerformancesResponse of(final RecentPerformancesDTO recentPerformancesDTO, final S3FileHandler s3FileHandler) { + return new RecentPerformancesResponse( + recentPerformancesDTO.performances().stream() + .map(recentPerformanceDTO -> RecentPerformanceResponse.of(recentPerformanceDTO, s3FileHandler)) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java index 9393d7d..6ac3d73 100644 --- a/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java +++ b/src/main/java/org/sopt/confeti/api/performance/facade/PerformanceFacade.java @@ -3,12 +3,18 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO; import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDTO; import org.sopt.confeti.api.performance.facade.dto.response.PerformanceReservationDTO; +import org.sopt.confeti.api.performance.facade.dto.response.RecentPerformancesDTO; +import org.sopt.confeti.domain.artistfavorite.ArtistFavorite; +import org.sopt.confeti.domain.artistfavorite.application.ArtistFavoriteService; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.application.ConcertService; import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService; @@ -16,17 +22,24 @@ import org.sopt.confeti.domain.festival.application.FestivalService; import org.sopt.confeti.domain.festivalfavorite.application.FestivalFavoriteService; import org.sopt.confeti.domain.user.application.UserService; +import org.sopt.confeti.domain.view.performance.Performance; import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; import org.sopt.confeti.domain.view.performance.application.PerformanceService; +import org.sopt.confeti.domain.view.performance.application.dto.request.GetPerformanceIdRequest; +import org.sopt.confeti.domain.view.performance.application.dto.response.GetPerformanceIdResponse; +import org.sopt.confeti.global.common.constant.PerformanceType; import org.sopt.confeti.global.exception.NotFoundException; import org.sopt.confeti.global.message.ErrorMessage; import org.sopt.confeti.global.util.S3FileHandler; +import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; import org.springframework.transaction.annotation.Transactional; @Facade @RequiredArgsConstructor public class PerformanceFacade { + private static final int RECENT_PERFORMANCES_SIZE = 7; + private final ConcertService concertService; private final FestivalService festivalService; private final UserService userService; @@ -34,6 +47,7 @@ public class PerformanceFacade { private final S3FileHandler s3FileHandler; private final PerformanceService performanceService; private final ConcertFavoriteService concertFavoriteService; + private final ArtistFavoriteService artistFavoriteService; @Transactional(readOnly = true) public ConcertDetailDTO getConcertDetailInfo(final Long userId, final long concertId) { @@ -105,4 +119,43 @@ public PerformanceReservationDTO getPerformReservationInfo(final Long userId){ List performanceReserve=performanceService.getPerformancesReservation(); return PerformanceReservationDTO.from(performanceReserve); } + + @Transactional(readOnly = true) + public RecentPerformancesDTO getRecentPerformances(final Long userId) { + if (userId == null || !hasFavoriteArtists(userId)) { + return getRecentPerformancesWithoutFavorites(); + } + + return getRecentPerformancesWithFavorites(userId); + } + + @Transactional(readOnly = true) + public RecentPerformancesDTO getRecentPerformancesWithFavorites(final long userId) { + List artistFavorites = artistFavoriteService.getArtistList(userId); + + return RecentPerformancesDTO.from( + performanceService.getPerformancesByArtistIds( + artistFavorites.stream() + .map(artistFavorite -> artistFavorite.getArtist().getArtistId()) + .toList(), + RECENT_PERFORMANCES_SIZE + ) + ); + } + + @Transactional(readOnly = true) + public RecentPerformancesDTO getRecentPerformancesWithoutFavorites() { + // 최신 콘서트 조회 + List concerts = concertService.getRecentConcerts(RECENT_PERFORMANCES_SIZE); + + // 최신 페스티벌 조회 + List festivals = festivalService.getRecentFestivals(RECENT_PERFORMANCES_SIZE); + + return RecentPerformancesDTO.of(concerts, festivals); + } + + @Transactional(readOnly = true) + public boolean hasFavoriteArtists(final long userId) { + return artistFavoriteService.existsByUserId(userId); + } } diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/RecentPerformanceDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/RecentPerformanceDTO.java new file mode 100644 index 0000000..2d70c45 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/RecentPerformanceDTO.java @@ -0,0 +1,53 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.time.LocalDateTime; +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.domain.festival.Festival; +import org.sopt.confeti.domain.view.performance.Performance; +import org.sopt.confeti.global.common.constant.PerformanceType; + +public record RecentPerformanceDTO( + long performanceId, + long typeId, + PerformanceType type, + String title, + String subtitle, + LocalDateTime performanceAt, + String posterPath +) { + public static RecentPerformanceDTO of(final Concert concert, final long performanceId) { + return new RecentPerformanceDTO( + performanceId, + concert.getId(), + PerformanceType.CONCERT, + concert.getConcertTitle(), + concert.getConcertSubtitle(), + concert.getConcertStartAt(), + concert.getConcertPosterPath() + ); + } + + public static RecentPerformanceDTO of(final Festival festival, final long performanceId) { + return new RecentPerformanceDTO( + performanceId, + festival.getId(), + PerformanceType.FESTIVAL, + festival.getFestivalTitle(), + festival.getFestivalSubtitle(), + festival.getFestivalStartAt(), + festival.getFestivalPosterPath() + ); + } + + public static RecentPerformanceDTO from(final Performance performance) { + return new RecentPerformanceDTO( + performance.getId(), + performance.getTypeId(), + performance.getType(), + performance.getTitle(), + performance.getSubtitle(), + performance.getStartAt(), + performance.getPosterPath() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/RecentPerformancesDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/RecentPerformancesDTO.java new file mode 100644 index 0000000..cccb9c3 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/response/RecentPerformancesDTO.java @@ -0,0 +1,34 @@ +package org.sopt.confeti.api.performance.facade.dto.response; + +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.domain.festival.Festival; +import org.sopt.confeti.domain.view.performance.Performance; + +public record RecentPerformancesDTO( + List performances +) { + public static RecentPerformancesDTO of( + List concerts, + List festivals + ) { + return new RecentPerformancesDTO( + Stream.concat( + IntStream.range(0, concerts.size()) + .mapToObj(i -> RecentPerformanceDTO.of(concerts.get(i), i)), + IntStream.range(concerts.size(), festivals.size()) + .mapToObj(i -> RecentPerformanceDTO.of(festivals.get(i), i)) + ).toList() + ); + } + + public static RecentPerformancesDTO from(final List performances) { + return new RecentPerformancesDTO( + performances.stream() + .map(RecentPerformanceDTO::from) + .toList() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java index ab62bfa..6de51d8 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/application/ArtistFavoriteService.java @@ -41,4 +41,9 @@ public void addFavorite(final User user, final String artistId) { public void removeFavorite(final long userId, final String artistId) { artistFavoriteRepository.deleteByUserIdAndArtist_ArtistId(userId, artistId); } + + @Transactional(readOnly = true) + public boolean existsByUserId(final long userId) { + return artistFavoriteRepository.existsByUserId(userId); + } } diff --git a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java index 60e099f..6452662 100644 --- a/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java +++ b/src/main/java/org/sopt/confeti/domain/artistfavorite/infra/repository/ArtistFavoriteRepository.java @@ -14,4 +14,6 @@ public interface ArtistFavoriteRepository extends JpaRepository new NotFoundException(ErrorMessage.NOT_FOUND) ); } + + @Transactional(readOnly = true) + public List getRecentConcerts(final int size) { + return concertRepository.findAllByConcertEndAtGreaterThanEqual( + LocalDateTime.now(), + getPageRequest(size, getRecentConcertsSort()) + ); + } + + private PageRequest getPageRequest(final int size, final Sort sort) { + return PageRequest.of(INIT_PAGE, size, sort); + } + + private Sort getRecentConcertsSort() { + return Sort.by( + Order.asc(START_AT_COLUMN) + ); + } } diff --git a/src/main/java/org/sopt/confeti/domain/concert/infra/repository/ConcertRepository.java b/src/main/java/org/sopt/confeti/domain/concert/infra/repository/ConcertRepository.java index b4add2e..fbce496 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/infra/repository/ConcertRepository.java +++ b/src/main/java/org/sopt/confeti/domain/concert/infra/repository/ConcertRepository.java @@ -1,7 +1,12 @@ package org.sopt.confeti.domain.concert.infra.repository; +import java.time.LocalDateTime; +import java.util.List; import org.sopt.confeti.domain.concert.Concert; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; public interface ConcertRepository extends JpaRepository { + + List findAllByConcertEndAtGreaterThanEqual(final LocalDateTime localDateTime, PageRequest pageRequest); } diff --git a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java index ff8a378..7fd7287 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java +++ b/src/main/java/org/sopt/confeti/domain/festival/application/FestivalService.java @@ -1,6 +1,6 @@ package org.sopt.confeti.domain.festival.application; -import jakarta.persistence.EntityManager; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -21,18 +21,18 @@ @RequiredArgsConstructor public class FestivalService { + private static final String START_AT_COLUMN = "festivalStartAt"; + private final FestivalRepository festivalRepository; private final ArtistResolver artistResolver; - private final EntityManager em; 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) + return festivalRepository.findById(festivalId) .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND)); - return festival; } @Transactional(readOnly = true) @@ -100,4 +100,22 @@ public List findFestivalsUsingCursor( public Optional findFestivalCursor(final long userId, final long festivalId) { return festivalRepository.findFestivalCursor(userId, festivalId); } + + @Transactional(readOnly = true) + public List getRecentFestivals(final int size) { + return festivalRepository.findAllByFestivalEndAtGreaterThanEqual( + LocalDateTime.now(), + getPageRequest(size, getRecentFestivalsSort()) + ); + } + + private PageRequest getPageRequest(final int size, final Sort sort) { + return PageRequest.of(INIT_PAGE, size, sort); + } + + private Sort getRecentFestivalsSort() { + return Sort.by( + Order.asc(START_AT_COLUMN) + ); + } } diff --git a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java index 2ca8ae6..4c83b4d 100644 --- a/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java +++ b/src/main/java/org/sopt/confeti/domain/festival/infra/repository/FestivalRepository.java @@ -1,5 +1,6 @@ package org.sopt.confeti.domain.festival.infra.repository; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import org.sopt.confeti.domain.festival.Festival; @@ -71,4 +72,6 @@ Optional findFestivalCursor( final @Param("userId") long userId, final @Param("festivalId") long festivalId ); + + List findAllByFestivalEndAtGreaterThanEqual(final LocalDateTime now, final PageRequest pageRequest); } diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java index 2070bd0..222eaa3 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/PerformanceService.java @@ -1,14 +1,17 @@ package org.sopt.confeti.domain.view.performance.application; +import java.time.LocalDateTime; import java.util.List; 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.view.performance.Performance; import org.sopt.confeti.domain.view.performance.PerformanceDTO; import org.sopt.confeti.domain.view.performance.PerformanceTicketDTO; import org.sopt.confeti.domain.view.performance.infra.repository.PerformanceDTORepository; import org.sopt.confeti.domain.view.performance.infra.repository.PerformanceRepository; +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; @@ -16,6 +19,9 @@ @RequiredArgsConstructor public class PerformanceService { + private static final int INIT_PAGE = 0; + private static final String START_AT_COLUMN = "startAt"; + private final PerformanceDTORepository performanceDTORepository; private final PerformanceRepository performanceRepository; @@ -45,4 +51,23 @@ public void create(final Festival festival) { .toList() ); } + + @Transactional(readOnly = true) + public List getPerformancesByArtistIds(final List artistIds, final int size) { + return performanceRepository.findPerformancesByArtistIdInAndEndAtGreaterThanEqual( + artistIds, + LocalDateTime.now(), + getPageRequest(size, getRecentPerformancesSort()) + ); + } + + private PageRequest getPageRequest(final int size, final Sort sort) { + return PageRequest.of(INIT_PAGE, size, sort); + } + + private Sort getRecentPerformancesSort() { + return Sort.by( + Order.asc(START_AT_COLUMN) + ); + } } \ No newline at end of file diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/.gitkeep b/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/GetPerformanceIdRequest.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/GetPerformanceIdRequest.java new file mode 100644 index 0000000..1fc1483 --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/request/GetPerformanceIdRequest.java @@ -0,0 +1,24 @@ +package org.sopt.confeti.domain.view.performance.application.dto.request; + +import org.sopt.confeti.domain.concert.Concert; +import org.sopt.confeti.domain.festival.Festival; +import org.sopt.confeti.global.common.constant.PerformanceType; + +public record GetPerformanceIdRequest( + long typeId, + PerformanceType type +) { + public static GetPerformanceIdRequest from(final Festival festival) { + return new GetPerformanceIdRequest( + festival.getId(), + PerformanceType.FESTIVAL + ); + } + + public static GetPerformanceIdRequest from(final Concert concert) { + return new GetPerformanceIdRequest( + concert.getId(), + PerformanceType.CONCERT + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/.gitkeep b/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/GetPerformanceIdResponse.java b/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/GetPerformanceIdResponse.java new file mode 100644 index 0000000..cbe372a --- /dev/null +++ b/src/main/java/org/sopt/confeti/domain/view/performance/application/dto/response/GetPerformanceIdResponse.java @@ -0,0 +1,18 @@ +package org.sopt.confeti.domain.view.performance.application.dto.response; + +import org.sopt.confeti.domain.view.performance.Performance; +import org.sopt.confeti.global.common.constant.PerformanceType; + +public record GetPerformanceIdResponse( + long performanceId, + long typeId, + PerformanceType type +) { + public static GetPerformanceIdResponse from(final Performance performance) { + return new GetPerformanceIdResponse( + performance.getId(), + performance.getTypeId(), + performance.getType() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java index f9964e2..0392ef1 100644 --- a/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java +++ b/src/main/java/org/sopt/confeti/domain/view/performance/infra/repository/PerformanceRepository.java @@ -1,7 +1,16 @@ package org.sopt.confeti.domain.view.performance.infra.repository; +import java.time.LocalDateTime; +import java.util.List; import org.sopt.confeti.domain.view.performance.Performance; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; public interface PerformanceRepository extends JpaRepository { + + List findPerformancesByArtistIdInAndEndAtGreaterThanEqual( + final List artistIds, + final LocalDateTime now, + final PageRequest pageRequest + ); } From 8e45b037f7d9044ab8bd814e47dfeabd87bd5510 Mon Sep 17 00:00:00 2001 From: chyun Date: Thu, 23 Jan 2025 17:47:31 +0900 Subject: [PATCH 96/96] =?UTF-8?q?fix=20[#164]=20=ED=8A=B8=EB=9E=9C?= =?UTF-8?q?=EC=9E=AD=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/confeti/api/user/facade/UserTimetableFacade.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java index 69bfd2c..0542646 100644 --- a/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java +++ b/src/main/java/org/sopt/confeti/api/user/facade/UserTimetableFacade.java @@ -51,6 +51,7 @@ public class UserTimetableFacade { private final ArtistResolver artistResolver; private final UserTimetableService userTimetableService; + @Transactional(readOnly = true) public UserTimetableDTO getTimetablesListAndDate(long userId) { validateExistUser(userId);