From 53044b0dda6a7469ff5087b95a13b792cd367976 Mon Sep 17 00:00:00 2001
From: Lightczx <1686188646@qq.com>
Date: Thu, 27 Jul 2023 17:23:28 +0800
Subject: [PATCH] refactor gacha service
---
.../Snap.Hutao/Core/Threading/AsyncBarrier.cs | 9 +-
.../Snap.Hutao/Extension/StringExtension.cs | 14 +--
.../Service/Cultivation/CultivationService.cs | 2 +-
.../Cultivation/ICultivationService.cs | 2 +-
.../Factory/GachaStatisticsFactory.cs | 12 ++-
.../Factory/GachaStatisticsSlimFactory.cs | 2 +-
.../Factory/IGachaStatisticsFactory.cs | 2 +-
.../Factory/IGachaStatisticsSlimFactory.cs | 2 +-
.../GachaLog/Factory/PullPrediction.cs | 4 +-
.../Factory/TypedWishSummaryBuilder.cs | 13 +--
.../Service/GachaLog/GachaArchives.cs | 11 ++-
.../Service/GachaLog/GachaLogService.cs | 47 +++++-----
.../GachaLog/GachaLogServiceContext.cs | 29 ++++--
.../Service/GachaLog/IGachaLogService.cs | 4 +-
.../GachaLog/QueryProvider/GachaLogQuery.cs | 2 +-
.../GachaLogQueryManualInputProvider.cs | 2 +-
.../GachaLogQueryProviderExtension.cs | 31 -------
.../GachaLogQueryProviderFactory.cs | 22 +++++
.../GachaLogQuerySTokenProvider.cs | 2 +-
.../GachaLogQueryWebCacheProvider.cs | 16 +++-
.../IGachaLogQueryProviderFactory.cs | 9 ++
.../ViewModel/GachaLog/GachaLogViewModel.cs | 91 +++++++++----------
.../Web/Request/QueryString/QueryString.cs | 6 ++
23 files changed, 173 insertions(+), 161 deletions(-)
delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderExtension.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderFactory.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/IGachaLogQueryProviderFactory.cs
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/AsyncBarrier.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/AsyncBarrier.cs
index 13dfab76..f05dab8d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/AsyncBarrier.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/AsyncBarrier.cs
@@ -5,6 +5,7 @@ namespace Snap.Hutao.Core.Threading;
///
/// An asynchronous barrier that blocks the signaler until all other participants have signaled.
+/// FIFO
///
internal class AsyncBarrier
{
@@ -16,7 +17,7 @@ internal class AsyncBarrier
///
/// The set of participants who have reached the barrier, with their awaiters that can resume those participants.
///
- private readonly Stack waiters;
+ private readonly Queue waiters;
///
/// Initializes a new instance of the class.
@@ -24,7 +25,7 @@ internal class AsyncBarrier
/// The number of participants.
public AsyncBarrier(int participants)
{
- Requires.Range(participants > 0, nameof(participants));
+ Must.Range(participants >= 1, "Participants of AsyncBarrier can not be less than 1");
participantCount = participants;
// Allocate the stack so no resizing is necessary.
@@ -47,7 +48,7 @@ internal class AsyncBarrier
// Unleash everyone that preceded this one.
while (waiters.Count > 0)
{
- _ = Task.Factory.StartNew(state => ((TaskCompletionSource)state!).SetResult(), waiters.Pop(), default, TaskCreationOptions.None, TaskScheduler.Default);
+ _ = Task.Factory.StartNew(state => ((TaskCompletionSource)state!).SetResult(), waiters.Dequeue(), default, TaskCreationOptions.None, TaskScheduler.Default);
}
// And allow this one to continue immediately.
@@ -57,7 +58,7 @@ internal class AsyncBarrier
{
// We need more folks. So suspend this caller.
TaskCompletionSource tcs = new();
- waiters.Push(tcs);
+ waiters.Enqueue(tcs);
return tcs.Task;
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/StringExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/StringExtension.cs
index a972547a..02d523aa 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/StringExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/StringExtension.cs
@@ -22,19 +22,9 @@ internal static class StringExtension
return new(value);
}
- ///
- /// 移除结尾可能存在的字符串
- ///
- /// 源
- /// 值
- /// 新的字符串
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string TrimEnd(this string source, string value)
{
- while (source.EndsWith(value))
- {
- source = source[..^value.Length];
- }
-
- return source;
+ return source.AsSpan().TrimEnd(value).ToString();
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
index 78ec27cf..b72833b8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
@@ -159,7 +159,7 @@ internal sealed partial class CultivationService : ICultivationService
}
///
- public async Task SaveConsumptionAsync(CultivateType type, uint itemId, List items)
+ public async ValueTask SaveConsumptionAsync(CultivateType type, uint itemId, List items)
{
if (items.Count == 0)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
index 9b212bd1..2859254a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
@@ -81,7 +81,7 @@ internal interface ICultivationService
/// 主Id
/// 待存物品
/// 是否保存成功
- Task SaveConsumptionAsync(CultivateType type, uint itemId, List- items);
+ ValueTask SaveConsumptionAsync(CultivateType type, uint itemId, List
- items);
///
/// 保存养成物品状态
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs
index c5e8d203..064080db 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs
@@ -26,11 +26,11 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
private readonly AppOptions options;
///
- public async Task CreateAsync(IOrderedQueryable items, GachaLogServiceContext context)
+ public async ValueTask CreateAsync(IOrderedQueryable items, GachaLogServiceContext context)
{
await taskContext.SwitchToBackgroundAsync();
List gachaEvents = await metadataService.GetGachaEventsAsync().ConfigureAwait(false);
- List historyWishBuilders = gachaEvents.SelectList(g => new HistoryWishBuilder(g, context));
+ List historyWishBuilders = gachaEvents.SelectList(gachaEvent => new HistoryWishBuilder(gachaEvent, context));
return CreateCore(serviceProvider, items, historyWishBuilders, context, options.IsEmptyHistoryWishVisible);
}
@@ -137,6 +137,8 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
}
}
+ AsyncBarrier barrier = new(3);
+
return new()
{
// history
@@ -157,9 +159,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
BlueWeapons = blueWeaponCounter.ToStatisticsList(),
// typed wish summary
- StandardWish = standardWishBuilder.ToTypedWishSummary(),
- AvatarWish = avatarWishBuilder.ToTypedWishSummary(),
- WeaponWish = weaponWishBuilder.ToTypedWishSummary(),
+ StandardWish = standardWishBuilder.ToTypedWishSummary(barrier),
+ AvatarWish = avatarWishBuilder.ToTypedWishSummary(barrier),
+ WeaponWish = weaponWishBuilder.ToTypedWishSummary(barrier),
};
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs
index cd537599..b87fd8e7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs
@@ -19,7 +19,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
private readonly ITaskContext taskContext;
///
- public async Task CreateAsync(IOrderedQueryable items, GachaLogServiceContext context)
+ public async ValueTask CreateAsync(IOrderedQueryable items, GachaLogServiceContext context)
{
await taskContext.SwitchToBackgroundAsync();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/IGachaStatisticsFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/IGachaStatisticsFactory.cs
index 0d100e87..1f1c7dbb 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/IGachaStatisticsFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/IGachaStatisticsFactory.cs
@@ -18,5 +18,5 @@ internal interface IGachaStatisticsFactory
/// 物品列表
/// 祈愿记录上下文
/// 祈愿统计对象
- Task CreateAsync(IOrderedQueryable items, GachaLogServiceContext context);
+ ValueTask CreateAsync(IOrderedQueryable items, GachaLogServiceContext context);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/IGachaStatisticsSlimFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/IGachaStatisticsSlimFactory.cs
index c7e8df33..7ffad2be 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/IGachaStatisticsSlimFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/IGachaStatisticsSlimFactory.cs
@@ -17,5 +17,5 @@ internal interface IGachaStatisticsSlimFactory
/// 排序的物品
/// 祈愿记录服务上下文
/// 简化的祈愿统计
- Task CreateAsync(IOrderedQueryable items, GachaLogServiceContext context);
+ ValueTask CreateAsync(IOrderedQueryable items, GachaLogServiceContext context);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/PullPrediction.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/PullPrediction.cs
index bca1b2ca..1b06a2c8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/PullPrediction.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/PullPrediction.cs
@@ -34,7 +34,7 @@ internal sealed class PullPrediction
this.distributionType = distributionType;
}
- public async Task PredictAsync()
+ public async Task PredictAsync(AsyncBarrier barrier)
{
await taskContext.SwitchToBackgroundAsync();
HomaGachaLogClient gachaLogClient = serviceProvider.GetRequiredService();
@@ -43,9 +43,9 @@ internal sealed class PullPrediction
if (response.IsOk())
{
PredictResult result = PredictCore(response.Data.Distribution, typedWishSummary);
+ await barrier.SignalAndWaitAsync().ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
-
typedWishSummary.ProbabilityOfNextPullIsOrange = result.ProbabilityOfNextPullIsOrange;
typedWishSummary.ProbabilityOfPredictedPullLeftToOrange = result.ProbabilityOfPredictedPullLeftToOrange;
typedWishSummary.PredictedPullLeftToOrange = result.PredictedPullLeftToOrange;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs
index 051271bf..d028e159 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs
@@ -54,15 +54,6 @@ internal sealed class TypedWishSummaryBuilder
private DateTimeOffset fromTimeTracker = DateTimeOffset.MaxValue;
private DateTimeOffset toTimeTracker = DateTimeOffset.MinValue;
- ///
- /// 构造一个新的类型化祈愿统计信息构建器
- ///
- /// 服务提供器
- /// 祈愿配置
- /// 祈愿类型判断器
- /// 分布类型
- /// 五星保底
- /// 四星保底
public TypedWishSummaryBuilder(
IServiceProvider serviceProvider,
string name,
@@ -140,7 +131,7 @@ internal sealed class TypedWishSummaryBuilder
/// 转换到类型化祈愿统计信息
///
/// 类型化祈愿统计信息
- public TypedWishSummary ToTypedWishSummary()
+ public TypedWishSummary ToTypedWishSummary(AsyncBarrier barrier)
{
summaryItems.CompleteAdding(guaranteeOrangeThreshold);
double totalCount = totalCountTracker;
@@ -172,7 +163,7 @@ internal sealed class TypedWishSummaryBuilder
};
// TODO: barrier all predictions.
- new PullPrediction(serviceProvider, summary, distributionType).PredictAsync().SafeForget();
+ new PullPrediction(serviceProvider, summary, distributionType).PredictAsync(barrier).SafeForget();
return summary;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchives.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchives.cs
index 7958da53..930418b4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchives.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchives.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
@@ -17,13 +18,17 @@ internal static class GachaArchives
///
/// 初始化存档集合
///
- /// 数据库上下文
+ /// 服务提供器
/// 集合
- public static void Initialize(AppDbContext appDbContext, out ObservableCollection collection)
+ public static void Initialize(IServiceProvider serviceProvider, out ObservableCollection collection)
{
try
{
- collection = appDbContext.GachaArchives.ToObservableCollection();
+ using (IServiceScope scope = serviceProvider.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ collection = appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
+ }
}
catch (SqliteException ex)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
index e79abc52..48e94181 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
@@ -46,7 +46,7 @@ internal sealed partial class GachaLogService : IGachaLogService
}
///
- public ObservableCollection ArchiveCollection
+ public ObservableCollection? ArchiveCollection
{
get => context.ArchiveCollection;
}
@@ -67,14 +67,10 @@ internal sealed partial class GachaLogService : IGachaLogService
Dictionary nameAvatarMap = await metadataService.GetNameToAvatarMapAsync(token).ConfigureAwait(false);
Dictionary nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false);
- using (IServiceScope scope = serviceProvider.CreateScope())
- {
- AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
- GachaArchives.Initialize(appDbContext, out ObservableCollection collection);
+ GachaArchives.Initialize(serviceProvider, out ObservableCollection collection);
- context = new(idAvatarMap, idWeaponMap, nameAvatarMap, nameWeaponMap, collection);
- return true;
- }
+ context = new(idAvatarMap, idWeaponMap, nameAvatarMap, nameWeaponMap, collection);
+ return true;
}
else
{
@@ -83,30 +79,24 @@ internal sealed partial class GachaLogService : IGachaLogService
}
///
- public async Task GetStatisticsAsync(GachaArchive? archive)
+ public async ValueTask GetStatisticsAsync(GachaArchive? archive)
{
archive ??= CurrentArchive;
+ ArgumentNullException.ThrowIfNull(archive);
// Return statistics
- if (archive != null)
+ using (ValueStopwatch.MeasureExecution(logger))
{
- using (ValueStopwatch.MeasureExecution(logger))
+ using (IServiceScope scope = serviceProvider.CreateScope())
{
- using (IServiceScope scope = serviceProvider.CreateScope())
- {
- AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
- IOrderedQueryable items = appDbContext.GachaItems
- .Where(i => i.ArchiveId == archive.InnerId)
- .OrderBy(i => i.Id);
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ IOrderedQueryable items = appDbContext.GachaItems
+ .Where(i => i.ArchiveId == archive.InnerId)
+ .OrderBy(i => i.Id);
- return await gachaStatisticsFactory.CreateAsync(items, context).ConfigureAwait(false);
- }
+ return await gachaStatisticsFactory.CreateAsync(items, context).ConfigureAwait(false);
}
}
- else
- {
- throw Must.NeverHappen();
- }
}
///
@@ -250,4 +240,15 @@ internal sealed partial class GachaLogService : IGachaLogService
return new(!fetchContext.FetchStatus.AuthKeyTimeout, fetchContext.TargetArchive);
}
+}
+
+[ConstructorGenerated]
+[Injection(InjectAs.Scoped, typeof(IGachaLogDbService))]
+internal sealed partial class GachaLogDbService : IGachaLogDbService
+{
+
+}
+
+internal interface IGachaLogDbService
+{
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceContext.cs
index 02980689..363e3a4b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceContext.cs
@@ -86,13 +86,17 @@ internal readonly struct GachaLogServiceContext
{
if (!ItemCache.TryGetValue(name, out Item? result))
{
- result = type switch
+ if (type == SH.ModelInterchangeUIGFItemTypeAvatar)
{
- "角色" => NameAvatarMap[name].ToItem(),
- "武器" => NameWeaponMap[name].ToItem(),
- _ => throw Must.NeverHappen(),
- };
+ result = NameAvatarMap[name].ToItem();
+ }
+ if (type == SH.ModelInterchangeUIGFItemTypeWeapon)
+ {
+ result = NameWeaponMap[name].ToItem();
+ }
+
+ ArgumentNullException.ThrowIfNull(result);
ItemCache[name] = result;
}
@@ -123,11 +127,16 @@ internal readonly struct GachaLogServiceContext
/// 物品 Id
public uint GetItemId(GachaLogItem item)
{
- return item.ItemType switch
+ if (item.ItemType == SH.ModelInterchangeUIGFItemTypeAvatar)
{
- "角色" => NameAvatarMap!.GetValueOrDefault(item.Name)?.Id ?? 0,
- "武器" => NameWeaponMap!.GetValueOrDefault(item.Name)?.Id ?? 0,
- _ => 0U,
- };
+ return NameAvatarMap!.GetValueOrDefault(item.Name)?.Id ?? 0;
+ }
+
+ if (item.ItemType == SH.ModelInterchangeUIGFItemTypeWeapon)
+ {
+ return NameWeaponMap!.GetValueOrDefault(item.Name)?.Id ?? 0;
+ }
+
+ return 0U;
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs
index 23b6961d..38ad34fe 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs
@@ -23,7 +23,7 @@ internal interface IGachaLogService
///
/// 获取可用于绑定的存档集合
///
- ObservableCollection ArchiveCollection { get; }
+ ObservableCollection? ArchiveCollection { get; }
///
/// 导出为一个新的UIGF对象
@@ -37,7 +37,7 @@ internal interface IGachaLogService
///
/// 存档
/// 祈愿统计
- Task GetStatisticsAsync(GachaArchive? archive);
+ ValueTask GetStatisticsAsync(GachaArchive? archive);
///
/// 异步获取简化的祈愿统计列表
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs
index 0808c2f6..95c6347c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs
@@ -4,7 +4,7 @@
namespace Snap.Hutao.Service.GachaLog.QueryProvider;
///
-/// 祈愿记录query
+/// 祈愿记录 query
///
[HighQuality]
internal readonly struct GachaLogQuery
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
index bba11860..064e52ab 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
///
[HighQuality]
[ConstructorGenerated]
-[Injection(InjectAs.Transient, typeof(IGachaLogQueryProvider))]
+[Injection(InjectAs.Transient)]
internal sealed partial class GachaLogQueryManualInputProvider : IGachaLogQueryProvider
{
private readonly IServiceProvider serviceProvider;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderExtension.cs
deleted file mode 100644
index 57642db0..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderExtension.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-namespace Snap.Hutao.Service.GachaLog.QueryProvider;
-
-///
-/// 祈愿记录Url提供器拓展
-///
-internal static class GachaLogQueryProviderExtension
-{
- ///
- /// 选出对应的祈愿 Url 提供器
- ///
- /// 服务提供器
- /// 刷新选项
- /// 对应的祈愿 Url 提供器
- public static IGachaLogQueryProvider? PickProvider(this IServiceProvider serviceProvider, RefreshOption option)
- {
- IEnumerable providers = serviceProvider.GetServices();
-
- string? name = option switch
- {
- RefreshOption.WebCache => nameof(GachaLogQueryWebCacheProvider),
- RefreshOption.SToken => nameof(GachaLogQuerySTokenProvider),
- RefreshOption.ManualInput => nameof(GachaLogQueryManualInputProvider),
- _ => null,
- };
-
- return providers.SingleOrDefault(p => p.Name == name);
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderFactory.cs
new file mode 100644
index 00000000..e26a43b4
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderFactory.cs
@@ -0,0 +1,22 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.GachaLog.QueryProvider;
+
+[ConstructorGenerated]
+[Injection(InjectAs.Transient, typeof(IGachaLogQueryProviderFactory))]
+internal sealed partial class GachaLogQueryProviderFactory : IGachaLogQueryProviderFactory
+{
+ private readonly IServiceProvider serviceProvider;
+
+ public IGachaLogQueryProvider Create(RefreshOption option)
+ {
+ return option switch
+ {
+ RefreshOption.SToken => serviceProvider.GetRequiredService(),
+ RefreshOption.WebCache => serviceProvider.GetRequiredService(),
+ RefreshOption.ManualInput => serviceProvider.GetRequiredService(),
+ _ => throw Must.NeverHappen("不支持的刷新选项"),
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuerySTokenProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuerySTokenProvider.cs
index 11ec1b07..482f09c0 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuerySTokenProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuerySTokenProvider.cs
@@ -15,7 +15,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
///
[HighQuality]
[ConstructorGenerated]
-[Injection(InjectAs.Transient, typeof(IGachaLogQueryProvider))]
+[Injection(InjectAs.Transient)]
internal sealed partial class GachaLogQuerySTokenProvider : IGachaLogQueryProvider
{
private readonly BindingClient2 bindingClient2;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
index 832e066c..f8464653 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
@@ -17,7 +17,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
///
[HighQuality]
[ConstructorGenerated]
-[Injection(InjectAs.Transient, typeof(IGachaLogQueryProvider))]
+[Injection(InjectAs.Transient)]
internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProvider
{
private readonly IGameService gameService;
@@ -38,8 +38,15 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
? GameConstants.GenshinImpactData
: GameConstants.YuanShenData;
- // TODO: make sure how the cache file located.
- return Path.Combine(Path.GetDirectoryName(path)!, dataFolder, @"webCaches\2.13.0.1\Cache\Cache_Data\data_2");
+ DirectoryInfo webCacheFolder = new(Path.Combine(Path.GetDirectoryName(path)!, dataFolder, "webCaches"));
+ Regex versionRegex = VersionRegex();
+ DirectoryInfo? lastestVersionCacheFolder = webCacheFolder
+ .EnumerateDirectories()
+ .Where(dir => versionRegex.IsMatch(dir.Name))
+ .MaxBy(dir => new Version(dir.Name));
+
+ lastestVersionCacheFolder ??= webCacheFolder;
+ return Path.Combine(lastestVersionCacheFolder.FullName, @"Cache\Cache_Data\data_2");
}
///
@@ -112,4 +119,7 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
return null;
}
+
+ [GeneratedRegex("^[1-9]+?\\.[0-9]+?\\.[0-9]+?\\.[0-9]+?$")]
+ private static partial Regex VersionRegex();
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/IGachaLogQueryProviderFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/IGachaLogQueryProviderFactory.cs
new file mode 100644
index 00000000..93aa61e6
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/IGachaLogQueryProviderFactory.cs
@@ -0,0 +1,9 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.GachaLog.QueryProvider;
+
+internal interface IGachaLogQueryProviderFactory
+{
+ IGachaLogQueryProvider Create(RefreshOption option);
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs
index 72d45ed6..5431503d 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs
@@ -146,65 +146,62 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
private async Task RefreshInternalAsync(RefreshOption option)
{
- IGachaLogQueryProvider? provider = serviceProvider.PickProvider(option);
+ IGachaLogQueryProvider provider = serviceProvider.GetRequiredService().Create(option);
- if (provider != null)
+ (bool isOk, GachaLogQuery query) = await provider.GetQueryAsync().ConfigureAwait(false);
+
+ if (isOk)
{
- (bool isOk, GachaLogQuery query) = await provider.GetQueryAsync().ConfigureAwait(false);
+ RefreshStrategy strategy = IsAggressiveRefresh ? RefreshStrategy.AggressiveMerge : RefreshStrategy.LazyMerge;
- if (isOk)
+ // ContentDialog must be created by main thread.
+ await taskContext.SwitchToMainThreadAsync();
+
+ GachaLogRefreshProgressDialog dialog = serviceProvider.CreateInstance();
+ IDisposable dialogHider = await dialog.BlockAsync(taskContext).ConfigureAwait(false);
+ Progress progress = new(dialog.OnReport);
+ bool authkeyValid;
+
+ try
{
- RefreshStrategy strategy = IsAggressiveRefresh ? RefreshStrategy.AggressiveMerge : RefreshStrategy.LazyMerge;
-
- // ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
-
- GachaLogRefreshProgressDialog dialog = serviceProvider.CreateInstance();
- IDisposable dialogHider = await dialog.BlockAsync(taskContext).ConfigureAwait(false);
- Progress progress = new(dialog.OnReport);
- bool authkeyValid;
-
- try
+ using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
- using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
+ try
{
- try
- {
- authkeyValid = await gachaLogService.RefreshGachaLogAsync(query, strategy, progress, CancellationToken).ConfigureAwait(false);
- }
- catch (UserdataCorruptedException ex)
- {
- authkeyValid = false;
- infoBarService.Error(ex);
- }
+ authkeyValid = await gachaLogService.RefreshGachaLogAsync(query, strategy, progress, CancellationToken).ConfigureAwait(false);
+ }
+ catch (UserdataCorruptedException ex)
+ {
+ authkeyValid = false;
+ infoBarService.Error(ex);
}
}
- catch (OperationCanceledException)
- {
- // We set true here in order to hide the dialog.
- authkeyValid = true;
- infoBarService.Warning(SH.ViewModelGachaLogRefreshOperationCancel);
- }
+ }
+ catch (OperationCanceledException)
+ {
+ // We set true here in order to hide the dialog.
+ authkeyValid = true;
+ infoBarService.Warning(SH.ViewModelGachaLogRefreshOperationCancel);
+ }
- await taskContext.SwitchToMainThreadAsync();
- if (authkeyValid)
- {
- SetSelectedArchiveAndUpdateStatistics(gachaLogService.CurrentArchive, true);
- dialogHider.Dispose();
- }
- else
- {
- dialog.Title = SH.ViewModelGachaLogRefreshFail;
- dialog.PrimaryButtonText = SH.ContentDialogConfirmPrimaryButtonText;
- dialog.DefaultButton = ContentDialogButton.Primary;
- }
+ await taskContext.SwitchToMainThreadAsync();
+ if (authkeyValid)
+ {
+ SetSelectedArchiveAndUpdateStatistics(gachaLogService.CurrentArchive, true);
+ dialogHider.Dispose();
}
else
{
- if (!string.IsNullOrEmpty(query.Message))
- {
- infoBarService.Warning(query.Message);
- }
+ dialog.Title = SH.ViewModelGachaLogRefreshFail;
+ dialog.PrimaryButtonText = SH.ContentDialogConfirmPrimaryButtonText;
+ dialog.DefaultButton = ContentDialogButton.Primary;
+ }
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(query.Message))
+ {
+ infoBarService.Warning(query.Message);
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryString.cs b/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryString.cs
index c076cb0f..06e4e18f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryString.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryString.cs
@@ -38,6 +38,12 @@ internal readonly struct QueryString
}
}
+ private static QueryString Parse(ReadOnlySpan value)
+ {
+ // TODO: .NET 8 ReadOnlySpan Split
+ return default;
+ }
+
///
/// Parses a query string into a object. Keys/values are automatically URL decoded.
///