refactor model

This commit is contained in:
DismissedLight
2023-07-19 16:44:38 +08:00
parent f531684e6a
commit 01fdcda729
38 changed files with 280 additions and 180 deletions

View File

@@ -0,0 +1,20 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Extension;
internal static class NullableExtension
{
public static bool TryGetValue<T>(this in T? nullable, out T value)
where T : struct
{
if (nullable.HasValue)
{
value = nullable.Value;
return true;
}
value = default;
return false;
}
}

View File

@@ -33,4 +33,22 @@ internal static class SpanExtension
return maxIndex;
}
public static bool TrySplitIntoTwo<T>(this in ReadOnlySpan<T> span, T separator, out ReadOnlySpan<T> left, out ReadOnlySpan<T> right)
where T : IEquatable<T>?
{
int indexOfSeparator = span.IndexOf(separator);
if (indexOfSeparator > 0)
{
left = span[..indexOfSeparator];
right = span[(indexOfSeparator + 1)..];
return true;
}
left = default;
right = default;
return false;
}
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity.Abstraction;
using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
@@ -14,7 +15,11 @@ namespace Snap.Hutao.Model.Entity;
/// </summary>
[HighQuality]
[Table("gacha_items")]
internal sealed class GachaItem
internal sealed partial class GachaItem
: IDbMappingForeignKeyFrom<GachaItem, GachaLogItem, uint>,
IDbMappingForeignKeyFrom<GachaItem, UIGFItem, uint>,
IDbMappingForeignKeyFrom<GachaItem, UIGFItem>,
IDbMappingForeignKeyFrom<GachaItem, Web.Hutao.GachaLog.GachaItem>
{
/// <summary>
/// 内部Id
@@ -61,21 +66,6 @@ internal sealed class GachaItem
/// </summary>
public long Id { get; set; }
/// <summary>
/// 获取物品类型字符串
/// </summary>
/// <param name="itemId">物品Id</param>
/// <returns>物品类型字符串</returns>
public static string GetItemTypeStringByItemId(uint itemId)
{
return itemId.Place() switch
{
8U => "角色",
5U => "武器",
_ => "未知",
};
}
/// <summary>
/// 构造一个新的数据库祈愿物品
/// </summary>
@@ -83,13 +73,13 @@ internal sealed class GachaItem
/// <param name="item">祈愿物品</param>
/// <param name="itemId">物品Id</param>
/// <returns>新的祈愿物品</returns>
public static GachaItem Create(in Guid archiveId, GachaLogItem item, uint itemId)
public static GachaItem From(in Guid archiveId, in GachaLogItem item, in uint itemId)
{
return new()
{
ArchiveId = archiveId,
GachaType = item.GachaType,
QueryType = ToQueryType(item.GachaType),
QueryType = item.GachaType.ToQueryType(),
ItemId = itemId,
Time = item.Time,
Id = item.Id,
@@ -103,7 +93,7 @@ internal sealed class GachaItem
/// <param name="item">祈愿物品</param>
/// <param name="itemId">物品Id</param>
/// <returns>新的祈愿物品</returns>
public static GachaItem CreateForMajor2Minor2OrLower(in Guid archiveId, UIGFItem item, uint itemId)
public static GachaItem From(in Guid archiveId, in UIGFItem item, in uint itemId)
{
return new()
{
@@ -122,7 +112,7 @@ internal sealed class GachaItem
/// <param name="archiveId">存档Id</param>
/// <param name="item">祈愿物品</param>
/// <returns>新的祈愿物品</returns>
public static GachaItem CreateForMajor2Minor3OrHigher(in Guid archiveId, UIGFItem item)
public static GachaItem From(in Guid archiveId, in UIGFItem item)
{
return new()
{
@@ -141,7 +131,7 @@ internal sealed class GachaItem
/// <param name="archiveId">存档Id</param>
/// <param name="item">祈愿物品</param>
/// <returns>新的祈愿物品</returns>
public static GachaItem Create(in Guid archiveId, Web.Hutao.GachaLog.GachaItem item)
public static GachaItem From(in Guid archiveId, in Web.Hutao.GachaLog.GachaItem item)
{
return new()
{
@@ -153,39 +143,4 @@ internal sealed class GachaItem
Id = item.Id,
};
}
/// <summary>
/// 将祈愿配置类型转换到祈愿查询类型
/// </summary>
/// <param name="configType">配置类型</param>
/// <returns>祈愿查询类型</returns>
public static GachaConfigType ToQueryType(GachaConfigType configType)
{
return configType switch
{
GachaConfigType.AvatarEventWish2 => GachaConfigType.AvatarEventWish,
_ => configType,
};
}
/// <summary>
/// 转换到UIGF物品
/// </summary>
/// <param name="nameQuality">物品</param>
/// <returns>UIGF 物品</returns>
public UIGFItem ToUIGFItem(INameQuality nameQuality)
{
return new()
{
GachaType = GachaType,
ItemId = $"{ItemId}",
Count = 1,
Time = Time,
Name = nameQuality.Name,
ItemType = GetItemTypeStringByItemId(ItemId),
Rank = nameQuality.Quality,
Id = Id,
UIGFGachaType = QueryType,
};
}
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Model.Entity.Primitive;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -13,7 +14,7 @@ namespace Snap.Hutao.Model.Entity;
/// </summary>
[HighQuality]
[Table("game_accounts")]
internal sealed class GameAccount : ObservableObject
internal sealed class GameAccount : ObservableObject, IMappingFrom<GameAccount, string, string>
{
/// <summary>
/// 内部Id
@@ -48,7 +49,7 @@ internal sealed class GameAccount : ObservableObject
/// <param name="name">名称</param>
/// <param name="sdk">sdk</param>
/// <returns>游戏内账号</returns>
public static GameAccount Create(string name, string sdk)
public static GameAccount From(string name, string sdk)
{
return new()
{

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity.Abstraction;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -11,7 +12,7 @@ namespace Snap.Hutao.Model.Entity;
/// </summary>
[HighQuality]
[Table("inventory_items")]
internal sealed class InventoryItem
internal sealed class InventoryItem : IDbMappingForeignKeyFrom<InventoryItem, uint>
{
/// <summary>
/// 内部Id
@@ -47,7 +48,7 @@ internal sealed class InventoryItem
/// <param name="projectId">项目Id</param>
/// <param name="itemId">物品Id</param>
/// <returns>新的个数为0的物品</returns>
public static InventoryItem Create(in Guid projectId, uint itemId)
public static InventoryItem From(in Guid projectId, in uint itemId)
{
return new()
{

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Core.Abstraction;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -12,7 +13,8 @@ namespace Snap.Hutao.Model.Entity;
/// </summary>
[HighQuality]
[Table("spiral_abysses")]
internal sealed class SpiralAbyssEntry : ObservableObject
internal sealed class SpiralAbyssEntry : ObservableObject,
IMappingFrom<SpiralAbyssEntry, string, Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss>
{
/// <summary>
/// 内部Id
@@ -48,7 +50,7 @@ internal sealed class SpiralAbyssEntry : ObservableObject
/// <param name="uid">uid</param>
/// <param name="spiralAbyss">深渊信息</param>
/// <returns>新的深渊信息</returns>
public static SpiralAbyssEntry Create(string uid, Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss)
public static SpiralAbyssEntry From(string uid, Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss)
{
return new()
{

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Web.Hoyolab;
using System.ComponentModel.DataAnnotations;
@@ -13,7 +14,7 @@ namespace Snap.Hutao.Model.Entity;
/// </summary>
[HighQuality]
[Table("users")]
internal sealed class User : ISelectable
internal sealed class User : ISelectable, IMappingFrom<User, Cookie, bool>
{
/// <summary>
/// 内部Id
@@ -65,7 +66,7 @@ internal sealed class User : ISelectable
/// <param name="cookie">cookie</param>
/// <param name="isOversea">是否为国际服</param>
/// <returns>新创建的用户</returns>
public static User Create(Cookie cookie, bool isOversea)
public static User From(Cookie cookie, bool isOversea)
{
_ = cookie.TryGetSToken(isOversea, out Cookie? sToken);
_ = cookie.TryGetLToken(out Cookie? lToken);

View File

@@ -24,8 +24,7 @@ internal sealed class UIAF
/// 信息
/// </summary>
[JsonPropertyName("info")]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public UIAFInfo Info { get; set; } = default!;
public required UIAFInfo Info { get; set; } = default!;
/// <summary>
/// 列表

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Core.Abstraction;
namespace Snap.Hutao.Model.InterChange.Achievement;
@@ -9,7 +10,7 @@ namespace Snap.Hutao.Model.InterChange.Achievement;
/// UIAF格式的信息
/// </summary>
[HighQuality]
internal sealed class UIAFInfo
internal sealed class UIAFInfo : IMappingFrom<UIAFInfo, RuntimeOptions>
{
/// <summary>
/// 导出的 App 名称
@@ -47,17 +48,15 @@ internal sealed class UIAFInfo
/// <summary>
/// 构造一个新的专用 UIAF 信息
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="runtimeOptions">运行时信息</param>
/// <returns>专用 UIAF 信息</returns>
public static UIAFInfo Create(IServiceProvider serviceProvider)
public static UIAFInfo From(RuntimeOptions runtimeOptions)
{
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
return new()
{
ExportTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds(),
ExportApp = SH.AppName,
ExportAppVersion = hutaoOptions.Version.ToString(),
ExportAppVersion = runtimeOptions.Version.ToString(),
UIAFVersion = UIAF.CurrentVersion,
};
}

View File

@@ -53,8 +53,7 @@ internal sealed class UIGF
{
foreach (UIGFItem item in List)
{
// Hard coded type name
if (item.ItemType != "角色" && item.ItemType != "武器")
if (item.ItemType != SH.ModelInterchangeUIGFItemTypeAvatar && item.ItemType != SH.ModelInterchangeUIGFItemTypeWeapon)
{
return false;
}

View File

@@ -2,6 +2,8 @@
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Service.Metadata;
namespace Snap.Hutao.Model.InterChange.GachaLog;
@@ -9,7 +11,7 @@ namespace Snap.Hutao.Model.InterChange.GachaLog;
/// UIGF格式的信息
/// </summary>
[HighQuality]
internal sealed class UIGFInfo
internal sealed class UIGFInfo : IMappingFrom<UIGFInfo, IServiceProvider, string>
{
/// <summary>
/// 用户Uid
@@ -62,17 +64,18 @@ internal sealed class UIGFInfo
/// <param name="serviceProvider">服务提供器</param>
/// <param name="uid">uid</param>
/// <returns>专用 UIGF 信息</returns>
public static UIGFInfo Create(IServiceProvider serviceProvider, string uid)
public static UIGFInfo From(IServiceProvider serviceProvider, string uid)
{
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
MetadataOptions metadataOptions = serviceProvider.GetRequiredService<MetadataOptions>();
return new()
{
Uid = uid,
Language = "zh-cn",
Language = metadataOptions.LanguageCode,
ExportTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds(),
ExportApp = SH.AppName,
ExportAppVersion = hutaoOptions.Version.ToString(),
ExportAppVersion = runtimeOptions.Version.ToString(),
UIGFVersion = UIGF.CurrentVersion,
};
}

View File

@@ -1,7 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Core.Json.Annotation;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
namespace Snap.Hutao.Model.InterChange.GachaLog;
@@ -10,7 +13,7 @@ namespace Snap.Hutao.Model.InterChange.GachaLog;
/// UIGF物品
/// </summary>
[HighQuality]
internal sealed class UIGFItem : GachaLogItem
internal sealed class UIGFItem : GachaLogItem, IMappingFrom<UIGFItem, GachaItem, INameQuality>
{
/// <summary>
/// 额外祈愿映射
@@ -18,4 +21,30 @@ internal sealed class UIGFItem : GachaLogItem
[JsonPropertyName("uigf_gacha_type")]
[JsonEnum(JsonSerializeType.NumberString)]
public GachaConfigType UIGFGachaType { get; set; } = default!;
public static UIGFItem From(GachaItem item, INameQuality nameQuality)
{
return new()
{
GachaType = item.GachaType,
ItemId = $"{item.ItemId:D}",
Count = 1,
Time = item.Time,
Name = nameQuality.Name,
ItemType = GetItemTypeStringByItemId(item.ItemId),
Rank = nameQuality.Quality,
Id = item.Id,
UIGFGachaType = item.QueryType,
};
}
private static string GetItemTypeStringByItemId(uint itemId)
{
return itemId.Place() switch
{
8U => SH.ModelInterchangeUIGFItemTypeAvatar,
5U => SH.ModelInterchangeUIGFItemTypeWeapon,
_ => SH.ModelInterchangeUIGFItemTypeUnknown,
};
}
}

View File

@@ -62,16 +62,15 @@ internal sealed class UIIFInfo
/// <param name="serviceProvider">服务提供器</param>
/// <param name="uid">uid</param>
/// <returns>专用 UIGF 信息</returns>
public static UIIFInfo Create(IServiceProvider serviceProvider, string uid)
public static UIIFInfo From(IServiceProvider serviceProvider, string uid)
{
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
return new()
{
Uid = uid,
Language = "zh-cn",
ExportTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds(),
ExportApp = "胡桃",
ExportApp = SH.AppName,
ExportAppVersion = hutaoOptions.Version.ToString(),
UIIFVersion = UIIF.CurrentVersion,
};

View File

@@ -51,13 +51,13 @@ internal static class IntrinsicImmutable
}.ToImmutableHashSet();
public static readonly ImmutableHashSet<string> MaterialTypeDescriptions = new HashSet<string>(7)
{
SH.ModelMetadataMaterialCharacterAndWeaponEnhancementMaterial,
SH.ModelMetadataMaterialCharacterEXPMaterial,
SH.ModelMetadataMaterialCharacterAscensionMaterial,
SH.ModelMetadataMaterialCharacterTalentMaterial,
SH.ModelMetadataMaterialCharacterLevelUpMaterial,
SH.ModelMetadataMaterialWeaponEnhancementMaterial,
SH.ModelMetadataMaterialWeaponAscensionMaterial,
}.ToImmutableHashSet();
{
SH.ModelMetadataMaterialCharacterAndWeaponEnhancementMaterial,
SH.ModelMetadataMaterialCharacterEXPMaterial,
SH.ModelMetadataMaterialCharacterAscensionMaterial,
SH.ModelMetadataMaterialCharacterTalentMaterial,
SH.ModelMetadataMaterialCharacterLevelUpMaterial,
SH.ModelMetadataMaterialWeaponEnhancementMaterial,
SH.ModelMetadataMaterialWeaponAscensionMaterial,
}.ToImmutableHashSet();
}

View File

@@ -30,8 +30,9 @@ internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IName
public CookBonusView? CookBonusView { get; set; }
/// <summary>
/// 养成物品视图
/// [非元数据] 养成物品视图
/// </summary>
[JsonIgnore]
public List<Material>? CultivationItemsView { get; set; }
/// <summary>

View File

@@ -1,10 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Avatar;
/// <summary>
/// 描述与参数
/// 通常用于技能
/// </summary>
[HighQuality]
internal sealed class DescriptionsParameters
@@ -17,5 +20,5 @@ internal sealed class DescriptionsParameters
/// <summary>
/// 参数
/// </summary>
public List<LevelParameters<int, float>> Parameters { get; set; } = default!;
public List<LevelParameters<SkillLevel, float>> Parameters { get; set; } = default!;
}

View File

@@ -101,7 +101,12 @@ internal static class AvatarIds
return avatarId == PlayerBoy || avatarId == PlayerGirl;
}
public static Dictionary<AvatarId, Avatar.Avatar> InsertPlayers(Dictionary<AvatarId, Avatar.Avatar> idAvatarMap)
/// <summary>
/// 复制一个映射并加入旅行者的基本信息
/// </summary>
/// <param name="idAvatarMap">映射</param>
/// <returns>加入旅行者基本信息的映射</returns>
public static Dictionary<AvatarId, Avatar.Avatar> WithPlayers(Dictionary<AvatarId, Avatar.Avatar> idAvatarMap)
{
return new(idAvatarMap)
{

View File

@@ -2,7 +2,9 @@
// Licensed under the MIT license.
using Snap.Hutao.Control;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Primitive;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
@@ -22,8 +24,8 @@ internal sealed partial class DescriptionsParametersDescriptor : ValueConverter<
/// <returns>特定等级的解释</returns>
public static LevelParameters<string, ParameterDescription> Convert(DescriptionsParameters from, uint level)
{
LevelParameters<int, float> param = from.Parameters.Single(param => param.Level == level);
return new LevelParameters<string, ParameterDescription>($"Lv.{param.Level}", GetParameterDescription(from.Descriptions, param.Parameters));
LevelParameters<SkillLevel, float> param = from.Parameters.Single(param => param.Level == level);
return new LevelParameters<string, ParameterDescription>($"Lv.{param.Level.Value}", GetParameterDescription(from.Descriptions, param.Parameters));
}
/// <inheritdoc/>
@@ -35,36 +37,36 @@ internal sealed partial class DescriptionsParametersDescriptor : ValueConverter<
return parameters;
}
[GeneratedRegex("{param[0-9]+.*?}")]
[GeneratedRegex("{param([1-9][0-9]*?):(.+?)}")]
private static partial Regex ParamRegex();
private static List<ParameterDescription> GetParameterDescription(List<string> descriptions, List<float> param)
private static List<ParameterDescription> GetParameterDescription(List<string> descriptions, List<float> paramList)
{
Span<string> span = CollectionsMarshal.AsSpan(descriptions);
List<ParameterDescription> results = new(span.Length);
foreach (ReadOnlySpan<char> desc in span)
foreach (string desc in span)
{
int indexOfSeparator = desc.IndexOf('|');
ReadOnlySpan<char> description = desc[..indexOfSeparator];
ReadOnlySpan<char> format = desc[(indexOfSeparator + 1)..];
string resultFormatted = ParamRegex().Replace(format.ToString(), match => ReplaceParamInMatch(match, param));
results.Add(new ParameterDescription { Description = description.ToString(), Parameter = resultFormatted });
if (desc.AsSpan().TrySplitIntoTwo('|', out ReadOnlySpan<char> description, out ReadOnlySpan<char> format))
{
string resultFormatted = ParamRegex().Replace(format.ToString(), match => ReplaceParamInMatch(match, paramList));
results.Add(new ParameterDescription { Description = description.ToString(), Parameter = resultFormatted });
}
else
{
ThrowHelper.InvalidOperation($"ParameterFormat failed, value: `{desc}`", default);
}
}
return results;
}
private static string ReplaceParamInMatch(Match match, List<float> param)
private static string ReplaceParamInMatch(Match match, List<float> paramList)
{
if (match.Success)
{
// remove parentheses and split by {value:format} like {param1:F}
string[] parts = match.Value[1..^1].Split(':', 2);
int index = int.Parse(parts[0]["param".Length..]) - 1;
return ParameterFormat.Format($"{{0:{parts[1]}}}", param[index]);
int index = int.Parse(match.Groups[1].Value) - 1;
return ParameterFormat.Format($"{{0:{match.Groups[2].Value}}}", paramList[index]);
}
else
{

View File

@@ -34,22 +34,13 @@ internal static class FightPropertyFormat
return new(property.GetLocalizedDescription(), FormatValue(property, value));
}
/// <summary>
/// 格式化有绿字的角色属性
/// </summary>
/// <param name="property">战斗属性</param>
/// <param name="baseValue">白字</param>
/// <param name="addValue">绿字</param>
/// <returns>对2</returns>
public static AvatarProperty ToAvatarProperty(FightProperty property, float baseValue, float addValue)
public static ParameterDescription ToParameterDescription(FightProperty property, float value)
{
string name = property.GetLocalizedDescription();
FormatMethod method = property.GetFormatMethod();
string value = FormatValue(method, baseValue + addValue);
string addedValue = $"[{FormatValue(method, baseValue)}+{FormatValue(method, addValue)}]";
return new(property, name, value, addedValue);
return new()
{
Description = property.GetLocalizedDescription(),
Parameter = FormatValue(property, value),
};
}
/// <summary>
@@ -82,6 +73,24 @@ internal static class FightPropertyFormat
return new(property, name, FormatValue(method, value));
}
/// <summary>
/// 格式化有绿字的角色属性
/// </summary>
/// <param name="property">战斗属性</param>
/// <param name="baseValue">白字</param>
/// <param name="addValue">绿字</param>
/// <returns>对2</returns>
public static AvatarProperty ToAvatarProperty(FightProperty property, float baseValue, float addValue)
{
string name = property.GetLocalizedDescription();
FormatMethod method = property.GetFormatMethod();
string value = FormatValue(method, baseValue + addValue);
string addedValue = $"[{FormatValue(method, baseValue)}+{FormatValue(method, addValue)}]";
return new(property, name, value, addedValue);
}
/// <summary>
/// 格式化战斗属性
/// </summary>

View File

@@ -24,15 +24,11 @@ internal sealed class PropertiesParametersDescriptor : ValueConverter<Properties
private static List<ParameterDescription> GetParameterDescriptions(List<float> parameters, List<FightProperty> properties)
{
List<ParameterDescription> results = new();
List<ParameterDescription> results = new(parameters.Count);
foreach ((float param, FightProperty property) in parameters.Zip(properties))
{
results.Add(new ParameterDescription
{
Description = property.GetLocalizedDescription(),
Parameter = FightPropertyFormat.FormatValue(property, param),
});
results.Add(FightPropertyFormat.ToParameterDescription(property, param));
}
return results;

View File

@@ -12,7 +12,7 @@ namespace Snap.Hutao.Model.Metadata;
[HighQuality]
internal sealed class GrowCurve
{
private Dictionary<GrowCurveType, float>? curveMap;
private Dictionary<GrowCurveType, float>? map;
/// <summary>
/// 等级
@@ -27,8 +27,8 @@ internal sealed class GrowCurve
/// <summary>
/// 曲线映射
/// </summary>
public Dictionary<GrowCurveType, float> CurveMap
public Dictionary<GrowCurveType, float> Map
{
get => curveMap ??= Curves.ToDictionary(v => v.Type, v => v.Value);
get => map ??= Curves.ToDictionary(v => v.Type, v => v.Value);
}
}

View File

@@ -420,6 +420,33 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 角色 的本地化字符串。
/// </summary>
internal static string ModelInterchangeUIGFItemTypeAvatar {
get {
return ResourceManager.GetString("ModelInterchangeUIGFItemTypeAvatar", resourceCulture);
}
}
/// <summary>
/// 查找类似 未知 的本地化字符串。
/// </summary>
internal static string ModelInterchangeUIGFItemTypeUnknown {
get {
return ResourceManager.GetString("ModelInterchangeUIGFItemTypeUnknown", resourceCulture);
}
}
/// <summary>
/// 查找类似 武器 的本地化字符串。
/// </summary>
internal static string ModelInterchangeUIGFItemTypeWeapon {
get {
return ResourceManager.GetString("ModelInterchangeUIGFItemTypeWeapon", resourceCulture);
}
}
/// <summary>
/// 查找类似 愚人众 的本地化字符串。
/// </summary>

View File

@@ -2063,4 +2063,13 @@
<data name="ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale" xml:space="preserve">
<value>Url 中的语言:{0} 与胡桃的语言:{1} 不匹配,请切换到对应语言重试</value>
</data>
<data name="ModelInterchangeUIGFItemTypeAvatar" xml:space="preserve">
<value>角色</value>
</data>
<data name="ModelInterchangeUIGFItemTypeWeapon" xml:space="preserve">
<value>武器</value>
</data>
<data name="ModelInterchangeUIGFItemTypeUnknown" xml:space="preserve">
<value>未知</value>
</data>
</root>

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.InterChange.Achievement;
@@ -63,7 +64,7 @@ internal sealed partial class AchievementService
return new()
{
Info = UIAFInfo.Create(serviceProvider),
Info = UIAFInfo.From(scope.ServiceProvider.GetRequiredService<RuntimeOptions>()),
List = list,
};
}

View File

@@ -68,16 +68,16 @@ internal sealed partial class AchievementService : IAchievementService
List<AchievementStatistics> results = new();
foreach (AchievementArchive archive in appDbContext.AchievementArchives)
{
int finished = await appDbContext.Achievements
int finishedCount = await appDbContext.Achievements
.Where(a => a.ArchiveId == archive.InnerId)
.Where(a => (int)a.Status >= (int)Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
.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 => (int)a.Status >= (int)Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
.OrderByDescending(a => a.Time.ToString())
.Take(2)
.ToListAsync()
@@ -86,7 +86,7 @@ internal sealed partial class AchievementService : IAchievementService
results.Add(new()
{
DisplayName = archive.Name,
FinishDescription = AchievementStatistics.Format(finished, totalCount, out _),
FinishDescription = AchievementStatistics.Format(finishedCount, totalCount, out _),
Achievements = achievements.SelectList(entity => new AchievementView(entity, achievementMap[entity.Id])),
});
}

View File

@@ -45,7 +45,7 @@ internal sealed partial class CultivationService : ICultivationService
List<InventoryItemView> results = new();
foreach (Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id.Value))
{
InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.Create(projectId, meta.Id);
InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id);
results.Add(new(entity, meta, saveCommand));
}

View File

@@ -131,7 +131,7 @@ internal struct GachaLogFetchContext
/// <param name="item">物品</param>
public void AddItem(GachaLogItem item)
{
ItemsToAdd.Add(GachaItem.Create(TargetArchive!.InnerId, item, serviceContext.GetItemId(item)));
ItemsToAdd.Add(GachaItem.From(TargetArchive!.InnerId, item, serviceContext.GetItemId(item)));
FetchStatus.Items.Add(serviceContext.GetItemByNameAndType(item.Name, item.ItemType));
QueryOptions.EndId = item.Id;
}

View File

@@ -80,7 +80,7 @@ internal sealed partial class HutaoCloudService : IHutaoCloudService
await appDbContext.GachaArchives.AddAndSaveAsync(archive).ConfigureAwait(false);
}
List<Model.Entity.GachaItem> gachaItems = resp.Data.SelectList(i => Model.Entity.GachaItem.Create(archive.InnerId, i));
List<Model.Entity.GachaItem> gachaItems = resp.Data.SelectList(i => Model.Entity.GachaItem.From(archive.InnerId, i));
await appDbContext.GachaItems.AddRangeAndSaveAsync(gachaItems).ConfigureAwait(false);
return new(true, archive);
}

View File

@@ -53,41 +53,41 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
using (TempFile? tempFile = TempFile.CopyFrom(cacheFile))
{
if (tempFile == null)
if (tempFile.TryGetValue(out TempFile file))
{
return new(false, string.Format(Regex.Unescape(SH.ServiceGachaLogUrlProviderCachePathNotFound), cacheFile));
}
using (FileStream fileStream = new(tempFile.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (MemoryStream memoryStream = new())
using (FileStream fileStream = new(file.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
string? result = Match(memoryStream, cacheFile.Contains(GameConstants.GenshinImpactData));
if (!string.IsNullOrEmpty(result))
using (MemoryStream memoryStream = new())
{
QueryString query = QueryString.Parse(result.TrimEnd("#/log"));
string queryLanguageCode = query["lang"];
if (metadataOptions.IsCurrentLocale(queryLanguageCode))
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
string? result = Match(memoryStream, cacheFile.Contains(GameConstants.GenshinImpactData));
if (!string.IsNullOrEmpty(result))
{
return new(true, new(result));
QueryString query = QueryString.Parse(result.TrimEnd("#/log"));
string queryLanguageCode = query["lang"];
if (metadataOptions.IsCurrentLocale(queryLanguageCode))
{
return new(true, new(result));
}
else
{
string message = string.Format(
SH.ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale,
queryLanguageCode,
metadataOptions.LocaleName);
return new(false, message);
}
}
else
{
string message = string.Format(
SH.ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale,
queryLanguageCode,
metadataOptions.LocaleName);
return new(false, message);
return new(false, SH.ServiceGachaLogUrlProviderCacheUrlNotFound);
}
}
else
{
return new(false, SH.ServiceGachaLogUrlProviderCacheUrlNotFound);
}
}
}
return new(false, string.Format(Regex.Unescape(SH.ServiceGachaLogUrlProviderCachePathNotFound), cacheFile));
}
}
else

View File

@@ -28,12 +28,12 @@ internal sealed partial class UIGFExportService : IUIGFExportService
List<UIGFItem> list = appDbContext.GachaItems
.Where(i => i.ArchiveId == archive.InnerId)
.AsEnumerable()
.Select(i => i.ToUIGFItem(context.GetNameQualityByItemId(i.ItemId)))
.Select(i => UIGFItem.From(i, context.GetNameQualityByItemId(i.ItemId)))
.ToList();
UIGF uigf = new()
{
Info = UIGFInfo.Create(serviceProvider, archive.Uid),
Info = UIGFInfo.From(serviceProvider, archive.Uid),
List = list,
};

View File

@@ -45,11 +45,11 @@ internal sealed partial class UIGFImportService : IUIGFImportService
UIGFVersion.Major2Minor3OrHigher => uigf.List
.OrderByDescending(i => i.Id)
.Where(i => i.Id < trimId)
.Select(i => GachaItem.CreateForMajor2Minor3OrHigher(archiveId, i)),
.Select(i => GachaItem.From(archiveId, i)),
UIGFVersion.Major2Minor2OrLower => uigf.List
.OrderByDescending(i => i.Id)
.Where(i => i.Id < trimId)
.Select(i => GachaItem.CreateForMajor2Minor2OrLower(archiveId, i, context.GetItemId(i))),
.Select(i => GachaItem.From(archiveId, i, context.GetItemId(i))),
_ => Enumerable.Empty<GachaItem>(),
};

View File

@@ -326,7 +326,7 @@ internal sealed partial class GameService : IGameService
if (isOk)
{
account = GameAccount.Create(name, registrySdk);
account = GameAccount.From(name, registrySdk);
// sync database
await taskContext.SwitchToBackgroundAsync();

View File

@@ -158,7 +158,7 @@ internal sealed partial class HutaoCache : IHutaoCache
if (idAvatarExtendedMap == null)
{
Dictionary<AvatarId, Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
idAvatarExtendedMap = AvatarIds.InsertPlayers(idAvatarMap);
idAvatarExtendedMap = AvatarIds.WithPlayers(idAvatarMap);
}
return idAvatarExtendedMap;

View File

@@ -94,19 +94,19 @@ internal sealed partial class MetadataService
/// <inheritdoc/>
public ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToAvatarCurveMapAsync(CancellationToken token = default)
{
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameAvatarCurve, a => a.Level, a => a.CurveMap, token);
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameAvatarCurve, a => a.Level, a => a.Map, token);
}
/// <inheritdoc/>
public ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToMonsterCurveMapAsync(CancellationToken token = default)
{
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameMonsterCurve, m => m.Level, m => m.CurveMap, token);
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameMonsterCurve, m => m.Level, m => m.Map, token);
}
/// <inheritdoc/>
public ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToWeaponCurveMapAsync(CancellationToken token = default)
{
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameWeaponCurve, w => w.Level, w => w.CurveMap, token);
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameWeaponCurve, w => w.Level, w => w.Map, token);
}
/// <inheritdoc/>

View File

@@ -88,7 +88,7 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
}
else
{
SpiralAbyssEntry newEntry = SpiralAbyssEntry.Create(userAndUid.Uid.Value, webSpiralAbyss);
SpiralAbyssEntry newEntry = SpiralAbyssEntry.From(userAndUid.Uid.Value, webSpiralAbyss);
await taskContext.SwitchToMainThreadAsync();
spiralAbysses!.Insert(0, newEntry);

View File

@@ -82,7 +82,7 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
idAvatarMap = AvatarIds.InsertPlayers(idAvatarMap);
idAvatarMap = AvatarIds.WithPlayers(idAvatarMap);
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{

View File

@@ -126,7 +126,7 @@ internal sealed class User : ObservableObject
internal static async Task<User?> CreateAsync(Cookie cookie, bool isOversea, CancellationToken token = default)
{
// 这里只负责创建实体用户,稍后在用户服务中保存到数据库
EntityUser entity = EntityUser.Create(cookie, isOversea);
EntityUser entity = EntityUser.From(cookie, isOversea);
entity.Aid = cookie.GetValueOrDefault(Cookie.STUID);
entity.Mid = isOversea ? entity.Aid : cookie.GetValueOrDefault(Cookie.MID);

View File

@@ -0,0 +1,21 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
internal static class GachaConfigTypeExtension
{
/// <summary>
/// 将祈愿配置类型转换到祈愿查询类型
/// </summary>
/// <param name="configType">配置类型</param>
/// <returns>祈愿查询类型</returns>
public static GachaConfigType ToQueryType(this GachaConfigType configType)
{
return configType switch
{
GachaConfigType.AvatarEventWish2 => GachaConfigType.AvatarEventWish,
_ => configType,
};
}
}