-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #39 from Dallili/dev/feat/unknown-matching
[feat] Matching 기본 작업 및 unknown 매칭 알고리즘 기능 구현
- Loading branch information
Showing
7 changed files
with
264 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/main/java/org/dallili/secretfriends/dto/MatchingDTO.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package org.dallili.secretfriends.dto; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import jakarta.persistence.Column; | ||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
@Data | ||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
public class MatchingDTO { | ||
|
||
private Long matchingID; | ||
|
||
@NotBlank | ||
private Long memberID; | ||
|
||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") | ||
private LocalDateTime createdAt; | ||
|
||
private Long firstInterest; | ||
|
||
private Long secondInterest; | ||
|
||
private Long thirdInterest; | ||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/org/dallili/secretfriends/service/MatchingService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package org.dallili.secretfriends.service; | ||
|
||
import org.dallili.secretfriends.dto.MatchingDTO; | ||
|
||
import java.util.Map; | ||
|
||
public interface MatchingService { | ||
|
||
Long addMatching(MatchingDTO matchingDTO); | ||
|
||
Long removeMatching(Long matchingID); | ||
|
||
Map<String, Long> saveMatchingSearch(MatchingDTO matchingDTO); | ||
// 매칭이 성공할 경우 Matching 테이블에서 old user의 레코드를 삭제 후, new user와 함께 diary 테이블에 등록 | ||
// 매칭이 실패할 경우 new user를 Matching 테이블에 등록 | ||
|
||
|
||
} |
159 changes: 159 additions & 0 deletions
159
src/main/java/org/dallili/secretfriends/service/MatchingServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package org.dallili.secretfriends.service; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.log4j.Log4j2; | ||
import org.dallili.secretfriends.domain.Diary; | ||
import org.dallili.secretfriends.domain.Matching; | ||
import org.dallili.secretfriends.dto.DiaryDTO; | ||
import org.dallili.secretfriends.dto.MatchingDTO; | ||
import org.dallili.secretfriends.repository.MatchingRepository; | ||
import org.modelmapper.ModelMapper; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.time.Duration; | ||
import java.time.LocalDateTime; | ||
import java.util.*; | ||
|
||
|
||
@Service | ||
@Log4j2 | ||
@RequiredArgsConstructor | ||
public class MatchingServiceImpl implements MatchingService{ | ||
|
||
private final float waitingPerHourPoint = 0.5f; | ||
private final float firstInterestPoint = 1f; | ||
private final float secondInterestPoint = 0.7f; | ||
private final float thirdInterestPoint = 0.4f; | ||
private final float scoreThreshold = 1; | ||
|
||
private final MatchingRepository matchingRepository; | ||
|
||
private final DiaryService diaryService; | ||
|
||
private final MatchingHistoryService matchingHistoryService; | ||
|
||
private final ModelMapper modelMapper; | ||
|
||
@Override | ||
public Long addMatching(MatchingDTO matchingDTO){ | ||
|
||
Matching matching = modelMapper.map(matchingDTO, Matching.class); | ||
|
||
Long matchingID = matchingRepository.save(matching).getMatchingID(); | ||
|
||
matchingRepository.flush(); | ||
|
||
return matchingID; | ||
|
||
} | ||
|
||
@Override | ||
public Long removeMatching(Long matchingID){ | ||
|
||
Long memberID = matchingRepository.findById(matchingID).get().getMemberID(); | ||
|
||
matchingRepository.deleteById(matchingID); | ||
|
||
return memberID; | ||
|
||
} | ||
|
||
@Override | ||
public Map<String, Long> saveMatchingSearch(MatchingDTO newMatching) { | ||
|
||
Map<String, Long> result = new HashMap<>(); | ||
|
||
List<Matching> matchingQueue = matchingRepository.findAll(); | ||
|
||
float maxScore = Float.MIN_VALUE; | ||
int maxIndex = -1; | ||
|
||
// Matching queue에 등록된 지 요청들 중 궁합이 가장 좋은 요청과 매칭 | ||
List<Float> scoreList = new ArrayList<>(); | ||
|
||
if (!matchingQueue.isEmpty()) { | ||
|
||
for (int i = 0; i < matchingQueue.size(); i++) { | ||
|
||
Matching oldMatching = matchingQueue.get(i); | ||
|
||
// 1. 웨이팅 타임 점수 | ||
LocalDateTime createdAt1 = oldMatching.getCreatedAt(); | ||
LocalDateTime createdAt2 = newMatching.getCreatedAt(); | ||
Duration duration = Duration.between(createdAt1, createdAt2); | ||
long hours = duration.toHours(); | ||
float score = waitingPerHourPoint * hours; | ||
|
||
// 2. Interest 일치 점수 | ||
if (newMatching.getFirstInterest().equals(oldMatching.getFirstInterest())){ | ||
score += firstInterestPoint * firstInterestPoint; | ||
} | ||
if ((newMatching.getFirstInterest().equals(oldMatching.getSecondInterest())) || (newMatching.getSecondInterest().equals(oldMatching.getFirstInterest()))) { | ||
score += firstInterestPoint * secondInterestPoint; | ||
} | ||
if ((newMatching.getFirstInterest().equals(oldMatching.getSecondInterest())) || (newMatching.getThirdInterest().equals(oldMatching.getFirstInterest()))) { | ||
score += firstInterestPoint * thirdInterestPoint; | ||
} | ||
if (newMatching.getSecondInterest().equals(oldMatching.getSecondInterest())) { | ||
score += secondInterestPoint * secondInterestPoint; | ||
} | ||
if ((newMatching.getThirdInterest().equals(oldMatching.getSecondInterest())) || (newMatching.getSecondInterest().equals(oldMatching.getThirdInterest()))) { | ||
score += secondInterestPoint * thirdInterestPoint; | ||
} | ||
if (newMatching.getThirdInterest().equals(oldMatching.getThirdInterest())) { | ||
score += thirdInterestPoint * thirdInterestPoint; | ||
} | ||
|
||
log.info(oldMatching.getMatchingID()+"번 매칭과의 점수: "+score); | ||
|
||
scoreList.add(score); | ||
} | ||
|
||
// 궁합 최대값 | ||
maxScore = Collections.max(scoreList); | ||
log.info("최종 최댓값: " + maxScore); | ||
|
||
// 매칭 성공 (일정 점수 이상 이어야 한다는 조건 통과) | ||
if(maxScore > scoreThreshold){ | ||
maxIndex = scoreList.indexOf(maxScore); | ||
Long maxMatchingID = matchingQueue.get(maxIndex).getMatchingID(); | ||
|
||
Long oldMemberID = removeMatching(maxMatchingID); // 매칭 큐에서 삭제 | ||
Long newMemberID = newMatching.getMemberID(); | ||
|
||
DiaryDTO diaryDTO = DiaryDTO.builder() | ||
.memberID(oldMemberID) | ||
.partnerID(newMemberID) | ||
.updatedAt(LocalDateTime.now()) | ||
.updatedBy(newMemberID) | ||
.color("#777777") | ||
.build(); | ||
|
||
Long diaryID = diaryService.addDiary(diaryDTO); // 다이어리 생성 | ||
Long historyID = matchingHistoryService.addHistory(oldMemberID, newMemberID); // 매칭히스토리 생성 | ||
|
||
result.put("user", oldMemberID); | ||
result.put("partner", newMemberID); | ||
result.put("diaryID", diaryID); | ||
return result; | ||
|
||
} else { // 매칭 실패 (점수 조건 통과 못함. 매칭큐에 추가해서 대기) | ||
|
||
Long newMatchingID = addMatching(newMatching); | ||
result.put("matchingID", newMatchingID); | ||
|
||
return result; | ||
} | ||
|
||
} else { // 매칭 실패 (매칭 큐가 비어있음. 매칭큐에 추가해서 대기) | ||
|
||
Long newMatchingID = addMatching(newMatching); | ||
result.put("matchingID", newMatchingID); | ||
|
||
return result; | ||
|
||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
src/test/java/org/dallili/secretfriends/service/MatchingServiceTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,64 @@ | ||
package org.dallili.secretfriends.service; | ||
|
||
import lombok.extern.log4j.Log4j2; | ||
import org.dallili.secretfriends.domain.Matching; | ||
import org.dallili.secretfriends.dto.MatchingDTO; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
@SpringBootTest | ||
@Log4j2 | ||
public class MatchingServiceTests { | ||
|
||
@Autowired | ||
MatchingHistoryService matchingHistoryService; | ||
|
||
@Autowired | ||
MatchingService matchingService; | ||
|
||
@Test | ||
public void testInsertMatchingHistory() { | ||
|
||
log.info(matchingHistoryService.addHistory(1L, 2L)); | ||
|
||
} | ||
|
||
@Test | ||
public void testAddMatching(){ | ||
|
||
MatchingDTO matchingDTO = MatchingDTO.builder() | ||
.createdAt(LocalDateTime.now()) | ||
.memberID(15L) | ||
.firstInterest(3L) | ||
.secondInterest(5L) | ||
.thirdInterest(6L) | ||
.build(); | ||
|
||
log.info(matchingService.addMatching(matchingDTO)); | ||
} | ||
|
||
@Test | ||
public void testRemoveMatching(){ | ||
|
||
matchingService.removeMatching(11L); | ||
} | ||
|
||
@Test | ||
public void testSaveMatchingSearch() { | ||
|
||
MatchingDTO matchingDTO = MatchingDTO.builder() | ||
.createdAt(LocalDateTime.now()) | ||
.firstInterest(6L) | ||
.secondInterest(7L) | ||
.thirdInterest(8L) | ||
.memberID(1L) | ||
.build(); | ||
|
||
matchingService.saveMatchingSearch(matchingDTO); | ||
|
||
|
||
} | ||
} |