mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
minor code style
This commit is contained in:
@@ -10,13 +10,6 @@ namespace Snap.Hutao.Extension;
|
||||
/// </summary>
|
||||
internal static class MemoryCacheExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 尝试从 IMemoryCache 中移除并返回具有指定键的值
|
||||
/// </summary>
|
||||
/// <param name="memoryCache">缓存</param>
|
||||
/// <param name="key">键</param>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>是否移除成功</returns>
|
||||
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<T>(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;
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,9 @@ internal sealed partial class AnnouncementService : IAnnouncementService
|
||||
public async ValueTask<AnnouncementWrapper> 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<ListWrapper<AnnouncementContent>> announcementContentResponse = await announcementClient
|
||||
.GetAnnouncementContentsAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (announcementContentResponse.IsOk())
|
||||
{
|
||||
List<AnnouncementContent> contents = announcementContentResponse.Data.List;
|
||||
|
||||
Dictionary<int, string> 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<ListWrapper<AnnouncementContent>> announcementContentResponse = await announcementClient
|
||||
.GetAnnouncementContentsAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!announcementContentResponse.IsOk())
|
||||
{
|
||||
return default!;
|
||||
}
|
||||
|
||||
List<AnnouncementContent> contents = announcementContentResponse.Data.List;
|
||||
|
||||
Dictionary<int, string> 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<int, string> contentMap, List<AnnouncementListWrapper> 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<DateTimeOffset> 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<DateTimeOffset> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<AchievementView> list)
|
||||
{
|
||||
// Fast path
|
||||
throw Must.NeverHappen("AchievementViewModel.Achievements.SourceCollection 应为 List<AchievementView>");
|
||||
}
|
||||
|
||||
Dictionary<AchievementGoalId, AchievementGoalStatistics> 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<AchievementGoalId, AchievementGoalStatistics> counter = achievementGoals.ToDictionary(x => x.Id, AchievementGoalStatistics.From);
|
||||
|
||||
// Fast path
|
||||
if (achievements.SourceCollection is List<AchievementView> 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<AchievementView>");
|
||||
}
|
||||
|
||||
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 _);
|
||||
}
|
||||
}
|
||||
@@ -72,17 +72,7 @@ internal sealed class AchievementGoalView : ObservableObject, INameIcon, IMappin
|
||||
/// <param name="statistics">统计</param>
|
||||
public void UpdateFinishDescriptionAndPercent(AchievementGoalStatistics statistics)
|
||||
{
|
||||
UpdateFinishDescriptionAndPercent(statistics.Finished, statistics.TotalCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新进度
|
||||
/// </summary>
|
||||
/// <param name="finished">完成项</param>
|
||||
/// <param name="count">总项</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user