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

feat: 연애 모의고사를 작성한다 #28

Merged
merged 67 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
1febece
feat: 연애고사 과목 등록 구현
devholic22 May 5, 2024
6bd6bbc
feat: 연애고사 과목 질문 등록 구현
devholic22 May 5, 2024
275b29c
feat: 연애 모의고사 과목, 항목, 문항 등록 통합 구현
devholic22 May 5, 2024
504a8fa
fix: SurveyAnswer NPE 수정
devholic22 May 5, 2024
30d0c1d
test: SurveyTest 작성
devholic22 May 5, 2024
233bff1
test: SurveyServiceTest 작성
devholic22 May 5, 2024
27824f8
test: SurveyJpaRepositoryTest 작성
devholic22 May 5, 2024
9aa4e33
fix: 잘못된 valid 어노테이션 수정
devholic22 May 5, 2024
9877159
refactor: 생성 반환 타입 수정
devholic22 May 5, 2024
07a4dcb
refactor: 설문 생성 반환 시 id만 사용되도록 수정
devholic22 May 5, 2024
429b82b
fix: 설문 등록 시 영속화된 설문의 id를 반환하도록 수정
devholic22 May 5, 2024
2fbe81c
test: SurveyControllerTest 작성
devholic22 May 5, 2024
26e61ed
feat: 회원 설문 등록 구현
devholic22 May 6, 2024
8ba0dd3
refactor: 설문 테스트 패키지 수정
devholic22 May 6, 2024
2a2542a
refactor: 양방향 관계 해제
devholic22 May 6, 2024
72ddf38
test: SurveyQuestionTest 작성
devholic22 May 6, 2024
470d2a7
test: SurveyAnswerTest 작성
devholic22 May 6, 2024
7225cb3
refactor: SurveyQuestion equals&hashcode 추가
devholic22 May 6, 2024
df774fb
test: MemberSurveyTest 작성
devholic22 May 6, 2024
7338a85
test: SurveyTest 테스트 추가
devholic22 May 6, 2024
46c47d4
refactor: Fixture static화
devholic22 May 6, 2024
4893885
refactor: 답변에 상대 번호 들어가도록 수정
devholic22 May 6, 2024
06cf507
refactor: 필요없는 메서드 삭제
devholic22 May 6, 2024
57b6e11
refactor: Survey 리팩터링
devholic22 May 7, 2024
19a05d2
refactor: SurveyQuestion 리팩터링
devholic22 May 7, 2024
bc4ad88
refactor: 설문 답변 생성 예외 추가
devholic22 May 7, 2024
8690a98
refactor: Survey 리팩터링
devholic22 May 7, 2024
13d3d5a
test: SurveyTest 예외 추가
devholic22 May 7, 2024
55d28b5
test: SurveyQuestionTest 예외 추가
devholic22 May 7, 2024
0209e4d
test: SurveyAnswer 번호 유효성 검증 추가
devholic22 May 7, 2024
e584e6a
refactor: Survey 생성 메서드명 수정
devholic22 May 7, 2024
2153d8b
refactor: 설문 대신 연애고사로 수정
devholic22 May 7, 2024
3e20b4b
test: SurveyJpaRepositoryTest 추가 작성
devholic22 May 7, 2024
59b1735
refactor: SurveyCreateRequest 예외 Fixture 등록
devholic22 May 7, 2024
9faf66e
test: 답변 중복 등록 예외 테스트 작성
devholic22 May 7, 2024
07b176a
test: 연애고사 과목 등록 인수 테스트 작성
devholic22 May 7, 2024
f12f378
docs: survey.adoc 링크 경로 수정
devholic22 May 8, 2024
73ab3e5
refactor: 연애과목 생성 응답 형식 수정
devholic22 May 8, 2024
fb794f3
docs: index.adoc 연애고사 API 링크 추가
devholic22 May 8, 2024
1d8437b
test: MemberSurveyServiceTest 작성
devholic22 May 13, 2024
5a9dbc3
refactor: MemberSurveys 추가
devholic22 May 13, 2024
017a243
test: MemberSurveysServiceTest 작성
devholic22 May 13, 2024
03c3474
refactor: submitSurveys 주석 삭제
devholic22 May 13, 2024
619104b
test: MemberSurveysTest 작성
devholic22 May 13, 2024
bcfe8d2
test: MemberSurveysJpaRepositoryTest 작성
devholic22 May 13, 2024
2c7897a
refactor: then 추가
devholic22 May 13, 2024
e2f6d37
refactor: 필요없는 SurveyRepository 의존 삭제
devholic22 May 13, 2024
6e7cfce
feat: 회원 연애고사 컨트롤러 작성
devholic22 May 13, 2024
81d8368
refactor: valid message 추가
devholic22 May 13, 2024
7e50bef
feat: 연애고사 매칭 회원 목록 조회 구현
devholic22 May 15, 2024
9bbf8e7
refactor: 함수명 수정
devholic22 May 15, 2024
2b9b007
refactor: 수정 기능 삭제
devholic22 May 15, 2024
974ac87
refactor: 리포지터리 이름 변경
devholic22 May 16, 2024
a722952
refactor: 회원 연애고사 응시 URL 변경
devholic22 May 16, 2024
c07576d
feat: 회원이 응시한 과목 조회 기능 개발
devholic22 May 16, 2024
d328f5e
test: 회원 연애고사 매칭 통합 테스트 작성
devholic22 May 16, 2024
f67c755
test: 회원 연애고사 조회 서비스 테스트 작성
devholic22 May 16, 2024
d34bebd
refactor: AuthMember 인터셉터 추가
devholic22 May 16, 2024
4dcad34
Merge branch 'develop' into feat/25
devholic22 May 18, 2024
0e4bc9c
refactor: 리턴 변수 인라인화
devholic22 May 18, 2024
52e1155
refactor: 함수명 문법 수정
devholic22 May 18, 2024
598fea8
refactor: SurveyCreateRequest ui DTO 의존 제거
devholic22 May 19, 2024
b613460
refactor: SurveySubmitRequest ui DTO 의존 제거
devholic22 May 19, 2024
4d2c5d5
refactor: 동사 중복 사용 제거
devholic22 May 19, 2024
e653c88
refactor: 동사 중복 사용 제거
devholic22 May 19, 2024
6768ae9
refactor: surveyId 중복 예외 메서드 분할
devholic22 May 19, 2024
446756c
Merge branch 'feat/25' of https://github.com/sosow0212/atwoz into fea…
devholic22 May 19, 2024
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
2 changes: 2 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@
* link:member.adoc[회원 정보 API]
* link:mission.adoc[미션 API]
* link:membermissions.adoc[회원 미션 API]
* link:survey.adoc[연애고사 API]
* link:membersurveys.adoc[회원 연애고사 API]
38 changes: 38 additions & 0 deletions src/docs/asciidoc/membersurveys.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
:toc: left
:source-highlighter: highlightjs
:sectlinks:
:toclevels: 2
:sectlinks:
:sectnums:

== MemberSurveys

=== 연애고사 응시 (POST /api/members/me/surveys)

==== 요청
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_응시/request-headers.adoc[]
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_응시/request-fields.adoc[]
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_응시/http-request.adoc[]

==== 응답
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_응시/http-response.adoc[]

=== 연애고사 조회 (GET /api/members/me/surveys/{surveyId})

==== 요청
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_조회/request-headers.adoc[]
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_조회/http-request.adoc[]

==== 응답
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_조회/response-fields.adoc[]
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_조회/http-response.adoc[]

=== 연애고사 매칭 조회 (GET /api/members/me/surveys/match)

==== 요청
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_매칭_조회/request-headers.adoc[]
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_매칭_조회/http-request.adoc[]

==== 응답
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_매칭_조회/response-fields.adoc[]
include::{snippets}/member-surveys-controller-web-mvc-test/회원_연애고사_매칭_조회/http-response.adoc[]
18 changes: 18 additions & 0 deletions src/docs/asciidoc/survey.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
:toc: left
:source-highlighter: highlightjs
:sectlinks:
:toclevels: 2
:sectlinks:
:sectnums:

== Survey

=== 설문 생성 (POST /api/surveys)

==== 요청
include::{snippets}/survey-controller-web-mvc-test/연애_모의고사_과목_생성/request-fields.adoc[]
include::{snippets}/survey-controller-web-mvc-test/연애_모의고사_과목_생성/http-request.adoc[]

==== 응답
include::{snippets}/survey-controller-web-mvc-test/연애_모의고사_과목_생성/response-headers.adoc[]
include::{snippets}/survey-controller-web-mvc-test/연애_모의고사_과목_생성/http-response.adoc[]
7 changes: 5 additions & 2 deletions src/main/java/com/atwoz/member/config/AuthConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import com.atwoz.member.ui.auth.interceptor.PathMatcherInterceptor;
import com.atwoz.member.ui.auth.interceptor.TokenRegenerateInterceptor;
import com.atwoz.member.ui.auth.support.resolver.AuthArgumentResolver;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;
import static com.atwoz.member.ui.auth.interceptor.HttpMethod.DELETE;
import static com.atwoz.member.ui.auth.interceptor.HttpMethod.GET;
import static com.atwoz.member.ui.auth.interceptor.HttpMethod.OPTIONS;
Expand All @@ -39,7 +39,9 @@ private HandlerInterceptor parseMemberIdFromTokenInterceptor() {
return new PathMatcherInterceptor(parseMemberIdFromTokenInterceptor)
.excludePathPattern("/**", OPTIONS)
.addPathPatterns("/api/info/**", GET, POST, PATCH)
.addPathPatterns("/api/members/me/missions/**", GET, POST, PATCH);
.addPathPatterns("/api/surveys/**", GET, POST)
.addPathPatterns("/api/members/me/missions/**", GET, POST, PATCH)
.addPathPatterns("/api/members/me/surveys/**", GET, POST);
}

/**
Expand All @@ -49,6 +51,7 @@ private HandlerInterceptor loginValidCheckerInterceptor() {
return new PathMatcherInterceptor(loginValidCheckerInterceptor)
.excludePathPattern("/**", OPTIONS)
.excludePathPattern("/api/missions/**", GET, POST, PATCH, DELETE)
.excludePathPattern("/api/surveys/**", GET, POST)
.addPathPatterns("/api/members/**", GET, POST, PATCH, DELETE);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.atwoz.survey.application.membersurvey;

import com.atwoz.survey.domain.membersurvey.MemberSurveysRepository;
import com.atwoz.survey.exception.membersurvey.exceptions.MemberSurveysNotFoundException;
import com.atwoz.survey.infrastructure.membersurvey.dto.MemberSurveyResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class MemberSurveysQueryService {

private final MemberSurveysRepository memberSurveysRepository;

public MemberSurveyResponse findMemberSurvey(final Long memberId, final Long surveyId) {
return memberSurveysRepository.findMemberSurvey(memberId, surveyId)
.orElseThrow(MemberSurveysNotFoundException::new);
}

public List<Long> findMatchMembers(final Long memberId) {
return memberSurveysRepository.findMatchMembers(memberId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.atwoz.survey.application.membersurvey;

import com.atwoz.survey.application.membersurvey.dto.SurveySubmitRequest;
import com.atwoz.survey.domain.membersurvey.MemberSurveys;
import com.atwoz.survey.domain.membersurvey.MemberSurveysRepository;
import com.atwoz.survey.domain.membersurvey.dto.SurveySubmitCreateDto;
import com.atwoz.survey.domain.survey.Survey;
import com.atwoz.survey.domain.survey.SurveyRepository;
import com.atwoz.survey.domain.survey.dto.SurveyComparisonRequest;
import com.atwoz.survey.exception.membersurvey.exceptions.RequiredSurveyNotSubmittedException;
import com.atwoz.survey.exception.membersurvey.exceptions.SurveySubmitDuplicateException;
import com.atwoz.survey.exception.survey.exceptions.SurveyNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@RequiredArgsConstructor
@Transactional
@Service
public class MemberSurveysService {

private final MemberSurveysRepository memberSurveysRepository;
private final SurveyRepository surveyRepository;

public void submitSurvey(final Long memberId, final List<SurveySubmitRequest> requests) {
validateIsAllSubmittedRequiredSurveys(requests);

MemberSurveys memberSurveys = memberSurveysRepository.findByMemberId(memberId)
.orElseGet(() -> createNewMemberSurveysWithMemberId(memberId));
validateEachSurveyRequestIsValid(requests);
memberSurveys.submitSurveys(convertSurveySubmits(requests));
}

private void validateIsAllSubmittedRequiredSurveys(final List<SurveySubmitRequest> requests) {
List<Long> requiredSurveyIds = surveyRepository.findAllRequiredSurveyIds();
Set<Long> submittedSurveyIds = extractSubmittedSurveyIds(requests);

if (!submittedSurveyIds.containsAll(requiredSurveyIds)) {
throw new RequiredSurveyNotSubmittedException();
}
}

private Set<Long> extractSubmittedSurveyIds(final List<SurveySubmitRequest> requests) {
List<Long> submittedSurveyIds = requests.stream()
.map(SurveySubmitRequest::surveyId)
.toList();
HashSet<Long> surveyIds = new HashSet<>(submittedSurveyIds);

validateSurveyIdNotDuplicated(surveyIds, submittedSurveyIds);

return surveyIds;
}

private void validateSurveyIdNotDuplicated(final HashSet<Long> surveyIds, final List<Long> submittedSurveyIds) {
if (surveyIds.size() != submittedSurveyIds.size()) {
throw new SurveySubmitDuplicateException();
}
}

private MemberSurveys createNewMemberSurveysWithMemberId(final Long memberId) {
MemberSurveys memberSurveys = MemberSurveys.createWithMemberId(memberId);
return memberSurveysRepository.save(memberSurveys);
}

private void validateEachSurveyRequestIsValid(final List<SurveySubmitRequest> requests) {
requests.forEach(request -> {
Survey survey = surveyRepository.findById(request.surveyId())
.orElseThrow(SurveyNotFoundException::new);
SurveyComparisonRequest comparisonRequest = SurveyComparisonRequest.from(request);
survey.validateIsValidSubmitSurveyRequest(comparisonRequest);
});
}

private List<SurveySubmitCreateDto> convertSurveySubmits(final List<SurveySubmitRequest> requests) {
return requests.stream()
.map(SurveySubmitCreateDto::from)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.atwoz.survey.application.membersurvey.dto;

import jakarta.validation.constraints.NotNull;

public record SurveyQuestionSubmitRequest(
@NotNull(message = "응시할 연애고사 질문 id가 작성되어야 합니다.")
Long questionId,
@NotNull(message = "응시할 연애고사 답변 번호가 작성되어야 합니다.")
Integer answerNumber
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.atwoz.survey.application.membersurvey.dto;

import jakarta.validation.constraints.NotNull;

import java.util.List;

public record SurveySubmitRequest(
@NotNull(message = "응시할 연애고사 과목 id가 작성되어야 합니다.")
Long surveyId,
@NotNull(message = "응시할 연애고사 질문이 작성되어야 합니다.")
List<SurveyQuestionSubmitRequest> questions
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.atwoz.survey.application.survey;

import com.atwoz.survey.application.survey.dto.SurveyCreateRequest;
import com.atwoz.survey.domain.survey.Survey;
import com.atwoz.survey.domain.survey.SurveyRepository;
import com.atwoz.survey.domain.survey.dto.SurveyCreateDto;
import com.atwoz.survey.exception.survey.exceptions.SurveyNameAlreadyExistException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Transactional
@Service
public class SurveyService {

private final SurveyRepository surveyRepository;

public Survey addSurvey(final SurveyCreateRequest request) {
validateIsAlreadyUsedName(request.name());
SurveyCreateDto surveyRequest = SurveyCreateDto.from(request);

return surveyRepository.save(Survey.from(surveyRequest));
}

private void validateIsAlreadyUsedName(final String name) {
if (surveyRepository.isExistedByName(name)) {
throw new SurveyNameAlreadyExistException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.atwoz.survey.application.survey.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record SurveyAnswerCreateRequest(
@NotNull(message = "답변 번호가 작성되어야 합니다. (ex: 1)")
Integer number,

@NotBlank(message = "답변 내용이 작성되어야 합니다. (ex: 무계획 여행)")
String answer
) {

public static SurveyAnswerCreateRequest of(final Integer number, final String answer) {
return new SurveyAnswerCreateRequest(number, answer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.atwoz.survey.application.survey.dto;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import java.util.List;

public record SurveyCreateRequest(
@NotBlank(message = "연애고사 과목 이름이 있어야 합니다.")
String name,

@NotNull(message = "연애고사 과목 필수 여부가 있어야 합니다.")
Boolean required,

@Valid
List<SurveyQuestionCreateRequest> questions
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.atwoz.survey.application.survey.dto;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;

import java.util.List;

public record SurveyQuestionCreateRequest(
@NotBlank(message = "질문 내용이 작성되어야 합니다. (ex: 다음 중 좋아하는 여행 스타일은?)")
String description,

@Valid
List<SurveyAnswerCreateRequest> answers
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.atwoz.survey.domain.membersurvey;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@EqualsAndHashCode(exclude = "id")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Entity
public class MemberSurvey {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private Long surveyId;

@Column(nullable = false)
private Long questionId;

@Column(nullable = false)
private Integer answerNumber;

private MemberSurvey(final Long surveyId, final Long questionId, final Integer answerNumber) {
this.surveyId = surveyId;
this.questionId = questionId;
this.answerNumber = answerNumber;
}

public static MemberSurvey of(final Long surveyId, final Long questionId, final Integer answerNumber) {
return new MemberSurvey(surveyId, questionId, answerNumber);
}
}
Loading
Loading