diff --git a/src/main/java/com/meetkey/server/domain/chat/repository/ChatRoomMemberRepository.java b/src/main/java/com/meetkey/server/domain/chat/repository/ChatRoomMemberRepository.java index e5d7731..e2582e9 100644 --- a/src/main/java/com/meetkey/server/domain/chat/repository/ChatRoomMemberRepository.java +++ b/src/main/java/com/meetkey/server/domain/chat/repository/ChatRoomMemberRepository.java @@ -7,6 +7,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -52,4 +53,13 @@ where crm.chatRoom.id in ( @Query("SELECT cm.member FROM ChatRoomMember cm WHERE cm.chatRoom.id = :roomId AND cm.member.id != :myId") Optional findPartner(@Param("roomId") Long roomId, @Param("myId") Long myId); + @Query(""" + select crm2.member.id + from ChatRoomMember crm1 + join ChatRoomMember crm2 on crm1.chatRoom = crm2.chatRoom + where crm1.member = :member + and crm2.member != :member + """) + List findChattedMemberIds(Member member); + } diff --git a/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java b/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java index 4387c71..fa2b41f 100644 --- a/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java +++ b/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java @@ -1,5 +1,6 @@ package com.meetkey.server.domain.match.service; +import com.meetkey.server.domain.chat.repository.ChatRoomMemberRepository; import com.meetkey.server.domain.match.dto.*; import com.meetkey.server.domain.match.entity.RecommendationQueue; import com.meetkey.server.domain.match.enums.MatchType; @@ -41,6 +42,7 @@ public class MatchServiceImpl implements MatchService { private final MemberRepository memberRepository; private final PreferenceRepository preferenceRepository; private final RecommendationQueueRepository recommendationQueueRepository; + private final ChatRoomMemberRepository chatRoomMemberRepository; @Transactional @Override @@ -62,17 +64,35 @@ public MatchListResDTO getRecommendations(Long memberId, RecommendationReqDTO re } // 2. 분기 처리: 일일 할당량 남음 vs 소진 (Recycle) + boolean forceRecycle = false; if (remainingQuota > 0) { // A. Daily Match Mode (새로울 후보 생성) // 할당량(remainingQuota)만큼만 생성 List newRecommendations = generateRecommendations(member, request, remainingQuota); - return buildResponse(newRecommendations, newRecommendations.size(), MatchType.DAILY_MATCH); - } else { - // B. Recycle Mode (할당량 소진 시) + if (!newRecommendations.isEmpty()) { + return buildResponse(newRecommendations, newRecommendations.size(), newRecommendations.size(), MatchType.DAILY_MATCH); + } + + // 추천된 후보가 없을 때, 프리미엄 유저는 Recycle 모드로 전환 + if (member.getMembership() == Membership.PREMIUM) { + forceRecycle = true; + } else { + return buildResponse(Collections.emptyList(), 0, 0, MatchType.DAILY_MATCH); + } + } + + if (remainingQuota <= 0 || forceRecycle) { + // B. Recycle Mode (할당량 소진 시 또는 프리미엄 유저 후보 없음) // 재활용 대상: DISLIKE 했거나, LIKE 했지만 채팅 시작 안 한 유저 List recycleMembers = memberLikeRepository.findRecycleMembers(member); + // 채팅방이 존재하는 유저 제외 + List chattedMemberIds = chatRoomMemberRepository.findChattedMemberIds(member); + recycleMembers = recycleMembers.stream() + .filter(m -> !chattedMemberIds.contains(m.getId())) + .collect(Collectors.toList()); + // 랜덤으로 섞어서 반환 (또는 최신순 등) Collections.shuffle(recycleMembers); List limitedRecycle = recycleMembers.stream().limit(10).toList(); @@ -96,16 +116,25 @@ public MatchListResDTO getRecommendations(Long memberId, RecommendationReqDTO re }) .collect(Collectors.toList()); - return buildResponse(dtos, dtos.size(), MatchType.RECYCLE); + MatchType type = MatchType.RECYCLE; + int remaining = 0; + if (member.getMembership() == Membership.PREMIUM) { + type = MatchType.DAILY_MATCH; + remaining = 10; + } + + return buildResponse(dtos, remaining, dtos.size(), type); } + + return buildResponse(Collections.emptyList(), 0, 0, MatchType.DAILY_MATCH); } - private MatchListResDTO buildResponse(List list, int remaining, MatchType type) { + private MatchListResDTO buildResponse(List list, int remaining, int total, MatchType type) { return MatchListResDTO.builder() .recommendations(list) .swipeInfo(MatchListResDTO.SwipeInfoDTO.builder() .remainingCount(remaining) - .totalCount(10) + .totalCount(total) .build()) .matchType(type) .build(); @@ -114,6 +143,8 @@ private MatchListResDTO buildResponse(List list, int remai private List generateRecommendations(Member member, RecommendationReqDTO request, int limit) { // 1. 기 스와이프 유저 제외 List excludedIds = memberLikeRepository.findSwipedMemberIdsByMember(member); + // 1.1 이미 채팅한 유저 제외 + excludedIds.addAll(chatRoomMemberRepository.findChattedMemberIds(member)); // 1.5. 위치 정보 보정 (요청에 좌표 없고 DB에 있으면 DB 값 사용) MemberLocation myLocation = memberLocationRepository.findByMember(member).orElse(null);