Skip to content
Open
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

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.example.blog.domain.comment.controller;

import com.example.blog.domain.comment.dto.CommentCreateRequest;
import com.example.blog.domain.comment.dto.CommentResponse;
import com.example.blog.domain.comment.dto.CommentUpdateRequest;
import com.example.blog.domain.comment.service.CommentService;
import com.example.blog.global.response.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
public class CommentController {

private final CommentService commentService;

@PostMapping("/api/v1/posts/{postId}/comments")
@ResponseStatus(HttpStatus.CREATED)
public ApiResponse<CommentResponse> create(
@RequestHeader("X-User-Id") Long userId,
@PathVariable Long postId,
@RequestBody @Valid CommentCreateRequest request
) {
return ApiResponse.success(commentService.create(userId, postId, request));
}

@GetMapping("/api/v1/posts/{postId}/comments")
public ApiResponse<List<CommentResponse>> findByPostId(@PathVariable Long postId) {
return ApiResponse.success(commentService.findByPostId(postId));
}

@PatchMapping("/api/v1/comments/{commentId}")
public ApiResponse<CommentResponse> update(
@RequestHeader("X-User-Id") Long userId,
@PathVariable Long commentId,
@RequestBody @Valid CommentUpdateRequest request
) {
return ApiResponse.success(commentService.update(userId, commentId, request));
}

@DeleteMapping("/api/v1/comments/{commentId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(
@RequestHeader("X-User-Id") Long userId,
@PathVariable Long commentId
) {
commentService.delete(userId, commentId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.blog.domain.comment.dto;

import jakarta.validation.constraints.NotBlank;

public record CommentCreateRequest(

@NotBlank(message = "댓글 내용은 필수입니다.")
String content,

Long parentCommentId

) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.example.blog.domain.comment.dto;

import com.example.blog.domain.comment.entity.Comment;

import java.time.LocalDateTime;
import java.util.List;

public record CommentResponse(
Long commentId,
Long postId,
Long userId,
String username,
String content,
Long parentCommentId,
List<CommentResponse> replies,
LocalDateTime createdAt
) {

public static CommentResponse from(Comment comment) {
List<CommentResponse> replies = comment.getReplies().stream()
.map(CommentResponse::from)
.toList();
return new CommentResponse(
comment.getId(),
comment.getPost().getId(),
comment.getUser().getId(),
comment.getUser().getUsername(),
comment.getContent(),
comment.getParentComment() != null ? comment.getParentComment().getId() : null,
replies,
comment.getCreatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.blog.domain.comment.dto;

import jakarta.validation.constraints.NotBlank;

public record CommentUpdateRequest(

@NotBlank(message = "댓글 내용은 필수입니다.")
String content

) {}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,17 @@ public class Comment extends BaseEntity {

@OneToMany(mappedBy = "parentComment")
private List<Comment> replies = new ArrayList<>();

public static Comment of(User user, Post post, String content, Comment parentComment) {
Comment comment = new Comment();
comment.user = user;
comment.post = post;
comment.content = content;
comment.parentComment = parentComment;
return comment;
}

public void update(String content) {
this.content = content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.blog.domain.comment.repository;

import com.example.blog.domain.comment.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface CommentRepository extends JpaRepository<Comment, Long> {

List<Comment> findByPost_IdAndParentCommentIsNull(Long postId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.example.blog.domain.comment.service;

import com.example.blog.domain.comment.dto.CommentCreateRequest;
import com.example.blog.domain.comment.dto.CommentResponse;
import com.example.blog.domain.comment.dto.CommentUpdateRequest;
import com.example.blog.domain.comment.entity.Comment;
import com.example.blog.domain.comment.repository.CommentRepository;
import com.example.blog.domain.post.entity.Post;
import com.example.blog.domain.post.repository.PostRepository;
import com.example.blog.domain.user.entity.User;
import com.example.blog.domain.user.repository.UserRepository;
import com.example.blog.global.exception.BusinessException;
import com.example.blog.global.exception.ErrorCode;
import com.example.blog.global.exception.NotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
@Transactional
public class CommentService {

private final CommentRepository commentRepository;
private final PostRepository postRepository;
private final UserRepository userRepository;

public CommentResponse create(Long userId, Long postId, CommentCreateRequest request) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new NotFoundException(ErrorCode.USER_NOT_FOUND));
Post post = postRepository.findById(postId)
.orElseThrow(() -> new NotFoundException(ErrorCode.POST_NOT_FOUND));

Comment parentComment = null;
if (request.parentCommentId() != null) {
parentComment = commentRepository.findById(request.parentCommentId())
.orElseThrow(() -> new NotFoundException(ErrorCode.COMMENT_NOT_FOUND));
if (!parentComment.getPost().getId().equals(postId)) {
throw new BusinessException(ErrorCode.INVALID_REPORT_TARGET);
}
if (parentComment.getParentComment() != null) {
throw new BusinessException(ErrorCode.INVALID_REPORT_TARGET);
}
}

Comment comment = Comment.of(user, post, request.content(), parentComment);
return CommentResponse.from(commentRepository.save(comment));
}

@Transactional(readOnly = true)
public List<CommentResponse> findByPostId(Long postId) {
postRepository.findById(postId)
.orElseThrow(() -> new NotFoundException(ErrorCode.POST_NOT_FOUND));
return commentRepository.findByPost_IdAndParentCommentIsNull(postId).stream()
.map(CommentResponse::from)
.toList();
}

public CommentResponse update(Long userId, Long commentId, CommentUpdateRequest request) {
Comment comment = commentRepository.findById(commentId)
.orElseThrow(() -> new NotFoundException(ErrorCode.COMMENT_NOT_FOUND));
if (!comment.getUser().getId().equals(userId)) {
throw new BusinessException(ErrorCode.FORBIDDEN_ACCESS);
}
comment.update(request.content());
return CommentResponse.from(comment);
}

public void delete(Long userId, Long commentId) {
Comment comment = commentRepository.findById(commentId)
.orElseThrow(() -> new NotFoundException(ErrorCode.COMMENT_NOT_FOUND));
if (!comment.getUser().getId().equals(userId)) {
throw new BusinessException(ErrorCode.FORBIDDEN_ACCESS);
}
commentRepository.delete(comment);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.example.blog.domain.post.controller;

import com.example.blog.domain.post.dto.PostCreateRequest;
import com.example.blog.domain.post.dto.PostListResponse;
import com.example.blog.domain.post.dto.PostResponse;
import com.example.blog.domain.post.dto.PostUpdateRequest;
import com.example.blog.domain.post.service.PostService;
import com.example.blog.global.response.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/posts")
@RequiredArgsConstructor
public class PostController {

private final PostService postService;

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ApiResponse<PostResponse> create(
@RequestHeader("X-User-Id") Long userId,
@RequestBody @Valid PostCreateRequest request
) {
return ApiResponse.success(postService.create(userId, request));
}

@GetMapping
public ApiResponse<PostListResponse> findAll(
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
return ApiResponse.success(postService.findAll(status, page, size));
}

@GetMapping("/{postId}")
public ApiResponse<PostResponse> findById(@PathVariable Long postId) {
return ApiResponse.success(postService.findById(postId));
}

@PatchMapping("/{postId}")
public ApiResponse<PostResponse> update(
@RequestHeader("X-User-Id") Long userId,
@PathVariable Long postId,
@RequestBody @Valid PostUpdateRequest request
) {
return ApiResponse.success(postService.update(userId, postId, request));
}

@DeleteMapping("/{postId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(
@RequestHeader("X-User-Id") Long userId,
@PathVariable Long postId
) {
postService.delete(userId, postId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.blog.domain.post.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record PostCreateRequest(

@NotBlank(message = "제목은 필수입니다.")
@Size(max = 200, message = "제목은 200자 이하여야 합니다.")
String title,

String content,

String status

) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.blog.domain.post.dto;

import java.util.List;

public record PostListResponse(
List<PostResponse> items,
int page,
int size,
long totalElements
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.blog.domain.post.dto;

import com.example.blog.domain.post.entity.Post;

import java.time.LocalDateTime;

public record PostResponse(
Long postId,
Long userId,
String username,
String title,
String content,
String status,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {

public static PostResponse from(Post post) {
return new PostResponse(
post.getId(),
post.getUser().getId(),
post.getUser().getUsername(),
post.getTitle(),
post.getContent(),
post.getStatus(),
post.getCreatedAt(),
post.getUpdatedAt()
);
}
}
Loading