refactor gacha service 3

This commit is contained in:
Lightczx
2023-07-28 14:57:51 +08:00
parent 5f38c370c1
commit 4226598442
13 changed files with 115 additions and 147 deletions

View File

@@ -15,38 +15,6 @@ namespace Snap.Hutao.Model.Entity;
/// </summary>
internal sealed partial class GachaArchive
{
/// <summary>
/// 初始化或跳过
/// </summary>
/// <param name="context">上下文</param>
/// <param name="archive">存档</param>
public static void SkipOrInit(in GachaArchiveInitializationContext context, [NotNull] ref GachaArchive? archive)
{
if (archive == null)
{
Init(context, out archive);
}
}
/// <summary>
/// 初始化
/// </summary>
/// <param name="context">上下文</param>
/// <param name="archive">存档</param>
[SuppressMessage("", "SH002")]
public static void Init(GachaArchiveInitializationContext context, [NotNull] out GachaArchive? archive)
{
archive = context.ArchiveCollection.SingleOrDefault(a => a.Uid == context.Uid);
if (archive == null)
{
GachaArchive created = From(context.Uid);
context.GachaArchives.AddAndSave(created);
context.TaskContext.InvokeOnMainThread(() => context.ArchiveCollection.Add(created));
archive = created;
}
}
/// <summary>
/// 保存祈愿物品
/// </summary>
@@ -68,33 +36,4 @@ internal sealed partial class GachaArchive
context.GachaItems.AddRangeAndSave(context.ItemsToAdd);
}
}
/// <summary>
/// 按卡池类型获取数据库中的最大 Id
/// </summary>
/// <param name="configType">卡池类型</param>
/// <param name="gachaItems">数据集</param>
/// <returns>最大 Id</returns>
public long GetEndId(GachaConfigType configType, DbSet<GachaItem> gachaItems)
{
GachaItem? item = null;
try
{
// TODO: replace with MaxBy
// https://github.com/dotnet/efcore/issues/25566
// .MaxBy(i => i.Id);
item = gachaItems
.Where(i => i.ArchiveId == InnerId)
.Where(i => i.QueryType == configType)
.OrderByDescending(i => i.Id)
.FirstOrDefault();
}
catch (SqliteException ex)
{
ThrowHelper.UserdataCorrupted(SH.ServiceGachaLogEndIdUserdataCorruptedMessage, ex);
}
return item?.Id ?? 0L;
}
}

View File

@@ -20,11 +20,11 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
private readonly ITaskContext taskContext;
/// <inheritdoc/>
public async ValueTask<GachaStatisticsSlim> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context)
public async ValueTask<GachaStatisticsSlim> CreateAsync(GachaLogServiceMetadataContext context, List<GachaItem> items, string uid)
{
await taskContext.SwitchToBackgroundAsync();
return CreateCore(items, context);
return CreateCore(context, items, uid);
}
private static void Track(INameQuality nameQuality, ref int orangeTracker, ref int purpleTracker)
@@ -48,7 +48,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
}
}
private GachaStatisticsSlim CreateCore(List<GachaItem> items, GachaLogServiceMetadataContext context)
private static GachaStatisticsSlim CreateCore(GachaLogServiceMetadataContext context, List<GachaItem> items, string uid)
{
int standardOrangeTracker = 0;
int standardPurpleTracker = 0;
@@ -92,6 +92,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
return new()
{
Uid = uid,
AvatarWish = avatarWish,
WeaponWish = weaponWish,
StandardWish = standardWish,

View File

@@ -14,8 +14,9 @@ internal interface IGachaStatisticsSlimFactory
/// <summary>
/// 异步创建一个新的简化的祈愿统计
/// </summary>
/// <param name="items">排序的物品</param>
/// <param name="context">祈愿记录服务上下文</param>
/// <param name="items">排序的物品</param>
/// <param name="uid">uid</param>
/// <returns>简化的祈愿统计</returns>
ValueTask<GachaStatisticsSlim> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context);
ValueTask<GachaStatisticsSlim> CreateAsync(GachaLogServiceMetadataContext context, List<GachaItem> items, string uid);
}

View File

@@ -1,50 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Model.Entity;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.GachaLog;
/// <summary>
/// 祈愿存档初始化上下文
/// </summary>
internal readonly struct GachaArchiveInitializationContext
{
/// <summary>
/// 任务上下文
/// </summary>
[Obsolete]
public readonly ITaskContext TaskContext;
/// <summary>
/// Uid
/// </summary>
public readonly string Uid;
/// <summary>
/// 数据集
/// </summary>
public readonly DbSet<GachaArchive> GachaArchives;
/// <summary>
/// 存档集合
/// </summary>
public readonly ObservableCollection<GachaArchive> ArchiveCollection;
/// <summary>
/// 初始化一个新的祈愿存档初始化上下文
/// </summary>
/// <param name="taskContext">任务上下文</param>
/// <param name="uid">uid</param>
/// <param name="gachaArchives">数据集</param>
/// <param name="archiveCollection">存档集合</param>
public GachaArchiveInitializationContext(ITaskContext taskContext, string uid, DbSet<GachaArchive> gachaArchives, ObservableCollection<GachaArchive> archiveCollection)
{
TaskContext = taskContext;
Uid = uid;
GachaArchives = gachaArchives;
ArchiveCollection = archiveCollection;
}
}

View File

@@ -0,0 +1,35 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.GachaLog;
/// <summary>
/// 祈愿存档初始化上下文
/// </summary>
internal static class GachaArchiveOperation
{
public static void GetOrAdd(IServiceProvider serviceProvider, string uid, ObservableCollection<GachaArchive> archives, [NotNull] out GachaArchive? archive)
{
archive = archives.SingleOrDefault(a => a.Uid == uid);
if (archive == null)
{
GachaArchive created = GachaArchive.From(uid);
using (IServiceScope scope = serviceProvider.CreateScope())
{
IGachaLogDbService gachaLogDbService = scope.ServiceProvider.GetRequiredService<IGachaLogDbService>();
gachaLogDbService.AddGachaArchive(created);
ITaskContext taskContext = scope.ServiceProvider.GetRequiredService<ITaskContext>();
taskContext.InvokeOnMainThread(() => archives.Add(created));
}
archive = created;
}
}
}

View File

@@ -5,6 +5,7 @@ using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.GachaLog;
@@ -92,17 +93,16 @@ internal struct GachaLogFetchContext
/// 确保 存档 与 EndId 不为空
/// </summary>
/// <param name="item">物品</param>
public void EnsureArchiveAndEndId(GachaLogItem item)
/// <param name="archives">存档集合</param>
/// <param name="gachaLogDbService">祈愿记录数据库服务</param>
public void EnsureArchiveAndEndId(GachaLogItem item, ObservableCollection<GachaArchive> archives, IGachaLogDbService gachaLogDbService)
{
using (IServiceScope scope = serviceProvider.CreateScope())
if (TargetArchive == null)
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
ITaskContext taskContext = scope.ServiceProvider.GetRequiredService<ITaskContext>();
GachaArchiveInitializationContext context = new(taskContext, item.Uid, appDbContext.GachaArchives, serviceContext.ArchiveCollection);
GachaArchive.SkipOrInit(context, ref TargetArchive);
DbEndId ??= TargetArchive.GetEndId(CurrentType, appDbContext.GachaItems);
GachaArchiveOperation.GetOrAdd(serviceProvider, item.Uid, archives, out TargetArchive);
}
DbEndId ??= gachaLogDbService.GetLastGachaItemIdByArchiveIdAndQueryType(TargetArchive.InnerId, CurrentType);
}
/// <summary>
@@ -110,7 +110,7 @@ internal struct GachaLogFetchContext
/// </summary>
/// <param name="item">物品</param>
/// <returns>是否应添加</returns>
public bool ShouldAdd(GachaLogItem item)
public bool ShouldAddItem(GachaLogItem item)
{
return !isLazy || item.Id > DbEndId;
}

View File

@@ -54,6 +54,7 @@ internal sealed partial class GachaLogService : IGachaLogService
public ObservableCollection<GachaArchive>? ArchiveCollection
{
get => archiveCollection;
private set => archiveCollection = value;
}
/// <inheritdoc/>
@@ -71,10 +72,9 @@ internal sealed partial class GachaLogService : IGachaLogService
Dictionary<string, Model.Metadata.Avatar.Avatar> nameAvatarMap = await metadataService.GetNameToAvatarMapAsync(token).ConfigureAwait(false);
Dictionary<string, Model.Metadata.Weapon.Weapon> nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false);
ObservableCollection<GachaArchive> collection = gachaLogDbService.GetGachaArchiveCollection();
context = new(idAvatarMap, idWeaponMap, nameAvatarMap, nameWeaponMap);
ArchiveCollection = gachaLogDbService.GetGachaArchiveCollection();
return true;
}
else
@@ -107,8 +107,7 @@ internal sealed partial class GachaLogService : IGachaLogService
foreach (GachaArchive archive in ArchiveCollection)
{
List<GachaItem> items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId);
GachaStatisticsSlim slim = await gachaStatisticsSlimFactory.CreateAsync(items, context).ConfigureAwait(false);
slim.Uid = archive.Uid;
GachaStatisticsSlim slim = await gachaStatisticsSlimFactory.CreateAsync(context, items, archive.Uid).ConfigureAwait(false);
statistics.Add(slim);
}
@@ -163,6 +162,7 @@ internal sealed partial class GachaLogService : IGachaLogService
private async Task<ValueResult<bool, GachaArchive?>> FetchGachaLogsAsync(GachaLogQuery query, bool isLazy, IProgress<GachaLogFetchStatus> progress, CancellationToken token)
{
ArgumentNullException.ThrowIfNull(ArchiveCollection);
GachaLogFetchContext fetchContext = new(serviceProvider, context, isLazy);
foreach (GachaConfigType configType in GachaLog.QueryTypes)
@@ -182,9 +182,9 @@ internal sealed partial class GachaLogService : IGachaLogService
foreach (GachaLogItem item in items)
{
fetchContext.EnsureArchiveAndEndId(item);
fetchContext.EnsureArchiveAndEndId(item, ArchiveCollection, gachaLogDbService);
if (fetchContext.ShouldAdd(item))
if (fetchContext.ShouldAddItem(item))
{
fetchContext.AddItem(item);
}
@@ -273,13 +273,55 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
.ConfigureAwait(false);
}
}
public long GetLastGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType)
{
GachaItem? item = null;
try
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// TODO: replace with MaxBy
// https://github.com/dotnet/efcore/issues/25566
// .MaxBy(i => i.Id);
item = appDbContext.GachaItems
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId)
.Where(i => i.QueryType == queryType)
.OrderByDescending(i => i.Id)
.FirstOrDefault();
}
}
catch (SqliteException ex)
{
ThrowHelper.UserdataCorrupted(SH.ServiceGachaLogEndIdUserdataCorruptedMessage, ex);
}
return item?.Id ?? 0L;
}
public void AddGachaArchive(GachaArchive archive)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
appDbContext.GachaArchives.AddAndSave(archive);
}
}
}
internal interface IGachaLogDbService
{
void AddGachaArchive(GachaArchive archive);
ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId);
ObservableCollection<GachaArchive> GetGachaArchiveCollection();
List<GachaItem> GetGachaItemListByArchiveId(Guid archiveId);
long GetLastGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
}

View File

@@ -68,7 +68,7 @@ internal interface IGachaLogService
/// <param name="strategy">刷新策略</param>
/// <param name="progress">进度</param>
/// <param name="token">取消令牌</param>
/// <returns>验证密钥是否可用</returns>
/// <returns>验证密钥是否有效</returns>
Task<bool> RefreshGachaLogAsync(GachaLogQuery query, RefreshStrategy strategy, IProgress<GachaLogFetchStatus> progress, CancellationToken token);
/// <summary>

View File

@@ -40,7 +40,7 @@ internal sealed partial class GachaLogQuerySTokenProvider : IGachaLogQueryProvid
if (authkeyResponse.IsOk())
{
return new(true, new(GachaLogQueryOptions.AsQuery(data, authkeyResponse.Data, metadataOptions.LanguageCode)));
return new(true, new(GachaLogQueryOptions.ToQueryString(data, authkeyResponse.Data, metadataOptions.LanguageCode)));
}
else
{

View File

@@ -27,8 +27,7 @@ internal sealed partial class UIGFImportService : IUIGFImportService
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
GachaArchiveInitializationContext initContext = new(taskContext, uigf.Info.Uid, appDbContext.GachaArchives, context.ArchiveCollection);
GachaArchive.Init(initContext, out GachaArchive? archive);
GachaArchiveOperation.GetOrAdd(serviceProvider, uigf.Info.Uid, context.ArchiveCollection, out GachaArchive? archive);
Guid archiveId = archive.InnerId;
long trimId = appDbContext.GachaItems

View File

@@ -22,19 +22,19 @@ internal sealed partial class GachaInfoClient
/// <summary>
/// 获取记录页面
/// </summary>
/// <param name="config">查询</param>
/// <param name="options">查询</param>
/// <param name="token">取消令牌</param>
/// <returns>单个祈愿记录页面</returns>
public async Task<Response<GachaLogPage>> GetGachaLogPageAsync(GachaLogQueryOptions config, CancellationToken token = default)
public async Task<Response<GachaLogPage>> GetGachaLogPageAsync(GachaLogQueryOptions options, CancellationToken token = default)
{
string query = config.AsQuery();
string query = options.ToQueryString();
string url = config.IsOversea
string url = options.IsOversea
? ApiOsEndpoints.GachaInfoGetGachaLog(query)
: ApiEndpoints.GachaInfoGetGachaLog(query);
Response<GachaLogPage>? response = await httpClient
.TryCatchGetFromJsonAsync<Response<GachaLogPage>>(url, options, logger, token)
.TryCatchGetFromJsonAsync<Response<GachaLogPage>>(url, this.options, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(response);
}

View File

@@ -70,7 +70,7 @@ internal struct GachaLogQueryOptions
/// <param name="gameAuthKey">验证包装</param>
/// <param name="lang">语言</param>
/// <returns>查询</returns>
public static string AsQuery(GenAuthKeyData genAuthKeyData, GameAuthKey gameAuthKey, string lang)
public static string ToQueryString(GenAuthKeyData genAuthKeyData, GameAuthKey gameAuthKey, string lang)
{
QueryString queryString = new();
queryString.Set("lang", lang);
@@ -86,7 +86,7 @@ internal struct GachaLogQueryOptions
/// 转换到查询字符串
/// </summary>
/// <returns>匹配的查询字符串</returns>
public string AsQuery()
public string ToQueryString()
{
// Make the cached end id into query.
innerQuery.Set("end_id", EndId);

View File

@@ -131,21 +131,22 @@ internal sealed class Response<TData> : Response, IJsResult
/// 响应是否正常
/// </summary>
/// <param name="showInfoBar">是否显示错误信息</param>
/// <param name="serviceProvider">服务提供器</param>
/// <returns>是否Ok</returns>
[MemberNotNullWhen(true, nameof(Data))]
public bool IsOk(bool showInfoBar = true)
public bool IsOk(bool showInfoBar = true, IServiceProvider? serviceProvider = null)
{
if (ReturnCode == 0)
{
#pragma warning disable CS8775
ArgumentNullException.ThrowIfNull(Data);
return true;
#pragma warning restore CS8775
}
else
{
if (showInfoBar)
{
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ToString());
serviceProvider ??= Ioc.Default;
serviceProvider.GetRequiredService<IInfoBarService>().Error(ToString());
}
return false;