Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: 뮤지컬 추가, 수정, 삭제 API 구현 #96

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ dependencies {
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'org.springframework.boot:spring-boot-starter-logging'
implementation 'net.logstash.logback:logstash-logback-encoder:7.4'

// Jsoup
implementation 'org.jsoup:jsoup:1.15.3'
implementation 'org.seleniumhq.selenium:selenium-java:4.0.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.0.3'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package encore.server.domain.musical.controller;

import encore.server.domain.musical.dto.request.MusicalCreateReq;
import encore.server.domain.musical.dto.response.MusicalDetailRes;
import encore.server.domain.musical.dto.response.MusicalRes;
import encore.server.domain.musical.dto.response.MusicalSeriesRes;
import encore.server.domain.musical.dto.response.MusicalSimpleRes;
import encore.server.domain.musical.entity.Musical;
import encore.server.domain.musical.service.MusicalService;
import encore.server.domain.review.dto.response.ReviewDetailRes;
import encore.server.global.common.ApplicationResponse;
import encore.server.domain.musical.service.MusicalListService;
import encore.server.global.exception.ApplicationException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand All @@ -23,6 +23,7 @@
@Tag(name = "Musical", description = "뮤지컬 API")
public class MusicalController {
private final MusicalService musicalService;
private final MusicalListService musicalListService;

@Operation(summary = "뮤지컬 검색", description = "뮤지컬을 키워드로 검색합니다.")
@GetMapping("/search")
Expand Down Expand Up @@ -58,6 +59,36 @@ public ApplicationResponse<List<MusicalSimpleRes>> getUpcomingMusicals() {
return ApplicationResponse.ok(responses);
}

// Todo: prod환경 테스트 후 스케줄링 할 예정
@Operation(summary = "뮤지컬 정보 크롤링 및 저장", description = "Interpark 뮤지컬 페이지에서 뮤지컬 정보를 크롤링하여 저장합니다.")
@GetMapping("/crawling")
public ApplicationResponse<String> crawlingMusicalInfo() {
musicalListService.crawlingMusicalInfo();
return ApplicationResponse.ok("크롤링 완료");
}

@Operation(summary = "뮤지컬 추가", description = "새로운 뮤지컬을 추가합니다.")
@PostMapping("/create")
public ApplicationResponse<MusicalDetailRes> addMusical(@RequestBody MusicalCreateReq request) {
MusicalDetailRes response = musicalService.addMusical(request);
return ApplicationResponse.ok(response);
}

@Operation(summary = "뮤지컬 수정", description = "입력된 필드만 수정합니다.")
@PatchMapping("/{musical_id}")
public ApplicationResponse<MusicalDetailRes> updateMusical(
@PathVariable("musical_id") Long musicalId,
@RequestBody MusicalCreateReq request) {
MusicalDetailRes response = musicalService.updateMusical(musicalId, request);
return ApplicationResponse.ok(response);
}

@Operation(summary = "뮤지컬 삭제", description = "뮤지컬을 삭제합니다.")
@DeleteMapping("/{musical_id}")
public ApplicationResponse<Void> deleteMusical(@PathVariable("musical_id") Long musicalId) {
musicalService.deleteMusical(musicalId);
return ApplicationResponse.ok();
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import encore.server.domain.musical.dto.response.MusicalRes;
import encore.server.domain.musical.entity.Musical;
import encore.server.domain.musical.entity.MusicalActor;
import encore.server.domain.musical.entity.ShowTime;

import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -15,7 +16,9 @@ public static MusicalRes toResponse(Musical musical) {
.title(musical.getTitle())
.series(musical.getSeries())
.location(musical.getLocation())
.showTimes(musical.getShowTimes())
.showTimes(musical.getShowTimes().stream()
.map(showTime -> showTime.getDay() + " " + showTime.getTime())
.collect(Collectors.toList()))
.imageUrl(musical.getImageUrl())
.build();
}
Expand All @@ -31,7 +34,9 @@ public static MusicalDetailRes toMusicalDetailRes(Musical musical) {
.age(musical.getAge())
.series(musical.getSeries())
.imageUrl(musical.getImageUrl())
.isFeatured(musical.isFeatured())
.actors(toActorResponses(musical.getMusicalActors()))
.showTimes(toShowTimeResponses(musical.getShowTimes()))
.build();
}

Expand All @@ -45,4 +50,13 @@ private static List<MusicalDetailRes.MusicalActorRes> toActorResponses(List<Musi
.build())
.collect(Collectors.toList());
}

private static List<MusicalDetailRes.ShowTimeRes> toShowTimeResponses(List<ShowTime> showTimes) {
return showTimes.stream()
.map(showTime -> MusicalDetailRes.ShowTimeRes.builder()
.day(showTime.getDay())
.time(showTime.getTime())
.build())
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package encore.server.domain.musical.dto.request;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import encore.server.domain.musical.enumerate.Day;
import lombok.Builder;

import java.time.LocalDateTime;
import java.util.List;

@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record MusicalCreateReq(
String title,
Long series,
String location,
LocalDateTime startDate,
LocalDateTime endDate,
Long runningTime,
String age,
String imageUrl,
boolean isFeatured,
List<ActorRequest> actors,
List<ShowTimeRequest> showTimes
) {
@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record ActorRequest(
Long actorId,
String roleName,
boolean isMainActor
) {
}
@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record ShowTimeRequest(
Day day,
String time
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import encore.server.domain.musical.enumerate.Day;
import lombok.Builder;

import java.time.LocalDateTime;
Expand All @@ -17,10 +18,12 @@ public record MusicalDetailRes(
LocalDateTime endDate,
String location,
Long runningTime,
Long age,
String age,
Long series,
String imageUrl,
List<MusicalActorRes> actors
boolean isFeatured,
List<MusicalActorRes> actors,
List<ShowTimeRes> showTimes
) {
@Builder
public record MusicalActorRes(
Expand All @@ -30,5 +33,11 @@ public record MusicalActorRes(
boolean isMainActor
) {
}
@Builder
public record ShowTimeRes(
Day day,
String time
) {
}
}

58 changes: 52 additions & 6 deletions src/main/java/encore/server/domain/musical/entity/Musical.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import encore.server.global.common.BaseTimeEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;
Expand Down Expand Up @@ -38,24 +39,69 @@ public class Musical extends BaseTimeEntity {
@Column(nullable = false, columnDefinition = "bigint")
private Long runningTime;

@Column(nullable = false, columnDefinition = "bigint")
private Long age;
@Column(nullable = false, columnDefinition = "varchar(255)")
private String age;

@Column(nullable = false, columnDefinition = "bigint")
private Long series;

@Column(columnDefinition = "text")
private String imageUrl;

@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "musical_show_times", joinColumns = @JoinColumn(name = "musical_id"))
@Column(name = "show_time")
private List<String> showTimes = new ArrayList<>();
@OneToMany(mappedBy = "musical", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ShowTime> showTimes = new ArrayList<>();

@OneToMany(mappedBy = "musical", cascade = CascadeType.ALL, orphanRemoval = true)
private List<MusicalActor> musicalActors = new ArrayList<>();

@Column(nullable = false, columnDefinition = "boolean default false")
private boolean isFeatured; // 이달의 인기 뮤지컬 여부

@Column(columnDefinition = "varchar(255)")
private String interparkId;

@Builder
public Musical(String title, LocalDateTime startDate, LocalDateTime endDate, String location, Long runningTime, String age, Long series, String imageUrl, List<ShowTime> showTimes, List<MusicalActor> musicalActors, boolean isFeatured, String interparkId) {
this.title = title;
this.startDate = startDate;
this.endDate = endDate;
this.location = location;
this.runningTime = runningTime;
this.age = age;
this.series = series != null ? series : 1L;
this.imageUrl = imageUrl;
this.showTimes = showTimes != null ? showTimes : new ArrayList<>();
this.musicalActors = musicalActors != null ? musicalActors : new ArrayList<>();
this.isFeatured = isFeatured;
this.interparkId = interparkId;
}

public void addMusicalActors(MusicalActor musicalActor) {
this.musicalActors.add(musicalActor);
}
public void addShowTime(ShowTime showTime) {
this.showTimes.add(showTime);
}
public void updateTitle(String title) { this.title = title;}

public void updateSeries(Long series) { this.series = series;}

public void updateLocation(String location) { this.location = location;}

public void updateStartDate(LocalDateTime startDate) { this.startDate = startDate;}

public void updateEndDate(LocalDateTime endDate) { this.endDate = endDate;}

public void updateRunningTime(Long runningTime) { this.runningTime = runningTime;}

public void updateAge(String age) { this.age = age;}

public void updateImageUrl(String imageUrl) { this.imageUrl = imageUrl;}

public void updateIsFeatured(boolean isFeatured) { this.isFeatured = isFeatured;}

public void clearMusicalActors() { this.musicalActors.clear();}

public void clearShowTimes() { this.showTimes.clear();}

}
37 changes: 37 additions & 0 deletions src/main/java/encore/server/domain/musical/entity/ShowTime.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package encore.server.domain.musical.entity;

import encore.server.domain.musical.enumerate.Day;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ShowTime {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, columnDefinition = "bigint")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "musical_id", nullable = false)
private Musical musical;

@Enumerated(EnumType.STRING)
@Column(nullable = false, columnDefinition = "varchar(255)")
private Day day;

@Column(nullable = false, columnDefinition = "varchar(255)")
private String time;

@Builder
public ShowTime(Musical musical, Day day, String time) {
this.musical = musical;
this.day = day;
this.time = time;
}
}
17 changes: 17 additions & 0 deletions src/main/java/encore/server/domain/musical/enumerate/Day.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package encore.server.domain.musical.enumerate;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public enum Day {
MONDAY("월요일"),
TUESDAY("화요일"),
WEDNESDAY("수요일"),
THURSDAY("목요일"),
FRIDAY("금요일"),
SATURDAY("토요일"),
SUNDAY("일요일"),
HOLIDAY("공휴일");

private final String day;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ public interface MusicalRepository extends JpaRepository<Musical, Long> {
@Query("SELECT m FROM Musical m WHERE m.startDate > :now AND m.deletedAt IS NULL ORDER BY m.startDate ASC")
List<Musical> findUpcomingMusicals(LocalDateTime now);


boolean existsByInterparkId(String interparkId);
boolean existsByTitleAndSeries(String title, Long series);
}
Loading
Loading