Skip to content
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

[Feat/48] 회원 신고 API 구현 #60

Merged
merged 10 commits into from
Jul 10, 2024
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'

implementation 'org.springframework.boot:spring-boot-starter-mail'

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ public enum ErrorStatus implements BaseErrorCode {
// 차단 관련 에러
TARGET_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "BLOCK401", "차단 대상 회원을 찾을 수 없습니다."),
ALREADY_BLOCKED(HttpStatus.BAD_REQUEST, "BLOCK402", "이미 차단한 회원입니다."),
TARGET_MEMBER_NOT_BLOCKED(HttpStatus.BAD_REQUEST, "BLOCK403", "차단 목록에 존재하지 않는 회원입니다.");
TARGET_MEMBER_NOT_BLOCKED(HttpStatus.BAD_REQUEST, "BLOCK403", "차단 목록에 존재하지 않는 회원입니다."),

// 신고 관련 에러
REPORT_TARGET_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "REPORT401", "신고 대상 회원을 찾을 수 없습니다."),
MEMBER_AND_TARGET_MEMBER_SAME(HttpStatus.BAD_REQUEST, "REPORT402", "회원과 신고 대상 회원이 같습니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.gamegoo.apiPayload.exception.handler;

import com.gamegoo.apiPayload.code.BaseErrorCode;
import com.gamegoo.apiPayload.exception.GeneralException;

public class ReportHandler extends GeneralException {
public ReportHandler(BaseErrorCode code) {
super(code);
}
}
51 changes: 51 additions & 0 deletions src/main/java/com/gamegoo/controller/report/ReportController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.gamegoo.controller.report;


import com.gamegoo.apiPayload.ApiResponse;
import com.gamegoo.domain.report.Report;
import com.gamegoo.dto.report.ReportRequest;
import com.gamegoo.dto.report.ReportResponse;
import com.gamegoo.service.report.ReportService;
import com.gamegoo.util.JWTUtil;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;

import javax.validation.Valid;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/api/reports")
@Tag(name = "Reports", description = "신고 관련 API")
public class ReportController {
private final ReportService reportService;

@PostMapping("")
public ApiResponse<ReportResponse.reportInsertResultDTO> reportInsert(
@RequestBody @Valid ReportRequest.reportInsertDTO request
){
Long memberId = JWTUtil.getCurrentUserId();

Report report = reportService.insertReport(request,memberId);

List<Long> reportTypeIdList = report.getReportTypeMappingList().stream()
.map(reportTypeMapping -> reportTypeMapping.getReportType().getId())
.collect(Collectors.toList());

ReportResponse.reportInsertResultDTO result = ReportResponse.reportInsertResultDTO.builder()
.targetId(report.getTarget().getId())
.reportId(report.getId())
.contents(report.getReportContent())
.reportTypeIdList(reportTypeIdList)
.build();

return ApiResponse.onSuccess(result);
}
}
19 changes: 15 additions & 4 deletions src/main/java/com/gamegoo/domain/report/Report.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import lombok.*;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "Report")
Expand All @@ -21,15 +23,24 @@ public class Report extends BaseDateTimeEntity {
@Column(name = "report_content", nullable = false, length = 1000)
private String reportContent;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "report_type_id", nullable = false)
private ReportType reportType;

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "target_id", nullable = false)
private Member target;

@OneToMany(mappedBy = "report", cascade = CascadeType.ALL)
private List<ReportTypeMapping> reportTypeMappingList = new ArrayList<>();

// 연관관계 메소드
public void setReporter(Member member) {
if (this.reporter != null) {
this.reporter.getReportList().remove(this);
}
this.reporter = member;
this.reporter.getReportList().add(this);
}

}
1 change: 1 addition & 0 deletions src/main/java/com/gamegoo/domain/report/ReportType.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class ReportType extends BaseDateTimeEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "report_type_id")
private Long id;

@Column(name = "report_type_content", nullable = false)
private String reportTypeContent;

Expand Down
37 changes: 37 additions & 0 deletions src/main/java/com/gamegoo/domain/report/ReportTypeMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.gamegoo.domain.report;

import com.gamegoo.domain.common.BaseDateTimeEntity;
import lombok.*;

import javax.persistence.*;

@Entity
@Table(name = "ReportTypeMapping")
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class ReportTypeMapping extends BaseDateTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "report_type_mapping_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "report_type_id", nullable = false)
private ReportType reportType;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "report_id", nullable = false)
private Report report;

// 연관관계 메소드
public void setReport(Report report) {
if (this.report != null) {
this.report.getReportTypeMappingList().remove(this);
}
this.report = report;
this.report.getReportTypeMappingList().add(this);
}

}
22 changes: 22 additions & 0 deletions src/main/java/com/gamegoo/dto/report/ReportRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.gamegoo.dto.report;

import lombok.Getter;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;

public class ReportRequest {

@Getter
public static class reportInsertDTO {
@NotNull
Long targetMemberId;

@NotEmpty
List<Long> reportTypeIdList;

String contents;
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/gamegoo/dto/report/ReportResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.gamegoo.dto.report;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

public class ReportResponse {
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class reportInsertResultDTO{
Copy link
Contributor

Choose a reason for hiding this comment

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

reportInsertResponseDTO

Long reportId;
Long targetId;
List<Long> reportTypeIdList;
String contents;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.gamegoo.repository.report;

import com.gamegoo.domain.report.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,7 @@
package com.gamegoo.repository.report;

import com.gamegoo.domain.report.ReportTypeMapping;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ReportTypeMappingRepository extends JpaRepository<ReportTypeMapping, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.gamegoo.repository.report;

import com.gamegoo.domain.report.ReportType;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ReportTypeRepository extends JpaRepository<ReportType, Long> {

}
80 changes: 80 additions & 0 deletions src/main/java/com/gamegoo/service/report/ReportService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.gamegoo.service.report;

import com.gamegoo.apiPayload.code.status.ErrorStatus;
import com.gamegoo.apiPayload.exception.handler.MemberHandler;
import com.gamegoo.apiPayload.exception.handler.ReportHandler;
import com.gamegoo.apiPayload.exception.handler.TempHandler;
import com.gamegoo.domain.Member;
import com.gamegoo.domain.report.Report;
import com.gamegoo.domain.report.ReportType;
import com.gamegoo.domain.report.ReportTypeMapping;
import com.gamegoo.dto.report.ReportRequest;
import com.gamegoo.repository.member.MemberRepository;
import com.gamegoo.repository.report.ReportRepository;
import com.gamegoo.repository.report.ReportTypeMappingRepository;
import com.gamegoo.repository.report.ReportTypeRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional
public class ReportService{
private final MemberRepository memberRepository;
private final ReportRepository reportRepository;
private final ReportTypeRepository reportTypeRepository;
private final ReportTypeMappingRepository reportTypeMappingRepository;

public Report insertReport(ReportRequest.reportInsertDTO request, Long memberId){
Member member = memberRepository.findById(memberId).orElseThrow(()->new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND));

Copy link
Member

Choose a reason for hiding this comment

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

member와 targetMember가 같은 회원인 경우 에러 처리 추가가 필요할 것 같아요

// target 회원 존재 여부 검증.
Member targetMember = memberRepository.findById(request.getTargetMemberId()).orElseThrow(()->new MemberHandler(ErrorStatus.REPORT_TARGET_MEMBER_NOT_FOUND));

// target 회원 탈퇴 여부 검증.
if (targetMember.getBlind()){
throw new MemberHandler(ErrorStatus.USER_DEACTIVATED);
}

// reportType이 실제 존재 여부 검증.
List<ReportType> reportTypeList = new ArrayList<>();
request.getReportTypeIdList()
.forEach(reportTypeId -> {
ReportType reportType = reportTypeRepository.findById(reportTypeId).orElseThrow(() -> new TempHandler(ErrorStatus._BAD_REQUEST));
reportTypeList.add(reportType);
});

// member 와 targetMember가 같은 회원인 경우.
if (member.getId().equals(targetMember.getId())){
throw new MemberHandler(ErrorStatus.MEMBER_AND_TARGET_MEMBER_SAME);
}

// report 엔티티 생성 및 연관관계 매핑.
Report report = Report.builder()
.target(targetMember)
.reportContent(request.getContents())
.reportTypeMappingList(new ArrayList<>())
.build();

report.setReporter(member);
Report saveReport = reportRepository.save(report);

// reportTypeMapping 엔티티 생성 및 연관관계 매핑.
reportTypeList.forEach(reportType -> {
ReportTypeMapping reportTypeMapping = ReportTypeMapping.builder()
.reportType(reportType)
.build();

reportTypeMapping.setReport(saveReport);
reportTypeMappingRepository.save(reportTypeMapping);
});

return saveReport;
}
}
Loading