-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
피드 신고 기능 구현 #285
피드 신고 기능 구현 #285
Changes from 3 commits
b20ae57
2eaeaeb
60b067d
92b4433
d12366d
8aa4ec7
3954f29
c85787b
fbe4a39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.depromeet.stonebed.domain.report.api; | ||
|
||
import com.depromeet.stonebed.domain.report.application.ReportService; | ||
import com.depromeet.stonebed.domain.report.dto.request.ReportRequest; | ||
import com.depromeet.stonebed.domain.report.dto.response.ReportReasonResponse; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
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 = "8. [신고]", description = "신고 기능 관련 API입니다.") | ||
@RestController | ||
@RequestMapping("/reports") | ||
@RequiredArgsConstructor | ||
public class ReportController { | ||
private final ReportService reportService; | ||
|
||
@Operation(summary = "신고 사유 목록 조회", description = "신고 사유 목록을 가져옵니다.") | ||
@GetMapping("/reasons") | ||
public ResponseEntity<List<ReportReasonResponse>> getReportReasons() { | ||
List<ReportReasonResponse> reasons = reportService.getReportReasons(); | ||
return ResponseEntity.ok(reasons); | ||
} | ||
|
||
@Operation(summary = "신고하기", description = "특정 피드를 신고한다.") | ||
@PostMapping | ||
public ResponseEntity<Void> reportFeed(@RequestBody ReportRequest reportRequest) { | ||
reportService.reportFeed(reportRequest); | ||
return ResponseEntity.ok().build(); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사실 예전부터 컨벤션 정의할 때부터 맞췄어야 했는데 Entity Resource 생성 시 ResponseEntity 201 Status Code로 return 하도록 하는 것이 가독성 측면과 RestFul하다고 하더라고용 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.depromeet.stonebed.domain.report.application; | ||
|
||
import com.depromeet.stonebed.domain.member.domain.Member; | ||
import com.depromeet.stonebed.domain.missionRecord.dao.MissionRecordRepository; | ||
import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord; | ||
import com.depromeet.stonebed.domain.report.dao.ReportRepository; | ||
import com.depromeet.stonebed.domain.report.domain.Report; | ||
import com.depromeet.stonebed.domain.report.domain.ReportReason; | ||
import com.depromeet.stonebed.domain.report.dto.request.ReportRequest; | ||
import com.depromeet.stonebed.domain.report.dto.response.ReportReasonResponse; | ||
import com.depromeet.stonebed.global.error.ErrorCode; | ||
import com.depromeet.stonebed.global.error.exception.CustomException; | ||
import com.depromeet.stonebed.global.util.MemberUtil; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Transactional | ||
public class ReportService { | ||
private final ReportRepository reportRepository; | ||
private final MissionRecordRepository missionRecordRepository; | ||
private final MemberUtil memberUtil; | ||
|
||
public void reportFeed(ReportRequest reportRequest) { | ||
final Member member = memberUtil.getCurrentMember(); | ||
|
||
MissionRecord missionRecord = | ||
missionRecordRepository | ||
.findById(reportRequest.recordId()) | ||
.orElseThrow(() -> new CustomException(ErrorCode.MISSION_RECORD_NOT_FOUND)); | ||
|
||
ReportReason reportReason = ReportReason.fromName(reportRequest.reason()); | ||
|
||
Report report = | ||
Report.builder() | ||
.missionRecord(missionRecord) | ||
.member(member) | ||
.reportReason(reportReason) | ||
.details(reportRequest.details()) | ||
.build(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 도메인 entity에서 정적 팩토리 메서드를 사용하면 좋을 거 같아용 |
||
|
||
reportRepository.save(report); | ||
} | ||
|
||
public List<ReportReasonResponse> getReportReasons() { | ||
return Arrays.stream(ReportReason.values()) | ||
.map(reason -> new ReportReasonResponse(reason.name(), reason.getValue())) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.depromeet.stonebed.domain.report.dao; | ||
|
||
import com.depromeet.stonebed.domain.report.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 |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.depromeet.stonebed.domain.report.domain; | ||
|
||
import com.depromeet.stonebed.domain.common.BaseTimeEntity; | ||
import com.depromeet.stonebed.domain.member.domain.Member; | ||
import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.FetchType; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import jakarta.persistence.Table; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@Entity | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Table(name = "feed_report") | ||
public class Report extends BaseTimeEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "mission_record_id", nullable = false) | ||
private MissionRecord missionRecord; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "member_id", nullable = false) | ||
private Member member; | ||
|
||
private String reason; | ||
|
||
Comment on lines
+36
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Enumerated(EnumType.STRING) 사용해서 Enum 타입으로 정의하지 않는 이유가 있을까용?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. EnumType.STRING을 사용하면 db에 enum의 name값이들어가서 convert를 사용하거나 String을사용해서 value를 저장하려했습니다. 혹시 좋은 방법이 있을까요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아니면 이건 기획 의도따라 다르겠지만 굳이 ReportReason을 enum으로 정의하지 않고, 신고하려는 이유가 너무 불특정하게 계속 생길 수도 있고, enum에 대한 길이도 계속 길어지다보니 굳이 enum 자체를 Request 받지 않고 String으로 Request 받아도 될 듯해용 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음.. figma를 봤을 땐 기획상 신고 사유가 많아지더라도 지금 수준이랑 비슷할 것 같다고 생각했어요! 그리고 기타라는 항목도 있기에 크게 이유가 불특정하게 enum값이 생길 것 같다고 생각을 안했습니다. 저는 Enum을 사용해서 관리하는 부분이 좀 더 낫다고 생각하는데 어떻게 생각하시나요? 추가로 현재는 Enum을 name-value형식으로 되어있는데 @convert를 사용해서 Enum을 사용하되, 원하는 value값을 저장하는 방식으로 코드를 수정하는건 어떨까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위의 댓글 취소!,,, 윤범님이 말씀하신대로 클라이언트한테 request를 String으로받고 하는식으로 진행하도록 하겠습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정된 부분 확인한번 부탁드릴게요~ |
||
private String details; | ||
|
||
@Builder | ||
public Report( | ||
MissionRecord missionRecord, Member member, ReportReason reportReason, String details) { | ||
this.missionRecord = missionRecord; | ||
this.member = member; | ||
this.reason = reportReason.getValue(); | ||
this.details = details; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.depromeet.stonebed.domain.report.domain; | ||
|
||
import com.depromeet.stonebed.global.error.ErrorCode; | ||
import com.depromeet.stonebed.global.error.exception.CustomException; | ||
import java.util.Arrays; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public enum ReportReason { | ||
HARASSMENT_OR_ABUSE("사기 또는 사칭"), | ||
NOT_A_PET("반려동물이 아님"), | ||
VIOLENCE_HARASSMENT_OR_HATE("폭력, 혐오 또는 학대"), | ||
ADVERTISEMENT_SPAM("광고, 홍보, 스팸"), | ||
ADULT_CONTENT("성인용 콘텐츠"), | ||
OTHER("기타"); | ||
|
||
private final String value; | ||
|
||
public static ReportReason fromName(String name) { | ||
return Arrays.stream(values()) | ||
.filter(reason -> reason.name().equals(name)) | ||
.findFirst() | ||
.orElseThrow(() -> new CustomException(ErrorCode.INVALID_REPORT_REASON)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.depromeet.stonebed.domain.report.dto.request; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
import jakarta.validation.constraints.Size; | ||
|
||
public record ReportRequest( | ||
@NotNull Long recordId, @NotNull String reason, @Size(max = 500) String details) {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package com.depromeet.stonebed.domain.report.dto.response; | ||
|
||
public record ReportReasonResponse(String enumValue, String description) {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,7 +57,10 @@ public enum ErrorCode { | |
// fcm | ||
INVALID_FCM_TOKEN(HttpStatus.BAD_REQUEST, "FCM 토큰값이 비어있습니다."), | ||
FAILED_TO_FIND_FCM_TOKEN(HttpStatus.NOT_FOUND, "해당 FCM 토큰을 찾을 수 없습니다."), | ||
NOTIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 알림을 찾을 수 없습니다."); | ||
NOTIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 알림을 찾을 수 없습니다."), | ||
|
||
// report | ||
INVALID_REPORT_REASON(HttpStatus.NOT_FOUND, "해당 신고 목록을 찾을 수 없습니다."); | ||
private final HttpStatus httpStatus; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 신고 목록보다는 |
||
private final String message; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
굳이 reasons 엔드포인트를 작성하지 않아도
/reports
만으로 충분해보입니당