Start implementing card
This commit is contained in:
parent
f7761f200f
commit
9c759c3b1a
@ -11,11 +11,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ChoETL" Version="1.2.1.52" />
|
||||
<PackageReference Include="MediatR" Version="11.1.0" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
|
||||
<PackageReference Include="Riok.Mapperly" Version="2.7.0-next.2" />
|
||||
<PackageReference Include="Throw" Version="1.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
24
Application/Common/Behaviours/LoggingBehaviour.cs
Normal file
24
Application/Common/Behaviours/LoggingBehaviour.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using MediatR.Pipeline;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Application.Common.Behaviours;
|
||||
|
||||
public class LoggingBehaviour<TRequest> : IRequestPreProcessor<TRequest> where TRequest : notnull
|
||||
{
|
||||
private readonly ILogger<TRequest> logger;
|
||||
|
||||
// ReSharper disable once ContextualLoggerProblem
|
||||
public LoggingBehaviour(ILogger<TRequest> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public Task Process(TRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var requestName = typeof(TRequest).Name;
|
||||
|
||||
logger.LogInformation("Received request: {RequestName}, content: {Request}", requestName, request);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
33
Application/Common/Behaviours/UnhandledExceptionBehaviour.cs
Normal file
33
Application/Common/Behaviours/UnhandledExceptionBehaviour.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Application.Common.Behaviours;
|
||||
|
||||
public class UnhandledExceptionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
{
|
||||
private readonly ILogger<TRequest> logger;
|
||||
|
||||
// ReSharper disable once ContextualLoggerProblem
|
||||
public UnhandledExceptionBehaviour(ILogger<TRequest> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await next();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var requestName = typeof(TRequest).Name;
|
||||
|
||||
logger.LogError(ex, "Unhandled Exception for Request {Name} {@Request}", requestName, request);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
8
Application/Common/Exceptions/CardExistsException.cs
Normal file
8
Application/Common/Exceptions/CardExistsException.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Application.Common.Exceptions;
|
||||
|
||||
public class CardExistsException : Exception
|
||||
{
|
||||
public CardExistsException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
17
Application/Common/Extensions/XmlSerializationExtensions.cs
Normal file
17
Application/Common/Extensions/XmlSerializationExtensions.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using ChoETL;
|
||||
using Throw;
|
||||
|
||||
namespace Application.Common;
|
||||
|
||||
public static class XmlSerializationExtensions
|
||||
{
|
||||
public static T DeserializeCardData<T>(this string source) where T : class
|
||||
{
|
||||
using var reader = new ChoXmlReader<T>(new StringReader(source)).WithXPath("/root/data");
|
||||
|
||||
var result = reader.Read();
|
||||
result.ThrowIfNull();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
140
Application/Common/Models/ServiceError.cs
Normal file
140
Application/Common/Models/ServiceError.cs
Normal file
@ -0,0 +1,140 @@
|
||||
namespace Application.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// All errors contained in ServiceResult objects must return an error of this type
|
||||
/// Error codes allow the caller to easily identify the received error and take action.
|
||||
/// Error messages allow the caller to easily show error messages to the end user.
|
||||
///
|
||||
/// Taken from https://github.com/iayti/CleanArchitecture/blob/master/src/Common/CleanArchitecture.Application/Common/Models/ServiceError.cs
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ServiceError
|
||||
{
|
||||
/// <summary>
|
||||
/// CTOR
|
||||
/// </summary>
|
||||
public ServiceError(string message, int code)
|
||||
{
|
||||
Message = message;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
public ServiceError()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Human readable error message
|
||||
/// </summary>
|
||||
public string Message { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Machine readable error code
|
||||
/// </summary>
|
||||
public int Code { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Default error for when we receive an exception
|
||||
/// </summary>
|
||||
public static ServiceError DefaultError => new("An unknown exception occured.", 999);
|
||||
|
||||
/// <summary>
|
||||
/// Default validation error. Use this for invalid parameters in controller actions and service methods.
|
||||
/// </summary>
|
||||
public static ServiceError ModelStateError(string validationError)
|
||||
{
|
||||
return new ServiceError(validationError, 998);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this for unauthorized responses.
|
||||
/// </summary>
|
||||
public static ServiceError ForbiddenError => new("You are not authorized to call this action.", 998);
|
||||
|
||||
/// <summary>
|
||||
/// Use this to send a custom error message
|
||||
/// </summary>
|
||||
public static ServiceError CustomMessage(string errorMessage)
|
||||
{
|
||||
return new ServiceError(errorMessage, 997);
|
||||
}
|
||||
|
||||
public static ServiceError UserNotFound => new("User with this id does not exist", 996);
|
||||
|
||||
public static ServiceError UserFailedToCreate => new("Failed to create User.", 995);
|
||||
|
||||
public static ServiceError Canceled => new("The request canceled successfully!", 994);
|
||||
|
||||
public static ServiceError NotFound => new("The specified resource was not found.", 990);
|
||||
|
||||
public static ServiceError ValidationFormat => new("Request object format is not true.", 901);
|
||||
|
||||
public static ServiceError Validation => new("One or more validation errors occurred.", 900);
|
||||
|
||||
public static ServiceError SearchAtLeastOneCharacter =>
|
||||
new("Search parameter must have at least one character!", 898);
|
||||
|
||||
/// <summary>
|
||||
/// Default error for when we receive an exception
|
||||
/// </summary>
|
||||
public static ServiceError ServiceProviderNotFound =>
|
||||
new("Service Provider with this name does not exist.", 700);
|
||||
|
||||
public static ServiceError ServiceProvider => new("Service Provider failed to return as expected.", 600);
|
||||
|
||||
public static ServiceError DateTimeFormatError =>
|
||||
new("Date format is not true. Date format must be like yyyy-MM-dd (2019-07-19)", 500);
|
||||
|
||||
#region Override Equals Operator
|
||||
|
||||
/// <summary>
|
||||
/// Use this to compare if two errors are equal
|
||||
/// Ref: https://msdn.microsoft.com/ru-ru/library/ms173147(v=vs.80).aspx
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
// If parameter cannot be cast to ServiceError or is null return false.
|
||||
var error = obj as ServiceError;
|
||||
|
||||
// Return true if the error codes match. False if the object we're comparing to is null
|
||||
// or if it has a different code.
|
||||
return Code == error?.Code;
|
||||
}
|
||||
|
||||
public bool Equals(ServiceError error)
|
||||
{
|
||||
// Return true if the error codes match. False if the object we're comparing to is null
|
||||
// or if it has a different code.
|
||||
return Code == error?.Code;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Code;
|
||||
}
|
||||
|
||||
public static bool operator ==(ServiceError? a, ServiceError? b)
|
||||
{
|
||||
// If both are null, or both are same instance, return true.
|
||||
if (ReferenceEquals(a, b))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If one is null, but not both, return false.
|
||||
if (a is null || b is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if the fields match:
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(ServiceError a, ServiceError b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
65
Application/Common/Models/ServiceResult.cs
Normal file
65
Application/Common/Models/ServiceResult.cs
Normal file
@ -0,0 +1,65 @@
|
||||
namespace Application.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A standard response for service calls.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return data type</typeparam>
|
||||
public class ServiceResult<T> : ServiceResult
|
||||
{
|
||||
public T? Data { get; set; }
|
||||
|
||||
public ServiceResult(T? data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public ServiceResult(T? data, ServiceError error) : base(error)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public ServiceResult(ServiceError error) : base(error)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class ServiceResult
|
||||
{
|
||||
public bool Succeeded => Error == null;
|
||||
|
||||
public ServiceError? Error { get; set; }
|
||||
|
||||
public ServiceResult(ServiceError? error)
|
||||
{
|
||||
error ??= ServiceError.DefaultError;
|
||||
|
||||
Error = error;
|
||||
}
|
||||
|
||||
public ServiceResult() { }
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
public static ServiceResult Failed(ServiceError error)
|
||||
{
|
||||
return new ServiceResult(error);
|
||||
}
|
||||
|
||||
public static ServiceResult<T> Failed<T>(ServiceError error)
|
||||
{
|
||||
return new ServiceResult<T>(error);
|
||||
}
|
||||
|
||||
public static ServiceResult<T> Failed<T>(T data, ServiceError error)
|
||||
{
|
||||
return new ServiceResult<T>(data, error);
|
||||
}
|
||||
|
||||
public static ServiceResult<T> Success<T>(T data)
|
||||
{
|
||||
return new ServiceResult<T>(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
using System.Reflection;
|
||||
using Application.Common.Behaviours;
|
||||
using Application.Game.Card;
|
||||
using Application.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -10,6 +13,8 @@ public static class DependencyInjection
|
||||
{
|
||||
services.AddMediatR(Assembly.GetExecutingAssembly());
|
||||
|
||||
services.AddScoped<ICardDependencyAggregate, CardDependencyAggregate>();
|
||||
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
|
||||
return services;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Xml.Serialization;
|
||||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Application.Dto;
|
||||
|
||||
@ -8,6 +9,7 @@ public class CardDto
|
||||
public long CardId { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "player_name")]
|
||||
[DefaultValue("")]
|
||||
public string PlayerName { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement("score_i1")]
|
||||
@ -23,11 +25,14 @@ public class CardDto
|
||||
public long Fcol3 { get; set; }
|
||||
|
||||
[XmlElement("achieve_status")]
|
||||
[DefaultValue("")]
|
||||
public string AchieveStatus { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement("created")]
|
||||
[DefaultValue("")]
|
||||
public string Created { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement("modified")]
|
||||
[DefaultValue("")]
|
||||
public string Modified { get; set; } = string.Empty;
|
||||
}
|
15
Application/Game/Card/CardDependencyAggregate.cs
Normal file
15
Application/Game/Card/CardDependencyAggregate.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Application.Interfaces;
|
||||
|
||||
namespace Application.Game.Card;
|
||||
|
||||
public class CardDependencyAggregate : ICardDependencyAggregate
|
||||
{
|
||||
public CardDependencyAggregate(ICardDbContext cardDbContext, IMusicDbContext musicDbContext)
|
||||
{
|
||||
CardDbContext = cardDbContext;
|
||||
MusicDbContext = musicDbContext;
|
||||
}
|
||||
|
||||
public ICardDbContext CardDbContext { get; }
|
||||
public IMusicDbContext MusicDbContext { get; }
|
||||
}
|
36
Application/Game/Card/CardRegisterCommand.cs
Normal file
36
Application/Game/Card/CardRegisterCommand.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using Application.Common;
|
||||
using Application.Common.Exceptions;
|
||||
using Application.Common.Models;
|
||||
using Application.Dto;
|
||||
using Application.Interfaces;
|
||||
using Application.Mappers;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Throw;
|
||||
|
||||
namespace Application.Game.Card;
|
||||
|
||||
public record CardRegisterCommand(long CardId, string Data) : IRequestWrapper<string>;
|
||||
|
||||
public class CardRegisterCommandHandler : CardRequestHandlerBase<CardRegisterCommand, string>
|
||||
{
|
||||
public CardRegisterCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<ServiceResult<string>> Handle(CardRegisterCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var exists = CardDbContext.CardMains.Any(card => card.CardId == request.CardId);
|
||||
if (!exists)
|
||||
{
|
||||
return ServiceResult.Failed<string>(ServiceError.CustomMessage($"Card {request.CardId} already exists!"));
|
||||
}
|
||||
|
||||
var card = request.Data.DeserializeCardData<CardDto>().CardDtoToCardMain();
|
||||
card.CardId = request.CardId;
|
||||
CardDbContext.CardMains.Add(card);
|
||||
await CardDbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new ServiceResult<string>(request.Data);
|
||||
}
|
||||
}
|
23
Application/Game/Card/CardRequest.cs
Normal file
23
Application/Game/Card/CardRequest.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using Domain;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Application.Game.Card;
|
||||
|
||||
public class CardRequest
|
||||
{
|
||||
[ModelBinder(Name = "mac_addr")]
|
||||
public string Mac { get; set; } = string.Empty;
|
||||
|
||||
[ModelBinder(Name = "cmd_str")]
|
||||
public int CardCommandType { get; set; }
|
||||
|
||||
[ModelBinder(Name = "type")]
|
||||
public int CardRequestType { get; set; }
|
||||
|
||||
[ModelBinder(Name = "card_no")]
|
||||
public long CardId { get; set; }
|
||||
|
||||
[ModelBinder(Name = "data")]
|
||||
public string Data { get; set; } = string.Empty;
|
||||
}
|
21
Application/Game/Card/CardRequestHandlerBase.cs
Normal file
21
Application/Game/Card/CardRequestHandlerBase.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using Application.Common.Models;
|
||||
using Application.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Application.Game.Card;
|
||||
|
||||
public abstract class CardRequestHandlerBase<TIn, TOut>: IRequestHandlerWrapper<TIn, TOut>
|
||||
where TIn : IRequestWrapper<TOut>
|
||||
{
|
||||
public ICardDbContext CardDbContext { get; }
|
||||
public IMusicDbContext MusicDbContext { get; }
|
||||
|
||||
public CardRequestHandlerBase(ICardDependencyAggregate aggregate)
|
||||
{
|
||||
CardDbContext = aggregate.CardDbContext;
|
||||
MusicDbContext = aggregate.MusicDbContext;
|
||||
}
|
||||
|
||||
public abstract Task<ServiceResult<TOut>> Handle(TIn request, CancellationToken cancellationToken);
|
||||
}
|
9
Application/Interfaces/ICardDependencyAggregate.cs
Normal file
9
Application/Interfaces/ICardDependencyAggregate.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Application.Interfaces;
|
||||
|
||||
public interface ICardDependencyAggregate
|
||||
{
|
||||
ICardDbContext CardDbContext { get; }
|
||||
IMusicDbContext MusicDbContext { get; }
|
||||
}
|
14
Application/Interfaces/IRequestWrapper.cs
Normal file
14
Application/Interfaces/IRequestWrapper.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using Application.Common.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace Application.Interfaces;
|
||||
|
||||
public interface IRequestWrapper<T> : IRequest<ServiceResult<T>>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IRequestHandlerWrapper<TIn, TOut> : IRequestHandler<TIn, ServiceResult<TOut>> where TIn : IRequestWrapper<TOut>
|
||||
{
|
||||
|
||||
}
|
@ -8,4 +8,6 @@ namespace Application.Mappers;
|
||||
public static partial class CardMapper
|
||||
{
|
||||
public static partial CardDto CardMainToCardDto(this CardMain cardMain);
|
||||
|
||||
public static partial CardMain CardDtoToCardMain(this CardDto cardDto);
|
||||
}
|
20
Domain/Config/GameConfig.cs
Normal file
20
Domain/Config/GameConfig.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace Domain.Config;
|
||||
|
||||
public class GameConfig
|
||||
{
|
||||
public const string GAME_SECTION = "Game";
|
||||
|
||||
public int AvatarCount { get; set; }
|
||||
|
||||
public int NavigatorCount { get; set; }
|
||||
|
||||
public int ItemCount { get; set; }
|
||||
|
||||
public int SkinCount { get; set; }
|
||||
|
||||
public int SeCount { get; set; }
|
||||
|
||||
public int TitleCount { get; set; }
|
||||
|
||||
public List<int> UnlockableSongIds { get; set; } = new();
|
||||
}
|
9
Domain/Enums/CardCommandType.cs
Normal file
9
Domain/Enums/CardCommandType.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Domain;
|
||||
|
||||
public enum CardCommandType
|
||||
{
|
||||
CardReadRequest = 256,
|
||||
CardWriteRequest = 768,
|
||||
RegisterRequest = 512,
|
||||
ReissueRequest = 1536
|
||||
}
|
55
Domain/Enums/CardRequestType.cs
Normal file
55
Domain/Enums/CardRequestType.cs
Normal file
@ -0,0 +1,55 @@
|
||||
namespace Domain;
|
||||
|
||||
public enum CardRequestType
|
||||
{
|
||||
#region Read
|
||||
ReadCard = 259,
|
||||
ReadCardDetail = 260,
|
||||
ReadCardDetails = 261,
|
||||
ReadCardBData = 264,
|
||||
ReadAvatar = 418,
|
||||
ReadItem = 420,
|
||||
ReadSkin = 422,
|
||||
ReadTitle = 424,
|
||||
ReadMusic = 428,
|
||||
ReadEventReward = 441,
|
||||
ReadNavigator = 443,
|
||||
ReadMusicExtra = 465,
|
||||
ReadMusicAou = 467,
|
||||
ReadCoin = 468,
|
||||
ReadUnlockReward = 507,
|
||||
ReadUnlockKeynum = 509,
|
||||
ReadSoundEffect = 8458,
|
||||
ReadGetMessage = 8461,
|
||||
ReadCond = 8465,
|
||||
ReadTotalTrophy = 8468,
|
||||
#endregion
|
||||
|
||||
#region Session
|
||||
GetSession = 401,
|
||||
StartSession = 402,
|
||||
#endregion
|
||||
|
||||
|
||||
#region Write
|
||||
WriteCard = 771,
|
||||
WriteCardDetail = 772,
|
||||
WriteCardBData = 776,
|
||||
WriteAvatar = 929,
|
||||
WriteItem = 931,
|
||||
WriteTitle = 935,
|
||||
WriteMusicDetail = 941,
|
||||
WriteNavigator = 954,
|
||||
WriteCoin = 980,
|
||||
WriteSkin = 933,
|
||||
WriteUnlockKeynum = 1020,
|
||||
WriteSoundEffect = 8969,
|
||||
#endregion
|
||||
|
||||
|
||||
#region Online Matching
|
||||
StartOnlineMatching = 8705,
|
||||
UpdateOnlineMatching = 8961,
|
||||
UploadOnlineMatchingResult = 8709,
|
||||
#endregion
|
||||
}
|
27
Domain/Enums/CardReturnCode.cs
Normal file
27
Domain/Enums/CardReturnCode.cs
Normal file
@ -0,0 +1,27 @@
|
||||
namespace Domain;
|
||||
|
||||
public enum CardReturnCode
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal
|
||||
/// 処理は正常に完了しました in debug string
|
||||
/// </summary>
|
||||
Ok = 1,
|
||||
|
||||
/// <summary>
|
||||
/// New card
|
||||
/// 未登録のカードです in debug string
|
||||
/// </summary>
|
||||
CardNotRegistered = 23,
|
||||
|
||||
/// <summary>
|
||||
/// Not reissue, to determine whether it is a new card or reissued card
|
||||
/// 再発行予約がありません in debug string
|
||||
/// </summary>
|
||||
NotReissue = 27,
|
||||
|
||||
/// <summary>
|
||||
/// Server side validation error
|
||||
/// </summary>
|
||||
Unknown = 999
|
||||
}
|
@ -9,6 +9,8 @@ namespace Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (!MigrationHelper.Exists("card_bdata"))
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "card_bdata",
|
||||
@ -22,7 +24,10 @@ namespace Infrastructure.Migrations
|
||||
{
|
||||
table.PrimaryKey("PK_card_bdata", x => x.cardid);
|
||||
});
|
||||
}
|
||||
|
||||
if (!MigrationHelper.Exists("card_detail"))
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "card_detail",
|
||||
columns: table => new
|
||||
@ -49,7 +54,10 @@ namespace Infrastructure.Migrations
|
||||
{
|
||||
table.PrimaryKey("PK_card_detail", x => new { x.cardid, x.pcol1, x.pcol2, x.pcol3 });
|
||||
});
|
||||
}
|
||||
|
||||
if (!MigrationHelper.Exists("card_main"))
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "card_main",
|
||||
columns: table => new
|
||||
@ -68,7 +76,10 @@ namespace Infrastructure.Migrations
|
||||
{
|
||||
table.PrimaryKey("PK_card_main", x => x.cardid);
|
||||
});
|
||||
}
|
||||
|
||||
if (!MigrationHelper.Exists("CardPlayCount"))
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CardPlayCount",
|
||||
columns: table => new
|
||||
@ -82,6 +93,7 @@ namespace Infrastructure.Migrations
|
||||
table.PrimaryKey("PK_CardPlayCount", x => x.cardid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
|
@ -21,13 +21,13 @@ public static class MigrationHelper
|
||||
return reader.Read()? (long)reader[0] == 1 : false;
|
||||
}
|
||||
|
||||
public static string GetConnectionString()
|
||||
private static string GetConnectionString()
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(PathHelper.ConfigurationPath)
|
||||
.AddJsonFile("database.json", optional: false, reloadOnChange: false);
|
||||
|
||||
var cardDbName = builder.Build()["CardDbName"];
|
||||
var cardDbName = builder.Build()["CardDbName"] ?? "card.db3";
|
||||
var cardDbPath = Path.Combine(PathHelper.DatabasePath, cardDbName);
|
||||
return cardDbPath;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"Game": {
|
||||
"AvatarCount": 356,
|
||||
"NavigatorCount": 118,
|
||||
"ItemCount": 21,
|
||||
@ -12,4 +13,5 @@
|
||||
632, 659, 666, 668, 670, 672, 676, 680, 682, 685, 686, 697, 700, 701,
|
||||
711, 720, 749, 875, 876, 877
|
||||
]
|
||||
}
|
||||
}
|
@ -20,5 +20,20 @@ namespace MainServer.Controllers.API
|
||||
{
|
||||
return context.MusicUnlocks.First();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult<string> TestXmlInputOutput([FromForm(Name = "my_model")]TestModel model,
|
||||
[FromForm(Name = "my_type")]int type)
|
||||
{
|
||||
return Ok($"{model.Name}\n{model.Age}\n{type}");
|
||||
}
|
||||
}
|
||||
|
||||
public class TestModel
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
134
MainServer/Controllers/Game/CardController.cs
Normal file
134
MainServer/Controllers/Game/CardController.cs
Normal file
@ -0,0 +1,134 @@
|
||||
using System.Net;
|
||||
using Application.Common.Models;
|
||||
using Application.Game.Card;
|
||||
using Domain;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Throw;
|
||||
|
||||
namespace MainServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("service/card")]
|
||||
public class CardController : BaseController<CardController>
|
||||
{
|
||||
[HttpPost("cardn.cgi")]
|
||||
public async Task<ActionResult<string>> CardService([FromForm]CardRequest request)
|
||||
{
|
||||
var cardRequestType = (CardRequestType)request.CardRequestType;
|
||||
var cardCommandType = (CardCommandType)request.CardCommandType;
|
||||
|
||||
cardCommandType.Throw().IfOutOfRange();
|
||||
if (cardCommandType is CardCommandType.CardReadRequest or CardCommandType.CardWriteRequest)
|
||||
{
|
||||
cardRequestType.Throw().IfOutOfRange();
|
||||
}
|
||||
|
||||
request.Data = WebUtility.UrlDecode(request.Data);
|
||||
var result = ServiceResult.Failed<string>(ServiceError.DefaultError);
|
||||
switch (cardCommandType)
|
||||
{
|
||||
case CardCommandType.CardReadRequest:
|
||||
{
|
||||
switch (cardRequestType)
|
||||
{
|
||||
case CardRequestType.ReadCard:
|
||||
break;
|
||||
case CardRequestType.ReadCardDetail:
|
||||
break;
|
||||
case CardRequestType.ReadCardDetails:
|
||||
break;
|
||||
case CardRequestType.ReadCardBData:
|
||||
break;
|
||||
case CardRequestType.ReadAvatar:
|
||||
break;
|
||||
case CardRequestType.ReadItem:
|
||||
break;
|
||||
case CardRequestType.ReadSkin:
|
||||
break;
|
||||
case CardRequestType.ReadTitle:
|
||||
break;
|
||||
case CardRequestType.ReadMusic:
|
||||
break;
|
||||
case CardRequestType.ReadEventReward:
|
||||
break;
|
||||
case CardRequestType.ReadNavigator:
|
||||
break;
|
||||
case CardRequestType.ReadMusicExtra:
|
||||
break;
|
||||
case CardRequestType.ReadMusicAou:
|
||||
break;
|
||||
case CardRequestType.ReadCoin:
|
||||
break;
|
||||
case CardRequestType.ReadUnlockReward:
|
||||
break;
|
||||
case CardRequestType.ReadUnlockKeynum:
|
||||
break;
|
||||
case CardRequestType.ReadSoundEffect:
|
||||
break;
|
||||
case CardRequestType.ReadGetMessage:
|
||||
break;
|
||||
case CardRequestType.ReadCond:
|
||||
break;
|
||||
case CardRequestType.ReadTotalTrophy:
|
||||
break;
|
||||
case CardRequestType.GetSession:
|
||||
break;
|
||||
case CardRequestType.StartSession:
|
||||
break;
|
||||
case CardRequestType.WriteCard:
|
||||
|
||||
break;
|
||||
case CardRequestType.WriteCardDetail:
|
||||
break;
|
||||
case CardRequestType.WriteCardBData:
|
||||
break;
|
||||
case CardRequestType.WriteAvatar:
|
||||
break;
|
||||
case CardRequestType.WriteItem:
|
||||
break;
|
||||
case CardRequestType.WriteTitle:
|
||||
break;
|
||||
case CardRequestType.WriteMusicDetail:
|
||||
break;
|
||||
case CardRequestType.WriteNavigator:
|
||||
break;
|
||||
case CardRequestType.WriteCoin:
|
||||
break;
|
||||
case CardRequestType.WriteSkin:
|
||||
break;
|
||||
case CardRequestType.WriteUnlockKeynum:
|
||||
break;
|
||||
case CardRequestType.WriteSoundEffect:
|
||||
break;
|
||||
case CardRequestType.StartOnlineMatching:
|
||||
break;
|
||||
case CardRequestType.UpdateOnlineMatching:
|
||||
break;
|
||||
case CardRequestType.UploadOnlineMatchingResult:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(message: "Should not happen", paramName:null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CardCommandType.CardWriteRequest:
|
||||
break;
|
||||
case CardCommandType.RegisterRequest:
|
||||
result = await Mediator.Send(new CardRegisterCommand(request.CardId, request.Data));
|
||||
break;
|
||||
case CardCommandType.ReissueRequest:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(message: "Should not happen", paramName:null);
|
||||
}
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return Ok(result.Data);
|
||||
}
|
||||
|
||||
var errorMessage = $"{(int)CardReturnCode.Unknown}\n" +
|
||||
$"{result.Error!.Message}";
|
||||
return Ok(errorMessage);
|
||||
}
|
||||
}
|
81
MainServer/Filters/ApiExceptionFilterAttributes.cs
Normal file
81
MainServer/Filters/ApiExceptionFilterAttributes.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System.Diagnostics;
|
||||
using Application.Common.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace MainServer.Filters;
|
||||
|
||||
/// <summary>
|
||||
/// Exception filters
|
||||
/// </summary>
|
||||
public class ApiExceptionFilterService : ExceptionFilterAttribute
|
||||
{
|
||||
private readonly IDictionary<Type, Action<ExceptionContext>> exceptionHandlers;
|
||||
|
||||
private readonly ILogger<ApiExceptionFilterService> logger;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public ApiExceptionFilterService(ILogger<ApiExceptionFilterService> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
// Register known exception types and handlers.
|
||||
exceptionHandlers = new Dictionary<Type, Action<ExceptionContext>>
|
||||
{
|
||||
{ typeof(ArgumentOutOfRangeException), HandleArgumentOutOfRangeException }
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On exception event
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public override void OnException(ExceptionContext context)
|
||||
{
|
||||
HandleException(context);
|
||||
|
||||
base.OnException(context);
|
||||
}
|
||||
|
||||
private void HandleException(ExceptionContext context)
|
||||
{
|
||||
var type = context.Exception.GetType();
|
||||
if (exceptionHandlers.ContainsKey(type))
|
||||
{
|
||||
exceptionHandlers[type].Invoke(context);
|
||||
return;
|
||||
}
|
||||
|
||||
HandleUnknownException(context);
|
||||
}
|
||||
|
||||
private static void HandleUnknownException(ExceptionContext context)
|
||||
{
|
||||
var details = ServiceResult.Failed(ServiceError.DefaultError);
|
||||
|
||||
context.Result = new ObjectResult(details)
|
||||
{
|
||||
StatusCode = StatusCodes.Status500InternalServerError
|
||||
};
|
||||
|
||||
context.ExceptionHandled = true;
|
||||
}
|
||||
|
||||
private void HandleArgumentOutOfRangeException(ExceptionContext context)
|
||||
{
|
||||
logger.LogError(context.Exception, "");
|
||||
var exception = context.Exception as ArgumentOutOfRangeException;
|
||||
Debug.Assert(exception != null, nameof(exception) + " != null");
|
||||
|
||||
var variable = exception.ParamName ?? "Unknown";
|
||||
var details = ServiceResult.Failed(ServiceError.CustomMessage($"Argument {variable} out of bounds!"));
|
||||
|
||||
context.Result = new ObjectResult(details)
|
||||
{
|
||||
StatusCode = StatusCodes.Status400BadRequest
|
||||
};
|
||||
|
||||
context.ExceptionHandled = true;
|
||||
}
|
||||
}
|
@ -43,6 +43,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\API" />
|
||||
<Folder Include="Logs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -5,6 +5,7 @@ using Domain.Config;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Common;
|
||||
using Infrastructure.Persistence;
|
||||
using MainServer.Filters;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
@ -38,6 +39,8 @@ try
|
||||
builder.Configuration.GetSection(EventConfig.EVENT_SECTION));
|
||||
builder.Services.Configure<RelayConfig>(
|
||||
builder.Configuration.GetSection(RelayConfig.RELAY_SECTION));
|
||||
builder.Services.Configure<GameConfig>(
|
||||
builder.Configuration.GetSection(GameConfig.GAME_SECTION));
|
||||
|
||||
var serverIp = builder.Configuration["ServerIp"] ?? "127.0.0.1";
|
||||
var certificateManager = new CertificateService(serverIp, new SerilogLoggerFactory(Log.Logger).CreateLogger(""));
|
||||
@ -51,7 +54,9 @@ try
|
||||
configuration.WriteTo.Console().ReadFrom.Configuration(context.Configuration);
|
||||
});
|
||||
|
||||
builder.Services.AddControllers().AddXmlSerializerFormatters();
|
||||
builder.Services.AddControllers(options =>
|
||||
options.Filters.Add<ApiExceptionFilterService>());
|
||||
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
@ -81,20 +86,22 @@ try
|
||||
eventService.ThrowIfNull();
|
||||
eventService.InitializeEvents();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
// app.UseExceptionHandler();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Unhandled exception");
|
||||
Log.Fatal(ex, "Unhandled exception in startup");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user