diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java b/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java new file mode 100644 index 0000000..863c2f2 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java @@ -0,0 +1,97 @@ +package com.mtvs.devlinkbackend.comment.controller; + +import com.mtvs.devlinkbackend.comment.dto.CommentRegistRequestDTO; +import com.mtvs.devlinkbackend.comment.dto.CommentUpdateRequestDTO; +import com.mtvs.devlinkbackend.comment.entity.Comment; +import com.mtvs.devlinkbackend.comment.service.CommentService; +import com.mtvs.devlinkbackend.config.JwtUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/comment") +public class CommentController { + private final CommentService commentService; + private final JwtUtil jwtUtil; + + public CommentController(CommentService commentService, JwtUtil jwtUtil) { + this.commentService = commentService; + this.jwtUtil = jwtUtil; + } + + @Operation(summary = "댓글 등록", description = "특정 요청에 대한 새로운 댓글을 등록합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "댓글이 성공적으로 등록되었습니다."), + @ApiResponse(responseCode = "404", description = "해당 요청을 찾을 수 없습니다.") + }) + @PostMapping + public ResponseEntity registComment( + @RequestBody CommentRegistRequestDTO commentRegistRequestDTO, + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + Comment comment = commentService.registComment(commentRegistRequestDTO, accountId); + return ResponseEntity.status(HttpStatus.CREATED).body(comment); + } + + @Operation(summary = "댓글 조회", description = "ID를 사용하여 댓글을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글이 성공적으로 조회되었습니다."), + @ApiResponse(responseCode = "404", description = "해당 댓글을 찾을 수 없습니다.") + }) + @GetMapping("/{commentId}") + public ResponseEntity findCommentByCommentId(@PathVariable Long commentId) { + Comment comment = commentService.findCommentByCommentId(commentId); + return comment != null ? ResponseEntity.ok(comment) : ResponseEntity.notFound().build(); + } + + @Operation(summary = "요청 ID로 댓글 조회", description = "특정 요청에 연관된 모든 댓글을 조회합니다.") + @ApiResponse(responseCode = "200", description = "댓글 목록이 성공적으로 조회되었습니다.") + @GetMapping("/request/{requestId}") + public ResponseEntity> findCommentsByRequestId(@PathVariable Long requestId) { + List comments = commentService.findCommentsByRequestId(requestId); + return ResponseEntity.ok(comments); + } + + @Operation(summary = "사용자 ID로 댓글 조회", description = "특정 사용자가 작성한 모든 댓글을 조회합니다.") + @ApiResponse(responseCode = "200", description = "사용자의 댓글 목록이 성공적으로 조회되었습니다.") + @GetMapping("/account") + public ResponseEntity> findCommentsByAccountId( + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + List comments = commentService.findCommentsByAccountId(accountId); + return ResponseEntity.ok(comments); + } + + @Operation(summary = "댓글 수정", description = "기존 댓글의 내용을 수정합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글이 성공적으로 수정되었습니다."), + @ApiResponse(responseCode = "403", description = "댓글을 수정할 권한이 없습니다."), + @ApiResponse(responseCode = "404", description = "해당 댓글을 찾을 수 없습니다.") + }) + @PatchMapping + public ResponseEntity updateComment( + @RequestBody CommentUpdateRequestDTO commentUpdateRequestDTO, + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + Comment updatedComment = commentService.updateComment(commentUpdateRequestDTO, accountId); + return ResponseEntity.ok(updatedComment); + } + + @Operation(summary = "댓글 삭제", description = "ID를 사용하여 댓글을 삭제합니다.") + @ApiResponse(responseCode = "204", description = "댓글이 성공적으로 삭제되었습니다.") + @DeleteMapping("/{commentId}") + public ResponseEntity deleteComment(@PathVariable Long commentId) { + + commentService.deleteComment(commentId); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentRegistRequestDTO.java b/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentRegistRequestDTO.java new file mode 100644 index 0000000..a2de63c --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentRegistRequestDTO.java @@ -0,0 +1,12 @@ +package com.mtvs.devlinkbackend.comment.dto; + +import lombok.*; + +@Getter @Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class CommentRegistRequestDTO { + private String content; + private Long requestId; +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentUpdateRequestDTO.java b/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentUpdateRequestDTO.java new file mode 100644 index 0000000..a82cfd6 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentUpdateRequestDTO.java @@ -0,0 +1,13 @@ +package com.mtvs.devlinkbackend.comment.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class CommentUpdateRequestDTO { + private Long commentId; + private String content; +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java b/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java new file mode 100644 index 0000000..36aeae3 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java @@ -0,0 +1,54 @@ +package com.mtvs.devlinkbackend.comment.entity; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.mtvs.devlinkbackend.request.entity.Request; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import java.time.LocalDateTime; + +@Table(name = "COMMENT") +@Entity(name = "Comment") +@NoArgsConstructor +@Getter +@ToString(exclude = "request") // request 필드를 toString에서 제외 +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "COMMENT_ID") + private Long commentId; + + @Column(name = "CONTENT") + private String content; + + @Column(name = "ACCOUNT_ID") + private String accountId; + + @CreationTimestamp + @Column(name = "CREATED_AT", updatable = false) + private LocalDateTime createdAt; + + @UpdateTimestamp + @Column(name = "MODIFIED_AT") + private LocalDateTime modifiedAt; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "REQUEST_ID", nullable = false) + @JsonIgnore + private Request request; + + public Comment(String content, String accountId, Request request) { + this.content = content; + this.accountId = accountId; + this.request = request; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java b/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java new file mode 100644 index 0000000..15ffe45 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java @@ -0,0 +1,13 @@ +package com.mtvs.devlinkbackend.comment.repository; + +import com.mtvs.devlinkbackend.comment.entity.Comment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface CommentRepository extends JpaRepository { + List findCommentsByAccountId(String accountId); + List findCommentsByRequest_RequestId(Long requestId); +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/service/CommentService.java b/src/main/java/com/mtvs/devlinkbackend/comment/service/CommentService.java new file mode 100644 index 0000000..c1bb6f0 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/service/CommentService.java @@ -0,0 +1,66 @@ +package com.mtvs.devlinkbackend.comment.service; + +import com.mtvs.devlinkbackend.comment.dto.CommentRegistRequestDTO; +import com.mtvs.devlinkbackend.comment.dto.CommentUpdateRequestDTO; +import com.mtvs.devlinkbackend.comment.entity.Comment; +import com.mtvs.devlinkbackend.comment.repository.CommentRepository; +import com.mtvs.devlinkbackend.request.entity.Request; +import com.mtvs.devlinkbackend.request.repository.RequestRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +@Service +public class CommentService { + private final CommentRepository commentRepository; + private final RequestRepository requestRepository; + + public CommentService(CommentRepository commentRepository, RequestRepository requestRepository) { + this.commentRepository = commentRepository; + this.requestRepository = requestRepository; + } + + @Transactional + public Comment registComment(CommentRegistRequestDTO commentRegistRequestDTO, String accountId) { + Request request = requestRepository.findById(commentRegistRequestDTO.getRequestId()).orElse(null); + return commentRepository.save(new Comment( + commentRegistRequestDTO.getContent(), + accountId, + request + )); + } + + public Comment findCommentByCommentId(Long commentId) { + return commentRepository.findById(commentId).orElse(null); + } + + public List findCommentsByRequestId(Long requestId) { + return commentRepository.findCommentsByRequest_RequestId(requestId); + } + + public List findCommentsByAccountId(String accountId) { + return commentRepository.findCommentsByAccountId(accountId); + } + + @Transactional + public Comment updateComment(CommentUpdateRequestDTO commentUpdateRequestDTO, String accountId) { + Optional comment = commentRepository.findById(commentUpdateRequestDTO.getCommentId()); + if (comment.isPresent()) { + Comment foundComment = comment.get(); + if(foundComment.getAccountId().equals(accountId)) { + foundComment.setContent(commentUpdateRequestDTO.getContent()); + return foundComment; + } + else throw new IllegalArgumentException("다른 사용자가 코멘트 수정 시도 / commentId : " + + commentUpdateRequestDTO.getCommentId() + + ", accountId : " + accountId); + } + else throw new IllegalArgumentException("잘못된 commentId로 코멘트 수정 시도"); + } + + public void deleteComment(Long commentId) { + commentRepository.deleteById(commentId); + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java index 1cecdac..e502348 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java @@ -17,11 +17,7 @@ public CorsConfigurationSource corsConfigurationSource() { configuration.addAllowedOrigin("http://localhost:8080"); configuration.addAllowedOrigin("http://localhost:5173"); // 테스트에서 사용되는 도메인 추가 - configuration.addAllowedMethod("GET"); - configuration.addAllowedMethod("POST"); - configuration.addAllowedMethod("PUT"); - configuration.addAllowedMethod("PATCH"); - configuration.addAllowedMethod("DELETE"); + configuration.addAllowedMethod("*"); configuration.addAllowedHeader("*"); // 모든 헤더 허용 diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java index c497b64..d0034d8 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java @@ -78,8 +78,7 @@ protected boolean shouldNotFilter(HttpServletRequest request) { || path.equals("/swagger-ui.html") || path.startsWith("/swagger-resources") || path.startsWith("/webjars") - || path.startsWith("/login") - || path.startsWith("/**"); + || path.startsWith("/api"); } // 쿠키에서 리프레시 토큰을 추출하는 메서드 diff --git a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java index 5481a22..d677600 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java @@ -51,7 +51,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ) .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers( - "/**", + "/api/**", "/v3/api-docs/**", // Swagger API Docs 경로 "/swagger-ui/**", // Swagger UI 정적 리소스 경로 "/swagger-ui.html", // Swagger UI 페이지 경로 diff --git a/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java b/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java index 0c8464a..2177ac9 100644 --- a/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java +++ b/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java @@ -9,7 +9,7 @@ import java.time.LocalDateTime; @Table(name = "ETHER") -@Entity(name = "ETHER") +@Entity(name = "Ether") @Getter @ToString public class Ether { diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/entity/User.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/entity/User.java index d710168..e6b2eb6 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/entity/User.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/entity/User.java @@ -8,7 +8,7 @@ import java.time.LocalDateTime; @Table(name = "USER") -@Entity(name = "USER") +@Entity(name = "User") @Getter public class User { @Id diff --git a/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java b/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java index da7343c..52e3427 100644 --- a/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java +++ b/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java @@ -40,15 +40,14 @@ public QuestionController(QuestionService questionService, JwtUtil jwtUtil) { }) public ResponseEntity createQuestion( @RequestBody QuestionRegistRequestDTO questionRegistRequestDTO, - @RequestHeader(name = "Authorization") String accessToken) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithAuth(accessToken); + String accountId = jwtUtil.getSubjectFromTokenWithAuth(authorizationHeader); Question createdQuestion = questionService.registQuestion(questionRegistRequestDTO, accountId); return ResponseEntity.ok(createdQuestion); } // Retrieve a question by ID - @GetMapping("/{questionId}") @Operation( summary = "PK에 따른 질문 조회", description = "PK값으로 사용자가 올린 공개 질문 1개를 조회한다." @@ -58,6 +57,7 @@ public ResponseEntity createQuestion( @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), @ApiResponse(responseCode = "401", description = "인증되지 않음") }) + @GetMapping("/{questionId}") public ResponseEntity getQuestionById(@PathVariable Long questionId) { Question question = questionService.findQuestionByQuestionId(questionId); if (question != null) { @@ -84,7 +84,6 @@ public ResponseEntity> getAllQuestionsWithPaging(@RequestParam in } // Retrieve questions by account ID with pagination - @GetMapping @Operation( summary = "Pagination으로 로그인한 사용자의 질문 조회", description = "Pagination으로 사용자가 했던 질문 전체를 조회한다. 최대 20개가 주어진다" @@ -94,17 +93,17 @@ public ResponseEntity> getAllQuestionsWithPaging(@RequestParam in @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), @ApiResponse(responseCode = "401", description = "인증되지 않음") }) + @GetMapping("/account") public ResponseEntity> getQuestionsByAccountIdWithPaging( @RequestParam int page, - @RequestHeader(name = "Authorization") String accessToken) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(accessToken); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); List questions = questionService.findQuestionsByAccountIdWithPaging(page, accountId); return ResponseEntity.ok(questions); } // Update a question by ID - @PatchMapping("/{id}") @Operation( summary = "사용자 질문 수정", description = "사용자가 했던 질문을 수정한다." @@ -114,11 +113,12 @@ public ResponseEntity> getQuestionsByAccountIdWithPaging( @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), @ApiResponse(responseCode = "401", description = "인증되지 않음") }) + @PatchMapping public ResponseEntity updateQuestion( @RequestBody QuestionUpdateRequestDTO questionUpdateRequestDTO, - @RequestHeader(name = "Authorization") String accessToken) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(accessToken); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); try { Question updatedQuestion = questionService.updateQuestion(questionUpdateRequestDTO, accountId); return ResponseEntity.ok(updatedQuestion); @@ -128,7 +128,6 @@ public ResponseEntity updateQuestion( } // Delete a question by ID - @DeleteMapping("/{questionId}") @Operation( summary = "사용자 질문 삭제", description = "사용자가 했던 질문을 삭제한다" @@ -138,6 +137,7 @@ public ResponseEntity updateQuestion( @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), @ApiResponse(responseCode = "401", description = "인증되지 않음") }) + @DeleteMapping("/{questionId}") public ResponseEntity deleteQuestion(@PathVariable Long questionId) { questionService.deleteQuestion(questionId); diff --git a/src/main/java/com/mtvs/devlinkbackend/question/entity/Question.java b/src/main/java/com/mtvs/devlinkbackend/question/entity/Question.java index 911f374..fd82d90 100644 --- a/src/main/java/com/mtvs/devlinkbackend/question/entity/Question.java +++ b/src/main/java/com/mtvs/devlinkbackend/question/entity/Question.java @@ -14,7 +14,7 @@ import java.util.List; @Table(name = "QUESTION") -@Entity(name = "QUESTION") +@Entity(name = "Question") @Getter @NoArgsConstructor public class Question { diff --git a/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java b/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java index d1ec63f..ef2014a 100644 --- a/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java +++ b/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java @@ -34,9 +34,9 @@ public ReplyController(ReplyService replyService, JwtUtil jwtUtil) { @PostMapping public ResponseEntity registReply( @RequestBody ReplyRegistRequestDTO replyRegistRequestDTO, - @RequestHeader(name = "Authorization") String token) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithAuth(token); + String accountId = jwtUtil.getSubjectFromTokenWithAuth(authorizationHeader); Reply reply = replyService.registReply(replyRegistRequestDTO, accountId); return ResponseEntity.ok(reply); } @@ -71,11 +71,11 @@ public ResponseEntity> findRepliesByQuestionId(@PathVariable Long qu @ApiResponse(responseCode = "401", description = "인증되지 않음"), @ApiResponse(responseCode = "404", description = "댓글을 찾을 수 없음") }) - @GetMapping("/account/{accountId}") + @GetMapping("/account") public ResponseEntity> findRepliesByAccountId( - @RequestHeader(name = "Authorization") String token) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(token); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); List replies = replyService.findRepliesByAccountId(accountId); return ResponseEntity.ok(replies); } @@ -89,9 +89,9 @@ public ResponseEntity> findRepliesByAccountId( @PatchMapping public ResponseEntity updateReply( @RequestBody ReplyUpdateRequestDTO replyUpdateRequestDTO, - @RequestHeader(name = "Authorization") String token) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(token); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); try { Reply updatedReply = replyService.updateReply(replyUpdateRequestDTO, accountId); return ResponseEntity.ok(updatedReply); diff --git a/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java b/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java index 5b28a9f..9f43a53 100644 --- a/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java +++ b/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java @@ -1,20 +1,21 @@ package com.mtvs.devlinkbackend.reply.entity; -import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.mtvs.devlinkbackend.question.entity.Question; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; -import net.minidev.json.annotate.JsonIgnore; +import lombok.ToString; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; import java.time.LocalDateTime; @Table(name = "REPLY") -@Entity(name = "REPLY") +@Entity(name = "Reply") @Getter @NoArgsConstructor +@ToString(exclude = "question") // request 필드를 toString에서 제외 public class Reply { @Id @@ -26,7 +27,7 @@ public class Reply { private String content; @CreationTimestamp - @Column(name = "CREATED_AT") + @Column(name = "CREATED_AT", updatable = false) private LocalDateTime createdAt; @UpdateTimestamp @@ -38,7 +39,7 @@ public class Reply { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "QUESTION_ID", nullable = false) - @JsonBackReference + @JsonIgnore private Question question; public Reply(String content, String accountId, Question question) { diff --git a/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java b/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java index 0a1f791..adae769 100644 --- a/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java +++ b/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java @@ -75,8 +75,11 @@ public ResponseEntity> getRequestsByAccountId( @ApiResponse(responseCode = "200", description = "의뢰 목록이 성공적으로 조회됨") }) @GetMapping("/date-range") - public ResponseEntity> getRequestsBetweenDates(@RequestParam LocalDateTime startDateTime, @RequestParam LocalDateTime endDateTime) { - List requests = requestService.findAllRequestsBetweenStarDateTimeAndEndDateTime(startDateTime, endDateTime); + public ResponseEntity> getRequestsBetweenDates( + @RequestParam LocalDateTime startDateTime, @RequestParam LocalDateTime endDateTime) { + + List requests = + requestService.findAllRequestsBetweenStarDateTimeAndEndDateTime(startDateTime, endDateTime); return ResponseEntity.ok(requests); } @@ -89,6 +92,7 @@ public ResponseEntity> getRequestsBetweenDates(@RequestParam Local public ResponseEntity updateRequest( @RequestBody RequestUpdateRequestDTO requestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) { + try { String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); Request updatedRequest = requestService.updateRequest(requestDTO, accountId); diff --git a/src/main/java/com/mtvs/devlinkbackend/request/entity/Request.java b/src/main/java/com/mtvs/devlinkbackend/request/entity/Request.java index ad04f8c..ad9e334 100644 --- a/src/main/java/com/mtvs/devlinkbackend/request/entity/Request.java +++ b/src/main/java/com/mtvs/devlinkbackend/request/entity/Request.java @@ -1,5 +1,7 @@ package com.mtvs.devlinkbackend.request.entity; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import com.mtvs.devlinkbackend.comment.entity.Comment; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @@ -8,9 +10,11 @@ import org.hibernate.annotations.UpdateTimestamp; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; @Table(name = "REQUEST") -@Entity(name = "REQUEST") +@Entity(name = "Request") @NoArgsConstructor @ToString @Getter @@ -36,13 +40,17 @@ public class Request { private String accountId; @CreationTimestamp - @Column(name = "CREATED_AT") + @Column(name = "CREATED_AT", updatable = false) private LocalDateTime createdAt; @UpdateTimestamp @Column(name = "MODIFIED_AT") private LocalDateTime modifiedAt; + @OneToMany(mappedBy = "request", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference + private List comments = new ArrayList<>(); + public Request(String title, String content, LocalDateTime startDateTime, LocalDateTime endDateTime, String accountId) { this.title = title; this.content = content; diff --git a/src/main/java/com/mtvs/devlinkbackend/request/repository/RequestRepository.java b/src/main/java/com/mtvs/devlinkbackend/request/repository/RequestRepository.java index 1bb3c2f..c8a2675 100644 --- a/src/main/java/com/mtvs/devlinkbackend/request/repository/RequestRepository.java +++ b/src/main/java/com/mtvs/devlinkbackend/request/repository/RequestRepository.java @@ -13,7 +13,7 @@ public interface RequestRepository extends JpaRepository { List findRequestsByAccountId(String accountId); - @Query("SELECT r FROM REQUEST r WHERE r.startDateTime BETWEEN :startDateTime AND :endDateTime " + + @Query("SELECT r FROM Request r WHERE r.startDateTime BETWEEN :startDateTime AND :endDateTime " + "AND r.endDateTime BETWEEN :startDateTime AND :endDateTime") List findRequestsWithinDateRange( @Param("startDateTime") LocalDateTime startDateTime, diff --git a/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java b/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java new file mode 100644 index 0000000..f408a98 --- /dev/null +++ b/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java @@ -0,0 +1,91 @@ +package com.mtvs.devlinkbackend.comment; + +import com.mtvs.devlinkbackend.comment.dto.CommentRegistRequestDTO; +import com.mtvs.devlinkbackend.comment.dto.CommentUpdateRequestDTO; +import com.mtvs.devlinkbackend.comment.service.CommentService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.stream.Stream; + +@SpringBootTest +@Transactional +public class CommentCRUDTest { + @Autowired + private CommentService commentService; + + private static Stream newComment() { + return Stream.of( + Arguments.of(new CommentRegistRequestDTO("내용0", 1L), "계정0"), + Arguments.of(new CommentRegistRequestDTO("내용00", 2L), "계정00") + ); + } + + private static Stream modifiedComment() { + return Stream.of( + Arguments.of(new CommentUpdateRequestDTO(1L, "내용0"), "계정1"), + Arguments.of(new CommentUpdateRequestDTO(2L, "내용00"), "계정2") + ); + } + + @DisplayName("코멘트 추가 테스트") + @ParameterizedTest + @MethodSource("newComment") + @Order(0) + public void testCreateComment(CommentRegistRequestDTO commentRegistRequestDTO, String accountId) { + Assertions.assertDoesNotThrow(() -> commentService.registComment(commentRegistRequestDTO, accountId)); + } + + @DisplayName("PK로 코멘트 조회 테스트") + @ValueSource(longs = {1,2}) + @ParameterizedTest + @Order(1) + public void testFindCommentByCommentId(long commentId) { + Assertions.assertDoesNotThrow(() -> + System.out.println("Comment = " + commentService.findCommentByCommentId(commentId))); + } + + @DisplayName("계정 ID에 따른 코멘트 조회 테스트") + @ValueSource( strings = {"계정1", "계정2"}) + @ParameterizedTest + @Order(2) + public void testFindCommentsByAccountId(String accountId) { + Assertions.assertDoesNotThrow(() -> + System.out.println("Comment = " + commentService.findCommentsByAccountId(accountId))); + } + + @DisplayName("의뢰 ID에 따른 코멘트 조회 테스트") + @ValueSource( longs = {1,2}) + @ParameterizedTest + @Order(3) + public void testFindCommentsByRequestId(long requestId) { + Assertions.assertDoesNotThrow(() -> + System.out.println("Comment = " + commentService.findCommentsByRequestId(requestId))); + } + + @DisplayName("코멘트 수정 테스트") + @MethodSource("modifiedComment") + @ParameterizedTest + @Order(4) + public void testUpdateComment(CommentUpdateRequestDTO commentUpdateRequestDTO, String accountId) { + Assertions.assertDoesNotThrow(() -> + System.out.println(commentService.updateComment(commentUpdateRequestDTO, accountId))); + } + + @DisplayName("코멘트 삭제 테스트") + @ValueSource(longs = {0,1}) + @ParameterizedTest + @Order(5) + public void testDeleteRequest(long commentId) { + Assertions.assertDoesNotThrow(() -> + commentService.deleteComment(commentId)); + } +} diff --git a/src/test/java/com/mtvs/devlinkbackend/request/RequestCRUDTest.java b/src/test/java/com/mtvs/devlinkbackend/request/RequestCRUDTest.java index 61a24f8..1f5a169 100644 --- a/src/test/java/com/mtvs/devlinkbackend/request/RequestCRUDTest.java +++ b/src/test/java/com/mtvs/devlinkbackend/request/RequestCRUDTest.java @@ -41,7 +41,7 @@ private static Stream modifiedRequest() { @ParameterizedTest @MethodSource("newRequest") @Order(0) - public void testCreateQuestion(RequestRegistRequestDTO requestRegistRequestDTO, String accountId) { + public void testCreateRequest(RequestRegistRequestDTO requestRegistRequestDTO, String accountId) { Assertions.assertDoesNotThrow(() -> requestService.registRequest(requestRegistRequestDTO, accountId)); }