Skip to content

Commit

Permalink
[#24] feat: 답변 작성 API 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
NaMinhyeok committed Feb 4, 2025
1 parent b1dfe13 commit f4878af
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public class CustomException extends RuntimeException {
new CustomException(ErrorType.INCORRECT_TOKEN_FORMAT);
public static final CustomException INVALID_APPLE_ID_TOKEN =
new CustomException(ErrorType.INVALID_APPLE_ID_TOKEN);
public static final CustomException MEMBER_NOT_FOUND =
new CustomException(ErrorType.MEMBER_NOT_FOUND);
public static final CustomException SURVEY_OPTION_NOT_FOUND =
new CustomException(ErrorType.SURVEY_OPTION_NOT_FOUND);
public static final CustomException SURVEY_NOT_FOUND =
new CustomException(ErrorType.SURVEY_NOT_FOUND);

private final ErrorType errorType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public enum ErrorType {
// business
INVALID_APPLE_ID_TOKEN(
HttpStatus.BAD_REQUEST, ErrorCode.E400, "유효하지 않은 애플로그인입니다.", LogLevel.WARN),

SURVEY_OPTION_NOT_FOUND(HttpStatus.NOT_FOUND, ErrorCode.E404, "존재하지 않는 설문 옵션입니다.", LogLevel.WARN),

SURVEY_NOT_FOUND(HttpStatus.NOT_FOUND, ErrorCode.E404, "존재하지 않는 설문입니다.", LogLevel.WARN),
;

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.nexters.jaknaesocore.domain.survey.dto;

public record SurveySubmissionServiceRequest(Long optionId, String comment) {}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.nexters.jaknaesocore.common.model.BaseTimeEntity;
import org.nexters.jaknaesocore.common.support.error.CustomException;

@Getter
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
Expand Down Expand Up @@ -34,4 +35,11 @@ public void addOption(final SurveyOption option) {
}

public abstract SurveyType getSurveyType();

public SurveyOption getOptionById(final Long optionId) {
return options.stream()
.filter(option -> option.getId().equals(optionId))
.findFirst()
.orElseThrow(() -> CustomException.SURVEY_OPTION_NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,26 @@ public class SurveySubmission extends BaseTimeEntity {

@Builder
private SurveySubmission(
Member member, Survey survey, SurveyOption selectedOption, String comment) {
final Member member,
final Survey survey,
final SurveyOption selectedOption,
final String comment) {
this.member = member;
this.survey = survey;
this.selectedOption = selectedOption;
this.comment = comment;
}

public static SurveySubmission create(
final Member member,
final Survey survey,
final SurveyOption selectedOption,
final String comment) {
return SurveySubmission.builder()
.member(member)
.survey(survey)
.selectedOption(selectedOption)
.comment(comment)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.nexters.jaknaesocore.common.support.error.CustomException;
import org.nexters.jaknaesocore.domain.member.model.Member;
import org.nexters.jaknaesocore.domain.member.repository.MemberRepository;
import org.nexters.jaknaesocore.domain.survey.dto.SurveyResponse;
import org.nexters.jaknaesocore.domain.survey.model.Survey;
import org.nexters.jaknaesocore.domain.survey.model.SurveyBundle;
import org.nexters.jaknaesocore.domain.survey.model.SurveySubmission;
import org.nexters.jaknaesocore.domain.survey.model.SurveySubscriptions;
import org.nexters.jaknaesocore.domain.survey.dto.SurveySubmissionServiceRequest;
import org.nexters.jaknaesocore.domain.survey.model.*;
import org.nexters.jaknaesocore.domain.survey.repository.SurveyBundleRepository;
import org.nexters.jaknaesocore.domain.survey.repository.SurveyRepository;
import org.nexters.jaknaesocore.domain.survey.repository.SurveySubmissionRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -16,8 +18,10 @@
@Service
public class SurveyService {

private final MemberRepository memberRepository;
private final SurveyBundleRepository surveyBundleRepository;
private final SurveySubmissionRepository surveySubmissionRepository;
private final SurveyRepository surveyRepository;

@Transactional(readOnly = true)
public SurveyResponse getNextSurvey(final Long bundleId, final Long memberId) {
Expand All @@ -36,4 +40,20 @@ private List<Survey> getSubmittedSurvey(final Long memberId) {
surveySubmissionRepository.findByMember_IdAndDeletedAtIsNull(memberId);
return new SurveySubscriptions(surveySubmissionsByMember).getSubmittedSurvey(memberId);
}

@Transactional
public void submitSurvey(Long surveyId, Long memberId, SurveySubmissionServiceRequest request) {
Survey survey =
surveyRepository.findById(surveyId).orElseThrow(() -> CustomException.SURVEY_NOT_FOUND);
SurveyOption surveyOption = survey.getOptionById(request.optionId());
Member member =
memberRepository
.findByIdAndDeletedAtIsNull(memberId)
.orElseThrow(() -> CustomException.MEMBER_NOT_FOUND);

SurveySubmission surveySubmission =
SurveySubmission.create(member, survey, surveyOption, request.comment());

surveySubmissionRepository.save(surveySubmission);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.nexters.jaknaesocore.domain.survey.model;

import static org.junit.jupiter.api.Assertions.*;

import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.nexters.jaknaesocore.common.support.error.CustomException;
import org.springframework.test.util.ReflectionTestUtils;

class SurveyTest {

@Test
void 설문의_선택지를_ID를_통해_찾는다() {
// given
SurveyBundle surveyBundle = new SurveyBundle();
BalanceSurvey survey = new BalanceSurvey("설문", surveyBundle);

SurveyOption option1 = createSurveyOptionWithId(survey, "옵션1", 1L);
SurveyOption option2 = createSurveyOptionWithId(survey, "옵션2", 2L);
SurveyOption option3 = createSurveyOptionWithId(survey, "옵션3", 3L);

// when
SurveyOption foundOption = survey.getOptionById(2L);

// then
BDDAssertions.then(foundOption).extracting("id", "content").containsExactly(2L, "옵션2");
}

@Test
void 설문의_선택지를_ID를_통해_찾을_수_없을_때_예외를_발생한다() {
// given
SurveyBundle surveyBundle = new SurveyBundle();
BalanceSurvey survey = new BalanceSurvey("설문", surveyBundle);

SurveyOption option1 = createSurveyOptionWithId(survey, "옵션1", 1L);
SurveyOption option2 = createSurveyOptionWithId(survey, "옵션2", 2L);
SurveyOption option3 = createSurveyOptionWithId(survey, "옵션3", 3L);
// when
// then
BDDAssertions.thenThrownBy(() -> survey.getOptionById(4L))
.isInstanceOf(CustomException.class)
.isEqualTo(CustomException.SURVEY_OPTION_NOT_FOUND);
}

private SurveyOption createSurveyOptionWithId(Survey survey, String content, Long id) {
SurveyOption option = SurveyOption.builder().survey(survey).content(content).build();
ReflectionTestUtils.setField(option, "id", id);
return option;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,37 @@
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.nexters.jaknaesocore.common.support.IntegrationTest;
import org.nexters.jaknaesocore.common.support.error.CustomException;
import org.nexters.jaknaesocore.domain.member.model.Member;
import org.nexters.jaknaesocore.domain.member.repository.MemberRepository;
import org.nexters.jaknaesocore.domain.survey.dto.SurveyResponse;
import org.nexters.jaknaesocore.domain.survey.dto.SurveySubmissionServiceRequest;
import org.nexters.jaknaesocore.domain.survey.model.*;
import org.nexters.jaknaesocore.domain.survey.repository.SurveyBundleRepository;
import org.nexters.jaknaesocore.domain.survey.repository.SurveyOptionRepository;
import org.nexters.jaknaesocore.domain.survey.repository.SurveyRepository;
import org.nexters.jaknaesocore.domain.survey.repository.SurveySubmissionRepository;
import org.springframework.beans.factory.annotation.Autowired;

class SurveyServiceTest extends IntegrationTest {
@Autowired private SurveyService surveyService;

@Autowired private MemberRepository memberRepository;
@Autowired private SurveySubmissionRepository surveySubmissionRepository;
@Autowired private SurveyBundleRepository surveyBundleRepository;
@Autowired private SurveyRepository surveyRepository;
@Autowired private SurveyOptionRepository surveyOptionRepository;

@AfterEach
void tearDown() {
surveyOptionRepository.deleteAll();
surveyRepository.deleteAll();
surveyBundleRepository.deleteAll();
memberRepository.deleteAll();
surveySubmissionRepository.deleteAllInBatch();
surveyOptionRepository.deleteAllInBatch();
surveyRepository.deleteAllInBatch();
surveyBundleRepository.deleteAllInBatch();
memberRepository.deleteAllInBatch();
}

@DisplayName("설문을 조회한다.")
Expand Down Expand Up @@ -61,4 +67,105 @@ void getNextSurvey() {
.extracting("id", "optionContents")
.containsExactly(tuple(option.getId(), "질문 옵션 내용"));
}

@DisplayName("submitSurvey 메서드는")
@Nested
class submitSurvey {

@DisplayName("회원이 존재하지 않으면")
@Nested
class whenMemberNotFound {
@Test
@DisplayName("예외를 발생시킨다")
void throwMemberNotFoundException() {
// given
Member member = Member.create();
memberRepository.save(member);
SurveyBundle surveyBundle = new SurveyBundle();
surveyBundleRepository.save(surveyBundle);
BalanceSurvey balanceSurvey = new BalanceSurvey("질문내용", surveyBundle);
surveyRepository.save(balanceSurvey);
List<KeywordScore> scores =
List.of(
KeywordScore.builder().keyword(Keyword.ACHIEVEMENT).score(BigDecimal.ONE).build(),
KeywordScore.builder().keyword(Keyword.BENEVOLENCE).score(BigDecimal.TWO).build());
SurveyOption option =
SurveyOption.builder().survey(balanceSurvey).scores(scores).content("질문 옵션 내용").build();
surveyOptionRepository.save(option);

SurveySubmissionServiceRequest request =
new SurveySubmissionServiceRequest(option.getId(), "나는 행복한게 좋으니까");

// when
// then
thenThrownBy(() -> surveyService.submitSurvey(balanceSurvey.getId(), 0L, request))
.isEqualTo(CustomException.MEMBER_NOT_FOUND);
}
}

@DisplayName("설문을 저장한다")
@Nested
class shouldSubmitted {
@Test
void 설문에_응답을_제출한다() {
// given
Member member = Member.create();
memberRepository.save(member);
SurveyBundle surveyBundle = new SurveyBundle();
surveyBundleRepository.save(surveyBundle);
BalanceSurvey balanceSurvey = new BalanceSurvey("질문내용", surveyBundle);
surveyRepository.save(balanceSurvey);
List<KeywordScore> scores =
List.of(
KeywordScore.builder().keyword(Keyword.ACHIEVEMENT).score(BigDecimal.ONE).build(),
KeywordScore.builder().keyword(Keyword.BENEVOLENCE).score(BigDecimal.TWO).build());
SurveyOption option =
SurveyOption.builder().survey(balanceSurvey).scores(scores).content("질문 옵션 내용").build();
surveyOptionRepository.save(option);

SurveySubmissionServiceRequest request =
new SurveySubmissionServiceRequest(option.getId(), "나는 행복한게 좋으니까");

// when
surveyService.submitSurvey(balanceSurvey.getId(), member.getId(), request);
// then
List<SurveySubmission> submissions = surveySubmissionRepository.findAll();
then(submissions).hasSize(1);
then(submissions.getFirst())
.extracting("member.id", "survey.id", "selectedOption.id", "comment")
.containsExactly(member.getId(), balanceSurvey.getId(), option.getId(), "나는 행복한게 좋으니까");
}
}

@DisplayName("설문이 존재하지 않으면")
@Nested
class whenSurveyNotFound {
@Test
@DisplayName("예외를 발생시킨다")
void throwSurveyNotFoundException() {
// given
Member member = Member.create();
memberRepository.save(member);
SurveyBundle surveyBundle = new SurveyBundle();
surveyBundleRepository.save(surveyBundle);
BalanceSurvey balanceSurvey = new BalanceSurvey("질문내용", surveyBundle);
surveyRepository.save(balanceSurvey);
List<KeywordScore> scores =
List.of(
KeywordScore.builder().keyword(Keyword.ACHIEVEMENT).score(BigDecimal.ONE).build(),
KeywordScore.builder().keyword(Keyword.BENEVOLENCE).score(BigDecimal.TWO).build());
SurveyOption option =
SurveyOption.builder().survey(balanceSurvey).scores(scores).content("질문 옵션 내용").build();
surveyOptionRepository.save(option);

SurveySubmissionServiceRequest request =
new SurveySubmissionServiceRequest(option.getId(), "나는 행복한게 좋으니까");

// when
// then
thenThrownBy(() -> surveyService.submitSurvey(0L, member.getId(), request))
.isEqualTo(CustomException.SURVEY_NOT_FOUND);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package org.nexters.jaknaesoserver.domain.survey.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.nexters.jaknaesocore.common.support.response.ApiResponse;
import org.nexters.jaknaesocore.domain.survey.dto.SurveyResponse;
import org.nexters.jaknaesocore.domain.survey.service.SurveyService;
import org.nexters.jaknaesoserver.domain.auth.model.CustomUserDetails;
import org.nexters.jaknaesoserver.domain.survey.controller.dto.SurveySubmissionRequest;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
Expand All @@ -23,4 +23,14 @@ public ApiResponse<SurveyResponse> getNextSurvey(
@AuthenticationPrincipal CustomUserDetails member, @PathVariable Long bundleId) {
return ApiResponse.success(surveyService.getNextSurvey(bundleId, member.getMemberId()));
}

@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping("/submit/{surveyId}")
public ApiResponse<?> submitSurvey(
@AuthenticationPrincipal CustomUserDetails member,
@PathVariable Long surveyId,
@Valid @RequestBody SurveySubmissionRequest request) {
surveyService.submitSurvey(surveyId, member.getMemberId(), request.toServiceRequest());
return ApiResponse.success();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.nexters.jaknaesoserver.domain.survey.controller.dto;

import jakarta.validation.constraints.NotNull;
import org.nexters.jaknaesocore.domain.survey.dto.SurveySubmissionServiceRequest;

public record SurveySubmissionRequest(@NotNull Long optionId, String comment) {
public SurveySubmissionServiceRequest toServiceRequest() {
return new SurveySubmissionServiceRequest(optionId(), comment());
}
}
Loading

0 comments on commit f4878af

Please sign in to comment.