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
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.command.itdaserver.domain.post.domain;

import com.command.itdaserver.domain.post.domain.enums.ReportReason;
import com.command.itdaserver.domain.user.domain.User;
import com.command.itdaserver.global.entity.BaseIdEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Report extends BaseIdEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id", nullable = false)
private Post post;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reporter_id", nullable = false)
private User reporter;

@NotNull
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private ReportReason reason;

@Size(max = 300)
private String detail;

@CreationTimestamp
@Column(nullable = false, name = "created_at")
private LocalDateTime createdAt;

public static Report create(Post post, User reporter, ReportReason reason, String detail) {
Report report = new Report();
report.post = post;
report.reporter = reporter;
report.reason = reason;
report.detail = detail;
return report;
}
Comment on lines +41 to +48
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

현재의 정적 팩토리 메서드는 new Report()로 빈 객체를 생성한 후 필드를 수동으로 할당하고 있습니다. 이 방식보다는 빌더 패턴을 사용하여 객체의 불변성을 보장하고 생성 로직을 더 명확하게 만드는 것이 좋습니다.

@Builder 어노테이션을 클래스에 추가하고, 이 create 메서드를 제거하는 것을 고려해보세요. 서비스 레이어에서는 빌더를 사용하여 Report 객체를 생성하게 됩니다.

Report.java 수정 제안:

@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Report extends BaseIdEntity {
    // ... 필드들 ...
    // create() 메서드는 제거합니다.
}

CreateReportService.java에서의 사용 예시:

Report report = Report.builder()
        .post(post)
        .reporter(reporter)
        .reason(request.getReason())
        .detail(request.getDetail())
        .build();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.command.itdaserver.domain.post.domain.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ReportReason {

SPAM("광고·홍보·스팸"),
HATE_SPEECH("욕설·비하·혐오 표현"),
OBSCENITY("음란·불쾌한 내용"),
IRRELEVANT("공고 목적과 맞지 않는 내용"),
OTHER("기타");

private final String displayName;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.command.itdaserver.domain.post.domain.repository;

import com.command.itdaserver.domain.post.domain.Report;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ReportRepository extends JpaRepository<Report, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.command.itdaserver.domain.post.presentation.dto.request.CreateFormRequest;
import com.command.itdaserver.domain.post.presentation.dto.request.CreatePostRequest;
import com.command.itdaserver.domain.post.presentation.dto.request.SubmitAnswerRequest;
import com.command.itdaserver.domain.post.presentation.dto.request.CreateReportRequest;
import com.command.itdaserver.domain.post.presentation.dto.request.UpdatePostRequest;
import com.command.itdaserver.domain.post.presentation.dto.response.AnswerResponse;
import com.command.itdaserver.domain.post.presentation.dto.response.ApplyFormResponse;
Expand All @@ -16,6 +17,7 @@
import com.command.itdaserver.domain.post.service.GetPostsService;
import com.command.itdaserver.domain.post.service.ToggleBookmarkService;
import com.command.itdaserver.domain.post.service.ToggleLikeService;
import com.command.itdaserver.domain.post.service.CreateReportService;
import com.command.itdaserver.domain.post.service.DeletePostService;
import com.command.itdaserver.domain.post.service.UpdatePostService;
import com.command.itdaserver.global.auth.CustomUserDetails;
Expand All @@ -40,6 +42,7 @@ public class PostController {
private final ToggleLikeService toggleLikeService;
private final ToggleBookmarkService toggleBookmarkService;
private final UpdatePostService updatePostService;
private final CreateReportService createReportService;
private final DeletePostService deletePostService;

@GetMapping
Expand Down Expand Up @@ -104,4 +107,11 @@ public void toggleBookmark(@PathVariable Long postId,
toggleBookmarkService.execute(postId, userDetails);
}

@PostMapping("/{postId}/report")
public void reportPost(@PathVariable Long postId,
@AuthenticationPrincipal CustomUserDetails userDetails,
@Valid @RequestBody CreateReportRequest request) {
createReportService.execute(postId, request, userDetails);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.command.itdaserver.domain.post.presentation.dto.request;

import com.command.itdaserver.domain.post.domain.enums.ReportReason;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class CreateReportRequest {

@NotNull(message = "신고 사유는 필수입니다.")
private ReportReason reason;

@Size(max = 300, message = "상세설명은 최대 300자까지 입력 가능합니다.")
private String detail;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.command.itdaserver.domain.post.service;

import com.command.itdaserver.domain.post.domain.Post;
import com.command.itdaserver.domain.post.domain.Report;
import com.command.itdaserver.domain.post.domain.repository.PostRepository;
import com.command.itdaserver.domain.post.domain.repository.ReportRepository;
import com.command.itdaserver.domain.post.exceptions.PostNotFoundException;
import com.command.itdaserver.domain.post.presentation.dto.request.CreateReportRequest;
import com.command.itdaserver.domain.user.domain.User;
import com.command.itdaserver.domain.user.domain.repository.UserRepository;
import com.command.itdaserver.domain.user.exception.UserNotFoundException;
import com.command.itdaserver.global.auth.CustomUserDetails;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class CreateReportService {

private final PostRepository postRepository;
private final ReportRepository reportRepository;
private final UserRepository userRepository;

@Transactional
public void execute(Long postId, CreateReportRequest request, CustomUserDetails userDetails) {

Post post = postRepository.findById(postId)
.orElseThrow(() -> PostNotFoundException.EXCEPTION);

User reporter = userRepository.findByUserId(userDetails.getUserId())
.orElseThrow(() -> UserNotFoundException.EXCEPTION);

Report report = Report.create(post, reporter, request.getReason(), request.getDetail());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

신고 기능에 대한 몇 가지 중요한 검증 로직이 누락된 것으로 보입니다. 아래 두 가지 검증을 추가하는 것을 강력히 권장합니다.

  1. 자신이 작성한 게시물 신고 방지: 사용자가 자신의 게시물을 신고하는 것은 일반적인 시나리오가 아니므로, 게시물 작성자와 신고자가 동일 인물인지 확인하여 이를 방지해야 합니다.
  2. 중복 신고 방지: 한 사용자가 동일한 게시물에 대해 여러 번 신고하는 것을 막아 시스템 악용을 방지해야 합니다.

아래와 같이 reporter를 조회한 후, Report 객체를 생성하기 전에 검증 로직을 추가할 수 있습니다.

// 자신의 게시물 신고 방지
if (post.getWriter().getId().equals(reporter.getId())) {
    // 적절한 예외(e.g., 400 Bad Request)를 발생시켜야 합니다.
    throw new SelfReportNotAllowedException("자신의 게시물은 신고할 수 없습니다.");
}

// 중복 신고 방지 (ReportRepository에 existsByPostAndReporter 메서드 추가 필요)
if (reportRepository.existsByPostAndReporter(post, reporter)) {
    // 적절한 예외(e.g., 409 Conflict)를 발생시켜야 합니다.
    throw new DuplicateReportException("이미 신고한 게시물입니다.");
}

위의 중복 신고 방지 로직을 위해 ReportRepository에 다음 메서드를 추가해야 합니다.

// ReportRepository.java
boolean existsByPostAndReporter(Post post, User reporter);

reportRepository.save(report);
}
}