From 4b7987032cafc185bcc3b3e5fdcedb7af2713459 Mon Sep 17 00:00:00 2001 From: ruchan04 Date: Tue, 28 Apr 2026 23:01:02 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=8B=A0=EA=B3=A0=20=EC=8B=9C=EC=8A=A4?= =?UTF-8?q?=ED=85=9C=20=EB=B0=8F=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=88=A8?= =?UTF-8?q?=EA=B9=80,=20=EB=8C=93=EA=B8=80=20=EC=B1=84=ED=83=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo1/domain/comment/entity/Comment.java | 7 ++++ .../entity/controller/CommentController.java | 0 .../entity/service/CommentService.java | 0 .../exception/GlobalExceptionHandler.java | 5 +++ .../post/controller/PostController.java | 6 ++++ .../demo1/domain/post/entity/Post.java | 7 ++++ .../demo1/domain/post/entity/PostStatus.java | 13 +++++++ .../domain/post/service/PostService.java | 10 ++++++ .../report/controller/ReportController.java | 23 ++++++++++++ .../domain/report/dto/ReportRequestDto.java | 11 ++++++ .../demo1/domain/report/entity/Report.java | 28 +++++++++++++++ .../domain/report/entity/ReportStatus.java | 13 +++++++ .../domain/report/entity/ReportType.java | 4 +++ .../report/repository/ReportRepository.java | 9 +++++ .../domain/report/service/ReportService.java | 35 +++++++++++++++++++ .../demo1/domain/user/entity/User.java | 14 ++++---- src/main/resources/application.properties | 1 + 17 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/example/demo1/domain/comment/entity/controller/CommentController.java create mode 100644 src/main/java/com/example/demo1/domain/comment/entity/service/CommentService.java create mode 100644 src/main/java/com/example/demo1/domain/post/entity/PostStatus.java create mode 100644 src/main/java/com/example/demo1/domain/report/controller/ReportController.java create mode 100644 src/main/java/com/example/demo1/domain/report/dto/ReportRequestDto.java create mode 100644 src/main/java/com/example/demo1/domain/report/entity/Report.java create mode 100644 src/main/java/com/example/demo1/domain/report/entity/ReportStatus.java create mode 100644 src/main/java/com/example/demo1/domain/report/entity/ReportType.java create mode 100644 src/main/java/com/example/demo1/domain/report/repository/ReportRepository.java create mode 100644 src/main/java/com/example/demo1/domain/report/service/ReportService.java diff --git a/src/main/java/com/example/demo1/domain/comment/entity/Comment.java b/src/main/java/com/example/demo1/domain/comment/entity/Comment.java index 9e8a4fe8..b9fea398 100644 --- a/src/main/java/com/example/demo1/domain/comment/entity/Comment.java +++ b/src/main/java/com/example/demo1/domain/comment/entity/Comment.java @@ -28,4 +28,11 @@ public class Comment extends BaseEntity { @Column(length = 255) private String content; + + // domain/comment/entity/Comment.java + private boolean isAdopted = false; // 기본값 false + + public void adopt() { + this.isAdopted = true; + } } \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/comment/entity/controller/CommentController.java b/src/main/java/com/example/demo1/domain/comment/entity/controller/CommentController.java new file mode 100644 index 00000000..e69de29b diff --git a/src/main/java/com/example/demo1/domain/comment/entity/service/CommentService.java b/src/main/java/com/example/demo1/domain/comment/entity/service/CommentService.java new file mode 100644 index 00000000..e69de29b diff --git a/src/main/java/com/example/demo1/domain/global/exception/GlobalExceptionHandler.java b/src/main/java/com/example/demo1/domain/global/exception/GlobalExceptionHandler.java index 0fb9a67d..b226de5c 100644 --- a/src/main/java/com/example/demo1/domain/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/demo1/domain/global/exception/GlobalExceptionHandler.java @@ -31,4 +31,9 @@ public ResponseEntity>> handleNotFoundException(N return ResponseEntity.status(HttpStatus.NOT_FOUND) .body(BaseResponse.onFailure("4040", e.getMessage(), null)); } + @ExceptionHandler(IllegalStateException.class) + public BaseResponse handleIllegalStateException(IllegalStateException e) { + // 500 에러 대신 400(Bad Request) 계열의 코드로 응답을 보냅니다. + return BaseResponse.onFailure("REPORT400", e.getMessage(), null); + } } \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/post/controller/PostController.java b/src/main/java/com/example/demo1/domain/post/controller/PostController.java index 65f50ddd..d024cada 100644 --- a/src/main/java/com/example/demo1/domain/post/controller/PostController.java +++ b/src/main/java/com/example/demo1/domain/post/controller/PostController.java @@ -47,4 +47,10 @@ public BaseResponse deletePost(@PathVariable Long postId) { postService.delete(postId); return BaseResponse.onSuccess("2003", "게시글 삭제 성공", "삭제된 게시글 ID: " + postId); } + // domain/post/controller/PostController.java (기존 파일에 추가) + @PatchMapping("/{postId}/hide") + public BaseResponse hidePost(@PathVariable Long postId) { + postService.hidePost(postId); // postStatus를 HIDDEN으로 바꾸는 로직 + return BaseResponse.onSuccess("POST200", "게시글 숨김 처리가 완료되었습니다.", null); + } } \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/post/entity/Post.java b/src/main/java/com/example/demo1/domain/post/entity/Post.java index ba452b1f..dc52a6b4 100644 --- a/src/main/java/com/example/demo1/domain/post/entity/Post.java +++ b/src/main/java/com/example/demo1/domain/post/entity/Post.java @@ -45,4 +45,11 @@ public void update(String title, String content, String description) { this.content = content; this.description = description; } + // domain/post/entity/Post.java 파일 안에 추가 + @Enumerated(EnumType.STRING) + private PostStatus status = PostStatus.ACTIVE; // 기본값은 ACTIVE + + public void hide() { + this.status = PostStatus.HIDDEN; // 상태를 HIDDEN으로 변경 + } } \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/post/entity/PostStatus.java b/src/main/java/com/example/demo1/domain/post/entity/PostStatus.java new file mode 100644 index 00000000..d9645b15 --- /dev/null +++ b/src/main/java/com/example/demo1/domain/post/entity/PostStatus.java @@ -0,0 +1,13 @@ +package com.example.demo1.domain.post.entity; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum PostStatus { + ACTIVE("활성화"), + HIDDEN("숨김"); + + private final String description; +} \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/post/service/PostService.java b/src/main/java/com/example/demo1/domain/post/service/PostService.java index fecadd0c..b193b0db 100644 --- a/src/main/java/com/example/demo1/domain/post/service/PostService.java +++ b/src/main/java/com/example/demo1/domain/post/service/PostService.java @@ -106,4 +106,14 @@ public void delete(Long postId) { } postRepository.deleteById(postId); } + // domain/post/service/PostService.java 파일 안에 추가 + @Transactional + public void hidePost(Long postId) { + // 1. 해당 ID의 게시글을 찾고, 없으면 에러 발생 + Post post = postRepository.findById(postId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게시글입니다.")); + + // 2. 게시글의 상태를 숨김으로 변경 + post.hide(); + } } \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/report/controller/ReportController.java b/src/main/java/com/example/demo1/domain/report/controller/ReportController.java new file mode 100644 index 00000000..bbd7d74a --- /dev/null +++ b/src/main/java/com/example/demo1/domain/report/controller/ReportController.java @@ -0,0 +1,23 @@ +package com.example.demo1.domain.report.controller; + +import com.example.demo1.domain.global.response.BaseResponse; +import com.example.demo1.domain.report.dto.ReportRequestDto; +import com.example.demo1.domain.report.service.ReportService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/reports") +@RequiredArgsConstructor +public class ReportController { + + private final ReportService reportService; + + @PostMapping + public BaseResponse report(@RequestBody ReportRequestDto dto) { + String result = reportService.createReport(dto.getReporterId(), dto.getTargetId()); + return BaseResponse.onSuccess("REP200", result, null); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/report/dto/ReportRequestDto.java b/src/main/java/com/example/demo1/domain/report/dto/ReportRequestDto.java new file mode 100644 index 00000000..834827b4 --- /dev/null +++ b/src/main/java/com/example/demo1/domain/report/dto/ReportRequestDto.java @@ -0,0 +1,11 @@ +package com.example.demo1.domain.report.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ReportRequestDto { + private Long reporterId; + private Long targetId; +} \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/report/entity/Report.java b/src/main/java/com/example/demo1/domain/report/entity/Report.java new file mode 100644 index 00000000..772ff0fb --- /dev/null +++ b/src/main/java/com/example/demo1/domain/report/entity/Report.java @@ -0,0 +1,28 @@ +package com.example.demo1.domain.report.entity; + +import com.example.demo1.domain.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) // JPA를 위한 기본 생성자 +@AllArgsConstructor +public class Report extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long reporterId; // 신고자 ID + private Long targetId; // 게시물 또는 댓글 ID + + @Enumerated(EnumType.STRING) + private ReportStatus status = ReportStatus.PENDING; // 기본값: 대기중 + + public void resolve() { + this.status = ReportStatus.RESOLVED; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/report/entity/ReportStatus.java b/src/main/java/com/example/demo1/domain/report/entity/ReportStatus.java new file mode 100644 index 00000000..27f692b1 --- /dev/null +++ b/src/main/java/com/example/demo1/domain/report/entity/ReportStatus.java @@ -0,0 +1,13 @@ +package com.example.demo1.domain.report.entity; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ReportStatus { + PENDING("대기 중"), + RESOLVED("처리 완료"); + + private final String description; +} \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/report/entity/ReportType.java b/src/main/java/com/example/demo1/domain/report/entity/ReportType.java new file mode 100644 index 00000000..ddec64f5 --- /dev/null +++ b/src/main/java/com/example/demo1/domain/report/entity/ReportType.java @@ -0,0 +1,4 @@ +package com.example.demo1.domain.report.entity; + +public class ReportType { +} diff --git a/src/main/java/com/example/demo1/domain/report/repository/ReportRepository.java b/src/main/java/com/example/demo1/domain/report/repository/ReportRepository.java new file mode 100644 index 00000000..dff66be8 --- /dev/null +++ b/src/main/java/com/example/demo1/domain/report/repository/ReportRepository.java @@ -0,0 +1,9 @@ +package com.example.demo1.domain.report.repository; + +import com.example.demo1.domain.report.entity.Report; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReportRepository extends JpaRepository { + // [중복 방지] 신고자와 타겟 ID로 이미 데이터가 있는지 확인하는 메서드 + boolean existsByReporterIdAndTargetId(Long reporterId, Long targetId); +} \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/report/service/ReportService.java b/src/main/java/com/example/demo1/domain/report/service/ReportService.java new file mode 100644 index 00000000..8efdf2b6 --- /dev/null +++ b/src/main/java/com/example/demo1/domain/report/service/ReportService.java @@ -0,0 +1,35 @@ +package com.example.demo1.domain.report.service; + +import com.example.demo1.domain.report.entity.Report; +import com.example.demo1.domain.report.entity.ReportStatus; +import com.example.demo1.domain.report.repository.ReportRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ReportService { + + private final ReportRepository reportRepository; + + @Transactional + public String createReport(Long reporterId, Long targetId) { + // [오류 해결 포인트 1] 리포지토리에 existsByReporterIdAndTargetId 메서드가 선언되어 있어야 합니다. + if (reportRepository.existsByReporterIdAndTargetId(reporterId, targetId)) { + throw new IllegalStateException("이미 신고한 대상입니다."); + } + + Report report = Report.builder() + .reporterId(reporterId) + .targetId(targetId) + .status(ReportStatus.PENDING) + .build(); + + reportRepository.save(report); + return "신고가 정상적으로 접수되었습니다."; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/example/demo1/domain/user/entity/User.java b/src/main/java/com/example/demo1/domain/user/entity/User.java index 240cb84a..6608c6a9 100644 --- a/src/main/java/com/example/demo1/domain/user/entity/User.java +++ b/src/main/java/com/example/demo1/domain/user/entity/User.java @@ -10,7 +10,8 @@ import java.util.List; @Entity -@Table(name = "user") +// [수정 포인트 1] 'user'는 예약어이므로 테이블 이름을 'users'로 변경합니다. +@Table(name = "users") @Builder @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -23,20 +24,21 @@ public class User extends BaseEntity { private Integer age; - @Column(length = 255) + // [수정 포인트 2] 실무에서는 null 방지나 유니크 설정을 위해 column 정의를 더 구체적으로 합니다. + @Column(nullable = false, length = 20) private String name; - @Column(length = 255) + @Column(nullable = false, unique = true, length = 100) private String email; - @Column(length = 255) + @Column(length = 50) private String nickname; @Builder.Default - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List posts = new ArrayList<>(); @Builder.Default - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List comments = new ArrayList<>(); } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 2109a440..dde1beef 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,2 @@ spring.application.name=demo +spring.jpa.open-in-view=false