diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/MemoryCacheExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/MemoryCacheExtension.cs
index 9ae998a5..77b7a755 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/MemoryCacheExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/MemoryCacheExtension.cs
@@ -10,13 +10,6 @@ namespace Snap.Hutao.Extension;
///
internal static class MemoryCacheExtension
{
- ///
- /// 尝试从 IMemoryCache 中移除并返回具有指定键的值
- ///
- /// 缓存
- /// 键
- /// 值
- /// 是否移除成功
public static bool TryRemove(this IMemoryCache memoryCache, string key, out object? value)
{
if (!memoryCache.TryGetValue(key, out value))
@@ -27,4 +20,16 @@ internal static class MemoryCacheExtension
memoryCache.Remove(key);
return true;
}
+
+ public static bool TryGetRequiredValue(this IMemoryCache memoryCache, string key, [NotNullWhen(true)] out T? value)
+ where T : class
+ {
+ if (!memoryCache.TryGetValue(key, out value))
+ {
+ return false;
+ }
+
+ ArgumentNullException.ThrowIfNull(value);
+ return true;
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
index 45a51287..df39f93b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
@@ -28,11 +28,9 @@ internal sealed partial class AnnouncementService : IAnnouncementService
public async ValueTask GetAnnouncementWrapperAsync(CancellationToken cancellationToken = default)
{
// 缓存中存在记录,直接返回
- if (memoryCache.TryGetValue(CacheKey, out object? cache))
+ if (memoryCache.TryGetRequiredValue(CacheKey, out AnnouncementWrapper? cache))
{
- AnnouncementWrapper? wrapper = (AnnouncementWrapper?)cache;
- ArgumentNullException.ThrowIfNull(wrapper);
- return wrapper;
+ return cache;
}
await taskContext.SwitchToBackgroundAsync();
@@ -40,30 +38,32 @@ internal sealed partial class AnnouncementService : IAnnouncementService
.GetAnnouncementsAsync(cancellationToken)
.ConfigureAwait(false);
- if (announcementWrapperResponse.IsOk())
+ if (!announcementWrapperResponse.IsOk())
{
- AnnouncementWrapper wrapper = announcementWrapperResponse.Data;
- Response> announcementContentResponse = await announcementClient
- .GetAnnouncementContentsAsync(cancellationToken)
- .ConfigureAwait(false);
-
- if (announcementContentResponse.IsOk())
- {
- List contents = announcementContentResponse.Data.List;
-
- Dictionary contentMap = contents
- .ToDictionary(id => id.AnnId, content => content.Content);
-
- // 将活动公告置于前方
- wrapper.List.Reverse();
-
- PreprocessAnnouncements(contentMap, wrapper.List);
-
- return memoryCache.Set(CacheKey, wrapper, TimeSpan.FromMinutes(30));
- }
+ return default!;
}
- return default!;
+ AnnouncementWrapper wrapper = announcementWrapperResponse.Data;
+ Response> announcementContentResponse = await announcementClient
+ .GetAnnouncementContentsAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ if (!announcementContentResponse.IsOk())
+ {
+ return default!;
+ }
+
+ List contents = announcementContentResponse.Data.List;
+
+ Dictionary contentMap = contents
+ .ToDictionary(id => id.AnnId, content => content.Content);
+
+ // 将活动公告置于前方
+ wrapper.List.Reverse();
+
+ PreprocessAnnouncements(contentMap, wrapper.List);
+
+ return memoryCache.Set(CacheKey, wrapper, TimeSpan.FromMinutes(30));
}
private static void PreprocessAnnouncements(Dictionary contentMap, List announcementListWrappers)
@@ -103,56 +103,60 @@ internal sealed partial class AnnouncementService : IAnnouncementService
.List
.Single(ann => AnnouncementRegex.VersionUpdateTitleRegex.IsMatch(ann.Title));
- if (AnnouncementRegex.VersionUpdateTimeRegex.Match(versionUpdate.Content) is { Success: true } match)
+ if (AnnouncementRegex.VersionUpdateTimeRegex.Match(versionUpdate.Content) is not { Success: true } match)
{
- DateTimeOffset versionUpdateTime = DateTimeOffset.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture);
- _ = 1;
- foreach (ref readonly Announcement announcement in CollectionsMarshal.AsSpan(activities))
+ return;
+ }
+
+ DateTimeOffset versionUpdateTime = DateTimeOffset.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture);
+
+ foreach (ref readonly Announcement announcement in CollectionsMarshal.AsSpan(activities))
+ {
+ if (AnnouncementRegex.PermanentActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } permanent)
{
- if (AnnouncementRegex.PermanentActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } permanent)
+ announcement.StartTime = versionUpdateTime;
+ continue;
+ }
+
+ if (AnnouncementRegex.PersistentActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } persistent)
+ {
+ announcement.StartTime = versionUpdateTime;
+ announcement.EndTime = versionUpdateTime + TimeSpan.FromDays(42);
+ continue;
+ }
+
+ if (AnnouncementRegex.TransientActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } transient)
+ {
+ announcement.StartTime = versionUpdateTime;
+ announcement.EndTime = DateTimeOffset.Parse(transient.Groups[2].ValueSpan, CultureInfo.InvariantCulture);
+ continue;
+ }
+
+ MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex.Matches(announcement.Content);
+ if (matches.Count < 2)
+ {
+ continue;
+ }
+
+ List dateTimes = matches.Select(match => DateTimeOffset.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture)).ToList();
+ DateTimeOffset min = DateTimeOffset.MaxValue;
+ DateTimeOffset max = DateTimeOffset.MinValue;
+
+ foreach (DateTimeOffset time in dateTimes)
+ {
+ if (time < min)
{
- announcement.StartTime = versionUpdateTime;
- continue;
+ min = time;
}
- if (AnnouncementRegex.PersistentActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } persistent)
+ if (time > max)
{
- announcement.StartTime = versionUpdateTime;
- announcement.EndTime = versionUpdateTime + TimeSpan.FromDays(42);
- continue;
- }
-
- if (AnnouncementRegex.TransientActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } transient)
- {
- announcement.StartTime = versionUpdateTime;
- announcement.EndTime = DateTimeOffset.Parse(transient.Groups[2].ValueSpan, CultureInfo.InvariantCulture);
- continue;
- }
-
- MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex.Matches(announcement.Content);
- if (matches.Count >= 2)
- {
- List dateTimes = matches.Select(match => DateTimeOffset.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture)).ToList();
- DateTimeOffset min = DateTimeOffset.MaxValue;
- DateTimeOffset max = DateTimeOffset.MinValue;
-
- foreach (DateTimeOffset time in dateTimes)
- {
- if (time < min)
- {
- min = time;
- }
-
- if (time > max)
- {
- max = time;
- }
- }
-
- announcement.StartTime = min;
- announcement.EndTime = max;
+ max = time;
}
}
+
+ announcement.StartTime = min;
+ announcement.EndTime = max;
}
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementFinishPercent.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementFinishPercent.cs
index 57bfc0af..7a4678cd 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementFinishPercent.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementFinishPercent.cs
@@ -21,41 +21,42 @@ internal static class AchievementFinishPercent
int totalFinished = 0;
int totalCount = 0;
- if (viewModel.Achievements is { } achievements)
+ if (viewModel.Achievements is not { } achievements)
{
- if (viewModel.AchievementGoals is { } achievementGoals)
+ return;
+ }
+
+ if (viewModel.AchievementGoals is not { } achievementGoals)
+ {
+ return;
+ }
+
+ if (achievements.SourceCollection is not List list)
+ {
+ // Fast path
+ throw Must.NeverHappen("AchievementViewModel.Achievements.SourceCollection 应为 List");
+ }
+
+ Dictionary counter = achievementGoals.ToDictionary(x => x.Id, AchievementGoalStatistics.From);
+
+ foreach (ref readonly AchievementView achievement in CollectionsMarshal.AsSpan(list))
+ {
+ ref AchievementGoalStatistics goalStat = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
+
+ goalStat.TotalCount += 1;
+ totalCount += 1;
+ if (achievement.IsChecked)
{
- Dictionary counter = achievementGoals.ToDictionary(x => x.Id, AchievementGoalStatistics.From);
-
- // Fast path
- if (achievements.SourceCollection is List list)
- {
- foreach (ref readonly AchievementView achievement in CollectionsMarshal.AsSpan(list))
- {
- // Make the state update as fast as possible
- ref AchievementGoalStatistics stat = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
-
- stat.TotalCount += 1;
- totalCount += 1;
- if (achievement.IsChecked)
- {
- stat.Finished += 1;
- totalFinished += 1;
- }
- }
- }
- else
- {
- Must.NeverHappen("AchievementViewModel.Achievements.SourceCollection 应为 List");
- }
-
- foreach (AchievementGoalStatistics statistics in counter.Values)
- {
- statistics.AchievementGoal.UpdateFinishDescriptionAndPercent(statistics);
- }
-
- viewModel.FinishDescription = AchievementStatistics.Format(totalFinished, totalCount, out _);
+ goalStat.Finished += 1;
+ totalFinished += 1;
}
}
+
+ foreach (AchievementGoalStatistics statistics in counter.Values)
+ {
+ statistics.AchievementGoal.UpdateFinishDescriptionAndPercent(statistics);
+ }
+
+ viewModel.FinishDescription = AchievementStatistics.Format(totalFinished, totalCount, out _);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementGoalView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementGoalView.cs
index bd5e5c48..c89d7b26 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementGoalView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementGoalView.cs
@@ -72,17 +72,7 @@ internal sealed class AchievementGoalView : ObservableObject, INameIcon, IMappin
/// 统计
public void UpdateFinishDescriptionAndPercent(AchievementGoalStatistics statistics)
{
- UpdateFinishDescriptionAndPercent(statistics.Finished, statistics.TotalCount);
- }
-
- ///
- /// 更新进度
- ///
- /// 完成项
- /// 总项
- private void UpdateFinishDescriptionAndPercent(int finished, int count)
- {
- FinishDescription = AchievementStatistics.Format(finished, count, out double finishPercent);
+ FinishDescription = AchievementStatistics.Format(statistics.Finished, statistics.TotalCount, out double finishPercent);
FinishPercent = finishPercent;
}
}
\ No newline at end of file