Skip to content

Commit

Permalink
Merge pull request #312 from Gamegoo-repo/feat/282
Browse files Browse the repository at this point in the history
[Feat/282] ๋งค์ผ ์•„์นจ 7์‹œ riot ์ •๋ณด ์—…๋ฐ์ดํŠธ
  • Loading branch information
rimi3226 authored Oct 30, 2024
2 parents 9bf36fb + b5b7340 commit 0199bcd
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.gamegoo.apiPayload.code.BaseErrorCode;
import com.gamegoo.apiPayload.exception.GeneralException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


public class MemberHandler extends GeneralException {
public MemberHandler(BaseErrorCode code) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/gamegoo/domain/champion/MemberChampion.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ public class MemberChampion extends BaseDateTimeEntity {
@Column(name = "member_champion_id", nullable = false)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "champion_id", nullable = false)
private Champion champion;

@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "member_id", nullable = false)
private Member member;

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/gamegoo/domain/member/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class Member extends BaseDateTimeEntity {
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<Board> boardList = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<MemberChampion> memberChampionList = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

public interface MemberRepository extends JpaRepository<Member, Long> {

List<Member> findAll();
Optional<Member> findByEmail(String email);

Optional<Member> findById(Long id);
Expand Down
171 changes: 170 additions & 1 deletion src/main/java/com/gamegoo/scheduler/SchedulerService.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,69 @@
package com.gamegoo.scheduler;

import com.gamegoo.apiPayload.code.status.ErrorStatus;
import com.gamegoo.apiPayload.exception.handler.MemberHandler;
import com.gamegoo.domain.champion.Champion;
import com.gamegoo.domain.champion.MemberChampion;
import com.gamegoo.domain.chat.Chat;
import com.gamegoo.domain.matching.MatchingRecord;
import com.gamegoo.domain.matching.MatchingStatus;
import com.gamegoo.domain.member.Tier;
import com.gamegoo.dto.member.RiotResponse;
import com.gamegoo.repository.matching.MatchingRecordRepository;
import com.gamegoo.repository.member.ChampionRepository;
import com.gamegoo.repository.member.MemberChampionRepository;
import com.gamegoo.repository.member.MemberRepository;
import com.gamegoo.service.chat.ChatCommandService;
import com.gamegoo.service.chat.ChatQueryService;
import com.gamegoo.service.member.AuthService;
import com.gamegoo.service.socket.SocketService;
import java.time.LocalDateTime;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

@Slf4j
@Service
@RequiredArgsConstructor
public class SchedulerService {

private final MatchingRecordRepository matchingRecordRepository;
private final MemberRepository memberRepository;
private final ChatCommandService chatCommandService;
private final ChatQueryService chatQueryService;
private final SocketService socketService;
private final ChampionRepository championRepository;
private final MemberChampionRepository memberChampionRepository;

private static final Long MANNER_MESSAGE_TIME = 60L; // ๋งค์นญ ์„ฑ๊ณต n์ดˆ ์ดํ›„์˜ ์—”ํ‹ฐํ‹ฐ ์กฐํšŒํ•จ
private static final String MANNER_SYSTEM_MESSAGE = "๋งค์นญ์€ ์–ด๋– ์…จ๋‚˜์š”? ์ƒ๋Œ€๋ฐฉ์˜ ๋งค๋„ˆ๋ฅผ ํ‰๊ฐ€ํ•ด์ฃผ์„ธ์š”!";

// RIOT
private final RestTemplate restTemplate;
@Value("${spring.riot.api.key}")
private String riotAPIKey;

private static final String RIOT_ACCOUNT_API_URL_TEMPLATE = "https://asia.api.riotgames.com/riot/account/v1/accounts/by-riot-id/%s/%s?api_key=%s";
private static final String RIOT_SUMMONER_API_URL_TEMPLATE = "https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/%s?api_key=%s";
private static final String RIOT_LEAGUE_API_URL_TEMPLATE = "https://kr.api.riotgames.com/lol/league/v4/entries/by-summoner/%s?api_key=%s";
private static final String RIOT_MATCH_API_URL_TEMPLATE = "https://asia.api.riotgames.com/lol/match/v5/matches/by-puuid/%s/ids?start=0&count=%s&api_key=%s";
private static final String RIOT_MATCH_INFO_API_URL_TEMPLATE = "https://asia.api.riotgames.com/lol/match/v5/matches/%s?api_key=%s";
private static final Map<String, Integer> romanToIntMap = new HashMap<>();

static {
romanToIntMap.put("I", 1);
romanToIntMap.put("II", 2);
romanToIntMap.put("III", 3);
romanToIntMap.put("IV", 4);
}


/**
* ๋งค์นญ ์„ฑ๊ณต 1์‹œ๊ฐ„์ด ๊ฒฝ๊ณผํ•œ ๊ฒฝ์šฐ, ๋‘ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋งค๋„ˆํ‰๊ฐ€ ์‹œ์Šคํ…œ ๋ฉ”์‹œ์ง€ ์ „์†ก
Expand Down Expand Up @@ -63,4 +100,136 @@ public void mannerSystemMessageRun() {
});

}

/**
* 07:00 ๋ชจ๋“  ์‚ฌ์šฉ์ž ์ •๋ณด ์—…๋ฐ์ดํŠธ
*/
@Scheduled(cron = "0 0 7 * * *")
public void updateAllUserRiotInformation(){
log.info("Riot ์—…๋ฐ์ดํŠธ - ์‹œ์ž‘");

memberChampionRepository.deleteAllInBatch();

memberRepository.findAll().forEach(member -> {
String gameName = member.getGameName();
String tag = member.getTag();

if(gameName.equals("SYSTEM")){
return;
}
try {
// 1. puuid ์กฐํšŒ
String url = String.format(RIOT_ACCOUNT_API_URL_TEMPLATE, gameName, tag, riotAPIKey);
RiotResponse.RiotAccountDTO accountResponse = null;
accountResponse = restTemplate.getForObject(url, RiotResponse.RiotAccountDTO.class);
String puuid = accountResponse.getPuuid();

// 2. encryptedSummonerId ์กฐํšŒ
String summonerUrl = String.format(RIOT_SUMMONER_API_URL_TEMPLATE, puuid, riotAPIKey);
RiotResponse.RiotSummonerDTO summonerResponse = null;
summonerResponse = restTemplate.getForObject(summonerUrl, RiotResponse.RiotSummonerDTO.class);
String encryptedSummonerId = summonerResponse.getId();

// 3. tier, rank, winrate ์กฐํšŒ
// (1) account id๋กœ ํ‹ฐ์–ด, ๋žญํฌ, ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
String leagueUrl = String.format(RIOT_LEAGUE_API_URL_TEMPLATE, encryptedSummonerId, riotAPIKey);
RiotResponse.RiotLeagueEntryDTO[] leagueEntries = restTemplate.getForObject(leagueUrl, RiotResponse.RiotLeagueEntryDTO[].class);

// (2) tier, rank, gameCount ์ •๋ณด ์ €์žฅ
for (RiotResponse.RiotLeagueEntryDTO entry : leagueEntries) {
// ์†”๋žญ์ผ ๊ฒฝ์šฐ์—๋งŒ ์ €์žฅ
if ("RANKED_SOLO_5x5".equals(entry.getQueueType())) {
int wins = entry.getWins();
int losses = entry.getLosses();
int gameCount = wins + losses;
double winrate = (double) wins / (wins + losses);
winrate = Math.round(winrate * 1000) / 10.0;
Tier tier = Tier.valueOf(entry.getTier().toUpperCase());
Integer rank = romanToIntMap.get(entry.getRank());

// DB์— ์ €์žฅ
member.updateRiotDetails(tier, rank, winrate, gameCount);
break;
}
}

if (member.getTier() == null) {
member.updateRiotDetails(Tier.UNRANKED,0,0.0,0);
}

// 4. ์ตœ๊ทผ ํ”Œ๋ ˆ์ดํ•œ ์‚ฌ์šฉ์ž 3๊ฐœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
List<Integer> recentChampionIds = null;
int count = 20;

// 4-1. ์ตœ๊ทผ ํ”Œ๋ ˆ์ดํ•œ ์ฑ”ํ”ผ์–ธ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ
while ((recentChampionIds == null || recentChampionIds.size() < 3) && count <= 100) {

String matchUrl = String.format(RIOT_MATCH_API_URL_TEMPLATE, puuid, count, riotAPIKey);
String[] matchIds = restTemplate.getForObject(matchUrl, String[].class);
List<String> recentMatchIds = Arrays.asList(Objects.requireNonNull(matchIds));

recentChampionIds = recentMatchIds.stream()
.map(matchId -> getChampionIdFromMatch(matchId, gameName))
.filter(championId -> championId < 1000)
.toList();

if (recentChampionIds.size() < 3) {
count += 10; // count๋ฅผ 10 ์ฆ๊ฐ€์‹œ์ผœ์„œ ๋‹ค์‹œ ์‹œ๋„
}
}

// 4-2. ํ•ด๋‹น ์บ๋ฆญํ„ฐ ์ค‘ ๋งŽ์ด ์‚ฌ์šฉํ•œ ์บ๋ฆญํ„ฐ ์„ธ ๊ฐœ ์ €์žฅํ•˜๊ธฐ
// (1) ์ฑ”ํ”ผ์–ธ ์‚ฌ์šฉ ๋นˆ๋„ ๊ณ„์‚ฐ
Map<Integer, Long> championFrequency = recentChampionIds.stream()
.collect(Collectors.groupingBy(championId -> championId, Collectors.counting()));

// (2) ๋นˆ๋„๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜์—ฌ ์ƒ์œ„ 3๊ฐœ์˜ ์ฑ”ํ”ผ์–ธ ์ถ”์ถœ
List<Integer> top3Champions = championFrequency.entrySet().stream()
.sorted(Map.Entry.<Integer, Long>comparingByValue().reversed())
.limit(3)
.map(Map.Entry::getKey)
.toList();


// 5. DB์— ์ €์žฅ
if (top3Champions != null) {
top3Champions
.forEach(championId -> {
Champion champion = championRepository.findById(Long.valueOf(championId))
.orElseThrow(() -> new MemberHandler(ErrorStatus.CHAMPION_NOT_FOUND));

MemberChampion memberChampion = MemberChampion.builder()
.champion(champion)
.build();

memberChampion.setMember(member);
});
}

} catch (Exception e) {
log.warn("Riot ์—…๋ฐ์ดํŠธ - ๋ผ์ด์—‡ ์ •๋ณด ์—ฐ๋™์— ๋ฌธ์ œ ๋ฐœ์ƒ : ",e);
}

memberRepository.save(member);
});
log.info("Riot ์—…๋ฐ์ดํŠธ - ์™„๋ฃŒ");
}

public Integer getChampionIdFromMatch(String matchId, String gameName) {
// ๋งค์น˜ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
String matchInfoUrl = String.format(RIOT_MATCH_INFO_API_URL_TEMPLATE, matchId, riotAPIKey);
RiotResponse.MatchDTO matchResponse = restTemplate.getForObject(matchInfoUrl, RiotResponse.MatchDTO.class);


// ์ฐธ๊ฐ€์ž ์ •๋ณด์—์„œ gameName๊ณผ ์ผ์น˜ํ•˜๋Š” ์‚ฌ์šฉ์ž์˜ champion ID ์ฐพ๊ธฐ
return matchResponse.getInfo().getParticipants().stream()
.filter(participant -> gameName.equals(participant.getRiotIdGameName()))
.map(RiotResponse.ParticipantDTO::getChampionId)
.findFirst()
.orElseGet(() -> {
log.info("Riot ์—…๋ฐ์ดํŠธ - ์ตœ๊ทผ ์„ ํ˜ธ ์ฑ”ํ”ผ์–ธ ๊ฐ’ ์ค‘ id๊ฐ€ ์—†๋Š” ์ฑ”ํ”ผ์–ธ์ด ์žˆ์Šต๋‹ˆ๋‹ค.");
return null;
});

}
}
26 changes: 18 additions & 8 deletions src/main/java/com/gamegoo/service/member/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,30 @@ public Member joinMember(String email, String password, String gameName, String
.isAgree(isAgree)
.build();

getRiotInformation(gameName, tag, puuid, member);

// ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด ๋กœ๊ทธ๋กœ ์ถœ๋ ฅ
log.info("ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ - ์ด๋ฉ”์ผ: {}, ํ”„๋กœํ•„ ์ด๋ฏธ์ง€: {}, ์†Œํ™˜์‚ฌ๋ช…: {}, ํƒœ๊ทธ: {}, ํ‹ฐ์–ด: {}, ๋žญํฌ: {}",
member.getEmail(), member.getProfileImage(), member.getGameName(), member.getTag(),
member.getTier(), member.getRank());

return member;
}

/**
* Riot ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์†Œ๋“œ
* @param gameName
* @param tag
* @param puuid
* @param member
*/
public void getRiotInformation(String gameName, String tag, String puuid, Member member) {
// 2. tier, rank, winrate ์ €์žฅ
String encryptedSummonerId = riotUtil.getSummonerId(puuid);
riotUtil.addTierRankWinRate(member, gameName, encryptedSummonerId, tag);

memberRepository.save(member);


// ์ตœ๊ทผ ์„ ํ˜ธ ์ฑ”ํ”ผ์–ธ 3๊ฐœ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ
List<Integer> top3Champions = null;
try {
Expand All @@ -118,13 +135,6 @@ public Member joinMember(String email, String password, String gameName, String
});

}

// ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด ๋กœ๊ทธ๋กœ ์ถœ๋ ฅ
log.info("ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ - ์ด๋ฉ”์ผ: {}, ํ”„๋กœํ•„ ์ด๋ฏธ์ง€: {}, ์†Œํ™˜์‚ฌ๋ช…: {}, ํƒœ๊ทธ: {}, ํ‹ฐ์–ด: {}, ๋žญํฌ: {}",
member.getEmail(), member.getProfileImage(), member.getGameName(), member.getTag(),
member.getTier(), member.getRank());

return member;
}

/**
Expand Down
11 changes: 2 additions & 9 deletions src/main/java/com/gamegoo/util/RiotUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
import org.springframework.web.client.RestTemplate;

import javax.transaction.Transactional;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

@Component
Expand Down Expand Up @@ -203,11 +200,7 @@ private List<String> getRecentMatchIds(String puuid, int count) {
String matchUrl = String.format(RIOT_MATCH_API_URL_TEMPLATE, puuid, count, riotAPIKey);
String[] matchIds = restTemplate.getForObject(matchUrl, String[].class);

if (matchIds == null || matchIds.length == 0) {

}

return Arrays.asList(matchIds);
return Arrays.asList(Objects.requireNonNull(matchIds));
}


Expand Down

0 comments on commit 0199bcd

Please sign in to comment.