diff --git a/backend/build.gradle b/backend/build.gradle index 9be2ddfc5..ea71c03f7 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -29,6 +29,7 @@ dependencies { // Etc implementation 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' // Database runtimeOnly 'com.h2database:h2' diff --git a/backend/src/main/java/corea/member/domain/Matching.java b/backend/src/main/java/corea/member/domain/Matching.java new file mode 100644 index 000000000..de23b7084 --- /dev/null +++ b/backend/src/main/java/corea/member/domain/Matching.java @@ -0,0 +1,33 @@ +package corea.member.domain; + +import corea.domain.Member; +import lombok.NoArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.IntStream; + +import static lombok.AccessLevel.PROTECTED; + +@NoArgsConstructor(access = PROTECTED) +@Component +public class Matching { + + public Map> matchGroup(final List members, final int matchingSize) { + final Map> matchedGroup = new HashMap<>(); + + final List memberIds = new ArrayList<>(members.stream().map(Member::getId).toList()); + Collections.shuffle(memberIds); + + final List> groupedMemberIds = IntStream.range(0, (members.size() + matchingSize - 1) / matchingSize) + .mapToObj(i -> memberIds.subList(i * matchingSize, Math.min(i * matchingSize + matchingSize, memberIds.size()))) + .toList(); + + long groupId = 1L; + for (List l : groupedMemberIds) { + matchedGroup.put(groupId++, l); + } + + return matchedGroup; + } +} diff --git a/backend/src/main/java/corea/member/entity/MatchedGroup.java b/backend/src/main/java/corea/member/entity/MatchedGroup.java new file mode 100644 index 000000000..bba05e210 --- /dev/null +++ b/backend/src/main/java/corea/member/entity/MatchedGroup.java @@ -0,0 +1,28 @@ +package corea.member.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PROTECTED; + +@Entity +@NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor +public class MatchedGroup { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + private Long groupId; + + private Long memberId; + + public MatchedGroup(final Long groupId, final Long memberId) { + this(null, groupId, memberId); + } +} diff --git a/backend/src/main/java/corea/member/repository/MatchedGroupRepository.java b/backend/src/main/java/corea/member/repository/MatchedGroupRepository.java new file mode 100644 index 000000000..6dad5156e --- /dev/null +++ b/backend/src/main/java/corea/member/repository/MatchedGroupRepository.java @@ -0,0 +1,13 @@ +package corea.member.repository; + +import corea.member.entity.MatchedGroup; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface MatchedGroupRepository extends JpaRepository { + + Long findGroupIdByMemberId(final Long memberId); + + List findMemberIdsByGroupId(final Long groupId); +} diff --git a/backend/src/main/java/corea/member/repository/MemberRepository.java b/backend/src/main/java/corea/member/repository/MemberRepository.java new file mode 100644 index 000000000..61c9db44e --- /dev/null +++ b/backend/src/main/java/corea/member/repository/MemberRepository.java @@ -0,0 +1,7 @@ +package corea.member.repository; + +import corea.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MemberRepository extends JpaRepository { +} diff --git a/backend/src/main/java/corea/member/service/MatchingService.java b/backend/src/main/java/corea/member/service/MatchingService.java new file mode 100644 index 000000000..6ab052005 --- /dev/null +++ b/backend/src/main/java/corea/member/service/MatchingService.java @@ -0,0 +1,27 @@ +package corea.member.service; + +import corea.member.domain.Matching; +import corea.member.entity.MatchedGroup; +import corea.domain.Member; +import corea.member.repository.MatchedGroupRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +@Service +@AllArgsConstructor +public class MatchingService { + + private final MatchedGroupRepository matchedGroupRepository; + private final Matching matching; + + public void matchMaking(final List members, final int matchingSize) { + final Map> results = matching.matchGroup(members, matchingSize); + results.entrySet().stream() + .flatMap(entry -> entry.getValue().stream() + .map(memberId -> new MatchedGroup(entry.getKey(), memberId))) + .forEach(matchedGroupRepository::save); + } +} diff --git a/backend/src/test/java/corea/member/domain/MatchingTest.java b/backend/src/test/java/corea/member/domain/MatchingTest.java new file mode 100644 index 000000000..291add7b8 --- /dev/null +++ b/backend/src/test/java/corea/member/domain/MatchingTest.java @@ -0,0 +1,33 @@ +package corea.member.domain; + +import corea.domain.Member; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class MatchingTest { + + private final Matching matching = new Matching(); + + @Test + @DisplayName("멤버 리스트를 받아서 매칭 결과를 반환한다.") + void matchGroup(){ + List members = List.of( + new Member(1L, "test1@email.com"), + new Member(2L, "test2@email.com"), + new Member(3L, "test3@email.com"), + new Member(4L, "test4@email.com") + ); + int matchingSize = 2; + + Map> results = matching.matchGroup(members, matchingSize); + + assertThat(results).hasSize(2); + assertThat(results.get(1L)).hasSize(2); + assertThat(results.get(2L)).hasSize(2); + } +} diff --git a/backend/src/test/java/corea/member/service/MatchingServiceTest.java b/backend/src/test/java/corea/member/service/MatchingServiceTest.java new file mode 100644 index 000000000..d429f7a5e --- /dev/null +++ b/backend/src/test/java/corea/member/service/MatchingServiceTest.java @@ -0,0 +1,42 @@ +package corea.member.service; + +import corea.domain.Member; +import corea.member.repository.MatchedGroupRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +class MatchingServiceTest { + + private final MatchingService matchingService; + private final MatchedGroupRepository matchedGroupRepository; + + @Autowired + public MatchingServiceTest(MatchingService matchingService, MatchedGroupRepository matchedGroupRepository) { + this.matchingService = matchingService; + this.matchedGroupRepository = matchedGroupRepository; + } + + @Test + @DisplayName("멤버 리스트를 받아 매칭 결과를 반환한다.") + void matchMaking() { + List members = List.of( + new Member(1L, "test1@email.com"), + new Member(2L, "test2@email.com"), + new Member(3L, "test3@email.com"), + new Member(4L, "test4@email.com") + ); + int matchingSize = 2; + + matchingService.matchMaking(members, matchingSize); + + assertThat(matchedGroupRepository.findAll()).hasSize(4); + } + +}