diff --git a/src/Snap.Hutao/.editorconfig b/src/Snap.Hutao/.editorconfig index a5fa13df09..c3b2ed217d 100644 --- a/src/Snap.Hutao/.editorconfig +++ b/src/Snap.Hutao/.editorconfig @@ -1,4 +1,6 @@ -[*.cs] +charset = utf-8-bom + +[*.cs] # SA1101: Prefix local calls with this dotnet_diagnostic.SA1101.severity = none @@ -76,31 +78,31 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.required_modifiers = # 命名样式 dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_diagnostic.SA1629.severity = none dotnet_diagnostic.SA1642.severity = none @@ -185,29 +187,29 @@ dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯 dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected -dotnet_naming_symbols.类型.required_modifiers = +dotnet_naming_symbols.类型.required_modifiers = dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected -dotnet_naming_symbols.非字段成员.required_modifiers = +dotnet_naming_symbols.非字段成员.required_modifiers = # 命名样式 dotnet_naming_style.以_i_开始.required_prefix = I -dotnet_naming_style.以_i_开始.required_suffix = -dotnet_naming_style.以_i_开始.word_separator = +dotnet_naming_style.以_i_开始.required_suffix = +dotnet_naming_style.以_i_开始.word_separator = dotnet_naming_style.以_i_开始.capitalization = pascal_case -dotnet_naming_style.帕斯卡拼写法.required_prefix = -dotnet_naming_style.帕斯卡拼写法.required_suffix = -dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case -dotnet_naming_style.帕斯卡拼写法.required_prefix = -dotnet_naming_style.帕斯卡拼写法.required_suffix = -dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/HttpClientGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/HttpClientGenerator.cs index 1ea27e0993..451bc28704 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/HttpClientGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/HttpClientGenerator.cs @@ -26,7 +26,6 @@ public class HttpClientGenerator : ISourceGenerator private const string PrimaryHttpMessageHandlerAttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.PrimaryHttpMessageHandlerAttribute"; private const string DynamicSecretAttributeName = "Snap.Hutao.Web.Hoyolab.DynamicSecret.UseDynamicSecretAttribute"; - private const string IgnoreSetCookieAttributeName = "Snap.Hutao.Web.Hoyolab.Annotation.IgnoreSetCookieAttribute"; /// public void Initialize(GeneratorInitializationContext context) @@ -54,7 +53,6 @@ public void Execute(GeneratorExecutionContext context) // This class is generated by Snap.Hutao.SourceGeneration using Microsoft.Extensions.DependencyInjection; -using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.DynamicSecret; using System.Net.Http; @@ -83,10 +81,7 @@ private static void FillWithInjectionServices(HttpClientSyntaxContextReceiver re foreach (INamedTypeSymbol classSymbol in receiver.Classes) { - lineBuilder - .Clear() - .Append("\r\n"); - + lineBuilder.Clear().Append(Environment.NewLine); lineBuilder.Append(@" services.AddHttpClient<"); lineBuilder.Append($"{classSymbol.ToDisplayString()}>("); @@ -139,11 +134,6 @@ private static void FillWithInjectionServices(HttpClientSyntaxContextReceiver re lineBuilder.Append(".AddHttpMessageHandler()"); } - if (classSymbol.GetAttributes().Any(attr => attr.AttributeClass!.ToDisplayString() == IgnoreSetCookieAttributeName)) - { - lineBuilder.Append(".AddHttpMessageHandler()"); - } - lineBuilder.Append(";"); lines.Add(lineBuilder.ToString()); diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml b/src/Snap.Hutao/Snap.Hutao/App.xaml index 20bbaecd5e..8a168c95d3 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml @@ -41,8 +41,10 @@ + + diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs index 43d12c36f8..eb1adc2597 100644 --- a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs @@ -10,8 +10,11 @@ namespace Snap.Hutao.Context.Database; /// /// 应用程序数据库上下文 /// -public class AppDbContext : DbContext +public sealed class AppDbContext : DbContext { + private readonly Guid contextId; + private readonly ILogger? logger; + /// /// 构造一个新的应用程序数据库上下文 /// @@ -21,6 +24,19 @@ public AppDbContext(DbContextOptions options) { } + /// + /// 构造一个新的应用程序数据库上下文 + /// + /// 选项 + /// 日志器 + public AppDbContext(DbContextOptions options, ILogger logger) + : this(options) + { + contextId = Guid.NewGuid(); + this.logger = logger; + logger.LogInformation("AppDbContext[{id}] created.", contextId); + } + /// /// 设置 /// @@ -66,6 +82,11 @@ public AppDbContext(DbContextOptions options) /// public DbSet DailyNotes { get; set; } = default!; + /// + /// 对象缓存 + /// + public DbSet ObjectCache { get; set; } = default!; + /// /// 构造一个临时的应用程序数据库上下文 /// @@ -76,6 +97,13 @@ public static AppDbContext Create(string sqlConnectionString) return new(new DbContextOptionsBuilder().UseSqlite(sqlConnectionString).Options); } + /// + public override void Dispose() + { + base.Dispose(); + logger?.LogInformation("AppDbContext[{id}] disposed.", contextId); + } + /// protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs index 92f6e22f70..151205b5e6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs @@ -5,6 +5,7 @@ using Snap.Hutao.Core.Logging; using System.Collections.Immutable; using System.IO; +using System.Net; using System.Net.Http; using System.Security.Cryptography; using System.Text; @@ -236,10 +237,11 @@ private async Task DownloadFileAsync(Uri uri, StorageFile baseFile) } } } - else + else if (message.StatusCode == HttpStatusCode.TooManyRequests) { retryCount++; TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount]; + logger.LogInformation("Retry after {delay}.", delay); await Task.Delay(delay).ConfigureAwait(false); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/LoggerExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/LoggerExtension.cs new file mode 100644 index 0000000000..ba27babf5e --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Extension/LoggerExtension.cs @@ -0,0 +1,25 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Extension; + +/// +/// 日志器扩展 +/// +[SuppressMessage("", "CA2254")] +public static class LoggerExtension +{ + /// + public static T LogInformation(this ILogger logger, string message, params object?[] param) + { + logger.LogInformation(message, param); + return default!; + } + + /// + public static T LogWarning(this ILogger logger, string message, params object?[] param) + { + logger.LogWarning(message, param); + return default!; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs index 088d814e06..44444b055f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs @@ -46,4 +46,4 @@ public static StringBuilder AppendIfElse(this StringBuilder sb, bool condition, { return condition ? sb.Append(trueValue) : sb.Append(falseValue); } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221128115346_ObjectCache.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221128115346_ObjectCache.Designer.cs new file mode 100644 index 0000000000..113d220436 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221128115346_ObjectCache.Designer.cs @@ -0,0 +1,311 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Snap.Hutao.Context.Database; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20221128115346_ObjectCache")] + partial class ObjectCache + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.0"); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("Current") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("achievements"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("achievement_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Info") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("avatar_infos"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DailyNote") + .HasColumnType("TEXT"); + + b.Property("DailyTaskNotify") + .HasColumnType("INTEGER"); + + b.Property("DailyTaskNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotify") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ShowInHomeWidget") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotify") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("UserId"); + + b.ToTable("daily_notes"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("gacha_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("GachaType") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("QueryType") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("gacha_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttachUid") + .HasColumnType("TEXT"); + + b.Property("MihoyoSDK") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.ToTable("game_accounts"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ExpireTime") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("object_cache"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("settings"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Aid") + .HasColumnType("TEXT"); + + b.Property("CookieToken") + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Ltoken") + .HasColumnType("TEXT"); + + b.Property("Mid") + .HasColumnType("TEXT"); + + b.Property("Stoken") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221128115346_ObjectCache.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221128115346_ObjectCache.cs new file mode 100644 index 0000000000..679836e883 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221128115346_ObjectCache.cs @@ -0,0 +1,36 @@ +// +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + /// + public partial class ObjectCache : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "object_cache", + columns: table => new + { + Key = table.Column(type: "TEXT", nullable: false), + ExpireTime = table.Column(type: "TEXT", nullable: false), + Value = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_object_cache", x => x.Key); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "object_cache"); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs index 2c5153f624..71d263ea5f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs @@ -212,6 +212,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("game_accounts"); }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ExpireTime") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("object_cache"); + }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b => { b.Property("Key") diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Gacha/TypedWishSummary.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Gacha/TypedWishSummary.cs index 3e89c7e1b3..0c686c9d7d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Gacha/TypedWishSummary.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Gacha/TypedWishSummary.cs @@ -41,6 +41,14 @@ public string MinOrangePullFormatted /// public int LastOrangePull { get; set; } + /// + /// 据上个五星抽数格式化 + /// + public string LastOrangePullFormatted + { + get => $"已垫 {LastOrangePull} 抽"; + } + /// /// 五星保底阈值 /// @@ -51,6 +59,14 @@ public string MinOrangePullFormatted /// public int LastPurplePull { get; set; } + /// + /// 据上个四星抽数格式化 + /// + public string LastPurplePullFormatted + { + get => $"已垫 {LastPurplePull} 抽"; + } + /// /// 四星保底阈值 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/ComplexAvatarCollocation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/ComplexAvatarCollocation.cs index dfed640465..85c08c6cea 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/ComplexAvatarCollocation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/ComplexAvatarCollocation.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Primitive; namespace Snap.Hutao.Model.Binding.Hutao; @@ -9,18 +8,8 @@ namespace Snap.Hutao.Model.Binding.Hutao; /// /// 角色搭配 /// -public class ComplexAvatarCollocation : ComplexAvatar +public class ComplexAvatarCollocation { - /// - /// 构造一个新的角色搭配 - /// - /// 角色 - /// 比率 - public ComplexAvatarCollocation(Avatar avatar) - : base(avatar, 0) - { - } - /// /// 角色Id /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/ComplexWeaponCollocation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/ComplexWeaponCollocation.cs new file mode 100644 index 0000000000..577a78aac9 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/ComplexWeaponCollocation.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Primitive; + +namespace Snap.Hutao.Model.Binding.Hutao; + +/// +/// 武器搭配 +/// +public class ComplexWeaponCollocation +{ + /// + /// 武器Id + /// + public WeaponId WeaponId { get; set; } + + /// + /// 角色 + /// + public List Avatars { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs index 9349c911af..59345dbbc6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs @@ -76,10 +76,7 @@ public Cookie? Ltoken public Cookie? Stoken { get => inner.Stoken; - set - { - inner.Stoken = value; - } + set => inner.Stoken = value; } /// @@ -96,8 +93,14 @@ public Cookie? Stoken internal static async Task ResumeAsync(EntityUser inner, CancellationToken token = default) { User user = new(inner); - bool successful = await user.InitializeCoreAsync(token).ConfigureAwait(false); - return successful ? user : null; + bool isOk = await user.InitializeCoreAsync(token).ConfigureAwait(false); + + if (!isOk) + { + user.UserInfo = new UserInfo() { Nickname = "网络异常" }; + } + + return user; } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/ObjectCacheEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/ObjectCacheEntry.cs new file mode 100644 index 0000000000..5235bd90a5 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/ObjectCacheEntry.cs @@ -0,0 +1,30 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Snap.Hutao.Model.Entity; + +/// +/// 数据库对象缓存 +/// +[Table("object_cache")] +public class ObjectCacheEntry +{ + /// + /// 主键 + /// + [Key] + public string Key { get; set; } = default!; + + /// + /// 过期时间 + /// + public DateTimeOffset ExpireTime { get; set; } + + /// + /// 值字符串 + /// + public string? Value { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs index 18d3e8bb07..f319f4c2cf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs @@ -92,4 +92,4 @@ public SettingEntry(string key, string? value) /// 值 /// public string? Value { get; set; } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/EquipIconConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/EquipIconConverter.cs index 295ee201eb..e5f7caf7e1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/EquipIconConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/EquipIconConverter.cs @@ -27,4 +27,4 @@ public override Uri Convert(string from) { return IconNameToUri(from); } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/GachaEquipIconConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/GachaEquipIconConverter.cs new file mode 100644 index 0000000000..f65fb0a585 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/GachaEquipIconConverter.cs @@ -0,0 +1,31 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Control; + +namespace Snap.Hutao.Model.Metadata.Converter; + +/// +/// 武器祈愿图片转换器 +/// +internal class GachaEquipIconConverter : ValueConverterBase +{ + private const string BaseUrl = "https://static.snapgenshin.com/GachaEquipIcon/UI_Gacha_{0}.png"; + + /// + /// 名称转Uri + /// + /// 名称 + /// 链接 + public static Uri IconNameToUri(string name) + { + name = name["UI_".Length..]; + return new Uri(string.Format(BaseUrl, name)); + } + + /// + public override Uri Convert(string from) + { + return IconNameToUri(from); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/AffixInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/AffixInfo.cs index df52d98e52..3116fba55c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/AffixInfo.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/AffixInfo.cs @@ -17,5 +17,5 @@ public class AffixInfo /// 各个等级的描述 /// 0-4 /// - public List> Descriptions { get; set; } = default!; + public List Descriptions { get; set; } = default!; } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/LevelDescription.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/LevelDescription.cs index 5924230d42..965042af59 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/LevelDescription.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/LevelDescription.cs @@ -7,12 +7,18 @@ namespace Snap.Hutao.Model.Metadata.Weapon; /// 等级与描述 /// /// 等级的类型 -public class LevelDescription +public class LevelDescription { /// /// 等级 /// - public TLevel Level { get; set; } = default!; + public int Level { get; set; } = default!; + + /// + /// 格式化的等级 + /// + [JsonIgnore] + public string LevelFormatted { get => $"精炼 {Level + 1} 阶"; } /// /// 描述 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs index f69b3e6f31..5fd2b8f48f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs @@ -3,6 +3,7 @@ using Snap.Hutao.Model.Binding.Gacha; using Snap.Hutao.Model.Binding.Gacha.Abstraction; +using Snap.Hutao.Model.Binding.Hutao; using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Metadata.Abstraction; using Snap.Hutao.Model.Metadata.Converter; @@ -60,6 +61,12 @@ public class Weapon : IStatisticsItemSource, ISummaryItemSource, INameQuality /// public AffixInfo? Affix { get; set; } = default!; + /// + /// [非元数据] 搭配数据 + /// + [JsonIgnore] + public ComplexWeaponCollocation? Collocation { get; set; } + /// [JsonIgnore] public ItemQuality Quality diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/IdentityConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/IdentityConverter.cs index 7f6e9d3680..babd8c7ebc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/IdentityConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/IdentityConverter.cs @@ -21,7 +21,6 @@ public override TWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, Jso /// public override void Write(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options) { - writer.WriteNumberValue(CastTo.From(value)); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest index 5dcd809687..4b3d87a52c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest +++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.2.6.0" /> 胡桃 diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_GachaShowPanel_Bg_Weapon.png b/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_GachaShowPanel_Bg_Weapon.png new file mode 100644 index 0000000000..5b8cf59655 Binary files /dev/null and b/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_GachaShowPanel_Bg_Weapon.png differ diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs index eb2bd94f50..d48a0d8195 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.Extensions.DependencyInjection; using Snap.Hutao.Model.Binding.Hutao; using Snap.Hutao.Model.Metadata; using Snap.Hutao.Model.Metadata.Avatar; @@ -17,20 +18,24 @@ namespace Snap.Hutao.Service.Hutao; [Injection(InjectAs.Singleton, typeof(IHutaoCache))] internal class HutaoCache : IHutaoCache { - private readonly IHutaoService hutaoService; private readonly IMetadataService metadataService; + private readonly IServiceScopeFactory scopeFactory; private Dictionary? idAvatarExtendedMap; + private bool isDatabaseViewModelInitialized; + private bool isWikiAvatarViewModelInitiaized; + private bool isWikiWeaponViewModelInitiaized; + /// /// 构造一个新的胡桃 API 缓存 /// - /// 胡桃服务 /// 元数据服务 - public HutaoCache(IHutaoService hutaoService, IMetadataService metadataService) + /// 范围工厂 + public HutaoCache(IMetadataService metadataService, IServiceScopeFactory scopeFactory) { - this.hutaoService = hutaoService; this.metadataService = metadataService; + this.scopeFactory = scopeFactory; } /// @@ -51,9 +56,17 @@ public HutaoCache(IHutaoService hutaoService, IMetadataService metadataService) /// public List? AvatarCollocations { get; set; } + /// + public List? WeaponCollocations { get; set; } + /// public async ValueTask InitializeForDatabaseViewModelAsync() { + if (isDatabaseViewModelInitialized) + { + return true; + } + if (await metadataService.InitializeAsync().ConfigureAwait(false)) { Dictionary idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false); @@ -72,6 +85,7 @@ await Task.WhenAll( ovewviewTask) .ConfigureAwait(false); + isDatabaseViewModelInitialized = true; return true; } @@ -81,25 +95,65 @@ await Task.WhenAll( /// public async ValueTask InitializeForWikiAvatarViewModelAsync() { + if (isWikiAvatarViewModelInitiaized) + { + return true; + } + if (await metadataService.InitializeAsync().ConfigureAwait(false)) { Dictionary idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false); Dictionary idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false); Dictionary idReliquarySetMap = await metadataService.GetEquipAffixIdToReliquarySetMapAsync().ConfigureAwait(false); - // AvatarCollocation - List avatarCollocationsRaw = await hutaoService.GetAvatarCollocationsAsync().ConfigureAwait(false); - AvatarCollocations = avatarCollocationsRaw.Select(co => + List avatarCollocationsRaw; + using (IServiceScope scope = scopeFactory.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + avatarCollocationsRaw = await hutaoService.GetAvatarCollocationsAsync().ConfigureAwait(false); + } + + AvatarCollocations = avatarCollocationsRaw.Select(co => new ComplexAvatarCollocation() + { + AvatarId = co.AvatarId, + Avatars = co.Avatars.Select(a => new ComplexAvatar(idAvatarMap[a.Item], a.Rate)).ToList(), + Weapons = co.Weapons.Select(w => new ComplexWeapon(idWeaponMap[w.Item], w.Rate)).ToList(), + ReliquarySets = co.Reliquaries.Select(r => new ComplexReliquarySet(r, idReliquarySetMap)).ToList(), + }).ToList(); + + isWikiAvatarViewModelInitiaized = true; + return true; + } + + return false; + } + + /// + public async ValueTask InitializeForWikiWeaponViewModelAsync() + { + if (isWikiWeaponViewModelInitiaized) + { + return true; + } + + if (await metadataService.InitializeAsync().ConfigureAwait(false)) + { + Dictionary idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false); + + List weaponCollocationsRaw; + using (IServiceScope scope = scopeFactory.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + weaponCollocationsRaw = await hutaoService.GetWeaponCollocationsAsync().ConfigureAwait(false); + } + + WeaponCollocations = weaponCollocationsRaw.Select(co => new ComplexWeaponCollocation() { - return new ComplexAvatarCollocation(idAvatarMap[co.AvatarId]) - { - AvatarId = co.AvatarId, - Avatars = co.Avatars.Select(a => new ComplexAvatar(idAvatarMap[a.Item], a.Rate)).ToList(), - Weapons = co.Weapons.Select(w => new ComplexWeapon(idWeaponMap[w.Item], w.Rate)).ToList(), - ReliquarySets = co.Reliquaries.Select(r => new ComplexReliquarySet(r, idReliquarySetMap)).ToList(), - }; + WeaponId = co.WeaponId, + Avatars = co.Avatars.Select(a => new ComplexAvatar(idAvatarMap[a.Item], a.Rate)).ToList(), }).ToList(); + isWikiWeaponViewModelInitiaized = true; return true; } @@ -123,7 +177,13 @@ private async ValueTask> GetIdAvatarMapExtendedAsyn private async Task AvatarAppearanceRankAsync(Dictionary idAvatarMap) { - List avatarAppearanceRanksRaw = await hutaoService.GetAvatarAppearanceRanksAsync().ConfigureAwait(false); + List avatarAppearanceRanksRaw; + using (IServiceScope scope = scopeFactory.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + avatarAppearanceRanksRaw = await hutaoService.GetAvatarAppearanceRanksAsync().ConfigureAwait(false); + } + AvatarAppearanceRanks = avatarAppearanceRanksRaw.OrderByDescending(r => r.Floor).Select(rank => new ComplexAvatarRank { Floor = $"第 {rank.Floor} 层", @@ -133,7 +193,13 @@ private async Task AvatarAppearanceRankAsync(Dictionary idAvat private async Task AvatarUsageRanksAsync(Dictionary idAvatarMap) { - List avatarUsageRanksRaw = await hutaoService.GetAvatarUsageRanksAsync().ConfigureAwait(false); + List avatarUsageRanksRaw; + using (IServiceScope scope = scopeFactory.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + avatarUsageRanksRaw = await hutaoService.GetAvatarUsageRanksAsync().ConfigureAwait(false); + } + AvatarUsageRanks = avatarUsageRanksRaw.OrderByDescending(r => r.Floor).Select(rank => new ComplexAvatarRank { Floor = $"第 {rank.Floor} 层", @@ -143,7 +209,13 @@ private async Task AvatarUsageRanksAsync(Dictionary idAvatarMa private async Task AvatarConstellationInfosAsync(Dictionary idAvatarMap) { - List avatarConstellationInfosRaw = await hutaoService.GetAvatarConstellationInfosAsync().ConfigureAwait(false); + List avatarConstellationInfosRaw; + using (IServiceScope scope = scopeFactory.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + avatarConstellationInfosRaw = await hutaoService.GetAvatarConstellationInfosAsync().ConfigureAwait(false); + } + AvatarConstellationInfos = avatarConstellationInfosRaw.OrderBy(i => i.HoldingRate).Select(info => { return new ComplexAvatarConstellationInfo(idAvatarMap[info.AvatarId], info.HoldingRate, info.Constellations.Select(x => x.Rate)); @@ -152,12 +224,22 @@ private async Task AvatarConstellationInfosAsync(Dictionary id private async Task TeamAppearancesAsync(Dictionary idAvatarMap) { - List teamAppearancesRaw = await hutaoService.GetTeamAppearancesAsync().ConfigureAwait(false); + List teamAppearancesRaw; + using (IServiceScope scope = scopeFactory.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + teamAppearancesRaw = await hutaoService.GetTeamAppearancesAsync().ConfigureAwait(false); + } + TeamAppearances = teamAppearancesRaw.OrderByDescending(t => t.Floor).Select(team => new ComplexTeamRank(team, idAvatarMap)).ToList(); } private async Task OverviewAsync() { - Overview = await hutaoService.GetOverviewAsync().ConfigureAwait(false); + using (IServiceScope scope = scopeFactory.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + Overview = await hutaoService.GetOverviewAsync().ConfigureAwait(false); + } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs index 7f30bbc40f..5d86135fcf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs @@ -2,6 +2,9 @@ // Licensed under the MIT license. using Microsoft.Extensions.Caching.Memory; +using Snap.Hutao.Context.Database; +using Snap.Hutao.Core.Database; +using Snap.Hutao.Model.Entity; using Snap.Hutao.Web.Hutao; using Snap.Hutao.Web.Hutao.Model; @@ -10,21 +13,27 @@ namespace Snap.Hutao.Service.Hutao; /// /// 胡桃 API 服务 /// -[Injection(InjectAs.Transient, typeof(IHutaoService))] +[Injection(InjectAs.Scoped, typeof(IHutaoService))] internal class HutaoService : IHutaoService { private readonly HomaClient homaClient; private readonly IMemoryCache memoryCache; + private readonly AppDbContext appDbContext; + private readonly JsonSerializerOptions options; /// /// 构造一个新的胡桃 API 服务 /// /// 胡桃 API 客户端 /// 内存缓存 - public HutaoService(HomaClient homaClient, IMemoryCache memoryCache) + /// 数据库上下文 + /// Json序列化选项 + public HutaoService(HomaClient homaClient, IMemoryCache memoryCache, AppDbContext appDbContext, JsonSerializerOptions options) { this.homaClient = homaClient; this.memoryCache = memoryCache; + this.appDbContext = appDbContext; + this.options = options; } /// @@ -57,6 +66,12 @@ public ValueTask> GetAvatarCollocationsAsync() return FromCacheOrWebAsync(nameof(AvatarCollocation), homaClient.GetAvatarCollocationsAsync); } + /// + public ValueTask> GetWeaponCollocationsAsync() + { + return FromCacheOrWebAsync(nameof(WeaponCollocation), homaClient.GetWeaponCollocationsAsync); + } + /// public ValueTask> GetTeamAppearancesAsync() { @@ -71,7 +86,27 @@ private async ValueTask FromCacheOrWebAsync(string typeName, Func e.Key == key) is ObjectCacheEntry entry) + { + if (entry.ExpireTime > DateTimeOffset.Now) + { + T value = JsonSerializer.Deserialize(entry.Value!, options)!; + return memoryCache.Set(key, value, TimeSpan.FromMinutes(30)); + } + else + { + appDbContext.ObjectCache.RemoveAndSave(entry); + } + } + T web = await taskFunc(default).ConfigureAwait(false); + appDbContext.ObjectCache.AddAndSave(new() + { + Key = key, + ExpireTime = DateTimeOffset.Now.AddHours(4), + Value = JsonSerializer.Serialize(web, options), + }); + return memoryCache.Set(key, web, TimeSpan.FromMinutes(30)); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoCache.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoCache.cs index e4bf0bd04a..fdb914a6f2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoCache.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoCache.cs @@ -41,15 +41,26 @@ internal interface IHutaoCache /// List? AvatarCollocations { get; set; } + /// + /// 武器搭配 + /// + List? WeaponCollocations { get; set; } + /// /// 为数据库视图模型初始化 /// - /// 任务 + /// 是否初始化完成 ValueTask InitializeForDatabaseViewModelAsync(); /// /// 为Wiki角色视图模型初始化 /// - /// 任务 + /// 是否初始化完成 ValueTask InitializeForWikiAvatarViewModelAsync(); + + /// + /// 为Wiki武器视图模型初始化 + /// + /// 是否初始化完成 + ValueTask InitializeForWikiWeaponViewModelAsync(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoService.cs index 1618541ea9..e0670a692e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoService.cs @@ -45,4 +45,10 @@ internal interface IHutaoService /// /// 队伍上场 ValueTask> GetTeamAppearancesAsync(); + + /// + /// 异步获取武器搭配 + /// + /// 武器搭配 + ValueTask> GetWeaponCollocationsAsync(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs index f684ad305f..58dd762041 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs @@ -3,8 +3,6 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; -using Microsoft.Xaml.Interactivity; -using Snap.Hutao.Control.Behavior; using Snap.Hutao.Service.Abstraction; namespace Snap.Hutao.Service; diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 4b5c281acc..26df4f20fd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -43,6 +43,7 @@ + @@ -66,6 +67,7 @@ + @@ -86,6 +88,7 @@ + @@ -110,6 +113,7 @@ + @@ -170,6 +174,16 @@ + + + MSBuild:Compile + + + + + MSBuild:Compile + + MSBuild:Compile diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/BottomTextControl.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/BottomTextControl.xaml index 70c10f11eb..7374ccc83b 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/BottomTextControl.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/BottomTextControl.xaml @@ -11,7 +11,7 @@ CornerRadius="{StaticResource CompatCornerRadius}" BorderBrush="{StaticResource CardStrokeColorDefault}" Background="{StaticResource CardBackgroundFillColorDefault}"> - + .Depend(nameof(Text), string.Empty, OnTextChanged); - private static readonly DependencyProperty TopContentProperty = Property.Depend(nameof(TopContent), default!, OnContentChanged2); + private static readonly DependencyProperty TopContentProperty = Property.Depend(nameof(TopContent), default!, OnContentChanged); + private static readonly DependencyProperty FillProperty = Property.Depend(nameof(Fill), default(Brush), OnFillChanged); /// /// 构造一个新的底部带有文本的控件 @@ -43,13 +45,27 @@ public string Text set => SetValue(TextProperty, value); } - private static void OnTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs dp) + /// + /// 填充 + /// + public Brush Fill + { + get => (Brush)GetValue(FillProperty); + set => SetValue(FillProperty, value); + } + + private static void OnTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + ((BottomTextControl)sender).TextHost.Text = (string)args.NewValue; + } + + private static void OnContentChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { - ((BottomTextControl)sender).TextHost.Text = (string)dp.NewValue; + ((BottomTextControl)sender).ContentHost.Content = args.NewValue; } - private static void OnContentChanged2(DependencyObject sender, DependencyPropertyChangedEventArgs dp) + private static void OnFillChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { - ((BottomTextControl)sender).ContentHost.Content = dp.NewValue; + ((BottomTextControl)sender).BackgroundStack.Background = (Brush)args.NewValue; } } diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml index de6d74cb36..954574906e 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml @@ -9,7 +9,7 @@ xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shcp="using:Snap.Hutao.Control.Panel" xmlns:shmbg="using:Snap.Hutao.Model.Binding.Gacha" - xmlns:shvc="using:Snap.Hutao.View.Converter" + xmlns:shvc="using:Snap.Hutao.View.Control" mc:Ignorable="d" d:DataContext="{d:DesignInstance shmbg:TypedWishSummary}"> @@ -18,8 +18,6 @@ - - @@ -67,47 +65,31 @@ - - - - - + + + + Height="40" Width="40"/>--> + + + + + + + @@ -157,61 +139,110 @@ - - - - - + - + - - - - + + + + + + + + + + + + BorderThickness="1" + CornerRadius="{StaticResource CompatCornerRadius}" + BorderBrush="{StaticResource CardStrokeColorDefault}" + Background="{StaticResource CardBackgroundFillColorDefault}"> + + + + + + + + + + + Style="{StaticResource CaptionTextBlockStyle}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml.cs index a72e9480c9..498203caac 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE in the project root for license information. +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml new file mode 100644 index 0000000000..f517a9a237 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml.cs new file mode 100644 index 0000000000..47ffa12cdc --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml.cs @@ -0,0 +1,65 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.Web.WebView2.Core; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Web.Bridge; +using Snap.Hutao.Web.Hoyolab; + +namespace Snap.Hutao.View.Dialog; + +/// +/// ʵʱ֤Ի +/// +public sealed partial class DailyNoteVerificationDialog : ContentDialog +{ + private readonly IServiceScope scope; + private readonly User user; + private readonly PlayerUid uid; + [SuppressMessage("", "IDE0052")] + private DailyNoteJsInterface? dailyNoteJsInterface; + + /// + /// һµʵʱ֤Ի + /// + /// + /// û + /// uid + public DailyNoteVerificationDialog(Window window, User user, PlayerUid uid) + { + InitializeComponent(); + XamlRoot = window.Content.XamlRoot; + this.user = user; + this.uid = uid; + scope = Ioc.Default.CreateScope(); + } + + private void OnGridLoaded(object sender, RoutedEventArgs e) + { + InitializeAsync().SafeForget(); + } + + private async Task InitializeAsync() + { + await WebView.EnsureCoreWebView2Async(); + CoreWebView2 coreWebView2 = WebView.CoreWebView2; + + coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent(); + dailyNoteJsInterface = new(coreWebView2, scope.ServiceProvider); + +#if DEBUG + coreWebView2.OpenDevToolsWindow(); +#endif + string query = $"?role_id={uid.Value}&server={uid.Region}"; + coreWebView2.Navigate($"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/daily/{query}"); + } + + private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) + { + dailyNoteJsInterface = null; + scope.Dispose(); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml index c8a028bf9d..88d24a60bf 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml @@ -65,6 +65,10 @@ shvh:NavHelper.NavigateTo="shvp:WikiAvatarPage" Icon="{shcm:BitmapIcon Source=ms-appx:///Resource/Icon/UI_BagTabIcon_Avatar.png}"/> + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml index 18116a9a87..c407bec28a 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml @@ -178,7 +178,7 @@ Text="对当前选中的账号进行签到"/> @@ -199,12 +199,12 @@ - + + Background="{StaticResource SystemFillColorCriticalBackgroundBrush}"> [JsonPropertyName("callback")] - public string Callback { get; set; } = default!; + public string? Callback { get; set; } } /// @@ -52,14 +52,14 @@ public class JsParam /// 回调的名称,调用 JavaScript:mhyWebBridge 时作为首个参数传入 /// [JsonPropertyName("callback")] - public string Callback { get; set; } = string.Empty; + public string? Callback { get; set; } public static implicit operator JsParam(JsParam jsParam) { return new JsParam() { Method = jsParam.Method, - Payload = jsParam.Payload.HasValue ? jsParam.Payload.Value.Deserialize() : default, + Payload = jsParam.Payload.HasValue ? jsParam.Payload.Value.Deserialize()! : default!, Callback = jsParam.Callback, }; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/SignInJsInterface.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/SignInJsInterface.cs index 6e4b04df77..a73b9c8e09 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/SignInJsInterface.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/SignInJsInterface.cs @@ -30,4 +30,4 @@ public override JsResult> GetHttpRequestHeader(JsPara }, }; } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Geetest/GeetestClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Geetest/GeetestClient.cs index 8507c345a0..0b067dd125 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Geetest/GeetestClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Geetest/GeetestClient.cs @@ -4,7 +4,6 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; using System.Net.Http; -using System.Text.RegularExpressions; namespace Snap.Hutao.Web.Geetest; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/IgnoreSetCookieAttribute.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/IgnoreSetCookieAttribute.cs index 66ef1a8b70..e93b7f5184 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/IgnoreSetCookieAttribute.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/IgnoreSetCookieAttribute.cs @@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hoyolab.Annotation; /// /// 指示相关的类忽略Http请求的Set-Cookie头 /// -[AttributeUsage(AttributeTargets.Class, AllowMultiple =false, Inherited =false)] +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] internal class IgnoreSetCookieAttribute : Attribute { } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/IgnoreSetCookieHandler.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/IgnoreSetCookieHandler.cs deleted file mode 100644 index 0f02bb9e29..0000000000 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/IgnoreSetCookieHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using System.Net.Http; - -namespace Snap.Hutao.Web.Hoyolab; - -/// -/// 忽略 Set-Cookie 头 -/// -[Injection(InjectAs.Transient)] -internal class IgnoreSetCookieHandler : DelegatingHandler -{ - /// - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - HttpResponseMessage message = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); - message.Headers.Remove("Set-Cookie"); - return message; - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs index 8ad89d92e5..2ceec60243 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs @@ -4,7 +4,6 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Model.Entity; using Snap.Hutao.Web.Hoyolab.Annotation; -using Snap.Hutao.Web.Request; using Snap.Hutao.Web.Response; using System.Net.Http; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/NameToken.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/NameToken.cs index 45c8c17519..710b55e7ab 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/NameToken.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/NameToken.cs @@ -1,14 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; -using Snap.Hutao.Model.Entity; -using Snap.Hutao.Web.Hoyolab.Annotation; -using Snap.Hutao.Web.Hoyolab.DynamicSecret; -using Snap.Hutao.Web.Hoyolab.Takumi.Binding; -using Snap.Hutao.Web.Response; -using System.Net.Http; - namespace Snap.Hutao.Web.Hoyolab.Takumi.Auth; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs index b4671b3660..353d0a71da 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs @@ -1,12 +1,10 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.Extensions.DependencyInjection; using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Extension; using Snap.Hutao.Model.Entity; using Snap.Hutao.Service.Abstraction; -using Snap.Hutao.Web.Geetest; using Snap.Hutao.Web.Hoyolab.Annotation; using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/VerificationRegistration.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/VerificationRegistration.cs index 8c6ea12386..22e1978131 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/VerificationRegistration.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/VerificationRegistration.cs @@ -1,14 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; -using Snap.Hutao.Model.Entity; -using Snap.Hutao.Web.Hoyolab.Annotation; -using Snap.Hutao.Web.Hoyolab.DynamicSecret; -using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Widget; -using Snap.Hutao.Web.Response; -using System.Net.Http; - namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/VerificationResult.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/VerificationResult.cs index 0255fd0593..9b1eb83ca4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/VerificationResult.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/VerificationResult.cs @@ -1,14 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; -using Snap.Hutao.Model.Entity; -using Snap.Hutao.Web.Hoyolab.Annotation; -using Snap.Hutao.Web.Hoyolab.DynamicSecret; -using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Widget; -using Snap.Hutao.Web.Response; -using System.Net.Http; - namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs index faf9349a34..033b856b74 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs @@ -137,6 +137,21 @@ public async Task> GetAvatarCollocationsAsync(Cancellati return EnumerableExtension.EmptyIfNull(resp?.Data); } + /// + /// 异步获取武器搭配 + /// GET /Statistics/Avatar/AvatarCollocation + /// + /// 取消令牌 + /// 角色/武器/圣遗物搭配 + public async Task> GetWeaponCollocationsAsync(CancellationToken token = default) + { + Response>? resp = await httpClient + .GetFromJsonAsync>>($"{HutaoAPI}/Statistics/Weapon/WeaponCollocation", token) + .ConfigureAwait(false); + + return EnumerableExtension.EmptyIfNull(resp?.Data); + } + /// /// 异步获取角色命座信息 /// GET /Statistics/Avatar/HoldingRate diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/AvatarBuild.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/AvatarBuild.cs index f5fb25fe8c..514e5267fb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/AvatarBuild.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/AvatarBuild.cs @@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hutao.Model; /// -/// 角色相关解构 +/// 角色相关构筑 /// public abstract class AvatarBuild { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/ReliquarySet.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/ReliquarySet.cs index b2ed9805c5..4c320640d7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/ReliquarySet.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/ReliquarySet.cs @@ -35,6 +35,6 @@ public ReliquarySet(string set) /// public override string ToString() { - return $"{EquipAffixId}-{Count}"; + return $"{(int)EquipAffixId}-{Count}"; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/WeaponBuild.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/WeaponBuild.cs new file mode 100644 index 0000000000..785643059d --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/WeaponBuild.cs @@ -0,0 +1,17 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Primitive; + +namespace Snap.Hutao.Web.Hutao.Model; + +/// +/// 武器相关构筑 +/// +public abstract class WeaponBuild +{ + /// + /// 角色Id + /// + public WeaponId WeaponId { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/WeaponCollocation.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/WeaponCollocation.cs new file mode 100644 index 0000000000..2099d94d69 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/WeaponCollocation.cs @@ -0,0 +1,15 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hutao.Model; + +/// +/// 武器搭配 +/// +public class WeaponCollocation : WeaponBuild +{ + /// + /// 其他角色 + /// + public List> Avatars { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs b/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs index 5315753a7f..cca1d8f943 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs @@ -43,6 +43,11 @@ public enum KnownReturnCode : int /// RET_NEED_AIGIS = -3101, + /// + /// 参数不合法 + /// + InvalidParameter = -3001, + /// /// 请在米游社App内打开~ ///