From 0ee875d28d956c8bdcc5469d2a2bec645a3e4433 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Wed, 2 Aug 2023 21:58:46 +0800 Subject: [PATCH] user service left --- .../Core/Database/ScopedDbCurrent.cs | 68 +++++++++++++ .../DependencyInjection/IocConfiguration.cs | 1 + .../Snap.Hutao/Core/Threading/Delay.cs | 6 ++ .../Snap.Hutao/Model/IEntityWithMetadata.cs | 15 +-- .../Service/Hutao/HutaoUserService.cs | 2 +- .../Hutao/IHutaoUserServiceInitialization.cs | 2 +- .../IMetadataServiceInitialization.cs | 2 +- .../Service/Metadata/MetadataService.cs | 33 +++---- .../Service/Navigation/INavigationAwaiter.cs | 2 +- .../Navigation/INavigationRecipient.cs | 2 +- .../Service/Navigation/INavigationService.cs | 2 +- .../Service/Navigation/NavigationExtra.cs | 4 +- .../Service/Navigation/NavigationService.cs | 51 +++++++--- .../Service/Notification/InfoBarService.cs | 22 +++-- .../ISpiralAbyssRecordDbService.cs | 16 ++++ .../SpiralAbyss/ISpiralAbyssRecordService.cs | 4 +- .../SpiralAbyss/SpiralAbyssRecordDbService.cs | 51 ++++++++++ .../SpiralAbyss/SpiralAbyssRecordService.cs | 63 ++++-------- .../Snap.Hutao/Service/User/IUserService.cs | 4 +- .../Snap.Hutao/Service/User/UserService.cs | 95 +++++++------------ .../Achievement/AchievementViewModel.cs | 2 +- .../Snap.Hutao/ViewModel/User/User.cs | 3 +- 22 files changed, 287 insertions(+), 163 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordDbService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs b/src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs index cade03ff..6e1ed000 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs @@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.Messaging; using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Model; using Snap.Hutao.Model.Entity.Database; namespace Snap.Hutao.Core.Database; @@ -72,6 +73,73 @@ internal sealed class ScopedDbCurrent dbSet.UpdateAndSave(current); } + messenger.Send(message); + } + } + } +} + +[SuppressMessage("", "SA1402")] +internal sealed class ScopedDbCurrent + where TEntityOnly : class, IEntityOnly + where TEntity : class, ISelectable + where TMessage : Message.ValueChangedMessage, new() +{ + private readonly IServiceProvider serviceProvider; + private readonly IMessenger messenger; + + private TEntityOnly? current; + + /// + /// 构造一个新的数据库当前项 + /// + /// 服务提供器 + public ScopedDbCurrent(IServiceProvider serviceProvider) + { + messenger = serviceProvider.GetRequiredService(); + this.serviceProvider = serviceProvider; + } + + /// + /// 当前选中的项 + /// + public TEntityOnly? Current + { + get => current; + set + { + // prevent useless sets + if (current == value) + { + return; + } + + // TODO: Troubeshooting why the serviceProvider will NRE + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + DbSet dbSet = appDbContext.Set(); + + // only update when not processing a deletion + if (value != null) + { + if (current != null) + { + current.Entity.IsSelected = false; + dbSet.UpdateAndSave(current.Entity); + } + } + + TMessage message = new() { OldValue = current, NewValue = value }; + + current = value; + + if (current != null) + { + current.Entity.IsSelected = true; + dbSet.UpdateAndSave(current.Entity); + } + messenger.Send(message); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs index 6c111994..1d01bcf7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs @@ -32,6 +32,7 @@ internal static class IocConfiguration { return services .AddTransient(typeof(Database.ScopedDbCurrent<,>)) + .AddTransient(typeof(Database.ScopedDbCurrent<,,>)) .AddDbContext(AddDbContextCore); } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/Delay.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/Delay.cs index 51428dba..b87abc2f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/Delay.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/Delay.cs @@ -22,4 +22,10 @@ internal readonly struct Delay { return Task.Delay(TimeSpan.FromSeconds(seconds)).AsValueTask(); } + + [SuppressMessage("", "VSTHRD200")] + public static ValueTask FromMilliSeconds(int seconds) + { + return Task.Delay(TimeSpan.FromMilliseconds(seconds)).AsValueTask(); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/IEntityWithMetadata.cs b/src/Snap.Hutao/Snap.Hutao/Model/IEntityWithMetadata.cs index 9fd4d1a5..3eedb828 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/IEntityWithMetadata.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/IEntityWithMetadata.cs @@ -9,15 +9,18 @@ namespace Snap.Hutao.Model; /// 实体 /// 元数据 [HighQuality] -internal interface IEntityWithMetadata +internal interface IEntityWithMetadata : IEntityOnly +{ + /// + /// 元数据 + /// + TMetadata Inner { get; } +} + +internal interface IEntityOnly { /// /// 实体 /// TEntity Entity { get; } - - /// - /// 元数据 - /// - TMetadata Inner { get; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs index 0af0e326..fec88588 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs @@ -29,7 +29,7 @@ internal sealed partial class HutaoUserService : IHutaoUserService, IHutaoUserSe } /// - public async Task InitializeInternalAsync(CancellationToken token = default) + public async ValueTask InitializeInternalAsync(CancellationToken token = default) { string userName = LocalSetting.Get(SettingKeys.PassportUserName, string.Empty); string passport = LocalSetting.Get(SettingKeys.PassportPassword, string.Empty); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserServiceInitialization.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserServiceInitialization.cs index e5a46442..608c69e3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserServiceInitialization.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserServiceInitialization.cs @@ -13,5 +13,5 @@ internal interface IHutaoUserServiceInitialization /// /// 取消令牌 /// 任务 - Task InitializeInternalAsync(CancellationToken token = default); + ValueTask InitializeInternalAsync(CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceInitialization.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceInitialization.cs index 42ef9a8e..ad06a00b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceInitialization.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceInitialization.cs @@ -14,5 +14,5 @@ internal interface IMetadataServiceInitialization /// /// 取消令牌 /// 任务 - Task InitializeInternalAsync(CancellationToken token = default); + ValueTask InitializeInternalAsync(CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs index d8b35b95..faef90ba 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs @@ -44,7 +44,7 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi } /// - public async Task InitializeInternalAsync(CancellationToken token = default) + public async ValueTask InitializeInternalAsync(CancellationToken token = default) { if (isInitialized) { @@ -58,19 +58,19 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi } } - private async Task TryUpdateMetadataAsync(CancellationToken token) + private async ValueTask TryUpdateMetadataAsync(CancellationToken token) { - IDictionary? metaMd5Map; + Dictionary? metaXXH64Map; try { string metadataFile = metadataOptions.GetLocalizedRemoteFile(MetaFileName); // download meta check file - metaMd5Map = await httpClient - .GetFromJsonAsync>(metadataFile, options, token) + metaXXH64Map = await httpClient + .GetFromJsonAsync>(metadataFile, options, token) .ConfigureAwait(false); - if (metaMd5Map is null) + if (metaXXH64Map is null) { infoBarService.Error(SH.ServiceMetadataParseFailed); return false; @@ -90,27 +90,20 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi return false; } - await CheckMetadataAsync(metaMd5Map, token).ConfigureAwait(false); + await CheckMetadataSourceFilesAsync(metaXXH64Map, token).ConfigureAwait(false); // save metadataFile using (FileStream metaFileStream = File.Create(metadataOptions.GetLocalizedLocalFile(MetaFileName))) { await JsonSerializer - .SerializeAsync(metaFileStream, metaMd5Map, options, token) + .SerializeAsync(metaFileStream, metaXXH64Map, options, token) .ConfigureAwait(false); } return true; } - /// - /// 检查元数据的Md5值是否匹配 - /// 如果不匹配则尝试下载 - /// - /// 元数据校验表 - /// 取消令牌 - /// 令牌 - private Task CheckMetadataAsync(IDictionary metaMd5Map, CancellationToken token) + private ValueTask CheckMetadataSourceFilesAsync(Dictionary metaMd5Map, CancellationToken token) { return Parallel.ForEachAsync(metaMd5Map, token, async (pair, token) => { @@ -126,14 +119,14 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi if (!skip) { - logger.LogInformation("{hash} of {file} not matched, begin downloading", nameof(XXH64), fileFullName); + logger.LogInformation("{Hash} of {File} not matched, begin downloading", nameof(XXH64), fileFullName); - await DownloadMetadataAsync(fileFullName, token).ConfigureAwait(false); + await DownloadMetadataSourceFilesAsync(fileFullName, token).ConfigureAwait(false); } - }); + }).AsValueTask(); } - private async Task DownloadMetadataAsync(string fileFullName, CancellationToken token) + private async ValueTask DownloadMetadataSourceFilesAsync(string fileFullName, CancellationToken token) { Stream sourceStream = await httpClient .GetStreamAsync(metadataOptions.GetLocalizedRemoteFile(fileFullName), token) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs index ec1255f8..d620d0d4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs @@ -18,5 +18,5 @@ internal interface INavigationAwaiter /// 等待导航完成,或直到抛出异常 /// /// 导航完成的任务 - Task WaitForCompletionAsync(); + ValueTask WaitForCompletionAsync(); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationRecipient.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationRecipient.cs index 03b765f3..e4c03f06 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationRecipient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationRecipient.cs @@ -16,5 +16,5 @@ internal interface INavigationRecipient /// /// 导航数据 /// 接收处理结果是否成功 - Task ReceiveAsync(INavigationData data); + ValueTask ReceiveAsync(INavigationData data); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs index ceb353b1..a098a5f3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs @@ -40,7 +40,7 @@ internal interface INavigationService : ICastService /// 要传递的数据 /// 是否同步标签,当在代码中调用时应设为 true /// 是否导航成功 - Task NavigateAsync(INavigationAwaiter data, bool syncNavigationViewItem = false) + ValueTask NavigateAsync(INavigationAwaiter data, bool syncNavigationViewItem = false) where TPage : Page; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs index 2563a897..c1f1c326 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs @@ -27,9 +27,9 @@ internal sealed class NavigationExtra : INavigationData, INavigationAwaiter public object? Data { get; set; } /// - public Task WaitForCompletionAsync() + public ValueTask WaitForCompletionAsync() { - return navigationCompletedTaskCompletionSource.Task; + return navigationCompletedTaskCompletionSource.Task.AsValueTask(); } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs index b15716c2..906708f0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs @@ -7,6 +7,7 @@ using Snap.Hutao.Core.Setting; using Snap.Hutao.Service.Notification; using Snap.Hutao.View.Helper; using Snap.Hutao.View.Page; +using Windows.Foundation; namespace Snap.Hutao.Service.Navigation; @@ -14,18 +15,34 @@ namespace Snap.Hutao.Service.Navigation; /// 导航服务 /// [HighQuality] -[ConstructorGenerated] [Injection(InjectAs.Singleton, typeof(INavigationService))] -internal sealed partial class NavigationService : INavigationService, INavigationInitialization +internal sealed class NavigationService : INavigationService, INavigationInitialization { private readonly ILogger logger; private readonly IInfoBarService infoBarService; private readonly ITaskContext taskContext; + private readonly TypedEventHandler itemInvokedEventHandler; + private readonly TypedEventHandler backRequestedEventHandler; + private readonly TypedEventHandler paneOpenedEventHandler; + private readonly TypedEventHandler paneClosedEventHandler; + private Frame? frame; private NavigationView? navigationView; private NavigationViewItem? selected; + public NavigationService(IServiceProvider serviceProvider) + { + logger = serviceProvider.GetRequiredService>(); + infoBarService = serviceProvider.GetRequiredService(); + taskContext = serviceProvider.GetRequiredService(); + + itemInvokedEventHandler = OnItemInvoked; + backRequestedEventHandler = OnBackRequested; + paneOpenedEventHandler = OnPaneStateChanged; + paneClosedEventHandler = OnPaneStateChanged; + } + private NavigationView? NavigationView { get => navigationView; @@ -33,24 +50,24 @@ internal sealed partial class NavigationService : INavigationService, INavigatio set { // remove old listener - if (navigationView != null) + if (navigationView is not null) { - navigationView.ItemInvoked -= OnItemInvoked; - navigationView.BackRequested -= OnBackRequested; - navigationView.PaneClosed -= OnPaneStateChanged; - navigationView.PaneOpened -= OnPaneStateChanged; + navigationView.ItemInvoked -= itemInvokedEventHandler; + navigationView.BackRequested -= backRequestedEventHandler; + navigationView.PaneClosed -= paneOpenedEventHandler; + navigationView.PaneOpened -= paneClosedEventHandler; } ArgumentNullException.ThrowIfNull(value); navigationView = value; // add new listener - if (navigationView != null) + if (navigationView is not null) { - navigationView.ItemInvoked += OnItemInvoked; - navigationView.BackRequested += OnBackRequested; - navigationView.PaneClosed += OnPaneStateChanged; - navigationView.PaneOpened += OnPaneStateChanged; + navigationView.ItemInvoked += itemInvokedEventHandler; + navigationView.BackRequested += backRequestedEventHandler; + navigationView.PaneClosed += paneOpenedEventHandler; + navigationView.PaneOpened += paneClosedEventHandler; } } } @@ -91,7 +108,7 @@ internal sealed partial class NavigationService : INavigationService, INavigatio } /// - public async Task NavigateAsync(INavigationAwaiter data, bool syncNavigationViewItem = false) + public async ValueTask NavigateAsync(INavigationAwaiter data, bool syncNavigationViewItem = false) where TPage : Page { NavigationResult result = Navigate(data, syncNavigationViewItem); @@ -180,9 +197,13 @@ internal sealed partial class NavigationService : INavigationService, INavigatio { yield return item; - foreach (NavigationViewItem subItem in EnumerateMenuItems(item.MenuItems)) + // Suppress recursion method call if possible + if (item.MenuItems.Count > 0) { - yield return subItem; + foreach (NavigationViewItem subItem in EnumerateMenuItems(item.MenuItems)) + { + yield return subItem; + } } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs index c8e62e8b..a504fb62 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs @@ -4,20 +4,30 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; using System.Collections.ObjectModel; +using Windows.Foundation; namespace Snap.Hutao.Service.Notification; /// [HighQuality] -[ConstructorGenerated] [Injection(InjectAs.Singleton, typeof(IInfoBarService))] -internal sealed partial class InfoBarService : IInfoBarService +internal sealed class InfoBarService : IInfoBarService { private readonly ILogger logger; private readonly ITaskContext taskContext; + private readonly TypedEventHandler infobarClosedEventHandler; + private ObservableCollection? collection; + public InfoBarService(IServiceProvider serviceProvider) + { + logger = serviceProvider.GetRequiredService>(); + taskContext = serviceProvider.GetRequiredService(); + + infobarClosedEventHandler = OnInfoBarClosed; + } + /// public ObservableCollection Collection { @@ -101,7 +111,7 @@ internal sealed partial class InfoBarService : IInfoBarService /// 标题 /// 消息 /// 关闭延迟 - private async Task PrepareInfoBarAndShowInternalAsync(InfoBarSeverity severity, string? title, string? message, int delay) + private async ValueTask PrepareInfoBarAndShowInternalAsync(InfoBarSeverity severity, string? title, string? message, int delay) { await taskContext.SwitchToMainThreadAsync(); @@ -114,12 +124,12 @@ internal sealed partial class InfoBarService : IInfoBarService Transitions = new() { new AddDeleteThemeTransition() }, }; - infoBar.Closed += OnInfoBarClosed; + infoBar.Closed += infobarClosedEventHandler; collection!.Add(infoBar); if (delay > 0) { - await Task.Delay(delay).ConfigureAwait(true); + await Delay.FromMilliSeconds(delay).ConfigureAwait(true); collection.Remove(infoBar); infoBar.IsOpen = false; } @@ -128,6 +138,6 @@ internal sealed partial class InfoBarService : IInfoBarService private void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args) { taskContext.InvokeOnMainThread(() => collection!.Remove(sender)); - sender.Closed -= OnInfoBarClosed; + sender.Closed -= infobarClosedEventHandler; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordDbService.cs new file mode 100644 index 00000000..6cdcd94c --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordDbService.cs @@ -0,0 +1,16 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Entity; +using System.Collections.ObjectModel; + +namespace Snap.Hutao.Service.SpiralAbyss; + +internal interface ISpiralAbyssRecordDbService +{ + ValueTask AddSpiralAbyssEntryAsync(SpiralAbyssEntry entry); + + ValueTask> GetSpiralAbyssEntryCollectionByUidAsync(string uid); + + ValueTask UpdateSpiralAbyssEntryAsync(SpiralAbyssEntry entry); +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordService.cs index 314b9b1a..52090398 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordService.cs @@ -18,12 +18,12 @@ internal interface ISpiralAbyssRecordService /// /// 当前角色 /// 深渊记录集合 - Task> GetSpiralAbyssCollectionAsync(UserAndUid userAndUid); + ValueTask> GetSpiralAbyssCollectionAsync(UserAndUid userAndUid); /// /// 异步刷新深渊记录 /// /// 当前角色 /// 任务 - Task RefreshSpiralAbyssAsync(UserAndUid userAndUid); + ValueTask RefreshSpiralAbyssAsync(UserAndUid userAndUid); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs new file mode 100644 index 00000000..64880fc8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs @@ -0,0 +1,51 @@ +// 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 System.Collections.ObjectModel; + +namespace Snap.Hutao.Service.SpiralAbyss; + +[ConstructorGenerated] +[Injection(InjectAs.Scoped, typeof(ISpiralAbyssRecordDbService))] +internal sealed partial class SpiralAbyssRecordDbService : ISpiralAbyssRecordDbService +{ + private readonly IServiceProvider serviceProvider; + + public async ValueTask> GetSpiralAbyssEntryCollectionByUidAsync(string uid) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + + List entries = await appDbContext.SpiralAbysses + .Where(s => s.Uid == uid) + .OrderByDescending(s => s.ScheduleId) + .ToListAsync() + .ConfigureAwait(false); + + return entries.ToObservableCollection(); + } + } + + public async ValueTask UpdateSpiralAbyssEntryAsync(SpiralAbyssEntry entry) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + await appDbContext.SpiralAbysses.UpdateAndSaveAsync(entry).ConfigureAwait(false); + } + } + + public async ValueTask AddSpiralAbyssEntryAsync(SpiralAbyssEntry entry) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + await appDbContext.SpiralAbysses.AddAndSaveAsync(entry).ConfigureAwait(false); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs index 1c6ba960..d5e264f1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs @@ -1,11 +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.Core.DependencyInjection.Abstraction; using Snap.Hutao.Model.Entity; -using Snap.Hutao.Model.Entity.Database; using Snap.Hutao.ViewModel.User; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; using Snap.Hutao.Web.Response; @@ -22,12 +19,14 @@ namespace Snap.Hutao.Service.SpiralAbyss; internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordService { private readonly IServiceProvider serviceProvider; + private readonly ITaskContext taskContext; + private readonly ISpiralAbyssRecordDbService spiralAbyssRecordDbService; private string? uid; private ObservableCollection? spiralAbysses; /// - public async Task> GetSpiralAbyssCollectionAsync(UserAndUid userAndUid) + public async ValueTask> GetSpiralAbyssCollectionAsync(UserAndUid userAndUid) { if (uid != userAndUid.Uid.Value) { @@ -35,33 +34,21 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi } uid = userAndUid.Uid.Value; - if (spiralAbysses == null) - { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - - List entries = await appDbContext.SpiralAbysses - .Where(s => s.Uid == userAndUid.Uid.Value) - .OrderByDescending(s => s.ScheduleId) - .ToListAsync() - .ConfigureAwait(false); - - spiralAbysses = entries.ToObservableCollection(); - } - } + spiralAbysses ??= await spiralAbyssRecordDbService + .GetSpiralAbyssEntryCollectionByUidAsync(userAndUid.Uid.Value) + .ConfigureAwait(false); return spiralAbysses; } /// - public async Task RefreshSpiralAbyssAsync(UserAndUid userAndUid) + public async ValueTask RefreshSpiralAbyssAsync(UserAndUid userAndUid) { await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Last).ConfigureAwait(false); await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Current).ConfigureAwait(false); } - private async Task RefreshSpiralAbyssCoreAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule) + private async ValueTask RefreshSpiralAbyssCoreAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule) { Response response = await serviceProvider .GetRequiredService>() @@ -73,31 +60,23 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi { Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss webSpiralAbyss = response.Data; - SpiralAbyssEntry? existEntry = spiralAbysses!.SingleOrDefault(s => s.ScheduleId == webSpiralAbyss.ScheduleId); - - using (IServiceScope scope = serviceProvider.CreateScope()) + if (spiralAbysses!.SingleOrDefault(s => s.ScheduleId == webSpiralAbyss.ScheduleId) is SpiralAbyssEntry existEntry) { - ITaskContext taskContext = scope.ServiceProvider.GetRequiredService(); - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + await taskContext.SwitchToMainThreadAsync(); + existEntry.UpdateSpiralAbyss(webSpiralAbyss); - if (existEntry != null) - { - await taskContext.SwitchToMainThreadAsync(); - existEntry.UpdateSpiralAbyss(webSpiralAbyss); + await taskContext.SwitchToBackgroundAsync(); + await spiralAbyssRecordDbService.UpdateSpiralAbyssEntryAsync(existEntry).ConfigureAwait(false); + } + else + { + SpiralAbyssEntry newEntry = SpiralAbyssEntry.From(userAndUid.Uid.Value, webSpiralAbyss); - await taskContext.SwitchToBackgroundAsync(); - await appDbContext.SpiralAbysses.UpdateAndSaveAsync(existEntry).ConfigureAwait(false); - } - else - { - SpiralAbyssEntry newEntry = SpiralAbyssEntry.From(userAndUid.Uid.Value, webSpiralAbyss); + await taskContext.SwitchToMainThreadAsync(); + spiralAbysses!.Insert(0, newEntry); - await taskContext.SwitchToMainThreadAsync(); - spiralAbysses!.Insert(0, newEntry); - - await taskContext.SwitchToBackgroundAsync(); - await appDbContext.SpiralAbysses.AddAndSaveAsync(newEntry).ConfigureAwait(false); - } + await taskContext.SwitchToBackgroundAsync(); + await spiralAbyssRecordDbService.AddSpiralAbyssEntryAsync(newEntry).ConfigureAwait(false); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs index cd419aca..7347ee37 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs @@ -33,7 +33,7 @@ internal interface IUserService /// 此操作不能取消 /// /// 准备完成的用户信息集合 - Task> GetUserCollectionAsync(); + ValueTask> GetUserCollectionAsync(); /// /// 获取角色信息 @@ -62,5 +62,5 @@ internal interface IUserService /// /// 待移除的用户 /// 任务 - Task RemoveUserAsync(BindingUser user); + ValueTask RemoveUserAsync(BindingUser user); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 4df98dc6..29479f63 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -25,84 +25,38 @@ namespace Snap.Hutao.Service.User; internal sealed partial class UserService : IUserService { private readonly ITaskContext taskContext; + private readonly IUserDbService userDbService; private readonly IServiceProvider serviceProvider; private readonly IMessenger messenger; + private readonly ScopedDbCurrent dbCurrent; - private BindingUser? currentUser; private ObservableCollection? userCollection; - private ObservableCollection? roleCollection; + private ObservableCollection? userAndUidCollection; /// public BindingUser? Current { - get => currentUser; - set - { - if (currentUser?.Entity.InnerId == value?.Entity.InnerId) - { - return; - } - - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - - // only update when not processing a deletion - if (value != null) - { - if (currentUser != null) - { - currentUser.IsSelected = false; - appDbContext.Users.UpdateAndSave(currentUser.Entity); - } - } - - UserChangedMessage message = new() { OldValue = currentUser, NewValue = value }; - - // 当删除到无用户时也能正常反应状态 - currentUser = value; - - if (currentUser != null) - { - currentUser.IsSelected = true; - try - { - appDbContext.Users.UpdateAndSave(currentUser.Entity); - } - catch (InvalidOperationException ex) - { - ThrowHelper.UserdataCorrupted(string.Format(SH.ServiceUserCurrentUpdateAndSaveFailed, currentUser.UserInfo?.Uid), ex); - } - } - - messenger.Send(message); - } - } + get => dbCurrent.Current; + set => dbCurrent.Current = value; } /// - public async Task RemoveUserAsync(BindingUser user) + public async ValueTask RemoveUserAsync(BindingUser user) { // Sync cache await taskContext.SwitchToMainThreadAsync(); userCollection!.Remove(user); - roleCollection?.RemoveWhere(r => r.User.Mid == user.Entity.Mid); + userAndUidCollection?.RemoveWhere(r => r.User.Mid == user.Entity.Mid); // Sync database await taskContext.SwitchToBackgroundAsync(); - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.Users - .ExecuteDeleteWhereAsync(u => u.InnerId == user.Entity.InnerId) - .ConfigureAwait(false); - } + await userDbService.DeleteUserByIdAsync(user.Entity.InnerId).ConfigureAwait(false); messenger.Send(new UserRemovedMessage(user.Entity)); } /// - public async Task> GetUserCollectionAsync() + public async ValueTask> GetUserCollectionAsync() { await taskContext.SwitchToBackgroundAsync(); if (userCollection == null) @@ -138,7 +92,7 @@ internal sealed partial class UserService : IUserService public async Task> GetRoleCollectionAsync() { await taskContext.SwitchToBackgroundAsync(); - if (roleCollection == null) + if (userAndUidCollection == null) { List userAndUids = new(); ObservableCollection observableUsers = await GetUserCollectionAsync().ConfigureAwait(false); @@ -150,10 +104,10 @@ internal sealed partial class UserService : IUserService } } - roleCollection = userAndUids.ToObservableCollection(); + userAndUidCollection = userAndUids.ToObservableCollection(); } - return roleCollection; + return userAndUidCollection; } /// @@ -269,11 +223,11 @@ internal sealed partial class UserService : IUserService { userCollection!.Add(newUser); - if (roleCollection != null) + if (userAndUidCollection != null) { foreach (UserGameRole role in newUser.UserGameRoles) { - roleCollection.Add(new(newUser.Entity, role)); + userAndUidCollection.Add(new(newUser.Entity, role)); } } } @@ -290,4 +244,25 @@ internal sealed partial class UserService : IUserService } } } +} + +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IUserDbService))] +internal sealed partial class UserDbService : IUserDbService +{ + private readonly IServiceProvider serviceProvider; + + public async ValueTask DeleteUserByIdAsync(Guid id) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + await appDbContext.Users.ExecuteDeleteWhereAsync(u => u.InnerId == id).ConfigureAwait(false); + } + } +} + +internal interface IUserDbService +{ + ValueTask DeleteUserByIdAsync(Guid id); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs index da02cf81..530c4946 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs @@ -135,7 +135,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav } /// - public async Task ReceiveAsync(INavigationData data) + public async ValueTask ReceiveAsync(INavigationData data) { if (await openUITaskCompletionSource.Task.ConfigureAwait(false)) { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/User.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/User.cs index 9cabdcd6..1e3f57ad 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/User.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/User.cs @@ -4,6 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; using Snap.Hutao.Core.DependencyInjection.Abstraction; +using Snap.Hutao.Model; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Bbs.User; using Snap.Hutao.Web.Hoyolab.Passport; @@ -17,7 +18,7 @@ namespace Snap.Hutao.ViewModel.User; /// 用于视图绑定的用户 /// [HighQuality] -internal sealed class User : ObservableObject +internal sealed class User : ObservableObject, IEntityOnly { private readonly EntityUser inner;