mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
add hutao api client impl
This commit is contained in:
@@ -91,6 +91,9 @@ dotnet_diagnostic.SA1623.severity = none
|
|||||||
# SA1636: File header copyright text should match
|
# SA1636: File header copyright text should match
|
||||||
dotnet_diagnostic.SA1636.severity = none
|
dotnet_diagnostic.SA1636.severity = none
|
||||||
|
|
||||||
|
# SA1414: Tuple types in signatures should have element names
|
||||||
|
dotnet_diagnostic.SA1414.severity = none
|
||||||
|
|
||||||
[*.vb]
|
[*.vb]
|
||||||
#### 命名样式 ####
|
#### 命名样式 ####
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
IServiceProvider services = new ServiceCollection()
|
IServiceProvider services = new ServiceCollection()
|
||||||
.AddLogging(builder => builder.AddDebug())
|
.AddLogging(builder => builder.AddDebug())
|
||||||
|
.AddDatebase()
|
||||||
.AddHttpClients()
|
.AddHttpClients()
|
||||||
.AddDefaultJsonSerializerOptions()
|
.AddDefaultJsonSerializerOptions()
|
||||||
.AddInjections(typeof(App))
|
.AddInjections(typeof(App))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
|
|
||||||
namespace Snap.Hutao.Context.Database;
|
namespace Snap.Hutao.Context.Database;
|
||||||
|
|
||||||
@@ -18,4 +19,9 @@ internal class AppDbContext : DbContext
|
|||||||
: base(options)
|
: base(options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置项
|
||||||
|
/// </summary>
|
||||||
|
public DbSet<SettingEntry> Settings { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using CommunityToolkit.WinUI.UI;
|
||||||
|
using CommunityToolkit.WinUI.UI.Animations;
|
||||||
|
using Microsoft.UI.Composition;
|
||||||
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Control.Animation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图片放大动画
|
||||||
|
/// </summary>
|
||||||
|
internal class ImageZoomInAnimation : ImplicitAnimation<string, Vector3>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的图片放大动画
|
||||||
|
/// </summary>
|
||||||
|
public ImageZoomInAnimation()
|
||||||
|
{
|
||||||
|
EasingMode = Microsoft.UI.Xaml.Media.Animation.EasingMode.EaseOut;
|
||||||
|
EasingType = CommunityToolkit.WinUI.UI.Animations.EasingType.Circle;
|
||||||
|
To = "1.1";
|
||||||
|
Duration = TimeSpan.FromSeconds(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override string ExplicitTarget => nameof(Visual.Scale);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override (Vector3?, Vector3?) GetParsedValues()
|
||||||
|
{
|
||||||
|
return (To?.ToVector3(), From?.ToVector3());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using CommunityToolkit.WinUI.UI;
|
||||||
|
using CommunityToolkit.WinUI.UI.Animations;
|
||||||
|
using Microsoft.UI.Composition;
|
||||||
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Control.Animation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图片缩小动画
|
||||||
|
/// </summary>
|
||||||
|
internal class ImageZoomOutAnimation : ImplicitAnimation<string, Vector3>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的图片缩小动画
|
||||||
|
/// </summary>
|
||||||
|
public ImageZoomOutAnimation()
|
||||||
|
{
|
||||||
|
EasingMode = Microsoft.UI.Xaml.Media.Animation.EasingMode.EaseOut;
|
||||||
|
EasingType = CommunityToolkit.WinUI.UI.Animations.EasingType.Circle;
|
||||||
|
To = "1";
|
||||||
|
Duration = TimeSpan.FromSeconds(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override string ExplicitTarget => nameof(Visual.Scale);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override (Vector3?, Vector3?) GetParsedValues()
|
||||||
|
{
|
||||||
|
return (To?.ToVector3(), From?.ToVector3());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,16 +40,22 @@ internal class AutoHeightBehavior : BehaviorBase<FrameworkElement>
|
|||||||
protected override void OnAssociatedObjectLoaded()
|
protected override void OnAssociatedObjectLoaded()
|
||||||
{
|
{
|
||||||
AssociatedObject.SizeChanged += OnSizeChanged;
|
AssociatedObject.SizeChanged += OnSizeChanged;
|
||||||
|
UpdateElementHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void OnAssociatedObjectUnloaded()
|
protected override void OnDetaching()
|
||||||
{
|
{
|
||||||
AssociatedObject.SizeChanged -= OnSizeChanged;
|
AssociatedObject.SizeChanged -= OnSizeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
{
|
{
|
||||||
AssociatedObject.Height = (double)((FrameworkElement)sender).ActualWidth * (TargetHeight / TargetWidth);
|
UpdateElementHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateElementHeight()
|
||||||
|
{
|
||||||
|
AssociatedObject.Height = (double)AssociatedObject.ActualWidth * (TargetHeight / TargetWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Abstraction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 可异步初始化
|
||||||
|
/// </summary>
|
||||||
|
internal interface IAsyncInitializable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已经初始化完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsInitialized { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步初始化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
|
/// <returns>初始化任务</returns>
|
||||||
|
Task<bool> InitializeAsync(CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -41,4 +41,4 @@ public static class Browser
|
|||||||
failAction?.Invoke(ex);
|
failAction?.Invoke(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/Snap.Hutao/Snap.Hutao/Core/Converting/Md5Convert.cs
Normal file
25
src/Snap.Hutao/Snap.Hutao/Core/Converting/Md5Convert.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Converting;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 支持Md5转换
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class Md5Convert
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取字符串的MD5计算结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">源字符串</param>
|
||||||
|
/// <returns>计算的结果</returns>
|
||||||
|
public static string ToHexString(string source)
|
||||||
|
{
|
||||||
|
byte[] bytes = Encoding.UTF8.GetBytes(source);
|
||||||
|
byte[] hash = MD5.HashData(bytes);
|
||||||
|
return Convert.ToHexString(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,8 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using Snap.Hutao.Core.Converting;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using Windows.ApplicationModel;
|
using Windows.ApplicationModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
namespace Snap.Hutao.Core;
|
||||||
@@ -41,8 +40,6 @@ internal static class CoreEnvironment
|
|||||||
{
|
{
|
||||||
string userName = Environment.UserName;
|
string userName = Environment.UserName;
|
||||||
object? machineGuid = Registry.GetValue(CryptographyKey, MachineGuidValue, userName);
|
object? machineGuid = Registry.GetValue(CryptographyKey, MachineGuidValue, userName);
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes($"{userName}{machineGuid}");
|
return Md5Convert.ToHexString($"{userName}{machineGuid}");
|
||||||
byte[] hash = MD5.HashData(bytes);
|
|
||||||
return Convert.ToHexString(hash);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Http Json 处理
|
|
||||||
/// </summary>
|
|
||||||
public class HttpJson
|
|
||||||
{
|
|
||||||
private readonly Json json;
|
|
||||||
private readonly HttpClient httpClient;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化一个新的 Http Json 处理 实例
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="json">Json 处理器</param>
|
|
||||||
/// <param name="httpClient">http 客户端</param>
|
|
||||||
public HttpJson(Json json, HttpClient httpClient)
|
|
||||||
{
|
|
||||||
this.json = json;
|
|
||||||
this.httpClient = httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从网站上下载json并转换为对象
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">对象的类型</typeparam>
|
|
||||||
/// <param name="url">链接</param>
|
|
||||||
/// <param name="cancellationToken">取消令牌</param>
|
|
||||||
/// <returns>Json字符串中的反序列化对象, 如果反序列化失败会抛出异常</returns>
|
|
||||||
public async Task<T?> FromWebsiteAsync<T>(string url, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
string response = await httpClient.GetStringAsync(url, cancellationToken);
|
|
||||||
return json.ToObject<T>(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Json操作
|
|
||||||
/// </summary>
|
|
||||||
[Injection(InjectAs.Transient)]
|
|
||||||
public class Json
|
|
||||||
{
|
|
||||||
private readonly ILogger logger;
|
|
||||||
|
|
||||||
private readonly JsonSerializerSettings jsonSerializerSettings = new()
|
|
||||||
{
|
|
||||||
DateFormatString = "yyyy'-'MM'-'dd' 'HH':'mm':'ss.FFFFFFFK",
|
|
||||||
Formatting = Formatting.Indented,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化一个新的 Json操作 实例
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">日志器</param>
|
|
||||||
public Json(ILogger<Json> logger)
|
|
||||||
{
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将JSON反序列化为指定的.NET类型
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">要反序列化的对象的类型</typeparam>
|
|
||||||
/// <param name="value">要反序列化的JSON</param>
|
|
||||||
/// <returns>Json字符串中的反序列化对象, 如果反序列化失败会抛出异常</returns>
|
|
||||||
public T? ToObject<T>(string value)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject<T>(value);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.LogError("反序列化Json时遇到问题:{ex}", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将JSON反序列化为指定的.NET类型
|
|
||||||
/// 若为null则返回一个新建的实例
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">指定的类型</typeparam>
|
|
||||||
/// <param name="value">字符串</param>
|
|
||||||
/// <returns>Json字符串中的反序列化对象, 如果反序列化失败会抛出异常</returns>
|
|
||||||
public T ToObjectOrNew<T>(string value)
|
|
||||||
where T : new()
|
|
||||||
{
|
|
||||||
return ToObject<T>(value) ?? new T();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将指定的对象序列化为JSON字符串
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">要序列化的对象</param>
|
|
||||||
/// <returns>对象的JSON字符串表示形式</returns>
|
|
||||||
public string Stringify(object? value)
|
|
||||||
{
|
|
||||||
return JsonConvert.SerializeObject(value, jsonSerializerSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 使用 <see cref="FileMode.Open"/>, <see cref="FileAccess.Read"/> 和 <see cref="FileShare.Read"/> 从文件中读取后转化为实体类
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">要反序列化的对象的类型</typeparam>
|
|
||||||
/// <param name="fileName">存放JSON数据的文件路径</param>
|
|
||||||
/// <returns>JSON字符串中的反序列化对象, 如果反序列化失败则抛出异常,若文件不存在则返回 <see langword="null"/></returns>
|
|
||||||
public T? FromFile<T>(string fileName)
|
|
||||||
{
|
|
||||||
if (File.Exists(fileName))
|
|
||||||
{
|
|
||||||
// FileShare.Read is important to read some file
|
|
||||||
using (StreamReader sr = new(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)))
|
|
||||||
{
|
|
||||||
return ToObject<T>(sr.ReadToEnd());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 使用 <see cref="FileMode.Open"/>, <see cref="FileAccess.Read"/> 和 <see cref="FileShare.Read"/> 从文件中读取后转化为实体类
|
|
||||||
/// 若为null则返回一个新建的实例
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">要反序列化的对象的类型</typeparam>
|
|
||||||
/// <param name="fileName">存放JSON数据的文件路径</param>
|
|
||||||
/// <returns>JSON字符串中的反序列化对象</returns>
|
|
||||||
public T FromFileOrNew<T>(string fileName)
|
|
||||||
where T : new()
|
|
||||||
{
|
|
||||||
return FromFile<T>(fileName) ?? new T();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从文件中读取后转化为实体类
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">要反序列化的对象的类型</typeparam>
|
|
||||||
/// <param name="file">存放JSON数据的文件</param>
|
|
||||||
/// <returns>JSON字符串中的反序列化对象</returns>
|
|
||||||
public T? FromFile<T>(FileInfo file)
|
|
||||||
{
|
|
||||||
using (StreamReader sr = file.OpenText())
|
|
||||||
{
|
|
||||||
return ToObject<T>(sr.ReadToEnd());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将对象保存到文件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName">文件名称</param>
|
|
||||||
/// <param name="value">对象</param>
|
|
||||||
public void ToFile(string fileName, object? value)
|
|
||||||
{
|
|
||||||
File.WriteAllText(fileName, Stringify(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Json.Converter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 逗号分隔列表转换器
|
||||||
|
/// </summary>
|
||||||
|
internal class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEnumerable<int>>
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override IEnumerable<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
string? team = reader.GetString();
|
||||||
|
IEnumerable<int>? ids = team?.Split(',').Select(x => int.Parse(x));
|
||||||
|
return ids ?? Enumerable.Empty<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Write(Utf8JsonWriter writer, IEnumerable<int> value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(string.Join(',', value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 简单的实现了 <see cref="INotifyPropertyChanged"/> 接口
|
|
||||||
/// </summary>
|
|
||||||
public class Observable : INotifyPropertyChanged
|
|
||||||
{
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置字段的值
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">字段类型</typeparam>
|
|
||||||
/// <param name="storage">现有值</param>
|
|
||||||
/// <param name="value">新的值</param>
|
|
||||||
/// <param name="propertyName">属性名称</param>
|
|
||||||
protected void Set<T>([NotNullIfNotNull("value")] ref T storage, T value, [CallerMemberName] string propertyName = default!)
|
|
||||||
{
|
|
||||||
if (Equals(storage, value))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
storage = value;
|
|
||||||
OnPropertyChanged(propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发 <see cref="PropertyChanged"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="propertyName">属性名称</param>
|
|
||||||
protected void OnPropertyChanged(string propertyName)
|
|
||||||
{
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -68,7 +68,7 @@ internal static class LocalSetting
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Set(key, defaultValue);
|
SetValueType(key, defaultValue);
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Snap.Hutao.Core.Validation;
|
|||||||
public static class Must
|
public static class Must
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Throws an exception if the specified parameter's value is null.
|
/// Throws an <see cref="ArgumentNullException"/> if the specified parameter's value is null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the parameter.</typeparam>
|
/// <typeparam name="T">The type of the parameter.</typeparam>
|
||||||
/// <param name="value">The value of the argument.</param>
|
/// <param name="value">The value of the argument.</param>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Core;
|
|||||||
/// 检测 WebView2运行时 是否存在
|
/// 检测 WebView2运行时 是否存在
|
||||||
/// 不再使用注册表检查方式
|
/// 不再使用注册表检查方式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class WebView2Helper
|
internal static class WebView2Helper
|
||||||
{
|
{
|
||||||
private static bool hasEverDetected = false;
|
private static bool hasEverDetected = false;
|
||||||
private static bool isSupported = false;
|
private static bool isSupported = false;
|
||||||
|
|||||||
69
src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtensions.cs
Normal file
69
src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="IEnumerable{T}"/> 扩展
|
||||||
|
/// </summary>
|
||||||
|
public static class EnumerableExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将二维可枚举对象一维化
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource">源类型</typeparam>
|
||||||
|
/// <param name="source">源</param>
|
||||||
|
/// <returns>扁平的对象</returns>
|
||||||
|
public static IEnumerable<TSource> Flatten<TSource>(this IEnumerable<IEnumerable<TSource>> source)
|
||||||
|
{
|
||||||
|
return source.SelectMany(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计数
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource">源类型</typeparam>
|
||||||
|
/// <typeparam name="TKey">计数的键类型</typeparam>
|
||||||
|
/// <param name="source">源</param>
|
||||||
|
/// <param name="keySelector">键选择器</param>
|
||||||
|
/// <returns>计数表</returns>
|
||||||
|
public static IEnumerable<KeyValuePair<TKey, int>> CountBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource,TKey> keySelector)
|
||||||
|
where TKey : notnull, IEquatable<TKey>
|
||||||
|
{
|
||||||
|
CounterInt32<TKey> counter = new();
|
||||||
|
foreach (TSource item in source)
|
||||||
|
{
|
||||||
|
counter.Increase(keySelector(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表示一个对 <see cref="TItem"/> 类型的计数器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TItem">待计数的类型</typeparam>
|
||||||
|
private class CounterInt32<TItem> : Dictionary<TItem, int>
|
||||||
|
where TItem : notnull, IEquatable<TItem>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 增加计数器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">物品</param>
|
||||||
|
public void Increase(TItem? item)
|
||||||
|
{
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
if (!ContainsKey(item))
|
||||||
|
{
|
||||||
|
this[item] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this[item] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
global using CommunityToolkit.Mvvm.DependencyInjection;
|
global using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
global using Microsoft;
|
||||||
global using Microsoft.Extensions.Logging;
|
global using Microsoft.Extensions.Logging;
|
||||||
global using Snap.Hutao.Core.DependencyInjection;
|
global using Snap.Hutao.Core.DependencyInjection;
|
||||||
global using Snap.Hutao.Core.DependencyInjection.Annotation;
|
global using Snap.Hutao.Core.DependencyInjection.Annotation;
|
||||||
|
|||||||
@@ -76,11 +76,7 @@ internal static class IocConfiguration
|
|||||||
|
|
||||||
bool shouldMigrate = false;
|
bool shouldMigrate = false;
|
||||||
|
|
||||||
if (!myDocument.FileExists(dbFile))
|
if (myDocument.FileExists(dbFile))
|
||||||
{
|
|
||||||
shouldMigrate = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
string? versionString = LocalSetting.Get<string>(SettingKeys.LastAppVersion);
|
string? versionString = LocalSetting.Get<string>(SettingKeys.LastAppVersion);
|
||||||
if (Version.TryParse(versionString, out Version? lastVersion))
|
if (Version.TryParse(versionString, out Version? lastVersion))
|
||||||
@@ -91,6 +87,10 @@ internal static class IocConfiguration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shouldMigrate = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldMigrate)
|
if (shouldMigrate)
|
||||||
{
|
{
|
||||||
@@ -104,6 +104,6 @@ internal static class IocConfiguration
|
|||||||
LocalSetting.Set(SettingKeys.LastAppVersion, CoreEnvironment.Version.ToString());
|
LocalSetting.Set(SettingKeys.LastAppVersion, CoreEnvironment.Version.ToString());
|
||||||
|
|
||||||
return services
|
return services
|
||||||
.AddPooledDbContextFactory<AppDbContext>(builder => builder.UseSqlite(sqlConnectionString));
|
.AddDbContextPool<AppDbContext>(builder => builder.UseSqlite(sqlConnectionString));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
36
src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs
Normal file
36
src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置入口
|
||||||
|
/// </summary>
|
||||||
|
[Table("settings")]
|
||||||
|
public class SettingEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的设置入口
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
public SettingEntry(string key, string? value)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 键
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
public string Key { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 值
|
||||||
|
/// </summary>
|
||||||
|
public string? Value { get; set; }
|
||||||
|
}
|
||||||
19
src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs
Normal file
19
src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户
|
||||||
|
/// </summary>
|
||||||
|
[Table("users")]
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户的Cookie
|
||||||
|
/// </summary>
|
||||||
|
public string? Cookie { get; set; }
|
||||||
|
}
|
||||||
@@ -62,4 +62,4 @@ public interface INavigationService
|
|||||||
/// <param name="pageType">同步的页面类型</param>
|
/// <param name="pageType">同步的页面类型</param>
|
||||||
/// <returns>是否同步成功</returns>
|
/// <returns>是否同步成功</returns>
|
||||||
bool SyncSelectedNavigationViewItemWith(Type pageType);
|
bool SyncSelectedNavigationViewItemWith(Type pageType);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Service.Abstraction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户服务
|
||||||
|
/// </summary>
|
||||||
|
public interface IUserService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前用户信息
|
||||||
|
/// </summary>
|
||||||
|
User CurrentUser { get; }
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ internal class AnnouncementService : IAnnouncementService
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<AnnouncementWrapper> GetAnnouncementsAsync(ICommand openAnnouncementUICommand, CancellationToken cancellationToken = default)
|
public async Task<AnnouncementWrapper> GetAnnouncementsAsync(ICommand openAnnouncementUICommand, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
AnnouncementWrapper? wrapper = await announcementProvider.GetAnnouncementWrapperAsync(cancellationToken);
|
AnnouncementWrapper? wrapper = await announcementProvider.GetAnnouncementsAsync(cancellationToken);
|
||||||
List<AnnouncementContent> contents = await announcementProvider.GetAnnouncementContentsAsync(cancellationToken);
|
List<AnnouncementContent> contents = await announcementProvider.GetAnnouncementContentsAsync(cancellationToken);
|
||||||
|
|
||||||
Dictionary<int, string?> contentMap = contents
|
Dictionary<int, string?> contentMap = contents
|
||||||
@@ -42,12 +42,6 @@ internal class AnnouncementService : IAnnouncementService
|
|||||||
// 将公告内容联入公告列表
|
// 将公告内容联入公告列表
|
||||||
JoinAnnouncements(openAnnouncementUICommand, contentMap, announcementListWrappers);
|
JoinAnnouncements(openAnnouncementUICommand, contentMap, announcementListWrappers);
|
||||||
|
|
||||||
// we only cares about activities
|
|
||||||
if (announcementListWrappers[0].List is List<Announcement> activities)
|
|
||||||
{
|
|
||||||
AdjustActivitiesTime(ref activities);
|
|
||||||
}
|
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +58,6 @@ internal class AnnouncementService : IAnnouncementService
|
|||||||
{
|
{
|
||||||
listWrapper.List?.ForEach(item =>
|
listWrapper.List?.ForEach(item =>
|
||||||
{
|
{
|
||||||
// fix key issue
|
|
||||||
if (contentMap.TryGetValue(item.AnnId, out string? rawContent))
|
if (contentMap.TryGetValue(item.AnnId, out string? rawContent))
|
||||||
{
|
{
|
||||||
// remove <t/> tag
|
// remove <t/> tag
|
||||||
@@ -76,26 +69,4 @@ internal class AnnouncementService : IAnnouncementService
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AdjustActivitiesTime(ref List<Announcement> activities)
|
|
||||||
{
|
|
||||||
// Match yyyy/MM/dd HH:mm:ss time format
|
|
||||||
Regex dateTimeRegex = new(@"(\d+\/\d+\/\d+\s\d+:\d+:\d+)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
|
|
||||||
activities.ForEach(item =>
|
|
||||||
{
|
|
||||||
Match matched = dateTimeRegex.Match(item.Content ?? string.Empty);
|
|
||||||
if (matched.Success && DateTime.TryParse(matched.Value, out DateTime time))
|
|
||||||
{
|
|
||||||
if (time > item.StartTime && time < item.EndTime)
|
|
||||||
{
|
|
||||||
item.StartTime = time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
activities = activities
|
|
||||||
.OrderBy(i => i.StartTime)
|
|
||||||
.ThenBy(i => i.EndTime)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -13,8 +13,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||||
<NeutralLanguage>zh-CN</NeutralLanguage>
|
<NeutralLanguage>zh-CN</NeutralLanguage>
|
||||||
<DefaultLanguage>zh-cn</DefaultLanguage>
|
<DefaultLanguage>zh-CN</DefaultLanguage>
|
||||||
|
|
||||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||||
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
||||||
<PackageCertificateThumbprint>F8C2255969BEA4A681CED102771BF807856AEC02</PackageCertificateThumbprint>
|
<PackageCertificateThumbprint>F8C2255969BEA4A681CED102771BF807856AEC02</PackageCertificateThumbprint>
|
||||||
@@ -53,15 +52,11 @@
|
|||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||||
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="4.5.1" />
|
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="4.5.1" />
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.5.1" />
|
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.5.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.4">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.1.46" />
|
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.2.32" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.1.46">
|
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.2.32">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ public sealed partial class MainView : UserControl
|
|||||||
|
|
||||||
navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
||||||
navigationService.Initialize(NavView, ContentFrame);
|
navigationService.Initialize(NavView, ContentFrame);
|
||||||
|
navigationService.Navigate<Page.AnnouncementPage>(true);
|
||||||
navigationService.Navigate<Page.AnnouncementPage>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||||
xmlns:shcc="using:Snap.Hutao.Control.Cancellable"
|
xmlns:shcc="using:Snap.Hutao.Control.Cancellable"
|
||||||
xmlns:shvc="using:Snap.Hutao.View.Converter"
|
xmlns:shvc="using:Snap.Hutao.View.Converter" xmlns:shca="using:Snap.Hutao.Control.Animation"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
@@ -82,18 +82,10 @@
|
|||||||
</Border.Background>
|
</Border.Background>
|
||||||
<cwua:Explicit.Animations>
|
<cwua:Explicit.Animations>
|
||||||
<cwua:AnimationSet x:Name="ImageZoomInAnimation">
|
<cwua:AnimationSet x:Name="ImageZoomInAnimation">
|
||||||
<cwua:ScaleAnimation
|
<shca:ImageZoomInAnimation/>
|
||||||
EasingMode="EaseOut"
|
|
||||||
EasingType="Circle"
|
|
||||||
To="1.1"
|
|
||||||
Duration="0:0:0.5"/>
|
|
||||||
</cwua:AnimationSet>
|
</cwua:AnimationSet>
|
||||||
<cwua:AnimationSet x:Name="ImageZoomOutAnimation">
|
<cwua:AnimationSet x:Name="ImageZoomOutAnimation">
|
||||||
<cwua:ScaleAnimation
|
<shca:ImageZoomOutAnimation/>
|
||||||
EasingMode="EaseOut"
|
|
||||||
EasingType="Circle"
|
|
||||||
To="1"
|
|
||||||
Duration="0:0:0.5"/>
|
|
||||||
</cwua:AnimationSet>
|
</cwua:AnimationSet>
|
||||||
</cwua:Explicit.Animations>
|
</cwua:Explicit.Animations>
|
||||||
</Border>
|
</Border>
|
||||||
@@ -105,12 +97,6 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Visibility="{Binding ShouldShowTimeDescription,Converter={StaticResource BoolToVisibilityConverter}}">
|
Visibility="{Binding ShouldShowTimeDescription,Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
<!--<Border.Background>
|
|
||||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
|
|
||||||
<GradientStop Color="#00000000"/>
|
|
||||||
<GradientStop Offset="1" Color="#A0000000"/>
|
|
||||||
</LinearGradientBrush>
|
|
||||||
</Border.Background>-->
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
MinHeight="2"
|
MinHeight="2"
|
||||||
Value="{Binding TimePercent,Mode=OneWay}"
|
Value="{Binding TimePercent,Mode=OneWay}"
|
||||||
|
|||||||
@@ -11,13 +11,34 @@ internal static class ApiEndpoints
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 公告列表
|
/// 公告列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string AnnList = $"{Hk4eApi}/common/hk4e_cn/announcement/api/getAnnList?{AnnouncementQuery}";
|
public const string AnnList = $"{Hk4eApi}/common/hk4e_cn/announcement/api/getAnnList?{AnnouncementQuery}";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 公告内容
|
/// 公告内容
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string AnnContent = $"{Hk4eApi}/common/hk4e_cn/announcement/api/getAnnContent?{AnnouncementQuery}";
|
public const string AnnContent = $"{Hk4eApi}/common/hk4e_cn/announcement/api/getAnnContent?{AnnouncementQuery}";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏记录主页
|
||||||
|
/// </summary>
|
||||||
|
public const string GameRecordIndex = $"{ApiTakumiRecordApi}/index?role_id={{0}}&server={{1}}";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 深渊信息
|
||||||
|
/// </summary>
|
||||||
|
public const string SpiralAbyss = $"{ApiTakumiRecordApi}/spiralAbyss?schedule_type={{0}}&role_id={{1}}&server={{2}}";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色信息
|
||||||
|
/// </summary>
|
||||||
|
public const string Character = $"{ApiTakumiRecordApi}/character";
|
||||||
|
|
||||||
|
public const string UserGameRoles = $"{ApiTakumi}/binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn";
|
||||||
|
|
||||||
|
private const string ApiTakumi = "https://api-takumi.mihoyo.com";
|
||||||
|
private const string ApiTakumiRecord = "https://api-takumi-record.mihoyo.com";
|
||||||
|
private const string ApiTakumiRecordApi = $"{ApiTakumiRecord}/game_record/app/genshin/api";
|
||||||
private const string Hk4eApi = "https://hk4e-api.mihoyo.com";
|
private const string Hk4eApi = "https://hk4e-api.mihoyo.com";
|
||||||
|
|
||||||
private const string AnnouncementQuery = "game=hk4e&game_biz=hk4e_cn&lang=zh-cn&bundle_id=hk4e_cn&platform=pc®ion=cn_gf01&level=55&uid=100000000";
|
private const string AnnouncementQuery = "game=hk4e&game_biz=hk4e_cn&lang=zh-cn&bundle_id=hk4e_cn&platform=pc®ion=cn_gf01&level=55&uid=100000000";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Core.Converting;
|
||||||
|
using Snap.Hutao.Core.Json;
|
||||||
|
using Snap.Hutao.Web.Response;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为MiHoYo接口请求器 <see cref="Requester"/> 提供2代动态密钥
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class DynamicSecretProvider2 : Md5Convert
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 似乎已经与版本号无关,自2.11.1以来未曾改变salt
|
||||||
|
/// </summary>
|
||||||
|
public const string AppVersion = "2.16.1";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 米游社的盐
|
||||||
|
/// 计算过程:https://gist.github.com/Lightczx/373c5940b36e24b25362728b52dec4fd
|
||||||
|
/// </summary>
|
||||||
|
private static readonly string APISalt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs";
|
||||||
|
private static readonly Random Random = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建动态密钥
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="json">json格式化器</param>
|
||||||
|
/// <param name="queryUrl">查询url</param>
|
||||||
|
/// <param name="postBody">请求体</param>
|
||||||
|
/// <returns>密钥</returns>
|
||||||
|
public static string Create(Json json, string queryUrl, object? postBody = null)
|
||||||
|
{
|
||||||
|
// unix timestamp
|
||||||
|
long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
|
||||||
|
// random
|
||||||
|
string r = GetRandomString();
|
||||||
|
|
||||||
|
// body
|
||||||
|
string b = postBody is null ? string.Empty : json.Stringify(postBody);
|
||||||
|
|
||||||
|
// query
|
||||||
|
string q = string.Empty;
|
||||||
|
string? query = new UriBuilder(queryUrl).Query;
|
||||||
|
if (!string.IsNullOrEmpty(query))
|
||||||
|
{
|
||||||
|
q = string.Join("&", query.Split('&').OrderBy(x => x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check
|
||||||
|
string check = ToHexString($"salt={APISalt}&t={t}&r={r}&b={b}&q={q}");
|
||||||
|
|
||||||
|
return $"{t},{r},{check}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRandomString()
|
||||||
|
{
|
||||||
|
// 原汁原味
|
||||||
|
// https://github.com/Azure99/GenshinPlayerQuery/issues/20#issuecomment-913641512
|
||||||
|
// int rand = (int)((Random.Next() / (int.MaxValue + 3D) * 100000D) + 100000D) % 1000000;
|
||||||
|
// if (rand < 100001)
|
||||||
|
// {
|
||||||
|
// rand += 542367;
|
||||||
|
// }
|
||||||
|
int rand = Random.Next(100000, 200000);
|
||||||
|
if (rand == 100000)
|
||||||
|
{
|
||||||
|
rand = 642367;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rand.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Web.Request;
|
||||||
|
using Snap.Hutao.Web.Response;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态密钥扩展
|
||||||
|
/// </summary>
|
||||||
|
internal static class RequesterExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 使用二代动态密钥执行 GET 操作
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResult">返回的类类型</typeparam>
|
||||||
|
/// <param name="requester">请求器</param>
|
||||||
|
/// <param name="url">地址</param>
|
||||||
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
|
/// <returns>响应</returns>
|
||||||
|
public static async Task<Response<TResult>?> GetUsingDS2Async<TResult>(
|
||||||
|
this Requester requester,
|
||||||
|
string url,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
requester.Headers["DS"] = DynamicSecretProvider2.Create(requester.Json, url);
|
||||||
|
|
||||||
|
return await requester
|
||||||
|
.GetAsync<TResult>(url, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用二代动态密钥执行 POST 操作
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResult">返回的类类型</typeparam>
|
||||||
|
/// <param name="requester">请求器</param>
|
||||||
|
/// <param name="url">地址</param>
|
||||||
|
/// <param name="data">post数据</param>
|
||||||
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
|
/// <returns>响应</returns>
|
||||||
|
public static async Task<Response<TResult>?> PostUsingDS2Async<TResult>(
|
||||||
|
this Requester requester,
|
||||||
|
string url,
|
||||||
|
object data,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
requester.Headers["DS"] = DynamicSecretProvider2.Create(requester.Json, url, data);
|
||||||
|
|
||||||
|
return await requester
|
||||||
|
.PostAsync<TResult>(url, data, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -100,19 +100,19 @@ public class Announcement : AnnouncementContent
|
|||||||
/// 类型标签
|
/// 类型标签
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("type_label")]
|
[JsonPropertyName("type_label")]
|
||||||
public string? TypeLabel { get; set; }
|
public string TypeLabel { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标签文本
|
/// 标签文本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("tag_label")]
|
[JsonPropertyName("tag_label")]
|
||||||
public string? TagLabel { get; set; }
|
public string TagLabel { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标签图标
|
/// 标签图标
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("tag_icon")]
|
[JsonPropertyName("tag_icon")]
|
||||||
public string? TagIcon { get; set; }
|
public string TagIcon { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 登录提醒
|
/// 登录提醒
|
||||||
@@ -156,13 +156,13 @@ public class Announcement : AnnouncementContent
|
|||||||
/// 标签开始时间
|
/// 标签开始时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("tag_start_time")]
|
[JsonPropertyName("tag_start_time")]
|
||||||
public string? TagStartTime { get; set; }
|
public string TagStartTime { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标签结束时间
|
/// 标签结束时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("tag_end_time")]
|
[JsonPropertyName("tag_end_time")]
|
||||||
public string? TagEndTime { get; set; }
|
public string TagEndTime { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提醒版本
|
/// 提醒版本
|
||||||
|
|||||||
@@ -20,30 +20,30 @@ public class AnnouncementContent
|
|||||||
/// 公告标题
|
/// 公告标题
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("title")]
|
[JsonPropertyName("title")]
|
||||||
public string? Title { get; set; }
|
public string Title { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 副标题
|
/// 副标题
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("subtitle")]
|
[JsonPropertyName("subtitle")]
|
||||||
public string? Subtitle { get; set; }
|
public string Subtitle { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 横幅Url
|
/// 横幅Url
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("banner")]
|
[JsonPropertyName("banner")]
|
||||||
public string? Banner { get; set; }
|
public string Banner { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内容字符串
|
/// 内容字符串
|
||||||
/// 可能包含了一些html格式
|
/// 可能包含了一些html格式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("content")]
|
[JsonPropertyName("content")]
|
||||||
public string? Content { get; set; }
|
public string Content { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 语言
|
/// 语言
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("lang")]
|
[JsonPropertyName("lang")]
|
||||||
public string? Lang { get; set; }
|
public string Lang { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ public class AnnouncementListWrapper : ListWrapper<Announcement>
|
|||||||
/// 类型标签
|
/// 类型标签
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("type_label")]
|
[JsonPropertyName("type_label")]
|
||||||
public string? TypeLabel { get; set; }
|
public string TypeLabel { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ internal class AnnouncementProvider
|
|||||||
/// 构造一个新的公告提供器
|
/// 构造一个新的公告提供器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requester">请求器</param>
|
/// <param name="requester">请求器</param>
|
||||||
/// <param name="gZipRequester">GZip 请求器</param>
|
|
||||||
public AnnouncementProvider(Requester requester)
|
public AnnouncementProvider(Requester requester)
|
||||||
{
|
{
|
||||||
this.requester = requester;
|
this.requester = requester;
|
||||||
@@ -31,12 +30,13 @@ internal class AnnouncementProvider
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cancellationToken">取消令牌</param>
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
/// <returns>公告列表</returns>
|
/// <returns>公告列表</returns>
|
||||||
public async Task<AnnouncementWrapper?> GetAnnouncementWrapperAsync(CancellationToken cancellationToken = default)
|
public async Task<AnnouncementWrapper?> GetAnnouncementsAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Response<AnnouncementWrapper>? resp = await requester
|
Response<AnnouncementWrapper>? resp = await requester
|
||||||
.Reset()
|
.Reset()
|
||||||
.GetAsync<AnnouncementWrapper>(ApiEndpoints.AnnList, cancellationToken)
|
.GetAsync<AnnouncementWrapper>(ApiEndpoints.AnnList, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return resp?.Data;
|
return resp?.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,9 +51,10 @@ internal class AnnouncementProvider
|
|||||||
|
|
||||||
Response<ListWrapper<AnnouncementContent>>? resp = await requester
|
Response<ListWrapper<AnnouncementContent>>? resp = await requester
|
||||||
.Reset()
|
.Reset()
|
||||||
.AddHeader("Accept", RequestOptions.Json)
|
.AddHeader(RequestHeaders.Accept, RequestOptions.Json)
|
||||||
.GetAsync<ListWrapper<AnnouncementContent>>(ApiEndpoints.AnnContent, cancellationToken)
|
.GetAsync<ListWrapper<AnnouncementContent>>(ApiEndpoints.AnnContent, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return resp?.Data?.List ?? new();
|
return resp?.Data?.List ?? new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ public class AnnouncementType
|
|||||||
/// 名称
|
/// 名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("name")]
|
[JsonPropertyName("name")]
|
||||||
public string? Name { get; set; }
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 国际化名称
|
/// 国际化名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("mi18n_name")]
|
[JsonPropertyName("mi18n_name")]
|
||||||
public string? MI18NName { get; set; }
|
public string MI18NName { get; set; } = default!;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ public class AnnouncementWrapper : ListWrapper<AnnouncementListWrapper>
|
|||||||
/// 类型列表
|
/// 类型列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("type_list")]
|
[JsonPropertyName("type_list")]
|
||||||
public List<AnnouncementType>? TypeList { get; set; }
|
public List<AnnouncementType> TypeList { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提醒
|
/// 提醒
|
||||||
@@ -46,5 +46,5 @@ public class AnnouncementWrapper : ListWrapper<AnnouncementListWrapper>
|
|||||||
/// 时间戳
|
/// 时间戳
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("t")]
|
[JsonPropertyName("t")]
|
||||||
public string? TimeStamp { get; set; }
|
public string TimeStamp { get; set; } = default!;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户游戏角色
|
||||||
|
/// </summary>
|
||||||
|
public record UserGameRole
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// hk4e_cn for Genshin Impact
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("game_biz")]
|
||||||
|
public string GameBiz { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务器
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("region")]
|
||||||
|
public string Region { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏Uid
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("game_uid")]
|
||||||
|
public string GameUid { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 昵称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("nickname")]
|
||||||
|
public string Nickname { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("level")]
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否选中
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("is_chosen")]
|
||||||
|
public bool IsChosen { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 地区名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("region_name")]
|
||||||
|
public string RegionName { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为官服
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("is_official")]
|
||||||
|
public string IsOfficial { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 转化为 <see cref="PlayerUid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>一个等价的 <see cref="PlayerUid"/> 实例</returns>
|
||||||
|
public PlayerUid AsPlayerUid()
|
||||||
|
{
|
||||||
|
return new PlayerUid(GameUid, Region);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Nickname} | {RegionName} | Lv.{Level}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Service.Abstraction;
|
||||||
|
using Snap.Hutao.Web.Request;
|
||||||
|
using Snap.Hutao.Web.Response;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户游戏角色提供器
|
||||||
|
/// </summary>
|
||||||
|
[Injection(InjectAs.Transient)]
|
||||||
|
internal class UserGameRoleProvider
|
||||||
|
{
|
||||||
|
private readonly IUserService userService;
|
||||||
|
private readonly Requester requester;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的用户游戏角色提供器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userService">用户服务</param>
|
||||||
|
/// <param name="requester">请求器</param>
|
||||||
|
public UserGameRoleProvider(IUserService userService, Requester requester)
|
||||||
|
{
|
||||||
|
this.userService = userService;
|
||||||
|
this.requester = requester;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取用户角色信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
|
/// <returns>用户角色信息</returns>
|
||||||
|
public async Task<List<UserGameRole>> GetUserGameRolesAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Response<ListWrapper<UserGameRole>>? resp = await requester
|
||||||
|
.Reset()
|
||||||
|
.SetAcceptJson()
|
||||||
|
.SetCommonUA()
|
||||||
|
.SetRequestWithHyperion()
|
||||||
|
.SetUser(userService.CurrentUser)
|
||||||
|
.GetAsync<ListWrapper<UserGameRole>>(ApiEndpoints.UserGameRoles, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
return resp?.Data?.List ?? new();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色的基础信息
|
||||||
|
/// </summary>
|
||||||
|
public class Avatar
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图片Url
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("image")]
|
||||||
|
public string Image { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元素英文名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("element")]
|
||||||
|
public string Element { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 好感度
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("fetter")]
|
||||||
|
public int Fetter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("level")]
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 稀有度
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("rarity")]
|
||||||
|
public Rarity Rarity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 激活的命座数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("actived_constellation_num")]
|
||||||
|
public int ActivedConstellationNum { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色详细详细
|
||||||
|
/// </summary>
|
||||||
|
public class Character : Avatar
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 角色图片
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("we don't want to use this ugly pic here")]
|
||||||
|
[JsonPropertyName("image")]
|
||||||
|
public new string Image { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("weapon")]
|
||||||
|
public Weapon Weapon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("reliquaries")]
|
||||||
|
public List<Reliquary> Reliquaries { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 命座
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("constellations")]
|
||||||
|
public List<Constellation> Constellations { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时装
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("costumes")]
|
||||||
|
public List<Costume> Costumes { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包装详细角色信息列表
|
||||||
|
/// </summary>
|
||||||
|
public class CharacterWrapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 角色列表
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("avatars")]
|
||||||
|
public List<Character> Avatars { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 命座
|
||||||
|
/// </summary>
|
||||||
|
public class Constellation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 效果描述
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("effect")]
|
||||||
|
public string Effect { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否激活
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("is_actived")]
|
||||||
|
public bool IsActived { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 位置
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("pos")]
|
||||||
|
public int Position { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色装扮
|
||||||
|
/// </summary>
|
||||||
|
public class Costume
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("不应使用此处的图标")]
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 稀有度
|
||||||
|
/// </summary>
|
||||||
|
public enum Rarity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 一星
|
||||||
|
/// </summary>
|
||||||
|
Gray = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 二星
|
||||||
|
/// </summary>
|
||||||
|
Green = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 三星
|
||||||
|
/// </summary>
|
||||||
|
Blue = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 四星
|
||||||
|
/// </summary>
|
||||||
|
Purple = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 五星
|
||||||
|
/// </summary>
|
||||||
|
Orange = 5,
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物
|
||||||
|
/// </summary>
|
||||||
|
public class Reliquary
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 部位
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("pos")]
|
||||||
|
public ReliquaryPosition Position { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 稀有度
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("rarity")]
|
||||||
|
public Rarity Rarity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("level")]
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物套装
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("set")]
|
||||||
|
public ReliquarySet ReliquarySet { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 部位名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("pos_name")]
|
||||||
|
public string PositionName { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物套装效果
|
||||||
|
/// </summary>
|
||||||
|
public class ReliquaryAffix
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 激活个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("activation_number")]
|
||||||
|
public int ActivationNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 效果描述
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("effect")]
|
||||||
|
public string? Effect { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物部位
|
||||||
|
/// </summary>
|
||||||
|
public enum ReliquaryPosition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 生之花
|
||||||
|
/// </summary>
|
||||||
|
FlowerOfLife = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 死之羽
|
||||||
|
/// </summary>
|
||||||
|
PlumeOfDeath = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时之沙
|
||||||
|
/// </summary>
|
||||||
|
SandOfEon = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 空之杯
|
||||||
|
/// </summary>
|
||||||
|
GobletOfEonothem = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 理之冠
|
||||||
|
/// </summary>
|
||||||
|
CircletOfLogos = 5,
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物套装信息
|
||||||
|
/// </summary>
|
||||||
|
public class ReliquarySet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 套装名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 套装效果
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("affixes")]
|
||||||
|
public List<ReliquaryAffix> Affixes { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器信息
|
||||||
|
/// </summary>
|
||||||
|
public class Weapon
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id")] public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")] public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")] public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("type")] public WeaponType Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 稀有度
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("rarity")] public Rarity Rarity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("level")] public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 突破等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("promote_level")] public int PromoteLevel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("type_name")] public string TypeName { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器介绍
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("desc")] public string Description { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 精炼等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("affix_level")] public int AffixLevel { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器类型
|
||||||
|
/// </summary>
|
||||||
|
public enum WeaponType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 单手剑
|
||||||
|
/// </summary>
|
||||||
|
Sword = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 法器
|
||||||
|
/// </summary>
|
||||||
|
Catalyst = 10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 双手剑
|
||||||
|
/// </summary>
|
||||||
|
Claymore = 11,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 弓
|
||||||
|
/// </summary>
|
||||||
|
Bow = 12,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 长柄武器
|
||||||
|
/// </summary>
|
||||||
|
Polearm = 13,
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Service.Abstraction;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
using Snap.Hutao.Web.Request;
|
||||||
|
using Snap.Hutao.Web.Response;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏记录提供器
|
||||||
|
/// </summary>
|
||||||
|
[Injection(InjectAs.Transient)]
|
||||||
|
internal class GameRecordProvider
|
||||||
|
{
|
||||||
|
private readonly IUserService userService;
|
||||||
|
private readonly Requester requester;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的游戏记录提供器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userService">用户服务</param>
|
||||||
|
/// <param name="requester">请求器</param>
|
||||||
|
public GameRecordProvider(IUserService userService, Requester requester)
|
||||||
|
{
|
||||||
|
this.userService = userService;
|
||||||
|
this.requester = requester;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取玩家基础信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">uid</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>玩家的基础信息</returns>
|
||||||
|
public async Task<PlayerInfo?> GetPlayerInfoAsync(PlayerUid uid, CancellationToken token)
|
||||||
|
{
|
||||||
|
string url = string.Format(ApiEndpoints.GameRecordIndex, uid.Value, uid.Region);
|
||||||
|
|
||||||
|
Response<PlayerInfo>? resp = await PrepareRequester()
|
||||||
|
.GetUsingDS2Async<PlayerInfo>(url, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取玩家深渊信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">uid</param>
|
||||||
|
/// <param name="schedule">1:当期,2:上期</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>深渊信息</returns>
|
||||||
|
public async Task<SpiralAbyss.SpiralAbyss?> GetSpiralAbyssAsync(PlayerUid uid, SpiralAbyssSchedule schedule, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
string url = string.Format(ApiEndpoints.SpiralAbyss, (int)schedule, uid.Value, uid.Region);
|
||||||
|
|
||||||
|
Response<SpiralAbyss.SpiralAbyss>? resp = await PrepareRequester()
|
||||||
|
.GetUsingDS2Async<SpiralAbyss.SpiralAbyss>(url, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取玩家角色详细信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">uid</param>
|
||||||
|
/// <param name="playerInfo">玩家的基础信息</param>
|
||||||
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
|
/// <returns>角色列表</returns>
|
||||||
|
public async Task<List<Character>> GetCharactersAsync(PlayerUid uid, PlayerInfo playerInfo, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
CharacterData data = new(uid, playerInfo.Avatars.Select(x => x.Id));
|
||||||
|
|
||||||
|
Response<CharacterWrapper>? resp = await PrepareRequester()
|
||||||
|
.PostUsingDS2Async<CharacterWrapper>(ApiEndpoints.Character, data, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data?.Avatars ?? new();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Requester PrepareRequester()
|
||||||
|
{
|
||||||
|
return requester
|
||||||
|
.Reset()
|
||||||
|
.SetAcceptJson()
|
||||||
|
.AddHeader(RequestHeaders.AppVersion, DynamicSecretProvider2.AppVersion)
|
||||||
|
.SetCommonUA()
|
||||||
|
.AddHeader(RequestHeaders.ClientType, RequestOptions.DefaultClientType)
|
||||||
|
.SetRequestWithHyperion()
|
||||||
|
.SetUser(userService.CurrentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CharacterData
|
||||||
|
{
|
||||||
|
public CharacterData(PlayerUid uid, IEnumerable<int> characterIds)
|
||||||
|
{
|
||||||
|
CharacterIds = characterIds;
|
||||||
|
Uid = uid.Value;
|
||||||
|
Server = uid.Region;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("character_ids")]
|
||||||
|
public IEnumerable<int> CharacterIds { get; }
|
||||||
|
|
||||||
|
[JsonPropertyName("role_id")]
|
||||||
|
public string Uid { get; }
|
||||||
|
|
||||||
|
[JsonPropertyName("server")]
|
||||||
|
public string Server { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 家园信息
|
||||||
|
/// </summary>
|
||||||
|
public class Home
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("level")]
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 历史访客数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("visit_num")]
|
||||||
|
public int VisitNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最高洞天仙力
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("comfort_num")]
|
||||||
|
public int ComfortNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得摆设数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("item_num")]
|
||||||
|
public int ItemNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 洞天名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 洞天等级名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("comfort_level_name")]
|
||||||
|
public string ComfortLevelName { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 洞天等级图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("comfort_level_icon")]
|
||||||
|
public string ComfortLevelIcon { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 供奉信息
|
||||||
|
/// </summary>
|
||||||
|
public class Offering
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("level")]
|
||||||
|
public string Level { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Service.Abstraction;
|
||||||
|
using Snap.Hutao.Web.Request;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家信息
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 持有的角色的信息
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("avatars")]
|
||||||
|
public List<Avatar.Avatar> Avatars { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家的基本信息
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("stats")]
|
||||||
|
public PlayerStats PlayerStat { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 世界探索
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("world_explorations")]
|
||||||
|
public List<WorldExploration> WorldExplorations { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 洞天
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("homes")]
|
||||||
|
public List<Home> Homes { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家统计数据
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerStats
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 活跃天数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("active_day_number")]
|
||||||
|
public int ActiveDayNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成就完成数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("achievement_number")]
|
||||||
|
public int AchievementNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 风神瞳个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("anemoculus_number")]
|
||||||
|
public int AnemoculusNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 岩神瞳个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("geoculus_number")]
|
||||||
|
public int GeoculusNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 雷神瞳个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("electroculus_number")]
|
||||||
|
public int ElectroculusNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("avatar_number")]
|
||||||
|
public int AvatarNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 传送锚点个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("way_point_number")]
|
||||||
|
public int WayPointNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 秘境个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("domain_number")]
|
||||||
|
public int DomainNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 深渊层数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("spiral_abyss")]
|
||||||
|
public string SpiralAbyss { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 华丽宝箱个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("luxurious_chest_number")]
|
||||||
|
public int LuxuriousChestNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 珍贵宝箱个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("precious_chest_number")]
|
||||||
|
public int PreciousChestNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 精致宝箱个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("exquisite_chest_number")]
|
||||||
|
public int ExquisiteChestNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 普通宝箱个数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("common_chest_number")]
|
||||||
|
public int CommonChestNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 奇馈宝箱
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("magic_chest_number")]
|
||||||
|
public int MagicChestNumber { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 仅包含头像的角色信息
|
||||||
|
/// </summary>
|
||||||
|
public class Avatar
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("level")]
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 稀有度
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("rarity")]
|
||||||
|
public int Rarity { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表示一次战斗
|
||||||
|
/// </summary>
|
||||||
|
public class Battle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 索引
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("index")]
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间戳
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("timestamp")]
|
||||||
|
public string Timestamp { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参战角色
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("avatars")]
|
||||||
|
public List<Avatar> Avatars { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime Time
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return DateTimeOffset.FromUnixTimeSeconds(int.Parse(Timestamp)).LocalDateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层
|
||||||
|
/// </summary>
|
||||||
|
public class Floor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 层号
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("index")]
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否解锁
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("is_unlock")]
|
||||||
|
public string IsUnlock { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束时间
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("settle_time")]
|
||||||
|
public string SettleTime { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 星数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("star")]
|
||||||
|
public int Star { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最大星数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("max_star")]
|
||||||
|
public int MaxStar { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层信息
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("levels")]
|
||||||
|
public List<Level> Levels { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 间
|
||||||
|
/// </summary>
|
||||||
|
public class Level
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 索引
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("index")]
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 星数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("star")]
|
||||||
|
public int Star { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最大星数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("max_star")]
|
||||||
|
public int MaxStar { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上下半
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("battles")]
|
||||||
|
public List<Battle> Battles { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色数值排行信息
|
||||||
|
/// </summary>
|
||||||
|
public class Rank
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 角色Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("avatar_id")]
|
||||||
|
public int AvatarId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("avatar_icon")]
|
||||||
|
public string AvatarIcon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 值
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("value")]
|
||||||
|
public int Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 稀有度
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("rarity")]
|
||||||
|
public int Rarity { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 深境螺旋信息
|
||||||
|
/// </summary>
|
||||||
|
public class SpiralAbyss
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 计划Id
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("schedule_id")]
|
||||||
|
public int ScheduleId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始时间
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("start_time")]
|
||||||
|
public string StartTime { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束时间
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("end_time")]
|
||||||
|
public string EndTime { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 战斗次数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("total_battle_times")]
|
||||||
|
public int TotalBattleTimes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 胜利次数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("total_win_times")]
|
||||||
|
public int TotalWinTimes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最深抵达
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("max_floor")]
|
||||||
|
public string MaxFloor { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 出战次数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("reveal_rank")]
|
||||||
|
public List<Rank> RevealRank { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 击破次数
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("defeat_rank")]
|
||||||
|
public List<Rank> DefeatRank { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最强一击
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("damage_rank")]
|
||||||
|
public List<Rank> DamageRank { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 承受伤害
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("take_damage_rank")]
|
||||||
|
public List<Rank> TakeDamageRank { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元素战技
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("normal_skill_rank")]
|
||||||
|
public List<Rank> NormalSkillRank { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元素爆发
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("energy_skill_rank")]
|
||||||
|
public List<Rank> EnergySkillRank { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层信息
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("floors")]
|
||||||
|
public List<Floor> Floors { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 共获得渊星
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("total_star")]
|
||||||
|
public int TotalStar { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否解锁
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("is_unlock")]
|
||||||
|
public bool IsUnlock { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 深渊期数类型
|
||||||
|
/// </summary>
|
||||||
|
public enum SpiralAbyssSchedule
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当期
|
||||||
|
/// </summary>
|
||||||
|
Current = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上期
|
||||||
|
/// </summary>
|
||||||
|
Last = 2,
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 世界探索
|
||||||
|
/// </summary>
|
||||||
|
public class WorldExploration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("level")]
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 探索度
|
||||||
|
/// Maxmium is 1000
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("exploration_percentage")]
|
||||||
|
public int ExplorationPercentage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型
|
||||||
|
/// Offering
|
||||||
|
/// Reputation
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 供奉进度
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("offerings")]
|
||||||
|
public List<Offering> Offerings { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 父ID 当无链接的父对象时为0
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("parent_id")]
|
||||||
|
public int ParentId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 地图链接
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("map_url")]
|
||||||
|
public string MapUrl { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 攻略链接 无攻略时为 <see cref="string.Empty"/>
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("strategy_url")]
|
||||||
|
public string StrategyUrl { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背景图片
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("background_image")]
|
||||||
|
public string BackgroundImage { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 反色图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("inner_icon")]
|
||||||
|
public string InnerIcon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背景图片
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("cover")]
|
||||||
|
public string Cover { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 百分比*100进度
|
||||||
|
/// </summary>
|
||||||
|
public double ExplorationPercentageBy10
|
||||||
|
{
|
||||||
|
get => ExplorationPercentage / 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型名称转换器
|
||||||
|
/// </summary>
|
||||||
|
public string TypeFormatted
|
||||||
|
{
|
||||||
|
get => IsReputation ? "声望等级" : "供奉等级";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 指示当前是否为声望
|
||||||
|
/// </summary>
|
||||||
|
public bool IsReputation
|
||||||
|
{
|
||||||
|
get => Type == "Reputation";
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/PlayerUid.cs
Normal file
59
src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/PlayerUid.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hoyolab.Takumi;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家 Uid
|
||||||
|
/// </summary>
|
||||||
|
public struct PlayerUid
|
||||||
|
{
|
||||||
|
private string? region = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的玩家 Uid 结构
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">uid</param>
|
||||||
|
/// <param name="region">服务器,当提供该参数时会无条件信任</param>
|
||||||
|
public PlayerUid(string value, string? region = default)
|
||||||
|
{
|
||||||
|
Requires.Argument(value.Length == 9, nameof(value), "uid应为9位数字");
|
||||||
|
Value = value;
|
||||||
|
|
||||||
|
if (region != null)
|
||||||
|
{
|
||||||
|
this.region = region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UID 的实际值
|
||||||
|
/// </summary>
|
||||||
|
public string Value { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 地区代码
|
||||||
|
/// </summary>
|
||||||
|
public string Region
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
region ??= EvaluateRegion(Value[0]);
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string EvaluateRegion(char first)
|
||||||
|
{
|
||||||
|
return first switch
|
||||||
|
{
|
||||||
|
>= '1' and <= '4' => "cn_gf01", // 国服
|
||||||
|
'5' => "cn_qd01", // 渠道
|
||||||
|
'6' => "os_usa", // 美服
|
||||||
|
'7' => "os_euro", // 欧服
|
||||||
|
'8' => "os_asia", // 亚服
|
||||||
|
'9' => "os_cht", // 台服
|
||||||
|
_ => Must.NeverHappen<string>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
341
src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoAPIProvider.cs
Normal file
341
src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoAPIProvider.cs
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Core.Abstraction;
|
||||||
|
using Snap.Hutao.Extension;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
using Snap.Hutao.Web.Hutao.Model;
|
||||||
|
using Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
using Snap.Hutao.Web.Request;
|
||||||
|
using Snap.Hutao.Web.Response;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 胡桃API提供器
|
||||||
|
/// </summary>
|
||||||
|
[Injection(InjectAs.Transient)]
|
||||||
|
internal class HutaoAPIProvider : IAsyncInitializable
|
||||||
|
{
|
||||||
|
private const string AuthAPIHost = "https://auth.snapgenshin.com";
|
||||||
|
private const string HutaoAPI = "https://hutao-api.snapgenshin.com";
|
||||||
|
private const string PostContentType = "text/json";
|
||||||
|
|
||||||
|
private readonly Requester requester;
|
||||||
|
private readonly AuthRequester authRequester;
|
||||||
|
private readonly GameRecordProvider gameRecordProvider;
|
||||||
|
private readonly UserGameRoleProvider userGameRoleProvider;
|
||||||
|
|
||||||
|
private bool isInitialized = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的胡桃API提供器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="requester">请求器</param>
|
||||||
|
/// <param name="authRequester">支持验证的请求器</param>
|
||||||
|
/// <param name="gameRecordProvider">游戏记录提供器</param>
|
||||||
|
/// <param name="userGameRoleProvider">用户游戏角色提供器</param>
|
||||||
|
public HutaoAPIProvider(
|
||||||
|
Requester requester,
|
||||||
|
AuthRequester authRequester,
|
||||||
|
GameRecordProvider gameRecordProvider,
|
||||||
|
UserGameRoleProvider userGameRoleProvider)
|
||||||
|
{
|
||||||
|
this.requester = requester;
|
||||||
|
this.authRequester = authRequester;
|
||||||
|
this.gameRecordProvider = gameRecordProvider;
|
||||||
|
this.userGameRoleProvider = userGameRoleProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IsInitialized { get => isInitialized; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<bool> InitializeAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Auth auth = new(
|
||||||
|
"08d9e212-0cb3-4d71-8ed7-003606da7b20",
|
||||||
|
"7ueWgZGn53dDhrm8L5ZRw+YWfOeSWtgQmJWquRgaygw=");
|
||||||
|
|
||||||
|
Response<Token>? resp = await requester
|
||||||
|
.Reset()
|
||||||
|
.PostAsync<Token>($"{AuthAPIHost}/Auth/Login", auth, PostContentType, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
authRequester.AuthToken = Must.NotNull(resp?.Data?.AccessToken!);
|
||||||
|
isInitialized = true;
|
||||||
|
|
||||||
|
return isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查对应的uid当前是否上传了数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">uid</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>当前是否上传了数据</returns>
|
||||||
|
public async Task<bool> CheckPeriodRecordUploadedAsync(string uid, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<UploadStatus>? resp = await authRequester
|
||||||
|
.GetAsync<UploadStatus>($"{HutaoAPI}/Record/CheckRecord/{uid}", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
return resp?.Data is not null && resp.Data.PeriodUploaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取总览数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>总览信息</returns>
|
||||||
|
public async Task<Overview?> GetOverviewAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<Overview>? resp = await authRequester
|
||||||
|
.GetAsync<Overview>($"{HutaoAPI}/Statistics/Overview", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取角色出场率
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>角色出场率</returns>
|
||||||
|
public async Task<IEnumerable<AvatarParticipation>> GetAvatarParticipationsAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<AvatarParticipation>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<AvatarParticipation>>($"{HutaoAPI}/Statistics/AvatarParticipation", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data ?? Enumerable.Empty<AvatarParticipation>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取角色圣遗物搭配
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>角色圣遗物搭配</returns>
|
||||||
|
public async Task<IEnumerable<AvatarReliquaryUsage>> GetAvatarReliquaryUsagesAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<AvatarReliquaryUsage>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<AvatarReliquaryUsage>>($"{HutaoAPI}/Statistics/AvatarReliquaryUsage", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data ?? Enumerable.Empty<AvatarReliquaryUsage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取角色搭配数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>角色搭配数据</returns>
|
||||||
|
public async Task<IEnumerable<TeamCollocation>> GetTeamCollocationsAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<TeamCollocation>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<TeamCollocation>>($"{HutaoAPI}/Statistics/TeamCollocation", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data ?? Enumerable.Empty<TeamCollocation>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取角色武器搭配数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>角色武器搭配数据</returns>
|
||||||
|
public async Task<IEnumerable<AvatarWeaponUsage>> GetAvatarWeaponUsagesAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<AvatarWeaponUsage>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<AvatarWeaponUsage>>($"{HutaoAPI}/Statistics/AvatarWeaponUsage", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data ?? Enumerable.Empty<AvatarWeaponUsage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取角色命座信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>角色图片列表</returns>
|
||||||
|
public async Task<IEnumerable<AvatarConstellationNum>> GetAvatarConstellationsAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<AvatarConstellationNum>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<AvatarConstellationNum>>($"{HutaoAPI}/Statistics/Constellation", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data ?? Enumerable.Empty<AvatarConstellationNum>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取队伍出场次数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>队伍出场列表</returns>
|
||||||
|
public async Task<IEnumerable<TeamCombination>> GetTeamCombinationsAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<TeamCombination>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<TeamCombination>>($"{HutaoAPI}/Statistics/TeamCombination", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
return resp?.Data ?? Enumerable.Empty<TeamCombination>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取角色图片列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>角色图片列表</returns>
|
||||||
|
public async Task<IEnumerable<Item>> GetAvatarMapAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<Item>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<Item>>($"{HutaoAPI}/GenshinItem/Avatars", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data ?? Enumerable.Empty<Item>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取武器图片列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>武器图片列表</returns>
|
||||||
|
public async Task<IEnumerable<Item>> GetWeaponMapAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<Item>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<Item>>($"{HutaoAPI}/GenshinItem/Weapons", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data ?? Enumerable.Empty<Item>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取圣遗物图片列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>圣遗物图片列表</returns>
|
||||||
|
public async Task<IEnumerable<Item>> GetReliquaryMapAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
Response<IEnumerable<Item>>? resp = await authRequester
|
||||||
|
.GetAsync<IEnumerable<Item>>($"{HutaoAPI}/GenshinItem/Reliquaries", token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return resp?.Data ?? Enumerable.Empty<Item>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取所有记录并上传到数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="confirmAsyncFunc">异步确认委托</param>
|
||||||
|
/// <param name="resultAsyncFunc">结果确认委托</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>任务</returns>
|
||||||
|
public async Task GetAllRecordsAndUploadAsync(Func<PlayerRecord, Task<bool>> confirmAsyncFunc, Func<Response.Response, Task> resultAsyncFunc, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
List<UserGameRole> userGameRoles = await userGameRoleProvider
|
||||||
|
.GetUserGameRolesAsync(token);
|
||||||
|
|
||||||
|
foreach (UserGameRole role in userGameRoles)
|
||||||
|
{
|
||||||
|
PlayerInfo? playerInfo = await gameRecordProvider
|
||||||
|
.GetPlayerInfoAsync(role.AsPlayerUid(), token);
|
||||||
|
Must.NotNull(playerInfo!);
|
||||||
|
|
||||||
|
List<Character> characters = await gameRecordProvider
|
||||||
|
.GetCharactersAsync(role.AsPlayerUid(), playerInfo, token);
|
||||||
|
|
||||||
|
SpiralAbyss? spiralAbyssInfo = await gameRecordProvider
|
||||||
|
.GetSpiralAbyssAsync(role.AsPlayerUid(), SpiralAbyssSchedule.Current, token);
|
||||||
|
Must.NotNull(spiralAbyssInfo!);
|
||||||
|
|
||||||
|
PlayerRecord playerRecord = PlayerRecord.Create(role.GameUid, characters, spiralAbyssInfo);
|
||||||
|
if (await confirmAsyncFunc(playerRecord))
|
||||||
|
{
|
||||||
|
Response<string>? resp = null;
|
||||||
|
if (Response.Response.IsOk(await UploadItemsAsync(characters, token)))
|
||||||
|
{
|
||||||
|
resp = await authRequester
|
||||||
|
.PostAsync<string>($"{HutaoAPI}/Record/Upload", playerRecord, PostContentType, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
await resultAsyncFunc(resp ?? Response.Response.CreateForException($"{role.GameUid}-记录提交失败。"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步上传物品所有物品
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="characters">角色详细信息</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>响应</returns>
|
||||||
|
private async Task<Response<string>?> UploadItemsAsync(List<Character> characters, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
||||||
|
|
||||||
|
IEnumerable<Item> avatars = characters
|
||||||
|
.Select(avatar => new Item(avatar.Id, avatar.Name, avatar.Icon))
|
||||||
|
.DistinctBy(item => item.Id);
|
||||||
|
|
||||||
|
IEnumerable<Item> weapons = characters
|
||||||
|
.Select(avatar => avatar.Weapon)
|
||||||
|
.Select(weapon => new Item(weapon.Id, weapon.Name, weapon.Icon))
|
||||||
|
.DistinctBy(item => item.Id);
|
||||||
|
|
||||||
|
IEnumerable<Item> reliquaries = characters
|
||||||
|
.Select(avatars => avatars.Reliquaries)
|
||||||
|
.Flatten()
|
||||||
|
.Where(relic => relic.Position == ReliquaryPosition.FlowerOfLife)
|
||||||
|
.DistinctBy(relic => relic.Id)
|
||||||
|
.Select(relic => new Item(relic.ReliquarySet.Id, relic.ReliquarySet.Name, relic.Icon));
|
||||||
|
|
||||||
|
GenshinItemWrapper? data = new(avatars, weapons, reliquaries);
|
||||||
|
|
||||||
|
return await authRequester
|
||||||
|
.PostAsync<string>($"{HutaoAPI}/GenshinItem/Upload", data, PostContentType, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Auth
|
||||||
|
{
|
||||||
|
public Auth(string appid, string secret)
|
||||||
|
{
|
||||||
|
Appid = appid;
|
||||||
|
Secret = secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Appid { get; }
|
||||||
|
|
||||||
|
public string Secret { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Token
|
||||||
|
{
|
||||||
|
public string AccessToken { get; } = default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 命座比例
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarConstellationNum
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 角色ID
|
||||||
|
/// </summary>
|
||||||
|
public int Avatar { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 持有率
|
||||||
|
/// </summary>
|
||||||
|
public decimal HoldingRate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 各命座比率
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Rate<int>> Rate { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 出场数据
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarParticipation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 层
|
||||||
|
/// </summary>
|
||||||
|
public int Floor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色比率
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Rate<int>> AvatarUsage { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物配置数据
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarReliquaryUsage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 角色Id
|
||||||
|
/// </summary>
|
||||||
|
public int Avatar { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物比率
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Rate<ReliquarySets>> ReliquaryUsage { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器使用数据
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarWeaponUsage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 角色Id
|
||||||
|
/// </summary>
|
||||||
|
public int Avatar { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器比率
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Rate<int>> Weapons { get; set; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Converter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物套装转换器
|
||||||
|
/// </summary>
|
||||||
|
internal class ReliquarySetsConverter : JsonConverter<ReliquarySets>
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override ReliquarySets? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
string? source = reader.GetString();
|
||||||
|
|
||||||
|
if (source != null)
|
||||||
|
{
|
||||||
|
string[] sets = source.Split(';');
|
||||||
|
return new(sets.Select(set => new ReliquarySet(set)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Write(Utf8JsonWriter writer, ReliquarySets value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(string.Join(';', value));
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Item.cs
Normal file
41
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Item.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 胡桃数据库物品
|
||||||
|
/// </summary>
|
||||||
|
public class Item
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的胡桃数据库物品
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">物品Id</param>
|
||||||
|
/// <param name="name">名称</param>
|
||||||
|
/// <param name="url">链接</param>
|
||||||
|
[JsonConstructor]
|
||||||
|
public Item(int id, string name, string url)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Name = name;
|
||||||
|
Url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 物品Id
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 链接
|
||||||
|
/// </summary>
|
||||||
|
public string Url { get; }
|
||||||
|
}
|
||||||
20
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/LevelInfo.cs
Normal file
20
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/LevelInfo.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层
|
||||||
|
/// </summary>
|
||||||
|
public class LevelInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 间
|
||||||
|
/// </summary>
|
||||||
|
public int Floor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上下半 0,1
|
||||||
|
/// </summary>
|
||||||
|
public int Index { get; set; }
|
||||||
|
}
|
||||||
25
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Overview.cs
Normal file
25
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Overview.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 统计数据
|
||||||
|
/// </summary>
|
||||||
|
public class Overview
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 所有用户数量
|
||||||
|
/// </summary>
|
||||||
|
public int TotalPlayerCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当期提交深渊数据用户数量
|
||||||
|
/// </summary>
|
||||||
|
public int CollectedPlayerCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当期满星用户数量
|
||||||
|
/// </summary>
|
||||||
|
public int FullStarPlayerCount { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色圣遗物套装
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarReliquarySet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的角色圣遗物套装
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">套装Id</param>
|
||||||
|
/// <param name="count">个数</param>
|
||||||
|
public AvatarReliquarySet(int id, int count)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的角色圣遗物套装
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kvp">键值对</param>
|
||||||
|
public AvatarReliquarySet(KeyValuePair<int, int> kvp)
|
||||||
|
: this(kvp.Key, kvp.Value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 套装Id
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 个数
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色武器
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarWeapon
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的角色武器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">武器Id</param>
|
||||||
|
/// <param name="level">武器等级</param>
|
||||||
|
/// <param name="affixLevel">精炼等级</param>
|
||||||
|
public AvatarWeapon(int id, int level, int affixLevel)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Level = level;
|
||||||
|
AffixLevel = affixLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器等级
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器等级
|
||||||
|
/// </summary>
|
||||||
|
public int Level { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 精炼
|
||||||
|
/// </summary>
|
||||||
|
public int AffixLevel { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 原神物品包装器
|
||||||
|
/// </summary>
|
||||||
|
public class GenshinItemWrapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的原神物品包装器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="avatars">角色</param>
|
||||||
|
/// <param name="weapons">武器</param>
|
||||||
|
/// <param name="reliquaries">圣遗物</param>
|
||||||
|
public GenshinItemWrapper(IEnumerable<Item> avatars, IEnumerable<Item> weapons, IEnumerable<Item> reliquaries)
|
||||||
|
{
|
||||||
|
Avatars = avatars;
|
||||||
|
Weapons = weapons;
|
||||||
|
Reliquaries = reliquaries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色列表
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Item> Avatars { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器列表
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Item> Weapons { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物列表
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Item> Reliquaries { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 带有层信息的间
|
||||||
|
/// </summary>
|
||||||
|
internal class IndexedLevel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的带有层信息的间
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="floorIndex">层号</param>
|
||||||
|
/// <param name="level">间信息</param>
|
||||||
|
public IndexedLevel(int floorIndex, Level level)
|
||||||
|
{
|
||||||
|
FloorIndex = floorIndex;
|
||||||
|
Level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层号
|
||||||
|
/// </summary>
|
||||||
|
public int FloorIndex { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层信息
|
||||||
|
/// </summary>
|
||||||
|
public Level Level { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Extension;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家角色
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerAvatar
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的玩家角色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="avatar">角色</param>
|
||||||
|
internal PlayerAvatar(Character avatar)
|
||||||
|
{
|
||||||
|
Id = avatar.Id;
|
||||||
|
Level = avatar.Level;
|
||||||
|
ActivedConstellationNum = avatar.ActivedConstellationNum;
|
||||||
|
Weapon = new(avatar.Weapon.Id, avatar.Weapon.Level, avatar.Weapon.AffixLevel);
|
||||||
|
ReliquarySets = avatar.Reliquaries
|
||||||
|
.CountBy(relic => relic.ReliquarySet.Id)
|
||||||
|
.Select(kvp => new AvatarReliquarySet(kvp))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色Id
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色等级
|
||||||
|
/// </summary>
|
||||||
|
public int Level { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 命座
|
||||||
|
/// </summary>
|
||||||
|
public int ActivedConstellationNum { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 武器
|
||||||
|
/// </summary>
|
||||||
|
public AvatarWeapon Weapon { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物套装
|
||||||
|
/// </summary>
|
||||||
|
public List<AvatarReliquarySet> ReliquarySets { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家记录
|
||||||
|
/// 使用 <see cref="Create(string, List{Character}, SpiralAbyss)"/> 来构建一个实例
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerRecord
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的玩家记录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">uid</param>
|
||||||
|
/// <param name="playerAvatars">玩家角色</param>
|
||||||
|
/// <param name="playerSpiralAbyssesLevels">玩家深渊信息</param>
|
||||||
|
private PlayerRecord(string uid, IEnumerable<PlayerAvatar> playerAvatars, IEnumerable<PlayerSpiralAbyssLevel> playerSpiralAbyssesLevels)
|
||||||
|
{
|
||||||
|
Uid = uid;
|
||||||
|
PlayerAvatars = playerAvatars;
|
||||||
|
PlayerSpiralAbyssesLevels = playerSpiralAbyssesLevels;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// uid
|
||||||
|
/// </summary>
|
||||||
|
public string Uid { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家角色
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<PlayerAvatar> PlayerAvatars { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家深渊信息
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<PlayerSpiralAbyssLevel> PlayerSpiralAbyssesLevels { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 建造玩家记录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">玩家的uid</param>
|
||||||
|
/// <param name="detailAvatars">角色详情信息</param>
|
||||||
|
/// <param name="spiralAbyss">深渊信息</param>
|
||||||
|
/// <returns>玩家记录</returns>
|
||||||
|
internal static PlayerRecord Create(string uid, List<Character> detailAvatars, SpiralAbyss spiralAbyss)
|
||||||
|
{
|
||||||
|
IEnumerable<PlayerAvatar> playerAvatars = detailAvatars
|
||||||
|
.Select(avatar => new PlayerAvatar(avatar));
|
||||||
|
|
||||||
|
IEnumerable<PlayerSpiralAbyssLevel> playerSpiralAbyssLevels = spiralAbyss.Floors
|
||||||
|
.SelectMany(f => f.Levels, (f, level) => new IndexedLevel(f.Index, level))
|
||||||
|
.Select(indexedLevel => new PlayerSpiralAbyssLevel(indexedLevel));
|
||||||
|
|
||||||
|
PlayerRecord playerRecord = new(uid, playerAvatars, playerSpiralAbyssLevels);
|
||||||
|
return playerRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家深渊某间的战斗信息
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerSpiralAbyssBattle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的战斗信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="battle">战斗</param>
|
||||||
|
internal PlayerSpiralAbyssBattle(Battle battle)
|
||||||
|
{
|
||||||
|
BattleIndex = battle.Index;
|
||||||
|
AvatarIds = battle.Avatars.Select(a => a.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 战斗上下半间 0,1
|
||||||
|
/// </summary>
|
||||||
|
public int BattleIndex { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色Id列表
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<int> AvatarIds { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家深渊战斗间信息
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerSpiralAbyssLevel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的玩家深渊战斗间信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="indexedLevel">楼层</param>
|
||||||
|
internal PlayerSpiralAbyssLevel(IndexedLevel indexedLevel)
|
||||||
|
{
|
||||||
|
FloorIndex = indexedLevel.FloorIndex;
|
||||||
|
LevelIndex = indexedLevel.Level.Index;
|
||||||
|
Star = indexedLevel.Level.Star;
|
||||||
|
Battles = indexedLevel.Level.Battles
|
||||||
|
.Select(battle => new PlayerSpiralAbyssBattle(battle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层号
|
||||||
|
/// </summary>
|
||||||
|
public int FloorIndex { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 间号
|
||||||
|
/// </summary>
|
||||||
|
public int LevelIndex { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 星数
|
||||||
|
/// </summary>
|
||||||
|
public int Star { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 战斗列表 分上下半间
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<PlayerSpiralAbyssBattle> Battles { get; }
|
||||||
|
}
|
||||||
21
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Rate{T}.cs
Normal file
21
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Rate{T}.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 比率
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"><see cref="Id"/> 的类型</typeparam>
|
||||||
|
public class Rate<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表示唯一标识符的实例
|
||||||
|
/// </summary>
|
||||||
|
public T Id { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 比率
|
||||||
|
/// </summary>
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
}
|
||||||
38
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/ReliquarySet.cs
Normal file
38
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/ReliquarySet.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 圣遗物套装
|
||||||
|
/// </summary>
|
||||||
|
public class ReliquarySet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的圣遗物套装
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set">简单套装字符串</param>
|
||||||
|
public ReliquarySet(string set)
|
||||||
|
{
|
||||||
|
string[]? deconstructed = set.Split('-');
|
||||||
|
|
||||||
|
Id = int.Parse(deconstructed[0]);
|
||||||
|
Count = int.Parse(deconstructed[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 个数
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Id}-{Count}";
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/ReliquarySets.cs
Normal file
24
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/ReliquarySets.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Web.Hutao.Model.Converter;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包装圣遗物套装
|
||||||
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(ReliquarySetsConverter))]
|
||||||
|
public class ReliquarySets : List<ReliquarySet>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的圣遗物包装器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sets">圣遗物套装</param>
|
||||||
|
public ReliquarySets(IEnumerable<ReliquarySet> sets)
|
||||||
|
: base(sets)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Team.cs
Normal file
26
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Team.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Core.Json.Converter;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 队伍
|
||||||
|
/// </summary>
|
||||||
|
public class Team
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 上半
|
||||||
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(SeparatorCommaInt32EnumerableConverter))]
|
||||||
|
public IEnumerable<int> UpHalf { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下半
|
||||||
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(SeparatorCommaInt32EnumerableConverter))]
|
||||||
|
public IEnumerable<int> DownHalf { get; set; } = null!;
|
||||||
|
}
|
||||||
22
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/TeamCollocation.cs
Normal file
22
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/TeamCollocation.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组队数据
|
||||||
|
/// </summary>
|
||||||
|
public class TeamCollocation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 角色Id
|
||||||
|
/// </summary>
|
||||||
|
public int Avatar { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色搭配比率
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Rate<int>> Collocations { get; set; } = default!;
|
||||||
|
}
|
||||||
22
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/TeamCombination.cs
Normal file
22
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/TeamCombination.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 队伍上场率
|
||||||
|
/// </summary>
|
||||||
|
public record TeamCombination
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 层
|
||||||
|
/// </summary>
|
||||||
|
public LevelInfo Level { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 队伍
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Rate<Team>> Teams { get; set; } = null!;
|
||||||
|
}
|
||||||
15
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/UploadStatus.cs
Normal file
15
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/UploadStatus.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Hutao.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上传信息
|
||||||
|
/// </summary>
|
||||||
|
public class UploadStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 本期是否已经上传
|
||||||
|
/// </summary>
|
||||||
|
public bool PeriodUploaded { get; set; }
|
||||||
|
}
|
||||||
41
src/Snap.Hutao/Snap.Hutao/Web/Request/RequestHeaders.cs
Normal file
41
src/Snap.Hutao/Snap.Hutao/Web/Request/RequestHeaders.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Web.Request
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 请求头的键
|
||||||
|
/// </summary>
|
||||||
|
public class RequestHeaders
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Accept
|
||||||
|
/// </summary>
|
||||||
|
public const string Accept = "Accept";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cookie
|
||||||
|
/// </summary>
|
||||||
|
public const string Cookie = "Cookie";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// x-rpc-app_version
|
||||||
|
/// </summary>
|
||||||
|
public const string AppVersion = "x-rpc-app_version";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User-Agent
|
||||||
|
/// </summary>
|
||||||
|
public const string UserAgent = "User-Agent";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// x-rpc-client_type
|
||||||
|
/// </summary>
|
||||||
|
public const string ClientType = "x-rpc-client_type";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// X-Requested-With
|
||||||
|
/// </summary>
|
||||||
|
public const string RequestWith = "X-Requested-With";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,22 +8,10 @@ namespace Snap.Hutao.Web.Request
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class RequestOptions : Dictionary<string, string>
|
public class RequestOptions : Dictionary<string, string>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 不再使用
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("不再使用")]
|
|
||||||
public const string CommonUA2101 = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/2.10.1";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 不再使用
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("不再使用")]
|
|
||||||
public const string CommonUA2111 = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/2.11.1";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 支持更新的DS2算法
|
/// 支持更新的DS2算法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string CommonUA2161 = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/2.16.1";
|
public const string CommonUA = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/2.16.1";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 应用程序/Json
|
/// 应用程序/Json
|
||||||
@@ -35,6 +23,11 @@ namespace Snap.Hutao.Web.Request
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Hyperion = "com.mihoyo.hyperion";
|
public const string Hyperion = "com.mihoyo.hyperion";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认的客户端类型
|
||||||
|
/// </summary>
|
||||||
|
public const string DefaultClientType = "5";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备Id
|
/// 设备Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Json;
|
using Snap.Hutao.Core.Json;
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -20,6 +21,8 @@ public class Requester
|
|||||||
private readonly IInfoBarService infoBarService;
|
private readonly IInfoBarService infoBarService;
|
||||||
private readonly ILogger<Requester> logger;
|
private readonly ILogger<Requester> logger;
|
||||||
|
|
||||||
|
private bool isRequesting = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的 <see cref="Requester"/> 对象
|
/// 构造一个新的 <see cref="Requester"/> 对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -40,6 +43,11 @@ public class Requester
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public RequestOptions Headers { get; set; } = new RequestOptions();
|
public RequestOptions Headers { get; set; } = new RequestOptions();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 此请求器使用的Json解析器
|
||||||
|
/// </summary>
|
||||||
|
public Json Json { get => json; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内部使用的 <see cref="System.Net.Http.HttpClient"/>
|
/// 内部使用的 <see cref="System.Net.Http.HttpClient"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -52,7 +60,7 @@ public class Requester
|
|||||||
/// <param name="url">地址</param>
|
/// <param name="url">地址</param>
|
||||||
/// <param name="cancellationToken">取消令牌</param>
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
/// <returns>响应</returns>
|
/// <returns>响应</returns>
|
||||||
public async Task<Response<TResult>?> GetAsync<TResult>(string? url, CancellationToken cancellationToken = default)
|
public async Task<Response<TResult>?> GetAsync<TResult>(string url, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (url is null)
|
if (url is null)
|
||||||
{
|
{
|
||||||
@@ -73,14 +81,14 @@ public class Requester
|
|||||||
/// <param name="data">要发送的.NET(匿名)对象</param>
|
/// <param name="data">要发送的.NET(匿名)对象</param>
|
||||||
/// <param name="cancellationToken">取消令牌</param>
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
/// <returns>响应</returns>
|
/// <returns>响应</returns>
|
||||||
public async Task<Response<TResult>?> PostAsync<TResult>(string? url, object data, CancellationToken cancellationToken = default)
|
public async Task<Response<TResult>?> PostAsync<TResult>(string url, object data, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (url is null)
|
if (url is null)
|
||||||
{
|
{
|
||||||
return Response<TResult>.CreateForEmptyUrl();
|
return Response<TResult>.CreateForEmptyUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
string dataString = json.Stringify(data);
|
string dataString = Json.Stringify(data);
|
||||||
HttpContent content = new StringContent(dataString);
|
HttpContent content = new StringContent(dataString);
|
||||||
|
|
||||||
Task<HttpResponseMessage> PostMethod(HttpClient client, CancellationToken token) => client.PostAsync(url, content, token);
|
Task<HttpResponseMessage> PostMethod(HttpClient client, CancellationToken token) => client.PostAsync(url, content, token);
|
||||||
@@ -98,14 +106,14 @@ public class Requester
|
|||||||
/// <param name="contentType">内容类型</param>
|
/// <param name="contentType">内容类型</param>
|
||||||
/// <param name="cancellationToken">取消令牌</param>
|
/// <param name="cancellationToken">取消令牌</param>
|
||||||
/// <returns>响应</returns>
|
/// <returns>响应</returns>
|
||||||
public async Task<Response<TResult>?> PostAsync<TResult>(string? url, object data, string contentType, CancellationToken cancellationToken = default)
|
public async Task<Response<TResult>?> PostAsync<TResult>(string url, object data, string contentType, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (url is null)
|
if (url is null)
|
||||||
{
|
{
|
||||||
return Response<TResult>.CreateForEmptyUrl();
|
return Response<TResult>.CreateForEmptyUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
string dataString = json.Stringify(data);
|
string dataString = Json.Stringify(data);
|
||||||
HttpContent content = new StringContent(dataString, Encoding.UTF8, contentType);
|
HttpContent content = new StringContent(dataString, Encoding.UTF8, contentType);
|
||||||
|
|
||||||
Task<HttpResponseMessage> PostMethod(HttpClient client, CancellationToken token) => client.PostAsync(url, content, token);
|
Task<HttpResponseMessage> PostMethod(HttpClient client, CancellationToken token) => client.PostAsync(url, content, token);
|
||||||
@@ -121,6 +129,7 @@ public class Requester
|
|||||||
/// <returns>链式调用需要的实例</returns>
|
/// <returns>链式调用需要的实例</returns>
|
||||||
public Requester Reset()
|
public Requester Reset()
|
||||||
{
|
{
|
||||||
|
Verify.Operation(!isRequesting, "无法在请求发生时重置请求器");
|
||||||
Headers.Clear();
|
Headers.Clear();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -133,10 +142,48 @@ public class Requester
|
|||||||
/// <returns>链式调用需要的实例</returns>
|
/// <returns>链式调用需要的实例</returns>
|
||||||
public Requester AddHeader(string key, string value)
|
public Requester AddHeader(string key, string value)
|
||||||
{
|
{
|
||||||
|
Verify.Operation(!isRequesting, "无法在请求发生时修改请求头");
|
||||||
Headers.Add(key, value);
|
Headers.Add(key, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 接受Json
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>链式调用需要的实例</returns>
|
||||||
|
public Requester SetAcceptJson()
|
||||||
|
{
|
||||||
|
return AddHeader(RequestHeaders.Accept, RequestOptions.Json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置常规UA
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>链式调用需要的实例</returns>
|
||||||
|
public Requester SetCommonUA()
|
||||||
|
{
|
||||||
|
return AddHeader(RequestHeaders.UserAgent, RequestOptions.CommonUA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置为由米游社请求
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>链式调用需要的实例</returns>
|
||||||
|
public Requester SetRequestWithHyperion()
|
||||||
|
{
|
||||||
|
return AddHeader(RequestHeaders.RequestWith, RequestOptions.Hyperion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加Cookie请求头
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">用户</param>
|
||||||
|
/// <returns>链式调用需要的实例</returns>
|
||||||
|
public Requester SetUser(User user)
|
||||||
|
{
|
||||||
|
return AddHeader(RequestHeaders.Cookie, user.Cookie ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在请求前准备 <see cref="System.Net.Http.HttpClient"/>
|
/// 在请求前准备 <see cref="System.Net.Http.HttpClient"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -151,14 +198,15 @@ public class Requester
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Response<TResult>?> RequestAsync<TResult>(
|
private async Task<Response<TResult>?> RequestAsync<TResult>(
|
||||||
Func<HttpClient, CancellationToken, Task<HttpResponseMessage>> requestFunc,
|
Func<HttpClient, CancellationToken, Task<HttpResponseMessage>> requestAsyncFunc,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
isRequesting = true;
|
||||||
PrepareHttpClient();
|
PrepareHttpClient();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await requestFunc
|
HttpResponseMessage response = await requestAsyncFunc
|
||||||
.Invoke(HttpClient, cancellationToken)
|
.Invoke(HttpClient, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -166,12 +214,15 @@ public class Requester
|
|||||||
.ReadAsStringAsync(cancellationToken)
|
.ReadAsStringAsync(cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
Response<TResult>? resp = json.ToObject<Response<TResult>>(contentString);
|
Response<TResult>? resp = Json.ToObject<Response<TResult>>(contentString);
|
||||||
if (resp?.ToString() is string representable)
|
|
||||||
|
if (resp is null)
|
||||||
{
|
{
|
||||||
infoBarService.Information(representable);
|
return Response<TResult>.CreateForJsonException(contentString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidateResponse(resp);
|
||||||
|
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -181,7 +232,17 @@ public class Requester
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
isRequesting = false;
|
||||||
logger.LogInformation("Request Completed");
|
logger.LogInformation("Request Completed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ValidateResponse<TResult>(Response<TResult> resp)
|
||||||
|
{
|
||||||
|
// 未知的返回代码
|
||||||
|
if (!Enum.IsDefined(typeof(KnownReturnCode), resp.ReturnCode))
|
||||||
|
{
|
||||||
|
infoBarService.Information(resp.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,11 @@ namespace Snap.Hutao.Web.Response;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public enum KnownReturnCode
|
public enum KnownReturnCode
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Url为 空
|
||||||
|
/// </summary>
|
||||||
|
JsonParseIssue = -2000000002,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Url为 空
|
/// Url为 空
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ public class ListWrapper<T>
|
|||||||
/// 列表
|
/// 列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("list")]
|
[JsonPropertyName("list")]
|
||||||
public List<T>? List { get; set; }
|
public List<T> List { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class Response
|
|||||||
/// 消息
|
/// 消息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("message")]
|
[JsonPropertyName("message")]
|
||||||
public string? Message { get; set; }
|
public string Message { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 响应是否正常
|
/// 响应是否正常
|
||||||
|
|||||||
@@ -31,6 +31,20 @@ public class Response<TData> : Response
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个失败的响应
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息</param>
|
||||||
|
/// <returns>响应</returns>
|
||||||
|
public static Response<TData> CreateForJsonException(string message)
|
||||||
|
{
|
||||||
|
return new Response<TData>()
|
||||||
|
{
|
||||||
|
ReturnCode = (int)KnownReturnCode.InternalFailure,
|
||||||
|
Message = message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个空Url的响应
|
/// 构造一个空Url的响应
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user