diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20260504033049_AddCultivateProjectAvatarPropertyBatchPreferences.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20260504033049_AddCultivateProjectAvatarPropertyBatchPreferences.Designer.cs new file mode 100644 index 0000000..942e0b3 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20260504033049_AddCultivateProjectAvatarPropertyBatchPreferences.Designer.cs @@ -0,0 +1,670 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Snap.Hutao.Model.Entity.Database; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260504033049_AddCultivateProjectAvatarPropertyBatchPreferences")] + partial class AddCultivateProjectAvatarPropertyBatchPreferences + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.0"); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("Current") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("achievements"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("achievement_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Info2") + .HasColumnType("TEXT"); + + b.Property("RefreshTime") + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("avatar_infos"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarStrategy", b => + { + b.Property("AvatarId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChineseStrategyId") + .HasColumnType("INTEGER"); + + b.Property("OverseaStrategyId") + .HasColumnType("INTEGER"); + + b.HasKey("AvatarId"); + + b.ToTable("avatar_strategies"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.Property("RelatedEntryId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("RelatedEntryId"); + + b.ToTable("cultivate_entries"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarIsPromoting") + .HasColumnType("INTEGER"); + + b.Property("AvatarLevelFrom") + .HasColumnType("INTEGER"); + + b.Property("AvatarLevelTo") + .HasColumnType("INTEGER"); + + b.Property("EntryId") + .HasColumnType("TEXT"); + + b.Property("SkillALevelFrom") + .HasColumnType("INTEGER"); + + b.Property("SkillALevelTo") + .HasColumnType("INTEGER"); + + b.Property("SkillELevelFrom") + .HasColumnType("INTEGER"); + + b.Property("SkillELevelTo") + .HasColumnType("INTEGER"); + + b.Property("SkillQLevelFrom") + .HasColumnType("INTEGER"); + + b.Property("SkillQLevelTo") + .HasColumnType("INTEGER"); + + b.Property("WeaponIsPromoting") + .HasColumnType("INTEGER"); + + b.Property("WeaponLevelFrom") + .HasColumnType("INTEGER"); + + b.Property("WeaponLevelTo") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.HasIndex("EntryId") + .IsUnique(); + + b.ToTable("cultivate_entry_level_informations"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.Property("EntryId") + .HasColumnType("TEXT"); + + b.Property("IsFinished") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.HasIndex("EntryId"); + + b.ToTable("cultivate_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarPropertyBatchCultivatePreferencesJson") + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ServerTimeZoneOffset") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("cultivate_projects"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DailyNote") + .HasColumnType("TEXT"); + + b.Property("DailyTaskDotVisible") + .HasColumnType("INTEGER"); + + b.Property("DailyTaskNotify") + .HasColumnType("INTEGER"); + + b.Property("DailyTaskNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionDotVisible") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotify") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinDotVisible") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("RefreshTime") + .HasColumnType("TEXT"); + + b.Property("ResinDotVisible") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("TransformerDotVisible") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotify") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("UserId"); + + b.ToTable("daily_notes"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("gacha_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("GachaType") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("QueryType") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("gacha_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MacAddress") + .HasColumnType("TEXT"); + + b.Property("Mid") + .HasColumnType("TEXT"); + + b.Property("MihoyoSDK") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.ToTable("game_accounts"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.HardChallengeEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("HardChallengeData") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ScheduleId") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("hard_challenges"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ProjectId"); + + b.ToTable("inventory_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ExpireTime") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("object_cache"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.RoleCombatEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("RoleCombatData") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ScheduleId") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("role_combats"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("settings"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ScheduleId") + .HasColumnType("INTEGER"); + + b.Property("SpiralAbyss") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("spiral_abysses"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.UidProfilePicture", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CostumeId") + .HasColumnType("INTEGER"); + + b.Property("ProfilePictureId") + .HasColumnType("INTEGER"); + + b.Property("RefreshTime") + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("uid_profile_pictures"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Aid") + .HasColumnType("TEXT"); + + b.Property("CookieToken") + .HasColumnType("TEXT"); + + b.Property("CookieTokenLastUpdateTime") + .HasColumnType("TEXT"); + + b.Property("Fingerprint") + .HasColumnType("TEXT"); + + b.Property("FingerprintLastUpdateTime") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("IsOversea") + .HasColumnType("INTEGER"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("LToken") + .HasColumnType("TEXT") + .HasColumnName("Ltoken"); + + b.Property("Mid") + .HasColumnType("TEXT"); + + b.Property("PreferredUid") + .HasColumnType("TEXT"); + + b.Property("SToken") + .HasColumnType("TEXT") + .HasColumnName("Stoken"); + + b.HasKey("InnerId"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "RelatedEntry") + .WithMany() + .HasForeignKey("RelatedEntryId"); + + b.Navigation("Project"); + + b.Navigation("RelatedEntry"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry") + .WithOne("LevelInformation") + .HasForeignKey("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", "EntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry") + .WithMany() + .HasForeignKey("EntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b => + { + b.Navigation("LevelInformation"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20260504033049_AddCultivateProjectAvatarPropertyBatchPreferences.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20260504033049_AddCultivateProjectAvatarPropertyBatchPreferences.cs new file mode 100644 index 0000000..9e5a9bd --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20260504033049_AddCultivateProjectAvatarPropertyBatchPreferences.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + /// + public partial class AddCultivateProjectAvatarPropertyBatchPreferences : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_cultivate_entries_cultivate_entries_RelatedEntryId", + table: "cultivate_entries"); + + migrationBuilder.AddColumn( + name: "AvatarPropertyBatchCultivatePreferencesJson", + table: "cultivate_projects", + type: "TEXT", + nullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_cultivate_entries_cultivate_entries_RelatedEntryId", + table: "cultivate_entries", + column: "RelatedEntryId", + principalTable: "cultivate_entries", + principalColumn: "InnerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_cultivate_entries_cultivate_entries_RelatedEntryId", + table: "cultivate_entries"); + + migrationBuilder.DropColumn( + name: "AvatarPropertyBatchCultivatePreferencesJson", + table: "cultivate_projects"); + + migrationBuilder.AddForeignKey( + name: "FK_cultivate_entries_cultivate_entries_RelatedEntryId", + table: "cultivate_entries", + column: "RelatedEntryId", + principalTable: "cultivate_entries", + principalColumn: "InnerId", + onDelete: ReferentialAction.SetNull); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs index 58a702c..32b2000 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs @@ -212,6 +212,9 @@ namespace Snap.Hutao.Migrations .ValueGeneratedOnAdd() .HasColumnType("TEXT"); + b.Property("AvatarPropertyBatchCultivatePreferencesJson") + .HasColumnType("TEXT"); + b.Property("IsSelected") .HasColumnType("INTEGER"); @@ -592,8 +595,7 @@ namespace Snap.Hutao.Migrations b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "RelatedEntry") .WithMany() - .HasForeignKey("RelatedEntryId") - .OnDelete(DeleteBehavior.SetNull); + .HasForeignKey("RelatedEntryId"); b.Navigation("Project"); diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Cultivation/CultivateProjectAvatarPropertyBatchPreferences.cs b/src/Snap.Hutao/Snap.Hutao/Model/Cultivation/CultivateProjectAvatarPropertyBatchPreferences.cs new file mode 100644 index 0000000..1f79c48 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Cultivation/CultivateProjectAvatarPropertyBatchPreferences.cs @@ -0,0 +1,24 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Cultivation; + +/// +/// 「我的角色」批量同步到当前养成计划时,在批量对话框中使用的目标等级、保存策略等;按 持久化。 +/// +internal sealed class CultivateProjectAvatarPropertyBatchPreferences +{ + public uint AvatarLevelTarget { get; set; } + + public uint SkillATarget { get; set; } + + public uint SkillETarget { get; set; } + + public uint SkillQTarget { get; set; } + + public uint WeaponLevelTarget { get; set; } + + public int ConsumptionSaveStrategyIndex { get; set; } + + public bool ClearAvatarAndWeaponEntriesBeforeSync { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateProject.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateProject.cs index 217c687..30ebde5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateProject.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateProject.cs @@ -22,6 +22,11 @@ internal sealed partial class CultivateProject : ISelectable, public TimeSpan ServerTimeZoneOffset { get; set; } + /// + /// 的 JSON,按项目记忆批量同步养成选项。 + /// + public string? AvatarPropertyBatchCultivatePreferencesJson { get; set; } + public static CultivateProject From(string name, in TimeSpan serverTimeOffset) { return new() diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index 9a0e04d..e8704e6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -2,13 +2,16 @@ // Licensed under the MIT license. using Snap.Hutao.Core.Database; +using Snap.Hutao.Core.Text.Json; using Snap.Hutao.Model; +using Snap.Hutao.Model.Cultivation; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Primitive; using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Metadata; using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Primitive; +using Snap.Hutao.Service.Abstraction; using Snap.Hutao.Service.Cultivation.Consumption; using Snap.Hutao.Service.Inventory; using Snap.Hutao.Service.Metadata.ContextAbstraction; @@ -20,6 +23,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Text.Json; using ModelItem = Snap.Hutao.Model.Item; namespace Snap.Hutao.Service.Cultivation; @@ -363,6 +367,50 @@ internal sealed partial class CultivationService : ICultivationService return ProjectAddResultKind.Added; } + public async ValueTask GetAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync() + { + IAdvancedDbCollectionView projects = await GetProjectCollectionAsync().ConfigureAwait(false); + if (!await EnsureCurrentProjectAsync(projects).ConfigureAwait(false)) + { + return null; + } + + string? json = projects.CurrentItem?.AvatarPropertyBatchCultivatePreferencesJson; + if (string.IsNullOrWhiteSpace(json)) + { + return null; + } + + try + { + return JsonSerializer.Deserialize(json, JsonOptions.Default); + } + catch (JsonException) + { + return null; + } + } + + public async ValueTask SaveAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync(CultivateProjectAvatarPropertyBatchPreferences preferences) + { + ArgumentNullException.ThrowIfNull(preferences); + + IAdvancedDbCollectionView projects = await GetProjectCollectionAsync().ConfigureAwait(false); + if (!await EnsureCurrentProjectAsync(projects).ConfigureAwait(false)) + { + return; + } + + ArgumentNullException.ThrowIfNull(projects.CurrentItem); + + CultivateProject project = projects.CurrentItem; + + await taskContext.SwitchToBackgroundAsync(); + + project.AvatarPropertyBatchCultivatePreferencesJson = JsonSerializer.Serialize(preferences, JsonOptions.Default); + cultivationRepository.Update(project); + } + public async ValueTask RemoveProjectAsync(CultivateProject project) { ArgumentNullException.ThrowIfNull(projects); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs index 7daf318..870dc85 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Core.Database; +using Snap.Hutao.Model.Cultivation; using Snap.Hutao.Model.Entity; using Snap.Hutao.Service.Cultivation.Consumption; using Snap.Hutao.ViewModel.Cultivation; @@ -43,4 +44,14 @@ internal interface ICultivationService void SaveCultivateItem(CultivateItemView item); ValueTask TryAddProjectAsync(CultivateProject project); + + /// + /// 读取当前选中养成计划下「我的角色」批量同步对话框的已保存选项;未保存时返回 。 + /// + ValueTask GetAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync(); + + /// + /// 将批量同步对话框的选项写入当前选中养成计划。 + /// + ValueTask SaveAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync(CultivateProjectAvatarPropertyBatchPreferences preferences); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Dialog/CultivatePromotionDeltaBatchDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Dialog/CultivatePromotionDeltaBatchDialog.xaml.cs index 00aa35e..e08a810 100644 --- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Dialog/CultivatePromotionDeltaBatchDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Dialog/CultivatePromotionDeltaBatchDialog.xaml.cs @@ -4,6 +4,7 @@ using Microsoft.UI.Xaml.Controls; using Snap.Hutao.Core.Setting; using Snap.Hutao.Factory.ContentDialog; +using Snap.Hutao.Model.Cultivation; using Snap.Hutao.Service.Cultivation.Consumption; using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; @@ -15,8 +16,17 @@ internal sealed partial class CultivatePromotionDeltaBatchDialog : ContentDialog { private readonly IContentDialogFactory contentDialogFactory; - [GeneratedConstructor(InitializeComponent = true)] - public partial CultivatePromotionDeltaBatchDialog(IServiceProvider serviceProvider); + public CultivatePromotionDeltaBatchDialog(IServiceProvider serviceProvider, CultivateProjectAvatarPropertyBatchPreferences? initialPreferences = null) + { + InitializeComponent(); + + contentDialogFactory = serviceProvider.GetRequiredService(); + + if (initialPreferences is not null) + { + ApplyInitialPreferences(initialPreferences); + } + } public async ValueTask> GetPromotionDeltaBaselineAsync() { @@ -49,8 +59,28 @@ internal sealed partial class CultivatePromotionDeltaBatchDialog : ContentDialog return new(true, new CultivatePromotionDeltaOptions(PromotionDelta, (ConsumptionSaveStrategyKind)SaveModeSelector.SelectedIndex, ClearAvatarAndWeaponEntriesBeforeSync)); } + private void ApplyInitialPreferences(CultivateProjectAvatarPropertyBatchPreferences p) + { + PromotionDelta.AvatarLevelTarget = p.AvatarLevelTarget; + + if (PromotionDelta.SkillList is [{ } a, { } e, { } q, ..]) + { + a.LevelTarget = p.SkillATarget; + e.LevelTarget = p.SkillETarget; + q.LevelTarget = p.SkillQTarget; + } + + if (PromotionDelta.Weapon is { } w) + { + w.LevelTarget = p.WeaponLevelTarget; + } + + SaveModeSelector.SelectedIndex = int.Clamp(p.ConsumptionSaveStrategyIndex, 0, 2); + ClearAvatarAndWeaponEntriesBeforeSync = p.ClearAvatarAndWeaponEntriesBeforeSync; + } + private static object CreatePromotionDeltaDefaultValue() { return AvatarPromotionDelta.CreateForBaseline(); } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs index 2aa581a..a2358dc 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs @@ -9,6 +9,7 @@ using Snap.Hutao.Core.Setting; using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Model; using Snap.Hutao.Model.Calculable; +using Snap.Hutao.Model.Cultivation; using Snap.Hutao.Model.Entity.Primitive; using Snap.Hutao.Service; using Snap.Hutao.Service.AvatarInfo; @@ -269,15 +270,27 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I targetAvatars = [.. avatars.Source]; } - CultivatePromotionDeltaBatchDialog dialog = await scopeContext.ContentDialogFactory - .CreateInstanceAsync(scopeContext.ServiceProvider) + CultivateProjectAvatarPropertyBatchPreferences? batchPrefs = await scopeContext.CultivationService + .GetAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync() .ConfigureAwait(false); + CultivatePromotionDeltaBatchDialog dialog = batchPrefs is null + ? await scopeContext.ContentDialogFactory + .CreateInstanceAsync(scopeContext.ServiceProvider) + .ConfigureAwait(false) + : await scopeContext.ContentDialogFactory + .CreateInstanceAsync(scopeContext.ServiceProvider, batchPrefs) + .ConfigureAwait(false); + if (await dialog.GetPromotionDeltaBaselineAsync().ConfigureAwait(false) is not (true, { } baseline)) { return; } + await scopeContext.CultivationService + .SaveAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync(ToAvatarPropertyBatchPreferences(baseline)) + .ConfigureAwait(false); + ArgumentNullException.ThrowIfNull(baseline.Delta.Weapon); ContentDialog progressDialog = await scopeContext.ContentDialogFactory @@ -329,6 +342,31 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I scopeContext.Messenger.Send(message); } + private static CultivateProjectAvatarPropertyBatchPreferences ToAvatarPropertyBatchPreferences(CultivatePromotionDeltaOptions baseline) + { + CalculatorAvatarPromotionDelta d = baseline.Delta; + uint sa = 10; + uint se = 10; + uint sq = 10; + if (d.SkillList is [{ } a, { } e, { } q, ..]) + { + sa = a.LevelTarget; + se = e.LevelTarget; + sq = q.LevelTarget; + } + + return new CultivateProjectAvatarPropertyBatchPreferences + { + AvatarLevelTarget = d.AvatarLevelTarget, + SkillATarget = sa, + SkillETarget = se, + SkillQTarget = sq, + WeaponLevelTarget = d.Weapon?.LevelTarget ?? 90U, + ConsumptionSaveStrategyIndex = (int)baseline.Strategy, + ClearAvatarAndWeaponEntriesBeforeSync = baseline.ClearAvatarAndWeaponEntriesBeforeSync, + }; + } + /// if we can continue saving consumptions, otherwise . private async ValueTask SaveCultivationAsync(CalculatorConsumption consumption, CultivatePromotionDeltaOptions options, bool isBatch = false) {