mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
refactor gacha service 2
This commit is contained in:
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Extension;
|
||||
internal static partial class EnumerableExtension
|
||||
{
|
||||
/// <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);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// <returns>平均值</returns>
|
||||
public static byte Average(this in ReadOnlySpan<byte> 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<T> AsSpan<T>(this List<T> list)
|
||||
{
|
||||
return CollectionsMarshal.AsSpan(list);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<GachaStatistics> CreateAsync(IOrderedQueryable<GachaItem> items, GachaLogServiceContext context)
|
||||
public async ValueTask<GachaStatistics> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
List<GachaEvent> gachaEvents = await metadataService.GetGachaEventsAsync().ConfigureAwait(false);
|
||||
@@ -37,9 +38,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
|
||||
private static GachaStatistics CreateCore(
|
||||
IServiceProvider serviceProvider,
|
||||
IOrderedQueryable<GachaItem> items,
|
||||
List<GachaItem> items,
|
||||
List<HistoryWishBuilder> historyWishBuilders,
|
||||
in GachaLogServiceContext context,
|
||||
in GachaLogServiceMetadataContext context,
|
||||
bool isEmptyHistoryWishVisible)
|
||||
{
|
||||
TypedWishSummaryBuilder standardWishBuilder = new(
|
||||
@@ -65,9 +66,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
Dictionary<Weapon, int> purpleWeaponCounter = new();
|
||||
Dictionary<Weapon, int> 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<GachaStatisticsSlim> CreateAsync(IOrderedQueryable<GachaItem> items, GachaLogServiceContext context)
|
||||
public async ValueTask<GachaStatisticsSlim> CreateAsync(List<GachaItem> 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<GachaItem> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ internal sealed class HistoryWishBuilder
|
||||
/// <param name="gachaEvent">卡池配置</param>
|
||||
/// <param name="context">祈愿记录上下文</param>
|
||||
[SuppressMessage("", "SH002")]
|
||||
public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceContext context)
|
||||
public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceMetadataContext context)
|
||||
{
|
||||
this.gachaEvent = gachaEvent;
|
||||
ConfigType = gachaEvent.Type;
|
||||
|
||||
@@ -18,5 +18,5 @@ internal interface IGachaStatisticsFactory
|
||||
/// <param name="items">物品列表</param>
|
||||
/// <param name="context">祈愿记录上下文</param>
|
||||
/// <returns>祈愿统计对象</returns>
|
||||
ValueTask<GachaStatistics> CreateAsync(IOrderedQueryable<GachaItem> items, GachaLogServiceContext context);
|
||||
ValueTask<GachaStatistics> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context);
|
||||
}
|
||||
@@ -17,5 +17,5 @@ internal interface IGachaStatisticsSlimFactory
|
||||
/// <param name="items">排序的物品</param>
|
||||
/// <param name="context">祈愿记录服务上下文</param>
|
||||
/// <returns>简化的祈愿统计</returns>
|
||||
ValueTask<GachaStatisticsSlim> CreateAsync(IOrderedQueryable<GachaItem> items, GachaLogServiceContext context);
|
||||
ValueTask<GachaStatisticsSlim> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context);
|
||||
}
|
||||
@@ -127,10 +127,6 @@ internal sealed class TypedWishSummaryBuilder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换到类型化祈愿统计信息
|
||||
/// </summary>
|
||||
/// <returns>类型化祈愿统计信息</returns>
|
||||
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;
|
||||
|
||||
@@ -15,6 +15,7 @@ internal readonly struct GachaArchiveInitializationContext
|
||||
/// <summary>
|
||||
/// 任务上下文
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public readonly ITaskContext TaskContext;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,5 +20,5 @@ internal static class GachaLog
|
||||
GachaConfigType.StandardWish,
|
||||
GachaConfigType.AvatarEventWish,
|
||||
GachaConfigType.WeaponEventWish,
|
||||
}.ToImmutableList();
|
||||
}.ToImmutableList(); // TODO: FrozenSet
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
@@ -58,7 +58,7 @@ internal struct GachaLogFetchContext
|
||||
/// <param name="serviceProvider">服务提供器</param>
|
||||
/// <param name="serviceContext">祈愿服务上下文</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.serviceContext = serviceContext;
|
||||
@@ -99,8 +99,8 @@ internal struct GachaLogFetchContext
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
ITaskContext taskContext = scope.ServiceProvider.GetRequiredService<ITaskContext>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GachaLogService> logger;
|
||||
private readonly GachaInfoClient gachaInfoClient;
|
||||
private readonly IGachaLogDbService gachaLogDbService;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private GachaLogServiceContext context;
|
||||
private GachaLogServiceMetadataContext context;
|
||||
private ObservableCollection<GachaArchive>? archiveCollection;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public GachaArchive? CurrentArchive
|
||||
@@ -48,11 +53,11 @@ internal sealed partial class GachaLogService : IGachaLogService
|
||||
/// <inheritdoc/>
|
||||
public ObservableCollection<GachaArchive>? ArchiveCollection
|
||||
{
|
||||
get => context.ArchiveCollection;
|
||||
get => archiveCollection;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<bool> InitializeAsync(CancellationToken token)
|
||||
public async ValueTask<bool> InitializeAsync(CancellationToken token = default)
|
||||
{
|
||||
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.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;
|
||||
}
|
||||
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<AppDbContext>();
|
||||
IOrderedQueryable<GachaItem> items = appDbContext.GachaItems
|
||||
.Where(i => i.ArchiveId == archive.InnerId)
|
||||
.OrderBy(i => i.Id);
|
||||
|
||||
return await gachaStatisticsFactory.CreateAsync(items, context).ConfigureAwait(false);
|
||||
}
|
||||
List<GachaItem> items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId);
|
||||
return await gachaStatisticsFactory.CreateAsync(items, context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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<GachaStatisticsSlim> statistics = new();
|
||||
foreach (GachaArchive archive in appDbContext.GachaArchives)
|
||||
{
|
||||
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;
|
||||
List<GachaItem> items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId);
|
||||
GachaStatisticsSlim slim = await gachaStatisticsSlimFactory.CreateAsync(items, context).ConfigureAwait(false);
|
||||
slim.Uid = archive.Uid;
|
||||
statistics.Add(slim);
|
||||
}
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<UIGF> ExportToUIGFAsync(GachaArchive archive)
|
||||
public ValueTask<UIGF> ExportToUIGFAsync(GachaArchive archive)
|
||||
{
|
||||
return gachaLogExportService.ExportAsync(context, archive);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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
|
||||
/// <inheritdoc/>
|
||||
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<AppDbContext>();
|
||||
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<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
|
||||
{
|
||||
ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId);
|
||||
ObservableCollection<GachaArchive> GetGachaArchiveCollection();
|
||||
|
||||
List<GachaItem> GetGachaItemListByArchiveId(Guid archiveId);
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Snap.Hutao.Service.GachaLog;
|
||||
/// <summary>
|
||||
/// 祈愿记录服务上下文
|
||||
/// </summary>
|
||||
internal readonly struct GachaLogServiceContext
|
||||
internal readonly struct GachaLogServiceMetadataContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品缓存
|
||||
@@ -45,6 +45,7 @@ internal readonly struct GachaLogServiceContext
|
||||
/// <summary>
|
||||
/// 存档集合
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public readonly ObservableCollection<GachaArchive> ArchiveCollection;
|
||||
|
||||
/// <summary>
|
||||
@@ -59,19 +60,16 @@ internal readonly struct GachaLogServiceContext
|
||||
/// <param name="idWeaponMap">Id 武器 映射</param>
|
||||
/// <param name="nameAvatarMap">名称 角色 映射</param>
|
||||
/// <param name="nameWeaponMap">名称 武器 映射</param>
|
||||
/// <param name="archiveCollection">存档集合</param>
|
||||
public GachaLogServiceContext(
|
||||
public GachaLogServiceMetadataContext(
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap,
|
||||
Dictionary<WeaponId, Weapon> idWeaponMap,
|
||||
Dictionary<string, Avatar> nameAvatarMap,
|
||||
Dictionary<string, Weapon> nameWeaponMap,
|
||||
ObservableCollection<GachaArchive> archiveCollection)
|
||||
Dictionary<string, Weapon> nameWeaponMap)
|
||||
{
|
||||
IdAvatarMap = idAvatarMap;
|
||||
IdWeaponMap = idWeaponMap;
|
||||
NameAvatarMap = nameAvatarMap;
|
||||
NameWeaponMap = nameWeaponMap;
|
||||
ArchiveCollection = archiveCollection;
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
@@ -30,7 +30,7 @@ internal interface IGachaLogService
|
||||
/// </summary>
|
||||
/// <param name="archive">存档</param>
|
||||
/// <returns>UIGF对象</returns>
|
||||
Task<UIGF> ExportToUIGFAsync(GachaArchive archive);
|
||||
ValueTask<UIGF> ExportToUIGFAsync(GachaArchive archive);
|
||||
|
||||
/// <summary>
|
||||
/// 获得对应的祈愿统计
|
||||
@@ -42,22 +42,23 @@ internal interface IGachaLogService
|
||||
/// <summary>
|
||||
/// 异步获取简化的祈愿统计列表
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>简化的祈愿统计列表</returns>
|
||||
Task<List<GachaStatisticsSlim>> GetStatisticsSlimsAsync();
|
||||
ValueTask<List<GachaStatisticsSlim>> GetStatisticsSlimListAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步从UIGF导入数据
|
||||
/// </summary>
|
||||
/// <param name="uigf">信息</param>
|
||||
/// <returns>任务</returns>
|
||||
Task ImportFromUIGFAsync(UIGF uigf);
|
||||
ValueTask ImportFromUIGFAsync(UIGF uigf);
|
||||
|
||||
/// <summary>
|
||||
/// 异步初始化
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>是否初始化成功</returns>
|
||||
ValueTask<bool> InitializeAsync(CancellationToken token);
|
||||
ValueTask<bool> InitializeAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 刷新祈愿记录
|
||||
|
||||
@@ -17,5 +17,5 @@ internal interface IUIGFExportService
|
||||
/// <param name="context">元数据上下文</param>
|
||||
/// <param name="archive">存档</param>
|
||||
/// <returns>UIGF</returns>
|
||||
Task<UIGF> ExportAsync(GachaLogServiceContext context, GachaArchive archive);
|
||||
ValueTask<UIGF> ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive);
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@ internal interface IUIGFImportService
|
||||
/// <param name="context">祈愿记录服务上下文</param>
|
||||
/// <param name="uigf">数据</param>
|
||||
/// <returns>存档</returns>
|
||||
Task<GachaArchive> ImportAsync(GachaLogServiceContext context, UIGF uigf);
|
||||
Task<GachaArchive> ImportAsync(GachaLogServiceMetadataContext context, UIGF uigf);
|
||||
}
|
||||
@@ -18,7 +18,7 @@ internal sealed partial class UIGFExportService : IUIGFExportService
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<UIGF> ExportAsync(GachaLogServiceContext context, GachaArchive archive)
|
||||
public async ValueTask<UIGF> ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
|
||||
@@ -20,7 +20,7 @@ internal sealed partial class UIGFImportService : IUIGFImportService
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<GachaArchive> ImportAsync(GachaLogServiceContext context, UIGF uigf)
|
||||
public async Task<GachaArchive> ImportAsync(GachaLogServiceMetadataContext context, UIGF uigf)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
|
||||
@@ -37,7 +37,7 @@ internal sealed class GachaLogViewModelSlim : Abstraction.ViewModelSlim<View.Pag
|
||||
|
||||
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();
|
||||
StatisticsList = list;
|
||||
IsInitialized = true;
|
||||
|
||||
Reference in New Issue
Block a user