diff --git a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs index 3efa4c37..76de5d85 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs @@ -5,7 +5,6 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Markup; using Microsoft.UI.Xaml.Navigation; -using Snap.Hutao.Core.Abstraction; using Snap.Hutao.Service.Navigation; using Snap.Hutao.View.Helper; using Snap.Hutao.ViewModel.Abstraction; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs index b0169e6c..ba9078e1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs @@ -3,7 +3,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using Snap.Hutao.Win32.Registry; -using System.Linq.Expressions; using System.Net; using System.Reflection; diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs index 14cab77b..ea0d0709 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs @@ -199,6 +199,13 @@ internal static partial class EnumerableExtension return list; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static List SortBy(this List list, Func keySelector, Comparison comparison) + { + list.Sort((left, right) => comparison(keySelector(left), keySelector(right))); + return list; + } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static List SortByDescending(this List list, Func keySelector) where TKey : IComparable @@ -213,4 +220,11 @@ internal static partial class EnumerableExtension list.Sort((left, right) => comparer.Compare(keySelector(right), keySelector(left))); return list; } + + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static List SortByDescending(this List list, Func keySelector, Comparison comparison) + { + list.Sort((left, right) => comparison(keySelector(right), keySelector(left))); + return list; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs index 4959215d..73136219 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs @@ -12,7 +12,8 @@ namespace Snap.Hutao.Model.Entity; /// [HighQuality] [Table("inventory_items")] -internal sealed class InventoryItem : IDbMappingForeignKeyFrom +internal sealed class InventoryItem : IDbMappingForeignKeyFrom, + IDbMappingForeignKeyFrom { /// /// 内部Id @@ -56,4 +57,21 @@ internal sealed class InventoryItem : IDbMappingForeignKeyFrom + /// 构造一个新的个数不为0的物品 + /// + /// 项目Id + /// 物品Id + /// 物品个数 + /// 新的个数不为0的物品 + public static InventoryItem From(in Guid projectId, in uint itemId, in uint count) + { + return new() + { + ProjectId = projectId, + ItemId = itemId, + Count = count, + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFItem.cs index a0b6fe2f..e6f51939 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFItem.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFItem.cs @@ -13,7 +13,7 @@ namespace Snap.Hutao.Model.InterChange.GachaLog; /// UIGF物品 /// [HighQuality] -internal sealed class UIGFItem : GachaLogItem, IMappingFrom +internal sealed class UIGFItem : GachaLogItem, IMappingFrom { /// /// 额外祈愿映射 @@ -22,7 +22,7 @@ internal sealed class UIGFItem : GachaLogItem, IMappingFrom CultivationItems { get; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemSource.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemConvertible.cs similarity index 82% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemSource.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemConvertible.cs index cca32549..535d7b7e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemConvertible.cs @@ -3,7 +3,7 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; -internal interface IItemSource +internal interface IItemConvertible { Model.Item ToItem(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQualityAccess.cs similarity index 91% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQualityAccess.cs index b29640d9..a6d83c8a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQualityAccess.cs @@ -9,7 +9,7 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; /// 物品与星级 /// [HighQuality] -internal interface INameQuality +internal interface INameQualityAccess { /// /// 名称 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemSource.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemConvertible.cs similarity index 90% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemSource.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemConvertible.cs index b1e71200..fb51bec9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemConvertible.cs @@ -9,7 +9,7 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; /// 指示该类为统计物品的源 /// [HighQuality] -internal interface IStatisticsItemSource +internal interface IStatisticsItemConvertible { /// /// 转换到统计物品 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemSource.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemConvertible.cs similarity index 53% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemSource.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemConvertible.cs index 2be603a4..97647310 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemConvertible.cs @@ -10,19 +10,9 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; /// 指示该类为简述统计物品的源 /// [HighQuality] -internal interface ISummaryItemSource +internal interface ISummaryItemConvertible { - /// - /// 星级 - /// QualityType Quality { get; } - /// - /// 转换到简述统计物品 - /// - /// 距上个五星 - /// 时间 - /// 是否为Up物品 - /// 简述统计物品 SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs deleted file mode 100644 index 4871692d..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Calculable; -using Snap.Hutao.Model.Metadata.Abstraction; -using Snap.Hutao.Model.Metadata.Converter; -using Snap.Hutao.Model.Metadata.Item; -using Snap.Hutao.ViewModel.Complex; -using Snap.Hutao.ViewModel.GachaLog; -using Snap.Hutao.ViewModel.Wiki; - -namespace Snap.Hutao.Model.Metadata.Avatar; - -/// -/// 角色的接口实现部分 -/// -internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource -{ - /// - /// [非元数据] 搭配数据 - /// TODO:Add View suffix. - /// - [JsonIgnore] - public AvatarCollocationView? Collocation { get; set; } - - /// - /// [非元数据] 烹饪奖励 - /// - [JsonIgnore] - public CookBonusView? CookBonusView { get; set; } - - /// - /// [非元数据] 养成物品视图 - /// - [JsonIgnore] - public List? CultivationItemsView { get; set; } - - /// - /// 最大等级 - /// - [SuppressMessage("", "CA1822")] - public uint MaxLevel { get => GetMaxLevel(); } - - public static uint GetMaxLevel() - { - return 90U; - } - - /// - public ICalculableAvatar ToCalculable() - { - return CalculableAvatar.From(this); - } - - /// - /// 转换为基础物品 - /// - /// 基础物品 - public Model.Item ToItem() - { - return new() - { - Name = Name, - Icon = AvatarIconConverter.IconNameToUri(Icon), - Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), - Quality = Quality, - }; - } - - /// - public StatisticsItem ToStatisticsItem(int count) - { - return new() - { - Name = Name, - Icon = AvatarIconConverter.IconNameToUri(Icon), - Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), - Quality = Quality, - - Count = count, - }; - } - - /// - public SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp) - { - return new() - { - Name = Name, - Icon = AvatarIconConverter.IconNameToUri(Icon), - Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), - Quality = Quality, - - Time = time, - LastPull = lastPull, - IsUp = isUp, - }; - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs index 79a81f4a..e88b0a80 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs @@ -1,99 +1,118 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Calculable; using Snap.Hutao.Model.Intrinsic; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Metadata.Converter; +using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Primitive; +using Snap.Hutao.ViewModel.Complex; +using Snap.Hutao.ViewModel.GachaLog; +using Snap.Hutao.ViewModel.Wiki; namespace Snap.Hutao.Model.Metadata.Avatar; -/// -/// 角色 -/// [HighQuality] -internal partial class Avatar +internal partial class Avatar : INameQualityAccess, + IStatisticsItemConvertible, + ISummaryItemConvertible, + IItemConvertible, + ICalculableSource, + ICultivationItemsAccess { - /// - /// Id - /// public AvatarId Id { get; set; } - /// - /// 突破提升 Id 外键 - /// public PromoteId PromoteId { get; set; } - /// - /// 排序号 - /// public uint Sort { get; set; } - /// - /// 体型 - /// public BodyType Body { get; set; } = default!; - /// - /// 正面图标 - /// public string Icon { get; set; } = default!; - /// - /// 侧面图标 - /// public string SideIcon { get; set; } = default!; - /// - /// 名称 - /// public string Name { get; set; } = default!; - /// - /// 描述 - /// public string Description { get; set; } = default!; - /// - /// 角色加入游戏时间 - /// public DateTimeOffset BeginTime { get; set; } - /// - /// 星级 - /// public QualityType Quality { get; set; } - /// - /// 武器类型 - /// public WeaponType Weapon { get; set; } - /// - /// 基础数值 - /// public AvatarBaseValue BaseValue { get; set; } = default!; - /// - /// 生长曲线 - /// public List> GrowCurves { get; set; } = default!; - /// - /// 技能 - /// public SkillDepot SkillDepot { get; set; } = default!; - /// - /// 好感信息/基本信息 - /// public FetterInfo FetterInfo { get; set; } = default!; - /// - /// 皮肤 - /// public List Costumes { get; set; } = default!; - /// - /// 养成物品 - /// public List CultivationItems { get; set; } = default!; + + [JsonIgnore] + public AvatarCollocationView? CollocationView { get; set; } + + [JsonIgnore] + public CookBonusView? CookBonusView { get; set; } + + [JsonIgnore] + public List? CultivationItemsView { get; set; } + + [SuppressMessage("", "CA1822")] + public uint MaxLevel { get => GetMaxLevel(); } + + public static uint GetMaxLevel() + { + return 90U; + } + + public ICalculableAvatar ToCalculable() + { + return CalculableAvatar.From(this); + } + + public Model.Item ToItem() + { + return new() + { + Name = Name, + Icon = AvatarIconConverter.IconNameToUri(Icon), + Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), + Quality = Quality, + }; + } + + public StatisticsItem ToStatisticsItem(int count) + { + return new() + { + Name = Name, + Icon = AvatarIconConverter.IconNameToUri(Icon), + Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), + Quality = Quality, + + Count = count, + }; + } + + public SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp) + { + return new() + { + Name = Name, + Icon = AvatarIconConverter.IconNameToUri(Icon), + Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), + Quality = Quality, + + Time = time, + LastPull = lastPull, + IsUp = isUp, + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs deleted file mode 100644 index 86a4465c..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Calculable; -using Snap.Hutao.Model.Intrinsic; -using Snap.Hutao.Model.Metadata.Abstraction; -using Snap.Hutao.Model.Metadata.Converter; -using Snap.Hutao.Model.Metadata.Item; -using Snap.Hutao.ViewModel.Complex; -using Snap.Hutao.ViewModel.GachaLog; - -namespace Snap.Hutao.Model.Metadata.Weapon; - -/// -/// 武器的接口实现 -/// -internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource -{ - /// - /// [非元数据] 搭配数据 - /// TODO:Add View suffix. - /// - [JsonIgnore] - public WeaponCollocationView? Collocation { get; set; } - - /// - /// [非元数据] 养成物品视图 - /// - [JsonIgnore] - public List? CultivationItemsView { get; set; } - - /// - [JsonIgnore] - public QualityType Quality - { - get => RankLevel; - } - - /// - /// 最大等级 - /// - internal uint MaxLevel { get => GetMaxLevelByQuality(Quality); } - - public static uint GetMaxLevelByQuality(QualityType quality) - { - return quality >= QualityType.QUALITY_BLUE ? 90U : 70U; - } - - /// - public ICalculableWeapon ToCalculable() - { - return CalculableWeapon.From(this); - } - - /// - /// 转换为基础物品 - /// - /// 基础物品 - public Model.Item ToItem() - { - return new() - { - Name = Name, - Icon = EquipIconConverter.IconNameToUri(Icon), - Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), - Quality = RankLevel, - }; - } - - /// - /// 转换到统计物品 - /// - /// 个数 - /// 统计物品 - public StatisticsItem ToStatisticsItem(int count) - { - return new() - { - Name = Name, - Icon = EquipIconConverter.IconNameToUri(Icon), - Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), - Quality = RankLevel, - Count = count, - }; - } - - /// - /// 转换到简述统计物品 - /// - /// 距上个五星 - /// 时间 - /// 是否为Up物品 - /// 简述统计物品 - public SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp) - { - return new() - { - Name = Name, - Icon = EquipIconConverter.IconNameToUri(Icon), - Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), - Time = time, - Quality = RankLevel, - LastPull = lastPull, - IsUp = isUp, - }; - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs index 19136d47..46233381 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs @@ -1,69 +1,105 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Calculable; using Snap.Hutao.Model.Intrinsic; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Metadata.Converter; +using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Primitive; +using Snap.Hutao.ViewModel.Complex; +using Snap.Hutao.ViewModel.GachaLog; namespace Snap.Hutao.Model.Metadata.Weapon; -/// -/// 武器 -/// [HighQuality] -internal sealed partial class Weapon +internal sealed partial class Weapon : INameQualityAccess, + IStatisticsItemConvertible, + ISummaryItemConvertible, + IItemConvertible, + ICalculableSource, + ICultivationItemsAccess { - /// - /// Id - /// public WeaponId Id { get; set; } - /// - /// 突破 Id - /// public PromoteId PromoteId { get; set; } - /// - /// 武器类型 - /// public WeaponType WeaponType { get; set; } - /// - /// 等级 - /// public QualityType RankLevel { get; set; } - /// - /// 名称 - /// public string Name { get; set; } = default!; - /// - /// 描述 - /// public string Description { get; set; } = default!; - /// - /// 图标 - /// public string Icon { get; set; } = default!; - /// - /// 觉醒图标 - /// public string AwakenIcon { get; set; } = default!; - /// - /// 生长曲线 - /// public List GrowCurves { get; set; } = default!; - /// - /// 被动信息, 无被动的武器为 - /// public NameDescriptions? Affix { get; set; } = default!; - /// - /// 养成物品 - /// public List CultivationItems { get; set; } = default!; + + [JsonIgnore] + public WeaponCollocationView? CollocationView { get; set; } + + [JsonIgnore] + public List? CultivationItemsView { get; set; } + + [JsonIgnore] + public QualityType Quality + { + get => RankLevel; + } + + internal uint MaxLevel { get => GetMaxLevelByQuality(Quality); } + + public static uint GetMaxLevelByQuality(QualityType quality) + { + return quality >= QualityType.QUALITY_BLUE ? 90U : 70U; + } + + public ICalculableWeapon ToCalculable() + { + return CalculableWeapon.From(this); + } + + public Model.Item ToItem() + { + return new() + { + Name = Name, + Icon = EquipIconConverter.IconNameToUri(Icon), + Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), + Quality = RankLevel, + }; + } + + public StatisticsItem ToStatisticsItem(int count) + { + return new() + { + Name = Name, + Icon = EquipIconConverter.IconNameToUri(Icon), + Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), + Quality = RankLevel, + Count = count, + }; + } + + public SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp) + { + return new() + { + Name = Name, + Icon = EquipIconConverter.IconNameToUri(Icon), + Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), + Time = time, + Quality = RankLevel, + LastPull = lastPull, + IsUp = isUp, + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 0b2806e1..6da301bb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -1568,6 +1568,9 @@ 不能添加名称无效的计划 + + 正在同步背包物品 + 此操作不可逆,此计划的养成物品与背包材料将会丢失 @@ -1925,6 +1928,9 @@ 前往 + + 同步背包物品 + 删除清单 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs index eae01ea0..13dfb710 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs @@ -20,7 +20,7 @@ namespace Snap.Hutao.Service; [Injection(InjectAs.Scoped, typeof(IAnnouncementService))] internal sealed partial class AnnouncementService : IAnnouncementService { - private static readonly string CacheKey = $"{nameof(AnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}"; + private const string CacheKey = $"{nameof(AnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}"; private readonly IServiceScopeFactory serviceScopeFactory; private readonly ITaskContext taskContext; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationDbService.cs index c7d5934e..34eca1e2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationDbService.cs @@ -16,16 +16,6 @@ internal sealed partial class CultivationDbService : ICultivationDbService public IServiceProvider ServiceProvider { get => serviceProvider; } - public List GetInventoryItemListByProjectId(Guid projectId) - { - return this.List(i => i.ProjectId == projectId); - } - - public ValueTask> GetInventoryItemListByProjectIdAsync(Guid projectId, CancellationToken token = default) - { - return this.ListAsync(i => i.ProjectId == projectId, token); - } - public ValueTask> GetCultivateEntryListByProjectIdAsync(Guid projectId, CancellationToken token = default) { return this.ListAsync(e => e.ProjectId == projectId, token); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index d1fddb5e..9f476741 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -2,15 +2,14 @@ // Licensed under the MIT license. using Snap.Hutao.Core.Database; -using Snap.Hutao.Model; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Primitive; -using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Service.Inventory; using Snap.Hutao.Service.Metadata.ContextAbstraction; using Snap.Hutao.ViewModel.Cultivation; using System.Collections.ObjectModel; using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item; +using ModelItem = Snap.Hutao.Model.Item; namespace Snap.Hutao.Service.Cultivation; @@ -51,22 +50,6 @@ internal sealed partial class CultivationService : ICultivationService } } - /// - public List GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand) - { - Guid projectId = cultivateProject.InnerId; - List entities = cultivationDbService.GetInventoryItemListByProjectId(projectId); - - List results = []; - foreach (Material meta in context.EnumerateInventoryMaterial()) - { - InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id); - results.Add(new(entity, meta, saveCommand)); - } - - return results; - } - /// public async ValueTask> GetCultivateEntriesAsync(CultivateProject cultivateProject, ICultivationMetadataContext context) { @@ -86,7 +69,7 @@ internal sealed partial class CultivationService : ICultivationService entryItems.Add(new(cultivateItem, context.GetMaterial(cultivateItem.ItemId))); } - Item item = entry.Type switch + ModelItem item = entry.Type switch { CultivateType.AvatarAndSkill => context.GetAvatar(entry.Id).ToItem(), CultivateType.Weapon => context.GetWeapon(entry.Id).ToItem(), @@ -130,7 +113,7 @@ internal sealed partial class CultivationService : ICultivationService } } - foreach (InventoryItem inventoryItem in await cultivationDbService.GetInventoryItemListByProjectIdAsync(projectId, token).ConfigureAwait(false)) + foreach (InventoryItem inventoryItem in await inventoryDbService.GetInventoryItemListByProjectIdAsync(projectId, token).ConfigureAwait(false)) { if (resultItems.SingleOrDefault(i => i.Inner.Id == inventoryItem.ItemId) is { } existedItem) { @@ -147,12 +130,6 @@ internal sealed partial class CultivationService : ICultivationService await cultivationDbService.RemoveCultivateEntryByIdAsync(entryId).ConfigureAwait(false); } - /// - public void SaveInventoryItem(InventoryItemView item) - { - inventoryDbService.UpdateInventoryItem(item.Entity); - } - /// public void SaveCultivateItem(CultivateItemView item) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationDbService.cs index c8173b50..d54dabec 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationDbService.cs @@ -7,8 +7,7 @@ using System.Collections.ObjectModel; namespace Snap.Hutao.Service.Cultivation; -internal interface ICultivationDbService : IAppDbService, - IAppDbService, +internal interface ICultivationDbService : IAppDbService, IAppDbService, IAppDbService, IAppDbService @@ -29,10 +28,6 @@ internal interface ICultivationDbService : IAppDbService, ObservableCollection GetCultivateProjectCollection(); - List GetInventoryItemListByProjectId(Guid projectId); - - ValueTask> GetInventoryItemListByProjectIdAsync(Guid projectId, CancellationToken token = default); - ValueTask AddCultivateEntryAsync(CultivateEntry entry, CancellationToken token = default); ValueTask AddCultivateItemRangeAsync(IEnumerable toAdd, CancellationToken token = default); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs index 0a83a3e0..42cb5211 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs @@ -27,8 +27,6 @@ internal interface ICultivationService ValueTask> GetCultivateEntriesAsync(CultivateProject cultivateProject, ICultivationMetadataContext context); - List GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand); - ValueTask> GetStatisticsCultivateItemCollectionAsync( CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token); @@ -54,12 +52,6 @@ internal interface ICultivationService /// 养成物品 void SaveCultivateItem(CultivateItemView item); - /// - /// 保存单个物品 - /// - /// 物品 - void SaveInventoryItem(InventoryItemView item); - /// /// 异步尝试添加新的项目 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsExtension.cs index 420fa93e..497ad2dd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsExtension.cs @@ -49,7 +49,7 @@ internal static class GachaStatisticsExtension /// 计数器 /// 统计物品列表 public static List ToStatisticsList(this Dictionary dict) - where TItem : IStatisticsItemSource + where TItem : IStatisticsItemConvertible { IOrderedEnumerable result = dict .Select(kvp => kvp.Key.ToStatisticsItem(kvp.Value)) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs index 22b02de4..1ef41991 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs @@ -27,7 +27,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF return CreateCore(context, items, uid); } - private static void Track(INameQuality nameQuality, ref int orangeTracker, ref int purpleTracker) + private static void Track(INameQualityAccess nameQuality, ref int orangeTracker, ref int purpleTracker) { switch (nameQuality.Quality) { @@ -69,7 +69,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF // O(n) operation foreach (ref readonly GachaItem item in CollectionsMarshal.AsSpan(items)) { - INameQuality nameQuality = context.GetNameQualityByItemId(item.ItemId); + INameQualityAccess nameQuality = context.GetNameQualityByItemId(item.ItemId); switch (item.QueryType) { case GachaType.Standard: diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs index 34ce5071..2541b719 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs @@ -16,11 +16,11 @@ internal sealed class HistoryWishBuilder { private readonly GachaEvent gachaEvent; - private readonly Dictionary orangeUpCounter = []; - private readonly Dictionary purpleUpCounter = []; - private readonly Dictionary orangeCounter = []; - private readonly Dictionary purpleCounter = []; - private readonly Dictionary blueCounter = []; + private readonly Dictionary orangeUpCounter = []; + private readonly Dictionary purpleUpCounter = []; + private readonly Dictionary orangeCounter = []; + private readonly Dictionary purpleCounter = []; + private readonly Dictionary blueCounter = []; private int totalCountTracker; @@ -37,18 +37,18 @@ internal sealed class HistoryWishBuilder switch (ConfigType) { case GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar: - orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0); - purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0); + orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemConvertible)a, a => 0); + purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemConvertible)a, a => 0); break; case GachaType.ActivityWeapon: - orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0); - purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0); + orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemConvertible)w, w => 0); + purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemConvertible)w, w => 0); break; case GachaType.ActivityCity: // Avatars are less than weapons, so we try to get the value from avatar map first - orangeUpCounter = gachaEvent.UpOrangeList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0); - purpleUpCounter = gachaEvent.UpPurpleList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0); + orangeUpCounter = gachaEvent.UpOrangeList.Select(id => (IStatisticsItemConvertible?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0); + purpleUpCounter = gachaEvent.UpPurpleList.Select(id => (IStatisticsItemConvertible?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0); break; } } @@ -74,7 +74,7 @@ internal sealed class HistoryWishBuilder /// /// 物品 /// 是否为Up物品 - public bool IncreaseOrange(IStatisticsItemSource item) + public bool IncreaseOrange(IStatisticsItemConvertible item) { orangeCounter.IncreaseOne(item); ++totalCountTracker; @@ -86,7 +86,7 @@ internal sealed class HistoryWishBuilder /// 计数四星物品 /// /// 物品 - public void IncreasePurple(IStatisticsItemSource item) + public void IncreasePurple(IStatisticsItemConvertible item) { purpleUpCounter.TryIncreaseOne(item); purpleCounter.IncreaseOne(item); @@ -97,7 +97,7 @@ internal sealed class HistoryWishBuilder /// 计数三星武器 /// /// 武器 - public void IncreaseBlue(IStatisticsItemSource item) + public void IncreaseBlue(IStatisticsItemConvertible item) { blueCounter.IncreaseOne(item); ++totalCountTracker; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HutaoStatisticsFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HutaoStatisticsFactory.cs index e98ed1d1..39387960 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HutaoStatisticsFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HutaoStatisticsFactory.cs @@ -55,7 +55,7 @@ internal sealed class HutaoStatisticsFactory foreach (ref readonly ItemCount item in CollectionsMarshal.AsSpan(items)) { - IStatisticsItemSource source = item.Item.StringLength() switch + IStatisticsItemConvertible source = item.Item.StringLength() switch { 8U => context.GetAvatar(item.Item), 5U => context.GetWeapon(item.Item), diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs index 067c739a..1bed7e2e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs @@ -44,7 +44,7 @@ internal sealed class TypedWishSummaryBuilder /// 祈愿物品 /// 对应武器 /// 是否为Up物品 - public void Track(GachaItem item, ISummaryItemSource source, bool isUp) + public void Track(GachaItem item, ISummaryItemConvertible source, bool isUp) { if (!context.TypeEvaluator(item.GachaType)) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs index e7dd7207..08c6bd73 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs @@ -59,7 +59,7 @@ internal sealed class GachaLogServiceMetadataContext : IMetadataContext, return result; } - public INameQuality GetNameQualityByItemId(uint id) + public INameQualityAccess GetNameQualityByItemId(uint id) { uint place = id.StringLength(); return place switch diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs index a5f3c1e8..c35cebf9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs @@ -3,7 +3,6 @@ using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Win32.Foundation; -using Snap.Hutao.Win32.Memory; using System.Diagnostics; using static Snap.Hutao.Win32.Kernel32; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs index 14430ec9..0098c263 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs @@ -2,16 +2,21 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Entity; +using Snap.Hutao.Service.Abstraction; namespace Snap.Hutao.Service.Inventory; -internal interface IInventoryDbService +internal interface IInventoryDbService : IAppDbService { - ValueTask AddInventoryItemRangeByProjectId(List items); + ValueTask AddInventoryItemRangeByProjectIdAsync(List items, CancellationToken token = default); - ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId); + ValueTask RemoveInventoryItemRangeByProjectIdAsync(Guid projectId, CancellationToken token = default); void UpdateInventoryItem(InventoryItem item); - ValueTask UpdateInventoryItemAsync(InventoryItem item); + ValueTask UpdateInventoryItemAsync(InventoryItem item, CancellationToken token = default); + + List GetInventoryItemListByProjectId(Guid projectId); + + ValueTask> GetInventoryItemListByProjectIdAsync(Guid projectId, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryService.cs index 4eb6ef0d..4278bfe7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryService.cs @@ -1,8 +1,17 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Service.Cultivation; +using Snap.Hutao.ViewModel.Cultivation; + namespace Snap.Hutao.Service.Inventory; internal interface IInventoryService { + List GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand); + + void SaveInventoryItem(InventoryItemView item); + + ValueTask RefreshInventoryAsync(CultivateProject project); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs index bf34de1b..236a4544 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs @@ -1,10 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.EntityFrameworkCore; -using Snap.Hutao.Core.Database; using Snap.Hutao.Model.Entity; -using Snap.Hutao.Model.Entity.Database; +using Snap.Hutao.Service.Abstraction; namespace Snap.Hutao.Service.Inventory; @@ -14,43 +12,35 @@ internal sealed partial class InventoryDbService : IInventoryDbService { private readonly IServiceProvider serviceProvider; - public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId) + public IServiceProvider ServiceProvider { get => serviceProvider; } + + public async ValueTask RemoveInventoryItemRangeByProjectIdAsync(Guid projectId, CancellationToken token = default) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.InventoryItems - .AsNoTracking() - .Where(a => a.ProjectId == projectId && a.ItemId != 202U) // 摩拉 - .ExecuteDeleteAsync() - .ConfigureAwait(false); - } + await this.DeleteAsync(i => i.ProjectId == projectId, token).ConfigureAwait(false); } - public async ValueTask AddInventoryItemRangeByProjectId(List items) + public async ValueTask AddInventoryItemRangeByProjectIdAsync(List items, CancellationToken token = default) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.InventoryItems.AddRangeAndSaveAsync(items).ConfigureAwait(false); - } + await this.AddRangeAsync(items, token).ConfigureAwait(false); } public void UpdateInventoryItem(InventoryItem item) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - appDbContext.InventoryItems.UpdateAndSave(item); - } + this.Update(item); } - public async ValueTask UpdateInventoryItemAsync(InventoryItem item) + public async ValueTask UpdateInventoryItemAsync(InventoryItem item, CancellationToken token = default) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.InventoryItems.UpdateAndSaveAsync(item).ConfigureAwait(false); - } + await this.UpdateAsync(item, token).ConfigureAwait(false); + } + + public List GetInventoryItemListByProjectId(Guid projectId) + { + return this.List(i => i.ProjectId == projectId); + } + + public ValueTask> GetInventoryItemListByProjectIdAsync(Guid projectId, CancellationToken token = default) + { + return this.ListAsync(i => i.ProjectId == projectId, token); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs index 88928948..9dea0796 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs @@ -1,9 +1,83 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Metadata.Item; +using Snap.Hutao.Service.Cultivation; +using Snap.Hutao.Service.Metadata.ContextAbstraction; +using Snap.Hutao.Service.Notification; +using Snap.Hutao.Service.User; +using Snap.Hutao.ViewModel.Cultivation; +using Snap.Hutao.ViewModel.User; +using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; +using Snap.Hutao.Web.Response; + namespace Snap.Hutao.Service.Inventory; -[Injection(InjectAs.Transient)] -internal sealed class InventoryService : IInventoryService +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IInventoryService))] +internal sealed partial class InventoryService : IInventoryService { + private readonly MinimalPromotionDelta minimalPromotionDelta; + private readonly IServiceScopeFactory serviceScopeFactory; + private readonly IInventoryDbService inventoryDbService; + private readonly IInfoBarService infoBarService; + private readonly IUserService userService; + + /// + public List GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand) + { + Guid projectId = cultivateProject.InnerId; + List entities = inventoryDbService.GetInventoryItemListByProjectId(projectId); + + List results = []; + foreach (Material meta in context.EnumerateInventoryMaterial()) + { + InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id); + results.Add(new(entity, meta, saveCommand)); + } + + return results; + } + + /// + public void SaveInventoryItem(InventoryItemView item) + { + inventoryDbService.UpdateInventoryItem(item.Entity); + } + + /// + public async ValueTask RefreshInventoryAsync(CultivateProject project) + { + List deltas = await minimalPromotionDelta.GetAsync().ConfigureAwait(false); + + BatchConsumption? batchConsumption = default; + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) + { + infoBarService.Warning(SH.MustSelectUserAndUid); + return; + } + + CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService(); + + Response? resp = await calculateClient + .BatchComputeAsync(userAndUid, deltas, true) + .ConfigureAwait(false); + + if (!resp.IsOk()) + { + return; + } + + batchConsumption = resp.Data; + } + + if (batchConsumption is { OverallConsume: { } items }) + { + await inventoryDbService.RemoveInventoryItemRangeByProjectIdAsync(project.InnerId).ConfigureAwait(false); + await inventoryDbService.AddInventoryItemRangeByProjectIdAsync(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false); + } + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs new file mode 100644 index 00000000..1b5066ac --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs @@ -0,0 +1,159 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Google.OrTools.LinearSolver; +using Microsoft.Extensions.Caching.Memory; +using Snap.Hutao.Core; +using Snap.Hutao.Core.ExceptionService; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Primitive; +using Snap.Hutao.Service.Metadata; +using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; +using System.Runtime.InteropServices; +using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar; +using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon; + +namespace Snap.Hutao.Service.Inventory; + +[ConstructorGenerated] +[Injection(InjectAs.Singleton)] +internal sealed partial class MinimalPromotionDelta +{ + private const string CacheKey = $"{nameof(MinimalPromotionDelta)}.Cache"; + + private readonly IMetadataService metadataService; + private readonly IMemoryCache memoryCache; + + public async ValueTask> GetAsync() + { + if (memoryCache.TryGetRequiredValue(CacheKey, out List? cache)) + { + return cache; + } + + List cultivationItemsEntryList = + [ + .. (await metadataService.GetAvatarListAsync().ConfigureAwait(false)).Where(a => a.BeginTime <= DateTimeOffset.Now), + .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(w => w.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), + ]; + + List minimal = Minimize(cultivationItemsEntryList); + + // Gurantee the order of avatar and weapon + // Make sure weapons can have avatar to attach + minimal.Sort(CultivationItemsAccessComparer.Shared); + return memoryCache.Set(CacheKey, ToPromotionDeltaList(minimal)); + } + + private static List Minimize(List cultivationItems) + { + using (Solver? solver = Solver.CreateSolver("SCIP")) + { + ArgumentNullException.ThrowIfNull(solver); + + Objective objective = solver.Objective(); + objective.SetMinimization(); + + Dictionary itemVariableMap = []; + foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) + { + Variable variable = solver.MakeBoolVar(item.Name); + itemVariableMap[item] = variable; + objective.SetCoefficient(variable, 1); + } + + Dictionary materialConstraintMap = []; + foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) + { + foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(item.CultivationItems)) + { + ref Constraint? constraint = ref CollectionsMarshal.GetValueRefOrAddDefault(materialConstraintMap, materialId, out _); + constraint ??= solver.MakeConstraint(1, double.PositiveInfinity, $"{materialId}"); + constraint.SetCoefficient(itemVariableMap[item], 1); + } + } + + Solver.ResultStatus status = solver.Solve(); + HutaoException.ThrowIf(status != Solver.ResultStatus.OPTIMAL, "Unable to solve minimal item set"); + + List results = []; + foreach ((ICultivationItemsAccess item, Variable variable) in itemVariableMap) + { + if (variable.SolutionValue() > 0.5) + { + results.Add(item); + } + } + + return results; + } + } + + private static List ToPromotionDeltaList(List cultivationItems) + { + List deltas = []; + int currentWeaponEmptyAvatarIndex = 0; + + foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) + { + switch (item) + { + case MetadataAvatar avatar: + deltas.Add(new() + { + AvatarId = avatar.Id, + AvatarLevelCurrent = 1, + AvatarLevelTarget = 90, + SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta() + { + Id = skill.GroupId, + LevelCurrent = 1, + LevelTarget = 10, + }), + }); + + break; + + case MetadataWeapon weapon: + AvatarPromotionDelta delta; + if (currentWeaponEmptyAvatarIndex < deltas.Count) + { + delta = deltas[currentWeaponEmptyAvatarIndex++]; + } + else + { + delta = new(); + deltas.Add(delta); + } + + delta.Weapon = new() + { + Id = weapon.Id, + LevelCurrent = 1, + LevelTarget = 90, + }; + + break; + } + } + + return deltas; + } + + private sealed class CultivationItemsAccessComparer : IComparer + { + private static readonly LazySlim LazyShared = new(() => new()); + + public static CultivationItemsAccessComparer Shared { get => LazyShared.Value; } + + public int Compare(ICultivationItemsAccess? x, ICultivationItemsAccess? y) + { + return (x, y) switch + { + (MetadataAvatar, MetadataWeapon) => -1, + (MetadataWeapon, MetadataAvatar) => 1, + _ => 0, + }; + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 40283196..8c589fbd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -313,6 +313,7 @@ + all diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml index df1c44af..f9e05085 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml @@ -11,7 +11,6 @@ xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:shc="using:Snap.Hutao.Control" xmlns:shcb="using:Snap.Hutao.Control.Behavior" - xmlns:shcca="using:Snap.Hutao.Control.Collection.Alternating" xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shcp="using:Snap.Hutao.Control.Panel" diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml index 9eca6358..e1883a5c 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml @@ -269,6 +269,10 @@ Style="{ThemeResource CommandBarComboBoxStyle}"/> + + ItemsSource="{Binding Selected.CollocationView.Avatars}"/> + ItemsSource="{Binding Selected.CollocationView.Weapons}"/> + ItemsSource="{Binding Selected.CollocationView.ReliquarySets}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml index 0e9d5d43..51d7054c 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml @@ -336,7 +336,7 @@ - + response = await calculatorClient.BatchComputeAsync(userAndUid, delta).ConfigureAwait(false); - switch (result) + if (!response.IsOk()) { - case CultivateCoreResult.Ok: - infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess); - break; - case CultivateCoreResult.SaveConsumptionFailed: - infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning); - break; + return; } + + if (!await SaveCultivationAsync(response.Data.Items.Single(), delta).ConfigureAwait(false)) + { + infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning); + return; + } + + infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess); } [Command("BatchCultivateCommand")] @@ -217,7 +221,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I return; } - if (userService.Current is null) + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { infoBarService.Warning(SH.MustSelectUserAndUid); return; @@ -237,9 +241,11 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I ContentDialog progressDialog = await contentDialogFactory .CreateForIndeterminateProgressAsync(SH.ViewModelAvatarPropertyBatchCultivateProgressTitle) .ConfigureAwait(false); + + BatchCultivateResult result = default; using (await progressDialog.BlockAsync(taskContext).ConfigureAwait(false)) { - BatchCultivateResult result = default; + List deltas = []; foreach (AvatarView avatar in avatars) { if (!baseline.TryGetNonErrorCopy(avatar, out CalculatorAvatarPromotionDelta? copy)) @@ -248,75 +254,64 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I continue; } - CultivateCoreResult coreResult = await CultivateCoreAsync(userService.Current.Entity, copy, avatar).ConfigureAwait(false); + deltas.Add(copy); + } - switch (coreResult) - { - case CultivateCoreResult.Ok: - ++result.SucceedCount; - break; - case CultivateCoreResult.ComputeConsumptionFailed: - result.Interrupted = true; - break; - case CultivateCoreResult.SaveConsumptionFailed: - result.Interrupted = true; - break; - } + Response response = await calculatorClient.BatchComputeAsync(userAndUid, deltas).ConfigureAwait(false); - if (result.Interrupted) + if (!response.IsOk()) + { + return; + } + + foreach ((CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) in response.Data.Items.Zip(deltas)) + { + if (!await SaveCultivationAsync(consumption, delta).ConfigureAwait(false)) { + result.Interrupted = true; break; } - } - if (result.Interrupted) - { - infoBarService.Warning(SH.FormatViewModelCultivationBatchAddIncompletedFormat(result.SucceedCount, result.SkippedCount)); - } - else - { - infoBarService.Success(SH.FormatViewModelCultivationBatchAddCompletedFormat(result.SucceedCount, result.SkippedCount)); + ++result.SucceedCount; } } + + if (result.Interrupted) + { + infoBarService.Warning(SH.FormatViewModelCultivationBatchAddIncompletedFormat(result.SucceedCount, result.SkippedCount)); + } + else + { + infoBarService.Success(SH.FormatViewModelCultivationBatchAddCompletedFormat(result.SucceedCount, result.SkippedCount)); + } } - private async ValueTask CultivateCoreAsync(Model.Entity.User user, CalculatorAvatarPromotionDelta delta, AvatarView avatar) + private async ValueTask SaveCultivationAsync(CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) { - Response consumptionResponse = await calculatorClient.ComputeAsync(user, delta).ConfigureAwait(false); - - if (!consumptionResponse.IsOk()) - { - return CultivateCoreResult.ComputeConsumptionFailed; - } - - CalculatorConsumption consumption = consumptionResponse.Data; LevelInformation levelInformation = LevelInformation.From(delta); List items = CalculatorItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume); bool avatarSaved = await cultivationService - .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items, levelInformation) + .SaveConsumptionAsync(CultivateType.AvatarAndSkill, delta.AvatarId, items, levelInformation) .ConfigureAwait(false); try { - ArgumentNullException.ThrowIfNull(avatar.Weapon); + ArgumentNullException.ThrowIfNull(delta.Weapon); // Take a hot path if avatar is not saved. bool avatarAndWeaponSaved = avatarSaved && await cultivationService - .SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation) + .SaveConsumptionAsync(CultivateType.Weapon, delta.Weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation) .ConfigureAwait(false); - if (!avatarAndWeaponSaved) - { - return CultivateCoreResult.SaveConsumptionFailed; - } + return avatarAndWeaponSaved; } catch (HutaoException ex) { infoBarService.Error(ex, SH.ViewModelCultivationAddWarning); } - return CultivateCoreResult.Ok; + return true; } [Command("ExportAsImageCommand")] diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs index b530eacc..0fe1ee39 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs @@ -2,10 +2,12 @@ // Licensed under the MIT license. using Microsoft.UI.Xaml.Controls; +using Snap.Hutao.Control.Extension; using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Model.Entity; using Snap.Hutao.Service.Cultivation; +using Snap.Hutao.Service.Inventory; using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata.ContextAbstraction; using Snap.Hutao.Service.Navigation; @@ -29,6 +31,7 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel private readonly ICultivationService cultivationService; private readonly ILogger logger; private readonly INavigationService navigationService; + private readonly IInventoryService inventoryService; private readonly IMetadataService metadataService; private readonly IInfoBarService infoBarService; private readonly ITaskContext taskContext; @@ -140,8 +143,8 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel await taskContext.SwitchToMainThreadAsync(); CultivateEntries = entries; - InventoryItems = cultivationService.GetInventoryItemViews(project, context, SaveInventoryItemCommand); + await UpdateInventoryItemsAsync().ConfigureAwait(false); await UpdateStatisticsItemsAsync().ConfigureAwait(false); } @@ -173,11 +176,35 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel { if (inventoryItem is not null) { - cultivationService.SaveInventoryItem(inventoryItem); + inventoryService.SaveInventoryItem(inventoryItem); await UpdateStatisticsItemsAsync().ConfigureAwait(false); } } + [Command("RefreshInventoryCommand")] + private async Task RefreshInventoryAsync() + { + if (SelectedProject is null) + { + return; + } + + using (await EnterCriticalExecutionAsync().ConfigureAwait(false)) + { + ContentDialog dialog = await contentDialogFactory + .CreateForIndeterminateProgressAsync(SH.ViewModelCultivationRefreshInventoryProgress) + .ConfigureAwait(false); + + using (await dialog.BlockAsync(taskContext).ConfigureAwait(false)) + { + await inventoryService.RefreshInventoryAsync(SelectedProject).ConfigureAwait(false); + + await UpdateInventoryItemsAsync().ConfigureAwait(false); + await UpdateStatisticsItemsAsync().ConfigureAwait(false); + } + } + } + private async ValueTask UpdateStatisticsItemsAsync() { if (SelectedProject is not null) @@ -201,6 +228,18 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel } } + private async ValueTask UpdateInventoryItemsAsync() + { + if (SelectedProject is not null) + { + await taskContext.SwitchToBackgroundAsync(); + CultivationMetadataContext context = await metadataService.GetContextAsync().ConfigureAwait(false); + + await taskContext.SwitchToMainThreadAsync(); + InventoryItems = inventoryService.GetInventoryItemViews(SelectedProject, context, SaveInventoryItemCommand); + } + } + [Command("NavigateToPageCommand")] private void NavigateToPage(string? typeString) { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs index 07e90422..70bdd518 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs @@ -10,7 +10,6 @@ using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Windowing; using Snap.Hutao.Service.Game.Automation.ScreenCapture; using Snap.Hutao.Service.Notification; -using Snap.Hutao.View.Converter; using Snap.Hutao.ViewModel.Guide; using Snap.Hutao.Web.Hutao.HutaoAsAService; using Snap.Hutao.Win32.Foundation; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs index cf8c9bb1..2a7f7b9f 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs @@ -20,15 +20,14 @@ using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Notification; using Snap.Hutao.Service.User; using Snap.Hutao.View.Dialog; +using Snap.Hutao.ViewModel.User; using Snap.Hutao.Web.Response; using System.Collections.Frozen; using System.Collections.ObjectModel; using System.Runtime.InteropServices; using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; +using CalculateBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption; using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; -using CalculateConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption; -using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item; -using CalculateItemHelper = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.ItemHelper; namespace Snap.Hutao.ViewModel.Wiki; @@ -149,7 +148,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel foreach (Avatar avatar in avatars) { - avatar.Collocation = hutaoCache.AvatarCollocations.GetValueOrDefault(avatar.Id); + avatar.CollocationView = hutaoCache.AvatarCollocations.GetValueOrDefault(avatar.Id); avatar.CookBonusView ??= CookBonusView.Create(avatar.FetterInfo.CookBonus, idMaterialMap); avatar.CultivationItemsView ??= avatar.CultivationItems.SelectList(i => idMaterialMap.GetValueOrDefault(i, Material.Default)); } @@ -163,7 +162,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel return; } - if (userService.Current is null) + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { infoBarService.Warning(SH.MustSelectUserAndUid); return; @@ -178,22 +177,21 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel return; } - Response consumptionResponse = await calculateClient - .ComputeAsync(userService.Current.Entity, delta) + Response response = await calculateClient + .BatchComputeAsync(userAndUid, delta) .ConfigureAwait(false); - if (!consumptionResponse.IsOk()) + if (!response.IsOk()) { return; } - CalculateConsumption consumption = consumptionResponse.Data; + CalculateBatchConsumption batchConsumption = response.Data; LevelInformation levelInformation = LevelInformation.From(delta); - List items = CalculateItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume); try { bool saved = await cultivationService - .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items, levelInformation) + .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, batchConsumption.OverallConsume, levelInformation) .ConfigureAwait(false); if (saved) diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs index bc09b39b..a90604e2 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs @@ -20,13 +20,14 @@ using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Notification; using Snap.Hutao.Service.User; using Snap.Hutao.View.Dialog; +using Snap.Hutao.ViewModel.User; using Snap.Hutao.Web.Response; using System.Collections.Frozen; using System.Collections.ObjectModel; using System.Runtime.InteropServices; using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; +using CalculateBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption; using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; -using CalculateConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption; namespace Snap.Hutao.ViewModel.Wiki; @@ -140,7 +141,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel foreach (Weapon weapon in weapons) { - weapon.Collocation = hutaoCache.WeaponCollocations.GetValueOrDefault(weapon.Id); + weapon.CollocationView = hutaoCache.WeaponCollocations.GetValueOrDefault(weapon.Id); weapon.CultivationItemsView ??= weapon.CultivationItems.SelectList(i => idMaterialMap.GetValueOrDefault(i, Material.Default)); } } @@ -154,7 +155,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel return; } - if (userService.Current is null) + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { infoBarService.Warning(SH.MustSelectUserAndUid); return; @@ -169,21 +170,21 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel return; } - Response consumptionResponse = await calculateClient - .ComputeAsync(userService.Current.Entity, delta) + Response response = await calculateClient + .BatchComputeAsync(userAndUid, delta) .ConfigureAwait(false); - if (!consumptionResponse.IsOk()) + if (!response.IsOk()) { return; } - CalculateConsumption consumption = consumptionResponse.Data; + CalculateBatchConsumption batchConsumption = response.Data; LevelInformation levelInformation = LevelInformation.From(delta); try { bool saved = await cultivationService - .SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation) + .SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, batchConsumption.OverallConsume, levelInformation) .ConfigureAwait(false); if (saved) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/BatchConsumption.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/BatchConsumption.cs index b43e7f8f..b1dd0310 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/BatchConsumption.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/BatchConsumption.cs @@ -6,13 +6,13 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; internal sealed class BatchConsumption { [JsonPropertyName("items")] - public List? Items { get; set; } + public List Items { get; set; } = default!; [JsonPropertyName("available_material")] public List? AvailableMaterial { get; set; } [JsonPropertyName("overall_consume")] - public List? OverallConsume { get; set; } + public List OverallConsume { get; set; } = default!; [JsonPropertyName("has_user_info")] public bool HasUserInfo { get; set; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs index 6e0aa7dd..a2fae462 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs @@ -19,6 +19,7 @@ internal sealed partial class CalculateClient private readonly ILogger logger; private readonly HttpClient httpClient; + [Obsolete("Use BatchComputeAsync instead")] public async ValueTask> ComputeAsync(Model.Entity.User user, AvatarPromotionDelta delta, CancellationToken token = default) { HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() @@ -34,15 +35,18 @@ internal sealed partial class CalculateClient return Response.Response.DefaultIfNull(resp); } - public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, List deltas, CancellationToken token = default) + public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, AvatarPromotionDelta delta, bool syncInventory = false, CancellationToken token = default) { - ArgumentOutOfRangeException.ThrowIfGreaterThan(deltas.Count, 8); + return await BatchComputeAsync(userAndUid, [delta], syncInventory, token).ConfigureAwait(false); + } + public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, List deltas, bool syncInventory = false, CancellationToken token = default) + { BatchConsumptionData data = new() { Items = deltas, - Region = userAndUid.Uid.Region, - Uid = userAndUid.Uid.ToString(), + Region = syncInventory ? userAndUid.Uid.Region : default!, + Uid = syncInventory ? userAndUid.Uid.ToString() : default!, }; HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs index b0a7b62e..58cda946 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs @@ -42,5 +42,5 @@ internal sealed class Item public QualityType Level { get; set; } [JsonPropertyName("lack_num")] - public uint LackNum { get; set; } + public int LackNum { get; set; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs b/src/Snap.Hutao/Snap.Hutao/Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs index 8e5bad92..d5f20a17 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs @@ -1,12 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Snap.Hutao.Win32.System.SystemInformation; internal enum IMAGE_FILE_MACHINE : ushort