Skip to content

Commit

Permalink
feat/LS-24: 특정 회고 질문 목록 조회 구현 (#33)
Browse files Browse the repository at this point in the history
* add: 회고 관련 예외 추가

* add: dto 추가

* feat: 컨트롤러 및 스웨거 추가

* chore: 엔티티 변경 및 순서 추가

* feat: 회고 진행중 검증 로직 추가

* chore: 순서 컬럼 추가로 인한 변경

* feat: 특정 회고 질문 목록 조회 로직 구현

* chore: 질문타입 추가
  • Loading branch information
mikekks authored Jul 14, 2024
1 parent f9a29a4 commit dee5506
Show file tree
Hide file tree
Showing 14 changed files with 221 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.layer.domain.question.controller;

import org.layer.common.annotation.MemberId;
import org.layer.domain.question.controller.dto.response.QuestionListGetResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "질문", description = "질문 관련 API")
public interface QuestionApi {
@Operation(summary = "특정 회고 질문 목록 조회", description = "")
ResponseEntity<QuestionListGetResponse> getRetrospectQuestions(@PathVariable("spaceId") Long spaceId,
@PathVariable("retrospectId") Long retrospectId, @MemberId Long memberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.layer.domain.question.controller;

import java.util.List;

import org.layer.common.annotation.MemberId;
import org.layer.domain.question.controller.dto.response.QuestionGetResponse;
import org.layer.domain.question.controller.dto.response.QuestionListGetResponse;
import org.layer.domain.question.service.QuestionService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
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 lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/space/{spaceId}/retrospect/{retrospectId}/question")
public class QuestionController implements QuestionApi{
private final QuestionService questionService;

@Override
@GetMapping
@PreAuthorize("isAuthenticated()")
public ResponseEntity<QuestionListGetResponse> getRetrospectQuestions(@PathVariable("spaceId") Long spaceId,
@PathVariable("retrospectId") Long retrospectId, @MemberId Long memberId) {

List<QuestionGetResponse> responses = questionService.getRetrospectQuestions(spaceId, retrospectId, memberId)
.questions()
.stream()
.map(q -> QuestionGetResponse.of(q.question(), q.order(), q.questionType()))
.toList();

return ResponseEntity.ok().body(QuestionListGetResponse.of(responses));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.layer.domain.question.controller.dto.response;


public record QuestionGetResponse(
String question,
int order,
String questionType
) {
public static QuestionGetResponse of(String question, int order, String questionType) {
return new QuestionGetResponse(question, order, questionType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.layer.domain.question.controller.dto.response;

import java.util.List;

public record QuestionListGetResponse(
List<QuestionGetResponse> questions
) {
public static QuestionListGetResponse of(List<QuestionGetResponse> questions){
return new QuestionListGetResponse(questions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.layer.domain.question.service;

import static org.layer.common.exception.MemberSpaceRelationExceptionType.*;

import java.util.List;
import java.util.Optional;

import org.layer.domain.question.entity.Question;
import org.layer.domain.question.enums.QuestionOwner;
import org.layer.domain.question.repository.QuestionRepository;
import org.layer.domain.question.service.dto.response.QuestionGetServiceResponse;
import org.layer.domain.question.service.dto.response.QuestionListGetServiceResponse;
import org.layer.domain.retrospect.entity.Retrospect;
import org.layer.domain.retrospect.repository.RetrospectRepository;
import org.layer.domain.space.entity.MemberSpaceRelation;
import org.layer.domain.space.exception.MemberSpaceRelationException;
import org.layer.domain.space.repository.MemberSpaceRelationRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class QuestionService {
private final QuestionRepository questionRepository;
private final MemberSpaceRelationRepository memberSpaceRelationRepository;
private final RetrospectRepository retrospectRepository;

public QuestionListGetServiceResponse getRetrospectQuestions(Long spaceId, Long retrospectId, Long memberId){

// 해당 멤버가 스페이스 소속인지 검증 로직
Optional<MemberSpaceRelation> team = memberSpaceRelationRepository.findBySpaceIdAndMemberId(
spaceId, memberId);
if(team.isEmpty()){
throw new MemberSpaceRelationException(NOT_FOUND_MEMBER_SPACE_RELATION);
}

// 해당 회고가 있는지, PROCEEDING 상태인지 검증
Retrospect retrospect = retrospectRepository.findByIdOrThrow(retrospectId);
retrospect.isProceedingRetrospect();

List<Question> questions = questionRepository.findAllByQuestionOwnerIdAndQuestionOwnerOrderByQuestionOrder(
retrospectId, QuestionOwner.TEAM);

List<QuestionGetServiceResponse> serviceResponses = questions.stream()
.map(q -> QuestionGetServiceResponse.of(q.getContent(), q.getQuestionOrder(), q.getQuestionType().getStyle()))
.toList();

return QuestionListGetServiceResponse.of(serviceResponses);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.layer.domain.question.service.dto.response;

public record QuestionGetServiceResponse(
String question,
int order,
String questionType
) {
public static QuestionGetServiceResponse of(String question, int order, String questionType) {
return new QuestionGetServiceResponse(question, order, questionType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.layer.domain.question.service.dto.response;

import java.util.List;

public record QuestionListGetServiceResponse(
List<QuestionGetServiceResponse> questions
) {
public static QuestionListGetServiceResponse of(List<QuestionGetServiceResponse> questions){
return new QuestionListGetServiceResponse(questions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

import org.layer.domain.answer.entity.Answers;
import org.layer.domain.answer.repository.AnswerRepository;
Expand Down Expand Up @@ -46,17 +47,19 @@ public void createRetrospect(RetrospectCreateServiceRequest request, Long member
Retrospect retrospect = getRetrospect(request);
Retrospect savedRetrospect = retrospectRepository.save(retrospect);

AtomicInteger teamIndex = new AtomicInteger(1);
List<Question> questions = request.questions().stream()
.map(q -> new Question(savedRetrospect.getId(), q, QuestionOwner.TEAM, QuestionType.PLAIN_TEXT))
.map(q -> new Question(savedRetrospect.getId(), q, teamIndex.getAndIncrement(), QuestionOwner.TEAM, QuestionType.PLAIN_TEXT))
.toList();
questionRepository.saveAll(questions);

// 내 회고 폼에 추가
Form form = new Form(memberId, request.title(), request.introduction());
Form savedForm = formRepository.save(form);

AtomicInteger myIndex = new AtomicInteger(1);
List<Question> myQuestions = request.questions().stream()
.map(q -> new Question(savedForm.getId(), q, QuestionOwner.INDIVIDUAL, QuestionType.PLAIN_TEXT))
.map(q -> new Question(savedForm.getId(), q, myIndex.getAndIncrement(), QuestionOwner.INDIVIDUAL, QuestionType.PLAIN_TEXT))
.toList();
questionRepository.saveAll(myQuestions);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.layer.common.exception;

import org.springframework.http.HttpStatus;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public enum RetrospectExceptionType implements ExceptionType{
NOT_PROCEEDING_RETROSPECT(HttpStatus.BAD_REQUEST, "진행중인 회고가 아닙니다."),
NOT_FOUND_RETROSPECT(HttpStatus.NOT_FOUND, "유효한 회고가 존재하지 않습니다.");


private final HttpStatus status;
private final String message;

@Override
public HttpStatus httpStatus() {
return status;
}

@Override
public String message() {
return message;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,33 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Question extends BaseEntity {

/*
questionOwnerId은 retrospectId or memberId 중 하나이다.
*/
@NotNull
private Long questionOwnerId;

@NotNull
private String content;

private int questionOrder;

@NotNull
@Enumerated(EnumType.STRING)
private QuestionOwner questionOwner;

@Column(length = 20)
@NotNull
@Convert(converter = QuestionTypeConverter.class)
@Enumerated(EnumType.STRING)
private QuestionType questionType;

@OneToMany(fetch = FetchType.LAZY, mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<QuestionOption> options = new HashSet<>();

public Question(Long questionOwnerId, String content, QuestionOwner questionOwner, QuestionType questionType) {
public Question(Long questionOwnerId, String content, int questionOrder, QuestionOwner questionOwner, QuestionType questionType) {
this.questionOwnerId = questionOwnerId;
this.content = content;
this.questionOrder = questionOrder;
this.questionOwner = questionOwner;
this.questionType = questionType;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.layer.domain.question.repository;

import java.util.List;

import org.layer.domain.question.entity.Question;
import org.layer.domain.question.enums.QuestionOwner;
import org.springframework.data.jpa.repository.JpaRepository;

public interface QuestionRepository extends JpaRepository<Question, Long> {

List<Question> findAllByQuestionOwnerIdAndQuestionOwnerOrderByQuestionOrder(Long questionOwnerId, QuestionOwner questionOwner);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.layer.domain.retrospect.entity;

import static org.layer.common.exception.RetrospectExceptionType.*;

import org.layer.domain.common.BaseTimeEntity;
import org.layer.domain.retrospect.exception.RetrospectException;

import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand Down Expand Up @@ -42,4 +45,10 @@ public Retrospect(Long spaceId, String title, String introduction, RetrospectSta
this.introduction = introduction;
this.retrospectStatus = retrospectStatus;
}

public void isProceedingRetrospect(){
if(!this.retrospectStatus.equals(RetrospectStatus.PROCEEDING)){
throw new RetrospectException(NOT_PROCEEDING_RETROSPECT);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.layer.domain.retrospect.exception;

import org.layer.common.exception.BaseCustomException;
import org.layer.common.exception.ExceptionType;

public class RetrospectException extends BaseCustomException {
public RetrospectException(ExceptionType exceptionType) {
super(exceptionType);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package org.layer.domain.retrospect.repository;

import static org.layer.common.exception.RetrospectExceptionType.*;

import java.util.List;

import org.layer.domain.retrospect.entity.Retrospect;
import org.layer.domain.retrospect.exception.RetrospectException;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RetrospectRepository extends JpaRepository<Retrospect, Long> {
List<Retrospect> findAllBySpaceId(Long spaceId);

default Retrospect findByIdOrThrow(Long retrospectId){
return findById(retrospectId)
.orElseThrow(() -> new RetrospectException(NOT_FOUND_RETROSPECT));
}
}

0 comments on commit dee5506

Please sign in to comment.