diff --git a/PaperMania/Server.Tests/Application/Character/CreatePlayerCharacterDataUseCaseTests.cs b/PaperMania/Server.Tests/Application/Character/CreatePlayerCharacterDataUseCaseTests.cs index 35bbdd9..1503581 100644 --- a/PaperMania/Server.Tests/Application/Character/CreatePlayerCharacterDataUseCaseTests.cs +++ b/PaperMania/Server.Tests/Application/Character/CreatePlayerCharacterDataUseCaseTests.cs @@ -7,7 +7,7 @@ using Server.Application.Port.Output.Transaction; using Server.Application.UseCase.Character; using Server.Application.UseCase.Character.Command; -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; using Server.Infrastructure.StaticData.Model; namespace Server.Tests.Application.Character; @@ -64,11 +64,11 @@ public async Task ExecuteAsync_Should_Create_CharacterData_And_PieceData() await useCase.ExecuteAsync(command, CancellationToken.None); - _repoMock.Verify(x => x.CreateAsync(It.Is( + _repoMock.Verify(x => x.CreateAsync(It.Is( p => p.UserId == 1 && p.CharacterId == 10 && p.CharacterLevel == 1 && p.CharacterExp == 0 ), It.IsAny()), Times.Once); - _repoMock.Verify(x => x.CreatePieceData(It.Is( + _repoMock.Verify(x => x.CreatePieceData(It.Is( p => p.UserId == 1 && p.CharacterId == 10 && p.PieceAmount == 0 ), It.IsAny()), Times.Once); } diff --git a/PaperMania/Server.Tests/Application/Character/GetAllPlayerCharacterDataUseCaseTests.cs b/PaperMania/Server.Tests/Application/Character/GetAllPlayerCharacterDataUseCaseTests.cs index 66673d8..08edc1a 100644 --- a/PaperMania/Server.Tests/Application/Character/GetAllPlayerCharacterDataUseCaseTests.cs +++ b/PaperMania/Server.Tests/Application/Character/GetAllPlayerCharacterDataUseCaseTests.cs @@ -3,7 +3,9 @@ using Server.Application.Exceptions; using Server.Application.Port.Output.Persistence; using Server.Application.UseCase.Character; +using Server.Domain.Entity; using Server.Infrastructure.Persistence.Model; +using Server.Infrastructure.StaticData.Model; namespace Server.Tests.Application.Character; @@ -31,7 +33,7 @@ public async Task ExecuteAsync_Should_Return_EmptyList_When_No_Characters() { int userId = 1; _repoMock.Setup(x => x.FindAll(userId, It.IsAny())) - .ReturnsAsync(Enumerable.Empty()); + .ReturnsAsync(Enumerable.Empty()); var useCase = CreateUseCase(); @@ -44,10 +46,10 @@ public async Task ExecuteAsync_Should_Return_EmptyList_When_No_Characters() public async Task ExecuteAsync_Should_Return_All_Characters() { int userId = 1; - var characters = new List + var characters = new List { - new PlayerCharacterData(userId, 10), - new PlayerCharacterData(userId, 20) + PlayerCharacter.Create(userId, 10, CharacterRole.Main), + PlayerCharacter.Create(userId, 20, CharacterRole.Main) }; _repoMock.Setup(x => x.FindAll(userId, It.IsAny())) diff --git a/PaperMania/Server.Tests/Application/Character/GetPlayerCharacterUseCaseTests.cs b/PaperMania/Server.Tests/Application/Character/GetPlayerCharacterUseCaseTests.cs index faceefe..43eef27 100644 --- a/PaperMania/Server.Tests/Application/Character/GetPlayerCharacterUseCaseTests.cs +++ b/PaperMania/Server.Tests/Application/Character/GetPlayerCharacterUseCaseTests.cs @@ -4,7 +4,8 @@ using Server.Application.Port.Output.Persistence; using Server.Application.UseCase.Character; using Server.Application.UseCase.Character.Command; -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; +using Server.Infrastructure.StaticData.Model; namespace Server.Tests.Application.Character; @@ -21,7 +22,7 @@ public async Task ExecuteAsync_Should_Throw_When_PlayerCharacter_Not_Found() var command = new GetPlayerCharacterCommand(1, 10); _repoMock.Setup(x => x.FindCharacter(1, 10, It.IsAny())) - .ReturnsAsync((PlayerCharacterData?)null); + .ReturnsAsync((PlayerCharacter?)null); var useCase = CreateUseCase(); @@ -37,15 +38,7 @@ public async Task ExecuteAsync_Should_Return_PlayerCharacterData_When_Found() { var command = new GetPlayerCharacterCommand(1, 10); - var characterData = new PlayerCharacterData( - UserId: 1, - CharacterId: 10, - CharacterLevel: 1, - CharacterExp: 0, - NormalSkillLevel: 1, - UltimateSkillLevel: 1, - SupportSkillLevel: 1 - ); + var characterData = PlayerCharacter.Create(1, 10, CharacterRole.Main); _repoMock.Setup(x => x.FindCharacter(1, 10, It.IsAny())) .ReturnsAsync(characterData); diff --git a/PaperMania/Server/Api/Dto/Response/Character/GetAllPlayerCharactersResponse.cs b/PaperMania/Server/Api/Dto/Response/Character/GetAllPlayerCharactersResponse.cs index 3d78731..e885107 100644 --- a/PaperMania/Server/Api/Dto/Response/Character/GetAllPlayerCharactersResponse.cs +++ b/PaperMania/Server/Api/Dto/Response/Character/GetAllPlayerCharactersResponse.cs @@ -1,8 +1,9 @@ -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; +using Server.Infrastructure.Persistence.Model; namespace Server.Api.Dto.Response.Character; public class GetAllPlayerCharactersResponse { - public List Characters { get; set; } + public List Characters { get; set; } } \ No newline at end of file diff --git a/PaperMania/Server/Api/Dto/Response/Character/GetCharacterDataResponse.cs b/PaperMania/Server/Api/Dto/Response/Character/GetCharacterDataResponse.cs index 5d89225..8bd7768 100644 --- a/PaperMania/Server/Api/Dto/Response/Character/GetCharacterDataResponse.cs +++ b/PaperMania/Server/Api/Dto/Response/Character/GetCharacterDataResponse.cs @@ -1,8 +1,9 @@ -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; +using Server.Infrastructure.Persistence.Model; namespace Server.Api.Dto.Response.Character; public class GetCharacterDataResponse { - public PlayerCharacterData Character { get; set; } + public PlayerCharacter Character { get; set; } } \ No newline at end of file diff --git a/PaperMania/Server/Application/Port/Input/Character/IGetAllPlayerCharacterDataUseCase.cs b/PaperMania/Server/Application/Port/Input/Character/IGetAllPlayerCharacterDataUseCase.cs index 2fdeb75..ba194b9 100644 --- a/PaperMania/Server/Application/Port/Input/Character/IGetAllPlayerCharacterDataUseCase.cs +++ b/PaperMania/Server/Application/Port/Input/Character/IGetAllPlayerCharacterDataUseCase.cs @@ -1,8 +1,8 @@ -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; namespace Server.Application.Port.Input.Character; public interface IGetAllPlayerCharacterDataUseCase { - Task> ExecuteAsync(int userId, CancellationToken ct); + Task> ExecuteAsync(int userId, CancellationToken ct); } \ No newline at end of file diff --git a/PaperMania/Server/Application/Port/Input/Character/IGetPlayerCharacterUseCase.cs b/PaperMania/Server/Application/Port/Input/Character/IGetPlayerCharacterUseCase.cs index 0bc6573..3fcc339 100644 --- a/PaperMania/Server/Application/Port/Input/Character/IGetPlayerCharacterUseCase.cs +++ b/PaperMania/Server/Application/Port/Input/Character/IGetPlayerCharacterUseCase.cs @@ -1,9 +1,9 @@ using Server.Application.UseCase.Character.Command; -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; namespace Server.Application.Port.Input.Character; public interface IGetPlayerCharacterUseCase { - Task ExecuteAsync(GetPlayerCharacterCommand request, CancellationToken ct); + Task ExecuteAsync(GetPlayerCharacterCommand request, CancellationToken ct); } \ No newline at end of file diff --git a/PaperMania/Server/Application/Port/Output/Persistence/ICharacterRepository.cs b/PaperMania/Server/Application/Port/Output/Persistence/ICharacterRepository.cs index 3fa811a..91f650a 100644 --- a/PaperMania/Server/Application/Port/Output/Persistence/ICharacterRepository.cs +++ b/PaperMania/Server/Application/Port/Output/Persistence/ICharacterRepository.cs @@ -1,12 +1,13 @@ +using Server.Domain.Entity; using Server.Infrastructure.Persistence.Model; namespace Server.Application.Port.Output.Persistence; public interface ICharacterRepository { - Task> FindAll(int userId, CancellationToken ct); - Task FindCharacter(int userId, int characterId, CancellationToken ct); - Task UpdateAsync(PlayerCharacterData data, CancellationToken ct); - Task CreateAsync(PlayerCharacterData data, CancellationToken ct); - Task CreatePieceData(PlayerCharacterPieceData data, CancellationToken ct); + Task> FindAll(int userId, CancellationToken ct); + Task FindCharacter(int userId, int characterId, CancellationToken ct); + Task UpdateAsync(PlayerCharacter entity, CancellationToken ct); + Task CreateAsync(PlayerCharacter entity, CancellationToken ct); + Task CreatePieceData(PlayerCharacter entity, CancellationToken ct); } \ No newline at end of file diff --git a/PaperMania/Server/Application/UseCase/Character/CreatePlayerCharacterDataUseCase.cs b/PaperMania/Server/Application/UseCase/Character/CreatePlayerCharacterDataUseCase.cs index d0d38e8..667a5d2 100644 --- a/PaperMania/Server/Application/UseCase/Character/CreatePlayerCharacterDataUseCase.cs +++ b/PaperMania/Server/Application/UseCase/Character/CreatePlayerCharacterDataUseCase.cs @@ -5,7 +5,7 @@ using Server.Application.Port.Output.StaticData; using Server.Application.Port.Output.Transaction; using Server.Application.UseCase.Character.Command; -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; namespace Server.Application.UseCase.Character; @@ -30,27 +30,18 @@ public async Task ExecuteAsync(CreatePlayerCharacterCommand request, Cancellatio { request.Validate(); - var character = _store.Get(request.CharacterId) - ?? throw new RequestException( - ErrorStatusCode.NotFound, - "CHARACTER_NOT_FOUND" + var characterDef = _store.Get(request.CharacterId) + ?? throw new RequestException( + ErrorStatusCode.NotFound, + "CHARACTER_NOT_FOUND" ); - + await _transactionScope.ExecuteAsync(async (innerCt) => { - var data = new PlayerCharacterData( - request.UserId, - request.CharacterId, - 1, - 0, - character.NormalSkillId == 0 ? 0 : 1, - character.UltimateSkillId == 0 ? 0 : 1, - character.SupportSkillId == 0 ? 0 : 1 - ); - - await _repository.CreateAsync(data, innerCt); + var character = PlayerCharacter.Create(request.UserId, request.CharacterId, characterDef.Role); - await _repository.CreatePieceData(new PlayerCharacterPieceData(data.UserId, data.CharacterId, 0),innerCt); + await _repository.CreateAsync(character, innerCt); + await _repository.CreatePieceData(character, innerCt); }, ct); } } \ No newline at end of file diff --git a/PaperMania/Server/Application/UseCase/Character/GetAllPlayerCharacterDataUseCase.cs b/PaperMania/Server/Application/UseCase/Character/GetAllPlayerCharacterDataUseCase.cs index 3934d1b..7519a93 100644 --- a/PaperMania/Server/Application/UseCase/Character/GetAllPlayerCharacterDataUseCase.cs +++ b/PaperMania/Server/Application/UseCase/Character/GetAllPlayerCharacterDataUseCase.cs @@ -2,7 +2,7 @@ using Server.Application.Exceptions; using Server.Application.Port.Input.Character; using Server.Application.Port.Output.Persistence; -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; namespace Server.Application.UseCase.Character; @@ -15,7 +15,7 @@ public GetAllPlayerCharacterDataUseCase(ICharacterRepository repository) _repository = repository; } - public async Task> ExecuteAsync(int userId, CancellationToken ct) + public async Task> ExecuteAsync(int userId, CancellationToken ct) { if (userId <= 0) throw new RequestException( diff --git a/PaperMania/Server/Application/UseCase/Character/GetPlayerCharacterUseCase.cs b/PaperMania/Server/Application/UseCase/Character/GetPlayerCharacterUseCase.cs index d9e35f8..ce096bf 100644 --- a/PaperMania/Server/Application/UseCase/Character/GetPlayerCharacterUseCase.cs +++ b/PaperMania/Server/Application/UseCase/Character/GetPlayerCharacterUseCase.cs @@ -3,7 +3,7 @@ using Server.Application.Port.Input.Character; using Server.Application.Port.Output.Persistence; using Server.Application.UseCase.Character.Command; -using Server.Infrastructure.Persistence.Model; +using Server.Domain.Entity; namespace Server.Application.UseCase.Character; @@ -16,7 +16,7 @@ public GetPlayerCharacterUseCase(ICharacterRepository repository) _repository = repository; } - public async Task ExecuteAsync(GetPlayerCharacterCommand request, CancellationToken ct) + public async Task ExecuteAsync(GetPlayerCharacterCommand request, CancellationToken ct) { request.Validate(); diff --git a/PaperMania/Server/Domain/Entity/PlayerCharacter.cs b/PaperMania/Server/Domain/Entity/PlayerCharacter.cs new file mode 100644 index 0000000..01b6180 --- /dev/null +++ b/PaperMania/Server/Domain/Entity/PlayerCharacter.cs @@ -0,0 +1,96 @@ +using Server.Infrastructure.StaticData.Model; + +namespace Server.Domain.Entity; + +public class PlayerCharacter +{ + public int UserId { get; set; } + public int CharacterId { get; set; } + public int CharacterLevel { get; set; } + public int CharacterExp { get; set; } + public CharacterRole Role { get; set; } + public int NormalSkillLevel { get; set; } + public int UltimateSkillLevel { get; set; } + public int SupportSkillLevel { get; set; } + public int PieceAmount { get; set; } + + public PlayerCharacter(int userId, int characterId, int characterLevel, int characterExp, + int normalSkillLevel, int ultimateSkillLevel, int supportSkillLevel, int pieceAmount) + { + UserId = userId; + CharacterId = characterId; + CharacterLevel = characterLevel; + CharacterExp = characterExp; + NormalSkillLevel = normalSkillLevel; + UltimateSkillLevel = ultimateSkillLevel; + SupportSkillLevel = supportSkillLevel; + PieceAmount = pieceAmount; + } + + public static PlayerCharacter Create(int userId, int characterId, CharacterRole role) + { + return role switch + { + CharacterRole.Main => CreateMain(userId, characterId), + CharacterRole.Support => CreateSupport(userId, characterId), + _ => throw new ArgumentException("Invalid character role") + }; + } + + private static PlayerCharacter CreateMain(int userId, int characterId) + { + return new PlayerCharacter(userId, characterId, + characterLevel: 1, characterExp: 0, + normalSkillLevel: 1, ultimateSkillLevel: 1, supportSkillLevel: 0, + pieceAmount: 0); + } + + private static PlayerCharacter CreateSupport(int userId, int characterId) + { + return new PlayerCharacter(userId, characterId, + characterLevel: 1, characterExp: 0, + normalSkillLevel: 1, ultimateSkillLevel: 0, supportSkillLevel: 1, + pieceAmount: 0); + } + + public void AddPiece(int amount) + { + if (amount <= 0) + throw new ArgumentException("Piece amount must be positive"); + + PieceAmount += amount; + } + + public void UpgradeNormalSkill(int maxLevel) + { + if (NormalSkillLevel >= maxLevel) + throw new InvalidOperationException("Normal skill is already at max level"); + + NormalSkillLevel++; + } + + public void UpgradeUltimateSkill(int maxLevel) + { + if (UltimateSkillLevel >= maxLevel) + throw new InvalidOperationException("Ultimate skill is already at max level"); + + UltimateSkillLevel++; + } + + public void UpgradeSupportSkill(int maxLevel) + { + if (SupportSkillLevel >= maxLevel) + throw new InvalidOperationException("Support skill is already at max level"); + + SupportSkillLevel++; + } + + public void GainExp(int amount, Action onLevelUp) + { + if (amount <= 0) + throw new ArgumentException("Exp amount must be positive"); + + CharacterExp += amount; + onLevelUp?.Invoke(CharacterLevel); + } +} \ No newline at end of file diff --git a/PaperMania/Server/Infrastructure/Persistence/Model/PlayerCharacterData.cs b/PaperMania/Server/Infrastructure/Persistence/Model/PlayerCharacterData.cs index 173f5ce..f320ced 100644 --- a/PaperMania/Server/Infrastructure/Persistence/Model/PlayerCharacterData.cs +++ b/PaperMania/Server/Infrastructure/Persistence/Model/PlayerCharacterData.cs @@ -1,11 +1,21 @@ namespace Server.Infrastructure.Persistence.Model; -public record PlayerCharacterData( - int UserId, - int CharacterId, - int CharacterLevel = 1, - int CharacterExp = 0, - int NormalSkillLevel = 1, - int UltimateSkillLevel = 1, - int SupportSkillLevel = 1 -); \ No newline at end of file +public class PlayerCharacterData +{ + public int UserId { get; set; } + public int CharacterId { get; set; } + public int CharacterLevel { get; set; } = 1; + public int CharacterExp { get; set; } = 0; + public int NormalSkillLevel { get; set; } + public int UltimateSkillLevel { get; set; } + public int SupportSkillLevel { get; set; } + public int PieceAmount { get; set; } + + public PlayerCharacterData() { } + + public PlayerCharacterData(int userId, int characterId) + { + UserId = userId; + CharacterId = characterId; + } +} \ No newline at end of file diff --git a/PaperMania/Server/Infrastructure/Persistence/Model/PlayerCharacterPieceData.cs b/PaperMania/Server/Infrastructure/Persistence/Model/PlayerCharacterPieceData.cs index 923a1c1..de7e124 100644 --- a/PaperMania/Server/Infrastructure/Persistence/Model/PlayerCharacterPieceData.cs +++ b/PaperMania/Server/Infrastructure/Persistence/Model/PlayerCharacterPieceData.cs @@ -1,7 +1,8 @@ namespace Server.Infrastructure.Persistence.Model; -public record PlayerCharacterPieceData( - int UserId, - int CharacterId, - int PieceAmount -); \ No newline at end of file +public class PlayerCharacterPieceData +{ + public int UserId { get; set; } + public int CharacterId { get; set; } + public int PieceAmount { get; set; } +} \ No newline at end of file diff --git a/PaperMania/Server/Infrastructure/Persistence/Repository/CharacterRepository.cs b/PaperMania/Server/Infrastructure/Persistence/Repository/CharacterRepository.cs index d9dfc49..b4cae1e 100644 --- a/PaperMania/Server/Infrastructure/Persistence/Repository/CharacterRepository.cs +++ b/PaperMania/Server/Infrastructure/Persistence/Repository/CharacterRepository.cs @@ -1,6 +1,7 @@ using Dapper; using Server.Application.Port.Output.Persistence; using Server.Application.Port.Output.Transaction; +using Server.Domain.Entity; using Server.Infrastructure.Persistence.Model; namespace Server.Infrastructure.Persistence.Repository; @@ -86,46 +87,95 @@ public CharacterRepository( ) : base(connectionString, transactionScope) { } + + private static PlayerCharacter MapToEntity(PlayerCharacterData data) + { + return new PlayerCharacter( + data.UserId, + data.CharacterId, + data.CharacterLevel, + data.CharacterExp, + data.NormalSkillLevel, + data.UltimateSkillLevel, + data.SupportSkillLevel, + data.PieceAmount + ); + } + + private static PlayerCharacterData MapToData(PlayerCharacter entity) + { + return new PlayerCharacterData + { + UserId = entity.UserId, + CharacterId = entity.CharacterId, + CharacterLevel = entity.CharacterLevel, + CharacterExp = entity.CharacterExp, + NormalSkillLevel = entity.NormalSkillLevel, + UltimateSkillLevel = entity.UltimateSkillLevel, + SupportSkillLevel = entity.SupportSkillLevel, + PieceAmount = entity.PieceAmount + }; + } + + private static PlayerCharacterPieceData MapToPieceData(PlayerCharacter entity) + { + return new PlayerCharacterPieceData + { + UserId = entity.UserId, + CharacterId = entity.CharacterId, + PieceAmount = entity.PieceAmount + }; + } - public async Task> FindAll(int userId, CancellationToken ct) + public async Task> FindAll(int userId, CancellationToken ct) { - return await QueryAsync(conn => + var rows = await QueryAsync(conn => conn.QueryAsync( new CommandDefinition(Sql.GetAllByUserId, new { UserId = userId }, cancellationToken: ct) ), ct); + + return rows.Select(MapToEntity); } - public async Task FindCharacter(int userId, int characterId, CancellationToken ct) + public async Task FindCharacter(int userId, int characterId, CancellationToken ct) { - return await QueryAsync(conn => + var row = await QueryAsync(conn => conn.QuerySingleOrDefaultAsync( new CommandDefinition(Sql.GetByUserId, new { UserId = userId, CharacterId = characterId }, cancellationToken: ct) ), ct); + + return MapToEntity(row); } - public async Task UpdateAsync(PlayerCharacterData data, CancellationToken ct) + public async Task UpdateAsync(PlayerCharacter entity, CancellationToken ct) { + var data = MapToData(entity); + await ExecuteAsync((connection, transaction) => connection.ExecuteAsync( new CommandDefinition(Sql.UpdateData, data, transaction: transaction, cancellationToken: ct) ), ct); - return data; + return entity; } - public async Task CreateAsync(PlayerCharacterData data, CancellationToken ct) + public async Task CreateAsync(PlayerCharacter entity, CancellationToken ct) { + var data = MapToData(entity); + await ExecuteAsync((connection, transaction) => connection.ExecuteAsync( new CommandDefinition(Sql.createData, data, transaction: transaction, cancellationToken: ct) ), ct); } - public async Task CreatePieceData(PlayerCharacterPieceData data, CancellationToken ct) + public async Task CreatePieceData(PlayerCharacter entity, CancellationToken ct) { + var pieceData = MapToPieceData(entity); + await ExecuteAsync((connection, transaction) => connection.ExecuteAsync( - new CommandDefinition(Sql.CreatePieceData, data, transaction: transaction, cancellationToken: ct) + new CommandDefinition(Sql.CreatePieceData, pieceData, transaction: transaction, cancellationToken: ct) ), ct); } } \ No newline at end of file