Skip to content

Commit

Permalink
✨ Feature - 4번대 API (질문 리스트 조회, 답변 리스트 조회, 질문하기, 답변하기) 구현 (#42)
Browse files Browse the repository at this point in the history
* feat: 4.1 질문 리스트 조회하기 구현

* feat: 4.2 답변 리스트 조회하기 API 구현

* feat: 4.3 질문하기 API 구현

* feat: 4.4 답변하기 API 구현
  • Loading branch information
dongkyeomjang authored Nov 14, 2024
1 parent ddadd2c commit 03a651f
Show file tree
Hide file tree
Showing 27 changed files with 640 additions and 13 deletions.
33 changes: 33 additions & 0 deletions http/question/QuestionControllerHttpRequest.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
### 4.1 질문 리스트 조회하기
//@no-log
GET {{host_url}}/v1/questions?id=1&week=1
Authorization: Bearer {{access_token}}

### 4.2 답변 리스트 조회하기
//@no-log
GET {{host_url}}/v1/questions/3/replies
Authorization: Bearer {{access_token}}


### 4.3 질문 등록하기
//@no-log
POST {{host_url}}/v1/questions
Authorization: Bearer {{access_token}}
Content-Type: application/json

{
"lecture_id": 1,
"week": 1,
"title": "질문 제목",
"content": "질문 내용"
}

### 4.4 답변 등록하기
//@no-log
POST {{host_url}}/v1/questions/3/replies
Authorization: Bearer {{access_token}}
Content-Type: application/json

{
"content": "답변 내용"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/
public class DateTimeUtil {

public static final DateTimeFormatter ISODateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
public static final DateTimeFormatter ISODateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
public static final DateTimeFormatter ISODateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static final DateTimeFormatter ISOTimeFormatter = DateTimeFormatter.ofPattern("HH:mm");
/**
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.dongguk.onroad.question.application.controller.command;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.dongguk.onroad.core.annotation.security.UserID;
import org.dongguk.onroad.core.dto.ResponseDto;
import org.dongguk.onroad.question.application.dto.request.CreateQuestionRequestDto;
import org.dongguk.onroad.question.application.dto.request.CreateReplyRequestDto;
import org.dongguk.onroad.question.application.usecase.CreateQuestionUseCase;
import org.dongguk.onroad.question.application.usecase.CreateReplyUseCase;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@RequiredArgsConstructor
public class QuestionCommandV1Controller {
private final CreateQuestionUseCase createQuestionUseCase;
private final CreateReplyUseCase createReplyUseCase;

@PostMapping("/v1/questions")
public ResponseDto<Void> createQuestion(
@UserID UUID userId,
@RequestBody @Valid CreateQuestionRequestDto requestDto
) {
createQuestionUseCase.execute(userId, requestDto);
return ResponseDto.created(null);
}

@PostMapping("/v1/questions/{id}/replies")
public ResponseDto<Void> createReply(
@UserID UUID userId,
@PathVariable Long id,
@RequestBody @Valid CreateReplyRequestDto requestDto
) {
createReplyUseCase.execute(userId, id, requestDto);
return ResponseDto.created(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.dongguk.onroad.question.application.controller.query;

import lombok.RequiredArgsConstructor;
import org.dongguk.onroad.core.annotation.security.UserID;
import org.dongguk.onroad.core.dto.ResponseDto;
import org.dongguk.onroad.question.application.dto.response.ReadQuestionResponseDto;
import org.dongguk.onroad.question.application.dto.response.ReadReplyResponseDto;
import org.dongguk.onroad.question.application.usecase.ReadQuestionUseCase;
import org.dongguk.onroad.question.application.usecase.ReadReplyUseCase;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@RequiredArgsConstructor
public class QuestionQueryV1Controller {

private final ReadQuestionUseCase readQuestionUseCase;
private final ReadReplyUseCase readReplyUseCase;

@GetMapping("/v1/questions")
public ResponseDto<ReadQuestionResponseDto> readQuestions(
@UserID UUID userId,
@RequestParam("id") Long lectureId,
@RequestParam("week") Integer weekIndex
) {
return ResponseDto.ok(readQuestionUseCase.execute(userId, lectureId, weekIndex));
}

@GetMapping("/v1/questions/{id}/replies")
public ResponseDto<ReadReplyResponseDto> readReplies(
@UserID UUID userId,
@PathVariable Long id
) {
return ResponseDto.ok(readReplyUseCase.execute(userId, id));
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.dongguk.onroad.question.application.dto.request;

import com.fasterxml.jackson.annotation.JsonProperty;

public record CreateQuestionRequestDto(
@JsonProperty("lecture_id")
Long lectureId,

@JsonProperty("week")
Integer week,

@JsonProperty("title")
String title,

@JsonProperty("content")
String content
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.dongguk.onroad.question.application.dto.request;

import com.fasterxml.jackson.annotation.JsonProperty;

public record CreateReplyRequestDto(
@JsonProperty("content")
String content
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package org.dongguk.onroad.question.application.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Getter;
import org.dongguk.onroad.core.dto.SelfValidating;
import org.dongguk.onroad.core.utility.DateTimeUtil;
import org.dongguk.onroad.question.domain.Question;

import java.util.List;

@Getter
public class ReadQuestionResponseDto extends SelfValidating<ReadQuestionResponseDto> {

@JsonProperty("question_list")
@NotNull(message = "question_list는 null일 수 없습니다.")
private final List<QuestionDto> questionList;

@Builder
public ReadQuestionResponseDto(List<QuestionDto> questionList) {
this.questionList = questionList;
this.validateSelf();
}

@Getter
public static class QuestionDto extends SelfValidating<QuestionDto> {

@JsonProperty("question_info")
@NotNull(message = "question_info는 null일 수 없습니다.")
private final QuestionInfoDto questionInfo;

@JsonProperty("reply_count")
@NotNull(message = "reply_count는 null일 수 없습니다.")
private final Integer replyCount;

@Builder
public QuestionDto(QuestionInfoDto questionInfo, Integer replyCount) {
this.questionInfo = questionInfo;
this.replyCount = replyCount;
this.validateSelf();
}

public static QuestionDto of(QuestionInfoDto questionInfo, Integer replyCount) {
return QuestionDto.builder()
.questionInfo(questionInfo)
.replyCount(replyCount)
.build();
}
}

@Getter
public static class QuestionInfoDto extends SelfValidating<QuestionInfoDto> {

@JsonProperty("writer_name")
@NotNull(message = "writer_name은 null일 수 없습니다.")
private final String writerName;

@JsonProperty("created_at")
@NotNull(message = "created_at은 null일 수 없습니다.")
private final String createdAt;

@JsonProperty("title")
@NotNull(message = "title은 null일 수 없습니다.")
private final String title;

@JsonProperty("content")
@NotNull(message = "content는 null일 수 없습니다.")
private final String content;

@Builder
public QuestionInfoDto(String writerName, String createdAt, String title, String content) {
this.writerName = writerName;
this.createdAt = createdAt;
this.title = title;
this.content = content;
this.validateSelf();
}

public static QuestionInfoDto fromEntityProfessor(Question question) {
return QuestionInfoDto.builder()
.writerName(question.getStudent().getName())
.createdAt(DateTimeUtil.convertLocalDateTimeToString(question.getCreatedAt()))
.title(question.getTitle())
.content(question.getContent())
.build();
}

public static QuestionInfoDto fromEntityStudent(Question question) {
return QuestionInfoDto.builder()
.writerName("annoymous")
.createdAt(DateTimeUtil.convertLocalDateTimeToString(question.getCreatedAt()))
.title(question.getTitle())
.content(question.getContent())
.build();
}
}

public static ReadQuestionResponseDto of(List<QuestionDto> questionList) {
return ReadQuestionResponseDto.builder()
.questionList(questionList)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.dongguk.onroad.question.application.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Getter;
import org.dongguk.onroad.core.dto.SelfValidating;
import org.dongguk.onroad.core.utility.DateTimeUtil;
import org.dongguk.onroad.question.domain.Reply;

import java.util.List;

@Getter
public class ReadReplyResponseDto extends SelfValidating<ReadReplyResponseDto> {

@JsonProperty("reply_list")
@NotNull(message = "reply_list는 null일 수 없습니다.")
private final List<ReplyDto> replyList;

@Builder
public ReadReplyResponseDto(List<ReplyDto> replyList) {
this.replyList = replyList;
this.validateSelf();
}

@Getter
public static class ReplyDto extends SelfValidating<ReplyDto> {

@JsonProperty("writer_name")
@NotNull(message = "writer_name은 null일 수 없습니다.")
private final String writerName;

@JsonProperty("created_at")
@NotNull(message = "created_at은 null일 수 없습니다.")
private final String createdAt;

@JsonProperty("content")
@NotNull(message = "content는 null일 수 없습니다.")
private final String content;

@Builder
public ReplyDto(String writerName, String createdAt, String content) {
this.writerName = writerName;
this.createdAt = createdAt;
this.content = content;
this.validateSelf();
}

public static ReplyDto fromEntityProfessor(Reply reply) {
return ReplyDto.builder()
.writerName(reply.getUser().getName())
.createdAt(DateTimeUtil.convertLocalDateTimeToString(reply.getCreatedAt()))
.content(reply.getContent())
.build();
}

public static ReplyDto fromEntityStudent(Reply reply) {
return ReplyDto.builder()
.writerName("anonymous")
.createdAt(DateTimeUtil.convertLocalDateTimeToString(reply.getCreatedAt()))
.content(reply.getContent())
.build();
}
}

public static ReadReplyResponseDto of(List<ReplyDto> replyList) {
return ReadReplyResponseDto.builder()
.replyList(replyList)
.build();
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.dongguk.onroad.question.application.service;

import lombok.RequiredArgsConstructor;
import org.dongguk.onroad.core.exception.error.ErrorCode;
import org.dongguk.onroad.core.exception.type.CommonException;
import org.dongguk.onroad.question.application.dto.request.CreateQuestionRequestDto;
import org.dongguk.onroad.question.application.usecase.CreateQuestionUseCase;
import org.dongguk.onroad.question.domain.Question;
import org.dongguk.onroad.question.domain.service.QuestionService;
import org.dongguk.onroad.question.repository.QuestionRepository;
import org.dongguk.onroad.roadmap.domain.Lecture;
import org.dongguk.onroad.roadmap.repository.LectureRepository;
import org.dongguk.onroad.security.domain.mysql.User;
import org.dongguk.onroad.security.domain.service.UserService;
import org.dongguk.onroad.security.repository.mysql.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

@Service
@RequiredArgsConstructor
public class CreateQuestionService implements CreateQuestionUseCase {

private final UserRepository userRepository;
private final QuestionRepository questionRepository;
private final LectureRepository lectureRepository;

private final UserService userService;
private final QuestionService questionService;

@Override
@Transactional
public void execute(UUID userId, CreateQuestionRequestDto requestDto) {

// 유저 조회
User user = userRepository.findById(userId)
.orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_RESOURCE));

// 학생 자격 검증
userService.validateStudent(user);

// 강의 조회
Lecture lecture = lectureRepository.findById(requestDto.lectureId())
.orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_RESOURCE));

// 질문 생성
Question question = questionService.createQuestion(
requestDto.title(),
requestDto.content(),
requestDto.week(),
lecture,
user);

questionRepository.save(question);
}

}
Loading

0 comments on commit 03a651f

Please sign in to comment.