diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs
index 5452b29a..9dea0796 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs
@@ -1,13 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity;
-using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Metadata.Item;
-using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Cultivation;
-using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
@@ -15,9 +11,6 @@ using Snap.Hutao.ViewModel.Cultivation;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
using Snap.Hutao.Web.Response;
-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;
@@ -25,9 +18,9 @@ namespace Snap.Hutao.Service.Inventory;
[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 IMetadataService metadataService;
private readonly IInfoBarService infoBarService;
private readonly IUserService userService;
@@ -56,13 +49,7 @@ internal sealed partial class InventoryService : IInventoryService
///
public async ValueTask RefreshInventoryAsync(CultivateProject project)
{
- List cultivationItemsEntryList =
- [
- .. await metadataService.GetAvatarListAsync().ConfigureAwait(false),
- .. await metadataService.GetWeaponListAsync().ConfigureAwait(false),
- ];
-
- cultivationItemsEntryList = MinimalPromotionDelta.Find(cultivationItemsEntryList);
+ List deltas = await minimalPromotionDelta.GetAsync().ConfigureAwait(false);
BatchConsumption? batchConsumption = default;
using (IServiceScope scope = serviceScopeFactory.CreateScope())
@@ -76,7 +63,7 @@ internal sealed partial class InventoryService : IInventoryService
CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService();
Response? resp = await calculateClient
- .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivationItemsEntryList), true)
+ .BatchComputeAsync(userAndUid, deltas, true)
.ConfigureAwait(false);
if (!resp.IsOk())
@@ -93,97 +80,4 @@ internal sealed partial class InventoryService : IInventoryService
await inventoryDbService.AddInventoryItemRangeByProjectIdAsync(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false);
}
}
-
- private static List GeneratePromotionDeltas(List cultivatables)
- {
- List avatars = [];
- List weapons = [];
- HashSet materialIds = [];
-
- while (cultivatables.Count > 0)
- {
- ICultivationItemsAccess bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First();
-
- if (bestItem.CultivationItems.All(materialIds.Contains))
- {
- break;
- }
-
- switch (bestItem)
- {
- case MetadataAvatar avatar:
- avatars.Add(avatar);
- break;
- case MetadataWeapon weapon:
- weapons.Add(weapon);
- break;
- default:
- throw HutaoException.NotSupported();
- }
-
- foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(bestItem.CultivationItems))
- {
- materialIds.Add(materialId);
- }
-
- cultivatables.Remove(bestItem);
- }
-
- List deltas = [];
-
- for (int i = 0; i < Math.Max(avatars.Count, weapons.Count); i++)
- {
- MetadataAvatar? avatar = avatars.ElementAtOrDefault(i);
- MetadataWeapon? weapon = weapons.ElementAtOrDefault(i);
-
- if (avatar is not null)
- {
- AvatarPromotionDelta delta = new()
- {
- AvatarId = avatar.Id,
- AvatarLevelCurrent = 1,
- AvatarLevelTarget = 90,
- SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta()
- {
- Id = skill.GroupId,
- LevelCurrent = 1,
- LevelTarget = 10,
- }),
- };
-
- if (weapon is not null)
- {
- delta.Weapon = new()
- {
- Id = weapon.Id,
- LevelCurrent = 1,
- LevelTarget = 90,
- };
- }
-
- deltas.Add(delta);
-
- continue;
- }
-
- if (weapon is not null)
- {
- AvatarPromotionDelta delta = new()
- {
- Weapon = new()
- {
- Id = weapon.Id,
- LevelCurrent = 1,
- LevelTarget = 90,
- },
- };
-
- deltas.Add(delta);
-
- continue;
- }
- }
-
- return deltas;
- }
}
\ 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
index 7ea09766..91b3ee84 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs
@@ -2,16 +2,43 @@
// Licensed under the MIT license.
using Google.OrTools.LinearSolver;
+using Microsoft.Extensions.Caching.Memory;
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;
-internal static class MinimalPromotionDelta
+[ConstructorGenerated]
+[Injection(InjectAs.Singleton)]
+internal sealed partial class MinimalPromotionDelta
{
- public static List Find(List cultivationItems)
+ 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),
+ .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(w => w.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE),
+ ];
+ return memoryCache.Set(CacheKey, GeneratePromotionDeltas(Minimize(cultivationItemsEntryList)));
+ }
+
+ private static List Minimize(List cultivationItems)
{
using (Solver? solver = Solver.CreateSolver("SCIP"))
{
@@ -54,4 +81,56 @@ internal static class MinimalPromotionDelta
return results;
}
}
+
+ private static List GeneratePromotionDeltas(List cultivationItems)
+ {
+ List deltas = [];
+
+ 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:
+ if (deltas.FirstOrDefault(d => d.Weapon is null) is { } delta)
+ {
+ delta.Weapon = new()
+ {
+ Id = weapon.Id,
+ LevelCurrent = 1,
+ LevelTarget = 90,
+ };
+
+ break;
+ }
+
+ deltas.Add(new()
+ {
+ Weapon = new()
+ {
+ Id = weapon.Id,
+ LevelCurrent = 1,
+ LevelTarget = 90,
+ },
+ });
+
+ break;
+ }
+ }
+
+ return deltas;
+ }
}
\ No newline at end of file