refactor achievementservice

This commit is contained in:
Lightczx
2023-07-20 08:08:29 +08:00
parent 01fdcda729
commit 2ad87d25df
13 changed files with 197 additions and 67 deletions

View File

@@ -169,6 +169,8 @@ csharp_style_prefer_utf8_string_literals = true:suggestion
# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none
dotnet_diagnostic.SA1601.severity = silent
dotnet_diagnostic.SA1602.severity = silent
[*.vb]
#### 命名样式 ####

View File

@@ -0,0 +1,21 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.ExceptionService;
/// <summary>
/// 数据库损坏异常
/// </summary>
[HighQuality]
internal sealed class DatabaseCorruptedException : Exception
{
/// <summary>
/// 构造一个新的用户数据损坏异常
/// </summary>
/// <param name="message">消息</param>
/// <param name="innerException">内部错误</param>
public DatabaseCorruptedException(string message, Exception? innerException)
: base(string.Format(SH.CoreExceptionServiceDatabaseCorruptedMessage, $"{message}\n{innerException?.Message}"), innerException)
{
}
}

View File

@@ -82,6 +82,13 @@ internal static class ThrowHelper
throw new UserdataCorruptedException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static DatabaseCorruptedException DatabaseCorrupted(string message, Exception inner)
{
throw new DatabaseCorruptedException(message, inner);
}
/// <summary>
/// 运行环境异常
/// </summary>

View File

@@ -168,6 +168,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 数据库已损坏:{0} 的本地化字符串。
/// </summary>
internal static string CoreExceptionServiceDatabaseCorruptedMessage {
get {
return ResourceManager.GetString("CoreExceptionServiceDatabaseCorruptedMessage", resourceCulture);
}
}
/// <summary>
/// 查找类似 用户数据已损坏:{0} 的本地化字符串。
/// </summary>

View File

@@ -2072,4 +2072,7 @@
<data name="ModelInterchangeUIGFItemTypeUnknown" xml:space="preserve">
<value>未知</value>
</data>
<data name="CoreExceptionServiceDatabaseCorruptedMessage" xml:space="preserve">
<value>数据库已损坏:{0}</value>
</data>
</root>

View File

@@ -0,0 +1,69 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Primitive;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
namespace Snap.Hutao.Service.Achievement;
/// <summary>
/// 成就数据库服务
/// </summary>
[ConstructorGenerated]
[Injection(InjectAs.Scoped, typeof(IAchievementDbService))]
internal sealed partial class AchievementDbService : IAchievementDbService
{
private readonly IServiceProvider serviceProvider;
public Dictionary<AchievementId, EntityAchievement> GetAchievementMapByArchiveId(Guid archiveId)
{
Dictionary<AchievementId, EntityAchievement> entities;
try
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
entities = appDbContext.Achievements
.Where(a => a.ArchiveId == archiveId)
.ToDictionary(a => (AchievementId)a.Id);
}
}
catch (ArgumentException ex)
{
throw ThrowHelper.DatabaseCorrupted(SH.ServiceAchievementUserdataCorruptedInnerIdNotUnique, ex);
}
return entities;
}
public async Task<int> GetFinishedAchievementCountByArchiveIdAsync(Guid archiveId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.Achievements
.Where(a => a.ArchiveId == archiveId)
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
.CountAsync()
.ConfigureAwait(false);
}
}
public async Task<List<EntityAchievement>> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.Achievements
.Where(a => a.ArchiveId == archiveId)
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
.OrderByDescending(a => a.Time.ToString())
.Take(take)
.ToListAsync()
.ConfigureAwait(false);
}
}
}

View File

@@ -13,6 +13,8 @@ namespace Snap.Hutao.Service.Achievement;
/// </summary>
internal sealed partial class AchievementService
{
private ObservableCollection<AchievementArchive>? archiveCollection;
/// <inheritdoc/>
public AchievementArchive? CurrentArchive
{

View File

@@ -1,15 +1,12 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.Achievement;
using System.Collections.ObjectModel;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
@@ -25,76 +22,23 @@ internal sealed partial class AchievementService : IAchievementService
{
private readonly ScopedDbCurrent<AchievementArchive, Message.AchievementArchiveChangedMessage> dbCurrent;
private readonly AchievementDbBulkOperation achievementDbBulkOperation;
private readonly IAchievementDbService achievementDbService;
private readonly ILogger<AchievementService> logger;
private readonly IServiceProvider serviceProvider;
private readonly ITaskContext taskContext;
private ObservableCollection<AchievementArchive>? archiveCollection;
/// <inheritdoc/>
public List<AchievementView> GetAchievements(AchievementArchive archive, List<MetadataAchievement> metadata)
{
Dictionary<AchievementId, EntityAchievement> entityMap;
try
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
entityMap = appDbContext.Achievements
.Where(a => a.ArchiveId == archive.InnerId)
.ToDictionary(a => (AchievementId)a.Id);
}
}
catch (ArgumentException ex)
{
throw ThrowHelper.UserdataCorrupted(SH.ServiceAchievementUserdataCorruptedInnerIdNotUnique, ex);
}
Dictionary<AchievementId, EntityAchievement> entities = achievementDbService.GetAchievementMapByArchiveId(archive.InnerId);
return metadata.SelectList(meta =>
{
EntityAchievement entity = entityMap.GetValueOrDefault(meta.Id) ?? EntityAchievement.From(archive.InnerId, meta.Id);
EntityAchievement entity = entities.GetValueOrDefault(meta.Id) ?? EntityAchievement.From(archive.InnerId, meta.Id);
return new AchievementView(entity, meta);
});
}
/// <inheritdoc/>
public async Task<List<AchievementStatistics>> GetAchievementStatisticsAsync(Dictionary<AchievementId, MetadataAchievement> achievementMap)
{
await taskContext.SwitchToBackgroundAsync();
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
List<AchievementStatistics> results = new();
foreach (AchievementArchive archive in appDbContext.AchievementArchives)
{
int finishedCount = await appDbContext.Achievements
.Where(a => a.ArchiveId == archive.InnerId)
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
.CountAsync()
.ConfigureAwait(false);
int totalCount = achievementMap.Count;
List<EntityAchievement> achievements = await appDbContext.Achievements
.Where(a => a.ArchiveId == archive.InnerId)
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
.OrderByDescending(a => a.Time.ToString())
.Take(2)
.ToListAsync()
.ConfigureAwait(false);
results.Add(new()
{
DisplayName = archive.Name,
FinishDescription = AchievementStatistics.Format(finishedCount, totalCount, out _),
Achievements = achievements.SelectList(entity => new AchievementView(entity, achievementMap[entity.Id])),
});
}
return results;
}
}
/// <inheritdoc/>
public void SaveAchievements(AchievementArchive archive, List<AchievementView> achievements)
{

View File

@@ -0,0 +1,53 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.Achievement;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
namespace Snap.Hutao.Service.Achievement;
[ConstructorGenerated]
[Injection(InjectAs.Scoped, typeof(IAchievementStatisticsService))]
internal sealed partial class AchievementStatisticsService : IAchievementStatisticsService
{
private readonly IAchievementDbService achievementDbService;
private readonly IServiceProvider serviceProvider;
private readonly ITaskContext taskContext;
/// <inheritdoc/>
public async Task<List<AchievementStatistics>> GetAchievementStatisticsAsync(Dictionary<AchievementId, MetadataAchievement> achievementMap)
{
await taskContext.SwitchToBackgroundAsync();
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
List<AchievementStatistics> results = new();
foreach (AchievementArchive archive in appDbContext.AchievementArchives)
{
int finishedCount = await achievementDbService
.GetFinishedAchievementCountByArchiveIdAsync(archive.InnerId)
.ConfigureAwait(false);
int totalCount = achievementMap.Count;
List<EntityAchievement> achievements = await achievementDbService
.GetLatestFinishedAchievementListByArchiveIdAsync(archive.InnerId, 2)
.ConfigureAwait(false);
results.Add(new()
{
DisplayName = archive.Name,
FinishDescription = AchievementStatistics.Format(finishedCount, totalCount, out _),
Achievements = achievements.SelectList(entity => new AchievementView(entity, achievementMap[entity.Id])),
});
}
return results;
}
}
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
namespace Snap.Hutao.Service.Achievement;
internal interface IAchievementDbService
{
Dictionary<AchievementId, EntityAchievement> GetAchievementMapByArchiveId(Guid archiveId);
Task<int> GetFinishedAchievementCountByArchiveIdAsync(Guid archiveId);
Task<List<EntityAchievement>> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take);
}

View File

@@ -41,13 +41,6 @@ internal interface IAchievementService
/// <returns>整合的成就</returns>
List<BindingAchievement> GetAchievements(EntityArchive archive, List<MetadataAchievement> metadata);
/// <summary>
/// 异步获取成就统计列表
/// </summary>
/// <param name="achievementMap">成就映射</param>
/// <returns>成就统计列表</returns>
Task<List<ViewModel.Achievement.AchievementStatistics>> GetAchievementStatisticsAsync(Dictionary<AchievementId, MetadataAchievement> achievementMap);
/// <summary>
/// 异步导入UIAF数据
/// </summary>

View File

@@ -0,0 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.Achievement;
using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
namespace Snap.Hutao.Service.Achievement;
internal interface IAchievementStatisticsService
{
Task<List<AchievementStatistics>> GetAchievementStatisticsAsync(Dictionary<AchievementId, MetadataAchievement> achievementMap);
}

View File

@@ -43,7 +43,7 @@ internal sealed class AchievementViewModelSlim : Abstraction.ViewModelSlim<View.
.GetIdToAchievementMapAsync()
.ConfigureAwait(false);
List<AchievementStatistics> list = await scope.ServiceProvider
.GetRequiredService<IAchievementService>()
.GetRequiredService<IAchievementStatisticsService>()
.GetAchievementStatisticsAsync(achievementMap)
.ConfigureAwait(false);