diff --git a/src/main/java/com/moongeul/backend/api/question/controller/QuestionController.java b/src/main/java/com/moongeul/backend/api/question/controller/QuestionController.java new file mode 100644 index 0000000..d3b2f32 --- /dev/null +++ b/src/main/java/com/moongeul/backend/api/question/controller/QuestionController.java @@ -0,0 +1,45 @@ +package com.moongeul.backend.api.question.controller; + +import com.moongeul.backend.api.question.dto.QuestionCreateRequestDTO; +import com.moongeul.backend.api.question.dto.QuestionIdResponseDTO; +import com.moongeul.backend.api.question.service.QuestionService; +import com.moongeul.backend.common.response.ApiResponse; +import com.moongeul.backend.common.response.SuccessStatus; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "Question", description = "Question(질문) 관련 API 입니다.") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v2/question") +public class QuestionController { + + private final QuestionService questionService; + + @Operation( + summary = "질문 생성 API", + description = "질문을 생성하는 API 입니다." + ) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "질문 생성 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "질문 작성은 필수입니다"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "해당 도서를 찾을 수 없습니다.") + }) + @PostMapping("/create") + public ResponseEntity> createQuestion(@AuthenticationPrincipal UserDetails userDetails, + @Valid @RequestBody QuestionCreateRequestDTO questionCreateRequestDTO) { + + QuestionIdResponseDTO response = questionService.createQuestion(questionCreateRequestDTO, userDetails.getUsername()); + return ApiResponse.success(SuccessStatus.CREATE_QUESTION_SUCCESS, response); + } +} diff --git a/src/main/java/com/moongeul/backend/api/question/dto/QuestionCreateRequestDTO.java b/src/main/java/com/moongeul/backend/api/question/dto/QuestionCreateRequestDTO.java new file mode 100644 index 0000000..1c45254 --- /dev/null +++ b/src/main/java/com/moongeul/backend/api/question/dto/QuestionCreateRequestDTO.java @@ -0,0 +1,32 @@ +package com.moongeul.backend.api.question.dto; + +import com.moongeul.backend.api.book.entity.Book; +import com.moongeul.backend.api.member.entity.Member; +import com.moongeul.backend.api.question.entity.Question; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuestionCreateRequestDTO { + + @NotBlank(message = "ISBN은 필수입니다") + private String isbn; // 책 + + @NotBlank(message = "질문 작성은 필수입니다") + private String content; // 질문 + + public Question toEntity(Member member, Book book){ + return Question.builder() + .content(this.content) + .commentCnt(0) + .member(member) + .book(book) + .build(); + } +} diff --git a/src/main/java/com/moongeul/backend/api/question/dto/QuestionIdResponseDTO.java b/src/main/java/com/moongeul/backend/api/question/dto/QuestionIdResponseDTO.java new file mode 100644 index 0000000..56027b1 --- /dev/null +++ b/src/main/java/com/moongeul/backend/api/question/dto/QuestionIdResponseDTO.java @@ -0,0 +1,15 @@ +package com.moongeul.backend.api.question.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuestionIdResponseDTO { + + private Long questionId; // 생성된 Question id +} diff --git a/src/main/java/com/moongeul/backend/api/question/entity/Question.java b/src/main/java/com/moongeul/backend/api/question/entity/Question.java new file mode 100644 index 0000000..9cd6a0a --- /dev/null +++ b/src/main/java/com/moongeul/backend/api/question/entity/Question.java @@ -0,0 +1,34 @@ +package com.moongeul.backend.api.question.entity; + +import com.moongeul.backend.api.book.entity.Book; +import com.moongeul.backend.api.member.entity.Member; +import com.moongeul.backend.common.entity.BaseTimeEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder // 빌더 패턴 사용을 위한 롬복 애너테이션 +@NoArgsConstructor // 기본 생성자 +@AllArgsConstructor // 모든 필드를 포함한 생성자 +@Table(name = "QUESTION") // 데이터베이스 테이블 이름 지정 +public class Question extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; // 게시글 id + + private String content; // 질문 내용 + private Integer commentCnt; // 댓글 개수 + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "book_isbn", nullable = false) + private Book book; +} diff --git a/src/main/java/com/moongeul/backend/api/question/repository/QuestionRepository.java b/src/main/java/com/moongeul/backend/api/question/repository/QuestionRepository.java new file mode 100644 index 0000000..a15b727 --- /dev/null +++ b/src/main/java/com/moongeul/backend/api/question/repository/QuestionRepository.java @@ -0,0 +1,7 @@ +package com.moongeul.backend.api.question.repository; + +import com.moongeul.backend.api.question.entity.Question; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface QuestionRepository extends JpaRepository { +} diff --git a/src/main/java/com/moongeul/backend/api/question/service/QuestionService.java b/src/main/java/com/moongeul/backend/api/question/service/QuestionService.java new file mode 100644 index 0000000..0b12540 --- /dev/null +++ b/src/main/java/com/moongeul/backend/api/question/service/QuestionService.java @@ -0,0 +1,42 @@ +package com.moongeul.backend.api.question.service; + +import com.moongeul.backend.api.book.entity.Book; +import com.moongeul.backend.api.book.repository.BookRepository; +import com.moongeul.backend.api.member.entity.Member; +import com.moongeul.backend.api.member.repository.MemberRepository; +import com.moongeul.backend.api.question.dto.QuestionCreateRequestDTO; +import com.moongeul.backend.api.question.dto.QuestionIdResponseDTO; +import com.moongeul.backend.api.question.entity.Question; +import com.moongeul.backend.api.question.repository.QuestionRepository; +import com.moongeul.backend.common.exception.NotFoundException; +import com.moongeul.backend.common.response.ErrorStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class QuestionService { + + private final MemberRepository memberRepository; + private final BookRepository bookRepository; + private final QuestionRepository questionRepository; + + /* 질문 생성 */ + public QuestionIdResponseDTO createQuestion(QuestionCreateRequestDTO questionCreateRequestDTO, String email){ + + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new NotFoundException(ErrorStatus.USER_NOTFOUND_EXCEPTION.getMessage())); + + Book book = bookRepository.findByIsbn(questionCreateRequestDTO.getIsbn()) + .orElseThrow(() -> new NotFoundException(ErrorStatus.BOOK_NOTFOUND_EXCEPTION.getMessage())); + + Question newQuestion = questionCreateRequestDTO.toEntity(member, book); + Question savedQuestion = questionRepository.save(newQuestion); + + return QuestionIdResponseDTO.builder() + .questionId(savedQuestion.getId()) + .build(); + } +} diff --git a/src/main/java/com/moongeul/backend/common/response/SuccessStatus.java b/src/main/java/com/moongeul/backend/common/response/SuccessStatus.java index cd8fa20..9631eb0 100644 --- a/src/main/java/com/moongeul/backend/common/response/SuccessStatus.java +++ b/src/main/java/com/moongeul/backend/common/response/SuccessStatus.java @@ -40,6 +40,9 @@ public enum SuccessStatus { CREATE_CATEGORY_SUCCESS(HttpStatus.OK, "카테고리 생성 성공"), GET_CATEGORY_SUCCESS(HttpStatus.OK, "카테고리 전체 조회 성공"), + /* QUESTION */ + CREATE_QUESTION_SUCCESS(HttpStatus.OK, "질문 생성 성공"), + /** * 201 */