From de9abcfad44a84a76fcd7b7dc220761a80bf116a Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Thu, 27 Jul 2023 22:26:19 +0800 Subject: [PATCH] refactor gacha service 2 --- .../Extension/EnumerableExtension.List.cs | 4 +- .../Snap.Hutao/Extension/SpanExtension.cs | 8 +- .../Service/DailyNote/DailyNoteDbService.cs | 10 -- .../Factory/GachaStatisticsFactory.cs | 13 +- .../Factory/GachaStatisticsSlimFactory.cs | 52 ++++---- .../GachaLog/Factory/HistoryWishBuilder.cs | 2 +- .../Factory/IGachaStatisticsFactory.cs | 2 +- .../Factory/IGachaStatisticsSlimFactory.cs | 2 +- .../Factory/TypedWishSummaryBuilder.cs | 9 +- .../GachaArchiveInitializationContext.cs | 1 + .../Service/GachaLog/GachaArchives.cs | 39 ------ .../Snap.Hutao/Service/GachaLog/GachaLog.cs | 2 +- .../Service/GachaLog/GachaLogFetchContext.cs | 8 +- .../Service/GachaLog/GachaLogService.cs | 115 ++++++++++++------ ...t.cs => GachaLogServiceMetadataContext.cs} | 10 +- .../Service/GachaLog/IGachaLogService.cs | 9 +- .../Service/GachaLog/IUIGFExportService.cs | 2 +- .../Service/GachaLog/IUIGFImportService.cs | 2 +- .../Service/GachaLog/UIGFExportService.cs | 2 +- .../Service/GachaLog/UIGFImportService.cs | 2 +- .../GachaLog/GachaLogViewModelSlim.cs | 2 +- 21 files changed, 145 insertions(+), 151 deletions(-) delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchives.cs rename src/Snap.Hutao/Snap.Hutao/Service/GachaLog/{GachaLogServiceContext.cs => GachaLogServiceMetadataContext.cs} (92%) diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs index 0fe984fe..26b97745 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs @@ -12,7 +12,7 @@ namespace Snap.Hutao.Extension; internal static partial class EnumerableExtension { /// - public static double SpanAverage(this List source) + public static double Average(this List source) { Span span = CollectionsMarshal.AsSpan(source); if (span.IsEmpty) @@ -21,7 +21,7 @@ internal static partial class EnumerableExtension } long sum = 0; - foreach (int item in span) + foreach (ref readonly int item in span) { sum += item; } diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/SpanExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/SpanExtension.cs index 18db77a0..dee1a93c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/SpanExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/SpanExtension.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Numerics; +using System.Runtime.InteropServices; namespace Snap.Hutao.Extension; @@ -59,7 +60,7 @@ internal static class SpanExtension /// 平均值 public static byte Average(this in ReadOnlySpan span) { - if (span.Length == 0) + if (span.IsEmpty) { return 0; } @@ -74,4 +75,9 @@ internal static class SpanExtension return unchecked((byte)(sum / count)); } + + public static Span AsSpan(this List list) + { + return CollectionsMarshal.AsSpan(list); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteDbService.cs index 68fa3d22..5829e0bb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteDbService.cs @@ -1,20 +1,10 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using CommunityToolkit.Mvvm.Messaging; using Microsoft.EntityFrameworkCore; using Snap.Hutao.Core.Database; -using Snap.Hutao.Core.DependencyInjection.Abstraction; -using Snap.Hutao.Message; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Database; -using Snap.Hutao.Service.Notification; -using Snap.Hutao.Service.User; -using Snap.Hutao.ViewModel.User; -using Snap.Hutao.Web.Hoyolab; -using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; -using System.Collections.ObjectModel; -using WebDailyNote = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote; namespace Snap.Hutao.Service.DailyNote; 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 064080db..162c0f76 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs @@ -9,6 +9,7 @@ using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Metadata.Weapon; using Snap.Hutao.Service.Metadata; using Snap.Hutao.ViewModel.GachaLog; +using System.Runtime.InteropServices; namespace Snap.Hutao.Service.GachaLog.Factory; @@ -26,7 +27,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory private readonly AppOptions options; /// - public async ValueTask CreateAsync(IOrderedQueryable items, GachaLogServiceContext context) + public async ValueTask CreateAsync(List items, GachaLogServiceMetadataContext context) { await taskContext.SwitchToBackgroundAsync(); List gachaEvents = await metadataService.GetGachaEventsAsync().ConfigureAwait(false); @@ -37,9 +38,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory private static GachaStatistics CreateCore( IServiceProvider serviceProvider, - IOrderedQueryable items, + List items, List historyWishBuilders, - in GachaLogServiceContext context, + in GachaLogServiceMetadataContext context, bool isEmptyHistoryWishVisible) { TypedWishSummaryBuilder standardWishBuilder = new( @@ -65,9 +66,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory Dictionary purpleWeaponCounter = new(); Dictionary blueWeaponCounter = new(); - // Items are ordered by precise time - // first is oldest - foreach (GachaItem item in items) + // Items are ordered by precise time, first is oldest + // 'ref' is not allowed here because we have lambda below + foreach (GachaItem item in CollectionsMarshal.AsSpan(items)) { // Find target history wish to operate. HistoryWishBuilder? targetHistoryWishBuilder = historyWishBuilders 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 b87fd8e7..aa6b2f00 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs @@ -6,6 +6,7 @@ using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Metadata.Abstraction; using Snap.Hutao.ViewModel.GachaLog; using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; +using System.Runtime.InteropServices; namespace Snap.Hutao.Service.GachaLog.Factory; @@ -19,10 +20,36 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF private readonly ITaskContext taskContext; /// - public async ValueTask CreateAsync(IOrderedQueryable items, GachaLogServiceContext context) + public async ValueTask CreateAsync(List items, GachaLogServiceMetadataContext context) { await taskContext.SwitchToBackgroundAsync(); + return CreateCore(items, context); + } + + private static void Track(INameQuality nameQuality, ref int orangeTracker, ref int purpleTracker) + { + switch (nameQuality.Quality) + { + case QualityType.QUALITY_ORANGE: + orangeTracker = 0; + ++purpleTracker; + break; + case QualityType.QUALITY_PURPLE: + ++orangeTracker; + purpleTracker = 0; + break; + case QualityType.QUALITY_BLUE: + ++orangeTracker; + ++purpleTracker; + break; + default: + break; + } + } + + private GachaStatisticsSlim CreateCore(List items, GachaLogServiceMetadataContext context) + { int standardOrangeTracker = 0; int standardPurpleTracker = 0; TypedWishSummarySlim standardWish = new(SH.ServiceGachaLogFactoryPermanentWishName, 90, 10); @@ -36,7 +63,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF TypedWishSummarySlim weaponWish = new(SH.ServiceGachaLogFactoryWeaponWishName, 80, 10); // O(n) operation - foreach (GachaItem item in items) + foreach (ref readonly GachaItem item in CollectionsMarshal.AsSpan(items)) { INameQuality nameQuality = context.GetNameQualityByItemId(item.ItemId); switch (item.QueryType) @@ -70,25 +97,4 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF StandardWish = standardWish, }; } - - private static void Track(INameQuality nameQuality, ref int orangeTracker, ref int purpleTracker) - { - switch (nameQuality.Quality) - { - case QualityType.QUALITY_ORANGE: - orangeTracker = 0; - ++purpleTracker; - break; - case QualityType.QUALITY_PURPLE: - ++orangeTracker; - purpleTracker = 0; - break; - case QualityType.QUALITY_BLUE: - ++orangeTracker; - ++purpleTracker; - break; - default: - break; - } - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs index 7585cd7c..6feac754 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs @@ -30,7 +30,7 @@ internal sealed class HistoryWishBuilder /// 卡池配置 /// 祈愿记录上下文 [SuppressMessage("", "SH002")] - public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceContext context) + public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceMetadataContext context) { this.gachaEvent = gachaEvent; ConfigType = gachaEvent.Type; 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 1f1c7dbb..13997252 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 /// 物品列表 /// 祈愿记录上下文 /// 祈愿统计对象 - ValueTask CreateAsync(IOrderedQueryable items, GachaLogServiceContext context); + ValueTask CreateAsync(List items, GachaLogServiceMetadataContext 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 7ffad2be..68c02bc6 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 /// 排序的物品 /// 祈愿记录服务上下文 /// 简化的祈愿统计 - ValueTask CreateAsync(IOrderedQueryable items, GachaLogServiceContext context); + ValueTask CreateAsync(List items, GachaLogServiceMetadataContext context); } \ No newline at end of file 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 d028e159..6163ae3c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs @@ -127,10 +127,6 @@ internal sealed class TypedWishSummaryBuilder } } - /// - /// 转换到类型化祈愿统计信息 - /// - /// 类型化祈愿统计信息 public TypedWishSummary ToTypedWishSummary(AsyncBarrier barrier) { summaryItems.CompleteAdding(guaranteeOrangeThreshold); @@ -157,12 +153,11 @@ internal sealed class TypedWishSummaryBuilder TotalOrangePercent = totalOrangePullTracker / totalCount, TotalPurplePercent = totalPurplePullTracker / totalCount, TotalBluePercent = totalBluePullTracker / totalCount, - AverageOrangePull = averageOrangePullTracker.SpanAverage(), - AverageUpOrangePull = averageUpOrangePullTracker.SpanAverage(), + AverageOrangePull = averageOrangePullTracker.Average(), + AverageUpOrangePull = averageUpOrangePullTracker.Average(), OrangeList = summaryItems, }; - // TODO: barrier all predictions. new PullPrediction(serviceProvider, summary, distributionType).PredictAsync(barrier).SafeForget(); return summary; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchiveInitializationContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchiveInitializationContext.cs index 5d132896..8969b2b7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchiveInitializationContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchiveInitializationContext.cs @@ -15,6 +15,7 @@ internal readonly struct GachaArchiveInitializationContext /// /// 任务上下文 /// + [Obsolete] public readonly ITaskContext TaskContext; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchives.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchives.cs deleted file mode 100644 index 930418b4..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchives.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// 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; -using System.Collections.ObjectModel; - -namespace Snap.Hutao.Service.GachaLog; - -/// -/// 存档操作 -/// -internal static class GachaArchives -{ - /// - /// 初始化存档集合 - /// - /// 服务提供器 - /// 集合 - public static void Initialize(IServiceProvider serviceProvider, out ObservableCollection collection) - { - try - { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - collection = appDbContext.GachaArchives.AsNoTracking().ToObservableCollection(); - } - } - catch (SqliteException ex) - { - string message = string.Format(SH.ServiceGachaLogArchiveCollectionUserdataCorruptedMessage, ex.Message); - throw ThrowHelper.UserdataCorrupted(message, ex); - } - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLog.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLog.cs index 96b5ebf8..7ace4b6a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLog.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLog.cs @@ -20,5 +20,5 @@ internal static class GachaLog GachaConfigType.StandardWish, GachaConfigType.AvatarEventWish, GachaConfigType.WeaponEventWish, - }.ToImmutableList(); + }.ToImmutableList(); // TODO: FrozenSet } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs index d2efc450..ef859c25 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs @@ -49,7 +49,7 @@ internal struct GachaLogFetchContext public GachaConfigType CurrentType; private readonly IServiceProvider serviceProvider; - private readonly GachaLogServiceContext serviceContext; + private readonly GachaLogServiceMetadataContext serviceContext; private readonly bool isLazy; /// @@ -58,7 +58,7 @@ internal struct GachaLogFetchContext /// 服务提供器 /// 祈愿服务上下文 /// 是否为懒惰模式 - public GachaLogFetchContext(IServiceProvider serviceProvider, in GachaLogServiceContext serviceContext, bool isLazy) + public GachaLogFetchContext(IServiceProvider serviceProvider, in GachaLogServiceMetadataContext serviceContext, bool isLazy) { this.serviceProvider = serviceProvider; this.serviceContext = serviceContext; @@ -99,8 +99,8 @@ internal struct GachaLogFetchContext AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); ITaskContext taskContext = scope.ServiceProvider.GetRequiredService(); - GachaArchiveInitializationContext initContext = new(taskContext, item.Uid, appDbContext.GachaArchives, serviceContext.ArchiveCollection); - GachaArchive.SkipOrInit(initContext, ref TargetArchive); + GachaArchiveInitializationContext context = new(taskContext, item.Uid, appDbContext.GachaArchives, serviceContext.ArchiveCollection); + GachaArchive.SkipOrInit(context, ref TargetArchive); DbEndId ??= TargetArchive.GetEndId(CurrentType, appDbContext.GachaItems); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs index 48e94181..1e873aed 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs @@ -1,8 +1,11 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; using Snap.Hutao.Core.Database; using Snap.Hutao.Core.Diagnostics; +using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Database; using Snap.Hutao.Model.InterChange.GachaLog; @@ -34,9 +37,11 @@ internal sealed partial class GachaLogService : IGachaLogService private readonly IMetadataService metadataService; private readonly ILogger logger; private readonly GachaInfoClient gachaInfoClient; + private readonly IGachaLogDbService gachaLogDbService; private readonly ITaskContext taskContext; - private GachaLogServiceContext context; + private GachaLogServiceMetadataContext context; + private ObservableCollection? archiveCollection; /// public GachaArchive? CurrentArchive @@ -48,11 +53,11 @@ internal sealed partial class GachaLogService : IGachaLogService /// public ObservableCollection? ArchiveCollection { - get => context.ArchiveCollection; + get => archiveCollection; } /// - public async ValueTask InitializeAsync(CancellationToken token) + public async ValueTask InitializeAsync(CancellationToken token = default) { if (context.IsInitialized) { @@ -67,9 +72,9 @@ internal sealed partial class GachaLogService : IGachaLogService Dictionary nameAvatarMap = await metadataService.GetNameToAvatarMapAsync(token).ConfigureAwait(false); Dictionary nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false); - GachaArchives.Initialize(serviceProvider, out ObservableCollection collection); + ObservableCollection collection = gachaLogDbService.GetGachaArchiveCollection(); - context = new(idAvatarMap, idWeaponMap, nameAvatarMap, nameWeaponMap, collection); + context = new(idAvatarMap, idWeaponMap, nameAvatarMap, nameWeaponMap); return true; } else @@ -87,48 +92,37 @@ internal sealed partial class GachaLogService : IGachaLogService // Return statistics using (ValueStopwatch.MeasureExecution(logger)) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - 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); - } + List items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId); + return await gachaStatisticsFactory.CreateAsync(items, context).ConfigureAwait(false); } } /// - public async Task> GetStatisticsSlimsAsync() + public async ValueTask> GetStatisticsSlimListAsync(CancellationToken token = default) { - using (IServiceScope scope = serviceProvider.CreateScope()) + await InitializeAsync(token).ConfigureAwait(false); + ArgumentNullException.ThrowIfNull(ArchiveCollection); + + List statistics = new(); + foreach (GachaArchive archive in ArchiveCollection) { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - List statistics = new(); - foreach (GachaArchive archive in appDbContext.GachaArchives) - { - IOrderedQueryable items = appDbContext.GachaItems - .Where(i => i.ArchiveId == archive.InnerId) - .OrderBy(i => i.Id); - - GachaStatisticsSlim slim = await gachaStatisticsSlimFactory.CreateAsync(items, context).ConfigureAwait(false); - slim.Uid = archive.Uid; - statistics.Add(slim); - } - - return statistics; + List items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId); + GachaStatisticsSlim slim = await gachaStatisticsSlimFactory.CreateAsync(items, context).ConfigureAwait(false); + slim.Uid = archive.Uid; + statistics.Add(slim); } + + return statistics; } /// - public Task ExportToUIGFAsync(GachaArchive archive) + public ValueTask ExportToUIGFAsync(GachaArchive archive) { return gachaLogExportService.ExportAsync(context, archive); } /// - public async Task ImportFromUIGFAsync(UIGF uigf) + public async ValueTask ImportFromUIGFAsync(UIGF uigf) { CurrentArchive = await gachaLogImportService.ImportAsync(context, uigf).ConfigureAwait(false); } @@ -156,19 +150,15 @@ internal sealed partial class GachaLogService : IGachaLogService /// public async Task RemoveArchiveAsync(GachaArchive archive) { + ArgumentNullException.ThrowIfNull(archiveCollection); + // Sync cache await taskContext.SwitchToMainThreadAsync(); - context.ArchiveCollection.Remove(archive); + archiveCollection.Remove(archive); // Sync database await taskContext.SwitchToBackgroundAsync(); - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.GachaArchives - .ExecuteDeleteWhereAsync(a => a.InnerId == archive.InnerId) - .ConfigureAwait(false); - } + await gachaLogDbService.DeleteGachaArchiveByIdAsync(archive.InnerId).ConfigureAwait(false); } private static Task RandomDelayAsync(CancellationToken token) @@ -246,9 +236,54 @@ internal sealed partial class GachaLogService : IGachaLogService [Injection(InjectAs.Scoped, typeof(IGachaLogDbService))] internal sealed partial class GachaLogDbService : IGachaLogDbService { + private readonly IServiceProvider serviceProvider; + public ObservableCollection GetGachaArchiveCollection() + { + try + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + return appDbContext.GachaArchives.AsNoTracking().ToObservableCollection(); + } + } + catch (SqliteException ex) + { + string message = string.Format(SH.ServiceGachaLogArchiveCollectionUserdataCorruptedMessage, ex.Message); + throw ThrowHelper.UserdataCorrupted(message, ex); + } + } + + public List GetGachaItemListByArchiveId(Guid archiveId) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + return appDbContext.GachaItems + .AsNoTracking() + .Where(i => i.ArchiveId == archiveId) + .OrderBy(i => i.Id) + .ToList(); + } + } + + public async ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + await appDbContext.GachaArchives + .ExecuteDeleteWhereAsync(a => a.InnerId == archiveId) + .ConfigureAwait(false); + } + } } internal interface IGachaLogDbService { + ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId); + ObservableCollection GetGachaArchiveCollection(); + + List GetGachaItemListByArchiveId(Guid archiveId); } \ 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/GachaLogServiceMetadataContext.cs similarity index 92% rename from src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceContext.cs rename to src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs index 363e3a4b..5d62d8d1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs @@ -15,7 +15,7 @@ namespace Snap.Hutao.Service.GachaLog; /// /// 祈愿记录服务上下文 /// -internal readonly struct GachaLogServiceContext +internal readonly struct GachaLogServiceMetadataContext { /// /// 物品缓存 @@ -45,6 +45,7 @@ internal readonly struct GachaLogServiceContext /// /// 存档集合 /// + [Obsolete] public readonly ObservableCollection ArchiveCollection; /// @@ -59,19 +60,16 @@ internal readonly struct GachaLogServiceContext /// Id 武器 映射 /// 名称 角色 映射 /// 名称 武器 映射 - /// 存档集合 - public GachaLogServiceContext( + public GachaLogServiceMetadataContext( Dictionary idAvatarMap, Dictionary idWeaponMap, Dictionary nameAvatarMap, - Dictionary nameWeaponMap, - ObservableCollection archiveCollection) + Dictionary nameWeaponMap) { IdAvatarMap = idAvatarMap; IdWeaponMap = idWeaponMap; NameAvatarMap = nameAvatarMap; NameWeaponMap = nameWeaponMap; - ArchiveCollection = archiveCollection; IsInitialized = true; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs index 38ad34fe..c3da913f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs @@ -30,7 +30,7 @@ internal interface IGachaLogService /// /// 存档 /// UIGF对象 - Task ExportToUIGFAsync(GachaArchive archive); + ValueTask ExportToUIGFAsync(GachaArchive archive); /// /// 获得对应的祈愿统计 @@ -42,22 +42,23 @@ internal interface IGachaLogService /// /// 异步获取简化的祈愿统计列表 /// + /// 取消令牌 /// 简化的祈愿统计列表 - Task> GetStatisticsSlimsAsync(); + ValueTask> GetStatisticsSlimListAsync(CancellationToken token = default); /// /// 异步从UIGF导入数据 /// /// 信息 /// 任务 - Task ImportFromUIGFAsync(UIGF uigf); + ValueTask ImportFromUIGFAsync(UIGF uigf); /// /// 异步初始化 /// /// 取消令牌 /// 是否初始化成功 - ValueTask InitializeAsync(CancellationToken token); + ValueTask InitializeAsync(CancellationToken token = default); /// /// 刷新祈愿记录 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IUIGFExportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IUIGFExportService.cs index 702a17fa..408da5c1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IUIGFExportService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IUIGFExportService.cs @@ -17,5 +17,5 @@ internal interface IUIGFExportService /// 元数据上下文 /// 存档 /// UIGF - Task ExportAsync(GachaLogServiceContext context, GachaArchive archive); + ValueTask ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IUIGFImportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IUIGFImportService.cs index feab2181..3e409e71 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IUIGFImportService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IUIGFImportService.cs @@ -17,5 +17,5 @@ internal interface IUIGFImportService /// 祈愿记录服务上下文 /// 数据 /// 存档 - Task ImportAsync(GachaLogServiceContext context, UIGF uigf); + Task ImportAsync(GachaLogServiceMetadataContext context, UIGF uigf); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFExportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFExportService.cs index 5a6ec3df..f94297b7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFExportService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFExportService.cs @@ -18,7 +18,7 @@ internal sealed partial class UIGFExportService : IUIGFExportService private readonly ITaskContext taskContext; /// - public async Task ExportAsync(GachaLogServiceContext context, GachaArchive archive) + public async ValueTask ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive) { await taskContext.SwitchToBackgroundAsync(); using (IServiceScope scope = serviceProvider.CreateScope()) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs index 10579235..508b9837 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs @@ -20,7 +20,7 @@ internal sealed partial class UIGFImportService : IUIGFImportService private readonly ITaskContext taskContext; /// - public async Task ImportAsync(GachaLogServiceContext context, UIGF uigf) + public async Task ImportAsync(GachaLogServiceMetadataContext context, UIGF uigf) { await taskContext.SwitchToBackgroundAsync(); using (IServiceScope scope = serviceProvider.CreateScope()) diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModelSlim.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModelSlim.cs index 8186f4b6..40e53f24 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModelSlim.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModelSlim.cs @@ -37,7 +37,7 @@ internal sealed class GachaLogViewModelSlim : Abstraction.ViewModelSlim list = await gachaLogService.GetStatisticsSlimsAsync().ConfigureAwait(false); + List list = await gachaLogService.GetStatisticsSlimListAsync().ConfigureAwait(false); await taskContext.SwitchToMainThreadAsync(); StatisticsList = list; IsInitialized = true;