fix #882 gachalog import & aggressive merge refresh

This commit is contained in:
DismissedLight
2023-09-02 20:23:25 +08:00
parent 300e99a9ae
commit 7a5dec4291
10 changed files with 84 additions and 40 deletions

View File

@@ -1860,6 +1860,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 不支持的 UIGF 版本 的本地化字符串。
/// </summary>
internal static string ServiceUIGFImportUnsupportedVersion {
get {
return ResourceManager.GetString("ServiceUIGFImportUnsupportedVersion", resourceCulture);
}
}
/// <summary>
/// 查找类似 多个用户记录为选中状态 的本地化字符串。
/// </summary>

View File

@@ -773,6 +773,9 @@
<data name="ServiceSignInSuccessRewardFormat" xml:space="preserve">
<value>签到成功,{0}×{1}</value>
</data>
<data name="ServiceUIGFImportUnsupportedVersion" xml:space="preserve">
<value>不支持的 UIGF 版本</value>
</data>
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
<value>多个用户记录为选中状态</value>
</data>

View File

@@ -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
/// </summary>
public readonly bool IsLazy;
public readonly GachaConfigType QueryType;
/// <summary>
/// 结尾 Id
/// </summary>
@@ -30,10 +33,11 @@ internal readonly struct GachaItemSaveContext
/// </summary>
public readonly IGachaLogDbService GachaLogDbService;
public GachaItemSaveContext(List<GachaItem> itemsToAdd, bool isLazy, long endId, IGachaLogDbService gachaLogDbService)
public GachaItemSaveContext(List<GachaItem> 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);

View File

@@ -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<AppDbContext>();
// 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>();
appDbContext.GachaItems
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId)
.Where(i => i.ArchiveId == archiveId && i.QueryType == queryType)
.Where(i => i.Id >= endId)
.ExecuteDelete();
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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<GachaArchive?> GetGachaArchiveByUidAsync(string uid, CancellationToken token);
@@ -34,4 +34,6 @@ internal interface IGachaLogDbService
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
long GetOldestGachaItemIdByArchiveId(Guid archiveId);
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
}

View File

@@ -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<GachaItem> 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<GachaItem> 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;
}
}

View File

@@ -61,7 +61,7 @@
Text="{shcm:ResourceString Name=ViewPageGachaLogRefreshByManualInput}"/>
<ToggleMenuFlyoutItem
Icon="{shcm:FontIcon Glyph=&#xE1CD;}"
IsChecked="{Binding IsAggressiveRefresh}"
IsChecked="{Binding IsAggressiveRefresh, Mode=TwoWay}"
Text="{shcm:ResourceString Name=ViewPageGachaLogAggressiveRefresh}"/>
</MenuFlyout>
</AppBarButton.Flyout>

View File

@@ -31,6 +31,8 @@ internal struct GachaLogQueryOptions
/// </summary>
public long EndId;
public GachaConfigType Type;
/// <summary>
/// Keys required:
/// authkey_ver
@@ -49,18 +51,20 @@ internal struct GachaLogQueryOptions
/// 构造一个新的祈愿记录请求配置
/// </summary>
/// <param name="query">原始查询字符串</param>
/// <param name="type">祈愿类型</param>
/// <param name="queryType">祈愿类型</param>
/// <param name="endId">终止Id</param>
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;
}
/// <summary>
@@ -82,10 +86,6 @@ internal struct GachaLogQueryOptions
return queryString.ToString();
}
/// <summary>
/// 转换到查询字符串
/// </summary>
/// <returns>匹配的查询字符串</returns>
public readonly string ToQueryString()
{
// Make the cached end id into query.