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
8 changes: 8 additions & 0 deletions src/main/java/com/example/demo/comment/entity/Comment.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@ public class Comment {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private CommentStatus status = CommentStatus.ACTIVE;

public void setUser(User user) { this.user = user; }
public void setPost(Post post) { this.post = post; }
public void setContent(String c) { this.content = c; }
public void setCreatedAt(LocalDateTime t) { this.createdAt = t; }
public void setUpdatedAt(LocalDateTime t) { this.updatedAt = t; }

public void hide() {
this.status = CommentStatus.HIDDEN;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.demo.comment.entity;

public enum CommentStatus {
ACTIVE,
HIDDEN
}
14 changes: 13 additions & 1 deletion src/main/java/com/example/demo/global/exception/BaseCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum BaseCode {

// user
USER_CREATE_SUCCESS("2001", "유저 생성 성공"),
USER_NOT_FOUND("4041", "사용자를 찾을 수 없습니다."),

// post
POST_CREATE_SUCCESS("2010", "게시글 생성 성공"),
Expand All @@ -23,7 +24,18 @@ public enum BaseCode {
// comment
COMMENT_CREATE_SUCCESS("2011", "댓글 생성 성공"),
COMMENT_UPDATE_SUCCESS("2004", "댓글 수정 성공"),
COMMENT_DELETE_SUCCESS("2005", "댓글 삭제 성공");
COMMENT_DELETE_SUCCESS("2005", "댓글 삭제 성공"),
COMMENT_NOT_FOUND("4042", "댓글을 찾을 수 없습니다."),

// report
REPORT_POST_SUCCESS("2020", "게시글 신고 성공"),
REPORT_COMMENT_SUCCESS("2021", "댓글 신고 성공"),
REPORT_RESOLVE_SUCCESS("2022", "신고 처리 완료"),

REPORT_NOT_FOUND("4043", "신고를 찾을 수 없습니다."),
ALREADY_REPORTED_POST("4003", "이미 신고한 게시글입니다."),
ALREADY_REPORTED_COMMENT("4004", "이미 신고한 댓글입니다."),
ALREADY_RESOLVED_REPORT("4005", "이미 처리 완료된 신고입니다.");

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.demo.global.exception;

import lombok.Getter;

@Getter
public class CustomException extends RuntimeException {

private final BaseCode baseCode;

public CustomException(BaseCode baseCode) {
super(baseCode.getMessage());
this.baseCode = baseCode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
@RestControllerAdvice
public class GlobalExceptionHandler {

// 검증 만족 못한 내용들
// 검증 실패 처리
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Map<String, String>>> handleValidation(
MethodArgumentNotValidException e) {

MethodArgumentNotValidException e
) {
Map<String, String> errors = new HashMap<>();

e.getBindingResult().getFieldErrors()
Expand All @@ -26,27 +26,30 @@ public ResponseEntity<ApiResponse<Map<String, String>>> handleValidation(
.body(ResponseUtil.fail(BaseCode.INVALID_REQUEST, errors));
}

// 게시글 없음
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ApiResponse<Map<String, Object>>> handleIllegal(
IllegalArgumentException e) {

if (e.getMessage().contains("게시글 없음")) {
return ResponseEntity.status(404)
.body(ResponseUtil.fail(
BaseCode.POST_NOT_FOUND,
Map.of("postId", -1)
));
}
// CustomException 처리
@ExceptionHandler(CustomException.class)
public ResponseEntity<ApiResponse<Object>> handleCustomException(
CustomException e
) {
return ResponseEntity.badRequest()
.body(ResponseUtil.fail(e.getBaseCode(), null));
}

// IllegalArgumentException 처리
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ApiResponse<Object>> handleIllegal(
IllegalArgumentException e
) {
return ResponseEntity.badRequest()
.body(ResponseUtil.fail(BaseCode.INVALID_REQUEST, null));
.body(ResponseUtil.fail(BaseCode.INVALID_REQUEST, e.getMessage()));
}

// 기타 예외상황
// 기타 예외 처리
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<Void>> handleException() {
public ResponseEntity<ApiResponse<Object>> handleException(
Exception e
) {
return ResponseEntity.internalServerError()
.body(ResponseUtil.fail(BaseCode.INVALID_REQUEST, null));
.body(ResponseUtil.fail(BaseCode.INVALID_REQUEST, e.getMessage()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

public class ResponseUtil {

// 성공 응답
public static <T> ApiResponse<T> success(BaseCode code, T result) {
return ApiResponse.<T>builder()
.isSuccess(true)
Expand All @@ -11,6 +12,7 @@ public static <T> ApiResponse<T> success(BaseCode code, T result) {
.build();
}

// 실패 응답
public static <T> ApiResponse<T> fail(BaseCode code, T result) {
return ApiResponse.<T>builder()
.isSuccess(false)
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/example/demo/post/entity/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
import com.example.demo.comment.entity.Comment;
import com.example.demo.user.entity.User;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;


import java.time.LocalDateTime;
import java.util.*;

@Builder
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class Post {

Expand All @@ -27,6 +32,11 @@ public class Post {
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
@Builder.Default
private PostStatus status = PostStatus.ACTIVE;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

과제 요구 사항 중 하나인 상태 변화 (ACTIVE->HIDDEN)를 잘 설계하신 것 같아요 👍


@OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<PostBlock> blocks = new ArrayList<>();

Expand All @@ -40,6 +50,10 @@ public class Post {
public void setUpdatedAt(LocalDateTime t) { this.updatedAt = t; }
public void setDeletedAt(LocalDateTime t) { this.deletedAt = t; }

public void hide() {
this.status = PostStatus.HIDDEN;
}

public void addBlock(PostBlock block) {
blocks.add(block);
block.setPost(this);
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/example/demo/post/entity/PostStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.demo.post.entity;

public enum PostStatus {
ACTIVE,
HIDDEN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.example.demo.report.controller;

import com.example.demo.global.exception.ApiResponse;
import com.example.demo.global.exception.BaseCode;
import com.example.demo.global.exception.ResponseUtil;
import com.example.demo.report.dto.ReportCreateRequest;
import com.example.demo.report.dto.ReportResponse;
import com.example.demo.report.service.ReportService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
public class ReportController {

private final ReportService reportService;

// 게시글 신고
@PostMapping("/posts/{postId}/reports")
public ResponseEntity<ApiResponse<ReportResponse>> reportPost(
@PathVariable Long postId,
@RequestBody ReportCreateRequest request
) {
ReportResponse response = reportService.reportPost(postId, request);

return ResponseEntity.ok(
ResponseUtil.success(BaseCode.REPORT_POST_SUCCESS, response)
);
}

// 댓글 신고
@PostMapping("/comments/{commentId}/reports")
public ResponseEntity<ApiResponse<ReportResponse>> reportComment(
@PathVariable Long commentId,
@RequestBody ReportCreateRequest request
) {
ReportResponse response = reportService.reportComment(commentId, request);

return ResponseEntity.ok(
ResponseUtil.success(BaseCode.REPORT_COMMENT_SUCCESS, response)
);
}

// 신고 처리 완료
@PatchMapping("/reports/{reportId}/resolve")
public ResponseEntity<ApiResponse<ReportResponse>> resolveReport(
@PathVariable Long reportId
) {
ReportResponse response = reportService.resolveReport(reportId);

return ResponseEntity.ok(
ResponseUtil.success(BaseCode.REPORT_RESOLVE_SUCCESS, response)
);
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/example/demo/report/dto/ReportCreateRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.demo.report.dto;

import lombok.Getter;

@Getter
public class ReportCreateRequest {

private Long reporterId;

private String reason;
}
46 changes: 46 additions & 0 deletions src/main/java/com/example/demo/report/dto/ReportResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.example.demo.report.dto;

import com.example.demo.report.entity.Report;
import com.example.demo.report.entity.ReportStatus;
import com.example.demo.report.entity.ReportTargetType;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@Builder
public class ReportResponse {

private Long reportId;
private Long reporterId;
private ReportTargetType targetType;
private Long targetId;
private String reason;
private ReportStatus status;
private LocalDateTime createdAt;
private LocalDateTime resolvedAt;

public static ReportResponse from(Report report) {
Long targetId = null;

if (report.getTargetType() == ReportTargetType.POST) {
targetId = report.getPost().getId();
}

if (report.getTargetType() == ReportTargetType.COMMENT) {
targetId = report.getComment().getId();
}

return ReportResponse.builder()
.reportId(report.getId())
.reporterId(report.getReporter().getId())
.targetType(report.getTargetType())
.targetId(targetId)
.reason(report.getReason())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 신고 사유 필드를 통해 관리자가 신고 내용을 쉽게 파악할 수 있겠네요. 📝 이전에 작성하신 서비스 로직의 title 필드와 이 DTO의 reason 필드 네이밍이 일치하는지 한 번 더 확인해 보시면 완벽할 것 같습니다.

.status(report.getStatus())
.createdAt(report.getCreatedAt())
.resolvedAt(report.getResolvedAt())
.build();
}
}
57 changes: 57 additions & 0 deletions src/main/java/com/example/demo/report/entity/Report.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.example.demo.report.entity;

import com.example.demo.comment.entity.Comment;
import com.example.demo.post.entity.Post;
import com.example.demo.user.entity.User;
import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Report {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// 신고한 사용자
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reporter_id", nullable = false)
private User reporter;

// 신고 대상 게시글
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;

// 신고 대상 댓글
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "comment_id")
private Comment comment;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private ReportTargetType targetType;

@Column(nullable = false)
private String reason;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private ReportStatus status;

@Column(nullable = false)
private LocalDateTime createdAt;

private LocalDateTime resolvedAt;

public void resolve() {
this.status = ReportStatus.RESOLVED;
this.resolvedAt = LocalDateTime.now();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.demo.report.entity;

public enum ReportStatus {
PENDING,
RESOLVED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.demo.report.entity;

public enum ReportTargetType {
POST,
COMMENT
}
Loading