diff --git a/customer-service/src/main/java/com/example/customerservice/actor/controller/AppearanceController.java b/customer-service/src/main/java/com/example/customerservice/actor/controller/AppearanceController.java new file mode 100644 index 0000000..191391c --- /dev/null +++ b/customer-service/src/main/java/com/example/customerservice/actor/controller/AppearanceController.java @@ -0,0 +1,31 @@ +package com.example.customerservice.actor.controller; + +import com.example.customerservice.actor.dto.response.AppearancePageResponse; +import com.example.customerservice.actor.service.AppearanceService; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/appearance") +public class AppearanceController { + private final AppearanceService appearanceService; + + @GetMapping + public ResponseEntity getAppearance( + @PositiveOrZero @RequestParam(defaultValue = "0") int page, + @PositiveOrZero @RequestParam(defaultValue = "10") int size, + @RequestParam(value = "actor") String actor + + ){ + AppearancePageResponse response = appearanceService.getAppearanceByName(actor, PageRequest.of(page, size)); + return ResponseEntity.ok(response); + } + +} diff --git a/customer-service/src/main/java/com/example/customerservice/actor/dto/response/AppearancePageResponse.java b/customer-service/src/main/java/com/example/customerservice/actor/dto/response/AppearancePageResponse.java new file mode 100644 index 0000000..dbc114c --- /dev/null +++ b/customer-service/src/main/java/com/example/customerservice/actor/dto/response/AppearancePageResponse.java @@ -0,0 +1,20 @@ +package com.example.customerservice.actor.dto.response; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Builder +@Getter +public class AppearancePageResponse { + PageableResponse pageable; + List content; + + public static AppearancePageResponse of(PageableResponse pageable, List content) { + return AppearancePageResponse.builder() + .pageable(pageable) + .content(content) + .build(); + } +} diff --git a/customer-service/src/main/java/com/example/customerservice/actor/dto/response/AppearanceResponse.java b/customer-service/src/main/java/com/example/customerservice/actor/dto/response/AppearanceResponse.java new file mode 100644 index 0000000..79f9be8 --- /dev/null +++ b/customer-service/src/main/java/com/example/customerservice/actor/dto/response/AppearanceResponse.java @@ -0,0 +1,23 @@ +package com.example.customerservice.actor.dto.response; + +import com.example.customerservice.contents.entity.Contents; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class AppearanceResponse { + private Long contentId; + private String category; + private String title; + private String writer; + + public static AppearanceResponse fromContent(Contents contents) { + return AppearanceResponse.builder() + .contentId(contents.getContentId()) + .category(contents.getCategory()) + .title(contents.getTitle()) + .writer(contents.getWriter()) + .build(); + } +} diff --git a/customer-service/src/main/java/com/example/customerservice/actor/dto/response/PageableResponse.java b/customer-service/src/main/java/com/example/customerservice/actor/dto/response/PageableResponse.java new file mode 100644 index 0000000..b4a65c5 --- /dev/null +++ b/customer-service/src/main/java/com/example/customerservice/actor/dto/response/PageableResponse.java @@ -0,0 +1,59 @@ +package com.example.customerservice.actor.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.domain.Pageable; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Builder +@Getter +public class PageableResponse { + @Schema( + defaultValue = "0", + description = "페이지 인덱스로, 0부터 시작합니다. 별도의 값 없이 요청 시, 0으로 설정됩니다.", + requiredMode = REQUIRED + ) + private int page; + + @Schema( + defaultValue = "10", + description = "페이지 내 최대 응답 개수입니다. 별도의 값 없이 요청 시, 10으로 설정됩니다.", + requiredMode = REQUIRED + ) + private int size; + + @Schema( + defaultValue = "3", + description = "전체 페이지 수 입니다.", + requiredMode = REQUIRED + ) + private int totalPages; + + @Schema( + defaultValue = "30", + description = "전체 요소 개수 입니다.", + requiredMode = REQUIRED + ) + private int totalElements; + + @Schema( + defaultValue = "false", + description = "현재 응답하는 페이지가 마지막 일 시, true로 설정됩니다.", + requiredMode = REQUIRED + ) + private boolean isEnd; + + public static PageableResponse of(Pageable pageable, int totalElementsSize) { + int totalPageSize = (int) Math.ceil((double) totalElementsSize / pageable.getPageSize()); + boolean isEnd = pageable.getPageNumber() + 1 >= totalPageSize; + return PageableResponse.builder() + .page(pageable.getPageNumber()) + .size(pageable.getPageSize()) + .totalPages(totalPageSize) + .totalElements(totalElementsSize) + .isEnd(isEnd) + .build(); + } +} diff --git a/customer-service/src/main/java/com/example/customerservice/actor/service/AppearanceService.java b/customer-service/src/main/java/com/example/customerservice/actor/service/AppearanceService.java new file mode 100644 index 0000000..1c2ac2d --- /dev/null +++ b/customer-service/src/main/java/com/example/customerservice/actor/service/AppearanceService.java @@ -0,0 +1,47 @@ +package com.example.customerservice.actor.service; + +import com.example.customerservice.actor.dto.response.AppearancePageResponse; +import com.example.customerservice.actor.dto.response.AppearanceResponse; +import com.example.customerservice.actor.dto.response.PageableResponse; +import com.example.customerservice.actor.entity.Actor; +import com.example.customerservice.actor.repository.ActorRepository; +import com.example.customerservice.contents.entity.Contents; +import com.example.customerservice.contents.repository.ContentsRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class AppearanceService { + private final ActorRepository actorRepository; + private final ContentsRepository contentsRepository; + + @Transactional(readOnly = true) + public AppearancePageResponse getAppearanceByName(String actorName, Pageable pageable) { + // 배우 이름으로 배우 조회 + Actor actor = actorRepository.findByActorName(actorName) + .orElseThrow(() -> new RuntimeException("Actor not found")); + + // 배우 출연작으로 총 작품 수 작품 조회 + int totalSize = actor.getActorAppearances().size(); + + // 페이징을 위해 limit와 offset을 적용해서 조회 + List contentsByActorName = contentsRepository.findContentsByActorName( + actorName, pageable.getPageSize(), pageable.getOffset()); + + // 작품을 페이징 형식에 맞게 교체 + List appearanceResponses = new ArrayList<>(); + for (Contents content : contentsByActorName) { + appearanceResponses.add(AppearanceResponse.fromContent(content)); + } + PageableResponse pageableResponse = PageableResponse.of(pageable, totalSize); + + // 리턴할 객체 빌드 + return AppearancePageResponse.of(pageableResponse, appearanceResponses); + } +} diff --git a/customer-service/src/main/java/com/example/customerservice/contents/repository/ContentsRepository.java b/customer-service/src/main/java/com/example/customerservice/contents/repository/ContentsRepository.java index b2b805f..644bc28 100644 --- a/customer-service/src/main/java/com/example/customerservice/contents/repository/ContentsRepository.java +++ b/customer-service/src/main/java/com/example/customerservice/contents/repository/ContentsRepository.java @@ -3,12 +3,33 @@ import com.example.customerservice.contents.entity.Contents; 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 ContentsRepository extends JpaRepository { - @Query("SELECT CONCAT(c.title, '|', c.writer) FROM Contents c") - List findAllTitlesAndWriters(); + @Query("SELECT CONCAT(c.title, '|', c.writer) FROM Contents c") + List findAllTitlesAndWriters(); - boolean existsByTitle(String title); + boolean existsByTitle(String title); + + @Query(value = """ + SELECT + * + FROM + actor a + JOIN + actor_appearances aa ON a.actor_id = aa.actor_id + JOIN + contents c ON aa.contents_id = c.content_id + WHERE + a.actor_name = :actorName + ORDER BY + c.content_id ASC + LIMIT :limit OFFSET :offset + """, nativeQuery = true) + List findContentsByActorName( + @Param("actorName") String actorName, + @Param("limit") int limit, + @Param("offset") long offset); }