From 5668aad9bcc2dba6773f58585aabcf42d5528e93 Mon Sep 17 00:00:00 2001 From: asesidaa <1061472754@qq.com> Date: Wed, 20 Nov 2024 20:05:53 +0800 Subject: [PATCH] Stage changes --- Application/Application.csproj | 1 + Application/DependencyInjection.cs | 16 +++ Application/GlobalUsings.cs | 2 + .../Handlers/Api/User/DeleteUserCommand.cs | 19 +++ .../Api/User/GetSongLeaderboardQuery.cs | 128 ++++++++++++++++++ Application/Handlers/Api/User/GetUserQuery.cs | 25 ++++ .../Handlers/Api/User/GetUsersQuery.cs | 58 ++++++++ .../Handlers/Game/GetInitialDataQuery.cs | 1 + Application/Interfaces/IGameDataService.cs | 1 + Application/Mappers/UserSettingMapper.cs | 33 +++++ .../Models/Game/CommonGetFolderResponse.cs | 4 +- .../Game/CommonGetShopFolderResponse.cs | 4 +- .../Game/CommonGetSongIntroductionResponse.cs | 4 +- Application/Utils/PlaySettingConverter.cs | 43 ++++++ Domain/Models/Base/PaginatedResult.cs | 11 ++ Domain/Models/DanData.cs | 1 + Domain/Models/{ => GameData}/Costume.cs | 2 +- .../{ => GameData}/DonCosRewardEntry.cs | 2 +- Domain/Models/{ => GameData}/DonCosRewards.cs | 2 +- .../Models/{ => GameData}/EventFolderData.cs | 2 +- Domain/Models/{ => GameData}/IVerupNo.cs | 2 +- Domain/Models/{ => GameData}/MusicDetail.cs | 2 +- .../Models/{ => GameData}/MusicInfoEntry.cs | 2 +- Domain/Models/{ => GameData}/MusicInfos.cs | 2 +- Domain/Models/{ => GameData}/MusicOrder.cs | 2 +- .../Models/{ => GameData}/MusicOrderEntry.cs | 2 +- Domain/Models/{ => GameData}/NeiroEntry.cs | 2 +- Domain/Models/{ => GameData}/Neiros.cs | 2 +- Domain/Models/{ => GameData}/QRCodeData.cs | 2 +- .../Models/{ => GameData}/ShopFolderData.cs | 2 +- Domain/Models/{ => GameData}/ShougouEntry.cs | 2 +- Domain/Models/{ => GameData}/Shougous.cs | 2 +- .../{ => GameData}/SongIntroductionData.cs | 2 +- Domain/Models/{ => GameData}/WordList.cs | 2 +- Domain/Models/{ => GameData}/WordListEntry.cs | 2 +- Domain/Models/SongLeaderboard.cs | 29 +--- Domain/Models/SongLeaderboardEntry.cs | 27 ++++ Domain/Models/UserSetting.cs | 2 +- Infrastructure/DependencyInjection.cs | 34 +++++ Infrastructure/Infrastructure.csproj | 2 + Infrastructure/Services/GameDataService.cs | 1 + Infrastructure/Services/JwtTokenService.cs | 35 +++++ .../Controllers/WeatherForecastController.cs | 32 ----- Server/Server.csproj | 4 + Server/Server.http | 6 - Server/WeatherForecast.cs | 12 -- Server/appsettings.Development.json | 8 -- 47 files changed, 477 insertions(+), 104 deletions(-) create mode 100644 Application/DependencyInjection.cs create mode 100644 Application/Handlers/Api/User/DeleteUserCommand.cs create mode 100644 Application/Handlers/Api/User/GetSongLeaderboardQuery.cs create mode 100644 Application/Handlers/Api/User/GetUserQuery.cs create mode 100644 Application/Handlers/Api/User/GetUsersQuery.cs create mode 100644 Application/Mappers/UserSettingMapper.cs create mode 100644 Application/Utils/PlaySettingConverter.cs create mode 100644 Domain/Models/Base/PaginatedResult.cs rename Domain/Models/{ => GameData}/Costume.cs (91%) rename Domain/Models/{ => GameData}/DonCosRewardEntry.cs (86%) rename Domain/Models/{ => GameData}/DonCosRewards.cs (83%) rename Domain/Models/{ => GameData}/EventFolderData.cs (92%) rename Domain/Models/{ => GameData}/IVerupNo.cs (64%) rename Domain/Models/{ => GameData}/MusicDetail.cs (96%) rename Domain/Models/{ => GameData}/MusicInfoEntry.cs (95%) rename Domain/Models/{ => GameData}/MusicInfos.cs (83%) rename Domain/Models/{ => GameData}/MusicOrder.cs (82%) rename Domain/Models/{ => GameData}/MusicOrderEntry.cs (81%) rename Domain/Models/{ => GameData}/NeiroEntry.cs (80%) rename Domain/Models/{ => GameData}/Neiros.cs (82%) rename Domain/Models/{ => GameData}/QRCodeData.cs (85%) rename Domain/Models/{ => GameData}/ShopFolderData.cs (89%) rename Domain/Models/{ => GameData}/ShougouEntry.cs (85%) rename Domain/Models/{ => GameData}/Shougous.cs (82%) rename Domain/Models/{ => GameData}/SongIntroductionData.cs (92%) rename Domain/Models/{ => GameData}/WordList.cs (83%) rename Domain/Models/{ => GameData}/WordListEntry.cs (94%) create mode 100644 Domain/Models/SongLeaderboardEntry.cs create mode 100644 Infrastructure/DependencyInjection.cs create mode 100644 Infrastructure/Services/JwtTokenService.cs delete mode 100644 Server/Controllers/WeatherForecastController.cs delete mode 100644 Server/Server.http delete mode 100644 Server/WeatherForecast.cs delete mode 100644 Server/appsettings.Development.json diff --git a/Application/Application.csproj b/Application/Application.csproj index 56b3fb5..572c37c 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -10,6 +10,7 @@ + diff --git a/Application/DependencyInjection.cs b/Application/DependencyInjection.cs new file mode 100644 index 0000000..ed20bf8 --- /dev/null +++ b/Application/DependencyInjection.cs @@ -0,0 +1,16 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; + +namespace Application; + +public static class DependencyInjection +{ + public static IServiceCollection AddApplication(this IServiceCollection services) + { + services.AddMediatR( + configuration => configuration.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); + + + return services; + } +} \ No newline at end of file diff --git a/Application/GlobalUsings.cs b/Application/GlobalUsings.cs index 9356528..4f8b1b5 100644 --- a/Application/GlobalUsings.cs +++ b/Application/GlobalUsings.cs @@ -8,6 +8,8 @@ global using Domain.Common; global using Domain.Entities; global using Domain.Enums; global using Domain.Models; +global using Domain.Models.Base; +global using Domain.Models.GameData; global using MediatR; global using Microsoft.EntityFrameworkCore; global using Microsoft.Extensions.Logging; diff --git a/Application/Handlers/Api/User/DeleteUserCommand.cs b/Application/Handlers/Api/User/DeleteUserCommand.cs new file mode 100644 index 0000000..b48a7e7 --- /dev/null +++ b/Application/Handlers/Api/User/DeleteUserCommand.cs @@ -0,0 +1,19 @@ +namespace Application.Handlers.Api.User; + +public record DeleteUserCommand(uint Baid) : IRequest>; + +public class DeleteUserCommandHandler(ITaikoDbContext context) : IRequestHandler> +{ + public async Task> Handle(DeleteUserCommand request, CancellationToken cancellationToken) + { + var userDatum = await context.UserData.FindAsync([request.Baid], cancellationToken); + if (userDatum == null) + { + return ApiResult.Failed("User not found."); + } + context.UserData.Remove(userDatum); + await context.SaveChangesAsync(cancellationToken); + + return ApiResult.Succeed(true); + } +} \ No newline at end of file diff --git a/Application/Handlers/Api/User/GetSongLeaderboardQuery.cs b/Application/Handlers/Api/User/GetSongLeaderboardQuery.cs new file mode 100644 index 0000000..cdce2f4 --- /dev/null +++ b/Application/Handlers/Api/User/GetSongLeaderboardQuery.cs @@ -0,0 +1,128 @@ +namespace Application.Handlers.Api.User; + +using LeaderBoard = PaginatedResult; +public record GetSongLeaderboardQuery(uint SongId, Difficulty Difficulty, int Baid, int Page, int Limit) : IRequest>; + +public class GetSongLeaderboardQueryHandler(ITaikoDbContext context, ILogger logger) + : IRequestHandler> +{ + public async Task> Handle(GetSongLeaderboardQuery request, CancellationToken cancellationToken) + { + var totalScores = await context.SongBestData + .Where(x => x.SongId == request.SongId && x.Difficulty == request.Difficulty) + .CountAsync(cancellationToken); + + var totalPages = (totalScores + request.Limit - 1) / request.Limit; + + var scores = await context.SongBestData + .Where(x => x.SongId == request.SongId && x.Difficulty == request.Difficulty) + .Select(x => new + { + x.Baid, + x.BestScore, + x.BestRate, + x.BestCrown, + x.BestScoreRank, + Rank = context.SongBestData.Count(y => y.SongId == request.SongId && y.Difficulty == request.Difficulty && y.BestScore > x.BestScore) + 1 + }) + .OrderByDescending(x => x.BestScore) + .ThenByDescending(x => x.BestRate) + .ThenByDescending(x => x.BestCrown) + .Skip((request.Page - 1) * request.Limit) + .Take(request.Limit) + .ToListAsync(cancellationToken); + + var userIds = scores.Select(x => x.Baid).Distinct().ToList(); + var users = await context.UserData + .Where(x => userIds.Contains(x.Baid)) + .ToDictionaryAsync(x => x.Baid, cancellationToken); + + var leaderboard = scores.Select(score => + { + var user = users.GetValueOrDefault(score.Baid); + return new SongLeaderboardEntry + { + Rank = score.Rank, + Baid = score.Baid, + UserName = user?.MyDonName, + BestScore = score.BestScore, + BestRate = score.BestRate, + BestCrown = score.BestCrown, + BestScoreRank = score.BestScoreRank + }; + }).ToList(); + + var userLeaderboardEntry = context.SongBestData + .Where(x => x.SongId == request.SongId && x.Difficulty == request.Difficulty && x.Baid == request.Baid) + .Select(x => new SongLeaderboardEntry + { + Baid = x.Baid, + BestScore = x.BestScore, + BestRate = x.BestRate, + BestCrown = x.BestCrown, + BestScoreRank = x.BestScoreRank, + Rank = context.SongBestData.Count(y => y.SongId == request.SongId && y.Difficulty == request.Difficulty && y.BestScore > x.BestScore) + 1 + }) + .FirstOrDefault(); + /*foreach (var score in scores) + { + var user = await context.UserData + .Where(x => x.Baid == score.Baid) + .FirstOrDefaultAsync(cancellationToken); + + var rank = await context.SongBestData + .Where(x => x.SongId == request.SongId && x.Difficulty == request.Difficulty && x.BestScore > score.BestScore) + .CountAsync(cancellationToken); + + leaderboard.Add(new SongLeaderboardEntry + { + Rank = rank + 1, + Baid = score.Baid, + UserName = user?.MyDonName, + BestScore = score.BestScore, + BestRate = score.BestRate, + BestCrown = score.BestCrown, + BestScoreRank = score.BestScoreRank + }); + }*/ + + /*SongLeaderboardEntry? userBestScore = null; + if (request.Baid != 0) + { + var score = await context.SongBestData + .Where(x => x.SongId == request.SongId && x.Difficulty == request.Difficulty && x.Baid == request.Baid) + .FirstOrDefaultAsync(cancellationToken); + + if (score != null) + { + var user = await context.UserData + .Where(x => x.Baid == request.Baid) + .FirstOrDefaultAsync(cancellationToken); + + var rank = await context.SongBestData + .Where(x => x.SongId == request.SongId && x.Difficulty == request.Difficulty && x.BestScore > score.BestScore) + .CountAsync(cancellationToken); + + userBestScore = new SongLeaderboardEntry + { + Rank = rank + 1, + Baid = score.Baid, + UserName = user?.MyDonName, + BestScore = score.BestScore, + BestRate = score.BestRate, + BestCrown = score.BestCrown, + BestScoreRank = score.BestScoreRank + }; + } + }*/ + + return ApiResult.Succeed(new LeaderBoard + { + Data = leaderboard, + Current = userLeaderboardEntry, + CurrentPage = request.Page, + TotalPages = totalPages, + TotalCount = totalScores + }); + } +} \ No newline at end of file diff --git a/Application/Handlers/Api/User/GetUserQuery.cs b/Application/Handlers/Api/User/GetUserQuery.cs new file mode 100644 index 0000000..74ceaa5 --- /dev/null +++ b/Application/Handlers/Api/User/GetUserQuery.cs @@ -0,0 +1,25 @@ +namespace Application.Handlers.Api.User; + +public record GetUserQuery(uint Baid) : IRequest>; + +public class GetUserQueryHandler(ITaikoDbContext context) : IRequestHandler> +{ + public async Task> Handle(GetUserQuery request, CancellationToken cancellationToken) + { + var userDatum = await context.UserData.Include(datum => datum.Cards) + .Where(datum => datum.Baid == request.Baid) + .FirstOrDefaultAsync(cancellationToken); + + if (userDatum == null) + { + return ApiResult.Failed("User not found."); + } + + return ApiResult.Succeed(new Domain.Models.User + { + Baid = userDatum.Baid, + AccessCodes = userDatum.Cards.Select(card => card.AccessCode).ToList(), + IsAdmin = userDatum.IsAdmin + }); + } +} \ No newline at end of file diff --git a/Application/Handlers/Api/User/GetUsersQuery.cs b/Application/Handlers/Api/User/GetUsersQuery.cs new file mode 100644 index 0000000..68b6318 --- /dev/null +++ b/Application/Handlers/Api/User/GetUsersQuery.cs @@ -0,0 +1,58 @@ +using Application.Mappers; + +namespace Application.Handlers.Api.User; + +using Users = PaginatedResult; + +public record GetUsersQuery(int Page, int Limit, string? SearchTerm) : IRequest>; + +public class GetUsersQueryHandler(ITaikoDbContext context) : IRequestHandler> +{ + public async Task> Handle(GetUsersQuery request, CancellationToken cancellationToken) + { + var users = new List(); + + var cardEntries = await context.Cards.ToListAsync(cancellationToken); + var userEntriesQuery = context.UserData.AsQueryable(); + + if (!string.IsNullOrEmpty(request.SearchTerm)) + { + var lowerCaseSearchTerm = request.SearchTerm.ToLower(); + userEntriesQuery = userEntriesQuery.Where(user => + user.Baid.ToString() == lowerCaseSearchTerm || + user.MyDonName.Contains(lowerCaseSearchTerm, StringComparison.CurrentCultureIgnoreCase) || + context.Cards.Any(card => card.Baid == user.Baid && + card.AccessCode.Contains(lowerCaseSearchTerm, StringComparison.CurrentCultureIgnoreCase))); + } + + var totalUsers = await userEntriesQuery.CountAsync(cancellationToken); + var totalPages = (totalUsers + request.Limit - 1) / request.Limit; + + var userEntries = await userEntriesQuery + .OrderBy(user => user.Baid) + .Skip((request.Page - 1) * request.Limit) + .Take(request.Limit) + .ToListAsync(cancellationToken); + + foreach (var user in userEntries) + { + var userSetting = UserSettingMapper.MapToUserSetting(user); + + users.Add(new Domain.Models.User + { + Baid = user.Baid, + AccessCodes = cardEntries.Where(card => card.Baid == user.Baid).Select(card => card.AccessCode).ToList(), + IsAdmin = user.IsAdmin, + UserSetting = userSetting + }); + } + + return ApiResult.Succeed( new Users + { + Data = users, + CurrentPage = request.Page, + TotalPages = totalPages, + TotalCount = totalUsers + }); + } +} \ No newline at end of file diff --git a/Application/Handlers/Game/GetInitialDataQuery.cs b/Application/Handlers/Game/GetInitialDataQuery.cs index c307fd2..2c87ac8 100644 --- a/Application/Handlers/Game/GetInitialDataQuery.cs +++ b/Application/Handlers/Game/GetInitialDataQuery.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using Domain.Models.GameData; using Domain.Settings; using Microsoft.Extensions.Options; diff --git a/Application/Interfaces/IGameDataService.cs b/Application/Interfaces/IGameDataService.cs index 4189928..393228d 100644 --- a/Application/Interfaces/IGameDataService.cs +++ b/Application/Interfaces/IGameDataService.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using Domain.Models.GameData; namespace Application.Interfaces; diff --git a/Application/Mappers/UserSettingMapper.cs b/Application/Mappers/UserSettingMapper.cs new file mode 100644 index 0000000..3a94b48 --- /dev/null +++ b/Application/Mappers/UserSettingMapper.cs @@ -0,0 +1,33 @@ +using System.Diagnostics.CodeAnalysis; +using Riok.Mapperly.Abstractions; + +namespace Application.Mappers; + +[Mapper(AutoUserMappings = false)] +public static partial class UserSettingMapper +{ + [SuppressMessage("Mapper", "RMG020:Source member is not mapped to any target member")] + [MapProperty(nameof(UserDatum.TitleFlgArray), nameof(UserSetting.UnlockedTitle))] + [MapProperty(nameof(UserDatum.OptionSetting), nameof(UserSetting.PlaySetting), Use = nameof(ShortToPlaySetting))] + [MapProperty(nameof(UserDatum.UnlockedKigurumi), nameof(UserSetting.UnlockedKigurumi), Use = nameof(FixUnlock))] + [MapProperty(nameof(UserDatum.UnlockedBody), nameof(UserSetting.UnlockedBody), Use = nameof(FixUnlock))] + [MapProperty(nameof(UserDatum.UnlockedFace), nameof(UserSetting.UnlockedFace), Use = nameof(FixUnlock))] + [MapProperty(nameof(UserDatum.UnlockedHead), nameof(UserSetting.UnlockedHead), Use = nameof(FixUnlock))] + [MapProperty(nameof(UserDatum.UnlockedPuchi), nameof(UserSetting.UnlockedPuchi), Use = nameof(FixUnlock))] + public static partial UserSetting MapToUserSetting(UserDatum user); + + public static PlaySetting ShortToPlaySetting(short option) + { + return PlaySettingConverter.ShortToPlaySetting(option); + } + + public static List FixUnlock(List unlock) + { + if (!unlock.Contains(0)) + { + unlock.Add(0); + } + + return unlock; + } +} \ No newline at end of file diff --git a/Application/Models/Game/CommonGetFolderResponse.cs b/Application/Models/Game/CommonGetFolderResponse.cs index 478c97e..b17057a 100644 --- a/Application/Models/Game/CommonGetFolderResponse.cs +++ b/Application/Models/Game/CommonGetFolderResponse.cs @@ -1,4 +1,6 @@ -namespace Application.Models.Game; +using Domain.Models.GameData; + +namespace Application.Models.Game; public class CommonGetFolderResponse { diff --git a/Application/Models/Game/CommonGetShopFolderResponse.cs b/Application/Models/Game/CommonGetShopFolderResponse.cs index 60810dd..4b5d396 100644 --- a/Application/Models/Game/CommonGetShopFolderResponse.cs +++ b/Application/Models/Game/CommonGetShopFolderResponse.cs @@ -1,4 +1,6 @@ -namespace Application.Models.Game; +using Domain.Models.GameData; + +namespace Application.Models.Game; public class CommonGetShopFolderResponse { diff --git a/Application/Models/Game/CommonGetSongIntroductionResponse.cs b/Application/Models/Game/CommonGetSongIntroductionResponse.cs index 0461d70..475d111 100644 --- a/Application/Models/Game/CommonGetSongIntroductionResponse.cs +++ b/Application/Models/Game/CommonGetSongIntroductionResponse.cs @@ -1,4 +1,6 @@ -namespace Application.Models.Game; +using Domain.Models.GameData; + +namespace Application.Models.Game; public class CommonGetSongIntroductionResponse { diff --git a/Application/Utils/PlaySettingConverter.cs b/Application/Utils/PlaySettingConverter.cs new file mode 100644 index 0000000..9d3b669 --- /dev/null +++ b/Application/Utils/PlaySettingConverter.cs @@ -0,0 +1,43 @@ +using System.Collections.Specialized; + +namespace Application.Utils; + +public static class PlaySettingConverter +{ + public static PlaySetting ShortToPlaySetting(short input) + { + var bits = new BitVector32(input); + var speedSection = BitVector32.CreateSection(15); + var vanishSection = BitVector32.CreateSection(1, speedSection); + var inverseSection = BitVector32.CreateSection(1, vanishSection); + var randomSection = BitVector32.CreateSection(2, inverseSection); + + var randomType = (RandomType)bits[randomSection]; + randomType.Throw().IfOutOfRange(); + var result = new PlaySetting + { + Speed = (uint)bits[speedSection], + IsVanishOn = bits[vanishSection] == 1, + IsInverseOn = bits[inverseSection] == 1, + RandomType = randomType + }; + + return result; + } + + public static short PlaySettingToShort(PlaySetting setting) + { + var bits = new BitVector32(); + var speedSection = BitVector32.CreateSection(15); + var vanishSection = BitVector32.CreateSection(1, speedSection); + var inverseSection = BitVector32.CreateSection(1, vanishSection); + var randomSection = BitVector32.CreateSection(2, inverseSection); + + bits[speedSection] = (int)setting.Speed; + bits[vanishSection] = setting.IsVanishOn ? 1 : 0; + bits[inverseSection] = setting.IsInverseOn ? 1 : 0; + bits[randomSection] = (int)setting.RandomType; + + return (short)bits.Data; + } +} \ No newline at end of file diff --git a/Domain/Models/Base/PaginatedResult.cs b/Domain/Models/Base/PaginatedResult.cs new file mode 100644 index 0000000..af9bee8 --- /dev/null +++ b/Domain/Models/Base/PaginatedResult.cs @@ -0,0 +1,11 @@ +namespace Domain.Models.Base; + +public class PaginatedResult +{ + public List Data { get; set; } = []; + public T? Current; + + public int CurrentPage { get; set; } + public int TotalPages { get; set; } + public int TotalCount { get; set; } +} \ No newline at end of file diff --git a/Domain/Models/DanData.cs b/Domain/Models/DanData.cs index 32651ec..b28f240 100644 --- a/Domain/Models/DanData.cs +++ b/Domain/Models/DanData.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using Domain.Models.GameData; namespace Domain.Models; diff --git a/Domain/Models/Costume.cs b/Domain/Models/GameData/Costume.cs similarity index 91% rename from Domain/Models/Costume.cs rename to Domain/Models/GameData/Costume.cs index 0744b13..958d592 100644 --- a/Domain/Models/Costume.cs +++ b/Domain/Models/GameData/Costume.cs @@ -1,4 +1,4 @@ -namespace Domain.Models; +namespace Domain.Models.GameData; public class Costume { diff --git a/Domain/Models/DonCosRewardEntry.cs b/Domain/Models/GameData/DonCosRewardEntry.cs similarity index 86% rename from Domain/Models/DonCosRewardEntry.cs rename to Domain/Models/GameData/DonCosRewardEntry.cs index ccdf21f..8da0d4f 100644 --- a/Domain/Models/DonCosRewardEntry.cs +++ b/Domain/Models/GameData/DonCosRewardEntry.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class DonCosRewardEntry { diff --git a/Domain/Models/DonCosRewards.cs b/Domain/Models/GameData/DonCosRewards.cs similarity index 83% rename from Domain/Models/DonCosRewards.cs rename to Domain/Models/GameData/DonCosRewards.cs index 28931b5..d4f5530 100644 --- a/Domain/Models/DonCosRewards.cs +++ b/Domain/Models/GameData/DonCosRewards.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class DonCosRewards { diff --git a/Domain/Models/EventFolderData.cs b/Domain/Models/GameData/EventFolderData.cs similarity index 92% rename from Domain/Models/EventFolderData.cs rename to Domain/Models/GameData/EventFolderData.cs index 2ba7cdc..2254173 100644 --- a/Domain/Models/EventFolderData.cs +++ b/Domain/Models/GameData/EventFolderData.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class EventFolderData : IVerupNo { diff --git a/Domain/Models/IVerupNo.cs b/Domain/Models/GameData/IVerupNo.cs similarity index 64% rename from Domain/Models/IVerupNo.cs rename to Domain/Models/GameData/IVerupNo.cs index 6d63ff1..cdf8f5e 100644 --- a/Domain/Models/IVerupNo.cs +++ b/Domain/Models/GameData/IVerupNo.cs @@ -1,4 +1,4 @@ -namespace Domain.Models; +namespace Domain.Models.GameData; public interface IVerupNo { diff --git a/Domain/Models/MusicDetail.cs b/Domain/Models/GameData/MusicDetail.cs similarity index 96% rename from Domain/Models/MusicDetail.cs rename to Domain/Models/GameData/MusicDetail.cs index 932a776..df5826a 100644 --- a/Domain/Models/MusicDetail.cs +++ b/Domain/Models/GameData/MusicDetail.cs @@ -1,6 +1,6 @@ using Domain.Enums; -namespace Domain.Models; +namespace Domain.Models.GameData; public class MusicDetail { diff --git a/Domain/Models/MusicInfoEntry.cs b/Domain/Models/GameData/MusicInfoEntry.cs similarity index 95% rename from Domain/Models/MusicInfoEntry.cs rename to Domain/Models/GameData/MusicInfoEntry.cs index 808a0ab..5029684 100644 --- a/Domain/Models/MusicInfoEntry.cs +++ b/Domain/Models/GameData/MusicInfoEntry.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; using Domain.Enums; -namespace Domain.Models; +namespace Domain.Models.GameData; public class MusicInfoEntry { diff --git a/Domain/Models/MusicInfos.cs b/Domain/Models/GameData/MusicInfos.cs similarity index 83% rename from Domain/Models/MusicInfos.cs rename to Domain/Models/GameData/MusicInfos.cs index 704f783..bb298c5 100644 --- a/Domain/Models/MusicInfos.cs +++ b/Domain/Models/GameData/MusicInfos.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class MusicInfos { diff --git a/Domain/Models/MusicOrder.cs b/Domain/Models/GameData/MusicOrder.cs similarity index 82% rename from Domain/Models/MusicOrder.cs rename to Domain/Models/GameData/MusicOrder.cs index 13c67f5..aa48be1 100644 --- a/Domain/Models/MusicOrder.cs +++ b/Domain/Models/GameData/MusicOrder.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class MusicOrder { diff --git a/Domain/Models/MusicOrderEntry.cs b/Domain/Models/GameData/MusicOrderEntry.cs similarity index 81% rename from Domain/Models/MusicOrderEntry.cs rename to Domain/Models/GameData/MusicOrderEntry.cs index faa2baf..e72be46 100644 --- a/Domain/Models/MusicOrderEntry.cs +++ b/Domain/Models/GameData/MusicOrderEntry.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class MusicOrderEntry { diff --git a/Domain/Models/NeiroEntry.cs b/Domain/Models/GameData/NeiroEntry.cs similarity index 80% rename from Domain/Models/NeiroEntry.cs rename to Domain/Models/GameData/NeiroEntry.cs index e87ba3c..606c0fe 100644 --- a/Domain/Models/NeiroEntry.cs +++ b/Domain/Models/GameData/NeiroEntry.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class NeiroEntry { diff --git a/Domain/Models/Neiros.cs b/Domain/Models/GameData/Neiros.cs similarity index 82% rename from Domain/Models/Neiros.cs rename to Domain/Models/GameData/Neiros.cs index c4a821a..d03b13c 100644 --- a/Domain/Models/Neiros.cs +++ b/Domain/Models/GameData/Neiros.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class Neiros { diff --git a/Domain/Models/QRCodeData.cs b/Domain/Models/GameData/QRCodeData.cs similarity index 85% rename from Domain/Models/QRCodeData.cs rename to Domain/Models/GameData/QRCodeData.cs index b46fce3..ed4a75b 100644 --- a/Domain/Models/QRCodeData.cs +++ b/Domain/Models/GameData/QRCodeData.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class QRCodeData { diff --git a/Domain/Models/ShopFolderData.cs b/Domain/Models/GameData/ShopFolderData.cs similarity index 89% rename from Domain/Models/ShopFolderData.cs rename to Domain/Models/GameData/ShopFolderData.cs index 3b12247..bc1c822 100644 --- a/Domain/Models/ShopFolderData.cs +++ b/Domain/Models/GameData/ShopFolderData.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class ShopFolderData { diff --git a/Domain/Models/ShougouEntry.cs b/Domain/Models/GameData/ShougouEntry.cs similarity index 85% rename from Domain/Models/ShougouEntry.cs rename to Domain/Models/GameData/ShougouEntry.cs index 0ccd0d9..383c15e 100644 --- a/Domain/Models/ShougouEntry.cs +++ b/Domain/Models/GameData/ShougouEntry.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class ShougouEntry { diff --git a/Domain/Models/Shougous.cs b/Domain/Models/GameData/Shougous.cs similarity index 82% rename from Domain/Models/Shougous.cs rename to Domain/Models/GameData/Shougous.cs index 895c53f..b60b491 100644 --- a/Domain/Models/Shougous.cs +++ b/Domain/Models/GameData/Shougous.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class Shougous { diff --git a/Domain/Models/SongIntroductionData.cs b/Domain/Models/GameData/SongIntroductionData.cs similarity index 92% rename from Domain/Models/SongIntroductionData.cs rename to Domain/Models/GameData/SongIntroductionData.cs index 6cc10cc..ea69f3d 100644 --- a/Domain/Models/SongIntroductionData.cs +++ b/Domain/Models/GameData/SongIntroductionData.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class SongIntroductionData : IVerupNo { diff --git a/Domain/Models/WordList.cs b/Domain/Models/GameData/WordList.cs similarity index 83% rename from Domain/Models/WordList.cs rename to Domain/Models/GameData/WordList.cs index aff8b07..5301bcf 100644 --- a/Domain/Models/WordList.cs +++ b/Domain/Models/GameData/WordList.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class WordList { diff --git a/Domain/Models/WordListEntry.cs b/Domain/Models/GameData/WordListEntry.cs similarity index 94% rename from Domain/Models/WordListEntry.cs rename to Domain/Models/GameData/WordListEntry.cs index b3a43bd..c26a156 100644 --- a/Domain/Models/WordListEntry.cs +++ b/Domain/Models/GameData/WordListEntry.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Domain.Models; +namespace Domain.Models.GameData; public class WordListEntry { diff --git a/Domain/Models/SongLeaderboard.cs b/Domain/Models/SongLeaderboard.cs index 1df16b5..4e823c3 100644 --- a/Domain/Models/SongLeaderboard.cs +++ b/Domain/Models/SongLeaderboard.cs @@ -1,27 +1,10 @@ -using Domain.Enums; - -namespace Domain.Models; +namespace Domain.Models; public class SongLeaderboard { - public int Rank { get; set; } - - public uint Baid { get; set; } - - public uint BestScore { get; set; } - - public uint BestRate { get; set; } - - public CrownType BestCrown { get; set; } - - public ScoreRank BestScoreRank { get; set; } - - public uint GoodCount { get; set; } - public uint OkCount { get; set; } - public uint MissCount { get; set; } - public uint ComboCount { get; set; } - public uint HitCount { get; set; } - public uint DrumrollCount { get; set; } - - public string? UserName { get; set; } + public List LeaderboardData { get; set; } = []; + public SongLeaderboardEntry? UserScore { get; set; } + public int CurrentPage { get; set; } + public int TotalPages { get; set; } + public int TotalScores { get; set; } } \ No newline at end of file diff --git a/Domain/Models/SongLeaderboardEntry.cs b/Domain/Models/SongLeaderboardEntry.cs new file mode 100644 index 0000000..9630c10 --- /dev/null +++ b/Domain/Models/SongLeaderboardEntry.cs @@ -0,0 +1,27 @@ +using Domain.Enums; + +namespace Domain.Models; + +public class SongLeaderboardEntry +{ + public int Rank { get; set; } + + public uint Baid { get; set; } + + public uint BestScore { get; set; } + + public uint BestRate { get; set; } + + public CrownType BestCrown { get; set; } + + public ScoreRank BestScoreRank { get; set; } + + public uint GoodCount { get; set; } + public uint OkCount { get; set; } + public uint MissCount { get; set; } + public uint ComboCount { get; set; } + public uint HitCount { get; set; } + public uint DrumrollCount { get; set; } + + public string? UserName { get; set; } +} \ No newline at end of file diff --git a/Domain/Models/UserSetting.cs b/Domain/Models/UserSetting.cs index 3a33b44..30dec9f 100644 --- a/Domain/Models/UserSetting.cs +++ b/Domain/Models/UserSetting.cs @@ -64,5 +64,5 @@ public class UserSetting public uint ColorLimb { get; set; } - public DateTime LastPlayDateTime { get; set; } + public DateTime LastPlayDatetime { get; set; } } \ No newline at end of file diff --git a/Infrastructure/DependencyInjection.cs b/Infrastructure/DependencyInjection.cs new file mode 100644 index 0000000..a17b952 --- /dev/null +++ b/Infrastructure/DependencyInjection.cs @@ -0,0 +1,34 @@ +using Application.Interfaces; +using Domain.Common; +using Infrastructure.Persistence; +using Infrastructure.Services; +using Infrastructure.Utils; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Infrastructure; + +public static class DependencyInjection +{ + public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddDbContext(option => + { + var dbName = configuration["DbFileName"]; + if (string.IsNullOrEmpty(dbName)) + { + dbName = Constants.DefaultDbName; + } + + var path = Path.Combine(PathHelper.GetRootPath(), dbName); + option.UseSqlite($"Data Source={path}"); + }); + services.AddScoped(provider => + provider.GetService() ?? throw new InvalidOperationException()); + + return services; + } +} \ No newline at end of file diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj index 10f4a2b..c07a6f8 100644 --- a/Infrastructure/Infrastructure.csproj +++ b/Infrastructure/Infrastructure.csproj @@ -15,6 +15,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/Infrastructure/Services/GameDataService.cs b/Infrastructure/Services/GameDataService.cs index 2893ff9..8ea9e22 100644 --- a/Infrastructure/Services/GameDataService.cs +++ b/Infrastructure/Services/GameDataService.cs @@ -5,6 +5,7 @@ using System.Text.Json; using Application.Interfaces; using Domain.Common; using Domain.Models; +using Domain.Models.GameData; using Domain.Settings; using Infrastructure.Utils; using Microsoft.Extensions.Options; diff --git a/Infrastructure/Services/JwtTokenService.cs b/Infrastructure/Services/JwtTokenService.cs new file mode 100644 index 0000000..8ac702d --- /dev/null +++ b/Infrastructure/Services/JwtTokenService.cs @@ -0,0 +1,35 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using Application.Interfaces; +using Domain.Settings; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; + +namespace Infrastructure.Services; + +public class JwtTokenService(IOptions options) : IJwtTokenService +{ + public string GenerateToken(uint baid, bool isAdmin) + { + var authSettings = options.Value; + var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authSettings.JwtKey)); + var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); + + var claims = new List + { + new(ClaimTypes.Name, baid.ToString()), + new(ClaimTypes.Role, isAdmin ? "Admin" : "User") + }; + + var token = new JwtSecurityToken( + issuer: authSettings.JwtIssuer, + audience: authSettings.JwtAudience, + expires: DateTime.UtcNow.AddHours(24), + signingCredentials: credentials, + claims: claims + ); + + return new JwtSecurityTokenHandler().WriteToken(token); + } +} \ No newline at end of file diff --git a/Server/Controllers/WeatherForecastController.cs b/Server/Controllers/WeatherForecastController.cs deleted file mode 100644 index f5b475e..0000000 --- a/Server/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Server.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } -} \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index e89945e..06e001a 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -10,4 +10,8 @@ + + + + diff --git a/Server/Server.http b/Server/Server.http deleted file mode 100644 index b36c8ea..0000000 --- a/Server/Server.http +++ /dev/null @@ -1,6 +0,0 @@ -@Server_HostAddress = http://localhost:5247 - -GET {{Server_HostAddress}}/weatherforecast/ -Accept: application/json - -### diff --git a/Server/WeatherForecast.cs b/Server/WeatherForecast.cs deleted file mode 100644 index 82e72a1..0000000 --- a/Server/WeatherForecast.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Server; - -public class WeatherForecast -{ - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string? Summary { get; set; } -} \ No newline at end of file diff --git a/Server/appsettings.Development.json b/Server/appsettings.Development.json deleted file mode 100644 index 0c208ae..0000000 --- a/Server/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -}