fix hutao cloud fetch merge and remove archive crash

This commit is contained in:
DismissedLight
2023-09-02 22:12:37 +08:00
parent 1594edc16c
commit 38747298b5
12 changed files with 111 additions and 33 deletions

View File

@@ -45,13 +45,10 @@ internal sealed partial class ScopedDbCurrent<TEntity, TMessage>
DbSet<TEntity> dbSet = appDbContext.Set<TEntity>();
// only update when not processing a deletion
if (value is not null)
if (value is not null && current is not null)
{
if (current is not null)
{
current.IsSelected = false;
dbSet.UpdateAndSave(current);
}
current.IsSelected = false;
dbSet.UpdateAndSave(current);
}
TMessage message = new() { OldValue = current, NewValue = value };

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Extension;
@@ -24,6 +25,17 @@ internal static partial class EnumerableExtension
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this Collection<TSource>? source)
{
if (source is { Count: > 0 })
{
return false;
}
return true;
}
/// <summary>
/// 移除集合中满足条件的项
/// </summary>

View File

@@ -15,13 +15,11 @@ internal static partial class EnumerableExtension
public static bool IsNullOrEmpty<TKey, TValue>([NotNullWhen(false)] this Dictionary<TKey, TValue>? source)
where TKey : notnull
{
if (source is { } dict)
if (source is { Count: >0 })
{
// empty
return dict.Count <= 0;
return false;
}
// null
return true;
}

View File

@@ -160,6 +160,28 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
return item?.Id ?? long.MaxValue;
}
public async ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token)
{
GachaItem? item = null;
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// TODO: replace with MaxBy
// https://github.com/dotnet/efcore/issues/25566
// .MaxBy(i => i.Id);
item = await appDbContext.GachaItems
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId && i.QueryType == queryType)
.OrderBy(i => i.Id)
.FirstOrDefaultAsync(token)
.ConfigureAwait(false);
}
return item?.Id ?? long.MaxValue;
}
public async ValueTask AddGachaArchiveAsync(GachaArchive archive)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -203,6 +225,18 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public async ValueTask<GachaArchive?> GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.GachaArchives
.AsNoTracking()
.SingleOrDefaultAsync(a => a.InnerId == archiveId, token)
.ConfigureAwait(false);
}
}
public async ValueTask<GachaArchive?> GetGachaArchiveByUidAsync(string uid, CancellationToken token)
{
using (IServiceScope scope = serviceProvider.CreateScope())

View File

@@ -52,7 +52,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
}
/// <inheritdoc/>
public async ValueTask<ValueResult<bool, GachaArchive?>> RetrieveGachaItemsAsync(string uid, CancellationToken token = default)
public async ValueTask<ValueResult<bool, Guid>> RetrieveGachaItemsAsync(string uid, CancellationToken token = default)
{
GachaArchive? archive = await gachaLogDbService
.GetGachaArchiveByUidAsync(uid, token)
@@ -63,7 +63,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
if (!resp.IsOk())
{
return new(false, null);
return new(false, default);
}
if (archive is null)
@@ -74,7 +74,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
List<Model.Entity.GachaItem> gachaItems = resp.Data.SelectList(i => Model.Entity.GachaItem.From(archive.InnerId, i));
await gachaLogDbService.AddGachaItemsAsync(gachaItems).ConfigureAwait(false);
return new(true, archive);
return new(true, archive.InnerId);
}
/// <inheritdoc/>
@@ -120,7 +120,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
if (archive is not null)
{
endIds[type] = await gachaLogDbService
.GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(archive.InnerId, type, token)
.GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(archive.InnerId, type, token)
.ConfigureAwait(false);
}
}

View File

@@ -82,6 +82,7 @@ internal sealed partial class GachaLogService : IGachaLogService
public async ValueTask<GachaStatistics> GetStatisticsAsync(GachaArchive? archive)
{
archive ??= CurrentArchive;
archive ??= ArchiveCollection?.FirstOrDefault();
ArgumentNullException.ThrowIfNull(archive);
// Return statistics
@@ -147,13 +148,33 @@ internal sealed partial class GachaLogService : IGachaLogService
{
ArgumentNullException.ThrowIfNull(archiveCollection);
// Sync cache
await taskContext.SwitchToMainThreadAsync();
archiveCollection.Remove(archive);
// Sync database
await taskContext.SwitchToBackgroundAsync();
await gachaLogDbService.DeleteGachaArchiveByIdAsync(archive.InnerId).ConfigureAwait(false);
// Sync cache
await taskContext.SwitchToMainThreadAsync();
archiveCollection.Remove(archive);
}
public async ValueTask<GachaArchive> EnsureArchiveInCollectionAsync(Guid archiveId, CancellationToken token = default)
{
ArgumentNullException.ThrowIfNull(ArchiveCollection);
if (ArchiveCollection.SingleOrDefault(a => a.InnerId == archiveId) is { } archive)
{
return archive;
}
else
{
// sync cache
GachaArchive? newArchive = await gachaLogDbService.GetGachaArchiveByIdAsync(archiveId, token).ConfigureAwait(false);
ArgumentNullException.ThrowIfNull(newArchive);
await taskContext.SwitchToMainThreadAsync();
ArchiveCollection.Add(newArchive);
return newArchive;
}
}
private async ValueTask<ValueResult<bool, GachaArchive?>> FetchGachaLogsAsync(GachaLogQuery query, bool isLazy, IProgress<GachaLogFetchStatus> progress, CancellationToken token)

View File

@@ -21,6 +21,8 @@ internal interface IGachaLogDbService
void DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId);
ValueTask<GachaArchive?> GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token);
ValueTask<GachaArchive?> GetGachaArchiveByUidAsync(string uid, CancellationToken token);
ObservableCollection<GachaArchive> GetGachaArchiveCollection();
@@ -36,4 +38,6 @@ internal interface IGachaLogDbService
long GetOldestGachaItemIdByArchiveId(Guid archiveId);
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
}

View File

@@ -36,7 +36,7 @@ internal interface IGachaLogHutaoCloudService
/// <param name="uid">uid</param>
/// <param name="token">取消令牌</param>
/// <returns>是否获取成功</returns>
ValueTask<ValueResult<bool, GachaArchive?>> RetrieveGachaItemsAsync(string uid, CancellationToken token = default);
ValueTask<ValueResult<bool, Guid>> RetrieveGachaItemsAsync(string uid, CancellationToken token = default);
/// <summary>
/// 异步上传祈愿记录

View File

@@ -25,6 +25,8 @@ internal interface IGachaLogService
/// </summary>
ObservableCollection<GachaArchive>? ArchiveCollection { get; }
ValueTask<GachaArchive> EnsureArchiveInCollectionAsync(Guid archiveId, CancellationToken token = default(CancellationToken));
/// <summary>
/// 导出为一个新的UIGF对象
/// </summary>

View File

@@ -108,11 +108,14 @@
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Style="{StaticResource BaseTextBlockStyle}"
Text="{Binding Uid}"/>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{Binding Uid}"/>
<TextBlock
Opacity="0.7"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding ItemCount}"/>
</StackPanel>
<StackPanel
Grid.Column="1"
Margin="6,0,0,0"

View File

@@ -287,12 +287,13 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
{
if (uid is not null)
{
ValueResult<bool, GachaArchive?> result = await HutaoCloudViewModel.RetrieveAsync(uid).ConfigureAwait(false);
ValueResult<bool, Guid> result = await HutaoCloudViewModel.RetrieveAsync(uid).ConfigureAwait(false);
if (result.TryGetValue(out GachaArchive? archive))
if (result.TryGetValue(out Guid archiveId))
{
GachaArchive archive = await gachaLogService.EnsureArchiveInCollectionAsync(archiveId).ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
Archives?.AddIfNotContains(archive);
await SetSelectedArchiveAndUpdateStatisticsAsync(archive, true).ConfigureAwait(false);
}
}
@@ -318,6 +319,15 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
{
await UpdateStatisticsAsync(archive).ConfigureAwait(false);
}
else
{
// 删光了存档或使用 Ctrl 取消了存档选择时触发
// 因此我们在这里额外判断一次是否删光了存档
if (Archives.IsNullOrEmpty())
{
Statistics = null;
}
}
}
}

View File

@@ -52,7 +52,7 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel
/// </summary>
/// <param name="uid">uid</param>
/// <returns>祈愿记录</returns>
internal async ValueTask<ValueResult<bool, GachaArchive?>> RetrieveAsync(string uid)
internal async ValueTask<ValueResult<bool, Guid>> RetrieveAsync(string uid)
{
ContentDialog dialog = await contentDialogFactory
.CreateForIndeterminateProgressAsync(SH.ViewModelGachaLogRetrieveFromHutaoCloudProgress)
@@ -64,14 +64,11 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel
}
}
/// <inheritdoc/>
protected override async Task OpenUIAsync()
protected override async ValueTask<bool> InitializeUIAsync()
{
await hutaoUserService.InitializeAsync().ConfigureAwait(false);
await RefreshUidCollectionAsync().ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
IsInitialized = true;
return true;
}
[Command("NavigateToAfdianSkuCommand")]