Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -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<Comment> 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<Comment> 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<List<Comment>> findCommentsByRequestId(@PathVariable Long requestId) {
List<Comment> comments = commentService.findCommentsByRequestId(requestId);
return ResponseEntity.ok(comments);
}

@Operation(summary = "사용자 ID로 댓글 조회", description = "특정 사용자가 작성한 모든 댓글을 조회합니다.")
@ApiResponse(responseCode = "200", description = "사용자의 댓글 목록이 성공적으로 조회되었습니다.")
@GetMapping("/account")
public ResponseEntity<List<Comment>> findCommentsByAccountId(
@RequestHeader(name = "Authorization") String authorizationHeader) throws Exception {

String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader);
List<Comment> 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<Comment> 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<Void> deleteComment(@PathVariable Long commentId) {

commentService.deleteComment(commentId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<Comment, Long> {
List<Comment> findCommentsByAccountId(String accountId);
List<Comment> findCommentsByRequest_RequestId(Long requestId);
}
Original file line number Diff line number Diff line change
@@ -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<Comment> findCommentsByRequestId(Long requestId) {
return commentRepository.findCommentsByRequest_RequestId(requestId);
}

public List<Comment> findCommentsByAccountId(String accountId) {
return commentRepository.findCommentsByAccountId(accountId);
}

@Transactional
public Comment updateComment(CommentUpdateRequestDTO commentUpdateRequestDTO, String accountId) {
Optional<Comment> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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("*"); // 모든 헤더 허용

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

// 쿠키에서 리프레시 토큰을 추출하는 메서드
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 페이지 경로
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import java.time.LocalDateTime;

@Table(name = "ETHER")
@Entity(name = "ETHER")
@Entity(name = "Ether")
@Getter
@ToString
public class Ether {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import java.time.LocalDateTime;

@Table(name = "USER")
@Entity(name = "USER")
@Entity(name = "User")
@Getter
public class User {
@Id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,14 @@ public QuestionController(QuestionService questionService, JwtUtil jwtUtil) {
})
public ResponseEntity<Question> 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개를 조회한다."
Expand All @@ -58,6 +57,7 @@ public ResponseEntity<Question> createQuestion(
@ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"),
@ApiResponse(responseCode = "401", description = "인증되지 않음")
})
@GetMapping("/{questionId}")
public ResponseEntity<Question> getQuestionById(@PathVariable Long questionId) {
Question question = questionService.findQuestionByQuestionId(questionId);
if (question != null) {
Expand All @@ -84,7 +84,6 @@ public ResponseEntity<List<Question>> getAllQuestionsWithPaging(@RequestParam in
}

// Retrieve questions by account ID with pagination
@GetMapping
@Operation(
summary = "Pagination으로 로그인한 사용자의 질문 조회",
description = "Pagination으로 사용자가 했던 질문 전체를 조회한다. 최대 20개가 주어진다"
Expand All @@ -94,17 +93,17 @@ public ResponseEntity<List<Question>> getAllQuestionsWithPaging(@RequestParam in
@ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"),
@ApiResponse(responseCode = "401", description = "인증되지 않음")
})
@GetMapping("/account")
public ResponseEntity<List<Question>> 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<Question> questions = questionService.findQuestionsByAccountIdWithPaging(page, accountId);
return ResponseEntity.ok(questions);
}

// Update a question by ID
@PatchMapping("/{id}")
@Operation(
summary = "사용자 질문 수정",
description = "사용자가 했던 질문을 수정한다."
Expand All @@ -114,11 +113,12 @@ public ResponseEntity<List<Question>> getQuestionsByAccountIdWithPaging(
@ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"),
@ApiResponse(responseCode = "401", description = "인증되지 않음")
})
@PatchMapping
public ResponseEntity<Question> 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);
Expand All @@ -128,7 +128,6 @@ public ResponseEntity<Question> updateQuestion(
}

// Delete a question by ID
@DeleteMapping("/{questionId}")
@Operation(
summary = "사용자 질문 삭제",
description = "사용자가 했던 질문을 삭제한다"
Expand All @@ -138,6 +137,7 @@ public ResponseEntity<Question> updateQuestion(
@ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"),
@ApiResponse(responseCode = "401", description = "인증되지 않음")
})
@DeleteMapping("/{questionId}")
public ResponseEntity<Void> deleteQuestion(@PathVariable Long questionId) {

questionService.deleteQuestion(questionId);
Expand Down
Loading