mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
refactor achievementservice
This commit is contained in:
@@ -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]
|
||||
#### 命名样式 ####
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2072,4 +2072,7 @@
|
||||
<data name="ModelInterchangeUIGFItemTypeUnknown" xml:space="preserve">
|
||||
<value>未知</value>
|
||||
</data>
|
||||
<data name="CoreExceptionServiceDatabaseCorruptedMessage" xml:space="preserve">
|
||||
<value>数据库已损坏:{0}</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ namespace Snap.Hutao.Service.Achievement;
|
||||
/// </summary>
|
||||
internal sealed partial class AchievementService
|
||||
{
|
||||
private ObservableCollection<AchievementArchive>? archiveCollection;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AchievementArchive? CurrentArchive
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user