Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

@Tag(name = "(Admin) Callvan: Reports", description = "Admin callvan report management")
@Tag(name = "(Admin) Callvan: 신고 처리", description = "어드민 콜벤 사용자 신고 내역 관리")
@RequestMapping("/admin/callvan/reports")
public interface AdminCallvanReportApi {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ ResponseEntity<Void> leaveCallvanPost(
- `reasons`: 신고 사유 목록 (1개 이상)
- `reason_code`: `NO_SHOW`, `NON_PAYMENT`, `PROFANITY`, `OTHER`
- `custom_text`: `OTHER`일 때만 입력 가능. `OTHER` 선택 시 `custom_text`는 필수입니다
- `attachments`: 첨부 사항
- `attachment_type`: `IMAGE`
- `url`: 업로드된 이미지 s3 링크

#### 비즈니스 로직
1. 신고자와 피신고자가 동일하면 실패합니다. (`CALLVAN_REPORT_SELF`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.util.StringUtils;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

Expand Down Expand Up @@ -52,7 +51,7 @@ public record CallvanPostDetailResponse(
List<CallvanParticipantResponse> participants
) {

public static CallvanPostDetailResponse from(CallvanPost post, Integer userId) {
public static CallvanPostDetailResponse from(CallvanPost post, Integer userId, Set<Integer> reportedUserIds) {
String departureName = post.getDepartureType().getName();
if (StringUtils.hasText(post.getDepartureCustomName())) {
departureName = post.getDepartureCustomName();
Expand All @@ -75,9 +74,8 @@ public static CallvanPostDetailResponse from(CallvanPost post, Integer userId) {
post.getStatus().name(),
post.getParticipants().stream()
.filter(p -> !p.getIsDeleted())
.map(it -> CallvanParticipantResponse.from(it, userId))
.toList()
);
.map(it -> CallvanParticipantResponse.from(it, userId, reportedUserIds))
.toList());
}

@JsonNaming(SnakeCaseStrategy.class)
Expand All @@ -89,16 +87,22 @@ public record CallvanParticipantResponse(
String nickname,

@Schema(description = "본인 여부", example = "false")
Boolean is_me
Boolean isMe,

@Schema(description = "신고 접수 여부", example = "false")
Boolean isReported
) {

public static CallvanParticipantResponse from(CallvanParticipant participant, Integer userId) {
String nickname = participant.getMember().getDisplayNickname();
public static CallvanParticipantResponse from(
CallvanParticipant participant, Integer userId, Set<Integer> reportedUserIds
) {
Integer participantId = participant.getMember().getId();
String nickname = participant.getMember().getDisplayNickname();
return new CallvanParticipantResponse(
participant.getMember().getId(),
participantId,
nickname,
Objects.equals(userId, participantId)
Objects.equals(userId, participantId),
reportedUserIds.contains(participantId)
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ public class CallvanPost extends BaseEntity {
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
private List<CallvanParticipant> participants = new ArrayList<>();

@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
private List<CallvanReport> reports = new ArrayList<>();

@Builder
private CallvanPost(
User author,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package in.koreatech.koin.domain.callvan.repository;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.repository.Repository;

import in.koreatech.koin.domain.callvan.model.CallvanPost;
Expand All @@ -21,6 +19,11 @@ default CallvanPost getById(Integer postId) {
return findById(postId).orElseThrow(() -> CustomException.of(ApiResponseCode.NOT_FOUND_ARTICLE));
}

List<CallvanPost> findAllByDepartureDateAndDepartureTimeAndIsDeletedFalse(LocalDate departureDate,
LocalTime departureTime);
@EntityGraph(attributePaths = { "participants", "participants.member" })
Optional<CallvanPost> findWithParticipantsById(Integer id);

default CallvanPost getWithParticipantsById(Integer postId) {
return findWithParticipantsById(postId)
.orElseThrow(() -> CustomException.of(ApiResponseCode.NOT_FOUND_ARTICLE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;

import in.koreatech.koin.domain.callvan.model.CallvanReport;
import in.koreatech.koin.domain.callvan.model.enums.CallvanReportStatus;
Expand Down Expand Up @@ -40,4 +43,23 @@ List<CallvanReport> findAllByReportedIdInAndStatusInAndIsDeletedFalseOrderByCrea
List<Integer> reportedIds,
List<CallvanReportStatus> statuses
);

@Query("SELECT DISTINCT r.reported.id FROM CallvanReport r " +
"WHERE r.post.id = :postId " +
"AND r.status IN :statuses " +
"AND r.isDeleted = false")
Set<Integer> findReportedUserIdsByPostIdAndStatusIn(
@Param("postId") Integer postId,
@Param("statuses") List<CallvanReportStatus> statuses
);

@Query("SELECT DISTINCT r.reported.id FROM CallvanReport r " +
"WHERE r.post.id = :postId " +
"AND r.reporter.id = :reporterId " +
"AND r.status IN :statuses " +
"AND r.isDeleted = false")
Set<Integer> findReportedUserIdsByPostIdAndReporterIdAndStatusIn(
@Param("postId") Integer postId,
@Param("reporterId") Integer reporterId,
@Param("statuses") List<CallvanReportStatus> statuses);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package in.koreatech.koin.domain.callvan.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -11,29 +12,37 @@
import in.koreatech.koin.domain.callvan.model.CallvanParticipant;
import in.koreatech.koin.domain.callvan.model.CallvanPost;
import in.koreatech.koin.domain.callvan.model.enums.CallvanLocation;
import in.koreatech.koin.domain.callvan.model.enums.CallvanReportStatus;
import in.koreatech.koin.domain.callvan.model.enums.CallvanRole;
import in.koreatech.koin.domain.callvan.model.enums.CallvanStatus;
import in.koreatech.koin.domain.callvan.model.filter.CallvanAuthorFilter;
import in.koreatech.koin.domain.callvan.model.filter.CallvanPostSortCriteria;
import in.koreatech.koin.domain.callvan.model.filter.CallvanPostStatusFilter;
import in.koreatech.koin.domain.callvan.repository.CallvanParticipantRepository;
import in.koreatech.koin.domain.callvan.repository.CallvanPostQueryRepository;
import in.koreatech.koin.domain.callvan.repository.CallvanPostRepository;
import in.koreatech.koin.domain.callvan.repository.CallvanReportRepository;
import in.koreatech.koin.global.code.ApiResponseCode;
import in.koreatech.koin.global.exception.CustomException;
import lombok.RequiredArgsConstructor;

import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CallvanPostQueryService {

private static final List<CallvanReportStatus> ACTIVE_REPORT_STATUSES = List.of(
CallvanReportStatus.PENDING,
CallvanReportStatus.UNDER_REVIEW,
CallvanReportStatus.CONFIRMED);

private final CallvanPostQueryRepository callvanPostQueryRepository;
private final CallvanParticipantRepository callvanParticipantRepository;
private final CallvanPostRepository callvanPostRepository;
private final CallvanReportRepository callvanReportRepository;

public CallvanPostSearchResponse getCallvanPosts(
CallvanAuthorFilter authorFilter,
Expand Down Expand Up @@ -89,8 +98,21 @@ public CallvanPostDetailResponse getCallvanPostDetail(Integer postId, Integer us
if (!callvanParticipantRepository.existsByPostIdAndMemberIdAndIsDeletedFalse(postId, userId)) {
throw CustomException.of(ApiResponseCode.FORBIDDEN_PARTICIPANT);
}
CallvanPost callvanPost = callvanPostRepository.getById(postId);
CallvanPost callvanPost = callvanPostRepository.getWithParticipantsById(postId);

boolean isAuthor = callvanPost.getParticipants().stream()
.anyMatch(p -> p.getMember().getId().equals(userId)
&& p.getRole() == CallvanRole.AUTHOR);

Set<Integer> reportedUserIds;
if (isAuthor) {
reportedUserIds = callvanReportRepository.findReportedUserIdsByPostIdAndStatusIn(
postId, ACTIVE_REPORT_STATUSES);
} else {
reportedUserIds = callvanReportRepository.findReportedUserIdsByPostIdAndReporterIdAndStatusIn(
postId, userId, ACTIVE_REPORT_STATUSES);
}

return CallvanPostDetailResponse.from(callvanPost, userId);
return CallvanPostDetailResponse.from(callvanPost, userId, reportedUserIds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public interface UploadApi {
- coop
- admin
- banner
- callvan_report
- callvan_chat
- lost_items
- club
""")
@PostMapping("/{domain}/upload/url")
ResponseEntity<UploadUrlResponse> getPresignedUrl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public enum ImageUploadDomain {
LOST_ITEMS,
BANNER,
CLUB,
CALLVAN_REPORT
CALLVAN_REPORT,
CALLVAN_CHAT
;

public static ImageUploadDomain from(String domain) {
Expand Down
Loading