mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
refactor gacha service 3
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user