From e98bee8a9b2f9345be28b68a7861ac38b5acea54 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sat, 13 Jul 2024 22:56:55 +0800 Subject: [PATCH] [skip ci] uigf 4 support part 3 --- .../Extension/CollectionExtension.cs | 2 +- .../Snap.Hutao/Extension/ListExtension.cs | 11 -- .../Abstraction/IDbMappingForeignKeyFrom.cs | 10 +- .../Snap.Hutao/Model/Entity/Achievement.cs | 22 +++- .../Snap.Hutao/Model/Entity/GachaItem.cs | 81 ++++---------- .../Achievement/AchievementDbService.cs | 10 ++ .../Achievement/IAchievementDbService.cs | 4 + .../Service/UIGF/IUIGFImportService.cs | 9 ++ .../Service/UIGF/UIGF40ExportService.cs | 17 --- .../Service/UIGF/UIGF40ImportService.cs | 105 ++++++++++++++++++ .../Service/UIGF/UIGFExportOptions.cs | 11 +- .../Service/UIGF/UIGFImportOptions.cs | 19 ++++ 12 files changed, 195 insertions(+), 106 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/UIGF/IUIGFImportService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ImportService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFImportOptions.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/CollectionExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/CollectionExtension.cs index 59d166ea..eaaac3e8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/CollectionExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/CollectionExtension.cs @@ -9,7 +9,7 @@ namespace Snap.Hutao.Extension; internal static class CollectionExtension { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNullOrEmpty([NotNullWhen(false)][MaybeNullWhen(true)] this Collection? source) + public static bool IsNullOrEmpty([NotNullWhen(false)][MaybeNullWhen(true)] this ICollection? source) { if (source is { Count: > 0 }) { diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/ListExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/ListExtension.cs index e6dd8f41..d4306bd5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/ListExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/ListExtension.cs @@ -67,17 +67,6 @@ internal static class ListExtension return list.GetRange(start, length); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNullOrEmpty([NotNullWhen(false)][MaybeNullWhen(true)] this List? source) - { - if (source is { Count: > 0 }) - { - return false; - } - - return true; - } - public static void RemoveLast(this IList collection) { collection.RemoveAt(collection.Count - 1); diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Abstraction/IDbMappingForeignKeyFrom.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Abstraction/IDbMappingForeignKeyFrom.cs index 68d89754..74c458ef 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Abstraction/IDbMappingForeignKeyFrom.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Abstraction/IDbMappingForeignKeyFrom.cs @@ -8,11 +8,17 @@ namespace Snap.Hutao.Model.Entity.Abstraction; internal interface IDbMappingForeignKeyFrom { [Pure] - static abstract TSource From(in Guid foreignKey, in TFrom from); + static abstract TSource From(Guid foreignKey, TFrom from); } internal interface IDbMappingForeignKeyFrom { [Pure] - static abstract TSource From(in Guid foreignKey, in T1 param1, in T2 param2); + static abstract TSource From(Guid foreignKey, T1 param1, T2 param2); +} + +internal interface IDbMappingForeignKeyFrom +{ + [Pure] + static abstract TSource From(Guid foreignKey, T1 param1, T2 param2, T3 param3); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Achievement.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Achievement.cs index ce387466..2543cad3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Achievement.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Achievement.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Entity.Abstraction; +using Snap.Hutao.Model.InterChange; using Snap.Hutao.Model.InterChange.Achievement; using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Primitive; @@ -14,7 +15,8 @@ namespace Snap.Hutao.Model.Entity; internal sealed class Achievement : IAppDbEntityHasArchive, IEquatable, IDbMappingForeignKeyFrom, - IDbMappingForeignKeyFrom + IDbMappingForeignKeyFrom, + IDbMappingForeignKeyFrom { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -33,7 +35,7 @@ internal sealed class Achievement : IAppDbEntityHasArchive, public AchievementStatus Status { get; set; } - public static Achievement From(in Guid archiveId, in AchievementId id) + public static Achievement From(Guid archiveId, AchievementId id) { return new() { @@ -44,11 +46,11 @@ internal sealed class Achievement : IAppDbEntityHasArchive, }; } - public static Achievement From(in Guid userId, in UIAFItem uiaf) + public static Achievement From(Guid archiveId, UIAFItem uiaf) { return new() { - ArchiveId = userId, + ArchiveId = archiveId, Id = uiaf.Id, Current = uiaf.Current, Status = uiaf.Status, @@ -56,6 +58,18 @@ internal sealed class Achievement : IAppDbEntityHasArchive, }; } + public static Achievement From(Guid archiveId, HutaoReservedAchievement achievement) + { + return new() + { + ArchiveId = archiveId, + Id = achievement.Id, + Current = achievement.Current, + Status = achievement.Status, + Time = achievement.Time, + }; + } + public bool Equals(Achievement? other) { if (other is null) diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaItem.cs index f34a7aa6..e4c26335 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaItem.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaItem.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Entity.Abstraction; +using Snap.Hutao.Model.InterChange; using Snap.Hutao.Model.InterChange.GachaLog; using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; using System.ComponentModel.DataAnnotations; @@ -10,70 +11,34 @@ using System.Globalization; namespace Snap.Hutao.Model.Entity; -/// -/// 抽卡记录物品 -/// -[HighQuality] [Table("gacha_items")] internal sealed partial class GachaItem : IDbMappingForeignKeyFrom, IDbMappingForeignKeyFrom, IDbMappingForeignKeyFrom, - IDbMappingForeignKeyFrom + IDbMappingForeignKeyFrom, + IDbMappingForeignKeyFrom { - /// - /// 内部Id - /// [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid InnerId { get; set; } - /// - /// 存档 - /// [ForeignKey(nameof(ArchiveId))] public GachaArchive Archive { get; set; } = default!; - /// - /// 存档Id - /// public Guid ArchiveId { get; set; } - /// - /// 祈愿记录分类 - /// public GachaType GachaType { get; set; } - /// - /// 祈愿记录查询分类 - /// 合并保底的卡池使用此属性 - /// 仅4种(不含400) - /// public GachaType QueryType { get; set; } - /// - /// 物品Id - /// public uint ItemId { get; set; } - /// - /// 获取时间 - /// public DateTimeOffset Time { get; set; } - /// - /// 物品 - /// public long Id { get; set; } - /// - /// 构造一个新的数据库祈愿物品 - /// - /// 存档Id - /// 祈愿物品 - /// 物品Id - /// 新的祈愿物品 - public static GachaItem From(in Guid archiveId, in GachaLogItem item, in uint itemId) + public static GachaItem From(Guid archiveId, GachaLogItem item, uint itemId) { return new() { @@ -86,14 +51,7 @@ internal sealed partial class GachaItem }; } - /// - /// 构造一个新的数据库祈愿物品 - /// - /// 存档Id - /// 祈愿物品 - /// 物品Id - /// 新的祈愿物品 - public static GachaItem From(in Guid archiveId, in LegacyUIGFItem item, in uint itemId) + public static GachaItem From(Guid archiveId, LegacyUIGFItem item, uint itemId) { return new() { @@ -106,13 +64,7 @@ internal sealed partial class GachaItem }; } - /// - /// 构造一个新的数据库祈愿物品 - /// - /// 存档Id - /// 祈愿物品 - /// 新的祈愿物品 - public static GachaItem From(in Guid archiveId, in LegacyUIGFItem item) + public static GachaItem From(Guid archiveId, LegacyUIGFItem item) { return new() { @@ -125,13 +77,7 @@ internal sealed partial class GachaItem }; } - /// - /// 构造一个新的数据库祈愿物品 - /// - /// 存档Id - /// 祈愿物品 - /// 新的祈愿物品 - public static GachaItem From(in Guid archiveId, in Web.Hutao.GachaLog.GachaItem item) + public static GachaItem From(Guid archiveId, Web.Hutao.GachaLog.GachaItem item) { return new() { @@ -143,4 +89,17 @@ internal sealed partial class GachaItem Id = item.Id, }; } + + public static GachaItem From(Guid archiveId, Hk4eItem item, int timezone) + { + return new() + { + ArchiveId = archiveId, + GachaType = item.GachaType, + QueryType = item.UIGFGachaType, + ItemId = item.ItemId, + Time = new(item.Time, new(timezone, 0, 0)), + Id = item.Id, + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs index c6450b16..91efbac5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs @@ -83,4 +83,14 @@ internal sealed partial class AchievementDbService : IAchievementDbService { return this.SingleOrDefault(a => a.InnerId == archiveId); } + + public AchievementArchive? GetAchievementArchiveByName(string name) + { + return this.SingleOrDefault(a => a.Name == name); + } + + public void AddAchievementArchive(AchievementArchive archive) + { + this.Add(archive); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementDbService.cs index cd0b57f0..90f130dc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementDbService.cs @@ -11,12 +11,16 @@ namespace Snap.Hutao.Service.Achievement; internal interface IAchievementDbService : IAppDbService, IAppDbService { + void AddAchievementArchive(EntityArchive archive); + ObservableCollection GetAchievementArchiveCollection(); List GetAchievementArchiveList(); EntityArchive? GetAchievementArchiveById(Guid archiveId); + EntityArchive? GetAchievementArchiveByName(string name); + void RemoveAchievementArchive(EntityArchive archive); List GetAchievementListByArchiveId(Guid archiveId); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/UIGF/IUIGFImportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/IUIGFImportService.cs new file mode 100644 index 00000000..8cf66656 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/IUIGFImportService.cs @@ -0,0 +1,9 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.UIGF; + +internal interface IUIGFImportService +{ + ValueTask ImportAsync(UIGFImportOptions importOptions, CancellationToken token); +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ExportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ExportService.cs index 32590c84..fbc31f23 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ExportService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ExportService.cs @@ -218,20 +218,3 @@ internal sealed partial class UIGF40ExportService : IUIGFExportService hutaoReserved.SpiralAbyss = results; } } - -[ConstructorGenerated] -[Injection(InjectAs.Transient, typeof(IUIGFImportService), Key = UIGFVersion.UIGF40)] -internal sealed partial class UIGF40ImportService : IUIGFImportService -{ - public async ValueTask ImportAsync(UIGFImportOptions importOptions, CancellationToken token) - { - await Task.CompletedTask; - - return true; - } -} - -internal interface IUIGFImportService -{ - -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ImportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ImportService.cs new file mode 100644 index 00000000..0e182d15 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ImportService.cs @@ -0,0 +1,105 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.InterChange; +using Snap.Hutao.Service.Achievement; +using Snap.Hutao.Service.GachaLog; +using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; + +namespace Snap.Hutao.Service.UIGF; + +[ConstructorGenerated] +[Injection(InjectAs.Transient, typeof(IUIGFImportService), Key = UIGFVersion.UIGF40)] +internal sealed partial class UIGF40ImportService : IUIGFImportService +{ + private readonly JsonSerializerOptions jsonOptions; + private readonly IServiceProvider serviceProvider; + private readonly RuntimeOptions runtimeOptions; + private readonly ITaskContext taskContext; + + public async ValueTask ImportAsync(UIGFImportOptions importOptions, CancellationToken token) + { + await taskContext.SwitchToBackgroundAsync(); + ImportGachaArchives(importOptions.UIGF.Hk4e, importOptions.GachaArchiveUids); + ImportAchievementArchives(importOptions.UIGF.HutaoReserved?.Achievement, importOptions.ReservedAchievementArchiveIdentities); + + return true; + } + + private void ImportGachaArchives(List>? entries, HashSet uids) + { + if (entries.IsNullOrEmpty() || uids.IsNullOrEmpty()) + { + return; + } + + IGachaLogDbService gachaLogDbService = serviceProvider.GetRequiredService(); + + foreach (UIGFEntry entry in entries) + { + if (!uids.Contains(entry.Uid)) + { + continue; + } + + GachaArchive? archive = gachaLogDbService.GetGachaArchiveByUid(entry.Uid); + + if (archive is null) + { + archive = GachaArchive.From(entry.Uid); + gachaLogDbService.AddGachaArchive(archive); + } + + Guid archiveId = archive.InnerId; + + List fullItems = []; + foreach (GachaType queryType in GachaLog.GachaLog.QueryTypes) + { + long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveIdAndQueryType(archiveId, queryType); + List currentTypedList = entry.List + .Where(item => item.UIGFGachaType == queryType && item.Id > trimId) + .OrderByDescending(item => item.Id) + .Select(item => GachaItem.From(archiveId, item, entry.TimeZone)) + .ToList(); + + fullItems.AddRange(currentTypedList); + } + + gachaLogDbService.AddGachaItemRange(fullItems); + } + } + + private void ImportAchievementArchives(List>? entries, HashSet identities) + { + if (entries.IsNullOrEmpty() || identities.IsNullOrEmpty()) + { + return; + } + + IAchievementDbService achievementDbService = serviceProvider.GetRequiredService(); + AchievementDbBulkOperation achievementDbBulkOperation = serviceProvider.GetRequiredService(); + + foreach (HutaoReservedEntry entry in entries) + { + if (!identities.Contains(entry.Identity)) + { + continue; + } + + AchievementArchive? archive = achievementDbService.GetAchievementArchiveByName(entry.Identity); + + if (archive is null) + { + archive = AchievementArchive.From(entry.Identity); + achievementDbService.AddAchievementArchive(archive); + } + + Guid archiveId = archive.InnerId; + + List achievements = entry.List.SelectList(item => Model.Entity.Achievement.From(archiveId, item)); + achievementDbBulkOperation.Overwrite(archiveId, achievements); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFExportOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFExportOptions.cs index bfa5c19b..352b5880 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFExportOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFExportOptions.cs @@ -9,20 +9,11 @@ internal sealed class UIGFExportOptions public required List GachaArchiveIds { get; set; } - public required List ReservedAchievementArchiveIds { get; set; } + public required List ReservedAchievementArchiveIds { get; set; } public required List ReservedAvatarInfoUids { get; set; } public required List ReservedCultivationProjectIds { get; set; } public required List ReservedSpiralAbyssUids { get; set; } -} - -internal sealed class UIGFImportOptions -{ - public Model.InterChange.UIGF UIGF { get; set; } = default!; - - public HashSet GachaArchiveUids { get; set; } = default!; - - public HashSet ReservedAchievementArchiveIdentities { get; set; } = default!; } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFImportOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFImportOptions.cs new file mode 100644 index 00000000..c5fc6677 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFImportOptions.cs @@ -0,0 +1,19 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.UIGF; + +internal sealed class UIGFImportOptions +{ + public required Model.InterChange.UIGF UIGF { get; set; } + + public required HashSet GachaArchiveUids { get; set; } + + public required HashSet ReservedAchievementArchiveIdentities { get; set; } + + public required HashSet ReservedAvatarInfoIdentities { get; set; } + + public required HashSet ReservedCultivationProjectIdentities { get; set; } + + public required HashSet ReservedSpiralAbyssIdentities { get; set; } +} \ No newline at end of file