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
Expand Up @@ -3,8 +3,6 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.sejongisc.backend.board.dto.BoardResponse;
import org.sejongisc.backend.board.dto.CommentRequest;
Expand All @@ -17,16 +15,9 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.UUID;

@RestController
@RequiredArgsConstructor
Expand All @@ -40,10 +31,12 @@ public class BoardController {
private final PostService postService;
private final PostInteractionService postInteractionService;

// 게시글 작성
// 게시물 생성
@Operation(
summary = "게시물 작성",
description = "게시판 ID, 제목, 내용, 첨부파일을 포함한 게시물을 작성합니다."
description = "게시판 ID, 제목, 내용, 첨부파일을 포함한 게시물 생성"
+ "anonymous 값을 true로 보내면 익명 게시물 작성됨"
+ "값을 보내지 않으면 기본값(false) 으로 설정됨"
)
@PostMapping(value = "/post", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> createPost(
Expand All @@ -54,12 +47,12 @@ public ResponseEntity<Void> createPost(
return ResponseEntity.ok().build();
}

// 게시글 수정
// 게시물 수정
@Operation(
summary = "게시물 수정",
description = "제목, 내용, 첨부파일을 포함한 게시물을 수정합니다."
+ "첨부파일은 전체 파일 삭제 후 재저장 방식으로 이루어집니다."
+ "게시판 종류는 수정할 수 없습니다."
description = "제목, 내용, 첨부파일을 포함한 게시물 수정"
+ "anonymous 값을 통해 익명 여부도 변경할 수 있음 "
+ "첨부파일은 전체 교체 방식으로 처리되며, 게시판 종류는 변경할 수 없음"
)
@PutMapping(value = "/post/{postId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> updatePost(
Expand All @@ -71,12 +64,11 @@ public ResponseEntity<Void> updatePost(
return ResponseEntity.ok().build();
}

// 게시글 삭제
// 게시물 삭제
@Operation(
summary = "게시글 삭제",
description = "게시글 ID를 통해 게시글을 삭제합니다."
+ "작성자 본인만 삭제할 수 있습니다."
+ "관련 첨부파일 및 댓글 등도 함께 삭제됩니다."
summary = "게시물 삭제",
description = "게시물 ID를 통해 게시물 삭제"
+ "작성자 본인만 삭제할 수 있으며, 관련 첨부파일과 댓글 정보도 함께 삭제"
)
@DeleteMapping("/post/{postId}")
public void deletePost(
Expand All @@ -86,12 +78,11 @@ public void deletePost(
postService.deletePost(postId, userId);
}

// 게시글 조회
// 게시물 조회
@Operation(
summary = "게시글 조회",
description = "게시판 ID를 통해 해당 게시판의 게시글 목록을 조회합니다."
+ "페이지 번호와 페이지 크기를 통해 페이징 처리가 가능합니다."
+ "기본값은 페이지 번호 0, 페이지 크기 20입니다."
summary = "게시물 조회",
description = "게시판 ID를 통해 해당 게시판의 게시물 목록 조회"
+ "pageNumber와 pageSize로 페이징할 수 있으며 기본값은 각각 0, 20"
)
@GetMapping("/posts")
public ResponseEntity<Page<PostResponse>> getPosts(
Expand All @@ -103,12 +94,11 @@ public ResponseEntity<Page<PostResponse>> getPosts(
return ResponseEntity.ok(postService.getPosts(boardId, userId, pageNumber, pageSize));
}

// 게시글 검색
// 게시물 검색
@Operation(
summary = "게시글 검색",
description = "게시판 ID와 키워드를 통해 해당 게시판의 게시글 목록을 검색합니다."
+ "페이지 번호와 페이지 크기를 통해 페이징 처리가 가능합니다."
+ "기본값은 페이지 번호 0, 페이지 크기 20입니다."
summary = "게시물 검색",
description = "게시판 ID와 검색어를 기준으로 제목 또는 내용에 검색어가 포함된 게시물 조회 "
+ "pageNumber와 pageSize로 페이징할 수 있으며 기본값은 각각 0, 20"
)
@GetMapping("/posts/search")
public ResponseEntity<Page<PostResponse>> searchPosts(
Expand All @@ -124,9 +114,9 @@ public ResponseEntity<Page<PostResponse>> searchPosts(
// 게시물 상세 조회
@Operation(
summary = "게시물 상세 조회",
description = "게시물 ID를 통해 게시물의 상세 정보를 조회합니다."
+ "댓글에 대해서도 페이지 번호와 페이지 크기를 통해 페이징 처리가 가능합니다."
+ "기본값은 댓글 페이지 번호 0, 댓글 페이지 크기 20입니다."
description = "게시물 ID를 통해 게시물의 상세 정보와 댓글 목록 조회 "
+ "익명 게시물과 익명 댓글은 작성자 정보가 닉네임은 \"익명\" 그 외 회원정보는 null로 반환됨"
+ "댓글은 commentPageNumber와 commentPageSize로 페이징할 수 있으며 기본값은 각각 0, 20"
)
@GetMapping("/post/{postId}")
public ResponseEntity<PostResponse> getPostDetail(
Expand All @@ -142,32 +132,30 @@ public ResponseEntity<PostResponse> getPostDetail(
// 최상위 게시판 목록 조회
@Operation(
summary = "부모 게시판 목록 조회",
description = "최상위 부모 게시판들의 목록을 조회합니다."
description = "최상위 부모 게시판 목록 조회"
)
@GetMapping("/parents")
public ResponseEntity<List<BoardResponse>> getParentBoards(
@AuthenticationPrincipal CustomUserDetails customUserDetails) {
return ResponseEntity.ok(postService.getParentBoards());
}


// 하위 게시판 목록 조회
@Operation(
summary = "하위 게시판 목록 조회",
description = "하위 게시판 목록을 조회합니다."
summary = "하위 게시판 목록 조회",
description = "하위 게시판 목록 조회"
)
@GetMapping("/childs")
public ResponseEntity<List<BoardResponse>> getChildBoards(
@AuthenticationPrincipal CustomUserDetails customUserDetails) {
@AuthenticationPrincipal CustomUserDetails customUserDetails) {
return ResponseEntity.ok(postService.getChildBoards());
}

// 좋아요 토글
@Operation(
summary = "좋아요 등록 및 취소",
description = "좋아요를 등록하거나 취소합니다. "
+ "이미 좋아요를 한 게시물인 경우 좋아요가 취소되고, "
+ "좋아요를 하지 않은 게시물인 경우 좋아요가 등록됩니다."
description = "게시물 좋아요를 토글"
+ "이미 좋아요를 누른 게시물이면 취소되고, 누르지 않은 게시물이면 등록"
)
@PostMapping("/{postId}/like")
public ResponseEntity<Void> toggleLike(
Expand All @@ -181,9 +169,8 @@ public ResponseEntity<Void> toggleLike(
// 북마크 토글
@Operation(
summary = "북마크 등록 및 취소",
description = "북마크를 등록하거나 취소합니다. "
+ "이미 북마크를 한 게시물인 경우 북마크가 취소되고, "
+ "북마크를 하지 않은 게시물인 경우 북마크가 등록됩니다."
description = "게시물 북마크를 토글 "
+ "이미 북마크한 게시물이면 취소되고, 북마크하지 않은 게시물이면 등록"
)
@PostMapping("/{postId}/bookmark")
public ResponseEntity<Void> toggleBookmark(
Expand All @@ -194,12 +181,12 @@ public ResponseEntity<Void> toggleBookmark(
return ResponseEntity.ok().build();
}

// 댓글 작성
// 댓글 생성
@Operation(
summary = "댓글 작성",
description = "게시물에 댓글을 작성합니다."
+ "parentCommentId가 제공되면 해당 댓글에 대한 대댓글로 작성됩니다."
+ "null일 경우 일반 댓글로 작성됩니다."
description = "게시물에 댓글 작성"
+ "anonymous 값을 true로 보내면 작성자 정보가 익명으로 표시 "
+ "parentCommentId가 있으면 대댓글로 작성되고, 없으면 일반 댓글로 작성"
)
@PostMapping("/comment")
public ResponseEntity<Void> createComment(
Expand All @@ -213,7 +200,8 @@ public ResponseEntity<Void> createComment(
// 댓글 수정
@Operation(
summary = "댓글 수정",
description = "댓글을 수정합니다. 작성자 본인만 수정할 수 있습니다."
description = "댓글 내용을 수정합니다. 작성자 본인만 수정할 수 있으며, "
+ "content와 anonymous 값을 함께 변경할 수 있음"
)
@PutMapping("/comment/{commentId}")
public void updateComment(
Expand All @@ -227,9 +215,8 @@ public void updateComment(
// 댓글 삭제
@Operation(
summary = "댓글 삭제",
description = "댓글을 삭제합니다. "
+ "작성자 본인과 관리자만 삭제할 수 있습니다."
+ "대댓글이 있는 경우 함께 삭제됩니다."
description = "댓글 삭제"
+ "작성자 본인과 관리자만 삭제할 수 있으며, 대댓글이 있는 경우 함께 삭제됨"
)
@DeleteMapping("/comment/{commentId}")
public void deleteComment(
Expand All @@ -238,7 +225,4 @@ public void deleteComment(
UUID userId = customUserDetails.getUserId();
postInteractionService.deleteComment(commentId, userId);
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class CommentRequest {
@NotBlank(message = "댓글 내용은 필수 항목입니다.")
private String content;

@Builder.Default
private boolean anonymous = false;

@Schema(description = "부모 댓글 ID (대댓글인 경우에만 필요)")
private UUID parentCommentId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,22 @@
@Setter
@Builder
public class CommentResponse {
private UUID commentId;
private UserInfoResponse user;
private UUID postId;
private String content;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
private UUID parentCommentId;
private List<CommentResponse> replies;
private UUID commentId;
private UserInfoResponse user;
private UUID postId;
private String content;
private boolean anonymous;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
private UUID parentCommentId;
private List<CommentResponse> replies;

private static UserInfoResponse getCommentUser(Comment comment) {
if (comment.isAnonymous()) {
return new UserInfoResponse(null, "익명", null, null, null, null, List.of());
}
return UserInfoResponse.from(comment.getUser());
}

public static CommentResponse from(Comment comment) {
UUID parentId = (comment.getParentComment() != null)
Expand All @@ -35,9 +43,10 @@ public static CommentResponse from(Comment comment) {

return CommentResponse.builder()
.commentId(comment.getCommentId())
.user(UserInfoResponse.from(comment.getUser()))
.user(getCommentUser(comment))
.postId(comment.getPost().getPostId())
.content(comment.getContent())
.anonymous(comment.isAnonymous())
.createdDate(comment.getCreatedDate())
.updatedDate(comment.getUpdatedDate())
.parentCommentId(parentId)
Expand All @@ -51,9 +60,10 @@ public static CommentResponse from(Comment comment, List<CommentResponse> replie

return CommentResponse.builder()
.commentId(comment.getCommentId())
.user(UserInfoResponse.from(comment.getUser()))
.user(getCommentUser(comment))
.postId(comment.getPost().getPostId())
.content(comment.getContent())
.anonymous(comment.isAnonymous())
.createdDate(comment.getCreatedDate())
.updatedDate(comment.getUpdatedDate())
.parentCommentId(parentCommentId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ public class PostRequest {
@NotBlank(message = "내용은 필수 항목입니다.")
private String content;

@Builder.Default
private boolean anonymous = false;

private List<MultipartFile> files;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class PostResponse {
private UserInfoResponse user;
private String title;
private String content;
private boolean anonymous;
private Integer bookmarkCount;
private Integer likeCount;
private Integer commentCount;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public class Comment extends BasePostgresEntity {
@Column(columnDefinition = "TEXT", nullable = false)
private String content;

@Builder.Default
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean anonymous = false;
Comment thread
nayoung04 marked this conversation as resolved.

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_comment_id")
private Comment parentComment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public class Post extends BasePostgresEntity {
@Column(columnDefinition = "TEXT", nullable = false)
private String content;

@Builder.Default
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean anonymous = false;

// 북마크 수
@Builder.Default
private Integer bookmarkCount = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package org.sejongisc.backend.board.service;

import jakarta.persistence.OptimisticLockException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.sejongisc.backend.activity.entity.ActivityType;
import org.sejongisc.backend.activity.event.ActivityEvent;
Expand All @@ -18,16 +15,20 @@
import org.sejongisc.backend.board.repository.PostRepository;
import org.sejongisc.backend.common.exception.CustomException;
import org.sejongisc.backend.common.exception.ErrorCode;
import org.sejongisc.backend.user.repository.UserRepository;
import org.sejongisc.backend.user.entity.Role;
import org.sejongisc.backend.user.entity.User;
import org.sejongisc.backend.user.repository.UserRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Service
@RequiredArgsConstructor
@Transactional
Expand Down Expand Up @@ -77,6 +78,7 @@ public void createComment(CommentRequest request, UUID userId) {
.post(post)
.user(user)
.content(request.getContent())
.anonymous(request.isAnonymous())
.parentComment(parentComment)
.build();

Expand All @@ -85,9 +87,11 @@ public void createComment(CommentRequest request, UUID userId) {
// 게시글의 댓글 수 1 증가
post.setCommentCount(post.getCommentCount() + 1);

String username = request.isAnonymous() ? "익명" : user.getName();

eventPublisher.publishEvent(new ActivityEvent(
user.getUserId(),
user.getName(),
username,
ActivityType.BOARD_COMMENT,
"[" + post.getTitle() + "]에 댓글을 달았습니다.",
post.getPostId(),
Expand All @@ -108,6 +112,7 @@ public void updateComment(CommentRequest request, UUID commentId, UUID userId) {

// 내용 업데이트
comment.setContent(request.getContent());
comment.setAnonymous(request.isAnonymous());
}

// 댓글 삭제
Expand Down
Loading