1
0
mirror of synced 2024-11-24 06:20:12 +01:00

Basically finish migration for server, implement play number rank, wait for testing

This commit is contained in:
asesidaa 2023-02-15 02:14:19 +08:00
parent 02a241567c
commit 9d58e71e06
27 changed files with 774 additions and 28 deletions

View File

@ -18,12 +18,9 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
<PackageReference Include="Quartz.AspNetCore" Version="3.6.0" />
<PackageReference Include="Riok.Mapperly" Version="2.7.0-next.2" /> <PackageReference Include="Riok.Mapperly" Version="2.7.0-next.2" />
<PackageReference Include="Throw" Version="1.3.1" /> <PackageReference Include="Throw" Version="1.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Game\Rank" />
</ItemGroup>
</Project> </Project>

View File

@ -28,6 +28,20 @@ public static class XmlSerializationExtensions
} }
return buffer.ToString(); return buffer.ToString();
} }
public static string SerializeCardData<T>(this T source) where T : class
{
var buffer = new StringBuilder();
using (var writer = new ChoXmlWriter<T>(buffer).UseXmlSerialization())
{
writer.Configuration.OmitXmlDeclaration = false;
writer.Configuration.DoNotEmitXmlNamespace = true;
writer.Configuration.IgnoreRootName = true;
writer.Write(source);
}
return buffer.ToString();
}
public static string SerializeCardDataList<T>(this IEnumerable<T> source, string xpath) where T : class public static string SerializeCardDataList<T>(this IEnumerable<T> source, string xpath) where T : class
{ {
var buffer = new StringBuilder(); var buffer = new StringBuilder();

View File

@ -6,4 +6,9 @@ public static class TimeHelper
{ {
return DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"); return DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
} }
public static string DateToString(DateTime time)
{
return time.ToString("yyyy-MM-dd");
}
} }

View File

@ -2,8 +2,10 @@
using Application.Common.Behaviours; using Application.Common.Behaviours;
using Application.Game.Card; using Application.Game.Card;
using Application.Interfaces; using Application.Interfaces;
using Application.Jobs;
using MediatR; using MediatR;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Quartz;
namespace Application; namespace Application;
@ -15,6 +17,27 @@ public static class DependencyInjection
services.AddScoped<ICardDependencyAggregate, CardDependencyAggregate>(); services.AddScoped<ICardDependencyAggregate, CardDependencyAggregate>();
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
var jobKey = new JobKey("UpdatePlayNumRankJob");
q.AddJob<UpdatePlayNumRankJob>(options => options.WithIdentity(jobKey));
q.AddTrigger(options =>
{
options.ForJob(jobKey)
.WithIdentity("UpdatePlayNumRankJob-trigger")
.StartNow()
.WithSimpleSchedule(x =>
{
x.WithIntervalInHours(24).RepeatForever();
});
});
});
services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});
return services; return services;
} }
} }

View File

@ -0,0 +1,33 @@
using System.Xml.Serialization;
namespace Application.Dto;
public class PlayNumRankDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement("rank")]
public int Rank { get; set; }
[XmlElement("rank2")]
public int Rank2 { get; set; }
[XmlElement("prev_rank")]
public int PrevRank { get; set; }
[XmlElement("prev_rank2")]
public int PrevRank2 { get; set; }
[XmlElement("pcol1")]
public int Pcol1 { get; set; }
[XmlElement("score_bi1")]
public int ScoreBi1 { get; set; }
[XmlElement("title")]
public string Title { get; set; } = string.Empty;
[XmlElement("artist")]
public string Artist { get; set; } = string.Empty;
}

View File

@ -1,9 +1,6 @@
using Application.Common.Models; using Application.Common.Models;
using Application.Interfaces; using Application.Interfaces;
using Domain.Config; using Domain.Config;
using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Application.Game.Card; namespace Application.Game.Card;

View File

@ -1,6 +1,7 @@
using Application.Common.Extensions; using Application.Common.Extensions;
using Application.Common.Models; using Application.Common.Models;
using Application.Interfaces; using Application.Interfaces;
using Application.Mappers;
using Domain.Enums; using Domain.Enums;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -26,7 +27,7 @@ public class ReadCardQueryHandler : CardRequestHandlerBase<ReadCardQuery, string
return ServiceResult.Failed<string>(new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered)); return ServiceResult.Failed<string>(new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered));
} }
var result = card.SerializeCardData("/root/card"); var result = card.CardMainToCardDto().SerializeCardData("/root/card");
return new ServiceResult<string>(result); return new ServiceResult<string>(result);
} }

View File

@ -33,7 +33,7 @@ public class WriteCardBDataCommandHandler : CardRequestHandlerBase<WriteCardBDat
var dto = request.Data.DeserializeCardData<CardBDatumDto>(); var dto = request.Data.DeserializeCardData<CardBDatumDto>();
var data = dto.DtoToCardBDatum(); var data = dto.DtoToCardBDatum();
data.CardId = request.CardId; data.CardId = request.CardId;
CardDbContext.CardBdata.Upsert(data); await CardDbContext.CardBdata.Upsert(data).RunAsync(cancellationToken);
await CardDbContext.SaveChangesAsync(cancellationToken); await CardDbContext.SaveChangesAsync(cancellationToken);

View File

@ -34,7 +34,7 @@ public class WriteCardDetailCommandHandler : CardRequestHandlerBase<WriteCardDet
var detail = dto.DtoToCardDetail(); var detail = dto.DtoToCardDetail();
detail.CardId = request.CardId; detail.CardId = request.CardId;
detail.LastPlayTime = DateTime.Now; detail.LastPlayTime = DateTime.Now;
CardDbContext.CardDetails.Upsert(detail); await CardDbContext.CardDetails.Upsert(detail).RunAsync(cancellationToken);
await CardDbContext.SaveChangesAsync(cancellationToken); await CardDbContext.SaveChangesAsync(cancellationToken);

View File

@ -0,0 +1,43 @@
using System.Xml.Serialization;
using Application.Common.Extensions;
using Application.Common.Helpers;
using Application.Common.Models;
using Application.Interfaces;
namespace Application.Game.Rank;
public record GetEventRankQuery() : IRequestWrapper<string>;
public class GetEventRankQueryHandler : IRequestHandlerWrapper<GetEventRankQuery, string>
{
public Task<ServiceResult<string>> Handle(GetEventRankQuery request, CancellationToken cancellationToken)
{
var container = new EventRankContainer
{
Ranks = new List<object>(),
Status = new RankStatus
{
TableName = "TenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 0
}
};
return Task.FromResult(new ServiceResult<string>(container.SerializeCardData()));
}
}
[XmlRoot("root")]
public class EventRankContainer
{
[XmlArray(ElementName = "event_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<object> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}

View File

@ -0,0 +1,43 @@
using System.Xml.Serialization;
using Application.Common.Extensions;
using Application.Common.Helpers;
using Application.Common.Models;
using Application.Interfaces;
namespace Application.Game.Rank;
public record GetGlobalScoreRankQuery() : IRequestWrapper<string>;
public class GetGlobalScoreRankQueryHandler : IRequestHandlerWrapper<GetGlobalScoreRankQuery, string>
{
public Task<ServiceResult<string>> Handle(GetGlobalScoreRankQuery request, CancellationToken cancellationToken)
{
var container = new GlobalScoreRankContainer
{
Ranks = new List<object>(),
Status = new RankStatus
{
TableName = "TenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 0
}
};
return Task.FromResult(new ServiceResult<string>(container.SerializeCardData()));
}
}
[XmlRoot("root")]
public class GlobalScoreRankContainer
{
[XmlArray(ElementName = "score_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<object> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}

View File

@ -0,0 +1,43 @@
using System.Xml.Serialization;
using Application.Common.Extensions;
using Application.Common.Helpers;
using Application.Common.Models;
using Application.Interfaces;
namespace Application.Game.Rank;
public record GetMonthlyScoreRankQuery() : IRequestWrapper<string>;
public class GetMonthlyScoreRankQueryHandler : IRequestHandlerWrapper<GetMonthlyScoreRankQuery, string>
{
public Task<ServiceResult<string>> Handle(GetMonthlyScoreRankQuery request, CancellationToken cancellationToken)
{
var container = new MonthlyScoreRankContainer
{
Ranks = new List<object>(),
Status = new RankStatus
{
TableName = "TenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 0
}
};
return Task.FromResult(new ServiceResult<string>(container.SerializeCardData()));
}
}
[XmlRoot("root")]
public class MonthlyScoreRankContainer
{
[XmlArray(ElementName = "m_score_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<object> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}

View File

@ -0,0 +1,68 @@
using System.Diagnostics;
using System.Xml.Serialization;
using Application.Common.Extensions;
using Application.Common.Helpers;
using Application.Common.Models;
using Application.Dto;
using Application.Interfaces;
using Application.Mappers;
using Microsoft.EntityFrameworkCore;
namespace Application.Game.Rank;
public record GetPlayNumRankQuery(): IRequestWrapper<string>;
public class GetPlayNumRankQueryHandler : IRequestHandlerWrapper<GetPlayNumRankQuery, string>
{
private readonly ICardDbContext cardDbContext;
public GetPlayNumRankQueryHandler(ICardDbContext cardDbContext)
{
this.cardDbContext = cardDbContext;
}
public async Task<ServiceResult<string>> Handle(GetPlayNumRankQuery request, CancellationToken cancellationToken)
{
var ranks = await cardDbContext.PlayNumRanks.OrderBy(rank => rank.Rank)
.Take(30).ToListAsync(cancellationToken: cancellationToken);
var status = new RankStatus
{
TableName = "PlayNumRank",
StartDate = TimeHelper.DateToString(Process.GetCurrentProcess().StartTime.Date),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = ranks.Count,
Status = 1
};
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.PlayNumRankToDto();
dto.Id = i;
return dto;
}).ToList();
var container = new PlayNumRankContainer
{
Ranks = dtoList,
Status = status
};
var result = container.SerializeCardData();
return new ServiceResult<string>(result);
}
}
[XmlRoot("root")]
public class PlayNumRankContainer
{
[XmlArray(ElementName = "play_num_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<PlayNumRankDto> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}

View File

@ -0,0 +1,43 @@
using System.Xml.Serialization;
using Application.Common.Extensions;
using Application.Common.Helpers;
using Application.Common.Models;
using Application.Dto;
using Application.Interfaces;
namespace Application.Game.Rank;
public record GetTenpoScoreRankQuery() : IRequestWrapper<string>;
public class GetTenpoScoreRankQueryHandler : IRequestHandlerWrapper<GetTenpoScoreRankQuery, string>
{
public Task<ServiceResult<string>> Handle(GetTenpoScoreRankQuery request, CancellationToken cancellationToken)
{
var container = new TenpoScoreRankContainer
{
Ranks = new List<object>(),
Status = new RankStatus
{
TableName = "TenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 0
}
};
return Task.FromResult(new ServiceResult<string>(container.SerializeCardData()));
}
}
[XmlRoot("root")]
public class TenpoScoreRankContainer
{
[XmlArray(ElementName = "t_score_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<object> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}

View File

@ -0,0 +1,21 @@
using System.Xml.Serialization;
namespace Application.Game.Rank;
public class RankStatus
{
[XmlElement("table_name")]
public string TableName { get; set; } = string.Empty;
[XmlElement("start_date")]
public string StartDate { get; set; } = string.Empty;
[XmlElement("end_date")]
public string EndDate { get; set; } = string.Empty;
[XmlElement("status")]
public int Status { get; set; }
[XmlElement("rows")]
public int Rows { get; set; }
}

View File

@ -57,7 +57,7 @@ public partial class CertifyCommandHandler : IRequestHandler<CertifyCommand, str
var response = $"host=card_id=7020392000147361,relay_addr={relayConfig.RelayServer},relay_port={relayConfig.RelayPort}\n" + var response = $"host=card_id=7020392000147361,relay_addr={relayConfig.RelayServer},relay_port={relayConfig.RelayPort}\n" +
"no=1337\n" + "no=1337\n" +
"name=123\n" + "name=Local\n" +
"pref=nesys\n" + "pref=nesys\n" +
"addr=nesys@home\n" + "addr=nesys@home\n" +
"x-next-time=15\n" + "x-next-time=15\n" +

View File

@ -13,6 +13,8 @@ public interface ICardDbContext
public DbSet<CardPlayCount> CardPlayCounts { get; set; } public DbSet<CardPlayCount> CardPlayCounts { get; set; }
public DbSet<PlayNumRank> PlayNumRanks { get; set; }
public Task<int> SaveChangesAsync(CancellationToken cancellationToken); public Task<int> SaveChangesAsync(CancellationToken cancellationToken);

View File

@ -0,0 +1,65 @@
using System.Diagnostics.CodeAnalysis;
using Application.Interfaces;
using Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Quartz;
namespace Application.Jobs;
public class UpdatePlayNumRankJob : IJob
{
private readonly ILogger<UpdatePlayNumRankJob> logger;
private readonly ICardDbContext cardDbContext;
private readonly IMusicDbContext musicDbContext;
public UpdatePlayNumRankJob(ILogger<UpdatePlayNumRankJob> logger, ICardDbContext cardDbContext,
IMusicDbContext musicDbContext)
{
this.logger = logger;
this.cardDbContext = cardDbContext;
this.musicDbContext = musicDbContext;
}
public async Task Execute(IJobExecutionContext context)
{
logger.LogInformation("Start maintaining play num rank");
await UpdatePlayNumRank();
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records",
Justification = "All music will be read")]
private async Task UpdatePlayNumRank()
{
var playRecords = await cardDbContext.CardDetails
.Where(detail => detail.Pcol1 == 20).ToListAsync();
var playNumRanks = new List<PlayNumRank>();
var musics = await musicDbContext.MusicUnlocks.ToListAsync();
foreach (var music in musics)
{
var playCount = playRecords
.Where(detail => detail.Pcol2 == music.MusicId)
.Sum(detail => detail.ScoreUi1);
var playNumRank = new PlayNumRank
{
MusicId = (int)music.MusicId,
Artist = music.Artist ?? string.Empty,
Title = music.Title,
PlayCount = (int)playCount
};
playNumRanks.Add(playNumRank);
}
playNumRanks = playNumRanks.OrderByDescending(rank => rank.PlayCount).ToList();
var result = playNumRanks.Select((rank, i) =>
{
rank.Rank = i+1;
return rank;
}).ToList();
await cardDbContext.PlayNumRanks.UpsertRange(result).RunAsync();
await cardDbContext.SaveChangesAsync(new CancellationToken());
}
}

View File

@ -0,0 +1,13 @@
using Application.Dto;
using Domain.Entities;
using Riok.Mapperly.Abstractions;
namespace Application.Mappers;
[Mapper]
public static partial class PlayNumRankMapper
{
[MapProperty(nameof(PlayNumRank.MusicId), nameof(PlayNumRankDto.Pcol1))]
[MapProperty(nameof(PlayNumRank.PlayCount), nameof(PlayNumRankDto.ScoreBi1))]
public static partial PlayNumRankDto PlayNumRankToDto(this PlayNumRank rank);
}

View File

@ -0,0 +1,19 @@
namespace Domain.Entities;
public class PlayNumRank
{
public int MusicId { get; set; }
public int PlayCount { get; set; }
public int Rank { get; set; }
public int Rank2 { get; set; }
public int PrevRank { get; set; }
public int PrevRank2 { get; set; }
public string Title { get; set; } = string.Empty;
public string Artist { get; set; } = string.Empty;
}

View File

@ -0,0 +1,215 @@
// <auto-generated />
using Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Infrastructure.Migrations
{
[DbContext(typeof(CardDbContext))]
[Migration("20230214162154_AddPlayNumRank")]
partial class AddPlayNumRank
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("Domain.Entities.CardBdatum", b =>
{
b.Property<long>("CardId")
.HasColumnType("INTEGER")
.HasColumnName("card_id");
b.Property<string>("Bdata")
.HasColumnType("TEXT")
.HasColumnName("bdata");
b.Property<long>("BdataSize")
.HasColumnType("INTEGER")
.HasColumnName("bdata_size");
b.HasKey("CardId");
b.ToTable("card_bdata", (string)null);
});
modelBuilder.Entity("Domain.Entities.CardDetail", b =>
{
b.Property<long>("CardId")
.HasColumnType("INTEGER")
.HasColumnName("card_id");
b.Property<long>("Pcol1")
.HasColumnType("INTEGER")
.HasColumnName("pcol1");
b.Property<long>("Pcol2")
.HasColumnType("INTEGER")
.HasColumnName("pcol2");
b.Property<long>("Pcol3")
.HasColumnType("INTEGER")
.HasColumnName("pcol3");
b.Property<long>("Fcol1")
.HasColumnType("INTEGER")
.HasColumnName("fcol1");
b.Property<long>("Fcol2")
.HasColumnType("INTEGER")
.HasColumnName("fcol2");
b.Property<long>("Fcol3")
.HasColumnType("INTEGER")
.HasColumnName("fcol3");
b.Property<string>("LastPlayTenpoId")
.HasColumnType("TEXT")
.HasColumnName("last_play_tenpo_id");
b.Property<long>("LastPlayTime")
.HasColumnType("INTEGER")
.HasColumnName("last_play_time");
b.Property<long>("ScoreBi1")
.HasColumnType("INTEGER")
.HasColumnName("score_bi1");
b.Property<long>("ScoreI1")
.HasColumnType("INTEGER")
.HasColumnName("score_i1");
b.Property<long>("ScoreUi1")
.HasColumnType("INTEGER")
.HasColumnName("score_ui1");
b.Property<long>("ScoreUi2")
.HasColumnType("INTEGER")
.HasColumnName("score_ui2");
b.Property<long>("ScoreUi3")
.HasColumnType("INTEGER")
.HasColumnName("score_ui3");
b.Property<long>("ScoreUi4")
.HasColumnType("INTEGER")
.HasColumnName("score_ui4");
b.Property<long>("ScoreUi5")
.HasColumnType("INTEGER")
.HasColumnName("score_ui5");
b.Property<long>("ScoreUi6")
.HasColumnType("INTEGER")
.HasColumnName("score_ui6");
b.HasKey("CardId", "Pcol1", "Pcol2", "Pcol3");
b.ToTable("card_detail", (string)null);
});
modelBuilder.Entity("Domain.Entities.CardMain", b =>
{
b.Property<long>("CardId")
.HasColumnType("INTEGER")
.HasColumnName("card_id");
b.Property<string>("AchieveStatus")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("achieve_status");
b.Property<string>("Created")
.HasColumnType("TEXT")
.HasColumnName("created");
b.Property<long>("Fcol1")
.HasColumnType("INTEGER")
.HasColumnName("fcol1");
b.Property<long>("Fcol2")
.HasColumnType("INTEGER")
.HasColumnName("fcol2");
b.Property<long>("Fcol3")
.HasColumnType("INTEGER")
.HasColumnName("fcol3");
b.Property<string>("Modified")
.HasColumnType("TEXT")
.HasColumnName("modified");
b.Property<string>("PlayerName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("player_name");
b.Property<long>("ScoreI1")
.HasColumnType("INTEGER")
.HasColumnName("score_i1");
b.HasKey("CardId");
b.ToTable("card_main", (string)null);
});
modelBuilder.Entity("Domain.Entities.CardPlayCount", b =>
{
b.Property<long>("CardId")
.HasColumnType("INTEGER")
.HasColumnName("card_id");
b.Property<long>("LastPlayedTime")
.HasColumnType("INTEGER")
.HasColumnName("last_played_time");
b.Property<long>("PlayCount")
.HasColumnType("INTEGER")
.HasColumnName("play_count");
b.HasKey("CardId");
b.ToTable("CardPlayCount", (string)null);
});
modelBuilder.Entity("Domain.Entities.PlayNumRank", b =>
{
b.Property<int>("MusicId")
.HasColumnType("INTEGER");
b.Property<string>("Artist")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("PlayCount")
.HasColumnType("INTEGER");
b.Property<int>("PrevRank")
.HasColumnType("INTEGER");
b.Property<int>("PrevRank2")
.HasColumnType("INTEGER");
b.Property<int>("Rank")
.HasColumnType("INTEGER");
b.Property<int>("Rank2")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("MusicId");
b.ToTable("PlayNumRank", (string)null);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,39 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddPlayNumRank : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PlayNumRank",
columns: table => new
{
MusicId = table.Column<int>(type: "INTEGER", nullable: false),
PlayCount = table.Column<int>(type: "INTEGER", nullable: false),
Rank = table.Column<int>(type: "INTEGER", nullable: false),
Rank2 = table.Column<int>(type: "INTEGER", nullable: false),
PrevRank = table.Column<int>(type: "INTEGER", nullable: false),
PrevRank2 = table.Column<int>(type: "INTEGER", nullable: false),
Title = table.Column<string>(type: "TEXT", nullable: false),
Artist = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PlayNumRank", x => x.MusicId);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PlayNumRank");
}
}
}

View File

@ -173,6 +173,39 @@ namespace Infrastructure.Migrations
b.ToTable("CardPlayCount", (string)null); b.ToTable("CardPlayCount", (string)null);
}); });
modelBuilder.Entity("Domain.Entities.PlayNumRank", b =>
{
b.Property<int>("MusicId")
.HasColumnType("INTEGER");
b.Property<string>("Artist")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("PlayCount")
.HasColumnType("INTEGER");
b.Property<int>("PrevRank")
.HasColumnType("INTEGER");
b.Property<int>("PrevRank2")
.HasColumnType("INTEGER");
b.Property<int>("Rank")
.HasColumnType("INTEGER");
b.Property<int>("Rank2")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("MusicId");
b.ToTable("PlayNumRank", (string)null);
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -25,6 +25,8 @@ public partial class CardDbContext : DbContext, ICardDbContext
public virtual DbSet<CardPlayCount> CardPlayCounts { get; set; } = null!; public virtual DbSet<CardPlayCount> CardPlayCounts { get; set; } = null!;
public virtual DbSet<PlayNumRank> PlayNumRanks { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {
if (optionsBuilder.IsConfigured) if (optionsBuilder.IsConfigured)
@ -111,6 +113,23 @@ public partial class CardDbContext : DbContext, ICardDbContext
entity.Property(e => e.PlayCount).HasColumnName("play_count"); entity.Property(e => e.PlayCount).HasColumnName("play_count");
}); });
modelBuilder.Entity<PlayNumRank>(entity =>
{
entity.HasKey(e => e.MusicId);
entity.ToTable("PlayNumRank");
entity.Property(e => e.MusicId).ValueGeneratedNever();
entity.Property(e => e.PlayCount);
entity.Property(e => e.Artist);
entity.Property(e => e.Title);
entity.Property(e => e.Rank);
entity.Property(e => e.Rank2);
entity.Property(e => e.PrevRank);
entity.Property(e => e.PrevRank2);
});
OnModelCreatingPartial(modelBuilder); OnModelCreatingPartial(modelBuilder);
} }

View File

@ -1,4 +1,6 @@
using Domain.Enums; using Application.Common.Models;
using Application.Game.Rank;
using Domain.Enums;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Throw; using Throw;
@ -14,21 +16,26 @@ public class RankingController : BaseController<RankingController>
var type = (RankingCommandType)rankType; var type = (RankingCommandType)rankType;
type.Throw().IfOutOfRange(); type.Throw().IfOutOfRange();
switch (type) var result = type switch
{ {
case RankingCommandType.GlobalRank: RankingCommandType.GlobalRank => await Mediator.Send(new GetGlobalScoreRankQuery()),
break; RankingCommandType.PlayNumRank => await Mediator.Send(new GetPlayNumRankQuery()),
case RankingCommandType.PlayNumRank: RankingCommandType.EventRank => await Mediator.Send(new GetEventRankQuery()),
break; RankingCommandType.MonthlyRank => await Mediator.Send(new GetMonthlyScoreRankQuery()),
case RankingCommandType.EventRank: RankingCommandType.ShopRank => await Mediator.Send(new GetTenpoScoreRankQuery()),
break; _ => throw new ArgumentOutOfRangeException(nameof(type), type, "Should not happen!")
case RankingCommandType.MonthlyRank: };
break;
case RankingCommandType.ShopRank: if (result.Succeeded)
break; {
default: var normalResult = "1\n" +
throw new ArgumentOutOfRangeException(nameof(type), type, "Should not happen!"); $"{result.Data}";
return Ok(normalResult);
} }
return "";
// Here error is not null since Succeeded => Error is null;
var errorMessage = $"{result.Error!.Code}\n" +
$"{result.Error!.Message}";
return Ok(errorMessage);
} }
} }

View File

@ -64,7 +64,7 @@ public class ApiExceptionFilterService : ExceptionFilterAttribute
private void HandleArgumentOutOfRangeException(ExceptionContext context) private void HandleArgumentOutOfRangeException(ExceptionContext context)
{ {
logger.LogError(context.Exception, ""); logger.LogError(context.Exception, "Get an argument out of bound exception");
var exception = context.Exception as ArgumentOutOfRangeException; var exception = context.Exception as ArgumentOutOfRangeException;
Debug.Assert(exception != null, nameof(exception) + " != null"); Debug.Assert(exception != null, nameof(exception) + " != null");

View File

@ -108,7 +108,10 @@ try
contentTypeProvider.Mappings[".cmp"] = "text/plain"; contentTypeProvider.Mappings[".cmp"] = "text/plain";
contentTypeProvider.Mappings[".evt"] = "text/plain"; contentTypeProvider.Mappings[".evt"] = "text/plain";
app.UseStaticFiles(); app.UseStaticFiles(new StaticFileOptions
{
ContentTypeProvider = contentTypeProvider
});
app.MapControllers(); app.MapControllers();