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

입덕포인트 평균 수정 및 회원, 애니 id 로 입덕포인트 수정 #145 #149

Merged
merged 10 commits into from
Dec 26, 2023
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