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}">