mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fully support UIGF v2.3
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Test;
|
||||
|
||||
@@ -11,6 +12,12 @@ public class JsonSerializeTest
|
||||
}
|
||||
""";
|
||||
|
||||
private const string SmapleNumberObjectJson = """
|
||||
{
|
||||
"A" : ""
|
||||
}
|
||||
""";
|
||||
|
||||
[TestMethod]
|
||||
public void DelegatePropertyCanSerialize()
|
||||
{
|
||||
@@ -18,9 +25,23 @@ public class JsonSerializeTest
|
||||
Assert.AreEqual(sample.B, 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EmptyStringCanSerializeAsNumber()
|
||||
{
|
||||
// Throw
|
||||
StringNumberSample sample = JsonSerializer.Deserialize<StringNumberSample>(SmapleNumberObjectJson)!;
|
||||
Assert.AreEqual(sample.A, 0);
|
||||
}
|
||||
|
||||
private class Sample
|
||||
{
|
||||
public int A { get => B; set => B = value; }
|
||||
public int B { get; set; }
|
||||
}
|
||||
|
||||
private class StringNumberSample
|
||||
{
|
||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public int A { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ internal sealed class GachaItem
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <param name="itemId">物品Id</param>
|
||||
/// <returns>新的祈愿物品</returns>
|
||||
public static GachaItem Create(in Guid archiveId, UIGFItem item, int itemId)
|
||||
public static GachaItem CreateForMajor2Minor2OrLower(in Guid archiveId, UIGFItem item, int itemId)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@@ -116,6 +116,25 @@ internal sealed class GachaItem
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的数据库祈愿物品
|
||||
/// </summary>
|
||||
/// <param name="archiveId">存档Id</param>
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <returns>新的祈愿物品</returns>
|
||||
public static GachaItem CreateForMajor2Minor3OrHigher(in Guid archiveId, UIGFItem item)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
ArchiveId = archiveId,
|
||||
GachaType = item.GachaType,
|
||||
QueryType = item.UIGFGachaType,
|
||||
ItemId = int.Parse(item.ItemId),
|
||||
Time = item.Time,
|
||||
Id = item.Id,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的数据库祈愿物品
|
||||
/// </summary>
|
||||
@@ -159,6 +178,7 @@ internal sealed class GachaItem
|
||||
return new()
|
||||
{
|
||||
GachaType = GachaType,
|
||||
ItemId = $"{ItemId}",
|
||||
Count = 1,
|
||||
Time = Time,
|
||||
Name = nameQuality.Name,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.Model.InterChange.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
@@ -13,14 +11,9 @@ namespace Snap.Hutao.Model.InterChange.GachaLog;
|
||||
internal sealed class UIGF
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前发行的版本
|
||||
/// 当前版本
|
||||
/// </summary>
|
||||
public const string CurrentVersion = "v2.2";
|
||||
|
||||
private static readonly ImmutableList<string> SupportedVersion = new List<string>()
|
||||
{
|
||||
"v2.1", CurrentVersion,
|
||||
}.ToImmutableList();
|
||||
public const string CurrentVersion = "v2.3";
|
||||
|
||||
/// <summary>
|
||||
/// 信息
|
||||
@@ -37,17 +30,26 @@ internal sealed class UIGF
|
||||
/// <summary>
|
||||
/// 确认当前UIGF对象的版本是否受支持
|
||||
/// </summary>
|
||||
/// <param name="version">版本</param>
|
||||
/// <returns>当前UIAF对象是否受支持</returns>
|
||||
public bool IsCurrentVersionSupported()
|
||||
public bool IsCurrentVersionSupported(out UIGFVersion version)
|
||||
{
|
||||
return SupportedVersion.Contains(Info?.UIGFVersion ?? string.Empty);
|
||||
version = Info.UIGFVersion switch
|
||||
{
|
||||
"v2.1" => UIGFVersion.Major2Minor2OrLower,
|
||||
"v2.2" => UIGFVersion.Major2Minor2OrLower,
|
||||
"v2.3" => UIGFVersion.Major2Minor3OrHigher,
|
||||
_ => UIGFVersion.NotSupported,
|
||||
};
|
||||
|
||||
return version != UIGFVersion.NotSupported;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 列表物品是否正常
|
||||
/// </summary>
|
||||
/// <returns>是否正常</returns>
|
||||
public bool IsValidList()
|
||||
public bool IsMajor2Minor2OrLowerListValid()
|
||||
{
|
||||
foreach (UIGFItem item in List)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.InterChange.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
/// UIGF版本
|
||||
/// </summary>
|
||||
internal enum UIGFVersion
|
||||
{
|
||||
/// <summary>
|
||||
/// 不支持的版本
|
||||
/// </summary>
|
||||
NotSupported,
|
||||
|
||||
/// <summary>
|
||||
/// v2.2以及之前的版本
|
||||
/// </summary>
|
||||
Major2Minor2OrLower,
|
||||
|
||||
/// <summary>
|
||||
/// v2.3以及之后的版本
|
||||
/// </summary>
|
||||
Major2Minor3OrHigher,
|
||||
}
|
||||
@@ -28,8 +28,8 @@ internal sealed partial class GachaLogService : IGachaLogService
|
||||
private readonly ScopedDbCurrent<GachaArchive, Message.GachaArchiveChangedMessage> dbCurrent;
|
||||
private readonly IGachaStatisticsSlimFactory gachaStatisticsSlimFactory;
|
||||
private readonly IGachaStatisticsFactory gachaStatisticsFactory;
|
||||
private readonly IGachaLogExportService gachaLogExportService;
|
||||
private readonly IGachaLogImportService gachaLogImportService;
|
||||
private readonly IUIGFExportService gachaLogExportService;
|
||||
private readonly IUIGFImportService gachaLogImportService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly ILogger<GachaLogService> logger;
|
||||
@@ -134,13 +134,13 @@ internal sealed partial class GachaLogService : IGachaLogService
|
||||
/// <inheritdoc/>
|
||||
public Task<UIGF> ExportToUIGFAsync(GachaArchive archive)
|
||||
{
|
||||
return gachaLogExportService.ExportToUIGFAsync(context, archive);
|
||||
return gachaLogExportService.ExportAsync(context, archive);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task ImportFromUIGFAsync(List<UIGFItem> list, string uid)
|
||||
public async Task ImportFromUIGFAsync(UIGF uigf)
|
||||
{
|
||||
CurrentArchive = await gachaLogImportService.ImportFromUIGFAsync(context, list, uid).ConfigureAwait(false);
|
||||
CurrentArchive = await gachaLogImportService.ImportAsync(context, uigf).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -48,10 +48,9 @@ internal interface IGachaLogService
|
||||
/// <summary>
|
||||
/// 异步从UIGF导入数据
|
||||
/// </summary>
|
||||
/// <param name="list">列表</param>
|
||||
/// <param name="uid">Uid</param>
|
||||
/// <param name="uigf">信息</param>
|
||||
/// <returns>任务</returns>
|
||||
Task ImportFromUIGFAsync(List<UIGFItem> list, string uid);
|
||||
Task ImportFromUIGFAsync(UIGF uigf);
|
||||
|
||||
/// <summary>
|
||||
/// 异步初始化
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Service.GachaLog;
|
||||
/// <summary>
|
||||
/// 祈愿记录导出服务
|
||||
/// </summary>
|
||||
internal interface IGachaLogExportService
|
||||
internal interface IUIGFExportService
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步导出存档到 UIGF
|
||||
@@ -17,5 +17,5 @@ internal interface IGachaLogExportService
|
||||
/// <param name="context">元数据上下文</param>
|
||||
/// <param name="archive">存档</param>
|
||||
/// <returns>UIGF</returns>
|
||||
Task<UIGF> ExportToUIGFAsync(GachaLogServiceContext context, GachaArchive archive);
|
||||
Task<UIGF> ExportAsync(GachaLogServiceContext context, GachaArchive archive);
|
||||
}
|
||||
@@ -9,14 +9,13 @@ namespace Snap.Hutao.Service.GachaLog;
|
||||
/// <summary>
|
||||
/// 祈愿记录导入服务
|
||||
/// </summary>
|
||||
internal interface IGachaLogImportService
|
||||
internal interface IUIGFImportService
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步从 UIGF 导入
|
||||
/// </summary>
|
||||
/// <param name="context">祈愿记录服务上下文</param>
|
||||
/// <param name="list">列表</param>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <param name="uigf">数据</param>
|
||||
/// <returns>存档</returns>
|
||||
Task<GachaArchive> ImportFromUIGFAsync(GachaLogServiceContext context, List<UIGFItem> list, string uid);
|
||||
Task<GachaArchive> ImportAsync(GachaLogServiceContext context, UIGF uigf);
|
||||
}
|
||||
@@ -11,14 +11,14 @@ namespace Snap.Hutao.Service.GachaLog;
|
||||
/// 祈愿记录导出服务
|
||||
/// </summary>
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Scoped, typeof(IGachaLogExportService))]
|
||||
internal sealed partial class GachaLogExportService : IGachaLogExportService
|
||||
[Injection(InjectAs.Scoped, typeof(IUIGFExportService))]
|
||||
internal sealed partial class UIGFExportService : IUIGFExportService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<UIGF> ExportToUIGFAsync(GachaLogServiceContext context, GachaArchive archive)
|
||||
public async Task<UIGF> ExportAsync(GachaLogServiceContext context, GachaArchive archive)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
@@ -9,25 +9,25 @@ using Snap.Hutao.Model.InterChange.GachaLog;
|
||||
namespace Snap.Hutao.Service.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿记录导入服务
|
||||
/// v2.1 v2.2 祈愿记录导入服务
|
||||
/// </summary>
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Scoped, typeof(IGachaLogImportService))]
|
||||
internal sealed partial class GachaLogImportService : IGachaLogImportService
|
||||
[Injection(InjectAs.Scoped, typeof(IUIGFImportService))]
|
||||
internal sealed partial class UIGFImportService : IUIGFImportService
|
||||
{
|
||||
private readonly ILogger<GachaLogImportService> logger;
|
||||
private readonly ILogger<UIGFImportService> logger;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<GachaArchive> ImportFromUIGFAsync(GachaLogServiceContext context, List<UIGFItem> list, string uid)
|
||||
public async Task<GachaArchive> ImportAsync(GachaLogServiceContext context, UIGF uigf)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
GachaArchiveInitializationContext initContext = new(taskContext, uid, appDbContext.GachaArchives, context.ArchiveCollection);
|
||||
GachaArchiveInitializationContext initContext = new(taskContext, uigf.Info.Uid, appDbContext.GachaArchives, context.ArchiveCollection);
|
||||
GachaArchive.Init(initContext, out GachaArchive? archive);
|
||||
Guid archiveId = archive.InnerId;
|
||||
|
||||
@@ -38,10 +38,20 @@ internal sealed partial class GachaLogImportService : IGachaLogImportService
|
||||
|
||||
logger.LogInformation("Last Id to trim with: [{id}]", trimId);
|
||||
|
||||
IEnumerable<GachaItem> toAdd = list
|
||||
.OrderByDescending(i => i.Id)
|
||||
.Where(i => i.Id < trimId)
|
||||
.Select(i => GachaItem.Create(archiveId, i, context.GetItemId(i)));
|
||||
_ = uigf.IsCurrentVersionSupported(out UIGFVersion version);
|
||||
|
||||
IEnumerable<GachaItem> toAdd = version switch
|
||||
{
|
||||
UIGFVersion.Major2Minor3OrHigher => uigf.List
|
||||
.OrderByDescending(i => i.Id)
|
||||
.Where(i => i.Id < trimId)
|
||||
.Select(i => GachaItem.CreateForMajor2Minor3OrHigher(archiveId, i)),
|
||||
UIGFVersion.Major2Minor2OrLower => uigf.List
|
||||
.OrderByDescending(i => i.Id)
|
||||
.Where(i => i.Id < trimId)
|
||||
.Select(i => GachaItem.CreateForMajor2Minor2OrLower(archiveId, i, context.GetItemId(i))),
|
||||
_ => Enumerable.Empty<GachaItem>(),
|
||||
};
|
||||
|
||||
await appDbContext.GachaItems.AddRangeAndSaveAsync(toAdd).ConfigureAwait(false);
|
||||
return archive;
|
||||
@@ -100,7 +100,7 @@ internal static class ProcessInterop
|
||||
HINSTANCE hKernelDll = GetModuleHandle("kernel32.dll");
|
||||
Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
|
||||
|
||||
FARPROC pLoadLibraryA = GetProcAddress(hKernelDll, libraryPathu8);
|
||||
FARPROC pLoadLibraryA = GetProcAddress(hKernelDll, "LoadLibraryA"u8);
|
||||
Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
|
||||
|
||||
void* pNativeLibraryPath = default;
|
||||
|
||||
@@ -104,6 +104,20 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanImport(UIGFVersion version, UIGF uigf)
|
||||
{
|
||||
if (version == UIGFVersion.Major2Minor3OrHigher)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (version == UIGFVersion.Major2Minor2OrLower && uigf.IsMajor2Minor2OrLowerListValid())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Command("RefreshByWebCacheCommand")]
|
||||
private Task RefreshByWebCacheAsync()
|
||||
{
|
||||
@@ -331,19 +345,19 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
|
||||
|
||||
private async Task<bool> TryImportUIGFInternalAsync(UIGF uigf)
|
||||
{
|
||||
if (uigf.IsCurrentVersionSupported())
|
||||
if (uigf.IsCurrentVersionSupported(out UIGFVersion version))
|
||||
{
|
||||
// ContentDialog must be created by main thread.
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
GachaLogImportDialog importDialog = serviceProvider.CreateInstance<GachaLogImportDialog>(uigf);
|
||||
if (await importDialog.GetShouldImportAsync().ConfigureAwait(true))
|
||||
{
|
||||
if (uigf.IsValidList())
|
||||
if (CanImport(version, uigf))
|
||||
{
|
||||
ContentDialog dialog = await contentDialogFactory.CreateForIndeterminateProgressAsync(SH.ViewModelGachaLogImportProgress).ConfigureAwait(true);
|
||||
using (await dialog.BlockAsync(taskContext).ConfigureAwait(false))
|
||||
{
|
||||
await gachaLogService.ImportFromUIGFAsync(uigf.List, uigf.Info.Uid).ConfigureAwait(false);
|
||||
await gachaLogService.ImportFromUIGFAsync(uigf).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
infoBarService.Success(SH.ViewModelGachaLogImportComplete);
|
||||
|
||||
@@ -27,10 +27,10 @@ internal class GachaLogItem
|
||||
|
||||
/// <summary>
|
||||
/// 总为 <see cref="string.Empty"/>
|
||||
/// v2.3 使用了此值
|
||||
/// </summary>
|
||||
[Obsolete("API set this property empty")]
|
||||
[JsonPropertyName("item_id")]
|
||||
public string ItemId { get; set; } = string.Empty;
|
||||
public string ItemId { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 个数 一般为 1
|
||||
|
||||
Reference in New Issue
Block a user