factory completed

This commit is contained in:
Lightczx
2024-03-03 17:25:51 +08:00
parent 6e10819609
commit 8a2fa3c701
37 changed files with 198 additions and 114 deletions

View File

@@ -1,6 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Numerics;
namespace Snap.Hutao.Core.ExceptionService;
internal sealed class HutaoException : Exception
@@ -37,4 +39,10 @@ internal sealed class HutaoException : Exception
string message = $"This instance of '{typeof(TFrom).FullName}' '{name}' doesn't implement '{typeof(TTo).FullName}'";
throw new HutaoException(HutaoExceptionKind.ServiceTypeCastFailed, message, innerException);
}
public static HutaoException GachaStatisticsInvalidItemId(uint id, Exception? innerException = default)
{
string message = SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(id);
throw new HutaoException(HutaoExceptionKind.GachaStatisticsInvalidItemId, message, innerException);
}
}

View File

@@ -9,4 +9,5 @@ internal enum HutaoExceptionKind
ServiceTypeCastFailed,
FileSystemCreateFileInsufficientPermissions,
PrivateNamedPipeContentHashIncorrect,
GachaStatisticsInvalidItemId,
}

View File

@@ -42,14 +42,14 @@ internal sealed partial class GachaItem
/// <summary>
/// 祈愿记录分类
/// </summary>
public GachaConfigType GachaType { get; set; }
public GachaType GachaType { get; set; }
/// <summary>
/// 祈愿记录查询分类
/// 合并保底的卡池使用此属性
/// 仅4种不含400
/// </summary>
public GachaConfigType QueryType { get; set; }
public GachaType QueryType { get; set; }
/// <summary>
/// 物品Id

View File

@@ -20,7 +20,7 @@ internal sealed class UIGFItem : GachaLogItem, IMappingFrom<UIGFItem, GachaItem,
/// </summary>
[JsonPropertyName("uigf_gacha_type")]
[JsonEnum(JsonSerializeType.NumberString)]
public GachaConfigType UIGFGachaType { get; set; } = default!;
public GachaType UIGFGachaType { get; set; } = default!;
public static UIGFItem From(GachaItem item, INameQuality nameQuality)
{

View File

@@ -49,7 +49,7 @@ internal sealed class GachaEvent
/// <summary>
/// 卡池类型
/// </summary>
public GachaConfigType Type { get; set; }
public GachaType Type { get; set; }
/// <summary>
/// 五星列表

View File

@@ -860,6 +860,9 @@
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
<value>角色活动</value>
</data>
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
<value>奔行世间</value>
</data>
@@ -3047,6 +3050,9 @@
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
<value>角色活动祈愿-2</value>
</data>
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
<value>集录祈愿</value>
</data>
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
<value>新手祈愿</value>
</data>

View File

@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
/// <summary>
/// 祈愿配置类型比较器
/// </summary>
internal sealed class GachaConfigTypeComparer : IComparer<GachaConfigType>
internal sealed class GachaConfigTypeComparer : IComparer<GachaType>
{
private static readonly Lazy<GachaConfigTypeComparer> LazyShared = new(() => new());
private static readonly FrozenDictionary<GachaConfigType, int> OrderMap = FrozenDictionary.ToFrozenDictionary(
@@ -28,13 +28,13 @@ internal sealed class GachaConfigTypeComparer : IComparer<GachaConfigType>
public static GachaConfigTypeComparer Shared { get => LazyShared.Value; }
/// <inheritdoc/>
public int Compare(GachaConfigType x, GachaConfigType y)
public int Compare(GachaType x, GachaType y)
{
return OrderOf(x) - OrderOf(y);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int OrderOf(GachaConfigType type)
private static int OrderOf(GachaType type)
{
return OrderMap.GetValueOrDefault(type, 0);
}

View File

@@ -62,4 +62,4 @@ internal static class GachaStatisticsExtension
ReadOnlySpan<byte> codes = MD5.HashData(Encoding.UTF8.GetBytes(name));
return Color.FromArgb(255, codes.Slice(0, 5).Average(), codes.Slice(5, 5).Average(), codes.Slice(10, 5).Average());
}
}
}

View File

@@ -54,6 +54,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
TypedWishSummaryBuilderContext weaponContext = TypedWishSummaryBuilderContext.WeaponEventWish(taskContext, gachaLogClient);
TypedWishSummaryBuilder weaponWishBuilder = new(weaponContext);
TypedWishSummaryBuilderContext chronicledContext = TypedWishSummaryBuilderContext.ChronicledWish(taskContext, gachaLogClient);
TypedWishSummaryBuilder chronicledWishBuilder = new(chronicledContext);
Dictionary<Avatar, int> orangeAvatarCounter = [];
Dictionary<Avatar, int> purpleAvatarCounter = [];
Dictionary<Weapon, int> orangeWeaponCounter = [];
@@ -61,7 +64,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
Dictionary<Weapon, int> blueWeaponCounter = [];
// Pre group builders
Dictionary<GachaConfigType, List<HistoryWishBuilder>> historyWishBuilderMap = historyWishBuilders
Dictionary<GachaType, List<HistoryWishBuilder>> historyWishBuilderMap = historyWishBuilders
.GroupBy(b => b.ConfigType)
.ToDictionary(g => g.Key, g => g.ToList().SortBy(b => b.From));
@@ -70,7 +73,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
foreach (Model.Entity.GachaItem item in CollectionsMarshal.AsSpan(items))
{
// Find target history wish to operate. // w.From <= item.Time <= w.To
HistoryWishBuilder? targetHistoryWishBuilder = item.GachaType is not (GachaConfigType.StandardWish or GachaConfigType.NoviceWish)
HistoryWishBuilder? targetHistoryWishBuilder = item.GachaType is not (GachaType.Standard or GachaType.NewBie)
? historyWishBuilderMap[item.GachaType].BinarySearch(w => item.Time < w.From ? -1 : item.Time > w.To ? 1 : 0)
: default;
@@ -98,6 +101,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
standardWishBuilder.Track(item, avatar, isUp);
avatarWishBuilder.Track(item, avatar, isUp);
weaponWishBuilder.Track(item, avatar, isUp);
chronicledWishBuilder.Track(item, avatar, isUp);
break;
}
@@ -127,12 +131,13 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
standardWishBuilder.Track(item, weapon, isUp);
avatarWishBuilder.Track(item, weapon, isUp);
weaponWishBuilder.Track(item, weapon, isUp);
chronicledWishBuilder.Track(item, weapon, isUp);
break;
}
default:
// ItemId string length not correct.
ThrowHelper.UserdataCorrupted(SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(item.ItemId), default!);
HutaoException.GachaStatisticsInvalidItemId(item.ItemId);
break;
}
}
@@ -162,6 +167,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
StandardWish = standardWishBuilder.ToTypedWishSummary(barrier),
AvatarWish = avatarWishBuilder.ToTypedWishSummary(barrier),
WeaponWish = weaponWishBuilder.ToTypedWishSummary(barrier),
ChronicledWish = chronicledWishBuilder.ToTypedWishSummary(barrier),
};
}
}

View File

@@ -62,22 +62,29 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
int weaponPurpleTracker = 0;
TypedWishSummarySlim weaponWish = new(SH.ServiceGachaLogFactoryWeaponWishName, 80, 10);
int chronicledOrangeTracker = 0;
int chronicledPurpleTracker = 0;
TypedWishSummarySlim chronicledWish = new(SH.ServiceGachaLogFactoryChronicledWishName, 90, 10);
// O(n) operation
foreach (ref readonly GachaItem item in CollectionsMarshal.AsSpan(items))
{
INameQuality nameQuality = context.GetNameQualityByItemId(item.ItemId);
switch (item.QueryType)
{
case GachaConfigType.StandardWish:
case GachaType.Standard:
Track(nameQuality, ref standardOrangeTracker, ref standardPurpleTracker);
break;
case GachaConfigType.AvatarEventWish:
case GachaConfigType.AvatarEventWish2:
case GachaType.ActivityAvatar:
case GachaType.SpecialActivityAvatar:
Track(nameQuality, ref avatarOrangeTracker, ref avatarPurpleTracker);
break;
case GachaConfigType.WeaponEventWish:
case GachaType.ActivityWeapon:
Track(nameQuality, ref weaponOrangeTracker, ref weaponPurpleTracker);
break;
case GachaType.ActivityCity:
Track(nameQuality, ref chronicledOrangeTracker, ref chronicledPurpleTracker);
break;
default:
break;
}
@@ -85,11 +92,16 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
standardWish.LastOrangePull = standardOrangeTracker;
standardWish.LastPurplePull = standardPurpleTracker;
avatarWish.LastOrangePull = avatarOrangeTracker;
avatarWish.LastPurplePull = avatarPurpleTracker;
weaponWish.LastOrangePull = weaponOrangeTracker;
weaponWish.LastPurplePull = weaponPurpleTracker;
chronicledWish.LastOrangePull = chronicledOrangeTracker;
chronicledWish.LastPurplePull = chronicledPurpleTracker;
return new()
{
Uid = uid,

View File

@@ -37,21 +37,27 @@ internal sealed class HistoryWishBuilder
switch (ConfigType)
{
case GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2:
case GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar:
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0);
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0);
break;
case GachaConfigType.WeaponEventWish:
case GachaType.ActivityWeapon:
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0);
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0);
break;
case GachaType.ActivityCity:
// Avatars are less than weapons, so we try to get the value from avatar map first
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0);
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0);
break;
}
}
/// <summary>
/// 祈愿配置类型
/// </summary>
public GachaConfigType ConfigType { get; }
public GachaType ConfigType { get; }
/// <inheritdoc cref="GachaEvent.From"/>
public DateTimeOffset From { get => gachaEvent.From; }

View File

@@ -18,18 +18,20 @@ internal sealed class HutaoStatisticsFactory
private readonly GachaEvent avatarEvent;
private readonly GachaEvent avatarEvent2;
private readonly GachaEvent weaponEvent;
private readonly GachaEvent chronicledEvent;
public HutaoStatisticsFactory(in HutaoStatisticsFactoryMetadataContext context)
{
this.context = context;
// TODO: when in new verion
// when in new verion
// due to lack of newer metadata
// this can crash
DateTimeOffset now = DateTimeOffset.UtcNow;
avatarEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.AvatarEventWish);
avatarEvent2 = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.AvatarEventWish2);
weaponEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.WeaponEventWish);
avatarEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityAvatar);
avatarEvent2 = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.SpecialActivityAvatar);
weaponEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityWeapon);
chronicledEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityCity);
}
public HutaoStatistics Create(GachaEventStatistics raw)
@@ -38,7 +40,8 @@ internal sealed class HutaoStatisticsFactory
{
AvatarEvent = CreateWishSummary(avatarEvent, raw.AvatarEvent),
AvatarEvent2 = CreateWishSummary(avatarEvent2, raw.AvatarEvent2),
WeaponWish = CreateWishSummary(weaponEvent, raw.WeaponEvent),
WeaponEvent = CreateWishSummary(weaponEvent, raw.WeaponEvent),
ChronicledWish = CreateWishSummary(chronicledEvent, raw.Chronicled),
};
}
@@ -55,7 +58,7 @@ internal sealed class HutaoStatisticsFactory
{
8U => context.IdAvatarMap[item.Item],
5U => context.IdWeaponMap[item.Item],
_ => throw ThrowHelper.UserdataCorrupted(SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(item.Item), default!),
_ => throw HutaoException.GachaStatisticsInvalidItemId(item.Item),
};
StatisticsItem statisticsItem = source.ToStatisticsItem(unchecked((int)item.Count));

View File

@@ -5,19 +5,18 @@ using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
namespace Snap.Hutao.Service.GachaLog.Factory;
internal readonly struct HutaoStatisticsFactoryMetadataContext
internal sealed class HutaoStatisticsFactoryMetadataContext : IMetadataContext,
IMetadataDictionaryIdAvatarSource,
IMetadataDictionaryIdWeaponSource,
IMetadataListGachaEventSource
{
public readonly Dictionary<AvatarId, Avatar> IdAvatarMap;
public readonly Dictionary<WeaponId, Weapon> IdWeaponMap;
public readonly List<GachaEvent> GachaEvents;
public Dictionary<AvatarId, Avatar> IdAvatarMap { get; set; } = default!;
public HutaoStatisticsFactoryMetadataContext(Dictionary<AvatarId, Avatar> idAvatarMap, Dictionary<WeaponId, Weapon> idWeaponMap, List<GachaEvent> gachaEvents)
{
IdAvatarMap = idAvatarMap;
IdWeaponMap = idWeaponMap;
GachaEvents = gachaEvents;
}
public Dictionary<WeaponId, Weapon> IdWeaponMap { get; set; } = default!;
public List<GachaEvent> GachaEvents { get; set; } = default!;
}

View File

@@ -18,17 +18,17 @@ internal sealed class TypedWishSummaryBuilder
/// <summary>
/// 常驻祈愿
/// </summary>
public static readonly Func<GachaConfigType, bool> IsStandardWish = type => type is GachaConfigType.StandardWish;
public static readonly Func<GachaType, bool> IsStandardWish = type => type is GachaType.Standard;
/// <summary>
/// 角色活动
/// </summary>
public static readonly Func<GachaConfigType, bool> IsAvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
public static readonly Func<GachaType, bool> IsAvatarEventWish = type => type is GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar;
/// <summary>
/// 武器活动
/// </summary>
public static readonly Func<GachaConfigType, bool> IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
public static readonly Func<GachaType, bool> IsWeaponEventWish = type => type is GachaType.ActivityWeapon;
private readonly TypedWishSummaryBuilderContext context;

View File

@@ -14,12 +14,13 @@ internal readonly struct TypedWishSummaryBuilderContext
public readonly string Name;
public readonly int GuaranteeOrangeThreshold;
public readonly int GuaranteePurpleThreshold;
public readonly Func<GachaConfigType, bool> TypeEvaluator;
public readonly Func<GachaType, bool> TypeEvaluator;
public readonly GachaDistributionType DistributionType;
private static readonly Func<GachaConfigType, bool> IsStandardWish = type => type is GachaConfigType.StandardWish;
private static readonly Func<GachaConfigType, bool> IsAvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
private static readonly Func<GachaConfigType, bool> IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
private static readonly Func<GachaType, bool> IsStandardWish = type => type is GachaType.Standard;
private static readonly Func<GachaType, bool> IsAvatarEventWish = type => type is GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar;
private static readonly Func<GachaType, bool> IsWeaponEventWish = type => type is GachaType.ActivityWeapon;
private static readonly Func<GachaType, bool> IsChronicledWish = type => type is GachaType.ActivityCity;
public TypedWishSummaryBuilderContext(
ITaskContext taskContext,
@@ -27,7 +28,7 @@ internal readonly struct TypedWishSummaryBuilderContext
string name,
int guaranteeOrangeThreshold,
int guaranteePurpleThreshold,
Func<GachaConfigType, bool> typeEvaluator,
Func<GachaType, bool> typeEvaluator,
GachaDistributionType distributionType)
{
TaskContext = taskContext;
@@ -54,6 +55,11 @@ internal readonly struct TypedWishSummaryBuilderContext
return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryWeaponWishName, 80, 10, IsWeaponEventWish, GachaDistributionType.WeaponEvent);
}
public static TypedWishSummaryBuilderContext ChronicledWish(ITaskContext taskContext, HomaGachaLogClient gachaLogClient)
{
return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryChronicledWishName, 90, 10, IsChronicledWish, GachaDistributionType.Chronicled);
}
public ValueTask<HutaoResponse<GachaDistribution>> GetGachaDistributionAsync()
{
return GachaLogClient.GetGachaDistributionAsync(DistributionType);

View File

@@ -21,7 +21,7 @@ internal readonly struct GachaItemSaveContext
/// </summary>
public readonly bool IsLazy;
public readonly GachaConfigType QueryType;
public readonly GachaType QueryType;
/// <summary>
/// 结尾 Id
@@ -33,7 +33,7 @@ internal readonly struct GachaItemSaveContext
/// </summary>
public readonly IGachaLogDbService GachaLogDbService;
public GachaItemSaveContext(List<GachaItem> itemsToAdd, bool isLazy, GachaConfigType queryType, long endId, IGachaLogDbService gachaLogDbService)
public GachaItemSaveContext(List<GachaItem> itemsToAdd, bool isLazy, GachaType queryType, long endId, IGachaLogDbService gachaLogDbService)
{
ItemsToAdd = itemsToAdd;
IsLazy = isLazy;

View File

@@ -14,11 +14,11 @@ internal static class GachaLog
/// <summary>
/// 查询类型
/// </summary>
public static readonly FrozenSet<GachaConfigType> QueryTypes = FrozenSet.ToFrozenSet(
public static readonly FrozenSet<GachaType> QueryTypes = FrozenSet.ToFrozenSet(
[
GachaConfigType.NoviceWish,
GachaConfigType.StandardWish,
GachaConfigType.AvatarEventWish,
GachaConfigType.WeaponEventWish,
GachaType.NewBie,
GachaType.Standard,
GachaType.ActivityAvatar,
GachaType.ActivityWeapon,
]);
}

View File

@@ -73,7 +73,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token)
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType, CancellationToken token)
{
GachaItem? item = null;
@@ -103,7 +103,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
return item?.Id ?? 0L;
}
public long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType)
public long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaType queryType)
{
GachaItem? item = null;
@@ -132,7 +132,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
return item?.Id ?? 0L;
}
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType)
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType)
{
GachaItem? item = null;
@@ -205,7 +205,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
return item?.Id ?? long.MaxValue;
}
public long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType)
public long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaType queryType)
{
GachaItem? item = null;
@@ -226,7 +226,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
return item?.Id ?? long.MaxValue;
}
public async ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token)
public async ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType, CancellationToken token)
{
GachaItem? item = null;
@@ -266,7 +266,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaConfigType queryType, long endId)
public List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaType queryType, long endId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -291,7 +291,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public async ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaConfigType queryType, long endId)
public async ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaType queryType, long endId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -368,7 +368,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId)
public void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaType queryType, long endId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -381,7 +381,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public async ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaConfigType queryType, long endId)
public async ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaType queryType, long endId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{

View File

@@ -46,7 +46,7 @@ internal struct GachaLogFetchContext
/// <summary>
/// 当前类型
/// </summary>
public GachaConfigType CurrentType;
public GachaType CurrentType;
private readonly GachaLogServiceMetadataContext serviceContext;
private readonly IGachaLogDbService gachaLogDbService;
@@ -66,7 +66,7 @@ internal struct GachaLogFetchContext
/// </summary>
/// <param name="configType">卡池类型</param>
/// <param name="query">查询</param>
public void ResetForProcessingType(GachaConfigType configType, in GachaLogQuery query)
public void ResetForProcessingType(GachaType configType, in GachaLogQuery query)
{
DbEndId = null;
CurrentType = configType;

View File

@@ -15,7 +15,7 @@ internal sealed class GachaLogFetchStatus
/// 构造一个新的祈愿记录获取状态
/// </summary>
/// <param name="configType">卡池类型</param>
public GachaLogFetchStatus(GachaConfigType configType)
public GachaLogFetchStatus(GachaType configType)
{
ConfigType = configType;
}
@@ -28,7 +28,7 @@ internal sealed class GachaLogFetchStatus
/// <summary>
/// 卡池类型
/// </summary>
public GachaConfigType ConfigType { get; set; }
public GachaType ConfigType { get; set; }
/// <summary>
/// 当前获取的物品

View File

@@ -8,6 +8,7 @@ using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.GachaLog.Factory;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hutao.GachaLog;
@@ -40,7 +41,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
if (await GetEndIdsFromCloudAsync(uid, token).ConfigureAwait(false) is { } endIds)
{
List<Web.Hutao.GachaLog.GachaItem> items = [];
foreach ((GachaConfigType type, long endId) in endIds)
foreach ((GachaType type, long endId) in endIds)
{
List<Web.Hutao.GachaLog.GachaItem> part = await gachaLogDbService
.GetHutaoGachaItemListAsync(gachaArchive.InnerId, type, endId)
@@ -94,10 +95,9 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
Dictionary<AvatarId, Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
Dictionary<WeaponId, Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
List<GachaEvent> gachaEvents = await metadataService.GetGachaEventListAsync(token).ConfigureAwait(false);
HutaoStatisticsFactoryMetadataContext context = new(idAvatarMap, idWeaponMap, gachaEvents);
HutaoStatisticsFactoryMetadataContext context = await metadataService
.GetContextAsync<HutaoStatisticsFactoryMetadataContext>(token)
.ConfigureAwait(false);
GachaEventStatistics raw = response.Data;
try
@@ -126,7 +126,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
private async ValueTask<EndIds> CreateEndIdsAsync(GachaArchive? archive, CancellationToken token)
{
EndIds endIds = new();
foreach (GachaConfigType type in GachaLog.QueryTypes)
foreach (GachaType type in GachaLog.QueryTypes)
{
if (archive is not null)
{

View File

@@ -182,7 +182,7 @@ internal sealed partial class GachaLogService : IGachaLogService
ArgumentNullException.ThrowIfNull(ArchiveCollection);
GachaLogFetchContext fetchContext = new(gachaLogDbService, taskContext, context, isLazy);
foreach (GachaConfigType configType in GachaLog.QueryTypes)
foreach (GachaType configType in GachaLog.QueryTypes)
{
fetchContext.ResetForProcessingType(configType, query);

View File

@@ -19,7 +19,7 @@ internal interface IGachaLogDbService
ValueTask RemoveGachaArchiveByIdAsync(Guid archiveId);
void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId);
void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaType queryType, long endId);
ValueTask<GachaArchive?> GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token);
@@ -31,25 +31,25 @@ internal interface IGachaLogDbService
ValueTask<List<GachaItem>> GetGachaItemListByArchiveIdAsync(Guid archiveId);
List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaConfigType queryType, long endId);
List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaType queryType, long endId);
long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaType queryType);
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType, CancellationToken token);
long GetOldestGachaItemIdByArchiveId(Guid archiveId);
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaType queryType);
ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType, CancellationToken token);
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType);
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType);
ValueTask<long> GetOldestGachaItemIdByArchiveIdAsync(Guid archiveId);
ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaConfigType queryType, long endId);
ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaType queryType, long endId);
ValueTask AddGachaItemRangeAsync(List<GachaItem> items);
ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaConfigType queryType, long endId);
ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaType queryType, long endId);
}

View File

@@ -62,7 +62,7 @@ internal sealed partial class UIGFImportService : IUIGFImportService
Guid archiveId = archive.InnerId;
List<GachaItem> fullItems = [];
foreach (GachaConfigType queryType in GachaLog.QueryTypes)
foreach (GachaType queryType in GachaLog.QueryTypes)
{
long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveIdAndQueryType(archiveId, queryType);
logger.LogInformation("Last Id to trim with: [{Id}]", trimId);

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Metadata;
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
internal interface IMetadataListGachaEventSource
{
public List<GachaEvent> GachaEvents { get; set; }
}

View File

@@ -21,6 +21,11 @@ internal static class MetadataServiceContextExtension
{
listMaterialSource.Materials = await metadataService.GetMaterialListAsync(token).ConfigureAwait(false);
}
if (context is IMetadataListGachaEventSource listGachaEventSource)
{
listGachaEventSource.GachaEvents = await metadataService.GetGachaEventListAsync(token).ConfigureAwait(false);
}
}
// Dictionary

View File

@@ -484,7 +484,7 @@
</Grid.ColumnDefinitions>
<shvc:HutaoStatisticsCard Grid.Column="0" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.AvatarEvent}"/>
<shvc:HutaoStatisticsCard Grid.Column="1" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.AvatarEvent2}"/>
<shvc:HutaoStatisticsCard Grid.Column="2" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.WeaponWish}"/>
<shvc:HutaoStatisticsCard Grid.Column="2" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.WeaponEvent}"/>
</Grid>
</Grid>

View File

@@ -19,6 +19,11 @@ internal sealed class GachaStatistics
/// </summary>
public TypedWishSummary WeaponWish { get; set; } = default!;
/// <summary>
/// 集录祈愿
/// </summary>
public TypedWishSummary ChronicledWish { get; set; } = default!;
/// <summary>
/// 奔行世间
/// </summary>

View File

@@ -21,5 +21,10 @@ internal sealed class HutaoStatistics
/// <summary>
/// 神铸赋形
/// </summary>
public HutaoWishSummary WeaponWish { get; set; } = default!;
public HutaoWishSummary WeaponEvent { get; set; } = default!;
/// <summary>
/// 集录祈愿
/// </summary>
public HutaoWishSummary ChronicledWish { get; set; } = default!;
}

View File

@@ -10,11 +10,11 @@ internal static class GachaConfigTypeExtension
/// </summary>
/// <param name="configType">配置类型</param>
/// <returns>祈愿查询类型</returns>
public static GachaConfigType ToQueryType(this GachaConfigType configType)
public static GachaType ToQueryType(this GachaType configType)
{
return configType switch
{
GachaConfigType.AvatarEventWish2 => GachaConfigType.AvatarEventWish,
GachaType.SpecialActivityAvatar => GachaType.ActivityAvatar,
_ => configType,
};
}

View File

@@ -23,7 +23,7 @@ internal class GachaLogItem
/// </summary>
[JsonPropertyName("gacha_type")]
[JsonEnum(JsonSerializeType.NumberString)]
public GachaConfigType GachaType { get; set; } = default!;
public GachaType GachaType { get; set; } = default!;
/// <summary>
/// 总为 <see cref="string.Empty"/>

View File

@@ -31,7 +31,7 @@ internal struct GachaLogQueryOptions
/// </summary>
public long EndId;
public GachaConfigType Type;
public GachaType Type;
/// <summary>
/// Keys required:
@@ -53,7 +53,7 @@ internal struct GachaLogQueryOptions
/// <param name="query">原始查询字符串</param>
/// <param name="queryType">祈愿类型</param>
/// <param name="endId">终止Id</param>
public GachaLogQueryOptions(in GachaLogQuery query, GachaConfigType queryType)
public GachaLogQueryOptions(in GachaLogQuery query, GachaType queryType)
{
IsOversea = query.IsOversea;

View File

@@ -4,44 +4,45 @@
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
/// <summary>
/// 祈愿配置类型
/// 祈愿类型
/// </summary>
[HighQuality]
[Localization]
internal enum GachaConfigType
internal enum GachaType
{
/// <summary>
/// 新手池
/// </summary>
[Description("新手祈愿")]
[LocalizationKey(nameof(SH.WebGachaConfigTypeNoviceWish))]
NoviceWish = 100,
NewBie = 100,
/// <summary>
/// 常驻池
/// </summary>
[Description("常驻祈愿")]
[LocalizationKey(nameof(SH.WebGachaConfigTypePermanentWish))]
StandardWish = 200,
Standard = 200,
/// <summary>
/// 角色1池
/// </summary>
[Description("角色活动祈愿")]
[LocalizationKey(nameof(SH.WebGachaConfigTypeAvatarEventWish))]
AvatarEventWish = 301,
ActivityAvatar = 301,
/// <summary>
/// 武器池
/// </summary>
[Description("武器活动祈愿")]
[LocalizationKey(nameof(SH.WebGachaConfigTypeWeaponEventWish))]
WeaponEventWish = 302,
ActivityWeapon = 302,
/// <summary>
/// 角色2池
/// </summary>
[Description("角色活动祈愿-2")]
[LocalizationKey(nameof(SH.WebGachaConfigTypeAvatarEventWish2))]
AvatarEventWish2 = 400,
SpecialActivityAvatar = 400,
/// <summary>
/// 集录池
/// </summary>
[LocalizationKey(nameof(SH.WebGachaConfigTypeChronicledWish))]
ActivityCity = 500,
}

View File

@@ -39,16 +39,16 @@ internal sealed class EndIds
/// </summary>
/// <param name="type">类型</param>
/// <returns>Last Id</returns>
public long this[GachaConfigType type]
public long this[GachaType type]
{
get
{
return type switch
{
GachaConfigType.NoviceWish => NoviceWish,
GachaConfigType.StandardWish => StandardWish,
GachaConfigType.AvatarEventWish => AvatarEventWish,
GachaConfigType.WeaponEventWish => WeaponEventWish,
GachaType.NewBie => NoviceWish,
GachaType.Standard => StandardWish,
GachaType.ActivityAvatar => AvatarEventWish,
GachaType.ActivityWeapon => WeaponEventWish,
_ => 0,
};
}
@@ -57,16 +57,16 @@ internal sealed class EndIds
{
switch (type)
{
case GachaConfigType.NoviceWish:
case GachaType.NewBie:
NoviceWish = value;
break;
case GachaConfigType.StandardWish:
case GachaType.Standard:
StandardWish = value;
break;
case GachaConfigType.AvatarEventWish:
case GachaType.ActivityAvatar:
AvatarEventWish = value;
break;
case GachaConfigType.WeaponEventWish:
case GachaType.ActivityWeapon:
WeaponEventWish = value;
break;
}
@@ -77,11 +77,11 @@ internal sealed class EndIds
/// 获取枚举器
/// </summary>
/// <returns>枚举器</returns>
public IEnumerator<KeyValuePair<GachaConfigType, long>> GetEnumerator()
public IEnumerator<KeyValuePair<GachaType, long>> GetEnumerator()
{
yield return new(GachaConfigType.NoviceWish, NoviceWish);
yield return new(GachaConfigType.StandardWish, StandardWish);
yield return new(GachaConfigType.AvatarEventWish, AvatarEventWish);
yield return new(GachaConfigType.WeaponEventWish, WeaponEventWish);
yield return new(GachaType.NewBie, NoviceWish);
yield return new(GachaType.Standard, StandardWish);
yield return new(GachaType.ActivityAvatar, AvatarEventWish);
yield return new(GachaType.ActivityWeapon, WeaponEventWish);
}
}

View File

@@ -18,6 +18,11 @@ internal enum GachaDistributionType
/// </summary>
WeaponEvent,
/// <summary>
/// 集录
/// </summary>
Chronicled,
/// <summary>
/// 常驻
/// </summary>

View File

@@ -22,4 +22,9 @@ internal sealed class GachaEventStatistics
/// 武器活动
/// </summary>
public List<ItemCount> WeaponEvent { get; set; } = default!;
/// <summary>
/// 集录祈愿
/// </summary>
public List<ItemCount> Chronicled { get; set; } = default!;
}

View File

@@ -13,14 +13,14 @@ internal sealed class GachaItem
/// <summary>
/// 祈愿记录分类
/// </summary>
public GachaConfigType GachaType { get; set; }
public GachaType GachaType { get; set; }
/// <summary>
/// 祈愿记录查询分类
/// 合并保底的卡池使用此属性
/// 仅4种不含400
/// </summary>
public GachaConfigType QueryType { get; set; }
public GachaType QueryType { get; set; }
/// <summary>
/// 物品Id