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

애니 상세 조회 시 평가 평균 조회 api #95 #105

Merged
merged 6 commits into from
Nov 14, 2023
Merged
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
20 changes: 20 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,26 @@ include::{snippets}/getAnimeById/success/response-body.adoc[]
include::{snippets}/getAnimeById/success/response-fields.adoc[]
==== 실패 시

=== GET api/v1/animes/:id/ratings/average
.curl-request
include::{snippets}/getAnimeScoreAvg/success/curl-request.adoc[]

.http-request
include::{snippets}/getAnimeScoreAvg/success/http-request.adoc[]

include::{snippets}/getAnimeScoreAvg/success/path-parameters.adoc[]

==== 성공시
.http-response
include::{snippets}/getAnimeScoreAvg/success/http-response.adoc[]

.response-body
include::{snippets}/getAnimeScoreAvg/success/response-body.adoc[]

.response-fields
include::{snippets}/getAnimeScoreAvg/success/response-fields.adoc[]
==== 실패 시

= oDuckio Documentation
:sectnums:
:toc: left
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.Length;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
Expand Down Expand Up @@ -68,17 +69,14 @@ public ResponseEntity<Object> postAnime(@RequestBody @Valid AnimeReq.PostReq req
// 관리자 애니 조회
@GetMapping("/animes")
public ResponseEntity<Object> getAnimes(
@RequestParam(required = false) String query,
@RequestParam(required = false) @Length(min = 0, max = 50) String query,
QueryType queryType,
@RequestParam(required = false, defaultValue = "latest") AdminReq.Sort sort,
@RequestParam(required = false, defaultValue = "DESC") OrderDirection order,
@RequestParam(required = false, defaultValue = "1") int page,
@RequestParam(required = false, defaultValue = "20") @Min(1) @Max(100) int size,
SearchFilter searchFilter
){

validateQueryLength(query, 50);

int validatedPage = validatePage(page);

PageResponse<AdminRes.SearchResult> res = animeService.getPageByCondition(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.oduck.api.domain.anime.controller;

import io.oduck.api.domain.anime.dto.AnimeRes;
import io.oduck.api.domain.anime.dto.AnimeRes.StarRatingAvg;
import io.oduck.api.domain.anime.dto.SearchFilterDsl;
import io.oduck.api.domain.anime.entity.BroadcastType;
import io.oduck.api.domain.anime.entity.Quarter;
Expand All @@ -13,6 +14,7 @@
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.Length;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
Expand All @@ -38,16 +40,14 @@ public class AnimeController {
// 애니 검색 조회
@GetMapping
public ResponseEntity<Object> getAnimesBySearchCondition(
@RequestParam(required = false) String query,
@RequestParam(required = false) @Length(min = 0, max = 50) String query,
@RequestParam(required = false) String cursor,
@RequestParam(required = false, defaultValue = "latest") Sort sort,
@RequestParam(required = false, defaultValue = "DESC") OrderDirection order,
@RequestParam(required = false, defaultValue = "10") @Min(1) @Max(100) int size,
@ModelAttribute SearchFilter searchFilter
){

validateQueryLength(query, 50);

List<Long> genreIds = searchFilter.getGenreIds();
List<BroadcastType> broadcastTypes = searchFilter.getBroadcastTypes();
List<EpisodeCountEnum> episodeCountEnums = searchFilter.getEpisodeCounts();
Expand Down Expand Up @@ -76,12 +76,13 @@ public ResponseEntity<Object> getAnimeById(@PathVariable Long animeId){
return ResponseEntity.ok(res);
}

private void validateQueryLength(String query, int maxLength) {
if(query != null) {
if(query.length() > maxLength){
throw new BadRequestException("글자수는 50자를 넘을 수 없습니다.");
}
}
// 애니 평가 평균 조회
@GetMapping("/{animeId}/ratings/average")
public ResponseEntity<Object> getStarRatingAvg(@PathVariable Long animeId){

StarRatingAvg res = animeService.getStarRatingAverage(animeId);

return ResponseEntity.ok(res);
}

private List<Integer> extractCurrentYearsNotInCurrentYear(List<Integer> years) {
Expand Down
32 changes: 28 additions & 4 deletions src/main/java/io/oduck/api/domain/anime/dto/AnimeRes.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package io.oduck.api.domain.anime.dto;

import io.oduck.api.domain.anime.entity.*;
import io.oduck.api.domain.anime.entity.Anime;
import io.oduck.api.domain.anime.entity.AnimeGenre;
import io.oduck.api.domain.anime.entity.AnimeOriginalAuthor;
import io.oduck.api.domain.anime.entity.AnimeStudio;
import io.oduck.api.domain.anime.entity.AnimeVoiceActor;
import io.oduck.api.domain.anime.entity.BroadcastType;
import io.oduck.api.domain.anime.entity.Quarter;
import io.oduck.api.domain.anime.entity.Rating;
import io.oduck.api.domain.anime.entity.Status;
import io.oduck.api.global.common.EntityBased;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.NoArgsConstructor;

public class AnimeRes {

Expand Down Expand Up @@ -87,4 +94,21 @@ public String bringCursor(String property) {
}
}

@Getter
@NoArgsConstructor
public static class StarRatingAvg {
private Double starRatingAvg;

public StarRatingAvg(Long starRatingScoreTotal, Long starRatingCount) {
this.starRatingAvg = calculateAvg(starRatingScoreTotal, starRatingCount);
}

private double calculateAvg(Long starRatingScoreTotal, Long starRatingCount) {
if(starRatingCount <= 0) {
return 0;
}
return starRatingScoreTotal / starRatingCount;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.oduck.api.domain.admin.dto.AdminReq.QueryType;
import io.oduck.api.domain.admin.dto.AdminReq.SearchFilter;
import io.oduck.api.domain.admin.dto.AdminRes;
import io.oduck.api.domain.anime.dto.AnimeRes.StarRatingAvg;
import io.oduck.api.domain.anime.dto.SearchFilterDsl;
import io.oduck.api.global.common.OrderDirection;
import io.oduck.api.global.common.PageResponse;
Expand Down Expand Up @@ -108,4 +109,11 @@ PageResponse<AdminRes.SearchResult> getPageByCondition(String query, QueryType q
* @param animeId 애니의 고유 식별자;
*/
void delete(Long animeId);

/**
* 애니의 평가 평균 가져오는 기준이다.
* @param animeId 애니의 고유 식별자;
* @return StarRatingAvg 애니의 평가 평균 dto;
*/
StarRatingAvg getStarRatingAverage(Long animeId);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
package io.oduck.api.domain.anime.service;

import static io.oduck.api.domain.anime.dto.AnimeReq.PatchAnimeReq;
import static io.oduck.api.domain.anime.dto.AnimeReq.PatchGenreIdsReq;
import static io.oduck.api.domain.anime.dto.AnimeReq.PatchOriginalAuthorIdsReq;
import static io.oduck.api.domain.anime.dto.AnimeReq.PatchSeriesIdReq;
import static io.oduck.api.domain.anime.dto.AnimeReq.PatchStudioIdsReq;
import static io.oduck.api.domain.anime.dto.AnimeReq.PatchVoiceActorIdsReq;
import static io.oduck.api.domain.anime.dto.AnimeReq.PostReq;
import static io.oduck.api.domain.anime.dto.AnimeReq.Sort;
import static io.oduck.api.domain.anime.dto.AnimeRes.DetailResult;
import static io.oduck.api.domain.anime.dto.AnimeRes.SearchResult;

import io.oduck.api.domain.admin.dto.AdminReq;
import io.oduck.api.domain.admin.dto.AdminReq.QueryType;
import io.oduck.api.domain.admin.dto.AdminRes;
import io.oduck.api.domain.anime.dto.AnimeRes.StarRatingAvg;
import io.oduck.api.domain.anime.dto.AnimeVoiceActorReq;
import io.oduck.api.domain.anime.dto.SearchFilterDsl;
import io.oduck.api.domain.anime.entity.*;
import io.oduck.api.domain.anime.repository.*;
import io.oduck.api.domain.anime.entity.Anime;
import io.oduck.api.domain.anime.entity.AnimeGenre;
import io.oduck.api.domain.anime.entity.AnimeOriginalAuthor;
import io.oduck.api.domain.anime.entity.AnimeStudio;
import io.oduck.api.domain.anime.entity.AnimeVoiceActor;
import io.oduck.api.domain.anime.repository.AnimeGenreRepository;
import io.oduck.api.domain.anime.repository.AnimeOriginalAuthorRepository;
import io.oduck.api.domain.anime.repository.AnimeRepository;
import io.oduck.api.domain.anime.repository.AnimeStudioRepository;
import io.oduck.api.domain.anime.repository.AnimeVoiceActorRepository;
import io.oduck.api.domain.genre.entity.Genre;
import io.oduck.api.domain.genre.repository.GenreRepository;
import io.oduck.api.domain.originalAuthor.entity.OriginalAuthor;
Expand All @@ -22,20 +42,15 @@
import io.oduck.api.global.common.SliceResponse;
import io.oduck.api.global.exception.NotFoundException;
import io.oduck.api.global.utils.PagingUtils;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static io.oduck.api.domain.anime.dto.AnimeReq.*;
import static io.oduck.api.domain.anime.dto.AnimeRes.DetailResult;
import static io.oduck.api.domain.anime.dto.AnimeRes.SearchResult;

@Service
@RequiredArgsConstructor
@Transactional
Expand Down Expand Up @@ -281,6 +296,14 @@ public void delete(Long animeId) {
anime.delete();
}

@Override
public StarRatingAvg getStarRatingAverage(Long animeId) {

Anime anime = findAnime(animeId);

return new StarRatingAvg(anime.getStarRatingScoreTotal(), anime.getStarRatingCount());
}

@Transactional(readOnly = true)
public Anime findAnime(Long animeId) {
return animeRepository.findById(animeId).orElseThrow(() -> new NotFoundException("Anime"));
Expand Down
39 changes: 38 additions & 1 deletion src/test/java/io/oduck/api/e2e/anime/AnimeControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class AnimeControllerTest {

@Nested
@DisplayName("조회")
class GetAnime{
class GetAnime {

@Test
@DisplayName("애니 검색 조회 성공 시 Http Status 200 반환")
Expand Down Expand Up @@ -247,4 +247,41 @@ void getAnimeById() throws Exception {
//TODO : 조회 실패 시
}
}

@Nested
@DisplayName("애니 평점")
class GetAnimeScoreAvg {
@Test
@DisplayName("애니 평가 평균 조회 성공 시 Http Status 200 반환")
void getAnimeScoreAvg() throws Exception {
//given
Long animeId = 1L;

//when
ResultActions actions = mockMvc.perform(
RestDocumentationRequestBuilders.get("/animes/"+"{animeId}"+"/ratings/average", animeId)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
);

//then
actions
.andExpect(status().isOk())
.andExpect(jsonPath("$.starRatingAvg").exists())
.andDo(document("getAnimeScoreAvg/success",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
pathParameters(
parameterWithName("animeId")
.description("애니의 고유 식별자")
),
responseFields(
fieldWithPath("starRatingAvg")
.type(JsonFieldType.NUMBER)
.description("애니의 평가 평균")
)
));

}
}
}