Skip to content

Commit

Permalink
Merge pull request #39 from Dallili/dev/feat/unknown-matching
Browse files Browse the repository at this point in the history
[feat] Matching 기본 작업 및 unknown 매칭 알고리즘 기능 구현
  • Loading branch information
crHwang0822 authored Apr 9, 2024
2 parents 6be7a58 + 7dbd994 commit 1e61d46
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class Matching {
private Long matchingID;

@JoinColumn(name = "memberID", referencedColumnName = "memberID", insertable = true, updatable = true)
private String memberID;
private Long memberID;

@Column(name = "createdAt", columnDefinition = "TIMESTAMP")
private LocalDateTime createdAt;
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/org/dallili/secretfriends/dto/MatchingDTO.java
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;
}
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 테이블에 등록


}
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;

}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ public void testInsertMatching() {

Random random = new Random();

IntStream.rangeClosed(1,50).forEach(i-> {
IntStream.rangeClosed(1,10).forEach(i-> {

LocalDateTime customTime = LocalDateTime.of(2024, 4, 8, i, 30, 0);

Matching matching = Matching.builder()
.memberID("member"+i)
.createdAt(LocalDateTime.now())
.firstInterest(Long.valueOf(random.nextInt(10)))
.secondInterest(Long.valueOf(random.nextInt(10)))
.thirdInterest(Long.valueOf(random.nextInt(10)))
.memberID(Long.valueOf(i))
.createdAt(customTime)
.firstInterest(Long.valueOf(random.nextInt(9)+1))
.secondInterest(Long.valueOf(random.nextInt(9)+1))
.thirdInterest(Long.valueOf(random.nextInt(9)+1))
.build();
matchingRepository.save(matching);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void testUpdate() {

log.info("업데이트 할 것 : "+ diaryDTO);

diaryService.modifyUpdate(diaryDTO);
diaryService.modifyUpdate(diaryDTO.getDiaryID(), diaryDTO.getMemberID());
}

@Test
Expand Down
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);


}
}

0 comments on commit 1e61d46

Please sign in to comment.