-
Notifications
You must be signed in to change notification settings - Fork 8
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
[BE] feat: 리뷰어/리뷰이 매칭 기능 구현 (#38) #70
Changes from 6 commits
7695e13
1ba0076
1e1d95e
d5d2578
8578595
09b9c58
9c6da58
86912c1
a9ec0b5
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,8 @@ | ||
package corea.matching.domain; | ||
|
||
import java.util.List; | ||
|
||
public interface MatchingStrategy { | ||
|
||
List<Pair> matchPairs(List<Long> memberIds, int matchingSize); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package corea.matching.domain; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@RequiredArgsConstructor | ||
@Getter | ||
public class Pair { | ||
|
||
private final Long fromMemberId; | ||
private final Long toMemberId; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package corea.matching.domain; | ||
|
||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
@Component | ||
public class PlainRandomMatching implements MatchingStrategy { | ||
|
||
@Override | ||
public List<Pair> matchPairs(List<Long> memberIds, int matchingSize) { | ||
|
||
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. 붙여주세용~ 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. ✅ |
||
List<Long> shuffledMemberIds = new ArrayList<>(memberIds); | ||
Collections.shuffle(shuffledMemberIds); | ||
|
||
return match(shuffledMemberIds, matchingSize); | ||
} | ||
|
||
private List<Pair> match(List<Long> shuffledMemberIds, int matchingSize) { | ||
List<Pair> reviewerResult = new ArrayList<>(); | ||
for (int i = 0; i < shuffledMemberIds.size(); i++) { | ||
for (int j = 1; j <= matchingSize; j++) { | ||
reviewerResult.add(new Pair(shuffledMemberIds.get(i), shuffledMemberIds.get((i + j) % shuffledMemberIds.size()))); | ||
} | ||
} | ||
return reviewerResult; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package corea.matching.repository; | ||
|
||
import corea.matching.domain.MatchResult; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface MatchResultRepository extends JpaRepository<MatchResult, Long> { | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,55 @@ | ||
package corea.member.service; | ||
|
||
import corea.exception.CoreaException; | ||
import corea.exception.ExceptionType; | ||
import corea.matching.domain.MatchResult; | ||
import corea.matching.domain.Pair; | ||
import corea.matching.domain.Participation; | ||
import corea.matching.service.Matching; | ||
import corea.member.entity.MatchedGroup; | ||
import corea.matching.repository.MatchedGroupRepository; | ||
import lombok.AllArgsConstructor; | ||
import corea.matching.domain.PlainRandomMatching; | ||
import corea.matching.repository.MatchResultRepository; | ||
import corea.member.repository.MemberRepository; | ||
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.Map; | ||
|
||
@Service | ||
@AllArgsConstructor | ||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
public class MatchingService { | ||
|
||
private final MatchedGroupRepository matchedGroupRepository; | ||
private final Matching matching; | ||
private final PlainRandomMatching plainRandomMatching; | ||
private final MatchResultRepository matchResultRepository; | ||
private final MemberRepository memberRepository; | ||
|
||
public void matchMaking(final List<Participation> participations, final int matchingSize) { | ||
final ArrayList<Long> memberIds = new ArrayList<>(participations.stream() | ||
public void matchMaking(List<Participation> participations, int matchingSize) { | ||
validateParticipationSize(participations, matchingSize); | ||
List<Long> memberIds = participations.stream() | ||
.map(Participation::getMemberId) | ||
.toList() | ||
); | ||
|
||
final Map<Long, List<Long>> results = matching.matchGroup(memberIds, matchingSize); | ||
results.entrySet() | ||
.stream() | ||
.flatMap(entry -> entry.getValue() | ||
.stream() | ||
.map(memberId -> new MatchedGroup(entry.getKey(), memberId))) | ||
.forEach(matchedGroupRepository::save); | ||
.toList(); | ||
|
||
long roomId = participations.get(0).getRoomId(); | ||
|
||
List<Pair> results = plainRandomMatching.matchPairs(memberIds, matchingSize); | ||
|
||
results.stream() | ||
.map(pair -> new MatchResult( | ||
roomId, | ||
memberRepository.findById(pair.getFromMemberId()).orElseThrow( | ||
() -> new CoreaException(ExceptionType.MEMBER_NOT_FOUND, String.format("%d에 해당하는 멤버가 없습니다.", pair.getFromMemberId())) | ||
), | ||
memberRepository.findById(pair.getToMemberId()).orElseThrow( | ||
() -> new CoreaException(ExceptionType.MEMBER_NOT_FOUND, String.format("%d에 해당하는 멤버가 없습니다.", pair.getToMemberId())) | ||
), | ||
null)) | ||
//TODO: prLink 차후 수정 | ||
.forEach(matchResultRepository::save); | ||
} | ||
|
||
private void validateParticipationSize(List<Participation> participations, int matchingSize) { | ||
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. 일단은 예외 던지고, 나중에는 바꿔나가면 괜찮을듯 🙂 |
||
if (participations.size() <= matchingSize) { | ||
throw new CoreaException(ExceptionType.PARTICIPANT_SIZE_LACK); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package corea.fixture; | ||
|
||
import corea.matching.domain.Participation; | ||
|
||
import java.util.List; | ||
|
||
public class ParticipationFixture { | ||
|
||
public static List<Participation> PARTICIPATIONS_EIGHT() { | ||
return List.of( | ||
new Participation(1L, 1L), | ||
new Participation(1L, 2L), | ||
new Participation(1L, 3L), | ||
new Participation(1L, 4L), | ||
new Participation(1L, 5L), | ||
new Participation(1L, 6L), | ||
new Participation(1L, 7L), | ||
new Participation(1L, 8L) | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package corea.matching.domain; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.List; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
class PlainRandomMatchingTest { | ||
|
||
private final PlainRandomMatching plainRandomMatching = new PlainRandomMatching(); | ||
|
||
@Test | ||
@DisplayName("아이디의 리스트가 들어오면 매칭 사이즈 만큼 매칭된 결과를 반환한다.") | ||
void matchPairs_1() { | ||
List<Long> memberIds = List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L); | ||
int matchingSize = 3; | ||
|
||
List<Pair> result = plainRandomMatching.matchPairs(memberIds, matchingSize); | ||
|
||
assertThat(result).hasSize(matchingSize * memberIds.size()); | ||
} | ||
|
||
@Test | ||
@DisplayName("매칭을 수행할 때에, 본인을 제외한 멤버 중에서 매칭이 된다.") | ||
void matchPairs_2() { | ||
List<Long> memberIds = List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L); | ||
int matchingSize = 3; | ||
|
||
List<Pair> result = plainRandomMatching.matchPairs(memberIds, matchingSize); | ||
|
||
for (Pair pair : result) { | ||
assertThat(pair.getFromMemberId()).isNotEqualTo(pair.getToMemberId()); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package corea.member.service; | ||
|
||
import config.ServiceTest; | ||
import corea.exception.CoreaException; | ||
import corea.fixture.MemberFixture; | ||
import corea.fixture.ParticipationFixture; | ||
import corea.matching.domain.Participation; | ||
import corea.member.repository.MemberRepository; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import static org.assertj.core.api.Assertions.assertThatCode; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
@ServiceTest | ||
class MatchingServiceTest { | ||
|
||
@Autowired | ||
private MatchingService matchingService; | ||
|
||
@Autowired | ||
private MemberRepository memberRepository; | ||
|
||
@Test | ||
@DisplayName("인원 수가 매칭 사이즈보다 큰 경우 매칭을 수행한다.") | ||
void matchMaking() { | ||
List<Participation> participations = new ArrayList<>(); | ||
int matchingSize = 3; | ||
|
||
for (int i = 0; i < 4; i++) { | ||
participations.add(new Participation(1L, memberRepository.save(MemberFixture.MEMBER_DOMAIN()).getId())); | ||
participations.add(new Participation(1L, memberRepository.save(MemberFixture.MEMBER_MANAGER()).getId())); | ||
} | ||
|
||
assertThatCode(() -> matchingService.matchMaking(participations, matchingSize)) | ||
.doesNotThrowAnyException(); | ||
} | ||
|
||
@Test | ||
@DisplayName("매칭을 수행할 때에, 인원 수가 매칭 사이즈보다 작거나 같으면 예외를 발생한다.") | ||
void matchMakingLackOfParticipationException() { | ||
List<Participation> participations = ParticipationFixture.PARTICIPATIONS_EIGHT(); | ||
int matchingSize = 9; | ||
|
||
assertThatThrownBy(() -> matchingService.matchMaking(participations, matchingSize)) | ||
.isInstanceOf(CoreaException.class) | ||
.hasMessage("참여 인원 수가 부족합니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
DELETE FROM MATCH_RESULT; | ||
DELETE FROM MEMBER; | ||
DELETE FROM ROOM; |
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.
혹여나 가능하다면 ENUM으로 바꿔주셔도 좋을 것 같아요~ 😀
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.
✅