Skip to content

Commit

Permalink
Merge pull request #149 from oduck-team/feature/145
Browse files Browse the repository at this point in the history
입덕포인트 평균 수정 및 회원, 애니 id 로 입덕포인트 수정 #145
  • Loading branch information
hanyMK authored Dec 26, 2023
2 parents 6610cc8 + e0b4328 commit 0563f23
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,11 @@ public ResponseEntity<?> getAttractionPoint(
return ResponseEntity.ok(attractionPointService.checkAttractionPoint(user.getId(), animeId));
}

@PatchMapping("/{attractionPointId}")
@PatchMapping
public ResponseEntity<?> patchAttractionPoint(
@LoginUser AuthUser user,
@PathVariable("attractionPointId") Long attractionPointId,
@RequestBody @Valid UpdateAttractionPoint req){
boolean update = attractionPointService.update(user.getId(), attractionPointId, req);
@RequestBody @Valid AttractionPointReq req){
boolean update = attractionPointService.update(user.getId(), req);
return ResponseEntity.status(update? HttpStatus.NO_CONTENT : HttpStatus.CONFLICT).build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,4 @@ public static class AttractionPointReq{
@NotNull(message = "입덕포인트를 선택하세요.")
List<AttractionElement> attractionElements;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class UpdateAttractionPoint{
@NotNull(message = "입덕포인트를 선택하세요.")
AttractionElement attractionElement;
}

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
package io.oduck.api.domain.attractionPoint.dto;

import io.oduck.api.domain.attractionPoint.entity.AttractionElement;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

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

@Getter
@Builder
public class AttractionPointResDto {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@

public interface AttractionPointRepositoryCustom {
Long countElementByAnimeId(AttractionElement attractionElement, Long animeId);
Long countByAnimeId(Long animeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@ class AttractionPointRepositoryImpl implements AttractionPointRepositoryCustom {

@Override
public Long countElementByAnimeId(AttractionElement attractionElement, Long animeId) {
Long elementCount = query
.select(attractionPoint.attractionElement.count())
return query
.select(attractionPoint.count())
.from(attractionPoint)
.where(attractionPoint.attractionElement.eq(attractionElement)
.and(attractionPoint.anime.id.eq(animeId)))
.fetchOne();
return elementCount;
}

@Override
public Long countByAnimeId(Long animeId) {
return query
.select(attractionPoint.count())
.from(attractionPoint)
.where(attractionPoint.anime.id.eq(animeId))
.fetchOne();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public interface AttractionPointService {
//입덕포인트 조회(true/false)
IsAttractionPoint isAttractionPoint(Long memberId, Long animeId);

boolean update(Long memberId, Long attractionPointId, UpdateAttractionPoint req);
boolean update(Long memberId, AttractionPointReq req);

AttractionPointStats getAttractionPointStats(Long anime);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,8 @@
import io.oduck.api.domain.attractionPoint.entity.AttractionElement;
import io.oduck.api.domain.attractionPoint.entity.AttractionPoint;
import io.oduck.api.domain.attractionPoint.repository.AttractionPointRepository;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import io.oduck.api.domain.member.entity.Member;
import io.oduck.api.domain.member.repository.MemberRepository;
import io.oduck.api.domain.review.entity.ShortReview;
import io.oduck.api.global.exception.BadRequestException;
import io.oduck.api.global.exception.ConflictException;
import io.oduck.api.global.exception.NotFoundException;
Expand All @@ -23,6 +17,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Slf4j
@Service
@RequiredArgsConstructor
Expand All @@ -34,13 +31,10 @@ public class AttractionPointServiceImpl implements AttractionPointService {

@Override
public IsAttractionPoint isAttractionPoint(Long memberId, Long animeId) {
boolean drawing = false;
boolean story = false;
boolean music = false;
boolean character = false;
boolean voiceActor = false;

List<AttractionPoint> points = attractionPointRepository.findAllByAnimeIdAndMemberId(memberId, animeId);

boolean drawing = false, story = false, music = false, character = false, voiceActor = false;

for (AttractionPoint point : points) {
switch (point.getAttractionElement()) {
case DRAWING -> drawing = true;
Expand All @@ -50,8 +44,8 @@ public IsAttractionPoint isAttractionPoint(Long memberId, Long animeId) {
default -> voiceActor = true;
}
}
return IsAttractionPoint
.builder()

return IsAttractionPoint.builder()
.drawing(drawing)
.story(story)
.music(music)
Expand All @@ -65,75 +59,59 @@ public IsAttractionPoint isAttractionPoint(Long memberId, Long animeId) {
public void save(Long memberId, AttractionPointReq req) {
List<AttractionPoint> findPoint = attractionPointRepository.findAllByAnimeIdAndMemberId(memberId, req.getAnimeId());

List<AttractionElement> elementList = findPoint.stream()
.map(AttractionPoint::getAttractionElement)
.toList();

//중복되지 않은 포인트
List<AttractionElement> matchPoint = req.getAttractionElements().stream()
.filter(attractionElements ->
elementList.stream()
.noneMatch(Predicate.isEqual(attractionElements)))
.toList();

List<AttractionElement> matchPoint = findNonOverlappingValues(findPoint, req);
if (matchPoint.isEmpty()) {
throw new ConflictException("AttractionPoint");
}

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new NotFoundException("Member"));

Anime anime = animeRepository.findById(req.getAnimeId())
.orElseThrow(() -> new NotFoundException("Anime"));

List<AttractionPoint> points = matchPoint
.stream()
.map(attractionElement -> AttractionPoint
.builder()
.member(member)
.anime(anime)
.attractionElement(attractionElement)
.build())
.toList();
attractionPointRepository.saveAll(points);
attractionPointRepository.saveAll(getAttractionPoint(matchPoint, member, anime));
}

@Override
public CheckAttractionPoint checkAttractionPoint(Long memberId, Long animeId) {
List<AttractionPoint> findPoint = attractionPointRepository.findAllByAnimeIdAndMemberId(memberId, animeId);
return CheckAttractionPoint
.builder()
return CheckAttractionPoint.builder()
.isAttractionPoint(!findPoint.isEmpty())
.build();
}

@Override
public boolean update(Long memberId, Long attractionPointId, UpdateAttractionPoint req) {
AttractionPoint findAttractionPoint = getAttractionPoint(attractionPointId);
public boolean update(Long memberId, AttractionPointReq req) {
List<AttractionPoint> findPoint = attractionPointRepository.findAllByAnimeIdAndMemberId(memberId, req.getAnimeId());

if (findAttractionPoint.getAttractionElement().equals(req.getAttractionElement())) {
if (findPoint.isEmpty()) {
return false;
}
Long findMemberId = findAttractionPoint.getMember().getId();
//입덕 포인트 작성자 인지 확인
Optional
.ofNullable(findMemberId)
.ifPresent(
id -> {
if (!findMemberId.equals(memberId)) {
throw new BadRequestException("Not the author of the attractionPoint.");
}
findAttractionPoint.updateElement(req.getAttractionElement());
}
);
attractionPointRepository.save(findAttractionPoint);

Long findMemberId = findPoint.get(0).getMember().getId();

if (!findMemberId.equals(memberId)) {
throw new BadRequestException("Not the author of the attractionPoint.");
}

List<AttractionElement> findAttractionElement = findNonOverlappingValues(findPoint, req);

if (findAttractionElement.isEmpty()) {
throw new ConflictException("AttractionPoint");
} else {
attractionPointRepository.deleteAllInBatch(findPoint);

Member member = findPoint.get(0).getMember();
Anime anime = findPoint.get(0).getAnime();

attractionPointRepository.saveAll(getAttractionPoint(findAttractionElement, member, anime));
}
return true;
}

@Override
public AttractionPointStats getAttractionPointStats(Long animeId) {
//입덕포인트 / 전체 입덕포인트 개수
Long totalCount = attractionPointRepository.count();
Long totalCount = attractionPointRepository.countByAnimeId(animeId);
double drawing = calculateElementRatio(AttractionElement.DRAWING, animeId, totalCount);
double story = calculateElementRatio(AttractionElement.STORY, animeId, totalCount);
double voiceActor = calculateElementRatio(AttractionElement.VOICE_ACTOR, animeId, totalCount);
Expand All @@ -149,19 +127,27 @@ public AttractionPointStats getAttractionPointStats(Long animeId) {
.build();
}

private AttractionPoint getAttractionPoint(Long attractionPointId) {
return attractionPointRepository.findById(attractionPointId)
.orElseThrow(
() -> new NotFoundException("AttractionPoint")
);
private List<AttractionPoint> getAttractionPoint(List<AttractionElement> elements, Member member, Anime anime){
return elements.stream()
.map(attractionElement -> AttractionPoint.builder()
.member(member)
.anime(anime)
.attractionElement(attractionElement)
.build())
.collect(Collectors.toList());
}

private double calculateElementRatio(AttractionElement element, Long animeId, Long totalCount) {
Long countElementByAnimeId = attractionPointRepository.countElementByAnimeId(element, animeId);
if (countElementByAnimeId <= 0) {
return 0;
}
return (double) countElementByAnimeId / totalCount;
return countElementByAnimeId > 0 ? (double) countElementByAnimeId / totalCount * 100 : 0;
}

}
private List<AttractionElement> findNonOverlappingValues(List<AttractionPoint> findPoint, AttractionPointReq req) {
List<AttractionElement> elementList = findPoint.stream()
.map(AttractionPoint::getAttractionElement)
.toList();
//중복되지 않은 값
return req.getAttractionElements().stream()
.filter(attractionElement -> !elementList.contains(attractionElement))
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.oduck.api.global.utils.ShortReviewTestUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -164,16 +165,21 @@ class PatchAttractionPoint {
@WithCustomMockMember(id = 2L, email = "john", password = "Qwer!234", role = Role.MEMBER)
void patchAttractionPointSuccess() throws Exception {
//given
Long attractionPointId = 1L;
UpdateAttractionPoint req = UpdateAttractionPoint
List<AttractionElement> elementList = new ArrayList<>();
elementList.add(AttractionElement.CHARACTER);
elementList.add(AttractionElement.DRAWING);

AttractionPointReq req = AttractionPointReq
.builder()
.attractionElement(AttractionElement.VOICE_ACTOR)
.animeId(1L)
.attractionElements(elementList)
.build();

String content = gson.toJson(req);

//when
ResultActions actions = mockMvc.perform(
patch(BASE_URL + "/{attractionPointId}", attractionPointId)
patch(BASE_URL )
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.COOKIE, "oDuckio.sid={SESSION_VALUE}")
Expand All @@ -191,31 +197,36 @@ void patchAttractionPointSuccess() throws Exception {
.attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}"))
.description("Header Cookie, 세션 쿠키")
),
pathParameters(
parameterWithName("attractionPointId")
.description("입덕포인트 식별자")),
requestFields(attributes(key("title").value("Fields for AttractionPoint creation")),
fieldWithPath("attractionElement")
.type(JsonFieldType.STRING)
.attributes(field("constraints", "DRAWING, STORY, MUSIC, CHARACTER, VOICE_ACTOR만 입력 가능합니다."))
.description("입덕 포인트"))
fieldWithPath("animeId")
.type(JsonFieldType.NUMBER)
.attributes(field("constraints", "애니 아이디, NotNull, Min(1)"))
.description("애니 고유 식별 번호"),
fieldWithPath("attractionElements")
.type(JsonFieldType.ARRAY)
.attributes(field("constraints", "DRAWING, STORY, MUSIC, CHARACTER, VOICE_ACTOR 리스트만 허용합니다. "))
.description("입덕포인트 리스트")
)
));
}
@DisplayName("입덕포인트 수정 성공시 Http Status 409 반환")
@DisplayName("입덕포인트 수정 실패시 Http Status 409 반환")
@Test
@WithCustomMockMember(id = 2L, email = "john", password = "Qwer!234", role = Role.MEMBER)
void patchAttractionPointFalse() throws Exception {
//given
Long attractionPointId = 1L;
UpdateAttractionPoint req = UpdateAttractionPoint
List<AttractionElement> elementList = new ArrayList<>();
elementList.add(AttractionElement.STORY);

AttractionPointReq req = AttractionPointReq
.builder()
.attractionElement(AttractionElement.DRAWING)
.animeId(1L)
.attractionElements(elementList)
.build();
String content = gson.toJson(req);

//when
ResultActions actions = mockMvc.perform(
patch(BASE_URL + "/{attractionPointId}", attractionPointId)
patch(BASE_URL)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.COOKIE, "oDuckio.sid={SESSION_VALUE}")
Expand All @@ -233,14 +244,16 @@ void patchAttractionPointFalse() throws Exception {
.attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}"))
.description("Header Cookie, 세션 쿠키")
),
pathParameters(
parameterWithName("attractionPointId")
.description("입덕포인트 식별자")),
requestFields(attributes(key("title").value("Fields for AttractionPoint creation")),
fieldWithPath("attractionElement")
.type(JsonFieldType.STRING)
.attributes(field("constraints", "DRAWING, STORY, MUSIC, CHARACTER, VOICE_ACTOR만 입력 가능합니다."))
.description("입덕 포인트"))
fieldWithPath("animeId")
.type(JsonFieldType.NUMBER)
.attributes(field("constraints", "애니 아이디, NotNull, Min(1)"))
.description("애니 고유 식별 번호"),
fieldWithPath("attractionElements")
.type(JsonFieldType.ARRAY)
.attributes(field("constraints", "DRAWING, STORY, MUSIC, CHARACTER, VOICE_ACTOR 리스트만 허용합니다. "))
.description("입덕포인트 리스트")
)
));
}
}
Expand Down
Loading

0 comments on commit 0563f23

Please sign in to comment.