refactor gacha service 2

This commit is contained in:
Lightczx
2023-07-27 22:26:19 +08:00
parent 53044b0dda
commit de9abcfad4
21 changed files with 145 additions and 151 deletions

View File

@@ -12,7 +12,7 @@ namespace Snap.Hutao.Extension;
internal static partial class EnumerableExtension internal static partial class EnumerableExtension
{ {
/// <inheritdoc cref="Enumerable.Average(IEnumerable{int})"/> /// <inheritdoc cref="Enumerable.Average(IEnumerable{int})"/>
public static double SpanAverage(this List<int> source) public static double Average(this List<int> source)
{ {
Span<int> span = CollectionsMarshal.AsSpan(source); Span<int> span = CollectionsMarshal.AsSpan(source);
if (span.IsEmpty) if (span.IsEmpty)
@@ -21,7 +21,7 @@ internal static partial class EnumerableExtension
} }
long sum = 0; long sum = 0;
foreach (int item in span) foreach (ref readonly int item in span)
{ {
sum += item; sum += item;
} }

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Extension; namespace Snap.Hutao.Extension;
@@ -59,7 +60,7 @@ internal static class SpanExtension
/// <returns>平均值</returns> /// <returns>平均值</returns>
public static byte Average(this in ReadOnlySpan<byte> span) public static byte Average(this in ReadOnlySpan<byte> span)
{ {
if (span.Length == 0) if (span.IsEmpty)
{ {
return 0; return 0;
} }
@@ -74,4 +75,9 @@ internal static class SpanExtension
return unchecked((byte)(sum / count)); return unchecked((byte)(sum / count));
} }
public static Span<T> AsSpan<T>(this List<T> list)
{
return CollectionsMarshal.AsSpan(list);
}
} }

View File

@@ -1,20 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database; 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;
using Snap.Hutao.Model.Entity.Database; 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; namespace Snap.Hutao.Service.DailyNote;

View File

@@ -9,6 +9,7 @@ using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Weapon; using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata;
using Snap.Hutao.ViewModel.GachaLog; using Snap.Hutao.ViewModel.GachaLog;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Service.GachaLog.Factory; namespace Snap.Hutao.Service.GachaLog.Factory;
@@ -26,7 +27,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
private readonly AppOptions options; private readonly AppOptions options;
/// <inheritdoc/> /// <inheritdoc/>
public async ValueTask<GachaStatistics> CreateAsync(IOrderedQueryable<GachaItem> items, GachaLogServiceContext context) public async ValueTask<GachaStatistics> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context)
{ {
await taskContext.SwitchToBackgroundAsync(); await taskContext.SwitchToBackgroundAsync();
List<GachaEvent> gachaEvents = await metadataService.GetGachaEventsAsync().ConfigureAwait(false); List<GachaEvent> gachaEvents = await metadataService.GetGachaEventsAsync().ConfigureAwait(false);
@@ -37,9 +38,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
private static GachaStatistics CreateCore( private static GachaStatistics CreateCore(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IOrderedQueryable<GachaItem> items, List<GachaItem> items,
List<HistoryWishBuilder> historyWishBuilders, List<HistoryWishBuilder> historyWishBuilders,
in GachaLogServiceContext context, in GachaLogServiceMetadataContext context,
bool isEmptyHistoryWishVisible) bool isEmptyHistoryWishVisible)
{ {
TypedWishSummaryBuilder standardWishBuilder = new( TypedWishSummaryBuilder standardWishBuilder = new(
@@ -65,9 +66,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
Dictionary<Weapon, int> purpleWeaponCounter = new(); Dictionary<Weapon, int> purpleWeaponCounter = new();
Dictionary<Weapon, int> blueWeaponCounter = new(); Dictionary<Weapon, int> blueWeaponCounter = new();
// Items are ordered by precise time // Items are ordered by precise time, first is oldest
// first is oldest // 'ref' is not allowed here because we have lambda below
foreach (GachaItem item in items) foreach (GachaItem item in CollectionsMarshal.AsSpan(items))
{ {
// Find target history wish to operate. // Find target history wish to operate.
HistoryWishBuilder? targetHistoryWishBuilder = historyWishBuilders HistoryWishBuilder? targetHistoryWishBuilder = historyWishBuilders

View File

@@ -6,6 +6,7 @@ using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Abstraction; using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.ViewModel.GachaLog; using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Service.GachaLog.Factory; namespace Snap.Hutao.Service.GachaLog.Factory;
@@ -19,10 +20,36 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
private readonly ITaskContext taskContext; private readonly ITaskContext taskContext;
/// <inheritdoc/> /// <inheritdoc/>
public async ValueTask<GachaStatisticsSlim> CreateAsync(IOrderedQueryable<GachaItem> items, GachaLogServiceContext context) public async ValueTask<GachaStatisticsSlim> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context)
{ {
await taskContext.SwitchToBackgroundAsync(); 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<GachaItem> items, GachaLogServiceMetadataContext context)
{
int standardOrangeTracker = 0; int standardOrangeTracker = 0;
int standardPurpleTracker = 0; int standardPurpleTracker = 0;
TypedWishSummarySlim standardWish = new(SH.ServiceGachaLogFactoryPermanentWishName, 90, 10); TypedWishSummarySlim standardWish = new(SH.ServiceGachaLogFactoryPermanentWishName, 90, 10);
@@ -36,7 +63,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
TypedWishSummarySlim weaponWish = new(SH.ServiceGachaLogFactoryWeaponWishName, 80, 10); TypedWishSummarySlim weaponWish = new(SH.ServiceGachaLogFactoryWeaponWishName, 80, 10);
// O(n) operation // O(n) operation
foreach (GachaItem item in items) foreach (ref readonly GachaItem item in CollectionsMarshal.AsSpan(items))
{ {
INameQuality nameQuality = context.GetNameQualityByItemId(item.ItemId); INameQuality nameQuality = context.GetNameQualityByItemId(item.ItemId);
switch (item.QueryType) switch (item.QueryType)
@@ -70,25 +97,4 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
StandardWish = standardWish, 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;
}
}
} }

View File

@@ -30,7 +30,7 @@ internal sealed class HistoryWishBuilder
/// <param name="gachaEvent">卡池配置</param> /// <param name="gachaEvent">卡池配置</param>
/// <param name="context">祈愿记录上下文</param> /// <param name="context">祈愿记录上下文</param>
[SuppressMessage("", "SH002")] [SuppressMessage("", "SH002")]
public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceContext context) public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceMetadataContext context)
{ {
this.gachaEvent = gachaEvent; this.gachaEvent = gachaEvent;
ConfigType = gachaEvent.Type; ConfigType = gachaEvent.Type;

View File

@@ -18,5 +18,5 @@ internal interface IGachaStatisticsFactory
/// <param name="items">物品列表</param> /// <param name="items">物品列表</param>
/// <param name="context">祈愿记录上下文</param> /// <param name="context">祈愿记录上下文</param>
/// <returns>祈愿统计对象</returns> /// <returns>祈愿统计对象</returns>
ValueTask<GachaStatistics> CreateAsync(IOrderedQueryable<GachaItem> items, GachaLogServiceContext context); ValueTask<GachaStatistics> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context);
} }

View File

@@ -17,5 +17,5 @@ internal interface IGachaStatisticsSlimFactory
/// <param name="items">排序的物品</param> /// <param name="items">排序的物品</param>
/// <param name="context">祈愿记录服务上下文</param> /// <param name="context">祈愿记录服务上下文</param>
/// <returns>简化的祈愿统计</returns> /// <returns>简化的祈愿统计</returns>
ValueTask<GachaStatisticsSlim> CreateAsync(IOrderedQueryable<GachaItem> items, GachaLogServiceContext context); ValueTask<GachaStatisticsSlim> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context);
} }

View File

@@ -127,10 +127,6 @@ internal sealed class TypedWishSummaryBuilder
} }
} }
/// <summary>
/// 转换到类型化祈愿统计信息
/// </summary>
/// <returns>类型化祈愿统计信息</returns>
public TypedWishSummary ToTypedWishSummary(AsyncBarrier barrier) public TypedWishSummary ToTypedWishSummary(AsyncBarrier barrier)
{ {
summaryItems.CompleteAdding(guaranteeOrangeThreshold); summaryItems.CompleteAdding(guaranteeOrangeThreshold);
@@ -157,12 +153,11 @@ internal sealed class TypedWishSummaryBuilder
TotalOrangePercent = totalOrangePullTracker / totalCount, TotalOrangePercent = totalOrangePullTracker / totalCount,
TotalPurplePercent = totalPurplePullTracker / totalCount, TotalPurplePercent = totalPurplePullTracker / totalCount,
TotalBluePercent = totalBluePullTracker / totalCount, TotalBluePercent = totalBluePullTracker / totalCount,
AverageOrangePull = averageOrangePullTracker.SpanAverage(), AverageOrangePull = averageOrangePullTracker.Average(),
AverageUpOrangePull = averageUpOrangePullTracker.SpanAverage(), AverageUpOrangePull = averageUpOrangePullTracker.Average(),
OrangeList = summaryItems, OrangeList = summaryItems,
}; };
// TODO: barrier all predictions.
new PullPrediction(serviceProvider, summary, distributionType).PredictAsync(barrier).SafeForget(); new PullPrediction(serviceProvider, summary, distributionType).PredictAsync(barrier).SafeForget();
return summary; return summary;

View File

@@ -15,6 +15,7 @@ internal readonly struct GachaArchiveInitializationContext
/// <summary> /// <summary>
/// 任务上下文 /// 任务上下文
/// </summary> /// </summary>
[Obsolete]
public readonly ITaskContext TaskContext; public readonly ITaskContext TaskContext;
/// <summary> /// <summary>

View File

@@ -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;
/// <summary>
/// 存档操作
/// </summary>
internal static class GachaArchives
{
/// <summary>
/// 初始化存档集合
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="collection">集合</param>
public static void Initialize(IServiceProvider serviceProvider, out ObservableCollection<GachaArchive> collection)
{
try
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
collection = appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
}
}
catch (SqliteException ex)
{
string message = string.Format(SH.ServiceGachaLogArchiveCollectionUserdataCorruptedMessage, ex.Message);
throw ThrowHelper.UserdataCorrupted(message, ex);
}
}
}

View File

@@ -20,5 +20,5 @@ internal static class GachaLog
GachaConfigType.StandardWish, GachaConfigType.StandardWish,
GachaConfigType.AvatarEventWish, GachaConfigType.AvatarEventWish,
GachaConfigType.WeaponEventWish, GachaConfigType.WeaponEventWish,
}.ToImmutableList(); }.ToImmutableList(); // TODO: FrozenSet
} }

View File

@@ -49,7 +49,7 @@ internal struct GachaLogFetchContext
public GachaConfigType CurrentType; public GachaConfigType CurrentType;
private readonly IServiceProvider serviceProvider; private readonly IServiceProvider serviceProvider;
private readonly GachaLogServiceContext serviceContext; private readonly GachaLogServiceMetadataContext serviceContext;
private readonly bool isLazy; private readonly bool isLazy;
/// <summary> /// <summary>
@@ -58,7 +58,7 @@ internal struct GachaLogFetchContext
/// <param name="serviceProvider">服务提供器</param> /// <param name="serviceProvider">服务提供器</param>
/// <param name="serviceContext">祈愿服务上下文</param> /// <param name="serviceContext">祈愿服务上下文</param>
/// <param name="isLazy">是否为懒惰模式</param> /// <param name="isLazy">是否为懒惰模式</param>
public GachaLogFetchContext(IServiceProvider serviceProvider, in GachaLogServiceContext serviceContext, bool isLazy) public GachaLogFetchContext(IServiceProvider serviceProvider, in GachaLogServiceMetadataContext serviceContext, bool isLazy)
{ {
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
this.serviceContext = serviceContext; this.serviceContext = serviceContext;
@@ -99,8 +99,8 @@ internal struct GachaLogFetchContext
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
ITaskContext taskContext = scope.ServiceProvider.GetRequiredService<ITaskContext>(); ITaskContext taskContext = scope.ServiceProvider.GetRequiredService<ITaskContext>();
GachaArchiveInitializationContext initContext = new(taskContext, item.Uid, appDbContext.GachaArchives, serviceContext.ArchiveCollection); GachaArchiveInitializationContext context = new(taskContext, item.Uid, appDbContext.GachaArchives, serviceContext.ArchiveCollection);
GachaArchive.SkipOrInit(initContext, ref TargetArchive); GachaArchive.SkipOrInit(context, ref TargetArchive);
DbEndId ??= TargetArchive.GetEndId(CurrentType, appDbContext.GachaItems); DbEndId ??= TargetArchive.GetEndId(CurrentType, appDbContext.GachaItems);
} }
} }

View File

@@ -1,8 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database; using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Diagnostics; using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database; using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.InterChange.GachaLog; using Snap.Hutao.Model.InterChange.GachaLog;
@@ -34,9 +37,11 @@ internal sealed partial class GachaLogService : IGachaLogService
private readonly IMetadataService metadataService; private readonly IMetadataService metadataService;
private readonly ILogger<GachaLogService> logger; private readonly ILogger<GachaLogService> logger;
private readonly GachaInfoClient gachaInfoClient; private readonly GachaInfoClient gachaInfoClient;
private readonly IGachaLogDbService gachaLogDbService;
private readonly ITaskContext taskContext; private readonly ITaskContext taskContext;
private GachaLogServiceContext context; private GachaLogServiceMetadataContext context;
private ObservableCollection<GachaArchive>? archiveCollection;
/// <inheritdoc/> /// <inheritdoc/>
public GachaArchive? CurrentArchive public GachaArchive? CurrentArchive
@@ -48,11 +53,11 @@ internal sealed partial class GachaLogService : IGachaLogService
/// <inheritdoc/> /// <inheritdoc/>
public ObservableCollection<GachaArchive>? ArchiveCollection public ObservableCollection<GachaArchive>? ArchiveCollection
{ {
get => context.ArchiveCollection; get => archiveCollection;
} }
/// <inheritdoc/> /// <inheritdoc/>
public async ValueTask<bool> InitializeAsync(CancellationToken token) public async ValueTask<bool> InitializeAsync(CancellationToken token = default)
{ {
if (context.IsInitialized) if (context.IsInitialized)
{ {
@@ -67,9 +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.Avatar.Avatar> nameAvatarMap = await metadataService.GetNameToAvatarMapAsync(token).ConfigureAwait(false);
Dictionary<string, Model.Metadata.Weapon.Weapon> nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false); Dictionary<string, Model.Metadata.Weapon.Weapon> nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false);
GachaArchives.Initialize(serviceProvider, out ObservableCollection<GachaArchive> collection); ObservableCollection<GachaArchive> collection = gachaLogDbService.GetGachaArchiveCollection();
context = new(idAvatarMap, idWeaponMap, nameAvatarMap, nameWeaponMap, collection); context = new(idAvatarMap, idWeaponMap, nameAvatarMap, nameWeaponMap);
return true; return true;
} }
else else
@@ -87,48 +92,37 @@ internal sealed partial class GachaLogService : IGachaLogService
// Return statistics // Return statistics
using (ValueStopwatch.MeasureExecution(logger)) using (ValueStopwatch.MeasureExecution(logger))
{ {
using (IServiceScope scope = serviceProvider.CreateScope()) List<GachaItem> items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId);
{ return await gachaStatisticsFactory.CreateAsync(items, context).ConfigureAwait(false);
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
IOrderedQueryable<GachaItem> items = appDbContext.GachaItems
.Where(i => i.ArchiveId == archive.InnerId)
.OrderBy(i => i.Id);
return await gachaStatisticsFactory.CreateAsync(items, context).ConfigureAwait(false);
}
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task<List<GachaStatisticsSlim>> GetStatisticsSlimsAsync() public async ValueTask<List<GachaStatisticsSlim>> GetStatisticsSlimListAsync(CancellationToken token = default)
{ {
using (IServiceScope scope = serviceProvider.CreateScope()) await InitializeAsync(token).ConfigureAwait(false);
ArgumentNullException.ThrowIfNull(ArchiveCollection);
List<GachaStatisticsSlim> statistics = new();
foreach (GachaArchive archive in ArchiveCollection)
{ {
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); List<GachaItem> items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId);
List<GachaStatisticsSlim> statistics = new(); GachaStatisticsSlim slim = await gachaStatisticsSlimFactory.CreateAsync(items, context).ConfigureAwait(false);
foreach (GachaArchive archive in appDbContext.GachaArchives) slim.Uid = archive.Uid;
{ statistics.Add(slim);
IOrderedQueryable<GachaItem> 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;
} }
return statistics;
} }
/// <inheritdoc/> /// <inheritdoc/>
public Task<UIGF> ExportToUIGFAsync(GachaArchive archive) public ValueTask<UIGF> ExportToUIGFAsync(GachaArchive archive)
{ {
return gachaLogExportService.ExportAsync(context, archive); return gachaLogExportService.ExportAsync(context, archive);
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task ImportFromUIGFAsync(UIGF uigf) public async ValueTask ImportFromUIGFAsync(UIGF uigf)
{ {
CurrentArchive = await gachaLogImportService.ImportAsync(context, uigf).ConfigureAwait(false); CurrentArchive = await gachaLogImportService.ImportAsync(context, uigf).ConfigureAwait(false);
} }
@@ -156,19 +150,15 @@ internal sealed partial class GachaLogService : IGachaLogService
/// <inheritdoc/> /// <inheritdoc/>
public async Task RemoveArchiveAsync(GachaArchive archive) public async Task RemoveArchiveAsync(GachaArchive archive)
{ {
ArgumentNullException.ThrowIfNull(archiveCollection);
// Sync cache // Sync cache
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();
context.ArchiveCollection.Remove(archive); archiveCollection.Remove(archive);
// Sync database // Sync database
await taskContext.SwitchToBackgroundAsync(); await taskContext.SwitchToBackgroundAsync();
using (IServiceScope scope = serviceProvider.CreateScope()) await gachaLogDbService.DeleteGachaArchiveByIdAsync(archive.InnerId).ConfigureAwait(false);
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.GachaArchives
.ExecuteDeleteWhereAsync(a => a.InnerId == archive.InnerId)
.ConfigureAwait(false);
}
} }
private static Task RandomDelayAsync(CancellationToken token) private static Task RandomDelayAsync(CancellationToken token)
@@ -246,9 +236,54 @@ internal sealed partial class GachaLogService : IGachaLogService
[Injection(InjectAs.Scoped, typeof(IGachaLogDbService))] [Injection(InjectAs.Scoped, typeof(IGachaLogDbService))]
internal sealed partial class GachaLogDbService : IGachaLogDbService internal sealed partial class GachaLogDbService : IGachaLogDbService
{ {
private readonly IServiceProvider serviceProvider;
public ObservableCollection<GachaArchive> GetGachaArchiveCollection()
{
try
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
}
}
catch (SqliteException ex)
{
string message = string.Format(SH.ServiceGachaLogArchiveCollectionUserdataCorruptedMessage, ex.Message);
throw ThrowHelper.UserdataCorrupted(message, ex);
}
}
public List<GachaItem> GetGachaItemListByArchiveId(Guid archiveId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
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<AppDbContext>();
await appDbContext.GachaArchives
.ExecuteDeleteWhereAsync(a => a.InnerId == archiveId)
.ConfigureAwait(false);
}
}
} }
internal interface IGachaLogDbService internal interface IGachaLogDbService
{ {
ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId);
ObservableCollection<GachaArchive> GetGachaArchiveCollection();
List<GachaItem> GetGachaItemListByArchiveId(Guid archiveId);
} }

View File

@@ -15,7 +15,7 @@ namespace Snap.Hutao.Service.GachaLog;
/// <summary> /// <summary>
/// 祈愿记录服务上下文 /// 祈愿记录服务上下文
/// </summary> /// </summary>
internal readonly struct GachaLogServiceContext internal readonly struct GachaLogServiceMetadataContext
{ {
/// <summary> /// <summary>
/// 物品缓存 /// 物品缓存
@@ -45,6 +45,7 @@ internal readonly struct GachaLogServiceContext
/// <summary> /// <summary>
/// 存档集合 /// 存档集合
/// </summary> /// </summary>
[Obsolete]
public readonly ObservableCollection<GachaArchive> ArchiveCollection; public readonly ObservableCollection<GachaArchive> ArchiveCollection;
/// <summary> /// <summary>
@@ -59,19 +60,16 @@ internal readonly struct GachaLogServiceContext
/// <param name="idWeaponMap">Id 武器 映射</param> /// <param name="idWeaponMap">Id 武器 映射</param>
/// <param name="nameAvatarMap">名称 角色 映射</param> /// <param name="nameAvatarMap">名称 角色 映射</param>
/// <param name="nameWeaponMap">名称 武器 映射</param> /// <param name="nameWeaponMap">名称 武器 映射</param>
/// <param name="archiveCollection">存档集合</param> public GachaLogServiceMetadataContext(
public GachaLogServiceContext(
Dictionary<AvatarId, Avatar> idAvatarMap, Dictionary<AvatarId, Avatar> idAvatarMap,
Dictionary<WeaponId, Weapon> idWeaponMap, Dictionary<WeaponId, Weapon> idWeaponMap,
Dictionary<string, Avatar> nameAvatarMap, Dictionary<string, Avatar> nameAvatarMap,
Dictionary<string, Weapon> nameWeaponMap, Dictionary<string, Weapon> nameWeaponMap)
ObservableCollection<GachaArchive> archiveCollection)
{ {
IdAvatarMap = idAvatarMap; IdAvatarMap = idAvatarMap;
IdWeaponMap = idWeaponMap; IdWeaponMap = idWeaponMap;
NameAvatarMap = nameAvatarMap; NameAvatarMap = nameAvatarMap;
NameWeaponMap = nameWeaponMap; NameWeaponMap = nameWeaponMap;
ArchiveCollection = archiveCollection;
IsInitialized = true; IsInitialized = true;
} }

View File

@@ -30,7 +30,7 @@ internal interface IGachaLogService
/// </summary> /// </summary>
/// <param name="archive">存档</param> /// <param name="archive">存档</param>
/// <returns>UIGF对象</returns> /// <returns>UIGF对象</returns>
Task<UIGF> ExportToUIGFAsync(GachaArchive archive); ValueTask<UIGF> ExportToUIGFAsync(GachaArchive archive);
/// <summary> /// <summary>
/// 获得对应的祈愿统计 /// 获得对应的祈愿统计
@@ -42,22 +42,23 @@ internal interface IGachaLogService
/// <summary> /// <summary>
/// 异步获取简化的祈愿统计列表 /// 异步获取简化的祈愿统计列表
/// </summary> /// </summary>
/// <param name="token">取消令牌</param>
/// <returns>简化的祈愿统计列表</returns> /// <returns>简化的祈愿统计列表</returns>
Task<List<GachaStatisticsSlim>> GetStatisticsSlimsAsync(); ValueTask<List<GachaStatisticsSlim>> GetStatisticsSlimListAsync(CancellationToken token = default);
/// <summary> /// <summary>
/// 异步从UIGF导入数据 /// 异步从UIGF导入数据
/// </summary> /// </summary>
/// <param name="uigf">信息</param> /// <param name="uigf">信息</param>
/// <returns>任务</returns> /// <returns>任务</returns>
Task ImportFromUIGFAsync(UIGF uigf); ValueTask ImportFromUIGFAsync(UIGF uigf);
/// <summary> /// <summary>
/// 异步初始化 /// 异步初始化
/// </summary> /// </summary>
/// <param name="token">取消令牌</param> /// <param name="token">取消令牌</param>
/// <returns>是否初始化成功</returns> /// <returns>是否初始化成功</returns>
ValueTask<bool> InitializeAsync(CancellationToken token); ValueTask<bool> InitializeAsync(CancellationToken token = default);
/// <summary> /// <summary>
/// 刷新祈愿记录 /// 刷新祈愿记录

View File

@@ -17,5 +17,5 @@ internal interface IUIGFExportService
/// <param name="context">元数据上下文</param> /// <param name="context">元数据上下文</param>
/// <param name="archive">存档</param> /// <param name="archive">存档</param>
/// <returns>UIGF</returns> /// <returns>UIGF</returns>
Task<UIGF> ExportAsync(GachaLogServiceContext context, GachaArchive archive); ValueTask<UIGF> ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive);
} }

View File

@@ -17,5 +17,5 @@ internal interface IUIGFImportService
/// <param name="context">祈愿记录服务上下文</param> /// <param name="context">祈愿记录服务上下文</param>
/// <param name="uigf">数据</param> /// <param name="uigf">数据</param>
/// <returns>存档</returns> /// <returns>存档</returns>
Task<GachaArchive> ImportAsync(GachaLogServiceContext context, UIGF uigf); Task<GachaArchive> ImportAsync(GachaLogServiceMetadataContext context, UIGF uigf);
} }

View File

@@ -18,7 +18,7 @@ internal sealed partial class UIGFExportService : IUIGFExportService
private readonly ITaskContext taskContext; private readonly ITaskContext taskContext;
/// <inheritdoc/> /// <inheritdoc/>
public async Task<UIGF> ExportAsync(GachaLogServiceContext context, GachaArchive archive) public async ValueTask<UIGF> ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive)
{ {
await taskContext.SwitchToBackgroundAsync(); await taskContext.SwitchToBackgroundAsync();
using (IServiceScope scope = serviceProvider.CreateScope()) using (IServiceScope scope = serviceProvider.CreateScope())

View File

@@ -20,7 +20,7 @@ internal sealed partial class UIGFImportService : IUIGFImportService
private readonly ITaskContext taskContext; private readonly ITaskContext taskContext;
/// <inheritdoc/> /// <inheritdoc/>
public async Task<GachaArchive> ImportAsync(GachaLogServiceContext context, UIGF uigf) public async Task<GachaArchive> ImportAsync(GachaLogServiceMetadataContext context, UIGF uigf)
{ {
await taskContext.SwitchToBackgroundAsync(); await taskContext.SwitchToBackgroundAsync();
using (IServiceScope scope = serviceProvider.CreateScope()) using (IServiceScope scope = serviceProvider.CreateScope())

View File

@@ -37,7 +37,7 @@ internal sealed class GachaLogViewModelSlim : Abstraction.ViewModelSlim<View.Pag
if (await gachaLogService.InitializeAsync(default).ConfigureAwait(false)) if (await gachaLogService.InitializeAsync(default).ConfigureAwait(false))
{ {
List<GachaStatisticsSlim> list = await gachaLogService.GetStatisticsSlimsAsync().ConfigureAwait(false); List<GachaStatisticsSlim> list = await gachaLogService.GetStatisticsSlimListAsync().ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();
StatisticsList = list; StatisticsList = list;
IsInitialized = true; IsInitialized = true;