mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix #882 gachalog import & aggressive merge refresh
This commit is contained in:
@@ -1860,6 +1860,15 @@ namespace Snap.Hutao.Resource.Localization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 不支持的 UIGF 版本 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string ServiceUIGFImportUnsupportedVersion {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ServiceUIGFImportUnsupportedVersion", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 多个用户记录为选中状态 的本地化字符串。
|
/// 查找类似 多个用户记录为选中状态 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -773,6 +773,9 @@
|
|||||||
<data name="ServiceSignInSuccessRewardFormat" xml:space="preserve">
|
<data name="ServiceSignInSuccessRewardFormat" xml:space="preserve">
|
||||||
<value>签到成功,{0}×{1}</value>
|
<value>签到成功,{0}×{1}</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ServiceUIGFImportUnsupportedVersion" xml:space="preserve">
|
||||||
|
<value>不支持的 UIGF 版本</value>
|
||||||
|
</data>
|
||||||
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
|
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
|
||||||
<value>多个用户记录为选中状态</value>
|
<value>多个用户记录为选中状态</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.GachaLog;
|
namespace Snap.Hutao.Service.GachaLog;
|
||||||
|
|
||||||
@@ -20,6 +21,8 @@ internal readonly struct GachaItemSaveContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly bool IsLazy;
|
public readonly bool IsLazy;
|
||||||
|
|
||||||
|
public readonly GachaConfigType QueryType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 结尾 Id
|
/// 结尾 Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -30,10 +33,11 @@ internal readonly struct GachaItemSaveContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly IGachaLogDbService GachaLogDbService;
|
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;
|
ItemsToAdd = itemsToAdd;
|
||||||
IsLazy = isLazy;
|
IsLazy = isLazy;
|
||||||
|
QueryType = queryType;
|
||||||
EndId = endId;
|
EndId = endId;
|
||||||
GachaLogDbService = gachaLogDbService;
|
GachaLogDbService = gachaLogDbService;
|
||||||
}
|
}
|
||||||
@@ -45,7 +49,7 @@ internal readonly struct GachaItemSaveContext
|
|||||||
// 全量刷新
|
// 全量刷新
|
||||||
if (!IsLazy)
|
if (!IsLazy)
|
||||||
{
|
{
|
||||||
GachaLogDbService.DeleteNewerGachaItemsByArchiveIdAndEndId(archive.InnerId, EndId);
|
GachaLogDbService.DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(archive.InnerId, QueryType, EndId);
|
||||||
}
|
}
|
||||||
|
|
||||||
GachaLogDbService.AddGachaItems(ItemsToAdd);
|
GachaLogDbService.AddGachaItems(ItemsToAdd);
|
||||||
|
|||||||
@@ -139,6 +139,27 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
|||||||
return item?.Id ?? long.MaxValue;
|
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)
|
public async ValueTask AddGachaArchiveAsync(GachaArchive archive)
|
||||||
{
|
{
|
||||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
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())
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
{
|
{
|
||||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
appDbContext.GachaItems
|
appDbContext.GachaItems
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(i => i.ArchiveId == archiveId)
|
.Where(i => i.ArchiveId == archiveId && i.QueryType == queryType)
|
||||||
.Where(i => i.Id >= endId)
|
.Where(i => i.Id >= endId)
|
||||||
.ExecuteDelete();
|
.ExecuteDelete();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ internal struct GachaLogFetchContext
|
|||||||
// While no item is fetched, archive can be null.
|
// While no item is fetched, archive can be null.
|
||||||
if (TargetArchive is not 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);
|
saveContext.SaveItems(TargetArchive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ internal sealed partial class GachaLogService : IGachaLogService
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save items for each queryType
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
fetchContext.SaveItems();
|
fetchContext.SaveItems();
|
||||||
await Delay.Random(1000, 2000).ConfigureAwait(false);
|
await Delay.Random(1000, 2000).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ internal interface IGachaLogDbService
|
|||||||
|
|
||||||
ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId);
|
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);
|
ValueTask<GachaArchive?> GetGachaArchiveByUidAsync(string uid, CancellationToken token);
|
||||||
|
|
||||||
@@ -34,4 +34,6 @@ internal interface IGachaLogDbService
|
|||||||
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
|
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
|
||||||
|
|
||||||
long GetOldestGachaItemIdByArchiveId(Guid archiveId);
|
long GetOldestGachaItemIdByArchiveId(Guid archiveId);
|
||||||
|
|
||||||
|
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Core.ExceptionService;
|
|||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
using Snap.Hutao.Model.InterChange.GachaLog;
|
using Snap.Hutao.Model.InterChange.GachaLog;
|
||||||
using Snap.Hutao.Service.Metadata;
|
using Snap.Hutao.Service.Metadata;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.GachaLog;
|
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);
|
GachaArchiveOperation.GetOrAdd(gachaLogDbService, taskContext, uigf.Info.Uid, archives, out GachaArchive? archive);
|
||||||
Guid archiveId = archive.InnerId;
|
Guid archiveId = archive.InnerId;
|
||||||
|
|
||||||
long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveId(archiveId);
|
|
||||||
|
|
||||||
logger.LogInformation("Last Id to trim with: [{Id}]", trimId);
|
|
||||||
|
|
||||||
_ = uigf.IsCurrentVersionSupported(out UIGFVersion version);
|
_ = uigf.IsCurrentVersionSupported(out UIGFVersion version);
|
||||||
|
|
||||||
List<GachaItem> toAdd = version switch
|
foreach (GachaConfigType queryType in GachaLog.QueryTypes)
|
||||||
{
|
{
|
||||||
UIGFVersion.Major2Minor3OrHigher => uigf.List
|
long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveIdAndQueryType(archiveId, queryType);
|
||||||
.OrderByDescending(i => i.Id)
|
logger.LogInformation("Last Id to trim with: [{Id}]", trimId);
|
||||||
.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(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// 越早的记录手工导入的可能性越高
|
List<GachaItem> currentTypeToAdd = version switch
|
||||||
// 错误率相对来说会更高
|
{
|
||||||
// 因此从尾部开始查找
|
UIGFVersion.Major2Minor3OrHigher => uigf.List
|
||||||
if (toAdd.LastOrDefault(item => item.ItemId is 0U) is { } item)
|
.Where(i => i.UIGFGachaType == queryType && i.Id < trimId)
|
||||||
{
|
.OrderByDescending(i => i.Id)
|
||||||
ThrowHelper.InvalidOperation(SH.ServiceGachaLogUIGFImportItemInvalidFormat.Format(item.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;
|
return archive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
Text="{shcm:ResourceString Name=ViewPageGachaLogRefreshByManualInput}"/>
|
Text="{shcm:ResourceString Name=ViewPageGachaLogRefreshByManualInput}"/>
|
||||||
<ToggleMenuFlyoutItem
|
<ToggleMenuFlyoutItem
|
||||||
Icon="{shcm:FontIcon Glyph=}"
|
Icon="{shcm:FontIcon Glyph=}"
|
||||||
IsChecked="{Binding IsAggressiveRefresh}"
|
IsChecked="{Binding IsAggressiveRefresh, Mode=TwoWay}"
|
||||||
Text="{shcm:ResourceString Name=ViewPageGachaLogAggressiveRefresh}"/>
|
Text="{shcm:ResourceString Name=ViewPageGachaLogAggressiveRefresh}"/>
|
||||||
</MenuFlyout>
|
</MenuFlyout>
|
||||||
</AppBarButton.Flyout>
|
</AppBarButton.Flyout>
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ internal struct GachaLogQueryOptions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long EndId;
|
public long EndId;
|
||||||
|
|
||||||
|
public GachaConfigType Type;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Keys required:
|
/// Keys required:
|
||||||
/// authkey_ver
|
/// authkey_ver
|
||||||
@@ -49,18 +51,20 @@ internal struct GachaLogQueryOptions
|
|||||||
/// 构造一个新的祈愿记录请求配置
|
/// 构造一个新的祈愿记录请求配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">原始查询字符串</param>
|
/// <param name="query">原始查询字符串</param>
|
||||||
/// <param name="type">祈愿类型</param>
|
/// <param name="queryType">祈愿类型</param>
|
||||||
/// <param name="endId">终止Id</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;
|
IsOversea = query.IsOversea;
|
||||||
|
|
||||||
|
// 对于每个类型我们需要单独创建
|
||||||
|
// 对应类型的 GachaLogQueryOptions
|
||||||
|
Type = queryType;
|
||||||
innerQuery = QueryString.Parse(query.Query);
|
innerQuery = QueryString.Parse(query.Query);
|
||||||
|
|
||||||
// innerQuery.Set("lang", "zh-cn");
|
// innerQuery.Set("lang", "zh-cn");
|
||||||
innerQuery.Set("gacha_type", (int)type);
|
innerQuery.Set("gacha_type", (int)queryType);
|
||||||
innerQuery.Set("size", Size);
|
innerQuery.Set("size", Size);
|
||||||
|
|
||||||
EndId = endId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -82,10 +86,6 @@ internal struct GachaLogQueryOptions
|
|||||||
return queryString.ToString();
|
return queryString.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 转换到查询字符串
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>匹配的查询字符串</returns>
|
|
||||||
public readonly string ToQueryString()
|
public readonly string ToQueryString()
|
||||||
{
|
{
|
||||||
// Make the cached end id into query.
|
// Make the cached end id into query.
|
||||||
|
|||||||
Reference in New Issue
Block a user