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

[BE] 제출한 인원 조회하는 기능 구현하기 #579

Merged
merged 8 commits into from
Sep 26, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ public boolean hasSameCycleWith(Study study) {
return cycle.equals(study.getCurrentCycle());
}

public boolean hasEmptyPlan() {
return plan.isEmpty();
public boolean isPlanWritten() {
return !plan.isEmpty();
}

public boolean isRetrospectWritten() {
return !retrospect.isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ private void validateStudyIsRetrospect(Study study) {
}

private void validateIsPlanFilled(Content recentContent) {
if (recentContent.hasEmptyPlan()) {
if (!recentContent.isPlanWritten()) {
throw new StudyStepException();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
package harustudy.backend.view.controller;

import harustudy.backend.auth.Authenticated;
import harustudy.backend.auth.dto.AuthMember;
import harustudy.backend.view.dto.SubmittersResponse;
import harustudy.backend.view.service.PollingService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RequiredArgsConstructor
@Controller
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RestController이어야 할 것 같지만 어차피 머지할때 해결할 것 같습니다

public class PollingController {

private final PollingService pollingService;

@Operation(summary = "스터디원 별 제출 여부 조회")
@GetMapping("/api/submitted")
public ResponseEntity<SubmittersResponse> findSubmitters(
@Authenticated AuthMember authMember,
@RequestParam Long studyId
) {
return ResponseEntity.ok(pollingService.findSubmitters(studyId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package harustudy.backend.view.dto;

public record SubmitterResponse(String nickname, Boolean submitted) {

public static SubmitterResponse of(String nickname, Boolean submitted) {
return new SubmitterResponse(nickname, submitted);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package harustudy.backend.view.dto;

import java.util.List;

public record SubmittersResponse(List<SubmitterResponse> status) {

public static SubmittersResponse from(List<SubmitterResponse> submitterResponses) {
return new SubmittersResponse(submitterResponses);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package harustudy.backend.view.exception;

import harustudy.backend.common.exception.HaruStudyException;

public class CurrentCycleContentNotExistsException extends HaruStudyException {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExceptionMapper 추가 됐나요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 클라까지 안 나가는 예외라 괜찮지 않을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#580 으로 이슈 파뒀습니다!


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package harustudy.backend.view.exception;

import harustudy.backend.common.exception.HaruStudyException;

public class SubmitNotAllowedStepException extends HaruStudyException {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExceptionMapper 추가 됐나요?


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
package harustudy.backend.view.service;

import harustudy.backend.content.domain.Content;
import harustudy.backend.participant.domain.Participant;
import harustudy.backend.participant.repository.ParticipantRepository;
import harustudy.backend.study.domain.Study;
import harustudy.backend.study.repository.StudyRepository;
import harustudy.backend.view.dto.SubmitterResponse;
import harustudy.backend.view.dto.SubmittersResponse;
import harustudy.backend.view.exception.CurrentCycleContentNotExistsException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor
@Service
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge할 때 트랜잭션 설정 추가해줘야겠네용

public class PollingService {

private final StudyRepository studyRepository;
private final ParticipantRepository participantRepository;

public SubmittersResponse findSubmitters(Long studyId) {
Study study = studyRepository.findByIdIfExists(studyId);
List<Participant> participants = participantRepository.findByStudy(study);
return generateSubmitterResponses(study, participants);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 구현한것도 마찬가지로 일단 구현 해놓고 최적화를 고려해야겠네요

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에 fetch join 하죠

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

최적화할게 많겠네요


private SubmittersResponse generateSubmitterResponses(Study study, List<Participant> participants) {
List<SubmitterResponse> submitterResponses = new ArrayList<>();

for (Participant participant : participants) {
Content currentCycleContent = extractCurrentCycleContent(study, participant);

submitterResponses.add(SubmitterResponse.of(
participant.getNickname(),
SubmitterCheckingStrategy.isSubmitted(study.getStep(), currentCycleContent)));
}
return SubmittersResponse.from(submitterResponses);
}

private Content extractCurrentCycleContent(Study study, Participant participant) {
return participant.getContents().stream()
.filter(content -> content.hasSameCycleWith(study))
.findFirst()
.orElseThrow(CurrentCycleContentNotExistsException::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package harustudy.backend.view.service;

import harustudy.backend.content.domain.Content;
import harustudy.backend.participant.domain.Step;
import harustudy.backend.view.exception.SubmitNotAllowedStepException;

import java.util.Arrays;
import java.util.function.Function;

public enum SubmitterCheckingStrategy {

PLANNING(Step.PLANNING, Content::isPlanWritten),
RETROSPECT(Step.RETROSPECT, Content::isRetrospectWritten);

private final Step step;
private final Function<Content, Boolean> strategy;

SubmitterCheckingStrategy(Step step, Function<Content, Boolean> strategy) {
this.step = step;
this.strategy = strategy;
}

public static boolean isSubmitted(Step step, Content content) {
return Arrays.stream(values())
.filter(each -> each.step.equals(step))
.map(each -> each.strategy)
.findFirst()
.orElseThrow(SubmitNotAllowedStepException::new)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 예외의 의미는 무엇인가요?
각 step마다 strategy가 없으면 발생하는 것 같은데, 예외의 이름이랑 의미가 모호한 것 같습니다

.apply(content);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package harustudy.backend.view.service;

import harustudy.backend.content.domain.Content;
import harustudy.backend.member.domain.LoginType;
import harustudy.backend.member.domain.Member;
import harustudy.backend.participant.domain.Participant;
import harustudy.backend.study.domain.Study;
import harustudy.backend.view.dto.SubmitterResponse;
import harustudy.backend.view.dto.SubmittersResponse;
import harustudy.backend.view.exception.SubmitNotAllowedStepException;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

@SuppressWarnings("NonAsciiCharacters")
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@Transactional
@SpringBootTest
class PollingServiceTest {

@Autowired
private EntityManager entityManager;

@Autowired
private PollingService pollingService;

private Study study;
private Content content1;
private Content content2;

@BeforeEach
void setUp() {
study = new Study("study", 1, 20);
Member member1 = new Member("name1", "email", "imageUrl", LoginType.GUEST);
Member member2 = new Member("name2", "email", "imageUrl", LoginType.GUEST);
Participant participant1 = new Participant(study, member1, "nickname1");
Participant participant2 = new Participant(study, member2, "nickname2");
content1 = new Content(participant1, 1);
content2 = new Content(participant2, 1);

entityManager.persist(study);
entityManager.persist(member1);
entityManager.persist(member2);
entityManager.persist(participant1);
entityManager.persist(participant2);
entityManager.persist(content1);
entityManager.persist(content2);
}

@Test
void 대기_상태에서_제출_인원을_확인하려_하면_예외가_발생한다() {
// given, when, then
entityManager.flush();
entityManager.clear();

assertThatThrownBy(() -> pollingService.findSubmitters(study.getId()))
.isInstanceOf(SubmitNotAllowedStepException.class);
}

@Test
void 계획_단계에서는_제출_인원을_확인할_수_있다() {
study.proceed();
content1.changePlan(Map.of("content", "written"));

entityManager.flush();
entityManager.clear();

// when
SubmittersResponse submitters = pollingService.findSubmitters(study.getId());

// then
SubmittersResponse expected = new SubmittersResponse(List.of(
new SubmitterResponse("nickname1", true),
new SubmitterResponse("nickname2", false)
));

assertThat(submitters).usingRecursiveComparison()
.isEqualTo(expected);
}

@Test
void 진행_단계에서는_제출_인원을_확인하려_하면_예외가_발생한다() {
// given
study.proceed();
study.proceed();

entityManager.flush();
entityManager.clear();

// when, then
assertThatThrownBy(() -> pollingService.findSubmitters(study.getId()))
.isInstanceOf(SubmitNotAllowedStepException.class);
}

@Test
void 회고_단계에서는_제출_인원을_확인할_수_있다() {
study.proceed();
study.proceed();
study.proceed();
content1.changeRetrospect(Map.of("content", "written"));
content2.changeRetrospect(Map.of("content", "written"));

entityManager.flush();
entityManager.clear();

// when
SubmittersResponse submitters = pollingService.findSubmitters(study.getId());

// then
SubmittersResponse expected = new SubmittersResponse(List.of(
new SubmitterResponse("nickname1", true),
new SubmitterResponse("nickname2", true)
));

assertThat(submitters).usingRecursiveComparison()
.isEqualTo(expected);
}

@Test
void 스터디가_종료한_뒤에는_제출_인원을_확인하려_하면_예외가_발생한다() {
// given
study.proceed();
study.proceed();
study.proceed();
study.proceed();

entityManager.flush();
entityManager.clear();

// when, then
assertThatThrownBy(() -> pollingService.findSubmitters(study.getId()))
.isInstanceOf(SubmitNotAllowedStepException.class);
}
}