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 26952df..3c8316c 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,9 +2,11 @@ import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; +import org.sopt.confeti.api.performance.dto.request.CreateConcertRequest; import org.sopt.confeti.api.performance.dto.request.CreateFestivalRequest; import org.sopt.confeti.api.performance.dto.response.*; import org.sopt.confeti.api.performance.facade.PerformanceFacade; +import org.sopt.confeti.api.performance.facade.dto.request.CreateConcertDTO; import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO; import org.sopt.confeti.api.performance.facade.dto.response.*; import org.sopt.confeti.api.performance.facade.dto.response.PerformanceReservationDTO; @@ -43,8 +45,14 @@ public ResponseEntity> getFestivalInfo(@RequestHeader(name = "Au return ApiResponseUtil.success(SuccessMessage.SUCCESS, FestivalDetailResponse.from(festivalDetailDTO)); } + @PostMapping("/concerts") + public ResponseEntity> createConcert(@RequestBody CreateConcertRequest createConcertRequest) { + performanceFacade.createConcert(CreateConcertDTO.from(createConcertRequest)); + return ApiResponseUtil.success(SuccessMessage.CREATED); + } + @PostMapping("/festivals") - public ResponseEntity> createConcert(@RequestBody CreateFestivalRequest createFestivalRequest) { + public ResponseEntity> createFestival(@RequestBody CreateFestivalRequest createFestivalRequest) { performanceFacade.createFestival(CreateFestivalDTO.from(createFestivalRequest)); return ApiResponseUtil.success(SuccessMessage.CREATED); diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateConcertArtistRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateConcertArtistRequest.java new file mode 100644 index 0000000..646a98b --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateConcertArtistRequest.java @@ -0,0 +1,9 @@ +package org.sopt.confeti.api.performance.dto.request; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record CreateConcertArtistRequest( + @JsonProperty(value = "artist_id") + String artistId +) { +} diff --git a/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateConcertRequest.java b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateConcertRequest.java new file mode 100644 index 0000000..0ec089b --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/dto/request/CreateConcertRequest.java @@ -0,0 +1,43 @@ +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 CreateConcertRequest( + @JsonProperty(value = "concert_title") + String concertTitle, + @JsonProperty(value = "concert_subtitle") + String concertSubtitle, + @JsonFormat(pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") + @JsonProperty(value = "concert_start_at") + LocalDate concertStartAt, + @JsonFormat(pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") + @JsonProperty(value = "concert_end_at") + LocalDate concertEndAt, + @JsonProperty(value = "concert_area") + String concertArea, + @JsonProperty(value = "concert_poster_path") + String concertPosterPath, + @JsonProperty(value = "concert_poster_bg_path") + String concertPosterBgPath, + @JsonProperty(value = "concert_info_img_path") + String concertInfoImgPath, + @JsonProperty(value = "concert_reservation_bg_path") + String concertReservationBgPath, + @JsonFormat(pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") + @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 = "concert_artists") + List 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 28e12b1..f9b09bd 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 @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.confeti.annotation.Facade; +import org.sopt.confeti.api.performance.facade.dto.request.CreateConcertDTO; 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; @@ -73,6 +74,12 @@ protected void validateConcertNotPassed(final Concert concert) { } } + @Transactional + public void createConcert(final CreateConcertDTO from) { + Concert concert = concertService.create(from); + performanceService.create(concert); + } + @Transactional public void createFestival(final CreateFestivalDTO createFestivalDTO) { Festival festival = festivalService.create(createFestivalDTO); diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateConcertArtistDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateConcertArtistDTO.java new file mode 100644 index 0000000..ea9bffa --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateConcertArtistDTO.java @@ -0,0 +1,13 @@ +package org.sopt.confeti.api.performance.facade.dto.request; + +import org.sopt.confeti.api.performance.dto.request.CreateConcertArtistRequest; + +public record CreateConcertArtistDTO( + String artistId +) { + public static CreateConcertArtistDTO from(final CreateConcertArtistRequest createConcertArtistRequest) { + return new CreateConcertArtistDTO( + createConcertArtistRequest.artistId() + ); + } +} diff --git a/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateConcertDTO.java b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateConcertDTO.java new file mode 100644 index 0000000..5352c14 --- /dev/null +++ b/src/main/java/org/sopt/confeti/api/performance/facade/dto/request/CreateConcertDTO.java @@ -0,0 +1,48 @@ +package org.sopt.confeti.api.performance.facade.dto.request; + +import java.time.LocalDateTime; +import java.util.List; +import org.sopt.confeti.api.performance.dto.request.CreateConcertRequest; +import org.sopt.confeti.global.util.DateConvertor; + +public record CreateConcertDTO( + String concertTitle, + String concertSubtitle, + LocalDateTime concertStartAt, + LocalDateTime concertEndAt, + String concertArea, + String concertPosterPath, + String concertPosterBgPath, + String concertInfoImgPath, + String concertReservationBgPath, + LocalDateTime reserveAt, + String reservationUrl, + String reservationOffice, + String ageRating, + String time, + String price, + List concertArtists +) { + public static CreateConcertDTO from(final CreateConcertRequest createConcertRequest) { + return new CreateConcertDTO( + createConcertRequest.concertTitle(), + createConcertRequest.concertSubtitle(), + createConcertRequest.concertStartAt().atStartOfDay(), + createConcertRequest.concertEndAt().atStartOfDay(), + createConcertRequest.concertArea(), + createConcertRequest.concertPosterPath(), + createConcertRequest.concertPosterBgPath(), + createConcertRequest.concertInfoImgPath(), + createConcertRequest.concertReservationBgPath(), + createConcertRequest.reserveAt().atStartOfDay(), + createConcertRequest.reservationUrl(), + createConcertRequest.reservationOffice(), + createConcertRequest.ageRating(), + createConcertRequest.time(), + createConcertRequest.price(), + createConcertRequest.concertArtists().stream() + .map(CreateConcertArtistDTO::from) + .toList() + ); + } +} 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 6e1077f..1047b9d 100644 --- a/src/main/java/org/sopt/confeti/domain/concert/Concert.java +++ b/src/main/java/org/sopt/confeti/domain/concert/Concert.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.CreateConcertDTO; import org.sopt.confeti.domain.concertartist.ConcertArtist; import java.time.LocalDateTime; import java.util.ArrayList; @@ -67,4 +69,55 @@ public class Concert { @OneToMany(mappedBy = "concert", cascade = CascadeType.REMOVE) private List artists = new ArrayList<>(); + + @Builder + public Concert(String concertTitle, String concertSubtitle, LocalDateTime concertStartAt, + LocalDateTime concertEndAt, String concertArea, String concertPosterPath, String concertPosterBgPath, + String concertInfoImgPath, String concertReservationBgPath, LocalDateTime reserveAt, + String reservationUrl, String reservationOffice, String ageRating, String time, String price, + List artists) { + this.concertTitle = concertTitle; + this.concertSubtitle = concertSubtitle; + this.concertStartAt = concertStartAt; + this.concertEndAt = concertEndAt; + this.concertArea = concertArea; + this.concertPosterPath = concertPosterPath; + this.concertPosterBgPath = concertPosterBgPath; + this.concertInfoImgPath = concertInfoImgPath; + this.concertReservationBgPath = concertReservationBgPath; + this.reserveAt = reserveAt; + this.reservationUrl = reservationUrl; + this.reservationOffice = reservationOffice; + this.ageRating = ageRating; + this.time = time; + this.price = price; + this.artists = artists; + + artists.forEach(artist -> artist.setConcert(this)); + } + + public static Concert create(final CreateConcertDTO from) { + return Concert.builder() + .concertTitle(from.concertTitle()) + .concertSubtitle(from.concertSubtitle()) + .concertStartAt(from.concertStartAt()) + .concertEndAt(from.concertEndAt()) + .concertArea(from.concertArea()) + .concertPosterPath(from.concertPosterPath()) + .concertPosterBgPath(from.concertPosterBgPath()) + .concertInfoImgPath(from.concertInfoImgPath()) + .concertReservationBgPath(from.concertReservationBgPath()) + .reserveAt(from.reserveAt()) + .reservationUrl(from.reservationUrl()) + .reservationOffice(from.reservationOffice()) + .ageRating(from.ageRating()) + .time(from.time()) + .price(from.price()) + .artists( + from.concertArtists().stream() + .map(ConcertArtist::create) + .toList() + ) + .build(); + } } 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 342f711..733cb0c 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 @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import java.util.List; import lombok.RequiredArgsConstructor; +import org.sopt.confeti.api.performance.facade.dto.request.CreateConcertDTO; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.concert.infra.repository.ConcertRepository; import org.sopt.confeti.global.exception.NotFoundException; @@ -66,4 +67,11 @@ private Sort getRecentConcertsSort() { Order.asc(START_AT_COLUMN) ); } + + @Transactional + public Concert create(final CreateConcertDTO from) { + return concertRepository.save( + Concert.create(from) + ); + } } diff --git a/src/main/java/org/sopt/confeti/domain/concertartist/ConcertArtist.java b/src/main/java/org/sopt/confeti/domain/concertartist/ConcertArtist.java index 8ce545f..498d6b2 100644 --- a/src/main/java/org/sopt/confeti/domain/concertartist/ConcertArtist.java +++ b/src/main/java/org/sopt/confeti/domain/concertartist/ConcertArtist.java @@ -5,6 +5,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.sopt.confeti.api.performance.facade.dto.request.CreateConcertArtistDTO; import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.global.util.artistsearcher.ConfetiArtist; @@ -24,4 +25,21 @@ public class ConcertArtist { @Embedded private ConfetiArtist artist; + + public void setConcert(Concert concert) { + this.concert = concert; + } + + @Builder + public ConcertArtist(ConfetiArtist artist) { + this.artist = artist; + } + + public static ConcertArtist create(final CreateConcertArtistDTO concertArtistDTO) { + return ConcertArtist.builder() + .artist( + ConfetiArtist.from(concertArtistDTO.artistId()) + ) + .build(); + } } 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 4646eec..336e784 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 @@ -14,6 +14,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.sopt.confeti.domain.concert.Concert; import org.sopt.confeti.domain.festival.Festival; import org.sopt.confeti.global.common.constant.PerformanceType; @@ -95,4 +96,20 @@ public static Performance create(final Festival festival, final String artistId, .reservationBgPath(festival.getFestivalReservationBgPath()) .build(); } + + public static Performance create(final Concert concert, final String artistId) { + return Performance.builder() + .typeId(concert.getId()) + .type(PerformanceType.CONCERT) + .artistId(artistId) + .area(concert.getConcertArea()) + .title(concert.getConcertTitle()) + .subtitle(concert.getConcertSubtitle()) + .performanceStartAt(concert.getConcertStartAt()) + .performanceEndAt(concert.getConcertEndAt()) + .artistStartAt(null) + .posterPath(concert.getConcertPosterPath()) + .reservationBgPath(concert.getConcertReservationBgPath()) + .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 3b87c51..12740db 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 @@ -6,6 +6,7 @@ import java.util.Optional; import lombok.RequiredArgsConstructor; +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.domain.view.performance.PerformanceDTO; @@ -56,6 +57,15 @@ public void create(final Festival festival) { ); } + @Transactional + public void create(final Concert concert) { + performanceRepository.saveAll( + concert.getArtists().stream() + .map(concertArtist -> Performance.create(concert, concertArtist.getArtist().getArtistId())) + .toList() + ); + } + @Transactional(readOnly = true) public List getPerformancesByArtistIds(final List artistIds, final int size) { return performanceRepository.findPerformancesByArtistIdInAndPerformanceEndAtGreaterThanEqual(