diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs index 0c5ef13c..180987c3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs @@ -1860,6 +1860,15 @@ namespace Snap.Hutao.Resource.Localization { } } + /// + /// 查找类似 不支持的 UIGF 版本 的本地化字符串。 + /// + internal static string ServiceUIGFImportUnsupportedVersion { + get { + return ResourceManager.GetString("ServiceUIGFImportUnsupportedVersion", resourceCulture); + } + } + /// /// 查找类似 多个用户记录为选中状态 的本地化字符串。 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 1150b286..d2f3ac95 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -773,6 +773,9 @@ 签到成功,{0}×{1} + + 不支持的 UIGF 版本 + 多个用户记录为选中状态 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaItemSaveContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaItemSaveContext.cs index bd8a88e5..9c031b2f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaItemSaveContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaItemSaveContext.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Entity; +using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; namespace Snap.Hutao.Service.GachaLog; @@ -20,6 +21,8 @@ internal readonly struct GachaItemSaveContext /// public readonly bool IsLazy; + public readonly GachaConfigType QueryType; + /// /// 结尾 Id /// @@ -30,10 +33,11 @@ internal readonly struct GachaItemSaveContext /// public readonly IGachaLogDbService GachaLogDbService; - public GachaItemSaveContext(List itemsToAdd, bool isLazy, long endId, IGachaLogDbService gachaLogDbService) + public GachaItemSaveContext(List itemsToAdd, bool isLazy, GachaConfigType queryType, long endId, IGachaLogDbService gachaLogDbService) { ItemsToAdd = itemsToAdd; IsLazy = isLazy; + QueryType = queryType; EndId = endId; GachaLogDbService = gachaLogDbService; } @@ -45,7 +49,7 @@ internal readonly struct GachaItemSaveContext // 全量刷新 if (!IsLazy) { - GachaLogDbService.DeleteNewerGachaItemsByArchiveIdAndEndId(archive.InnerId, EndId); + GachaLogDbService.DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(archive.InnerId, QueryType, EndId); } GachaLogDbService.AddGachaItems(ItemsToAdd); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs index b963b45d..39225a7e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs @@ -139,6 +139,27 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService return item?.Id ?? long.MaxValue; } + public long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType) + { + GachaItem? item = null; + + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + + // TODO: replace with MaxBy + // https://github.com/dotnet/efcore/issues/25566 + // .MaxBy(i => i.Id); + item = appDbContext.GachaItems + .AsNoTracking() + .Where(i => i.ArchiveId == archiveId && i.QueryType == queryType) + .OrderBy(i => i.Id) + .FirstOrDefault(); + } + + return item?.Id ?? long.MaxValue; + } + public async ValueTask AddGachaArchiveAsync(GachaArchive archive) { using (IServiceScope scope = serviceProvider.CreateScope()) @@ -212,14 +233,14 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService } } - public void DeleteNewerGachaItemsByArchiveIdAndEndId(Guid archiveId, long endId) + public void DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId) { using (IServiceScope scope = serviceProvider.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); appDbContext.GachaItems .AsNoTracking() - .Where(i => i.ArchiveId == archiveId) + .Where(i => i.ArchiveId == archiveId && i.QueryType == queryType) .Where(i => i.Id >= endId) .ExecuteDelete(); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs index 756d04b4..e243efe7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs @@ -140,7 +140,7 @@ internal struct GachaLogFetchContext // While no item is fetched, archive can be null. if (TargetArchive is not null) { - GachaItemSaveContext saveContext = new(ItemsToAdd, isLazy, QueryOptions.EndId, gachaLogDbService); + GachaItemSaveContext saveContext = new(ItemsToAdd, isLazy, QueryOptions.Type, QueryOptions.EndId, gachaLogDbService); saveContext.SaveItems(TargetArchive); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs index 3796c577..4ff85ee4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs @@ -214,6 +214,7 @@ internal sealed partial class GachaLogService : IGachaLogService break; } + // save items for each queryType token.ThrowIfCancellationRequested(); fetchContext.SaveItems(); await Delay.Random(1000, 2000).ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs index 4940c1d6..ffa59866 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs @@ -19,7 +19,7 @@ internal interface IGachaLogDbService ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId); - void DeleteNewerGachaItemsByArchiveIdAndEndId(Guid archiveId, long endId); + void DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId); ValueTask GetGachaArchiveByUidAsync(string uid, CancellationToken token); @@ -34,4 +34,6 @@ internal interface IGachaLogDbService ValueTask GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token); long GetOldestGachaItemIdByArchiveId(Guid archiveId); + + long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs index d0c06ddf..93fd0ccf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs @@ -5,6 +5,7 @@ using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.InterChange.GachaLog; using Snap.Hutao.Service.Metadata; +using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; using System.Collections.ObjectModel; namespace Snap.Hutao.Service.GachaLog; @@ -35,36 +36,39 @@ internal sealed partial class UIGFImportService : IUIGFImportService GachaArchiveOperation.GetOrAdd(gachaLogDbService, taskContext, uigf.Info.Uid, archives, out GachaArchive? archive); Guid archiveId = archive.InnerId; - long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveId(archiveId); - - logger.LogInformation("Last Id to trim with: [{Id}]", trimId); - _ = uigf.IsCurrentVersionSupported(out UIGFVersion version); - List toAdd = version switch + foreach (GachaConfigType queryType in GachaLog.QueryTypes) { - UIGFVersion.Major2Minor3OrHigher => uigf.List - .OrderByDescending(i => i.Id) - .Where(i => i.Id < trimId) - .Select(i => GachaItem.From(archiveId, i)) - .ToList(), - UIGFVersion.Major2Minor2OrLower => uigf.List - .OrderByDescending(i => i.Id) - .Where(i => i.Id < trimId) - .Select(i => GachaItem.From(archiveId, i, context.GetItemId(i))) - .ToList(), - _ => new(), - }; + long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveIdAndQueryType(archiveId, queryType); + logger.LogInformation("Last Id to trim with: [{Id}]", trimId); - // 越早的记录手工导入的可能性越高 - // 错误率相对来说会更高 - // 因此从尾部开始查找 - if (toAdd.LastOrDefault(item => item.ItemId is 0U) is { } item) - { - ThrowHelper.InvalidOperation(SH.ServiceGachaLogUIGFImportItemInvalidFormat.Format(item.Id)); + List currentTypeToAdd = version switch + { + UIGFVersion.Major2Minor3OrHigher => uigf.List + .Where(i => i.UIGFGachaType == queryType && i.Id < trimId) + .OrderByDescending(i => i.Id) + .Select(i => GachaItem.From(archiveId, i)) + .ToList(), + UIGFVersion.Major2Minor2OrLower => uigf.List + .Where(i => i.UIGFGachaType == queryType && i.Id < trimId) + .OrderByDescending(i => i.Id) + .Select(i => GachaItem.From(archiveId, i, context.GetItemId(i))) + .ToList(), + _ => throw ThrowHelper.InvalidOperation(SH.ServiceUIGFImportUnsupportedVersion), + }; + + // 越早的记录手工导入的可能性越高 + // 错误率相对来说会更高 + // 因此从尾部开始查找 + if (currentTypeToAdd.LastOrDefault(item => item.ItemId is 0U) is { } item) + { + ThrowHelper.InvalidOperation(SH.ServiceGachaLogUIGFImportItemInvalidFormat.Format(item.Id)); + } + + await gachaLogDbService.AddGachaItemsAsync(currentTypeToAdd).ConfigureAwait(false); } - await gachaLogDbService.AddGachaItemsAsync(toAdd).ConfigureAwait(false); return archive; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml index 3572727d..cfc62001 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml @@ -61,7 +61,7 @@ Text="{shcm:ResourceString Name=ViewPageGachaLogRefreshByManualInput}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs index 8019cfa0..5352e0c6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs @@ -31,6 +31,8 @@ internal struct GachaLogQueryOptions /// public long EndId; + public GachaConfigType Type; + /// /// Keys required: /// authkey_ver @@ -49,18 +51,20 @@ internal struct GachaLogQueryOptions /// 构造一个新的祈愿记录请求配置 /// /// 原始查询字符串 - /// 祈愿类型 + /// 祈愿类型 /// 终止Id - public GachaLogQueryOptions(in GachaLogQuery query, GachaConfigType type, long endId = 0L) + public GachaLogQueryOptions(in GachaLogQuery query, GachaConfigType queryType) { IsOversea = query.IsOversea; + + // 对于每个类型我们需要单独创建 + // 对应类型的 GachaLogQueryOptions + Type = queryType; innerQuery = QueryString.Parse(query.Query); // innerQuery.Set("lang", "zh-cn"); - innerQuery.Set("gacha_type", (int)type); + innerQuery.Set("gacha_type", (int)queryType); innerQuery.Set("size", Size); - - EndId = endId; } /// @@ -82,10 +86,6 @@ internal struct GachaLogQueryOptions return queryString.ToString(); } - /// - /// 转换到查询字符串 - /// - /// 匹配的查询字符串 public readonly string ToQueryString() { // Make the cached end id into query.