diff --git a/src/main/java/meltingpot/server/auth/service/MailService.java b/src/main/java/meltingpot/server/auth/service/MailService.java index b3db63b..45dc0c9 100644 --- a/src/main/java/meltingpot/server/auth/service/MailService.java +++ b/src/main/java/meltingpot/server/auth/service/MailService.java @@ -3,6 +3,8 @@ import lombok.RequiredArgsConstructor; import meltingpot.server.auth.controller.dto.MailVerificationRequestDto; import meltingpot.server.auth.controller.dto.VerificationCodeRequestDto; +import meltingpot.server.domain.entity.Account; +import meltingpot.server.domain.entity.UserReportCount; import meltingpot.server.util.Constants; import meltingpot.server.domain.entity.MailVerification; import meltingpot.server.domain.repository.AccountRepository; @@ -118,4 +120,21 @@ public void checkUserName(String username) { } } + @Transactional + public ResponseCode sendReportAlertEmail(Account account, int reportCount) { + String title = "누적 신고 5회 이상 알림"; + String to = "support@meltingpot.kr"; + String templateName = "ReportAlertTemplate.html"; + + Map mailValues = Map.of( + "username", account.getName(), + "userEmail",account.getUsername(), + "userId", account.getId().toString(), + "reportCount", String.valueOf(reportCount) + ); + + mailUtil.sendMimeMessageMailWithValues(title, to, templateName, mailValues); + return ResponseCode.MAIL_REPORT_ALRERT_SEND_SUCCESS; + } + } diff --git a/src/main/java/meltingpot/server/domain/entity/Account.java b/src/main/java/meltingpot/server/domain/entity/Account.java index d33de76..1a1a141 100644 --- a/src/main/java/meltingpot/server/domain/entity/Account.java +++ b/src/main/java/meltingpot/server/domain/entity/Account.java @@ -85,6 +85,9 @@ public class Account extends BaseEntity { @OneToMany(mappedBy = "account") private List accountRoles = new ArrayList<>(); + @OneToOne(mappedBy = "account", cascade = CascadeType.ALL) + private UserReportCount userReportCount; + public List toAuthStringList() { return accountRoles.stream().map(a -> a.getRole().getAuthority()) .collect(Collectors.toList()); diff --git a/src/main/java/meltingpot/server/domain/entity/Report.java b/src/main/java/meltingpot/server/domain/entity/Report.java index 509dd5f..ca515bd 100644 --- a/src/main/java/meltingpot/server/domain/entity/Report.java +++ b/src/main/java/meltingpot/server/domain/entity/Report.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import meltingpot.server.domain.entity.comment.Comment; import meltingpot.server.domain.entity.common.BaseEntity; import meltingpot.server.domain.entity.post.Post; @@ -16,13 +17,20 @@ public class Report extends BaseEntity { @Column(name = "report_id") private Long id; + @Column(length = 1500) private String content; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id") private Post post; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "comment_id") + private Comment comment; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private Account account; + + } diff --git a/src/main/java/meltingpot/server/domain/entity/UserReportCount.java b/src/main/java/meltingpot/server/domain/entity/UserReportCount.java new file mode 100644 index 0000000..e377b5f --- /dev/null +++ b/src/main/java/meltingpot/server/domain/entity/UserReportCount.java @@ -0,0 +1,33 @@ +package meltingpot.server.domain.entity; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@Builder +@Getter +@Setter +public class UserReportCount { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_report_count_id") + private Long id; + + @Column(name = "report_count") + private int reportCount; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "account_id") + private Account account; + + public void incrementReportCount() { + this.reportCount++; + } + + public UserReportCount(Account account, int reportCount) { + this.account = account; + this.reportCount = reportCount; + } +} diff --git a/src/main/java/meltingpot/server/domain/entity/comment/Comment.java b/src/main/java/meltingpot/server/domain/entity/comment/Comment.java index 529edfe..d6a5379 100644 --- a/src/main/java/meltingpot/server/domain/entity/comment/Comment.java +++ b/src/main/java/meltingpot/server/domain/entity/comment/Comment.java @@ -21,12 +21,15 @@ public class Comment extends BaseEntity { @Column(name = "comment_id") private Long id; - @Size(min = 10, max = 500) + @Column(length = 1500) private String content; @Column(name = "is_anonymous") private boolean isAnonymous = false; + @Column(name = "report_count") + private int reportCount; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id") private Post post; @@ -104,4 +107,8 @@ public void setParent(Comment parent) { public void setChildren(List children) { this.children = children; } + + public void incrementReportCount() { + this.reportCount++; + } } diff --git a/src/main/java/meltingpot/server/domain/entity/post/Post.java b/src/main/java/meltingpot/server/domain/entity/post/Post.java index fde69e0..f6a9bce 100644 --- a/src/main/java/meltingpot/server/domain/entity/post/Post.java +++ b/src/main/java/meltingpot/server/domain/entity/post/Post.java @@ -29,10 +29,12 @@ public class Post extends BaseEntity { @Column(name = "post_id") private Long id; - @Size(min = 10, max = 500) + @Column(length = 500) + @Size(max = 500, message = "제목은 한글 기준 500자 이내로 입력해주세요.") private String title; - @Size(min = 10, max = 500) + @Column(length = 500) + @Size(max = 500, message = "내용은 한글 기준 500자 이내로 입력해주세요.") private String content; @Enumerated(EnumType.STRING) @@ -40,6 +42,9 @@ public class Post extends BaseEntity { private Boolean isDraft; + @Column(name = "report_count") + private int reportCount; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private Account account; @@ -69,4 +74,8 @@ public void setPostImages(List postImages) { public void setIsDraft(boolean isDraft) { this.isDraft = isDraft; } + + public void incrementReportCount() { + this.reportCount++; + } } diff --git a/src/main/java/meltingpot/server/domain/repository/UserReportCountRepository.java b/src/main/java/meltingpot/server/domain/repository/UserReportCountRepository.java new file mode 100644 index 0000000..402100d --- /dev/null +++ b/src/main/java/meltingpot/server/domain/repository/UserReportCountRepository.java @@ -0,0 +1,7 @@ +package meltingpot.server.domain.repository; + +import meltingpot.server.domain.entity.UserReportCount; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserReportCountRepository extends JpaRepository { +} diff --git a/src/main/java/meltingpot/server/post/dto/PostDetailResponse.java b/src/main/java/meltingpot/server/post/dto/PostDetailResponse.java index cde2713..f152c6a 100644 --- a/src/main/java/meltingpot/server/post/dto/PostDetailResponse.java +++ b/src/main/java/meltingpot/server/post/dto/PostDetailResponse.java @@ -1,6 +1,7 @@ package meltingpot.server.post.dto; import lombok.*; +import meltingpot.server.domain.entity.enums.PostType; import meltingpot.server.domain.entity.post.Post; import java.time.LocalDateTime; import java.util.List; @@ -13,14 +14,14 @@ public class PostDetailResponse { private Long postId; private Long userId; + private PostType postType; private String name; private String title; private String content; - private List imgData; // Image data including ID and URL + private List imgData; private Integer commentCount; private LocalDateTime updatedAt; - // Static nested class to hold image data @Getter @AllArgsConstructor @NoArgsConstructor @@ -41,6 +42,7 @@ public static PostDetailResponse of(Post post) { return PostDetailResponse.builder() .postId(post.getId()) .userId(post.getAccount().getId()) + .postType(post.getPostType()) .name(post.getAccount().getName()) .title(post.getTitle()) .content(post.getContent()) diff --git a/src/main/java/meltingpot/server/report/controller/ReportController.java b/src/main/java/meltingpot/server/report/controller/ReportController.java index 102f4d0..1a82547 100644 --- a/src/main/java/meltingpot/server/report/controller/ReportController.java +++ b/src/main/java/meltingpot/server/report/controller/ReportController.java @@ -3,7 +3,7 @@ import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import meltingpot.server.domain.entity.Account; -import meltingpot.server.report.dto.ReportRequestDTO; +import meltingpot.server.report.dto.ReportRequest; import meltingpot.server.report.service.ReportService; import meltingpot.server.util.*; import org.springframework.http.ResponseEntity; @@ -17,11 +17,22 @@ public class ReportController { private final ReportService reportService; - @Operation(summary = "신고 작성") + @Operation(summary = "게시글 신고 작성") @PostMapping("/{postId}") - public ResponseEntity createPost(@CurrentUser Account account, @RequestBody ReportRequestDTO.CreateReportDTO createReportDTO, @PathVariable Long postId) { + public ResponseEntity createPostReport (@CurrentUser Account account, @RequestBody ReportRequest reportRequest, @PathVariable Long postId) { try{ - reportService.createReport(createReportDTO, account, postId); + reportService.createReport(reportRequest, account, postId,null); + return ResponseData.toResponseEntity (ResponseCode.REPORT_CREATE_SUCCESS); + }catch (NoSuchElementException e) { + return ResponseData.toResponseEntity( ResponseCode.REPORT_CREATE_FAIL); + } + } + + @Operation(summary = "댓글 신고 작성") + @PostMapping("/{commentId}") + public ResponseEntity createCommentReport(@CurrentUser Account account, @RequestBody ReportRequest reportRequest, @PathVariable Long commentId) { + try{ + reportService.createReport(reportRequest, account, null ,commentId); return ResponseData.toResponseEntity (ResponseCode.REPORT_CREATE_SUCCESS); }catch (NoSuchElementException e) { return ResponseData.toResponseEntity( ResponseCode.REPORT_CREATE_FAIL); diff --git a/src/main/java/meltingpot/server/report/converter/ReportConverter.java b/src/main/java/meltingpot/server/report/converter/ReportConverter.java deleted file mode 100644 index 1fd4633..0000000 --- a/src/main/java/meltingpot/server/report/converter/ReportConverter.java +++ /dev/null @@ -1,17 +0,0 @@ -package meltingpot.server.report.converter; - -import meltingpot.server.domain.entity.Account; -import meltingpot.server.domain.entity.Report; -import meltingpot.server.domain.entity.post.Post; -import meltingpot.server.report.dto.ReportRequestDTO; - -public class ReportConverter { - - public static Report toReport (ReportRequestDTO.CreateReportDTO createReportDTO, Account account, Post post){ - return Report.builder() - .content(createReportDTO.getContent()) - .account(account) - .post(post) - .build(); - } -} diff --git a/src/main/java/meltingpot/server/report/dto/ReportRequest.java b/src/main/java/meltingpot/server/report/dto/ReportRequest.java new file mode 100644 index 0000000..9f4bdc8 --- /dev/null +++ b/src/main/java/meltingpot/server/report/dto/ReportRequest.java @@ -0,0 +1,34 @@ +package meltingpot.server.report.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import meltingpot.server.domain.entity.Account; +import meltingpot.server.domain.entity.Report; +import meltingpot.server.domain.entity.comment.Comment; +import meltingpot.server.domain.entity.post.Post; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ReportRequest { + private String content; + + public Report toEntity(Account account, Post post){ + return Report.builder() + .content(content) + .post(post) + .account(account) + .build(); + } + + public Report toEntity(Account account, Comment comment){ + return Report.builder() + .content(content) + .comment(comment) + .account(account) + .build(); + } +} diff --git a/src/main/java/meltingpot/server/report/dto/ReportRequestDTO.java b/src/main/java/meltingpot/server/report/dto/ReportRequestDTO.java deleted file mode 100644 index f36dd96..0000000 --- a/src/main/java/meltingpot/server/report/dto/ReportRequestDTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package meltingpot.server.report.dto; - -import lombok.Getter; - -public class ReportRequestDTO { - - @Getter - public static class CreateReportDTO { - private String content; - } -} diff --git a/src/main/java/meltingpot/server/report/service/ReportService.java b/src/main/java/meltingpot/server/report/service/ReportService.java index dda0feb..b79956e 100644 --- a/src/main/java/meltingpot/server/report/service/ReportService.java +++ b/src/main/java/meltingpot/server/report/service/ReportService.java @@ -1,9 +1,87 @@ package meltingpot.server.report.service; +import lombok.RequiredArgsConstructor; +import meltingpot.server.auth.service.MailService; import meltingpot.server.domain.entity.Account; -import meltingpot.server.report.dto.ReportRequestDTO; +import meltingpot.server.domain.entity.Report; +import meltingpot.server.domain.entity.UserReportCount; +import meltingpot.server.domain.entity.comment.Comment; +import meltingpot.server.domain.entity.post.Post; +import meltingpot.server.domain.repository.CommentRepository; +import meltingpot.server.domain.repository.PostRepository; +import meltingpot.server.domain.repository.ReportRepository; +import meltingpot.server.domain.repository.UserReportCountRepository; +import meltingpot.server.report.dto.ReportRequest; +import meltingpot.server.util.ResponseCode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class ReportService { + private final PostRepository postRepository; + private final ReportRepository reportRepository; + private final CommentRepository commentRepository; + private final UserReportCountRepository userReportCountRepository; + private final MailService mailService; + + public ResponseCode createReport (ReportRequest reportRequest, Account account, Long postId, Long commentId){ + if (postId != null) { + handlePostReport(reportRequest, postId); + } else if (commentId != null) { + handleCommentReport(reportRequest, commentId); + } + return ResponseCode.REPORT_CREATE_SUCCESS; + } + + private void updateUserReportCount(Account account){ + UserReportCount userReportCount = userReportCountRepository.findById(account.getId()) + .orElse(new UserReportCount(account, 0)); + userReportCount.incrementReportCount(); + userReportCountRepository.save(userReportCount); + + if (userReportCount.getReportCount() >= 5) { + mailService.sendReportAlertEmail(account,userReportCount.getReportCount()); + } + + } + private void handlePostReport(ReportRequest reportRequest,Long postId){ + Post post = findPostById(postId); + Report report = reportRequest.toEntity(post.getAccount(),post); + reportRepository.save(report); + + post.incrementReportCount(); + postRepository.save(post); + updateUserReportCount(post.getAccount()); + + if(post.getReportCount()>=5){ + mailService.sendReportAlertEmail(post.getAccount(),post.getReportCount()); + } + } + + private void handleCommentReport(ReportRequest reportRequest,Long commentId){ + Comment comment = findCommentById(commentId); + Report report = reportRequest.toEntity(comment.getAccount(),comment); + reportRepository.save(report); + + comment.incrementReportCount(); + commentRepository.save(comment); + updateUserReportCount(comment.getAccount()); + + if(comment.getReportCount()>=5){ + mailService.sendReportAlertEmail(comment.getAccount(),comment.getReportCount()); + } + } + + private Post findPostById(Long postId) { + return postRepository.findById(postId) + .orElseThrow(() -> new RuntimeException("게시물을 찾을 수 없습니다.")); + } + private Comment findCommentById(Long commentId){ + return commentRepository.findById(commentId) + .orElseThrow(()->new RuntimeException("댓글을 찾을 수 없습니다.")); + } -public interface ReportService { - void createReport (ReportRequestDTO.CreateReportDTO createReportDTO, Account account, Long postId); } diff --git a/src/main/java/meltingpot/server/report/service/ReportServiceImpl.java b/src/main/java/meltingpot/server/report/service/ReportServiceImpl.java deleted file mode 100644 index cb82240..0000000 --- a/src/main/java/meltingpot/server/report/service/ReportServiceImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package meltingpot.server.report.service; - -import lombok.RequiredArgsConstructor; -import meltingpot.server.domain.entity.Account; -import meltingpot.server.domain.entity.Report; -import meltingpot.server.domain.entity.post.Post; -import meltingpot.server.domain.repository.PostRepository; -import meltingpot.server.domain.repository.ReportRepository; -import meltingpot.server.report.dto.ReportRequestDTO; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import static meltingpot.server.report.converter.ReportConverter.toReport; - -@Service -@RequiredArgsConstructor -@Transactional -public class ReportServiceImpl implements ReportService { - private final PostRepository postRepository; - private final ReportRepository reportRepository; - - @Override - public void createReport (ReportRequestDTO.CreateReportDTO createReportDTO, Account account, Long postId){ - Post post = findPostById(postId); - Report report = toReport(createReportDTO,account,post); - reportRepository.save(report); - } - - private Post findPostById(Long postId) { - return postRepository.findById(postId) - .orElseThrow(() -> new RuntimeException("게시물을 찾을 수 없습니다.")); - } -} diff --git a/src/main/java/meltingpot/server/util/ResponseCode.java b/src/main/java/meltingpot/server/util/ResponseCode.java index b502ae4..f15b07f 100644 --- a/src/main/java/meltingpot/server/util/ResponseCode.java +++ b/src/main/java/meltingpot/server/util/ResponseCode.java @@ -15,6 +15,7 @@ public enum ResponseCode { OAUTH_SIGNIN_SUCCESS(OK, "SNS 로그인 성공"), SIGNOUT_SUCCESS(OK, "로그아웃 성공"), REISSUE_TOKEN_SUCCESS(OK, "토큰 재발급 성공"), + MAIL_REPORT_ALRERT_SEND_SUCCESS(OK,"누적 신고 알림 메일 전송 성공"), MAIL_VERIFICATION_SEND_SUCCESS(OK, "이메일 인증번호 전송 성공"), MAIL_VERIFICATION_CHECK_SUCCESS(OK, "인증번호가 일치합니다"), MAIL_AVAILABLE(OK, "사용 가능한 이메일입니다"), diff --git a/src/main/resources/templates/ReportAlertTemplate.html b/src/main/resources/templates/ReportAlertTemplate.html new file mode 100644 index 0000000..d99dc2f --- /dev/null +++ b/src/main/resources/templates/ReportAlertTemplate.html @@ -0,0 +1,13 @@ + + + + 누적 신고 5회 이상 알림 + + +

누적 신고 5회 이상인 사용자 알림

+

사용자Id: [[${userId}]]

+

사용자명: [[${username}]]

+

사용자 이메일: [[${userEmail}]]

+

신고 횟수: [[${reportCount}]]회

+ +