mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix up oversea launcher support
This commit is contained in:
@@ -14,9 +14,9 @@ csharp_style_expression_bodied_accessors = when_on_single_line:silent
|
||||
csharp_style_expression_bodied_lambdas = when_on_single_line:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
csharp_style_var_for_built_in_types = false:silent
|
||||
csharp_style_var_when_type_is_apparent = false:silent
|
||||
csharp_style_var_elsewhere = false:silent
|
||||
csharp_style_var_for_built_in_types = false:warning
|
||||
csharp_style_var_when_type_is_apparent = false:warning
|
||||
csharp_style_var_elsewhere = false:warning
|
||||
csharp_prefer_simple_using_statement = false:suggestion
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_style_namespace_declarations = file_scoped:silent
|
||||
@@ -24,11 +24,11 @@ csharp_prefer_static_local_function = false:suggestion
|
||||
|
||||
[*.{cs,vb}]
|
||||
end_of_line = crlf
|
||||
dotnet_style_qualification_for_field = false:silent
|
||||
dotnet_style_qualification_for_property = false:silent
|
||||
dotnet_style_qualification_for_method = false:silent
|
||||
dotnet_style_qualification_for_event = false:silent
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
dotnet_code_quality_unused_parameters = non_public:suggestion
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
indent_size = 4
|
||||
@@ -47,8 +47,8 @@ dotnet_style_prefer_auto_properties = false:silent
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
@@ -165,6 +165,7 @@ dotnet_diagnostic.CA1805.severity = suggestion
|
||||
dotnet_diagnostic.VSTHRD111.severity = suggestion
|
||||
csharp_style_prefer_top_level_statements = true:silent
|
||||
csharp_style_prefer_readonly_struct = true:suggestion
|
||||
csharp_style_prefer_utf8_string_literals = true:suggestion
|
||||
|
||||
[*.vb]
|
||||
#### 命名样式 ####
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration;
|
||||
|
||||
/// <summary>
|
||||
/// 高亮TODO
|
||||
/// </summary>
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
internal class TodoAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private static readonly DiagnosticDescriptor Descriptor =
|
||||
new("SH0001", "TODO 项尚未实现", "此 TODO 项需要实现", "Standard", DiagnosticSeverity.Info, true);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get => ImmutableArray.Create(Descriptor); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterSyntaxTreeAction(HandleSyntaxTree);
|
||||
}
|
||||
|
||||
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
|
||||
{
|
||||
SyntaxNode root = context.Tree.GetCompilationUnitRoot(context.CancellationToken);
|
||||
foreach (SyntaxTrivia node in root.DescendantTrivia(descendIntoTrivia: true))
|
||||
{
|
||||
switch (node.Kind())
|
||||
{
|
||||
case SyntaxKind.SingleLineCommentTrivia:
|
||||
case SyntaxKind.MultiLineCommentTrivia:
|
||||
string text = node.ToString().ToLowerInvariant();
|
||||
if (text.Contains("todo:"))
|
||||
{
|
||||
string hint = node.ToString().Substring(text.IndexOf("todo:") + 6);
|
||||
DiagnosticDescriptor descriptor = new("SH0001", "TODO 项尚未实现", hint, "Standard", DiagnosticSeverity.Info, true);
|
||||
context.ReportDiagnostic(Diagnostic.Create(descriptor, node.GetLocation()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,10 +153,11 @@ public class User : ObservableObject
|
||||
|
||||
using (IServiceScope scope = Ioc.Default.CreateScope())
|
||||
{
|
||||
UserInfo = await scope.ServiceProvider
|
||||
Web.Response.Response<UserFullInfoWrapper> response = await scope.ServiceProvider
|
||||
.GetRequiredService<UserClient2>()
|
||||
.GetUserFullInfoAsync(Entity, token)
|
||||
.ConfigureAwait(false);
|
||||
UserInfo = response.Data?.UserInfo;
|
||||
|
||||
// 自动填充 Ltoken
|
||||
if (Ltoken == null)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
@@ -34,29 +35,39 @@ internal partial class AnnouncementService : IAnnouncementService
|
||||
// 缓存中存在记录,直接返回
|
||||
if (memoryCache.TryGetValue(CacheKey, out object? cache))
|
||||
{
|
||||
return Must.NotNull((AnnouncementWrapper)cache!);
|
||||
return (AnnouncementWrapper)cache!;
|
||||
}
|
||||
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
AnnouncementWrapper? wrapper = await announcementClient
|
||||
Response<AnnouncementWrapper> announcementWrapperResponse = await announcementClient
|
||||
.GetAnnouncementsAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
List<AnnouncementContent> contents = await announcementClient
|
||||
.GetAnnouncementContentsAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Dictionary<int, string> contentMap = contents
|
||||
.ToDictionary(id => id.AnnId, content => content.Content);
|
||||
if (announcementWrapperResponse.IsOk())
|
||||
{
|
||||
AnnouncementWrapper wrapper = announcementWrapperResponse.Data;
|
||||
Response<ListWrapper<AnnouncementContent>> announcementContentResponse = await announcementClient
|
||||
.GetAnnouncementContentsAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Must.NotNull(wrapper!);
|
||||
if (announcementContentResponse.IsOk())
|
||||
{
|
||||
List<AnnouncementContent> contents = announcementContentResponse.Data.List;
|
||||
|
||||
// 将活动公告置于上方
|
||||
wrapper.List.Reverse();
|
||||
Dictionary<int, string> contentMap = contents
|
||||
.ToDictionary(id => id.AnnId, content => content.Content);
|
||||
|
||||
// 将公告内容联入公告列表
|
||||
JoinAnnouncements(contentMap, wrapper.List);
|
||||
// 将活动公告置于上方
|
||||
wrapper.List.Reverse();
|
||||
|
||||
return memoryCache.Set(CacheKey, wrapper, TimeSpan.FromMinutes(30));
|
||||
// 将公告内容联入公告列表
|
||||
JoinAnnouncements(contentMap, wrapper.List);
|
||||
|
||||
return memoryCache.Set(CacheKey, wrapper, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
return null!;
|
||||
}
|
||||
|
||||
private static void JoinAnnouncements(Dictionary<int, string> contentMap, List<AnnouncementListWrapper> announcementListWrappers)
|
||||
|
||||
@@ -36,16 +36,10 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
|
||||
public static string GetCacheFile(string path)
|
||||
{
|
||||
string folder = Path.GetDirectoryName(path) ?? string.Empty;
|
||||
var cacheDataPath = Path.Combine(folder, @"YuanShen_Data\webCaches\Cache\Cache_Data\data_2");
|
||||
var cacheDataPathIntl = Path.Combine(folder, @"GenshinImpact_Data\webCaches\Cache\Cache_Data\data_2");
|
||||
if (File.Exists(cacheDataPath))
|
||||
{
|
||||
return cacheDataPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return cacheDataPathIntl;
|
||||
}
|
||||
string cacheDataPathChinese = Path.Combine(folder, @"YuanShen_Data\webCaches\Cache\Cache_Data\data_2");
|
||||
string cacheDataPathOversea = Path.Combine(folder, @"GenshinImpact_Data\webCaches\Cache\Cache_Data\data_2");
|
||||
|
||||
return File.Exists(cacheDataPathChinese) ? cacheDataPathChinese : cacheDataPathOversea;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -200,6 +200,7 @@ internal class GameService : IGameService, IDisposable
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SuppressMessage("", "IDE0046")]
|
||||
public bool IsGameRunning()
|
||||
{
|
||||
if (gameSemaphore.CurrentCount == 0)
|
||||
@@ -207,7 +208,8 @@ internal class GameService : IGameService, IDisposable
|
||||
return true;
|
||||
}
|
||||
|
||||
return Process.GetProcessesByName("YuanShen.exe").Any() || Process.GetProcessesByName("GenshinImpact.exe").Any();
|
||||
return Process.GetProcessesByName("YuanShen.exe").Any()
|
||||
|| Process.GetProcessesByName("GenshinImpact.exe").Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Factory.Abstraction;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Pickers;
|
||||
@@ -30,26 +31,21 @@ internal class ManualGameLocator : IGameLocator
|
||||
/// <inheritdoc/>
|
||||
public Task<ValueResult<bool, string>> LocateGamePathAsync()
|
||||
{
|
||||
List<string> filenames = new List<string>()
|
||||
{
|
||||
"YuanShen.exe",
|
||||
"GenshinImpact.exe",
|
||||
};
|
||||
List<string> filenames = new(2) { "YuanShen.exe", "GenshinImpact.exe", };
|
||||
return LocateInternalAsync(filenames);
|
||||
}
|
||||
|
||||
private async Task<ValueResult<bool, string>> LocateInternalAsync(List<string> fileNames)
|
||||
{
|
||||
FileOpenPicker picker = pickerFactory.GetFileOpenPicker(PickerLocationId.Desktop, "选择游戏本体", ".exe");
|
||||
if (await picker.PickSingleFileAsync() is StorageFile file)
|
||||
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
|
||||
|
||||
if (isPickerOk)
|
||||
{
|
||||
string path = file.Path;
|
||||
foreach (string fileName in fileNames)
|
||||
string fileName = System.IO.Path.GetFileName(file);
|
||||
if (fileNames.Contains(fileName))
|
||||
{
|
||||
if (path.Contains(fileName))
|
||||
{
|
||||
return new(true, path);
|
||||
}
|
||||
return new(true, file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,13 @@ internal partial class UnityLogGameLocator : IGameLocator
|
||||
{
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string logFilePath = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
|
||||
string logFilePathIntl = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt");
|
||||
string logFilePathChinese = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
|
||||
string logFilePathOvsesea = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt");
|
||||
|
||||
using (TempFile? tempFile = TempFile.CreateFromFileCopy(logFilePath), tempFileIntl = TempFile.CreateFromFileCopy(logFilePathIntl))
|
||||
// We need to fallback the the cn server rather than os server.
|
||||
string logFilePathFinal = File.Exists(logFilePathOvsesea) ? logFilePathOvsesea : logFilePathChinese;
|
||||
|
||||
using (TempFile? tempFile = TempFile.CreateFromFileCopy(logFilePathFinal))
|
||||
{
|
||||
if (tempFile != null)
|
||||
{
|
||||
@@ -40,30 +43,13 @@ internal partial class UnityLogGameLocator : IGameLocator
|
||||
string fullPath = Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
|
||||
return new(true, fullPath);
|
||||
}
|
||||
else if (tempFileIntl != null)
|
||||
{
|
||||
string content = File.ReadAllText(tempFileIntl.Path);
|
||||
|
||||
Match matchResult = WarmupFileLineIntl().Match(content);
|
||||
if (!matchResult.Success)
|
||||
{
|
||||
return new(false, $"在 Unity 日志文件中找不到游戏路径");
|
||||
}
|
||||
|
||||
string entryName = matchResult.Groups[0].Value.Replace("_Data", ".exe");
|
||||
string fullPath = Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
|
||||
return new(true, fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, $"找不到 Unity 日志文件:\n{logFilePath}\n{logFilePathIntl}");
|
||||
return new(false, $"找不到 Unity 日志文件");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"(?m).:/.+YuanShen_Data")]
|
||||
[GeneratedRegex(@"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)")]
|
||||
private static partial Regex WarmupFileLine();
|
||||
|
||||
[GeneratedRegex(@"(?m).:/.+GenshinImpact_Data")]
|
||||
private static partial Regex WarmupFileLineIntl();
|
||||
}
|
||||
@@ -87,7 +87,7 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
|
||||
else
|
||||
{
|
||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
infoBarService.Warning("尚未安装 WebView2 运行时。");
|
||||
infoBarService.Warning("尚未安装 WebView2 Runtime");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ internal class SettingViewModel : ObservableObject
|
||||
|
||||
Experimental = experimental;
|
||||
|
||||
isEmptyHistoryWishVisibleEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.IsEmptyHistoryWishVisible, true.ToString());
|
||||
isEmptyHistoryWishVisibleEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.IsEmptyHistoryWishVisible, SettingEntryHelper.TrueString);
|
||||
IsEmptyHistoryWishVisible = bool.Parse(isEmptyHistoryWishVisibleEntry.Value!);
|
||||
|
||||
selectedBackdropTypeEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.SystemBackdropType, BackdropType.Mica.ToString());
|
||||
|
||||
@@ -40,7 +40,7 @@ internal class EnkaClient
|
||||
/// <param name="playerUid">玩家Uid</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>Enka API 响应</returns>
|
||||
public Task<EnkaResponse?> GetForwardDataAsync(PlayerUid playerUid, CancellationToken token)
|
||||
public Task<EnkaResponse?> GetForwardDataAsync(PlayerUid playerUid, CancellationToken token = default)
|
||||
{
|
||||
return httpClient.TryCatchGetFromJsonAsync<EnkaResponse>(string.Format(EnkaAPIHutaoForward, playerUid.Value), options, logger, token);
|
||||
}
|
||||
@@ -51,8 +51,8 @@ internal class EnkaClient
|
||||
/// <param name="playerUid">玩家Uid</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>Enka API 响应</returns>
|
||||
public Task<EnkaResponse?> GetDataAsync(PlayerUid playerUid, CancellationToken token)
|
||||
public Task<EnkaResponse?> GetDataAsync(PlayerUid playerUid, CancellationToken token = default)
|
||||
{
|
||||
return httpClient.TryCatchGetFromJsonAsync<EnkaResponse>(string.Format(EnkaAPI, playerUid.Value), options, logger, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ internal class AccountClient
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>用户角色信息</returns>
|
||||
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
|
||||
public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
|
||||
public async Task<Response<GameAuthKey>> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
|
||||
{
|
||||
Response<GameAuthKey>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Stoken)
|
||||
@@ -53,6 +53,6 @@ internal class AccountClient
|
||||
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.AppAuthGenAuthKey, data, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data;
|
||||
return Response.Response.DefaultIfNull(resp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Web.Hoyolab.Annotation;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Bbs.User;
|
||||
|
||||
/// <summary>
|
||||
/// 用户信息客户端
|
||||
/// </summary>
|
||||
[HttpClient(HttpClientConfigration.XRpc)]
|
||||
internal class UserClient
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly JsonSerializerOptions options;
|
||||
private readonly ILogger<UserClient> logger;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的用户信息客户端
|
||||
/// </summary>
|
||||
/// <param name="httpClient">http客户端</param>
|
||||
/// <param name="options">Json序列化选项</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
public UserClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<UserClient> logger)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前用户详细信息
|
||||
/// </summary>
|
||||
/// <param name="user">用户</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>详细信息</returns>
|
||||
[ApiInformation(Cookie = CookieType.Ltoken)]
|
||||
public async Task<UserInfo?> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
|
||||
{
|
||||
Response<UserFullInfoWrapper>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Ltoken)
|
||||
.SetReferer(ApiEndpoints.BbsReferer) // Otherwise HTTP 403
|
||||
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfo, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data?.UserInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前用户详细信息
|
||||
/// </summary>
|
||||
/// <param name="user">用户</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>详细信息</returns>
|
||||
[ApiInformation(Cookie = CookieType.Ltoken)]
|
||||
public async Task<UserInfo?> GetUserFullInfoByUidAsync(Model.Entity.User user, CancellationToken token = default)
|
||||
{
|
||||
Response<UserFullInfoWrapper>? resp = await httpClient
|
||||
|
||||
// .SetUser(user, CookieType.Cookie)
|
||||
.SetReferer(ApiEndpoints.BbsReferer) // Otherwise HTTP 403
|
||||
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfoQuery(user.Aid!), options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data?.UserInfo;
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ internal class UserClient2
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>详细信息</returns>
|
||||
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
|
||||
public async Task<UserInfo?> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
|
||||
public async Task<Response<UserFullInfoWrapper>> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
|
||||
{
|
||||
Response<UserFullInfoWrapper>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Stoken)
|
||||
@@ -49,6 +49,6 @@ internal class UserClient2
|
||||
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfoQuery(user.Aid!), options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data?.UserInfo;
|
||||
return Response.Response.DefaultIfNull(resp);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||
|
||||
/// <summary>
|
||||
/// 指示此客户端使用动态密钥
|
||||
/// 会为依附类自动生成相关代码
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal class UseDynamicSecretAttribute : Attribute
|
||||
|
||||
@@ -36,13 +36,13 @@ internal class AnnouncementClient
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>公告列表</returns>
|
||||
public async Task<AnnouncementWrapper?> GetAnnouncementsAsync(CancellationToken cancellationToken = default)
|
||||
public async Task<Response<AnnouncementWrapper>> GetAnnouncementsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
Response<AnnouncementWrapper>? resp = await httpClient
|
||||
.TryCatchGetFromJsonAsync<Response<AnnouncementWrapper>>(ApiEndpoints.AnnList, jsonSerializerOptions, logger, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data;
|
||||
return Response.Response.DefaultIfNull(resp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -50,13 +50,12 @@ internal class AnnouncementClient
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>公告内容列表</returns>
|
||||
public async Task<List<AnnouncementContent>> GetAnnouncementContentsAsync(CancellationToken cancellationToken = default)
|
||||
public async Task<Response<ListWrapper<AnnouncementContent>>> GetAnnouncementContentsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
Response<ListWrapper<AnnouncementContent>>? resp = await httpClient
|
||||
.TryCatchGetFromJsonAsync<Response<ListWrapper<AnnouncementContent>>>(ApiEndpoints.AnnContent, jsonSerializerOptions, logger, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return EnumerableExtension.EmptyIfNull(resp?.Data?.List);
|
||||
return Response.Response.DefaultIfNull(resp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ internal static class HttpClientExtensions
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
@@ -38,7 +38,7 @@ internal static class HttpClientExtensions
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
}
|
||||
catch (IOException ex)
|
||||
catch (SocketException ex)
|
||||
{
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
@@ -59,7 +59,7 @@ internal static class HttpClientExtensions
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
@@ -69,7 +69,7 @@ internal static class HttpClientExtensions
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
}
|
||||
catch (IOException ex)
|
||||
catch (SocketException ex)
|
||||
{
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
@@ -89,7 +89,7 @@ internal static class HttpClientExtensions
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (SocketException)
|
||||
catch (IOException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -97,7 +97,7 @@ internal static class HttpClientExtensions
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (IOException)
|
||||
catch (SocketException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -22,13 +22,9 @@ public class Response : ISupportValidation
|
||||
{
|
||||
ReturnCode = returnCode;
|
||||
Message = message;
|
||||
|
||||
if (!Validate())
|
||||
{
|
||||
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ToString());
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
Ioc.Default.GetRequiredService<ILogger<Response>>().LogInformation("Response [{resp}]", ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -43,6 +39,18 @@ public class Response : ISupportValidation
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 返回本体或带有消息提示的默认值
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">类型</typeparam>
|
||||
/// <param name="response">本体</param>
|
||||
/// <returns>本体或默认值,当本体为 null 时 返回默认值</returns>
|
||||
public static Response<TData> DefaultIfNull<TData>(Response<TData>? response)
|
||||
{
|
||||
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
|
||||
return response ?? new(0x26F19335, "请求异常", default);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Validate()
|
||||
{
|
||||
@@ -90,7 +98,17 @@ public class Response<TData> : Response, IJsResult
|
||||
[MemberNotNullWhen(true, nameof(Data))]
|
||||
public bool IsOk()
|
||||
{
|
||||
return ReturnCode == 0;
|
||||
if (ReturnCode == 0)
|
||||
{
|
||||
#pragma warning disable CS8775
|
||||
return true;
|
||||
#pragma warning restore CS8775
|
||||
}
|
||||
else
|
||||
{
|
||||
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -9,20 +9,22 @@ namespace Snap.Hutao.Win32;
|
||||
/// <summary>
|
||||
/// 内存拓展 for <see cref="Memory{T}"/> and <see cref="Span{T}"/>
|
||||
/// </summary>
|
||||
internal static class MemoryExtensions
|
||||
internal static class MemoryExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 将 __CHAR_256 转换到 字符串
|
||||
/// </summary>
|
||||
/// <param name="char256">目标字符数组</param>
|
||||
/// <returns>结果字符串</returns>
|
||||
public static unsafe string AsString(this __CHAR_256 char256)
|
||||
public static unsafe string AsString(this ref __CHAR_256 char256)
|
||||
{
|
||||
byte* pszModule = (byte*)&char256;
|
||||
return Encoding.UTF8.GetString(pszModule, StringLength(pszModule));
|
||||
fixed (CHAR* pszModule = &char256._0)
|
||||
{
|
||||
return Encoding.UTF8.GetString((byte*)pszModule, StringLength(pszModule));
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe int StringLength(byte* pszStr)
|
||||
private static unsafe int StringLength(CHAR* pszStr)
|
||||
{
|
||||
int len = 0;
|
||||
while (*pszStr++ != 0)
|
||||
@@ -63,14 +63,4 @@ internal static class StructMarshal
|
||||
{
|
||||
return moduleEntry32.dwSize == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断结构实例是否为默认结构
|
||||
/// </summary>
|
||||
/// <param name="uid">待测试的结构</param>
|
||||
/// <returns>是否为默认结构</returns>
|
||||
public static bool IsDefault(PlayerUid uid)
|
||||
{
|
||||
return uid.Value == default;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user