diff --git a/src/main/java/com/back/catchmate/domain/board/controller/BoardController.java b/src/main/java/com/back/catchmate/domain/board/controller/BoardController.java index ab3616b..68a93b4 100644 --- a/src/main/java/com/back/catchmate/domain/board/controller/BoardController.java +++ b/src/main/java/com/back/catchmate/domain/board/controller/BoardController.java @@ -41,7 +41,7 @@ public class BoardController { private final BookMarkService bookMarkService; @PostMapping - @Operation(summary = "게시글 등록 API & 게시글 임시 등록 API", description = "게시글을 등록합니다.") + @Operation(summary = "게시글 등록 API & 게시글 임시 등록 API", description = "게시글을 등록하는 API 입니다.") public BoardInfo createOrUpdateBoard(@JwtValidation Long userId, @Valid @RequestBody CreateOrUpdateBoardRequest request) { return boardService.createOrUpdateBoard(userId, null, request); @@ -88,7 +88,7 @@ public StateResponse addBookMark(@JwtValidation Long userId, } @GetMapping("/bookmark") - @Operation(summary = "찜한 게시글 조회 API", description = "사용자가 찜한 게시글을 조회하는 API입니다.") + @Operation(summary = "찜한 게시글 조회 API", description = "사용자가 찜한 게시글을 조회하는 API 입니다.") public PagedBoardInfo getBookMarkBoardList(@JwtValidation Long userId, @PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC) @Parameter(hidden = true) Pageable pageable) { @@ -103,7 +103,7 @@ public StateResponse removeBookMark(@JwtValidation Long userId, } @PatchMapping("/{boardId}") - @Operation(summary = "게시글 수정 API", description = "게시글을 수정합니다.") + @Operation(summary = "게시글 수정 API", description = "게시글을 수정하는 API 입니다.") public BoardInfo updateBoard(@JwtValidation Long userId, @PathVariable Long boardId, @Valid @RequestBody CreateOrUpdateBoardRequest request) { @@ -111,14 +111,14 @@ public BoardInfo updateBoard(@JwtValidation Long userId, } @DeleteMapping("/{boardId}") - @Operation(summary = "게시글 삭제 API", description = "게시글을 삭제합니다.") + @Operation(summary = "게시글 삭제 API", description = "게시글을 삭제하는 API 입니다.") public BoardDeleteInfo deleteBoard(@JwtValidation Long userId, @PathVariable Long boardId) { return boardService.deleteBoard(userId, boardId); } @PatchMapping("/{boardId}/lift-up") - @Operation(summary = "게시글 끌어올리기 API", description = "게시글을 끌어올립니다.") + @Operation(summary = "게시글 끌어올리기 API", description = "게시글을 끌어올리는 API 입니다.") public LiftUpStatusInfo updateLiftUpDate(@JwtValidation Long userId, @PathVariable Long boardId) { return boardService.updateLiftUpDate(userId, boardId); diff --git a/src/main/java/com/back/catchmate/domain/board/entity/Board.java b/src/main/java/com/back/catchmate/domain/board/entity/Board.java index fc5be9f..05af60d 100644 --- a/src/main/java/com/back/catchmate/domain/board/entity/Board.java +++ b/src/main/java/com/back/catchmate/domain/board/entity/Board.java @@ -8,13 +8,22 @@ import com.back.catchmate.domain.notification.entity.Notification; import com.back.catchmate.domain.user.entity.User; import com.back.catchmate.global.entity.BaseTimeEntity; -import jakarta.persistence.*; +import jakarta.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; import java.time.LocalDateTime; import java.util.ArrayList; @@ -22,10 +31,8 @@ @Entity @Getter -@Setter @Builder -@ToString -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @Table(name = "boards") public class Board extends BaseTimeEntity { @@ -100,6 +107,10 @@ public void updateBoard(Club cheerClub, Game game, CreateOrUpdateBoardRequest bo this.game = game; } + public void updateLiftUpDate(LocalDateTime localDateTime) { + this.liftUpDate = localDateTime; + } + public void deleteBoard() { // Enroll 리스트 삭제 for (Enroll enroll : enrollList) { diff --git a/src/main/java/com/back/catchmate/domain/board/service/BoardServiceImpl.java b/src/main/java/com/back/catchmate/domain/board/service/BoardServiceImpl.java index 7facda9..9b03e9f 100644 --- a/src/main/java/com/back/catchmate/domain/board/service/BoardServiceImpl.java +++ b/src/main/java/com/back/catchmate/domain/board/service/BoardServiceImpl.java @@ -272,7 +272,7 @@ public LiftUpStatusInfo updateLiftUpDate(Long userId, Long boardId) { LocalDateTime nextLiftUpAllowed = board.getLiftUpDate().plusDays(3); if (nextLiftUpAllowed.isBefore(now)) { - board.setLiftUpDate(now); + board.updateLiftUpDate(now); return boardConverter.toLiftUpStatusInfo(true, null); } else { // 남은 시간 계산 diff --git a/src/main/java/com/back/catchmate/domain/chat/controller/ChatController.java b/src/main/java/com/back/catchmate/domain/chat/controller/ChatController.java index b8f38fd..f40c56d 100644 --- a/src/main/java/com/back/catchmate/domain/chat/controller/ChatController.java +++ b/src/main/java/com/back/catchmate/domain/chat/controller/ChatController.java @@ -7,7 +7,6 @@ import com.google.firebase.messaging.FirebaseMessagingException; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; diff --git a/src/main/java/com/back/catchmate/domain/game/entity/Game.java b/src/main/java/com/back/catchmate/domain/game/entity/Game.java index 306c31f..b1eba8b 100644 --- a/src/main/java/com/back/catchmate/domain/game/entity/Game.java +++ b/src/main/java/com/back/catchmate/domain/game/entity/Game.java @@ -11,21 +11,18 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; import java.time.LocalDateTime; @Entity @Getter -@Setter @Builder -@ToString -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @Table(name = "games") public class Game extends BaseTimeEntity { diff --git a/src/main/java/com/back/catchmate/domain/report/controller/ReportController.java b/src/main/java/com/back/catchmate/domain/report/controller/ReportController.java new file mode 100644 index 0000000..98caaa0 --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/report/controller/ReportController.java @@ -0,0 +1,45 @@ +package com.back.catchmate.domain.report.controller; + +import com.back.catchmate.domain.report.dto.ReportRequest.CreateReportRequest; +import com.back.catchmate.domain.report.service.ReportService; +import com.back.catchmate.global.dto.StateResponse; +import com.back.catchmate.global.jwt.JwtValidation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@Tag(name = "신고 관련 API") +@RestController +@RequestMapping("/reports") +@RequiredArgsConstructor +public class ReportController { + private final ReportService reportService; + + @PostMapping + @Operation(summary = "유저 신고 API", description = """ + 유저를 신고하는 API입니다. 신고 사유 목록은 다음과 같습니다: + - **PROFANITY**: 욕설 / 비하 발언 + + - **DEFAMATION**: 선수 혹은 특정인 비방 + + - **PRIVACY_INVASION**: 개인 사생활 침해 + + - **SPAM**: 게시글 도배 + + - **ADVERTISEMENT**: 홍보성 게시글 + + - **FALSE_INFORMATION**: 허위사실 유포 + + - **OTHER**: 기타 + """ + ) + public StateResponse reportUser(@JwtValidation Long userId, + @RequestBody CreateReportRequest request) { + return reportService.reportUser(userId, request); + } +} diff --git a/src/main/java/com/back/catchmate/domain/report/converter/ReportConverter.java b/src/main/java/com/back/catchmate/domain/report/converter/ReportConverter.java new file mode 100644 index 0000000..389b9e9 --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/report/converter/ReportConverter.java @@ -0,0 +1,18 @@ +package com.back.catchmate.domain.report.converter; + +import com.back.catchmate.domain.report.dto.ReportRequest; +import com.back.catchmate.domain.report.entity.Report; +import com.back.catchmate.domain.user.entity.User; +import org.springframework.stereotype.Component; + +@Component +public class ReportConverter { + public Report toEntity(User reporter, User reportedUser, ReportRequest.CreateReportRequest request) { + return Report.builder() + .reporter(reporter) + .reportedUser(reportedUser) + .reportReason(request.getReportReason()) + .reasonDetail(request.getReasonDetail()) + .build(); + } +} diff --git a/src/main/java/com/back/catchmate/domain/report/dto/ReportRequest.java b/src/main/java/com/back/catchmate/domain/report/dto/ReportRequest.java new file mode 100644 index 0000000..a8182cf --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/report/dto/ReportRequest.java @@ -0,0 +1,23 @@ +package com.back.catchmate.domain.report.dto; + +import com.back.catchmate.domain.report.entity.ReportReason; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public abstract class ReportRequest { + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class CreateReportRequest { + @NotNull + private Long reportedUserId; + @NotNull + private ReportReason reportReason; + @NotNull + private String reasonDetail; + } +} diff --git a/src/main/java/com/back/catchmate/domain/report/entity/Report.java b/src/main/java/com/back/catchmate/domain/report/entity/Report.java new file mode 100644 index 0000000..c0a7eb5 --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/report/entity/Report.java @@ -0,0 +1,48 @@ +package com.back.catchmate.domain.report.entity; + +import com.back.catchmate.domain.user.entity.User; +import com.back.catchmate.global.entity.BaseTimeEntity; +import jakarta.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Table(name = "reports") +public class Report extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "reported_user_id", nullable = false) + private User reportedUser; // 신고 대상 + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "reporter_id", nullable = false) + private User reporter; // 신고자 + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 500) + private ReportReason reportReason; // 신고 사유 + + @Column(nullable = false, length = 500) + private String reasonDetail; // 신고 사유 +} + diff --git a/src/main/java/com/back/catchmate/domain/report/entity/ReportReason.java b/src/main/java/com/back/catchmate/domain/report/entity/ReportReason.java new file mode 100644 index 0000000..adec53e --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/report/entity/ReportReason.java @@ -0,0 +1,18 @@ +package com.back.catchmate.domain.report.entity; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ReportReason { + PROFANITY("욕설 / 비하 발언"), + DEFAMATION("선수 혹은 특정인 비방"), + PRIVACY_INVASION("개인 사생활 침해"), + SPAM("게시글 도배"), + ADVERTISEMENT("홍보성 게시글"), + FALSE_INFORMATION("허위사실 유포"), + OTHER("기타"); + + private final String description; +} diff --git a/src/main/java/com/back/catchmate/domain/report/repository/ReportRepository.java b/src/main/java/com/back/catchmate/domain/report/repository/ReportRepository.java new file mode 100644 index 0000000..38ea791 --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/report/repository/ReportRepository.java @@ -0,0 +1,7 @@ +package com.back.catchmate.domain.report.repository; + +import com.back.catchmate.domain.report.entity.Report; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReportRepository extends JpaRepository { +} diff --git a/src/main/java/com/back/catchmate/domain/report/service/ReportService.java b/src/main/java/com/back/catchmate/domain/report/service/ReportService.java new file mode 100644 index 0000000..3270a6a --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/report/service/ReportService.java @@ -0,0 +1,8 @@ +package com.back.catchmate.domain.report.service; + +import com.back.catchmate.domain.report.dto.ReportRequest.CreateReportRequest; +import com.back.catchmate.global.dto.StateResponse; + +public interface ReportService { + StateResponse reportUser(Long reporterId, CreateReportRequest request); +} diff --git a/src/main/java/com/back/catchmate/domain/report/service/ReportServiceImpl.java b/src/main/java/com/back/catchmate/domain/report/service/ReportServiceImpl.java new file mode 100644 index 0000000..5b63bfc --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/report/service/ReportServiceImpl.java @@ -0,0 +1,35 @@ +package com.back.catchmate.domain.report.service; + +import com.back.catchmate.domain.report.converter.ReportConverter; +import com.back.catchmate.domain.report.dto.ReportRequest; +import com.back.catchmate.domain.report.entity.Report; +import com.back.catchmate.domain.report.repository.ReportRepository; +import com.back.catchmate.domain.user.entity.User; +import com.back.catchmate.domain.user.repository.UserRepository; +import com.back.catchmate.global.dto.StateResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ReportServiceImpl implements ReportService { + private final ReportRepository reportRepository; + private final UserRepository userRepository; + private final ReportConverter reportConverter; + + @Override + @Transactional + public StateResponse reportUser(Long reporterId, ReportRequest.CreateReportRequest request) { + User reporter = userRepository.findById(reporterId) + .orElseThrow(() -> new IllegalArgumentException("신고자를 찾을 수 없습니다.")); + + User reportedUser = userRepository.findById(request.getReportedUserId()) + .orElseThrow(() -> new IllegalArgumentException("신고 대상 사용자를 찾을 수 없습니다.")); + + Report report = reportConverter.toEntity(reporter, reportedUser, request); + reportRepository.save(report); + + return new StateResponse(true); + } +}