Skip to content

Commit

Permalink
Merge pull request #53 from DGP-Studio/role-combat
Browse files Browse the repository at this point in the history
  • Loading branch information
Lightczx authored Nov 11, 2024
2 parents 1143863 + 8cd1ea4 commit d266cbe
Show file tree
Hide file tree
Showing 23 changed files with 1,708 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.

using Microsoft.EntityFrameworkCore.Storage;
using Snap.Hutao.Server.Controller.Filter;
using Snap.Hutao.Server.Extension;
using Snap.Hutao.Server.Model.Context;
using Snap.Hutao.Server.Model.Entity.RoleCombat;
using Snap.Hutao.Server.Model.Response;
using Snap.Hutao.Server.Model.RoleCombat;
using Snap.Hutao.Server.Service.RoleCombat;
using System.Collections.Concurrent;

namespace Snap.Hutao.Server.Controller;

[ApiController]
[Route("[controller]")]
[ServiceFilter(typeof(CountRequests))]
[ApiExplorerSettings(GroupName = "RoleCombat")]
public class RoleCombatController
{
private static readonly ConcurrentDictionary<string, UploadToken> UploadingUids = new();

private readonly AppDbContext appDbContext;
private readonly IMemoryCache memoryCache;

public RoleCombatController(IServiceProvider serviceProvider)
{
appDbContext = serviceProvider.GetRequiredService<AppDbContext>();
memoryCache = serviceProvider.GetRequiredService<IMemoryCache>();
}

[HttpPost("Upload")]
public async Task<IActionResult> Upload([FromBody] SimpleRoleCombatRecord record)
{
if (memoryCache.TryGetValue(RoleCombatService.Working, out _))
{
return Response.Fail(ReturnCode.ComputingStatistics, "正在计算统计数据", ServerKeys.ServerRecordComputingStatistics);
}

if (record.ScheduleId != RoleCombatScheduleId.GetForNow())
{
return Response.Fail(ReturnCode.NotCurrentSchedule, "非当前剧演数据", ServerKeys.ServerRecordNotCurrentSchedule);
}

if (!record.Validate())
{
return Response.Fail(ReturnCode.InvalidUploadData, "无效的提交数据", ServerKeys.ServerRecordInvalidData);
}

if (UploadingUids.TryGetValue(record.Uid, out _) || !UploadingUids.TryAdd(record.Uid, default))
{
return Response.Fail(ReturnCode.PreviousRequestNotCompleted, "该UID的请求尚在处理", ServerKeys.ServerRecordPreviousRequestNotCompleted);
}

using (IDbContextTransaction transaction = await appDbContext.Database.BeginTransactionAsync().ConfigureAwait(false))
{
RoleCombatRecord? exist = await appDbContext.RoleCombatRecords.SingleOrDefaultAsync(r => r.Uid == record.Uid).ConfigureAwait(false);

if (exist is not null)
{
await appDbContext.RoleCombatAvatars.Where(a => a.RecordId == exist.PrimaryId).ExecuteDeleteAsync().ConfigureAwait(false);
}

await appDbContext.RoleCombatRecords.Where(r => r.Uid == record.Uid).ExecuteDeleteAsync().ConfigureAwait(false);

RoleCombatRecord newRecord = new()
{
Uid = record.Uid,
Uploader = record.Identity,
UploadTime = DateTimeOffset.Now.ToUnixTimeSeconds(),
};
await appDbContext.RoleCombatRecords.AddAndSaveAsync(newRecord).ConfigureAwait(false);

long recordId = newRecord.PrimaryId;
List<RoleCombatAvatar> avatars = record.BackupAvatars.Select(id => new RoleCombatAvatar() { AvatarId = id, RecordId = recordId }).ToList();
await appDbContext.RoleCombatAvatars.AddRangeAndSaveAsync(avatars).ConfigureAwait(false);

await transaction.CommitAsync().ConfigureAwait(false);
}

if (!UploadingUids.TryRemove(record.Uid, out _))
{
return Response.Fail(ReturnCode.InternalStateException, "提交状态异常", ServerKeys.ServerRecordInternalException);
}

return Response.Success("数据提交成功");
}

[HttpGet("Statistics")]
public IActionResult GetStatistics([FromQuery(Name = "Last")] bool last = false)
{
int scheduleId = RoleCombatScheduleId.GetForNow();
if (last)
{
scheduleId--;
}

string key = $"RoleCombatStatistics:{scheduleId}";
if (memoryCache.TryGetValue(key, out RoleCombatStatisticsItem? data))
{
return Response<RoleCombatStatisticsItem>.Success("获取剧演统计数据成功", data!);
}

RoleCombatStatistics? statistics = appDbContext.RoleCombatStatistics
.SingleOrDefault(s => s.ScheduleId == scheduleId);

if (statistics is null)
{
return Response<RoleCombatStatisticsItem>.Success("获取剧演统计数据成功", default!);
}

RoleCombatStatisticsItem? typedData = JsonSerializer.Deserialize<RoleCombatStatisticsItem>(statistics.Data);
memoryCache.Set(key, typedData, TimeSpan.FromDays(1));

return Response<RoleCombatStatisticsItem>.Success("获取深渊统计数据成功", typedData!);
}

private readonly struct UploadToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Snap.Hutao.Server.Service.GachaLog;
using Snap.Hutao.Server.Service.GachaLog.Statistics;
using Snap.Hutao.Server.Service.Legacy;
using Snap.Hutao.Server.Service.RoleCombat;

namespace Snap.Hutao.Server.Discord;

Expand Down Expand Up @@ -38,6 +39,19 @@ public sealed class HutaoServerCommands : DiscordApplicationModuleBase
return Response(response);
}

[OwnerOnly]
[SlashCommand("run-statistics-rolecombat")]
[Description("运行剧演记录统计")]
public async ValueTask<Qmmands.IResult> RunRoleCombatStatisticsAsync()
{
await Context.Services.GetRequiredService<RoleCombatService>().RunAsync().ConfigureAwait(false);
LocalEmbed embed = Embed.CreateStandardEmbed("幻想真境剧诗统计", Embed.GachaLogIcon);
embed.WithDescription("幻想真境剧诗完成");
LocalInteractionMessageResponse response = new LocalInteractionMessageResponse()
.WithEmbeds(embed);
return Response(response);
}

[OwnerOnly]
[SlashCommand("extend-gachalog-all")]
[Description("延长祈愿记录时间")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Snap.Hutao.Server.Job;

public class GachaLogStatisticsRefreshJob : IJob
public sealed class GachaLogStatisticsRefreshJob : IJob
{
private readonly GachaLogStatisticsService statisticsService;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.

using Quartz;
using Snap.Hutao.Server.Service.GachaLog.Statistics;
using Snap.Hutao.Server.Service.RoleCombat;

namespace Snap.Hutao.Server.Job;

public sealed class RoleCombatStatisticsRefreshJob : IJob
{
private readonly RoleCombatService statisticsService;

public RoleCombatStatisticsRefreshJob(RoleCombatService statisticsService)
{
this.statisticsService = statisticsService;
}

public Task Execute(IJobExecutionContext context)
{
return statisticsService.RunAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,38 @@ public async Task Execute(IJobExecutionContext context)
memoryCache.Remove(GachaLogStatisticsService.Working);
}
}
}

public sealed class RoleCombatRecordCleanJob : IJob
{
private readonly DiscordService discordService;
private readonly AppDbContext appDbContext;
private readonly IMemoryCache memoryCache;

public RoleCombatRecordCleanJob(IServiceProvider serviceProvider)
{
memoryCache = serviceProvider.GetRequiredService<IMemoryCache>();
appDbContext = serviceProvider.GetRequiredService<AppDbContext>();
discordService = serviceProvider.GetRequiredService<DiscordService>();
}

/// <inheritdoc/>
public async Task Execute(IJobExecutionContext context)
{
try
{
memoryCache.Set(GachaLogStatisticsService.Working, true);

await appDbContext.RoleCombatAvatars.ExecuteDeleteAsync().ConfigureAwait(false);

int deletedRecordsCount = await appDbContext.RoleCombatRecords.ExecuteDeleteAsync().ConfigureAwait(false);

RoleCombatRecordCleanResult result = new(deletedRecordsCount);
await discordService.ReportRoleCombatCleanResultAsync(result).ConfigureAwait(false);
}
finally
{
memoryCache.Remove(GachaLogStatisticsService.Working);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,14 @@ public SpiralAbyssRecordCleanResult(int records, int spiralAbyss, long redis)
DeletedNumberOfSpiralAbysses = spiralAbyss;
RemovedNumberOfRedisKeys = redis;
}
}

public readonly struct RoleCombatRecordCleanResult
{
public readonly int DeletedNumberOfRecords;

public RoleCombatRecordCleanResult(int records)
{
DeletedNumberOfRecords = records;
}
}
Loading

0 comments on commit d266cbe

Please sign in to comment.