From ad6588b7297999b9c2550fbbc2e042c235d6a99a Mon Sep 17 00:00:00 2001 From: Atralupus Date: Thu, 19 Jun 2025 20:57:21 +0900 Subject: [PATCH 1/9] Add round index colume --- ArenaService.Shared/Models/Battle.cs | 3 + ArenaService.Shared/Models/Round.cs | 5 ++ .../Repositories/ClanRankingRepository.cs | 70 +++++++++--------- .../Repositories/RankingRepository.cs | 74 +++++++++---------- .../Repositories/SeasonCacheRepository.cs | 12 +-- .../Services/ParticipateService.cs | 10 ++- .../Services/RankingService.cs | 18 ++--- .../Services/RoundPreparationService.cs | 6 +- .../Services/SeasonPreparationService.cs | 8 +- .../Controllers/ArenaInfoController.cs | 4 +- ArenaService/Processor/BattleProcessor.cs | 5 +- ArenaService/Worker/CacheBlockTipWorker.cs | 1 + ArenaService/Worker/PrepareRankingWorker.cs | 8 +- ArenaService/Worker/RankingCopyWorker.cs | 4 +- 14 files changed, 121 insertions(+), 107 deletions(-) diff --git a/ArenaService.Shared/Models/Battle.cs b/ArenaService.Shared/Models/Battle.cs index c963bac..b04ed6f 100644 --- a/ArenaService.Shared/Models/Battle.cs +++ b/ArenaService.Shared/Models/Battle.cs @@ -30,6 +30,9 @@ public class Battle [Required] public int RoundId { get; set; } + [ForeignKey(nameof(RoundId))] + public Round Round { get; set; } = null!; + [Required] public int AvailableOpponentId { get; set; } diff --git a/ArenaService.Shared/Models/Round.cs b/ArenaService.Shared/Models/Round.cs index 725b9a4..c09ef76 100644 --- a/ArenaService.Shared/Models/Round.cs +++ b/ArenaService.Shared/Models/Round.cs @@ -1,9 +1,11 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; namespace ArenaService.Shared.Models; [Table("rounds")] +[Index(nameof(SeasonId), nameof(RoundIndex), IsUnique = true)] public class Round { [Key] @@ -21,6 +23,9 @@ public class Round [Required] public long EndBlock { get; set; } + [Required] + public int RoundIndex { get; set; } + [Required] [Column(TypeName = "timestamptz")] public DateTime CreatedAt { get; set; } = DateTime.UtcNow; diff --git a/ArenaService.Shared/Repositories/ClanRankingRepository.cs b/ArenaService.Shared/Repositories/ClanRankingRepository.cs index b5e1e00..9e614f0 100644 --- a/ArenaService.Shared/Repositories/ClanRankingRepository.cs +++ b/ArenaService.Shared/Repositories/ClanRankingRepository.cs @@ -11,25 +11,25 @@ Task UpdateScoreAsync( int clanId, Address avatarAddress, int seasonId, - int roundId, + int roundIndex, int scoreChange ); - Task GetRankAsync(int clanId, Address avatarAddress, int seasonId, int roundId); + Task GetRankAsync(int clanId, Address avatarAddress, int seasonId, int roundIndex); - Task> GetClansAsync(int seasonId, int roundId); + Task> GetClansAsync(int seasonId, int roundIndex); Task> GetScoresAsync( int clanId, int seasonId, - int roundId + int roundIndex ); Task CopyRoundDataAsync( int clanId, int seasonId, - int sourceRoundId, - int targetRoundId, + int sourceRoundIndex, + int targetRoundIndex, int roundInterval ); @@ -37,14 +37,14 @@ Task InitRankingAsync( List<(Address AvatarAddress, int Score)> rankingData, int clanId, int seasonId, - int roundId, + int roundIndex, int roundInterval ); Task> GetTopClansAsync( int clanId, int seasonId, - int roundId, + int roundIndex, int topN = 10 ); } @@ -75,13 +75,13 @@ public async Task UpdateScoreAsync( int clanId, Address avatarAddress, int seasonId, - int roundId, + int roundIndex, int scoreChange ) { - await InsureRankingStatus(seasonId, roundId, clanId); + await InsureRankingStatus(seasonId, roundIndex, clanId); - string clanRankingKey = string.Format(ClanRankingFormat, seasonId, roundId, clanId); + string clanRankingKey = string.Format(ClanRankingFormat, seasonId, roundIndex, clanId); string participantKey = string.Format( ParticipantKeyFormat, avatarAddress.ToHex().ToLower() @@ -94,12 +94,12 @@ public async Task GetRankAsync( int clanId, Address avatarAddress, int seasonId, - int roundId + int roundIndex ) { - await InsureRankingStatus(seasonId, roundId, clanId); + await InsureRankingStatus(seasonId, roundIndex, clanId); - string clanRankingKey = string.Format(ClanRankingFormat, seasonId, roundId, clanId); + string clanRankingKey = string.Format(ClanRankingFormat, seasonId, roundIndex, clanId); string participantKey = string.Format( ParticipantKeyFormat, avatarAddress.ToHex().ToLower() @@ -122,12 +122,12 @@ int roundId public async Task> GetScoresAsync( int clanId, int seasonId, - int roundId + int roundIndex ) { - await InsureRankingStatus(seasonId, roundId, clanId); + await InsureRankingStatus(seasonId, roundIndex, clanId); - string clanRankingKey = string.Format(ClanRankingFormat, seasonId, roundId, clanId); + string clanRankingKey = string.Format(ClanRankingFormat, seasonId, roundIndex, clanId); var scores = await _redis.SortedSetRangeByRankWithScoresAsync(clanRankingKey); @@ -148,13 +148,13 @@ public async Task InitRankingAsync( List<(Address AvatarAddress, int Score)> rankingData, int clanId, int seasonId, - int roundId, + int roundIndex, int roundInterval ) { - string statusKey = string.Format(StatusKeyFormat, seasonId, roundId, clanId); + string statusKey = string.Format(StatusKeyFormat, seasonId, roundIndex, clanId); await _redis.StringSetAsync(statusKey, RankingStatus.INITIALIZING.ToString()); - string clanRankingKey = string.Format(ClanRankingFormat, seasonId, roundId, clanId); + string clanRankingKey = string.Format(ClanRankingFormat, seasonId, roundIndex, clanId); foreach (var rankingEntry in rankingData) { @@ -184,7 +184,7 @@ await _redis.StringSetAsync( ) ); - string clansKey = string.Format(ClansKeyFormat, seasonId, roundId); + string clansKey = string.Format(ClansKeyFormat, seasonId, roundIndex); await _redis.SetAddAsync(clansKey, clanId.ToString()); await _redis.KeyExpireAsync( clansKey, @@ -197,15 +197,15 @@ await _redis.KeyExpireAsync( public async Task CopyRoundDataAsync( int clanId, int seasonId, - int sourceRoundId, - int targetRoundId, + int sourceRoundIndex, + int targetRoundIndex, int roundInterval ) { - string statusKey = string.Format(StatusKeyFormat, seasonId, targetRoundId, clanId); + string statusKey = string.Format(StatusKeyFormat, seasonId, targetRoundIndex, clanId); await _redis.StringSetAsync(statusKey, RankingStatus.COPYING_IN_PROGRESS.ToString()); - string sourceKey = string.Format(ClanRankingFormat, seasonId, sourceRoundId, clanId); - string targetKey = string.Format(ClanRankingFormat, seasonId, targetRoundId, clanId); + string sourceKey = string.Format(ClanRankingFormat, seasonId, sourceRoundIndex, clanId); + string targetKey = string.Format(ClanRankingFormat, seasonId, targetRoundIndex, clanId); await _redis.SortedSetCombineAndStoreAsync(SetOperation.Union, targetKey, [sourceKey]); await _redis.KeyExpireAsync( @@ -222,9 +222,9 @@ await _redis.StringSetAsync( ) ); - string clansKey = string.Format(ClansKeyFormat, seasonId, targetRoundId); + string clansKey = string.Format(ClansKeyFormat, seasonId, targetRoundIndex); - var clans = await GetClansAsync(seasonId, sourceRoundId); + var clans = await GetClansAsync(seasonId, sourceRoundIndex); foreach (var existsClanId in clans) { await _redis.SetAddAsync(clansKey, existsClanId.ToString()); @@ -238,9 +238,9 @@ await _redis.KeyExpireAsync( ); } - public async Task> GetClansAsync(int seasonId, int roundId) + public async Task> GetClansAsync(int seasonId, int roundIndex) { - string clansKey = string.Format(ClansKeyFormat, seasonId, roundId); + string clansKey = string.Format(ClansKeyFormat, seasonId, roundIndex); var clanIds = await _redis.SetMembersAsync(clansKey); return clanIds.Select(id => int.Parse(id.ToString())).ToList(); } @@ -248,13 +248,13 @@ public async Task> GetClansAsync(int seasonId, int roundId) public async Task> GetTopClansAsync( int clanId, int seasonId, - int roundId, + int roundIndex, int topN ) { - await InsureRankingStatus(seasonId, roundId, clanId); + await InsureRankingStatus(seasonId, roundIndex, clanId); - string clanRankingPerClanKey = string.Format(ClanRankingFormat, seasonId, roundId, clanId); + string clanRankingPerClanKey = string.Format(ClanRankingFormat, seasonId, roundIndex, clanId); var topParticipants = await _redis.SortedSetRangeByRankWithScoresAsync( clanRankingPerClanKey, @@ -273,9 +273,9 @@ int topN .ToList(); } - private async Task InsureRankingStatus(int seasonId, int roundId, int clanId) + private async Task InsureRankingStatus(int seasonId, int roundIndex, int clanId) { - string statusKey = string.Format(StatusKeyFormat, seasonId, roundId, clanId); + string statusKey = string.Format(StatusKeyFormat, seasonId, roundIndex, clanId); var rankingStatus = await _redis.StringGetAsync(statusKey); if (rankingStatus != RankingStatus.DONE.ToString()) { diff --git a/ArenaService.Shared/Repositories/RankingRepository.cs b/ArenaService.Shared/Repositories/RankingRepository.cs index ee24ea4..1dbd755 100644 --- a/ArenaService.Shared/Repositories/RankingRepository.cs +++ b/ArenaService.Shared/Repositories/RankingRepository.cs @@ -7,33 +7,33 @@ namespace ArenaService.Shared.Repositories; public interface IRankingRepository { - Task GetRankingStatus(int seasonId, int roundId); + Task GetRankingStatus(int seasonId, int roundIndex); - Task UpdateScoreAsync(Address avatarAddress, int seasonId, int roundId, int scoreChange); + Task UpdateScoreAsync(Address avatarAddress, int seasonId, int roundIndex, int scoreChange); - Task GetRankAsync(Address avatarAddress, int seasonId, int roundId); + Task GetRankAsync(Address avatarAddress, int seasonId, int roundIndex); - Task> GetScoresAsync(int seasonId, int roundId); + Task> GetScoresAsync(int seasonId, int roundIndex); - Task GetScoreAsync(Address avatarAddress, int seasonId, int roundId); + Task GetScoreAsync(Address avatarAddress, int seasonId, int roundIndex); Task InitRankingAsync( List<(Address AvatarAddress, int Score)> rankingData, int seasonId, - int roundId, + int roundIndex, int roundInterval ); Task> SelectBattleOpponentsAsync( Address avatarAddress, int seasonId, - int roundId, + int roundIndex, bool isFirstRound ); - Task CopyRoundDataAsync(int seasonId, int sourceRoundId, int targetRoundId, int roundInterval); + Task CopyRoundDataAsync(int seasonId, int sourceRoundIndex, int targetRoundIndex, int roundInterval); - Task GetRankingCountAsync(int seasonId, int roundId); + Task GetRankingCountAsync(int seasonId, int roundIndex); } public class RankingRepository : IRankingRepository @@ -58,9 +58,9 @@ public RankingRepository(IConnectionMultiplexer redis, int? databaseNumber = nul } } - public async Task GetRankingStatus(int seasonId, int roundId) + public async Task GetRankingStatus(int seasonId, int roundIndex) { - string statusKey = string.Format(StatusKeyFormat, seasonId, roundId); + string statusKey = string.Format(StatusKeyFormat, seasonId, roundIndex); var rankingStatus = await _redis.StringGetAsync(statusKey); return rankingStatus; } @@ -68,13 +68,13 @@ public RankingRepository(IConnectionMultiplexer redis, int? databaseNumber = nul public async Task UpdateScoreAsync( Address avatarAddress, int seasonId, - int roundId, + int roundIndex, int scoreChange ) { - await InsureRankingStatus(seasonId, roundId); + await InsureRankingStatus(seasonId, roundIndex); - string rankingKey = string.Format(RankingKeyFormat, seasonId, roundId); + string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); string participantKey = string.Format( ParticipantKeyFormat, avatarAddress.ToHex().ToLower() @@ -83,11 +83,11 @@ int scoreChange await _redis.SortedSetIncrementAsync(rankingKey, participantKey, scoreChange); } - public async Task GetRankAsync(Address avatarAddress, int seasonId, int roundId) + public async Task GetRankAsync(Address avatarAddress, int seasonId, int roundIndex) { - await InsureRankingStatus(seasonId, roundId); + await InsureRankingStatus(seasonId, roundIndex); - string rankingKey = string.Format(RankingKeyFormat, seasonId, roundId); + string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); string participantKey = string.Format( ParticipantKeyFormat, avatarAddress.ToHex().ToLower() @@ -113,12 +113,12 @@ await _redis.SortedSetRangeByScoreWithScoresAsync( public async Task> GetScoresAsync( int seasonId, - int roundId + int roundIndex ) { - await InsureRankingStatus(seasonId, roundId); + await InsureRankingStatus(seasonId, roundIndex); - string rankingKey = string.Format(RankingKeyFormat, seasonId, roundId); + string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); var scores = await _redis.SortedSetRangeByRankWithScoresAsync(rankingKey); @@ -135,20 +135,20 @@ int roundId .ToList(); } - public async Task GetRankingCountAsync(int seasonId, int roundId) + public async Task GetRankingCountAsync(int seasonId, int roundIndex) { - string rankingKey = string.Format(RankingKeyFormat, seasonId, roundId); + string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); int totalRankingCount = (int)await _redis.SortedSetLengthAsync(rankingKey); return totalRankingCount; } - public async Task GetScoreAsync(Address avatarAddress, int seasonId, int roundId) + public async Task GetScoreAsync(Address avatarAddress, int seasonId, int roundIndex) { - await InsureRankingStatus(seasonId, roundId); + await InsureRankingStatus(seasonId, roundIndex); - string rankingKey = string.Format(RankingKeyFormat, seasonId, roundId); + string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); string participantKey = string.Format( ParticipantKeyFormat, avatarAddress.ToHex().ToLower() @@ -162,9 +162,9 @@ public async Task GetScoreAsync(Address avatarAddress, int seasonId, int ro public async Task< Dictionary - > SelectBattleOpponentsAsync(Address avatarAddress, int seasonId, int roundId, bool isFirstRound) + > SelectBattleOpponentsAsync(Address avatarAddress, int seasonId, int roundIndex, bool isFirstRound) { - string rankingKey = string.Format(RankingKeyFormat, seasonId, roundId); + string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); string participantKey = string.Format( ParticipantKeyFormat, avatarAddress.ToHex().ToLower() @@ -255,13 +255,13 @@ await _redis.SortedSetRangeByRankWithScoresAsync( public async Task InitRankingAsync( List<(Address AvatarAddress, int Score)> rankingData, int seasonId, - int roundId, + int roundIndex, int roundInterval ) { - string statusKey = string.Format(StatusKeyFormat, seasonId, roundId); + string statusKey = string.Format(StatusKeyFormat, seasonId, roundIndex); await _redis.StringSetAsync(statusKey, RankingStatus.INITIALIZING.ToString()); - string rankingKey = string.Format(RankingKeyFormat, seasonId, roundId); + string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); foreach (var rankingEntry in rankingData) { @@ -289,16 +289,16 @@ await _redis.StringSetAsync( public async Task CopyRoundDataAsync( int seasonId, - int sourceRoundId, - int targetRoundId, + int sourceRoundIndex, + int targetRoundIndex, int roundInterval ) { - string statusKey = string.Format(StatusKeyFormat, seasonId, targetRoundId); + string statusKey = string.Format(StatusKeyFormat, seasonId, targetRoundIndex); await _redis.StringSetAsync(statusKey, RankingStatus.COPYING_IN_PROGRESS.ToString()); - string sourceKey = string.Format(RankingKeyFormat, seasonId, sourceRoundId); - string targetKey = string.Format(RankingKeyFormat, seasonId, targetRoundId); + string sourceKey = string.Format(RankingKeyFormat, seasonId, sourceRoundIndex); + string targetKey = string.Format(RankingKeyFormat, seasonId, targetRoundIndex); await _redis.SortedSetCombineAndStoreAsync(SetOperation.Union, targetKey, [sourceKey]); await _redis.KeyExpireAsync( @@ -316,9 +316,9 @@ await _redis.StringSetAsync( ); } - private async Task InsureRankingStatus(int seasonId, int roundId) + private async Task InsureRankingStatus(int seasonId, int roundIndex) { - string statusKey = string.Format(StatusKeyFormat, seasonId, roundId); + string statusKey = string.Format(StatusKeyFormat, seasonId, roundIndex); var rankingStatus = await _redis.StringGetAsync(statusKey); if (rankingStatus != RankingStatus.DONE.ToString()) { diff --git a/ArenaService.Shared/Repositories/SeasonCacheRepository.cs b/ArenaService.Shared/Repositories/SeasonCacheRepository.cs index 8db3b79..50f6470 100644 --- a/ArenaService.Shared/Repositories/SeasonCacheRepository.cs +++ b/ArenaService.Shared/Repositories/SeasonCacheRepository.cs @@ -8,10 +8,10 @@ public interface ISeasonCacheRepository { Task GetBlockIndexAsync(); Task<(int Id, long StartBlock, long EndBlock)> GetSeasonAsync(); - Task<(int Id, long StartBlock, long EndBlock)> GetRoundAsync(); + Task<(int Id, int RoundIndex, long StartBlock, long EndBlock)> GetRoundAsync(); Task SetBlockIndexAsync(long blockIndex); Task SetSeasonAsync(int seasonId, long startBlock, long endBlock); - Task SetRoundAsync(int roundId, long startBlock, long endBlock); + Task SetRoundAsync(int roundId, int roundIndex, long startBlock, long endBlock); } public class SeasonCacheRepository : ISeasonCacheRepository @@ -52,7 +52,7 @@ public async Task GetBlockIndexAsync() return (seasonData!.Id, seasonData.StartBlock, seasonData.EndBlock); } - public async Task<(int Id, long StartBlock, long EndBlock)> GetRoundAsync() + public async Task<(int Id, int RoundIndex, long StartBlock, long EndBlock)> GetRoundAsync() { var value = await _redis.StringGetAsync($"{PREFIX}:{RoundKey}"); @@ -62,7 +62,7 @@ public async Task GetBlockIndexAsync() } var roundData = JsonSerializer.Deserialize(value!); - return (roundData!.Id, roundData.StartBlock, roundData.EndBlock); + return (roundData!.Id, roundData.RoundIndex, roundData.StartBlock, roundData.EndBlock); } public async Task SetBlockIndexAsync(long blockIndex) @@ -84,11 +84,12 @@ public async Task SetSeasonAsync(int seasonId, long startBlock, long endBlock) await _redis.StringSetAsync($"{PREFIX}:{SeasonKey}", json); } - public async Task SetRoundAsync(int roundId, long startBlock, long endBlock) + public async Task SetRoundAsync(int roundId, int roundIndex, long startBlock, long endBlock) { var roundData = new CachedRound { Id = roundId, + RoundIndex = roundIndex, StartBlock = startBlock, EndBlock = endBlock }; @@ -108,6 +109,7 @@ private class CachedSeason private class CachedRound { public int Id { get; set; } + public int RoundIndex { get; set; } public long StartBlock { get; set; } public long EndBlock { get; set; } } diff --git a/ArenaService.Shared/Services/ParticipateService.cs b/ArenaService.Shared/Services/ParticipateService.cs index 936cdca..09fba5b 100644 --- a/ArenaService.Shared/Services/ParticipateService.cs +++ b/ArenaService.Shared/Services/ParticipateService.cs @@ -13,6 +13,7 @@ public interface IParticipateService Task ParticipateAsync( int seasonId, int roundId, + int roundIndex, Address avatarAddress, int roundInterval, Func, IQueryable>? includeQuery = null @@ -54,6 +55,7 @@ IClanRankingRepository clanRankingRepo public async Task ParticipateAsync( int seasonId, int roundId, + int roundIndex, Address avatarAddress, int roundInterval, Func, IQueryable>? includeQuery = null @@ -122,13 +124,13 @@ await _rankingSnapshotRepo.AddRankingsSnapshot( await _rankingRepo.UpdateScoreAsync( participant.AvatarAddress, seasonId, - roundId, + roundIndex, participant.Score ); await _rankingRepo.UpdateScoreAsync( participant.AvatarAddress, seasonId, - roundId + 1, + roundIndex + 1, participant.Score ); @@ -138,14 +140,14 @@ await _clanRankingRepo.UpdateScoreAsync( user.ClanId.Value, avatarAddress, seasonId, - roundId, + roundIndex, participant.Score ); await _clanRankingRepo.UpdateScoreAsync( user.ClanId.Value, avatarAddress, seasonId, - roundId + 1, + roundIndex + 1, participant.Score ); } diff --git a/ArenaService.Shared/Services/RankingService.cs b/ArenaService.Shared/Services/RankingService.cs index 4491039..96cfae8 100644 --- a/ArenaService.Shared/Services/RankingService.cs +++ b/ArenaService.Shared/Services/RankingService.cs @@ -7,12 +7,12 @@ public interface IRankingService Task UpdateScoreAsync( Address avatarAddress, int seasonId, - int roundId, + int roundIndex, int scoreChange, int? clanId ); - Task UpdateAllClanRankingAsync(int seasonId, int roundId, int roundInterval); + Task UpdateAllClanRankingAsync(int seasonId, int roundIndex, int roundInterval); } public class RankingService : IRankingService @@ -35,12 +35,12 @@ IAllClanRankingRepository allClanRankingRepo public async Task UpdateScoreAsync( Address avatarAddress, int seasonId, - int roundId, + int roundIndex, int scoreChange, int? clanId = null ) { - await _rankingRepo.UpdateScoreAsync(avatarAddress, seasonId, roundId, scoreChange); + await _rankingRepo.UpdateScoreAsync(avatarAddress, seasonId, roundIndex, scoreChange); if (clanId is not null) { @@ -48,27 +48,27 @@ await _clanRankingRepo.UpdateScoreAsync( clanId.Value, avatarAddress, seasonId, - roundId, + roundIndex, scoreChange ); } } - public async Task UpdateAllClanRankingAsync(int seasonId, int roundId, int roundInterval) + public async Task UpdateAllClanRankingAsync(int seasonId, int roundIndex, int roundInterval) { - var clans = await _clanRankingRepo.GetClansAsync(seasonId, roundId); + var clans = await _clanRankingRepo.GetClansAsync(seasonId, roundIndex); var clanScores = new List<(int ClanId, int Score)>(); foreach (var clanId in clans) { - var topMembers = await _clanRankingRepo.GetTopClansAsync(clanId, seasonId, roundId); + var topMembers = await _clanRankingRepo.GetTopClansAsync(clanId, seasonId, roundIndex); int totalScore = topMembers.Sum(member => member.Score); clanScores.Add((clanId, totalScore)); } - await _allClanRankingRepo.InitRankingAsync(clanScores, seasonId, roundId, roundInterval); + await _allClanRankingRepo.InitRankingAsync(clanScores, seasonId, roundIndex, roundInterval); } } diff --git a/ArenaService.Shared/Services/RoundPreparationService.cs b/ArenaService.Shared/Services/RoundPreparationService.cs index 7816246..9619ce3 100644 --- a/ArenaService.Shared/Services/RoundPreparationService.cs +++ b/ArenaService.Shared/Services/RoundPreparationService.cs @@ -113,14 +113,14 @@ await _rankingSnapshotRepo.AddRankingsSnapshot( await _rankingService.UpdateAllClanRankingAsync( seasonAndRound.Season.Id, - seasonAndRound.Round.Id + 1, + seasonAndRound.Round.RoundIndex + 1, seasonAndRound.Season.RoundInterval ); await _rankingRepo.InitRankingAsync( rankingData, seasonAndRound.Season.Id, - seasonAndRound.Round.Id + 1, + seasonAndRound.Round.RoundIndex + 1, seasonAndRound.Season.RoundInterval ); _logger.LogInformation($"{nameof(RoundPreparationService)} Update Redis Ranking"); @@ -141,7 +141,7 @@ await _clanRankingRepo.CopyRoundDataAsync( clanId, seasonAndRound.Season.Id, seasonAndRound.Round.Id, - seasonAndRound.Round.Id + 1, + seasonAndRound.Round.RoundIndex + 1, seasonAndRound.Season.RoundInterval ); diff --git a/ArenaService.Shared/Services/SeasonPreparationService.cs b/ArenaService.Shared/Services/SeasonPreparationService.cs index 6da07d2..e684b25 100644 --- a/ArenaService.Shared/Services/SeasonPreparationService.cs +++ b/ArenaService.Shared/Services/SeasonPreparationService.cs @@ -171,7 +171,7 @@ Round round await _rankingRepo.InitRankingAsync( rankingData, season.Id, - round.Id + 1, + round.RoundIndex + 1, season.RoundInterval ); @@ -188,14 +188,14 @@ await _clanRankingRepo.InitRankingAsync( clanRankingData, clanId, season.Id, - round.Id + 1, + round.RoundIndex + 1, season.RoundInterval ); } - await _rankingService.UpdateAllClanRankingAsync(season.Id, round.Id, season.RoundInterval); + await _rankingService.UpdateAllClanRankingAsync(season.Id, round.RoundIndex, season.RoundInterval); await _rankingService.UpdateAllClanRankingAsync( season.Id, - round.Id + 1, + round.RoundIndex + 1, season.RoundInterval ); } diff --git a/ArenaService/Controllers/ArenaInfoController.cs b/ArenaService/Controllers/ArenaInfoController.cs index 9986af7..6fcbf07 100644 --- a/ArenaService/Controllers/ArenaInfoController.cs +++ b/ArenaService/Controllers/ArenaInfoController.cs @@ -140,12 +140,12 @@ public async Task GetArenaInfo() var nextScore = await _rankingRepo.GetScoreAsync( avatarAddress, cachedSeason.Id, - cachedRound.Id + 1 + cachedRound.RoundIndex + 1 ); var nextRank = await _rankingRepo.GetRankAsync( avatarAddress, cachedSeason.Id, - cachedRound.Id + 1 + cachedRound.RoundIndex + 1 ); var arenaInfo = new ArenaInfoResponse diff --git a/ArenaService/Processor/BattleProcessor.cs b/ArenaService/Processor/BattleProcessor.cs index 4d2433a..b975768 100644 --- a/ArenaService/Processor/BattleProcessor.cs +++ b/ArenaService/Processor/BattleProcessor.cs @@ -83,6 +83,7 @@ public async Task ProcessAsync(int battleId) .ThenInclude(u => u.Clan) .Include(b => b.Season) .ThenInclude(s => s.BattleTicketPolicy) + .Include(b => b.Round) .Include(b => b.Participant) .ThenInclude(p => p.User) .ThenInclude(u => u.Clan) @@ -395,7 +396,7 @@ await _battleRepo.UpdateBattle( await _rankingService.UpdateScoreAsync( battle.AvatarAddress, battle.SeasonId, - battle.RoundId + 1, + battle.Round.RoundIndex + 1, myScoreChange, battle.Participant.User.Clan is null ? null : battle.Participant.User.Clan.Id ); @@ -404,7 +405,7 @@ await _rankingService.UpdateScoreAsync( await _rankingService.UpdateScoreAsync( battle.AvailableOpponent.OpponentAvatarAddress, battle.SeasonId, - battle.RoundId + 1, + battle.Round.RoundIndex + 1, opponentScoreChange, battle.AvailableOpponent.Opponent.User.Clan is null ? null diff --git a/ArenaService/Worker/CacheBlockTipWorker.cs b/ArenaService/Worker/CacheBlockTipWorker.cs index e52783f..1a62559 100644 --- a/ArenaService/Worker/CacheBlockTipWorker.cs +++ b/ArenaService/Worker/CacheBlockTipWorker.cs @@ -155,6 +155,7 @@ ISeasonCacheRepository seasonCacheRepo await seasonCacheRepo.SetRoundAsync( seasonInfo.Round.Id, + seasonInfo.Round.RoundIndex, seasonInfo.Round.StartBlock, seasonInfo.Round.EndBlock ); diff --git a/ArenaService/Worker/PrepareRankingWorker.cs b/ArenaService/Worker/PrepareRankingWorker.cs index 65f8ea3..da1a670 100644 --- a/ArenaService/Worker/PrepareRankingWorker.cs +++ b/ArenaService/Worker/PrepareRankingWorker.cs @@ -289,7 +289,7 @@ await clanRankingRepo.InitRankingAsync( await rankingRepo.InitRankingAsync( nextRoundRankingData, seasonInfo.Season.Id, - seasonInfo.Round.Id + 1, + seasonInfo.Round.RoundIndex + 1, seasonInfo.Season.RoundInterval ); @@ -299,7 +299,7 @@ await clanRankingRepo.InitRankingAsync( nextRoundClanRankingData, clanId, seasonInfo.Season.Id, - seasonInfo.Round.Id + 1, + seasonInfo.Round.RoundIndex + 1, seasonInfo.Season.RoundInterval ); } @@ -308,12 +308,12 @@ await clanRankingRepo.InitRankingAsync( } await rankingService.UpdateAllClanRankingAsync( seasonInfo.Season.Id, - seasonInfo.Round.Id, + seasonInfo.Round.RoundIndex, seasonInfo.Season.RoundInterval ); await rankingService.UpdateAllClanRankingAsync( seasonInfo.Season.Id, - seasonInfo.Round.Id + 1, + seasonInfo.Round.RoundIndex + 1, seasonInfo.Season.RoundInterval ); diff --git a/ArenaService/Worker/RankingCopyWorker.cs b/ArenaService/Worker/RankingCopyWorker.cs index bb37eee..2f27c8d 100644 --- a/ArenaService/Worker/RankingCopyWorker.cs +++ b/ArenaService/Worker/RankingCopyWorker.cs @@ -123,11 +123,11 @@ IRoundPreparationService roundPreparationService var nextRoundInfo = await seasonService.GetSeasonAndRoundByBlock(blockIndex + 10); var nextRoundRankingCount = await rankingSnapshotRepo.GetRankingSnapshotsCount( nextRoundInfo.Season.Id, - nextRoundInfo.Round.Id + nextRoundInfo.Round.RoundIndex ); var previousRoundRankingCount = await rankingSnapshotRepo.GetRankingSnapshotsCount( nextRoundInfo.Season.Id, - nextRoundInfo.Round.Id - 1 + nextRoundInfo.Round.RoundIndex - 1 ); _logger.LogInformation( From d23827fdd20ef26a9bc68126f571afc416a11f14 Mon Sep 17 00:00:00 2001 From: Atralupus Date: Thu, 19 Jun 2025 21:24:34 +0900 Subject: [PATCH 2/9] Add season delete func --- .../Pages/CacheInitialization.razor | 185 ++++++++ .../Components/Pages/ManageSeasons.razor | 4 +- .../Components/Pages/Seasons.razor | 59 ++- .../Components/Shared/NavMenu.razor | 99 ++-- ArenaService.BackOffice/Program.cs | 423 +++++++++--------- .../Repositories/RankingRepository.cs | 49 ++ .../Repositories/RoundRepository.cs | 20 + .../Repositories/SeasonRepository.cs | 14 +- .../Services/CacheInitializationService.cs | 50 +++ .../Services/SeasonBlockAdjustmentService.cs | 4 +- ArenaService.Shared/Services/SeasonService.cs | 15 + .../AvailableOpponentController.cs | 2 + ArenaService/Controllers/BattleController.cs | 1 + 13 files changed, 662 insertions(+), 263 deletions(-) create mode 100644 ArenaService.BackOffice/Components/Pages/CacheInitialization.razor create mode 100644 ArenaService.Shared/Services/CacheInitializationService.cs diff --git a/ArenaService.BackOffice/Components/Pages/CacheInitialization.razor b/ArenaService.BackOffice/Components/Pages/CacheInitialization.razor new file mode 100644 index 0000000..688e08d --- /dev/null +++ b/ArenaService.BackOffice/Components/Pages/CacheInitialization.razor @@ -0,0 +1,185 @@ +@page "/cache-initialization" +@using ArenaService.Shared.Services +@using ArenaService.Shared.Repositories +@using ArenaService.Shared.Models +@inject ICacheInitializationService CacheInitializationService +@inject ISeasonCacheRepository SeasonCacheRepo +@inject IRankingRepository RankingRepo + +캐시 초기화 + +
+

🔄 캐시 초기화

+ + +

📅 현재 진행 중인 시즌

+ @if (currentSeason != null) + { + + + + + + + + + + + + + + + +
ID시작 블록종료 블록
@currentSeason?.Id@currentSeason?.StartBlock@currentSeason?.EndBlock
+ +

📅 현재 진행 중인 라운드

+ + + + + + + + + + + + + + + + + +
ID라운드 인덱스시작 블록종료 블록
@currentRound?.Id@currentRound?.RoundIndex@currentRound?.StartBlock@currentRound?.EndBlock
+ +

📊 랭킹 데이터 가용성

+ + + + + + + + + + @foreach (var roundRanking in rankingCounts) + { + + + + + + } + +
라운드랭킹 데이터 수상태
@roundRanking.RoundOffset@roundRanking.RankingCount@(roundRanking.RankingCount > 0 ? "✅ 준비됨" : "❌ 불완전")
+ } + else + { +

⚠️ 현재 진행 중인 시즌이 없습니다.

+ } + +
+ + +
+
+
🗑️ 전체 캐시 초기화
+
+
+

모든 랭킹 캐시를 초기화합니다. 이 작업은 되돌릴 수 없습니다.

+ +
+
+ + @if (!string.IsNullOrEmpty(message)) + { + + } +
+ +@code { + private bool isLoading = false; + private string message = ""; + private string alertClass = ""; + private (int Id, long StartBlock, long EndBlock)? currentSeason; + private (int Id, int RoundIndex, long StartBlock, long EndBlock)? currentRound; + private List<(int RoundOffset, int RankingCount)> rankingCounts = new(); + + protected override async Task OnInitializedAsync() + { + try + { + currentSeason = await SeasonCacheRepo.GetSeasonAsync(); + currentRound = await SeasonCacheRepo.GetRoundAsync(); + + if (currentSeason.HasValue && currentRound.HasValue) + { + await LoadRankingCounts(currentSeason.Value.Id, currentRound.Value.Id); + } + } + catch (Exception ex) + { + message = $"시즌 정보를 불러오는 중 오류가 발생했습니다: {ex.Message}"; + alertClass = "alert-danger"; + } + } + + private async Task LoadRankingCounts(int seasonId, int currentRoundId) + { + rankingCounts.Clear(); + + for (int offset = -1; offset <= 2; offset++) + { + int roundToCheck = currentRoundId + offset; + int rankingCount = await RankingRepo.GetRankingCountAsync(seasonId, roundToCheck); + rankingCounts.Add((currentRoundId + offset, rankingCount)); + } + } + + private async Task InitializeAllCache() + { + isLoading = true; + message = ""; + + try + { + var result = await CacheInitializationService.InitializeAllCacheAsync(); + + if (result) + { + message = "모든 랭킹 캐시가 성공적으로 초기화되었습니다."; + alertClass = "alert-success"; + + // 페이지 새로고침하여 최신 정보 표시 + await Task.Delay(1000); + await LoadRankingCounts(currentSeason!.Value.Id, currentRound!.Value.Id); + } + else + { + message = "캐시 초기화에 실패했습니다."; + alertClass = "alert-danger"; + } + } + catch (Exception ex) + { + message = $"캐시 초기화 중 오류가 발생했습니다: {ex.Message}"; + alertClass = "alert-danger"; + } + finally + { + isLoading = false; + } + } +} \ No newline at end of file diff --git a/ArenaService.BackOffice/Components/Pages/ManageSeasons.razor b/ArenaService.BackOffice/Components/Pages/ManageSeasons.razor index ae6f9df..da3f1a0 100644 --- a/ArenaService.BackOffice/Components/Pages/ManageSeasons.razor +++ b/ArenaService.BackOffice/Components/Pages/ManageSeasons.razor @@ -42,6 +42,7 @@ ID + Round Index Start Block End Block @@ -49,6 +50,7 @@ @currentRound?.Id + @currentRound?.RoundIndex @currentRound?.StartBlock @currentRound?.EndBlock @@ -114,7 +116,7 @@ else @code { private (int Id, long StartBlock, long EndBlock)? currentSeason; - private (int Id, long StartBlock, long EndBlock)? currentRound; + private (int Id, int RoundIndex, long StartBlock, long EndBlock)? currentRound; private List<(int RoundOffset, int RankingCount)> rankingCounts = new(); private int seasonId; private int roundId; diff --git a/ArenaService.BackOffice/Components/Pages/Seasons.razor b/ArenaService.BackOffice/Components/Pages/Seasons.razor index 63e020b..7c9d945 100644 --- a/ArenaService.BackOffice/Components/Pages/Seasons.razor +++ b/ArenaService.BackOffice/Components/Pages/Seasons.razor @@ -10,6 +10,7 @@ @inject ISeasonRepository SeasonRepository @inject ISeasonCacheRepository SeasonCacheRepo @inject ISeasonBlockAdjustmentService SeasonBlockAdjustmentService +@inject ISeasonService SeasonService @inject NavigationManager NavigationManager

🏆 Season List

@@ -42,7 +43,16 @@ else { @season.Id - @season.SeasonGroupId + + @if (editingSeasonId == season.Id) + { + + } + else + { + @season.SeasonGroupId + } + @season.StartBlock @season.EndBlock @@ -120,6 +130,14 @@ else { + @if (deletableSeasonIds.Contains(season.Id)) + { + + } + else + { + + } } @@ -173,6 +191,10 @@ else }

🆕 Add New Season

+
+ + +
@@ -240,6 +262,9 @@ else private Season editingSeason = new(); private long newEndBlock; + private int newSeasonGroupId; + + private HashSet deletableSeasonIds = new(); protected override async Task OnInitializedAsync() { @@ -253,6 +278,19 @@ else loading = true; paginatedSeasons = await SeasonRepository.GetSeasonsPagedAsync(currentPage, pageSize, q => q.Include(s => s.Rounds).Include(s => s.BattleTicketPolicy).Include(s => s.RefreshTicketPolicy)); + + // 현재 캐싱된 block index 가져오기 + var currentBlockIndex = await SeasonCacheRepo.GetBlockIndexAsync(); + deletableSeasonIds.Clear(); + + foreach (var season in paginatedSeasons) + { + if (season.StartBlock >= currentBlockIndex) + { + deletableSeasonIds.Add(season.Id); + } + } + loading = false; } @@ -264,7 +302,7 @@ else newStartBlock, newRoundInterval, newRoundCount, - 0, + newSeasonGroupId, newArenaType, newRequiredMedalCount, newTotalPrize, @@ -317,6 +355,7 @@ else editingSeason = new Season { Id = season.Id, + SeasonGroupId = season.SeasonGroupId, ArenaType = season.ArenaType, RoundInterval = season.RoundInterval, RequiredMedalCount = season.RequiredMedalCount, @@ -332,6 +371,7 @@ else { await SeasonRepository.UpdateSeasonAsync( seasonId, + editingSeason.SeasonGroupId, editingSeason.ArenaType, editingSeason.RoundInterval, editingSeason.RequiredMedalCount, @@ -373,4 +413,19 @@ else message = $"❌ Failed to adjust season end block: {ex.Message}"; } } + + private async Task DeleteSeason(int seasonId) + { + try + { + await SeasonService.DeleteSeasonAsync(seasonId); + message = "✅ Season deleted successfully!"; + totalSeasons = await SeasonRepository.GetTotalSeasonsCountAsync(); + await LoadSeasons(); + } + catch (Exception ex) + { + message = $"❌ Failed to delete season: {ex.Message}"; + } + } } diff --git a/ArenaService.BackOffice/Components/Shared/NavMenu.razor b/ArenaService.BackOffice/Components/Shared/NavMenu.razor index 7731a7c..6f3dd1a 100644 --- a/ArenaService.BackOffice/Components/Shared/NavMenu.razor +++ b/ArenaService.BackOffice/Components/Shared/NavMenu.razor @@ -1,48 +1,53 @@ - - - - -

모든 랭킹 캐시를 초기화합니다. 이 작업은 되돌릴 수 없습니다.

- + Page @(currentPage) / @totalPages + +
+} + +@if (selectedSeason != null) +{ +

📋 Selected Season's Round List

- + - - - - - - - -
IDRound ID Round Index Start Block End Block
@currentRound?.Id@currentRound?.RoundIndex@currentRound?.StartBlock@currentRound?.EndBlock
- -

📊 Ranking Data Availability

- - - - - - - - - - @foreach (var roundRanking in rankingCounts) + @foreach (var round in rounds) { - - - + + + + }
RoundRanking Data CountStatus
@roundRanking.RoundOffset@roundRanking.RankingCount@(roundRanking.RankingCount > 0 ? "✅ Ready" : "❌ Incomplete")@round.Id@round.RoundIndex@round.StartBlock@round.EndBlock
-} -else -{ -

⚠️ No active season currently.

-} -
+

🔧 Adjust Season End Block

+
+ +
+
+ + +
+ +
+ ⚠️ Warning: This action will affect all subsequent seasons and rounds. + If you decrease the end block, some rounds may be deleted or adjusted. +
+} - +

🆕 Add New Season

- - + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ - - -@if (seasonMessage is not null) +@if (message is not null) { -

@seasonMessage

+

@message

} -
+@code { + private List paginatedSeasons = new(); + private List rounds = new(); + private Season? selectedSeason; + private int currentPage = 1; + private int pageSize = 10; + private int totalSeasons = 0; + private bool loading = true; - -
- - -
+ private int newStartBlock; + private int newRoundInterval; + private int newRoundCount; + private ArenaType newArenaType = ArenaType.SEASON; + private int newRequiredMedalCount; + private int newTotalPrize; + private int newBattlePolicyId; + private int newRefreshPolicyId; + private string? message; - + private int editingSeasonId; + private Season editingSeason = new(); -@if (roundMessage is not null) -{ -

@roundMessage

-} + private long newEndBlock; + private int newSeasonGroupId; -@code { - private (int Id, long StartBlock, long EndBlock)? currentSeason; - private (int Id, int RoundIndex, long StartBlock, long EndBlock)? currentRound; - private List<(int RoundOffset, int RankingCount)> rankingCounts = new(); - private int seasonId; - private int roundId; - private string? seasonMessage; - private string? roundMessage; + private HashSet deletableSeasonIds = new(); protected override async Task OnInitializedAsync() { - try - { - currentSeason = await SeasonCacheRepo.GetSeasonAsync(); - currentRound = await SeasonCacheRepo.GetRoundAsync(); + newStartBlock = await SeasonRepository.GetLastSeasonEndBlockAsync() + 1 ?? 1; + totalSeasons = await SeasonRepository.GetTotalSeasonsCountAsync(); + await LoadSeasons(); + } - if (currentSeason.HasValue && currentRound.HasValue) + private async Task LoadSeasons() + { + loading = true; + paginatedSeasons = await SeasonRepository.GetSeasonsPagedAsync(currentPage, pageSize, q => q.Include(s => + s.Rounds).Include(s => s.BattleTicketPolicy).Include(s => s.RefreshTicketPolicy)); + + // 현재 캐싱된 block index 가져오기 + var currentBlockIndex = await SeasonCacheRepo.GetBlockIndexAsync(); + deletableSeasonIds.Clear(); + + foreach (var season in paginatedSeasons) + { + if (season.StartBlock >= currentBlockIndex) { - await LoadRankingCounts(currentSeason.Value.Id, currentRound.Value.Id); + deletableSeasonIds.Add(season.Id); } } + + loading = false; + } + + private async Task AddSeason() + { + try + { + await SeasonRepository.AddSeasonWithRoundsAsync( + newStartBlock, + newRoundInterval, + newRoundCount, + newSeasonGroupId, + newArenaType, + newRequiredMedalCount, + newTotalPrize, + newBattlePolicyId, + newRefreshPolicyId + ); + + totalSeasons = await SeasonRepository.GetTotalSeasonsCountAsync(); + + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } catch (Exception ex) { - Console.WriteLine($"⚠️ Error occurred while loading active season data: {ex.Message}"); + message = $"❌ Failed to add season: {ex.Message}"; } } - private async Task LoadRankingCounts(int seasonId, int currentRoundId) + private async Task SelectSeason(Season season) { - rankingCounts.Clear(); + selectedSeason = season; + rounds = season.Rounds.OrderBy(r => r.StartBlock).ToList(); + newEndBlock = season.EndBlock; + } - for (int offset = -1; offset <= 2; offset++) + private async Task NextPage() + { + if (CanGoNext) { - int roundToCheck = currentRoundId + offset; - int rankingCount = await RankingRepo.GetRankingCountAsync(seasonId, roundToCheck); - rankingCounts.Add((currentRoundId + offset, rankingCount)); + currentPage++; + await LoadSeasons(); } } - private async Task InitializeSeason() + private async Task PreviousPage() { - seasonMessage = "Initializing season..."; - try + if (CanGoPrevious) { - var season = await SeasonRepo.GetSeasonAsync(seasonId, q => q.Include(s => s.Rounds)); - - if (season == null) - { - seasonMessage = "❌ Season not found."; - return; - } + currentPage--; + await LoadSeasons(); + } + } - var firstRound = season.Rounds.OrderBy(r => r.StartBlock).FirstOrDefault(); + private int totalPages => (int)Math.Ceiling((double)totalSeasons / pageSize); + private bool CanGoPrevious => currentPage > 1; + private bool CanGoNext => currentPage < totalPages; - if (firstRound == null) - { - seasonMessage = "⚠️ No rounds exist for this season."; - return; - } + private void StartEdit(Season season) + { + editingSeasonId = season.Id; + editingSeason = new Season + { + Id = season.Id, + SeasonGroupId = season.SeasonGroupId, + ArenaType = season.ArenaType, + RoundInterval = season.RoundInterval, + RequiredMedalCount = season.RequiredMedalCount, + TotalPrize = season.TotalPrize, + BattleTicketPolicyId = season.BattleTicketPolicyId, + RefreshTicketPolicyId = season.RefreshTicketPolicyId + }; + } - await SeasonPreparationService.PrepareSeasonAsync((season, firstRound)); + private async Task SaveSeason(int seasonId) + { + try + { + await SeasonRepository.UpdateSeasonAsync( + seasonId, + editingSeason.SeasonGroupId, + editingSeason.ArenaType, + editingSeason.RoundInterval, + editingSeason.RequiredMedalCount, + editingSeason.TotalPrize, + editingSeason.BattleTicketPolicyId, + editingSeason.RefreshTicketPolicyId + ); - seasonMessage = "✅ Season initialized successfully!"; - NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + editingSeasonId = 0; + message = "✅ Season updated successfully!"; + await LoadSeasons(); } catch (Exception ex) { - seasonMessage = $"❌ Error occurred: {ex.Message}"; + message = $"❌ Failed to save season: {ex.Message}"; } } - private async Task PrepareNextRound() + private void CancelEdit() { - roundMessage = "Preparing next round..."; - try - { - var round = await RoundRepo.GetRoundAsync(roundId, q => q.Include(r => r.Season)); - var season = await SeasonRepo.GetSeasonAsync(round!.SeasonId, q => q.Include(s => s.Rounds)); + editingSeasonId = 0; + message = null; + } - if (round == null || round.Season == null) - { - roundMessage = "❌ Round not found."; - return; - } + private async Task AdjustSeasonEndBlock() + { + if (selectedSeason == null) return; - await RoundPreparationService.PrepareNextRoundWithSnapshotAsync((season, round)); + try + { + await SeasonBlockAdjustmentService.AdjustSeasonEndBlockAsync(selectedSeason.Id, newEndBlock); + message = "✅ Season end block adjusted successfully!"; + selectedSeason = null; + rounds.Clear(); + await LoadSeasons(); + } + catch (Exception ex) + { + message = $"❌ Failed to adjust season end block: {ex.Message}"; + } + } - roundMessage = "✅ Next round prepared successfully!"; - NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + private async Task DeleteSeason(int seasonId) + { + try + { + await SeasonService.DeleteSeasonAsync(seasonId); + message = "✅ Season deleted successfully!"; + totalSeasons = await SeasonRepository.GetTotalSeasonsCountAsync(); + await LoadSeasons(); } catch (Exception ex) { - roundMessage = $"❌ Error occurred: {ex.Message}"; + message = $"❌ Failed to delete season: {ex.Message}"; } } } diff --git a/ArenaService.BackOffice/Components/Pages/RankingCache.razor b/ArenaService.BackOffice/Components/Pages/RankingCache.razor new file mode 100644 index 0000000..924d1cb --- /dev/null +++ b/ArenaService.BackOffice/Components/Pages/RankingCache.razor @@ -0,0 +1,216 @@ +@page "/manage-seasons" +@using Microsoft.AspNetCore.Authorization +@using ArenaService.Shared.Repositories +@using ArenaService.Shared.Services +@using ArenaService.Shared.Models +@using Microsoft.EntityFrameworkCore +@attribute [Authorize] +@rendermode InteractiveServer +@inject ISeasonRepository SeasonRepo +@inject ISeasonCacheRepository SeasonCacheRepo +@inject IRankingRepository RankingRepo +@inject IRoundRepository RoundRepo +@inject ISeasonPreparationService SeasonPreparationService +@inject IRoundPreparationService RoundPreparationService +@inject NavigationManager NavigationManager + +

🏆 Season Management

+ + +

📅 Currently Active Season

+@if (currentSeason != null) +{ + + + + + + + + + + + + + + + +
IDStart BlockEnd Block
@currentSeason?.Id@currentSeason?.StartBlock@currentSeason?.EndBlock
+ +

📅 Currently Active Round

+ + + + + + + + + + + + + + + + + +
IDRound IndexStart BlockEnd Block
@currentRound?.Id@currentRound?.RoundIndex@currentRound?.StartBlock@currentRound?.EndBlock
+ +

📊 Ranking Data Availability

+ + + + + + + + + + @foreach (var roundRanking in rankingCounts) + { + + + + + + } + +
RoundRanking Data CountStatus
@roundRanking.RoundOffset@roundRanking.RankingCount@(roundRanking.RankingCount > 0 ? "✅ Ready" : "❌ Incomplete")
+} +else +{ +

⚠️ No active season currently.

+} + +
+ + +
+ + +
+ + + +@if (seasonMessage is not null) +{ +

@seasonMessage

+} + +
+ + +
+ + +
+ + + +@if (roundMessage is not null) +{ +

@roundMessage

+} + +@code { + private (int Id, long StartBlock, long EndBlock)? currentSeason; + private (int Id, int RoundIndex, long StartBlock, long EndBlock)? currentRound; + private List<(int RoundOffset, int RankingCount)> rankingCounts = new(); + private int seasonId; + private int roundId; + private string? seasonMessage; + private string? roundMessage; + + protected override async Task OnInitializedAsync() + { + try + { + currentSeason = await SeasonCacheRepo.GetSeasonAsync(); + currentRound = await SeasonCacheRepo.GetRoundAsync(); + + if (currentSeason.HasValue && currentRound.HasValue) + { + await LoadRankingCounts(currentSeason.Value.Id, currentRound.Value.RoundIndex); + } + } + catch (Exception ex) + { + Console.WriteLine($"⚠️ Error occurred while loading active season data: {ex.Message}"); + } + } + + private async Task LoadRankingCounts(int seasonId, int currentRoundIndex) + { + rankingCounts.Clear(); + + for (int offset = -1; offset <= 1; offset++) + { + int roundToCheck = currentRoundIndex + offset; + if (roundToCheck < 0) { + continue; + } + + int rankingCount = await RankingRepo.GetRankingCountAsync(seasonId, roundToCheck); + rankingCounts.Add((roundToCheck, rankingCount)); + } + } + + private async Task InitializeSeason() + { + seasonMessage = "Initializing season..."; + try + { + var season = await SeasonRepo.GetSeasonAsync(seasonId, q => q.Include(s => s.Rounds)); + + if (season == null) + { + seasonMessage = "❌ Season not found."; + return; + } + + var firstRound = season.Rounds.OrderBy(r => r.RoundIndex).FirstOrDefault(); + + if (firstRound == null) + { + seasonMessage = "⚠️ No rounds exist for this season."; + return; + } + + await SeasonPreparationService.PrepareSeasonAsync((season, firstRound)); + + seasonMessage = "✅ Season initialized successfully!"; + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + catch (Exception ex) + { + seasonMessage = $"❌ Error occurred: {ex.Message}"; + } + } + + private async Task PrepareNextRound() + { + roundMessage = "Preparing next round..."; + try + { + var round = await RoundRepo.GetRoundAsync(roundId, q => q.Include(r => r.Season)); + var season = await SeasonRepo.GetSeasonAsync(round!.SeasonId, q => q.Include(s => s.Rounds)); + + if (round == null || round.Season == null) + { + roundMessage = "❌ Round not found."; + return; + } + + await RoundPreparationService.PrepareNextRoundWithSnapshotAsync((season, round)); + + roundMessage = "✅ Next round prepared successfully!"; + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + catch (Exception ex) + { + roundMessage = $"❌ Error occurred: {ex.Message}"; + } + } +} diff --git a/ArenaService.BackOffice/Components/Pages/Seasons.razor b/ArenaService.BackOffice/Components/Pages/Seasons.razor deleted file mode 100644 index 173af90..0000000 --- a/ArenaService.BackOffice/Components/Pages/Seasons.razor +++ /dev/null @@ -1,433 +0,0 @@ -@page "/seasons" -@using Microsoft.AspNetCore.Authorization -@using ArenaService.Shared.Constants -@using ArenaService.Shared.Services -@using ArenaService.Shared.Models -@using ArenaService.Shared.Repositories -@using Microsoft.EntityFrameworkCore -@attribute [Authorize] -@rendermode InteractiveServer -@inject ISeasonRepository SeasonRepository -@inject ISeasonCacheRepository SeasonCacheRepo -@inject ISeasonBlockAdjustmentService SeasonBlockAdjustmentService -@inject ISeasonService SeasonService -@inject NavigationManager NavigationManager - -

🏆 Season List

- -@if (loading) -{ -

Loading...

-} -else -{ -

📅 Season List

- - - - - - - - - - - - - - - - - - @foreach (var season in paginatedSeasons) - { - - - - - - - - - - - - - - } - -
IDSeason Group IDStart BlockEnd BlockTypeRound IntervalRequired MedalsTotal PrizeBattle Ticket PolicyRefresh Ticket PolicyActions
@season.Id - @if (editingSeasonId == season.Id) - { - - } - else - { - @season.SeasonGroupId - } - @season.StartBlock@season.EndBlock - @if (editingSeasonId == season.Id) - { - - } - else - { - @season.ArenaType - } - - @if (editingSeasonId == season.Id) - { - - } - else - { - @season.RoundInterval - } - - @if (editingSeasonId == season.Id) - { - - } - else - { - @season.RequiredMedalCount - } - - @if (editingSeasonId == season.Id) - { - - } - else - { - @season.TotalPrize - } - - @if (editingSeasonId == season.Id) - { - - } - else - { - @season.BattleTicketPolicy.Name - } - - @if (editingSeasonId == season.Id) - { - - } - else - { - @season.RefreshTicketPolicy.Name - } - - @if (editingSeasonId == season.Id) - { - - - } - else - { - - - @if (deletableSeasonIds.Contains(season.Id)) - { - - } - else - { - - } - } -
- - -} - -@if (selectedSeason != null) -{ -

📋 Selected Season's Round List

- - - - - - - - - - - @foreach (var round in rounds) - { - - - - - - - } - -
Round IDRound IndexStart BlockEnd Block
@round.Id@round.RoundIndex@round.StartBlock@round.EndBlock
- -

🔧 Adjust Season End Block

-
- -
-
- - -
- -
- ⚠️ Warning: This action will affect all subsequent seasons and rounds. - If you decrease the end block, some rounds may be deleted or adjusted. -
-} - -

🆕 Add New Season

-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- - -@if (message is not null) -{ -

@message

-} - -@code { - private List paginatedSeasons = new(); - private List rounds = new(); - private Season? selectedSeason; - private int currentPage = 1; - private int pageSize = 10; - private int totalSeasons = 0; - private bool loading = true; - - private int newStartBlock; - private int newRoundInterval; - private int newRoundCount; - private ArenaType newArenaType = ArenaType.SEASON; - private int newRequiredMedalCount; - private int newTotalPrize; - private int newBattlePolicyId; - private int newRefreshPolicyId; - private string? message; - - private int editingSeasonId; - private Season editingSeason = new(); - - private long newEndBlock; - private int newSeasonGroupId; - - private HashSet deletableSeasonIds = new(); - - protected override async Task OnInitializedAsync() - { - newStartBlock = await SeasonRepository.GetLastSeasonEndBlockAsync() + 1 ?? 1; - totalSeasons = await SeasonRepository.GetTotalSeasonsCountAsync(); - await LoadSeasons(); - } - - private async Task LoadSeasons() - { - loading = true; - paginatedSeasons = await SeasonRepository.GetSeasonsPagedAsync(currentPage, pageSize, q => q.Include(s => - s.Rounds).Include(s => s.BattleTicketPolicy).Include(s => s.RefreshTicketPolicy)); - - // 현재 캐싱된 block index 가져오기 - var currentBlockIndex = await SeasonCacheRepo.GetBlockIndexAsync(); - deletableSeasonIds.Clear(); - - foreach (var season in paginatedSeasons) - { - if (season.StartBlock >= currentBlockIndex) - { - deletableSeasonIds.Add(season.Id); - } - } - - loading = false; - } - - private async Task AddSeason() - { - try - { - await SeasonRepository.AddSeasonWithRoundsAsync( - newStartBlock, - newRoundInterval, - newRoundCount, - newSeasonGroupId, - newArenaType, - newRequiredMedalCount, - newTotalPrize, - newBattlePolicyId, - newRefreshPolicyId - ); - - totalSeasons = await SeasonRepository.GetTotalSeasonsCountAsync(); - - NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); - } - catch (Exception ex) - { - message = $"❌ Failed to add season: {ex.Message}"; - } - } - - private async Task SelectSeason(Season season) - { - selectedSeason = season; - rounds = season.Rounds.OrderBy(r => r.StartBlock).ToList(); - newEndBlock = season.EndBlock; - } - - private async Task NextPage() - { - if (CanGoNext) - { - currentPage++; - await LoadSeasons(); - } - } - - private async Task PreviousPage() - { - if (CanGoPrevious) - { - currentPage--; - await LoadSeasons(); - } - } - - private int totalPages => (int)Math.Ceiling((double)totalSeasons / pageSize); - private bool CanGoPrevious => currentPage > 1; - private bool CanGoNext => currentPage < totalPages; - - private void StartEdit(Season season) - { - editingSeasonId = season.Id; - editingSeason = new Season - { - Id = season.Id, - SeasonGroupId = season.SeasonGroupId, - ArenaType = season.ArenaType, - RoundInterval = season.RoundInterval, - RequiredMedalCount = season.RequiredMedalCount, - TotalPrize = season.TotalPrize, - BattleTicketPolicyId = season.BattleTicketPolicyId, - RefreshTicketPolicyId = season.RefreshTicketPolicyId - }; - } - - private async Task SaveSeason(int seasonId) - { - try - { - await SeasonRepository.UpdateSeasonAsync( - seasonId, - editingSeason.SeasonGroupId, - editingSeason.ArenaType, - editingSeason.RoundInterval, - editingSeason.RequiredMedalCount, - editingSeason.TotalPrize, - editingSeason.BattleTicketPolicyId, - editingSeason.RefreshTicketPolicyId - ); - - editingSeasonId = 0; - message = "✅ Season updated successfully!"; - await LoadSeasons(); - } - catch (Exception ex) - { - message = $"❌ Failed to save season: {ex.Message}"; - } - } - - private void CancelEdit() - { - editingSeasonId = 0; - message = null; - } - - private async Task AdjustSeasonEndBlock() - { - if (selectedSeason == null) return; - - try - { - await SeasonBlockAdjustmentService.AdjustSeasonEndBlockAsync(selectedSeason.Id, newEndBlock); - message = "✅ Season end block adjusted successfully!"; - selectedSeason = null; - rounds.Clear(); - await LoadSeasons(); - } - catch (Exception ex) - { - message = $"❌ Failed to adjust season end block: {ex.Message}"; - } - } - - private async Task DeleteSeason(int seasonId) - { - try - { - await SeasonService.DeleteSeasonAsync(seasonId); - message = "✅ Season deleted successfully!"; - totalSeasons = await SeasonRepository.GetTotalSeasonsCountAsync(); - await LoadSeasons(); - } - catch (Exception ex) - { - message = $"❌ Failed to delete season: {ex.Message}"; - } - } -} diff --git a/ArenaService.BackOffice/Components/Shared/NavMenu.razor b/ArenaService.BackOffice/Components/Shared/NavMenu.razor index 6f3dd1a..b65153c 100644 --- a/ArenaService.BackOffice/Components/Shared/NavMenu.razor +++ b/ArenaService.BackOffice/Components/Shared/NavMenu.razor @@ -15,13 +15,13 @@
diff --git a/ArenaService.Shared/Repositories/RankingRepository.cs b/ArenaService.Shared/Repositories/RankingRepository.cs index bc1ddad..9832bea 100644 --- a/ArenaService.Shared/Repositories/RankingRepository.cs +++ b/ArenaService.Shared/Repositories/RankingRepository.cs @@ -31,15 +31,16 @@ int roundInterval bool isFirstRound ); - Task CopyRoundDataAsync(int seasonId, int sourceRoundIndex, int targetRoundIndex, int roundInterval); + Task CopyRoundDataAsync( + int seasonId, + int sourceRoundIndex, + int targetRoundIndex, + int roundInterval + ); Task GetRankingCountAsync(int seasonId, int roundIndex); - Task ClearRankingCacheAsync(int seasonId, int roundIndex); - Task ClearAllRankingCacheAsync(); - - Task ClearSeasonAndRoundCacheAsync(); } public class RankingRepository : IRankingRepository @@ -168,7 +169,12 @@ public async Task GetScoreAsync(Address avatarAddress, int seasonId, int ro public async Task< Dictionary - > SelectBattleOpponentsAsync(Address avatarAddress, int seasonId, int roundIndex, bool isFirstRound) + > SelectBattleOpponentsAsync( + Address avatarAddress, + int seasonId, + int roundIndex, + bool isFirstRound + ) { string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); string participantKey = string.Format( @@ -332,37 +338,11 @@ private async Task InsureRankingStatus(int seasonId, int roundIndex) } } - public async Task ClearRankingCacheAsync(int seasonId, int roundIndex) - { - string rankingKey = string.Format(RankingKeyFormat, seasonId, roundIndex); - string statusKey = string.Format(StatusKeyFormat, seasonId, roundIndex); - - await _redis.KeyDeleteAsync(rankingKey); - await _redis.KeyDeleteAsync(statusKey); - } - public async Task ClearAllRankingCacheAsync() { var server = _redis.Multiplexer.GetServer(_redis.Multiplexer.GetEndPoints().First()); var keys = server.Keys(pattern: "season:*:round:*:ranking"); - - foreach (var key in keys) - { - await _redis.KeyDeleteAsync(key); - } - var statusKeys = server.Keys(pattern: "season:*:round:*:ranking:status"); - foreach (var key in statusKeys) - { - await _redis.KeyDeleteAsync(key); - } - } - - public async Task ClearSeasonAndRoundCacheAsync() - { - var server = _redis.Multiplexer.GetServer(_redis.Multiplexer.GetEndPoints().First()); - var keys = server.Keys(pattern: "season:*:round:*:ranking"); - foreach (var key in keys) { await _redis.KeyDeleteAsync(key); diff --git a/ArenaService.Shared/Repositories/SeasonCacheRepository.cs b/ArenaService.Shared/Repositories/SeasonCacheRepository.cs index 50f6470..d9cc411 100644 --- a/ArenaService.Shared/Repositories/SeasonCacheRepository.cs +++ b/ArenaService.Shared/Repositories/SeasonCacheRepository.cs @@ -12,6 +12,10 @@ public interface ISeasonCacheRepository Task SetBlockIndexAsync(long blockIndex); Task SetSeasonAsync(int seasonId, long startBlock, long endBlock); Task SetRoundAsync(int roundId, int roundIndex, long startBlock, long endBlock); + Task DeleteBlockIndexAsync(); + Task DeleteSeasonAsync(); + Task DeleteRoundAsync(); + Task DeleteAllAsync(); } public class SeasonCacheRepository : ISeasonCacheRepository @@ -99,6 +103,28 @@ public async Task SetRoundAsync(int roundId, int roundIndex, long startBlock, lo await _redis.StringSetAsync($"{PREFIX}:{RoundKey}", json); } + public async Task DeleteBlockIndexAsync() + { + await _redis.KeyDeleteAsync($"{PREFIX}:{BlockIndexKey}"); + } + + public async Task DeleteSeasonAsync() + { + await _redis.KeyDeleteAsync($"{PREFIX}:{SeasonKey}"); + } + + public async Task DeleteRoundAsync() + { + await _redis.KeyDeleteAsync($"{PREFIX}:{RoundKey}"); + } + + public async Task DeleteAllAsync() + { + await _redis.KeyDeleteAsync($"{PREFIX}:{BlockIndexKey}"); + await _redis.KeyDeleteAsync($"{PREFIX}:{SeasonKey}"); + await _redis.KeyDeleteAsync($"{PREFIX}:{RoundKey}"); + } + private class CachedSeason { public int Id { get; set; } diff --git a/ArenaService.Shared/Services/CacheInitializationService.cs b/ArenaService.Shared/Services/CacheInitializationService.cs index 0d8baea..a53c7a9 100644 --- a/ArenaService.Shared/Services/CacheInitializationService.cs +++ b/ArenaService.Shared/Services/CacheInitializationService.cs @@ -4,47 +4,44 @@ namespace ArenaService.Shared.Services; public interface ICacheInitializationService { - Task InitializeCacheAsync(int seasonId, int roundIndex); - Task InitializeAllCacheAsync(); - Task InitializeSeasonAndRoundCacheAsync(); + Task InitializeRankingCacheAsync(int seasonId, int roundIndex); } public class CacheInitializationService : ICacheInitializationService { private readonly IRankingRepository _rankingRepository; private readonly IRankingSnapshotRepository _rankingSnapshotRepository; + private readonly IParticipantRepository _participantRepository; + private readonly ISeasonCacheRepository _seasonCacheRepository; public CacheInitializationService( IRankingRepository rankingRepository, - IRankingSnapshotRepository rankingSnapshotRepository) + IRankingSnapshotRepository rankingSnapshotRepository, + IParticipantRepository participantRepository, + ISeasonCacheRepository seasonCacheRepository + ) { _rankingRepository = rankingRepository; _rankingSnapshotRepository = rankingSnapshotRepository; + _participantRepository = participantRepository; + _seasonCacheRepository = seasonCacheRepository; } - public async Task InitializeCacheAsync(int seasonId, int roundIndex) + public async Task InitializeRankingCacheAsync(int seasonId, int roundIndex) { - var rankingCount = await _rankingRepository.GetRankingCountAsync(seasonId, roundIndex); - var snapshotCount = await _rankingSnapshotRepository.GetRankingSnapshotsCount(seasonId, roundIndex); - - if (rankingCount == 0 || snapshotCount < rankingCount) + var snapshotCount = await _rankingSnapshotRepository.GetRankingSnapshotsCount( + seasonId, + roundIndex + ); + var participantCount = await _participantRepository.GetParticipantCountAsync(seasonId); + + if (snapshotCount < participantCount - 50) { return false; } - await _rankingRepository.ClearRankingCacheAsync(seasonId, roundIndex); - return true; - } - - public async Task InitializeAllCacheAsync() - { await _rankingRepository.ClearAllRankingCacheAsync(); + await _seasonCacheRepository.DeleteAllAsync(); return true; } - - public async Task InitializeSeasonAndRoundCacheAsync() - { - await _rankingRepository.ClearSeasonAndRoundCacheAsync(); - return true; - } -} \ No newline at end of file +} From 5f0ea6fc470d53a3946446d3dd958ed6eb68e32f Mon Sep 17 00:00:00 2001 From: Atralupus Date: Mon, 23 Jun 2025 11:06:07 +0900 Subject: [PATCH 9/9] Restore cache clear --- .../Pages/CacheInitialization.razor | 36 ++++++++++++++++--- .../Components/Pages/ManageSeasons.razor | 2 +- .../Components/Pages/RankingCache.razor | 4 +-- .../Repositories/ParticipantRepository.cs | 2 +- .../Services/CacheInitializationService.cs | 4 +-- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/ArenaService.BackOffice/Components/Pages/CacheInitialization.razor b/ArenaService.BackOffice/Components/Pages/CacheInitialization.razor index 3bca332..d64048a 100644 --- a/ArenaService.BackOffice/Components/Pages/CacheInitialization.razor +++ b/ArenaService.BackOffice/Components/Pages/CacheInitialization.razor @@ -2,9 +2,13 @@ @using ArenaService.Shared.Services @using ArenaService.Shared.Repositories @using ArenaService.Shared.Models +@using Microsoft.AspNetCore.Authorization +@rendermode InteractiveServer +@attribute [Authorize] @inject ICacheInitializationService CacheInitializationService @inject ISeasonCacheRepository SeasonCacheRepo @inject IRankingRepository RankingRepo +@inject IJSRuntime JSRuntime 캐시 초기화 @@ -87,7 +91,7 @@

모든 랭킹 캐시를 초기화합니다. 이 작업은 되돌릴 수 없습니다.

- + + 버튼 상태: isLoading=@isLoading, currentSeason=@(currentSeason != null), currentRound=@(currentRound != null) +
@@ -121,6 +128,7 @@ { try { + Console.WriteLine("OnInitializedAsync 시작"); currentSeason = await SeasonCacheRepo.GetSeasonAsync(); currentRound = await SeasonCacheRepo.GetRoundAsync(); @@ -128,11 +136,25 @@ { await LoadRankingCounts(currentSeason.Value.Id, currentRound.Value.RoundIndex); } + + Console.WriteLine($"초기화 완료 - isLoading: {isLoading}, currentSeason: {currentSeason}, currentRound: {currentRound}"); + StateHasChanged(); } catch (Exception ex) { + Console.WriteLine($"OnInitializedAsync 오류: {ex}"); message = $"시즌 정보를 불러오는 중 오류가 발생했습니다: {ex.Message}"; alertClass = "alert-danger"; + StateHasChanged(); + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("console.log", "CacheInitialization 페이지가 로드되었습니다."); + await JSRuntime.InvokeVoidAsync("console.log", "SignalR 연결 상태를 확인하세요."); } } @@ -143,7 +165,7 @@ for (int offset = -1; offset <= 1; offset++) { int roundToCheck = currentRoundIndex + offset; - if (roundToCheck < 0) { + if (roundToCheck < 1) { continue; } @@ -154,19 +176,23 @@ private async Task InitializeRankingCacheAsync() { + Console.WriteLine("InitializeRankingCacheAsync 시작"); isLoading = true; message = ""; + StateHasChanged(); try { - var result = await CacheInitializationService.InitializeRankingCacheAsync(currentSeason.Value.Id, currentRound.Value.RoundIndex); + Console.WriteLine($"시즌 ID: {currentSeason!.Value.Id}, 라운드 인덱스: {currentRound!.Value.RoundIndex}"); + var result = await CacheInitializationService.InitializeRankingCacheAsync(currentSeason!.Value.Id, currentRound!.Value.Id); + + Console.WriteLine($"캐시 초기화 결과: {result}"); if (result) { message = "모든 랭킹 캐시가 성공적으로 초기화되었습니다."; alertClass = "alert-success"; - // 페이지 새로고침하여 최신 정보 표시 await Task.Delay(1000); await LoadRankingCounts(currentSeason!.Value.Id, currentRound!.Value.RoundIndex); } @@ -178,12 +204,14 @@ } catch (Exception ex) { + Console.WriteLine($"오류 발생: {ex}"); message = $"캐시 초기화 중 오류가 발생했습니다: {ex.Message}"; alertClass = "alert-danger"; } finally { isLoading = false; + StateHasChanged(); } } } \ No newline at end of file diff --git a/ArenaService.BackOffice/Components/Pages/ManageSeasons.razor b/ArenaService.BackOffice/Components/Pages/ManageSeasons.razor index 173af90..b37dc6a 100644 --- a/ArenaService.BackOffice/Components/Pages/ManageSeasons.razor +++ b/ArenaService.BackOffice/Components/Pages/ManageSeasons.razor @@ -1,4 +1,4 @@ -@page "/seasons" +@page "/manage-seasons" @using Microsoft.AspNetCore.Authorization @using ArenaService.Shared.Constants @using ArenaService.Shared.Services diff --git a/ArenaService.BackOffice/Components/Pages/RankingCache.razor b/ArenaService.BackOffice/Components/Pages/RankingCache.razor index 924d1cb..d059732 100644 --- a/ArenaService.BackOffice/Components/Pages/RankingCache.razor +++ b/ArenaService.BackOffice/Components/Pages/RankingCache.razor @@ -1,4 +1,4 @@ -@page "/manage-seasons" +@page "/ranking-cache" @using Microsoft.AspNetCore.Authorization @using ArenaService.Shared.Repositories @using ArenaService.Shared.Services @@ -148,7 +148,7 @@ else for (int offset = -1; offset <= 1; offset++) { int roundToCheck = currentRoundIndex + offset; - if (roundToCheck < 0) { + if (roundToCheck < 1) { continue; } diff --git a/ArenaService.Shared/Repositories/ParticipantRepository.cs b/ArenaService.Shared/Repositories/ParticipantRepository.cs index 27cb7e3..050d55f 100644 --- a/ArenaService.Shared/Repositories/ParticipantRepository.cs +++ b/ArenaService.Shared/Repositories/ParticipantRepository.cs @@ -86,7 +86,7 @@ public async Task AddParticipantsAsync(List users, int seasonId) public async Task GetParticipantCountAsync(int seasonId) { return await _context - .RankingSnapshots.AsQueryable() + .Participants.AsQueryable() .AsNoTracking() .Where(p => p.SeasonId == seasonId) .CountAsync(); diff --git a/ArenaService.Shared/Services/CacheInitializationService.cs b/ArenaService.Shared/Services/CacheInitializationService.cs index a53c7a9..31d3e82 100644 --- a/ArenaService.Shared/Services/CacheInitializationService.cs +++ b/ArenaService.Shared/Services/CacheInitializationService.cs @@ -27,11 +27,11 @@ ISeasonCacheRepository seasonCacheRepository _seasonCacheRepository = seasonCacheRepository; } - public async Task InitializeRankingCacheAsync(int seasonId, int roundIndex) + public async Task InitializeRankingCacheAsync(int seasonId, int roundId) { var snapshotCount = await _rankingSnapshotRepository.GetRankingSnapshotsCount( seasonId, - roundIndex + roundId ); var participantCount = await _participantRepository.GetParticipantCountAsync(seasonId);