스케줄러 사용해서 구현
// 실행 후 1분마다 실행 // MEMO : 유저가 매칭 여러개 동시에 돌리는 경우엔 partyInvitation 정보에 파티 매칭 정보도 적어놔야하나
@Scheduled(fixedDelay = 60000)
public void runMatching() {
// 유저를 기준으로 맞는 파티 매칭
// MatchingStatus 가 MATCHING 인 유저들만 불러오기
// 유저에게 파티 초대 상태가 없는 파티인지 확인 필요
// 유저별로 제일 적합한 파티 하나 추천
// MEMO : 다 불러와도 되나?
// MEMO : 있는지 체크하고 그다음에 불러오는 방식 vs (지금) 다 불러오고 체크
List<UserMatchCond> matchingUserList =
userMatchCondRepository.findAllByMatchStatus(MatchStatus.MATCHING);
// 매칭 중인 유저가 없는 경우
if (matchingUserList.isEmpty()) {
return;
}
List<PartyMatchCond> matchingPartyList = partyMatchCondRepository.findAll();
// 매칭 중인 파티가 없는 경우
if (matchingPartyList.isEmpty()) {
return;
}
// 매칭 알고리즘
List<PartyInvitation> matchedList = new ArrayList<>();
for (UserMatchCond matchingUser : matchingUserList) {
int bestScore = 0;
List<Long> bestScorePartyCondIdList = new ArrayList<>(List.of(0L));
// 1. 필터링
Stream<PartyMatchCond> partyStream = matchingPartyList.stream();
// 초대 이력이 없는 파티들만 필터링
List<Long> invitedPartyIdList = partyInvitationRepository.findAllPartyIdByUser(matchingUser.getUser());
partyStream = partyStream.filter(p -> invitedPartyIdList.contains(p.getParty().getId()));
// 가게 필터링
if (matchingUser.getStores() != null && matchingUser.getStores().isEmpty()) {
partyStream = matchStore(matchingUser, partyStream);
}
// 위치 필터링
if (matchingUser.getRegion() != null) {
partyStream = matchRegion(matchingUser, partyStream);
}
// 성별 필터링
if (matchingUser.getUserGender() != null) {
partyStream = matchGender(matchingUser, partyStream);
}
// 나이 필터링
partyStream = matchAgeRange(matchingUser, partyStream);
// 2. 가중치 계산
// 카테고리 일치, 날짜 범위
List<PartyMatchCond> filteredParty = partyStream.toList();
for (PartyMatchCond matchingParty : filteredParty) {
int nowMatchingScore = 0;
nowMatchingScore += calculateCategoryScore(matchingUser, matchingParty);
nowMatchingScore += calculateMeetingDateScore(matchingUser, matchingParty);
// 동일 최고점 추가 (0이면 무시)
if (nowMatchingScore != 0 && nowMatchingScore == bestScore) {
bestScorePartyCondIdList.add(matchingParty.getId());
}
// 갱신
else if (nowMatchingScore > bestScore) {
bestScore = nowMatchingScore;
bestScorePartyCondIdList.clear();
bestScorePartyCondIdList.add(matchingParty.getId());
}
}
// 최종 최고 점수 파티와 매칭
// 1) 필터링-가중치 계산 이후에도 모든 파티가 0점인 경우, 2)최고 점수인 파티가 여러 개인 경우
PartyMatchCond selectedParty = null;
if (bestScore == 0 || bestScorePartyCondIdList.size() > 1) {
selectedParty = filteredParty.get(
ThreadLocalRandom.current().nextInt(0, filteredParty.size()));
}
if (bestScorePartyCondIdList.size() == 1) {
selectedParty = filteredParty.get(0);
}
if (selectedParty == null) {
log.warn("랜덤 매칭 중 매칭 가능한 파티 찾기에 실패하였습니다. User ID: {}, UserMathCond ID: {}",
matchingUser.getUser().getId(), matchingUser.getId());
continue;
}
matchingUser.setMatchStatus(MatchStatus.WAITING_HOST);
matchedList.add(new PartyInvitation(
selectedParty.getParty(), matchingUser.getUser(), InvitationType.RANDOM, InvitationStatus.WAITING));
}