unify response behavior

This commit is contained in:
DismissedLight
2023-12-17 16:48:16 +08:00
parent e60956c5c8
commit 10b282a88a
19 changed files with 303 additions and 266 deletions

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -65,6 +66,20 @@ public sealed class JsonSerializeTest
Assert.AreEqual(result, """{"Array":"AQIDBAU="}""");
}
[TestMethod]
public void InterfaceDefaultMethodCanSerializeActualInstanceMember()
{
ISampleInterface sample = new SampleClassImplementedInterface()
{
A = 1,
B = 2,
};
string result = sample.ToJson();
Console.WriteLine(result);
Assert.AreEqual(result, """{"A":1,"B":2}""");
}
private sealed class SampleDelegatePropertyClass
{
public int A { get => B; set => B = value; }
@@ -81,4 +96,22 @@ public sealed class JsonSerializeTest
{
public byte[]? Array { get; set; }
}
private sealed class SampleClassImplementedInterface : ISampleInterface
{
public int A { get; set; }
public int B { get; set; }
}
[JsonDerivedType(typeof(SampleClassImplementedInterface))]
private interface ISampleInterface
{
int A { get; set; }
string ToJson()
{
return JsonSerializer.Serialize(this);
}
}
}

View File

@@ -21,6 +21,7 @@ K32EnumProcessModules
K32GetModuleBaseNameW
K32GetModuleInformation
ReadProcessMemory
SetConsoleTitle
SetEvent
VirtualAlloc
VirtualAllocEx

View File

@@ -15,6 +15,7 @@ internal sealed class ConsoleWindowLifeTime : IDisposable
if (LocalSetting.Get(SettingKeys.IsAllocConsoleDebugModeEnabled, false))
{
consoleWindowAllocated = AllocConsole();
SetConsoleTitle("Snap Hutao Debug Console");
}
}

View File

@@ -86,7 +86,7 @@ internal class MiHoYoJSBridge
/// </summary>
/// <param name="param">参数</param>
/// <returns>响应</returns>
protected virtual async ValueTask<IJsResult?> ClosePageAsync(JsParam param)
protected virtual async ValueTask<IJsBridgeResult?> ClosePageAsync(JsParam param)
{
await taskContext.SwitchToMainThreadAsync();
if (coreWebView2.CanGoBack)
@@ -106,7 +106,7 @@ internal class MiHoYoJSBridge
/// </summary>
/// <param name="param">参数</param>
/// <returns>响应</returns>
protected virtual IJsResult? ConfigureShare(JsParam param)
protected virtual IJsBridgeResult? ConfigureShare(JsParam param)
{
return null;
}
@@ -116,7 +116,7 @@ internal class MiHoYoJSBridge
/// </summary>
/// <param name="jsParam">参数</param>
/// <returns>响应</returns>
protected virtual async ValueTask<IJsResult?> GetActionTicketAsync(JsParam<ActionTypePayload> jsParam)
protected virtual async ValueTask<IJsBridgeResult?> GetActionTicketAsync(JsParam<ActionTypePayload> jsParam)
{
return await serviceProvider
.GetRequiredService<AuthClient>()
@@ -299,7 +299,7 @@ internal class MiHoYoJSBridge
}
}
protected virtual async ValueTask<IJsResult?> PushPageAsync(JsParam<PushPagePayload> param)
protected virtual async ValueTask<IJsBridgeResult?> PushPageAsync(JsParam<PushPagePayload> param)
{
const string bbsSchema = "mihoyobbs://";
string pageUrl = param.Payload.Page;
@@ -323,7 +323,7 @@ internal class MiHoYoJSBridge
return null;
}
protected virtual IJsResult? Share(JsParam<SharePayload> param)
protected virtual IJsBridgeResult? Share(JsParam<SharePayload> param)
{
return new JsResult<Dictionary<string, string>>()
{
@@ -334,47 +334,47 @@ internal class MiHoYoJSBridge
};
}
protected virtual ValueTask<IJsResult?> ShowAlertDialogAsync(JsParam param)
protected virtual ValueTask<IJsBridgeResult?> ShowAlertDialogAsync(JsParam param)
{
return ValueTask.FromException<IJsResult?>(new NotSupportedException());
return ValueTask.FromException<IJsBridgeResult?>(new NotSupportedException());
}
protected virtual IJsResult? StartRealPersonValidation(JsParam param)
protected virtual IJsBridgeResult? StartRealPersonValidation(JsParam param)
{
throw new NotImplementedException();
}
protected virtual IJsResult? StartRealnameAuth(JsParam param)
protected virtual IJsBridgeResult? StartRealnameAuth(JsParam param)
{
throw new NotImplementedException();
}
protected virtual IJsResult? GenAuthKey(JsParam param)
protected virtual IJsBridgeResult? GenAuthKey(JsParam param)
{
throw new NotImplementedException();
}
protected virtual IJsResult? GenAppAuthKey(JsParam param)
protected virtual IJsBridgeResult? GenAppAuthKey(JsParam param)
{
throw new NotImplementedException();
}
protected virtual IJsResult? OpenSystemBrowser(JsParam param)
protected virtual IJsBridgeResult? OpenSystemBrowser(JsParam param)
{
throw new NotImplementedException();
}
protected virtual IJsResult? SaveLoginTicket(JsParam param)
protected virtual IJsBridgeResult? SaveLoginTicket(JsParam param)
{
throw new NotImplementedException();
}
protected virtual ValueTask<IJsResult?> GetNotificationSettingsAsync(JsParam param)
protected virtual ValueTask<IJsBridgeResult?> GetNotificationSettingsAsync(JsParam param)
{
throw new NotImplementedException();
}
protected virtual IJsResult? ShowToast(JsParam param)
protected virtual IJsBridgeResult? ShowToast(JsParam param)
{
throw new NotImplementedException();
}
@@ -430,7 +430,7 @@ internal class MiHoYoJSBridge
logger.LogInformation("[OnMessage]\nMethod : {method}\nPayload : {payload}\nCallback: {callback}", param.Method, param.Payload, param.Callback);
using (await webMessageSemaphore.EnterAsync().ConfigureAwait(false))
{
IJsResult? result = await TryGetJsResultFromJsParamAsync(param).ConfigureAwait(false);
IJsBridgeResult? result = await TryGetJsResultFromJsParamAsync(param).ConfigureAwait(false);
if (result is not null && param.Callback is not null)
{
@@ -440,13 +440,13 @@ internal class MiHoYoJSBridge
}
[SuppressMessage("", "CA2254")]
private IJsResult? LogUnhandledMessage(string message, params object?[] param)
private IJsBridgeResult? LogUnhandledMessage(string message, params object?[] param)
{
logger.LogWarning(message, param);
return default;
}
private async ValueTask<IJsResult?> TryGetJsResultFromJsParamAsync(JsParam param)
private async ValueTask<IJsBridgeResult?> TryGetJsResultFromJsParamAsync(JsParam param)
{
if (coreWebView2.IsDisposed())
{

View File

@@ -7,11 +7,10 @@ namespace Snap.Hutao.Web.Bridge.Model;
/// 指示此为Js结果
/// </summary>
[HighQuality]
internal interface IJsResult
internal interface IJsBridgeResult
{
/// <summary>
/// 转换到Json字符串表示
/// </summary>
/// <returns>JSON字符串</returns>
string ToJson();
}
string ToJson()
{
return JsonSerializer.Serialize(this);
}
}

View File

@@ -9,7 +9,7 @@ namespace Snap.Hutao.Web.Bridge.Model;
/// </summary>
/// <typeparam name="TData">内部数据类型</typeparam>
[HighQuality]
internal sealed class JsResult<TData> : IJsResult
internal sealed class JsResult<TData> : IJsBridgeResult
{
/// <summary>
/// 代码
@@ -28,10 +28,4 @@ internal sealed class JsResult<TData> : IJsResult
/// </summary>
[JsonPropertyName("data")]
public TData Data { get; set; } = default!;
/// <inheritdoc/>
string IJsResult.ToJson()
{
return JsonSerializer.Serialize(this);
}
}

View File

@@ -50,10 +50,8 @@ internal sealed partial class BindingClient
string actionTicket = actionTicketResponse.Data.Ticket;
return await GetUserGameRolesByActionTicketAsync(actionTicket, user, token).ConfigureAwait(false);
}
else
{
return Response.Response.DefaultIfNull<ListWrapper<UserGameRole>, ActionTicketWrapper>(actionTicketResponse);
}
return Response.Response.CloneReturnCodeAndMessage<ListWrapper<UserGameRole>, ActionTicketWrapper>(actionTicketResponse);
}
}

View File

@@ -39,7 +39,7 @@ internal sealed partial class HomaGachaLogClient
.TryCatchSendAsync<HutaoResponse<GachaEventStatistics>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -60,7 +60,7 @@ internal sealed partial class HomaGachaLogClient
.TryCatchSendAsync<HutaoResponse<GachaDistribution>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -80,7 +80,7 @@ internal sealed partial class HomaGachaLogClient
.TryCatchSendAsync<HutaoResponse<List<GachaEntry>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -101,7 +101,7 @@ internal sealed partial class HomaGachaLogClient
.TryCatchSendAsync<HutaoResponse<EndIds>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -125,7 +125,7 @@ internal sealed partial class HomaGachaLogClient
.TryCatchSendAsync<HutaoResponse<List<GachaItem>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -149,7 +149,7 @@ internal sealed partial class HomaGachaLogClient
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -170,7 +170,7 @@ internal sealed partial class HomaGachaLogClient
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
private sealed class UidAndEndIds

View File

@@ -34,7 +34,7 @@ internal sealed partial class HutaoAsAServiceClient
.TryCatchSendAsync<HutaoResponse<List<Announcement>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
public async ValueTask<HutaoResponse> UploadAnnouncementAsync(UploadAnnouncement uploadAnnouncement, CancellationToken token = default)
@@ -49,7 +49,7 @@ internal sealed partial class HutaoAsAServiceClient
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
public async ValueTask<HutaoResponse> GachaLogCompensationAsync(int days, CancellationToken token = default)
@@ -64,7 +64,7 @@ internal sealed partial class HutaoAsAServiceClient
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
public async ValueTask<HutaoResponse> GachaLogDesignationAsync(string userName, int days, CancellationToken token = default)
@@ -79,6 +79,6 @@ internal sealed partial class HutaoAsAServiceClient
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Web.Hutao.Response;
using Snap.Hutao.Web.Request.Builder;
using Snap.Hutao.Web.Request.Builder.Abstraction;
using Snap.Hutao.Web.Response;
@@ -17,7 +18,7 @@ internal sealed partial class HutaoInfrastructureClient
private readonly ILogger<HutaoInfrastructureClient> logger;
private readonly HttpClient httpClient;
public async ValueTask<Response<IPInformation>> GetIPInformationAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<IPInformation>> GetIPInformationAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.Ip)
@@ -26,4 +27,24 @@ internal sealed partial class HutaoInfrastructureClient
Response<IPInformation>? resp = await builder.TryCatchSendAsync<Response<IPInformation>>(httpClient, logger, token).ConfigureAwait(false);
return Web.Response.Response.DefaultIfNull(resp);
}
public async ValueTask<HutaoResponse<HutaoVersionInformation>> GetHutaoVersionInfomationAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.PatchSnapHutao)
.Get();
Response<HutaoVersionInformation>? resp = await builder.TryCatchSendAsync<Response<HutaoVersionInformation>>(httpClient, logger, token).ConfigureAwait(false);
return Web.Response.Response.DefaultIfNull(resp);
}
public async ValueTask<HutaoResponse<YaeVersionInformation>> GetYaeVersionInformationAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.PatchYaeAchievement)
.Get();
Response<YaeVersionInformation>? resp = await builder.TryCatchSendAsync<Response<YaeVersionInformation>>(httpClient, logger, token).ConfigureAwait(false);
return Web.Response.Response.DefaultIfNull(resp);
}
}

View File

@@ -67,7 +67,7 @@ internal sealed partial class HutaoPassportClient
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -95,7 +95,7 @@ internal sealed partial class HutaoPassportClient
.TryCatchSendAsync<HutaoResponse<string>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
public async ValueTask<HutaoResponse> UnregisterAsync(string email, string password, string verifyCode, CancellationToken token = default)
@@ -117,7 +117,7 @@ internal sealed partial class HutaoPassportClient
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -145,7 +145,7 @@ internal sealed partial class HutaoPassportClient
.TryCatchSendAsync<HutaoResponse<string>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -171,7 +171,7 @@ internal sealed partial class HutaoPassportClient
.TryCatchSendAsync<HutaoResponse<string>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -191,7 +191,7 @@ internal sealed partial class HutaoPassportClient
.TryCatchSendAsync<HutaoResponse<UserInfo>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
private static string Encrypt(string text)

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hutao;
internal sealed class HutaoVersionInformation
{
[JsonPropertyName("version")]
public Version Version { get; set; } = default!;
}

View File

@@ -1,11 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.CompilerServices;
using Snap.Hutao.Web.Response;
namespace Snap.Hutao.Web.Hutao.Response;
internal sealed class HutaoResponse : Web.Response.Response, ILocalizableResponse
internal sealed class HutaoResponse : Web.Response.Response, ILocalizableResponse, ICommonResponse<HutaoResponse>
{
[JsonConstructor]
public HutaoResponse(int returnCode, string message, string? localizationKey)
@@ -17,18 +17,9 @@ internal sealed class HutaoResponse : Web.Response.Response, ILocalizableRespons
[JsonPropertyName("l10nKey")]
public string? LocalizationKey { get; set; }
public static HutaoResponse DefaultIfNull(HutaoResponse? response, [CallerMemberName] string callerName = default!)
static HutaoResponse ICommonResponse<HutaoResponse>.CreateDefault(int returnCode, string message)
{
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
response ??= new(InternalFailure, SH.FormatWebResponseRequestExceptionFormat(callerName, null), default);
return response;
}
public static HutaoResponse<TData> DefaultIfNull<TData>(HutaoResponse<TData>? response, [CallerMemberName] string callerName = default!)
{
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
response ??= new(InternalFailure, SH.FormatWebResponseRequestExceptionFormat(callerName, typeof(TData).Name), default, default);
return response ?? new(InternalFailure, SH.FormatWebResponseRequestExceptionFormat(callerName, typeof(TData).Name), default, default);
return new(returnCode, message, default);
}
public override string ToString()
@@ -38,7 +29,7 @@ internal sealed class HutaoResponse : Web.Response.Response, ILocalizableRespons
}
[SuppressMessage("", "SA1402")]
internal sealed class HutaoResponse<TData> : Web.Response.Response<TData>, ILocalizableResponse
internal sealed class HutaoResponse<TData> : Response<TData>, ILocalizableResponse, ICommonResponse<HutaoResponse<TData>>
{
[JsonConstructor]
public HutaoResponse(int returnCode, string message, TData? data, string? localizationKey)
@@ -50,6 +41,11 @@ internal sealed class HutaoResponse<TData> : Web.Response.Response<TData>, ILoca
[JsonPropertyName("l10nKey")]
public string? LocalizationKey { get; set; }
static HutaoResponse<TData> ICommonResponse<HutaoResponse<TData>>.CreateDefault(int returnCode, string message)
{
return new(returnCode, message, default, default);
}
public override string ToString()
{
return SH.FormatWebResponseFormat(ReturnCode, this.GetLocalizationMessageOrDefault());

View File

@@ -47,7 +47,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<bool>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -67,7 +67,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<RankInfo>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -86,7 +86,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<Overview>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -105,7 +105,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<List<AvatarAppearanceRank>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -124,7 +124,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<List<AvatarUsageRank>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -143,7 +143,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<List<AvatarCollocation>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -162,7 +162,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<List<WeaponCollocation>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -181,7 +181,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<List<AvatarConstellationInfo>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -200,7 +200,7 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse<List<TeamAppearance>>>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -259,6 +259,6 @@ internal sealed partial class HutaoSpiralAbyssClient
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return HutaoResponse.DefaultIfNull(resp);
return Web.Response.Response.DefaultIfNull(resp);
}
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hutao;
internal sealed class YaeVersionInformation
{
[JsonPropertyName("tagName")]
public Version Version { get; set; } = default!;
[JsonPropertyName("url")]
public string Url { get; set; } = default!;
[JsonPropertyName("source")]
public string Source { get; set; } = default!;
[JsonPropertyName("frameworkUrl")]
public string FrameworkUrl { get; set; } = default!;
}

View File

@@ -15,24 +15,7 @@ namespace Snap.Hutao.Web;
[SuppressMessage("", "SA1124")]
internal static class HutaoEndpoints
{
#region Hutao as a Service
public static string Announcement(string locale)
{
return $"{HomaSnapGenshinApi}/Announcement/List?locale={locale}";
}
public const string AnnouncementUpload = $"{HomaSnapGenshinApi}/Service/Announcement/Upload";
public static string GachaLogCompensation(int days)
{
return $"{HomaSnapGenshinApi}/Service/GachaLog/Compensation?days={days}";
}
public static string GachaLogDesignation(string userName, int days)
{
return $"{HomaSnapGenshinApi}/Service/GachaLog/Designation?userName={userName}&days={days}";
}
#endregion
#region HomaAPI
#region GachaLog
@@ -43,28 +26,28 @@ internal static class HutaoEndpoints
/// <returns>获取末尾Id Url</returns>
public static string GachaLogEndIds(string uid)
{
return $"{HomaSnapGenshinApi}/GachaLog/EndIds?Uid={uid}";
return $"{HomaSnapGenshin}/GachaLog/EndIds?Uid={uid}";
}
/// <summary>
/// 获取祈愿记录
/// </summary>
public const string GachaLogRetrieve = $"{HomaSnapGenshinApi}/GachaLog/Retrieve";
public const string GachaLogRetrieve = $"{HomaSnapGenshin}/GachaLog/Retrieve";
/// <summary>
/// 上传祈愿记录
/// </summary>
public const string GachaLogUpload = $"{HomaSnapGenshinApi}/GachaLog/Upload";
public const string GachaLogUpload = $"{HomaSnapGenshin}/GachaLog/Upload";
/// <summary>
/// 获取Uid列表
/// </summary>
public const string GachaLogUids = $"{HomaSnapGenshinApi}/GachaLog/Uids";
public const string GachaLogUids = $"{HomaSnapGenshin}/GachaLog/Uids";
/// <summary>
/// 获取Uid列表
/// </summary>
public const string GachaLogEntries = $"{HomaSnapGenshinApi}/GachaLog/Entries";
public const string GachaLogEntries = $"{HomaSnapGenshin}/GachaLog/Entries";
/// <summary>
/// 删除祈愿记录
@@ -73,13 +56,13 @@ internal static class HutaoEndpoints
/// <returns>删除祈愿记录 Url</returns>
public static string GachaLogDelete(string uid)
{
return $"{HomaSnapGenshinApi}/GachaLog/Delete?Uid={uid}";
return $"{HomaSnapGenshin}/GachaLog/Delete?Uid={uid}";
}
/// <summary>
/// 获取祈愿统计信息
/// </summary>
public const string GachaLogStatisticsCurrentEvents = $"{HomaSnapGenshinApi}/GachaLog/Statistics/CurrentEventStatistics";
public const string GachaLogStatisticsCurrentEvents = $"{HomaSnapGenshin}/GachaLog/Statistics/CurrentEventStatistics";
/// <summary>
/// 获取祈愿统计信息
@@ -88,41 +71,27 @@ internal static class HutaoEndpoints
/// <returns>祈愿统计信息Url</returns>
public static string GachaLogStatisticsDistribution(GachaDistributionType distributionType)
{
return $"{HomaSnapGenshinApi}/GachaLog/Statistics/Distribution/{distributionType}";
return $"{HomaSnapGenshin}/GachaLog/Statistics/Distribution/{distributionType}";
}
#endregion
#region Passport
#region Hutao as a Service
public static string Announcement(string locale)
{
return $"{HomaSnapGenshin}/Announcement/List?locale={locale}";
}
/// <summary>
/// 获取注册验证码
/// </summary>
public const string PassportVerify = $"{HomaSnapGenshinApi}/Passport/Verify";
public const string AnnouncementUpload = $"{HomaSnapGenshin}/Service/Announcement/Upload";
/// <summary>
/// 注册账号
/// </summary>
public const string PassportRegister = $"{HomaSnapGenshinApi}/Passport/Register";
public static string GachaLogCompensation(int days)
{
return $"{HomaSnapGenshin}/Service/GachaLog/Compensation?days={days}";
}
/// <summary>
/// 注销账号
/// </summary>
public const string PassportCancel = $"{HomaSnapGenshinApi}/Passport/Cancel";
/// <summary>
/// 重设密码
/// </summary>
public const string PassportResetPassword = $"{HomaSnapGenshinApi}/Passport/ResetPassword";
/// <summary>
/// 登录
/// </summary>
public const string PassportLogin = $"{HomaSnapGenshinApi}/Passport/Login";
/// <summary>
/// 用户信息
/// </summary>
public const string PassportUserInfo = $"{HomaSnapGenshinApi}/Passport/UserInfo";
public static string GachaLogDesignation(string userName, int days)
{
return $"{HomaSnapGenshin}/Service/GachaLog/Designation?userName={userName}&days={days}";
}
#endregion
#region LogUpload
@@ -130,7 +99,40 @@ internal static class HutaoEndpoints
/// <summary>
/// 上传日志
/// </summary>
public const string HutaoLogUpload = $"{HomaSnapGenshinApi}/HutaoLog/Upload";
public const string HutaoLogUpload = $"{HomaSnapGenshin}/HutaoLog/Upload";
#endregion
#region Passport
/// <summary>
/// 获取注册验证码
/// </summary>
public const string PassportVerify = $"{HomaSnapGenshin}/Passport/Verify";
/// <summary>
/// 注册账号
/// </summary>
public const string PassportRegister = $"{HomaSnapGenshin}/Passport/Register";
/// <summary>
/// 注销账号
/// </summary>
public const string PassportCancel = $"{HomaSnapGenshin}/Passport/Cancel";
/// <summary>
/// 重设密码
/// </summary>
public const string PassportResetPassword = $"{HomaSnapGenshin}/Passport/ResetPassword";
/// <summary>
/// 登录
/// </summary>
public const string PassportLogin = $"{HomaSnapGenshin}/Passport/Login";
/// <summary>
/// 用户信息
/// </summary>
public const string PassportUserInfo = $"{HomaSnapGenshin}/Passport/UserInfo";
#endregion
#region SpiralAbyss
@@ -142,7 +144,7 @@ internal static class HutaoEndpoints
/// <returns>路径</returns>
public static string RecordCheck(string uid)
{
return $"{HomaSnapGenshinApi}/Record/Check?uid={uid}";
return $"{HomaSnapGenshin}/Record/Check?uid={uid}";
}
/// <summary>
@@ -152,50 +154,66 @@ internal static class HutaoEndpoints
/// <returns>路径</returns>
public static string RecordRank(string uid)
{
return $"{HomaSnapGenshinApi}/Record/Rank?uid={uid}";
return $"{HomaSnapGenshin}/Record/Rank?uid={uid}";
}
/// <summary>
/// 上传记录
/// </summary>
public const string RecordUpload = $"{HomaSnapGenshinApi}/Record/Upload";
public const string RecordUpload = $"{HomaSnapGenshin}/Record/Upload";
/// <summary>
/// 统计信息
/// </summary>
public const string StatisticsOverview = $"{HomaSnapGenshinApi}/Statistics/Overview";
public const string StatisticsOverview = $"{HomaSnapGenshin}/Statistics/Overview";
/// <summary>
/// 出场率
/// </summary>
public const string StatisticsAvatarAttendanceRate = $"{HomaSnapGenshinApi}/Statistics/Avatar/AttendanceRate";
public const string StatisticsAvatarAttendanceRate = $"{HomaSnapGenshin}/Statistics/Avatar/AttendanceRate";
/// <summary>
/// 使用率
/// </summary>
public const string StatisticsAvatarUtilizationRate = $"{HomaSnapGenshinApi}/Statistics/Avatar/UtilizationRate";
public const string StatisticsAvatarUtilizationRate = $"{HomaSnapGenshin}/Statistics/Avatar/UtilizationRate";
/// <summary>
/// 角色搭配
/// </summary>
public const string StatisticsAvatarAvatarCollocation = $"{HomaSnapGenshinApi}/Statistics/Avatar/AvatarCollocation";
public const string StatisticsAvatarAvatarCollocation = $"{HomaSnapGenshin}/Statistics/Avatar/AvatarCollocation";
/// <summary>
/// 角色持有率
/// </summary>
public const string StatisticsAvatarHoldingRate = $"{HomaSnapGenshinApi}/Statistics/Avatar/HoldingRate";
public const string StatisticsAvatarHoldingRate = $"{HomaSnapGenshin}/Statistics/Avatar/HoldingRate";
/// <summary>
/// 武器搭配
/// </summary>
public const string StatisticsWeaponWeaponCollocation = $"{HomaSnapGenshinApi}/Statistics/Weapon/WeaponCollocation";
public const string StatisticsWeaponWeaponCollocation = $"{HomaSnapGenshin}/Statistics/Weapon/WeaponCollocation";
/// <summary>
/// 持有率
/// </summary>
public const string StatisticsTeamCombination = $"{HomaSnapGenshinApi}/Statistics/Team/Combination";
public const string StatisticsTeamCombination = $"{HomaSnapGenshin}/Statistics/Team/Combination";
#endregion
public static string Website(string path)
{
return $"{HomaSnapGenshin}/{path}";
}
#endregion
#region Infrasturcture
public static string Enka(in PlayerUid uid)
{
return $"{ApiSnapGenshinEnka}/{uid}";
}
public const string Ip = $"{ApiSnapGenshin}/ip";
#region Metadata
/// <summary>
@@ -210,7 +228,12 @@ internal static class HutaoEndpoints
}
#endregion
#region Static & Zip
#region Patch
public const string PatchYaeAchievement = $"{ApiSnapGenshinPatch}/yae";
public const string PatchSnapHutao = $"{ApiSnapGenshinPatch}/hutao";
#endregion
#region StaticResources
/// <summary>
/// UI_Icon_None
@@ -249,21 +272,13 @@ internal static class HutaoEndpoints
}
#endregion
public static string Website(string path)
{
return $"{HomaSnapGenshinApi}/{path}";
}
#endregion
public static string Enka(in PlayerUid uid)
{
return $"{ApiSnapGenshinEnka}/{uid}";
}
public const string Ip = $"{ApiSnapGenshin}/ip";
private const string ApiSnapGenshin = "https://api.snapgenshin.com";
private const string ApiSnapGenshinMetadata = $"{ApiSnapGenshin}/metadata";
private const string ApiSnapGenshinPatch = $"{ApiSnapGenshin}/patch";
private const string ApiSnapGenshinStaticRaw = $"{ApiSnapGenshin}/static/raw";
private const string ApiSnapGenshinStaticZip = $"{ApiSnapGenshin}/static/zip";
private const string ApiSnapGenshinEnka = $"{ApiSnapGenshin}/enka";
private const string HomaSnapGenshinApi = "https://homa.snapgenshin.com";
private const string HomaSnapGenshin = "https://homa.snapgenshin.com";
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Response;
internal interface ICommonResponse<TResponse>
where TResponse : ICommonResponse<TResponse>
{
int ReturnCode { get; }
string Message { get; set; }
static abstract TResponse CreateDefault(int returnCode, string message);
}

View File

@@ -1,25 +1,17 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Web.Bridge.Model;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Web.Response;
/// <summary>
/// 提供 <see cref="Response{T}"/> 的非泛型基类
/// </summary>
[HighQuality]
internal class Response
internal class Response : ICommonResponse<Response>
{
public const int InternalFailure = 0x26F19335;
/// <summary>
/// 构造一个新的响应
/// </summary>
/// <param name="returnCode">返回代码</param>
/// <param name="message">消息</param>
[JsonConstructor]
public Response(int returnCode, string message)
{
@@ -30,15 +22,9 @@ internal class Response
#endif
}
/// <summary>
/// 返回代码
/// </summary>
[JsonPropertyName("retcode")]
public int ReturnCode { get; set; }
/// <summary>
/// 消息
/// </summary>
[JsonPropertyName("message")]
public string Message { get; set; } = default!;
@@ -47,16 +33,16 @@ internal class Response
return new(response.ReturnCode == 0, response.Message);
}
/// <summary>
/// 返回本体或带有消息提示的默认值
/// </summary>
/// <param name="response">本体</param>
/// <param name="callerName">调用方法名称</param>
/// <returns>本体或默认值,当本体为 null 时 返回默认值</returns>
public static Response DefaultIfNull(Response? response, [CallerMemberName] string callerName = default!)
static Response ICommonResponse<Response>.CreateDefault(int returnCode, string message)
{
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
response ??= new(InternalFailure, SH.FormatWebResponseRequestExceptionFormat(callerName, null));
return new(returnCode, message);
}
public static TResponse DefaultIfNull<TResponse>(TResponse? response, [CallerMemberName] string callerName = default!)
where TResponse : ICommonResponse<TResponse>
{
string message = SH.FormatWebResponseRequestExceptionFormat(callerName, TypeNameHelper.GetTypeDisplayName(typeof(TResponse)));
response ??= TResponse.CreateDefault(InternalFailure, message);
if (((KnownReturnCode)response.ReturnCode) is KnownReturnCode.PleaseLogin or KnownReturnCode.RET_TOKEN_INVALID)
{
@@ -66,46 +52,9 @@ internal class Response
return response;
}
/// <summary>
/// 返回本体或带有消息提示的默认值
/// </summary>
/// <typeparam name="TData">类型</typeparam>
/// <param name="response">本体</param>
/// <param name="callerName">调用方法名称</param>
/// <returns>本体或默认值,当本体为 null 时 返回默认值</returns>
public static Response<TData> DefaultIfNull<TData>(Response<TData>? response, [CallerMemberName] string callerName = default!)
public static Response<TData> CloneReturnCodeAndMessage<TData, TOther>(Response<TOther> response, [CallerMemberName] string callerName = default!)
{
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
response ??= new(InternalFailure, SH.FormatWebResponseRequestExceptionFormat(callerName, typeof(TData).Name), default);
if (((KnownReturnCode)response.ReturnCode) is KnownReturnCode.PleaseLogin or KnownReturnCode.RET_TOKEN_INVALID)
{
response.Message = SH.FormatWebResponseRefreshCookieHintFormat(response.Message);
}
return response ?? new(InternalFailure, SH.FormatWebResponseRequestExceptionFormat(callerName, typeof(TData).Name), default);
}
/// <summary>
/// 返回本体或带有消息提示的默认值
/// </summary>
/// <typeparam name="TData">类型</typeparam>
/// <typeparam name="TOther">其他类型</typeparam>
/// <param name="response">本体</param>
/// <param name="callerName">调用方法名称</param>
/// <returns>本体或默认值,当本体为 null 时 返回默认值</returns>
public static Response<TData> DefaultIfNull<TData, TOther>(Response<TOther>? response, [CallerMemberName] string callerName = default!)
{
if (response is not null)
{
Must.Argument(response.ReturnCode != 0, "RetCode has to be 0");
return new(response.ReturnCode, response.Message, default);
}
else
{
// Magic number that hashed from "Snap.Hutao"
return new(InternalFailure, SH.FormatWebResponseRequestExceptionFormat(callerName, typeof(TData).Name), default);
}
return new(response.ReturnCode, response.Message, default);
}
public virtual bool IsOk(bool showInfoBar = true, IServiceProvider? serviceProvider = null)
@@ -126,27 +75,15 @@ internal class Response
}
}
/// <inheritdoc/>
public override string ToString()
{
return SH.FormatWebResponseFormat(ReturnCode, Message);
}
}
/// <summary>
/// Mihoyo 标准API响应
/// </summary>
/// <typeparam name="TData">数据类型</typeparam>
[SuppressMessage("", "SA1402")]
[HighQuality]
internal class Response<TData> : Response, IJsResult
internal class Response<TData> : Response, ICommonResponse<Response<TData>>, IJsBridgeResult
{
/// <summary>
/// 构造一个新的 Mihoyo 标准API响应
/// </summary>
/// <param name="returnCode">返回代码</param>
/// <param name="message">消息</param>
/// <param name="data">数据</param>
[JsonConstructor]
public Response(int returnCode, string message, TData? data)
: base(returnCode, message)
@@ -154,18 +91,14 @@ internal class Response<TData> : Response, IJsResult
Data = data;
}
/// <summary>
/// 数据
/// </summary>
[JsonPropertyName("data")]
public TData? Data { get; set; }
/// <summary>
/// 响应是否正常
/// </summary>
/// <param name="showInfoBar">是否显示错误信息</param>
/// <param name="serviceProvider">服务提供器</param>
/// <returns>是否Ok</returns>
static Response<TData> ICommonResponse<Response<TData>>.CreateDefault(int returnCode, string message)
{
return new(returnCode, message, default);
}
[MemberNotNullWhen(true, nameof(Data))]
public override bool IsOk(bool showInfoBar = true, IServiceProvider? serviceProvider = null)
{
@@ -185,28 +118,4 @@ internal class Response<TData> : Response, IJsResult
return false;
}
}
public bool TryGetData([NotNullWhen(true)] out TData? data, IInfoBarService? infoBarService = null, IServiceProvider? serviceProvider = null)
{
if (ReturnCode == 0)
{
ArgumentNullException.ThrowIfNull(Data);
data = Data;
return true;
}
else
{
serviceProvider ??= Ioc.Default;
infoBarService ??= serviceProvider.GetRequiredService<IInfoBarService>();
infoBarService.Error(ToString());
data = default;
return false;
}
}
/// <inheritdoc/>
public string ToJson()
{
return JsonSerializer.Serialize(this);
}
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Service.Notification;
namespace Snap.Hutao.Web.Response;
internal static class ResponseExtension
{
public static bool TryGetData<TData>(this Response<TData> response, [NotNullWhen(true)] out TData? data, IInfoBarService? infoBarService = null, IServiceProvider? serviceProvider = null)
{
if (response.ReturnCode == 0)
{
ArgumentNullException.ThrowIfNull(response.Data);
data = response.Data;
return true;
}
else
{
serviceProvider ??= Ioc.Default;
infoBarService ??= serviceProvider.GetRequiredService<IInfoBarService>();
infoBarService.Error(response.ToString());
data = default;
return false;
}
}
}