From 45e46d817ad08cc33288c85fb74cb04d909d8d0d Mon Sep 17 00:00:00 2001 From: Scighost Date: Wed, 25 Sep 2024 23:01:58 +0800 Subject: [PATCH] inter knot monthly reporter --- .../GameRecord/GameRecordClient.cs | 61 +++ .../GameRecord/GameRecordJsonContext.cs | 3 + src/Starward.Core/GameRecord/HoyolabClient.cs | 107 ++++- .../GameRecord/HyperionClient.cs | 112 ++++- .../InterKnotReportDataType.cs | 33 ++ .../InterKnotReport/InterKnotReportDetail.cs | 53 +++ .../InterKnotReportDetailItem.cs | 35 ++ .../InterKnotReportIncomeComponent.cs | 23 ++ .../InterKnotReportMonthData.cs | 14 + .../InterKnotReportRoleInfo.cs | 14 + .../InterKnotReport/InterKnotReportSummary.cs | 54 +++ src/Starward.Language/Lang.Designer.cs | 81 ++++ src/Starward.Language/Lang.resx | 27 ++ src/Starward.Language/Lang.zh-CN.resx | 27 ++ src/Starward/Controls/BBSWebBridge.xaml.cs | 4 +- .../HoyolabToolbox/HoyolabToolboxPage.xaml | 17 + .../HoyolabToolbox/HoyolabToolboxPage.xaml.cs | 8 + .../InterKnotMonthlyReportPage.xaml | 387 ++++++++++++++++++ .../InterKnotMonthlyReportPage.xaml.cs | 369 +++++++++++++++++ src/Starward/Pages/MainPage.xaml.cs | 4 +- src/Starward/Services/DatabaseService.cs | 32 +- src/Starward/Services/GameRecordService.cs | 89 ++++ 22 files changed, 1547 insertions(+), 7 deletions(-) create mode 100644 src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDataType.cs create mode 100644 src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDetail.cs create mode 100644 src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDetailItem.cs create mode 100644 src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportIncomeComponent.cs create mode 100644 src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportMonthData.cs create mode 100644 src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportRoleInfo.cs create mode 100644 src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportSummary.cs create mode 100644 src/Starward/Pages/HoyolabToolbox/InterKnotMonthlyReportPage.xaml create mode 100644 src/Starward/Pages/HoyolabToolbox/InterKnotMonthlyReportPage.xaml.cs diff --git a/src/Starward.Core/GameRecord/GameRecordClient.cs b/src/Starward.Core/GameRecord/GameRecordClient.cs index 86a77b184..094ebc32e 100644 --- a/src/Starward.Core/GameRecord/GameRecordClient.cs +++ b/src/Starward.Core/GameRecord/GameRecordClient.cs @@ -9,6 +9,8 @@ using System.Security.Cryptography; using System.Text; using Starward.Core.GameRecord.Genshin.ImaginariumTheater; +using Starward.Core.GameRecord.ZZZ.InterKnotReport; + #if !DEBUG using System.Net.Http.Json; @@ -382,6 +384,65 @@ protected virtual async Task CommonSendAsync(HttpRequestMessage request, Cancell #endregion + + + #region ZZZ + + + + /// + /// 获取绝区零账号信息 + /// + /// + /// + /// + public abstract Task> GetZZZGameRolesAsync(string cookie, CancellationToken cancellationToken = default); + + + + + /// + /// 绳网月报总结 + /// + /// + /// 202409 + /// + /// + public abstract Task GetInterKnotReportSummaryAsync(GameRecordRole role, string month = "", CancellationToken cancellationToken = default); + + + /// + /// 绳网月报收入详情 + /// + /// + /// 202409 + /// + /// 从1开始 + /// 最大100 + /// + /// 返回一页收入记录 + public abstract Task GetInterKnotReportDetailByPageAsync(GameRecordRole role, string month, string type, int page, int page_size = 100, CancellationToken cancellationToken = default); + + + /// + /// 绳网月报收入详情 + /// + /// + /// 202409 + /// + /// 最大100 + /// + /// 返回该月所有收入记录 + public abstract Task GetInterKnotReportDetailAsync(GameRecordRole role, string month, string type, int page_size = 100, CancellationToken cancellationToken = default); + + + + + #endregion + + + + // 寰宇蝗灾 // https://api-takumi-record.mihoyo.com/game_record/app/hkrpg/api/rogue_locust?server=prod_gf_cn&role_id={uid}&need_detail=true diff --git a/src/Starward.Core/GameRecord/GameRecordJsonContext.cs b/src/Starward.Core/GameRecord/GameRecordJsonContext.cs index 86714bbd5..e9d08ae79 100644 --- a/src/Starward.Core/GameRecord/GameRecordJsonContext.cs +++ b/src/Starward.Core/GameRecord/GameRecordJsonContext.cs @@ -6,6 +6,7 @@ using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; +using Starward.Core.GameRecord.ZZZ.InterKnotReport; using System.Text.Json.Serialization; namespace Starward.Core.GameRecord; @@ -26,6 +27,8 @@ namespace Starward.Core.GameRecord; [JsonSerializable(typeof(miHoYoApiWrapper))] [JsonSerializable(typeof(miHoYoApiWrapper))] [JsonSerializable(typeof(miHoYoApiWrapper))] +[JsonSerializable(typeof(miHoYoApiWrapper))] +[JsonSerializable(typeof(miHoYoApiWrapper))] internal partial class GameRecordJsonContext : JsonSerializerContext { diff --git a/src/Starward.Core/GameRecord/HoyolabClient.cs b/src/Starward.Core/GameRecord/HoyolabClient.cs index a1cf98ca1..7c7c76d3e 100644 --- a/src/Starward.Core/GameRecord/HoyolabClient.cs +++ b/src/Starward.Core/GameRecord/HoyolabClient.cs @@ -6,6 +6,7 @@ using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; +using Starward.Core.GameRecord.ZZZ.InterKnotReport; namespace Starward.Core.GameRecord; @@ -15,7 +16,7 @@ public class HoyolabClient : GameRecordClient public override string UAContent => $"Mozilla/5.0 (Linux; Android 13; Pixel 5 Build/TQ3A.230901.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/118.0.0.0 Mobile Safari/537.36 miHoYoBBSOversea/{AppVersion}"; - public override string AppVersion => "2.54.0"; + public override string AppVersion => "2.59.0"; protected override string ApiSalt => "okr4obncj8bw5a65hbnn5oo6ixjc3l9w"; @@ -84,6 +85,7 @@ public override async Task> GetAllGameRolesAsync(string coo var list = new List(); list.AddRange(await GetGenshinGameRolesAsync(cookie, cancellationToken)); list.AddRange(await GetStarRailGameRolesAsync(cookie, cancellationToken)); + list.AddRange(await GetZZZGameRolesAsync(cookie, cancellationToken)); return list; } @@ -496,4 +498,107 @@ public override async Task GetTrailblazeCalendarDetail #endregion + + + #region ZZZ + + + /// + /// 获取绝区零账号信息 + /// + /// + /// + /// + public override async Task> GetZZZGameRolesAsync(string cookie, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(cookie)) + { + throw new ArgumentNullException(nameof(cookie)); + } + var url = "https://api-account-os.hoyolab.com/binding/api/getUserGameRolesByCookieToken?game_biz=nap_global"; + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add(Cookie, cookie); + request.Headers.Add(X_Request_With, com_mihoyo_hoyolab); + request.Headers.Add(Referer, "https://act.hoyolab.com"); + var data = await CommonSendAsync(request, cancellationToken); + data.List?.ForEach(x => x.Cookie = cookie); + return data.List ?? new List(); + } + + + + /// + /// 绳网月报总结 + /// + /// + /// 202409 + /// + /// + public override async Task GetInterKnotReportSummaryAsync(GameRecordRole role, string month = "", CancellationToken cancellationToken = default) + { + var url = $"https://sg-public-api.hoyolab.com/event/nap_ledger/month_info?uid={role.Uid}®ion={role.Region}&month={month}"; + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add(Cookie, role.Cookie); + request.Headers.Add(Referer, "https://act.hoyolab.com/"); + request.Headers.Add(X_Request_With, com_mihoyo_hoyolab); + return await CommonSendAsync(request, cancellationToken); + } + + /// + /// 绳网月报收入详情 + /// + /// + /// 202409 + /// + /// 从1开始 + /// 最大100 + /// + /// 返回一页收入记录 + public override async Task GetInterKnotReportDetailByPageAsync(GameRecordRole role, string month, string type, int page, int page_size = 100, CancellationToken cancellationToken = default) + { + var url = $"https://sg-public-api.hoyolab.com/event/nap_ledger/month_detail?uid={role.Uid}®ion={role.Region}&month={month}&type={type}¤t_page={page}&page_size={page_size}"; + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add(Cookie, role.Cookie); + request.Headers.Add(Referer, "https://act.hoyolab.com/"); + request.Headers.Add(X_Request_With, com_mihoyo_hoyolab); + return await CommonSendAsync(request, cancellationToken); + } + + + /// + /// 绳网月报收入详情 + /// + /// + /// 202409 + /// + /// 最大100 + /// + /// 返回该月所有收入记录 + public override async Task GetInterKnotReportDetailAsync(GameRecordRole role, string month, string type, int page_size = 100, CancellationToken cancellationToken = default) + { + page_size = Math.Clamp(page_size, 20, 100); + var data = await GetInterKnotReportDetailByPageAsync(role, month, type, 1, page_size, cancellationToken); + if (data.List.Count < page_size) + { + return data; + } + for (int i = 2; ; i++) + { + var addData = await GetInterKnotReportDetailByPageAsync(role, month, type, i, page_size, cancellationToken); + data.List.AddRange(addData.List); + if (addData.List.Count < page_size) + { + break; + } + } + return data; + } + + + + #endregion + + + + } diff --git a/src/Starward.Core/GameRecord/HyperionClient.cs b/src/Starward.Core/GameRecord/HyperionClient.cs index a6296a090..05af455df 100644 --- a/src/Starward.Core/GameRecord/HyperionClient.cs +++ b/src/Starward.Core/GameRecord/HyperionClient.cs @@ -6,6 +6,7 @@ using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; +using Starward.Core.GameRecord.ZZZ.InterKnotReport; namespace Starward.Core.GameRecord; @@ -16,7 +17,7 @@ public class HyperionClient : GameRecordClient public override string UAContent => $"Mozilla/5.0 (Linux; Android 13; Pixel 5 Build/TQ3A.230901.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/118.0.0.0 Mobile Safari/537.36 miHoYoBBS/{AppVersion}"; - public override string AppVersion => "2.71.1"; + public override string AppVersion => "2.75.2"; protected override string ApiSalt => "t0qEgfub6cvueAPgR5m9aQWWVciEer7v"; @@ -81,6 +82,7 @@ public override async Task> GetAllGameRolesAsync(string coo var list = new List(); list.AddRange(await GetGenshinGameRolesAsync(cookie, cancellationToken)); list.AddRange(await GetStarRailGameRolesAsync(cookie, cancellationToken)); + list.AddRange(await GetZZZGameRolesAsync(cookie, cancellationToken)); return list; } @@ -556,4 +558,112 @@ public override async Task GetTrailblazeCalendarDetail + #region ZZZ + + + + /// + /// 获取绝区零账号信息 + /// + /// + /// + /// + public override async Task> GetZZZGameRolesAsync(string cookie, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(cookie)) + { + throw new ArgumentNullException(nameof(cookie)); + } + var url = "https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=nap_cn"; + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add(Cookie, cookie); + request.Headers.Add(DS, CreateSecret2(url)); + request.Headers.Add(X_Request_With, com_mihoyo_hyperion); + request.Headers.Add(x_rpc_app_version, AppVersion); + request.Headers.Add(x_rpc_client_type, "5"); + request.Headers.Add(Referer, "https://webstatic.mihoyo.com/"); + var data = await CommonSendAsync(request, cancellationToken); + data.List?.ForEach(x => x.Cookie = cookie); + return data.List ?? new List(); + } + + + + /// + /// 绳网月报总结 + /// + /// + /// 202409 + /// + /// + public override async Task GetInterKnotReportSummaryAsync(GameRecordRole role, string month = "", CancellationToken cancellationToken = default) + { + var url = $"https://api-takumi.mihoyo.com/event/nap_ledger/month_info?uid={role.Uid}®ion={role.Region}&month={month}"; + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add(Cookie, role.Cookie); + request.Headers.Add(Referer, "https://act.mihoyo.com/"); + request.Headers.Add(x_rpc_app_version, AppVersion); + request.Headers.Add(x_rpc_device_id, DeviceId); + request.Headers.Add(x_rpc_device_fp, DeviceFp); + return await CommonSendAsync(request, cancellationToken); + } + + /// + /// 绳网月报收入详情 + /// + /// + /// 202409 + /// + /// 从1开始 + /// 最大100 + /// + /// 返回一页收入记录 + public override async Task GetInterKnotReportDetailByPageAsync(GameRecordRole role, string month, string type, int page, int page_size = 100, CancellationToken cancellationToken = default) + { + var url = $"https://api-takumi.mihoyo.com/event/nap_ledger/month_detail?uid={role.Uid}®ion={role.Region}&month={month}&type={type}¤t_page={page}&page_size={page_size}"; + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add(Cookie, role.Cookie); + request.Headers.Add(Referer, "https://act.mihoyo.com/"); + request.Headers.Add(x_rpc_app_version, AppVersion); + request.Headers.Add(x_rpc_device_id, DeviceId); + request.Headers.Add(x_rpc_device_fp, DeviceFp); + return await CommonSendAsync(request, cancellationToken); + } + + + /// + /// 绳网月报收入详情 + /// + /// + /// 202409 + /// + /// 最大100 + /// + /// 返回该月所有收入记录 + public override async Task GetInterKnotReportDetailAsync(GameRecordRole role, string month, string type, int page_size = 100, CancellationToken cancellationToken = default) + { + page_size = Math.Clamp(page_size, 20, 100); + var data = await GetInterKnotReportDetailByPageAsync(role, month, type, 1, page_size, cancellationToken); + if (data.List.Count < page_size) + { + return data; + } + for (int i = 2; ; i++) + { + var addData = await GetInterKnotReportDetailByPageAsync(role, month, type, i, page_size, cancellationToken); + data.List.AddRange(addData.List); + if (addData.List.Count < page_size) + { + break; + } + } + return data; + } + + + + + #endregion + + } diff --git a/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDataType.cs b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDataType.cs new file mode 100644 index 000000000..b672d9bec --- /dev/null +++ b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDataType.cs @@ -0,0 +1,33 @@ +namespace Starward.Core.GameRecord.ZZZ.InterKnotReport; + +public abstract class InterKnotReportDataType +{ + + /// + /// 菲林 + /// + public const string PolychromesData = nameof(PolychromesData); + + /// + /// 加密母带 & 原装母带 + /// + public const string MatserTapeData = nameof(MatserTapeData); + + /// + /// 邦布券 + /// + public const string BooponsData = nameof(BooponsData); + + + + public static string ToLocalization(string type) + { + return type switch + { + _ => type, + }; + } + + + +} \ No newline at end of file diff --git a/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDetail.cs b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDetail.cs new file mode 100644 index 000000000..9cac5ea47 --- /dev/null +++ b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDetail.cs @@ -0,0 +1,53 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.ZZZ.InterKnotReport; + +public class InterKnotReportDetail : IJsonOnDeserialized +{ + + [JsonPropertyName("uid")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] + public long Uid { get; set; } + + + [JsonPropertyName("region")] + public string Region { get; set; } + + + [JsonPropertyName("data_month")] + public string DataMonth { get; set; } + + + [JsonPropertyName("current_page")] + public int CurrentPage { get; set; } + + + [JsonPropertyName("list")] + public List List { get; set; } + + /// + /// 当月该类别记录总数 + /// + [JsonPropertyName("total")] + public int Total { get; set; } + + + [JsonPropertyName("data_name")] + public string DataName { get; set; } + + + [JsonPropertyName("data_type")] + public string DataType { get; set; } + + + public void OnDeserialized() + { + foreach (var item in List) + { + item.Uid = this.Uid; + item.DataMonth = this.DataMonth; + item.DataType = this.DataType; + } + } + +} diff --git a/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDetailItem.cs b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDetailItem.cs new file mode 100644 index 000000000..441ade4ef --- /dev/null +++ b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportDetailItem.cs @@ -0,0 +1,35 @@ +using Starward.Core.GameRecord.Genshin.SpiralAbyss; +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.ZZZ.InterKnotReport; + +public class InterKnotReportDetailItem +{ + + [JsonIgnore] + public long Uid { get; set; } + + [JsonPropertyName("id")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] + public long Id { get; set; } + + [JsonIgnore] + public string DataMonth { get; set; } + + [JsonIgnore] + public string DataType { get; set; } + + + [JsonPropertyName("action")] + public string Action { get; set; } + + + [JsonPropertyName("time")] + [JsonConverter(typeof(SpiralAbyssTimeJsonConverter))] + public DateTimeOffset Time { get; set; } + + + [JsonPropertyName("num")] + public int Number { get; set; } + +} diff --git a/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportIncomeComponent.cs b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportIncomeComponent.cs new file mode 100644 index 000000000..81b7c84f5 --- /dev/null +++ b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportIncomeComponent.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.ZZZ.InterKnotReport; + +public class InterKnotReportIncomeComponent +{ + + [JsonPropertyName("action")] + public string Action { get; set; } + + + [JsonPropertyName("num")] + public int Num { get; set; } + + /// + /// 百分比,单位 % + /// + [JsonPropertyName("percent")] + public int Percent { get; set; } + +} + + diff --git a/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportMonthData.cs b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportMonthData.cs new file mode 100644 index 000000000..7309747e7 --- /dev/null +++ b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportMonthData.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.ZZZ.InterKnotReport; + +public class InterKnotReportMonthData +{ + [JsonPropertyName("list")] + public List List { get; set; } + + [JsonPropertyName("income_components")] + public List IncomeComponents { get; set; } +} + + diff --git a/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportRoleInfo.cs b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportRoleInfo.cs new file mode 100644 index 000000000..43c37dce5 --- /dev/null +++ b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportRoleInfo.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.ZZZ.InterKnotReport; + +public class InterKnotReportRoleInfo +{ + [JsonPropertyName("nickname")] + public string Nickname { get; set; } + + [JsonPropertyName("avatar")] + public string Avatar { get; set; } +} + + diff --git a/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportSummary.cs b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportSummary.cs new file mode 100644 index 000000000..38bcf8cf4 --- /dev/null +++ b/src/Starward.Core/GameRecord/ZZZ/InterKnotReport/InterKnotReportSummary.cs @@ -0,0 +1,54 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.ZZZ.InterKnotReport; + +public class InterKnotReportSummaryAward +{ + [JsonPropertyName("data_type")] + public string DataType { get; set; } + + [JsonPropertyName("count")] + public int Count { get; set; } + + [JsonPropertyName("data_name")] + public string DataName { get; set; } +} + +public class InterKnotReportSummary +{ + + [JsonPropertyName("uid")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] + public long Uid { get; set; } + + + [JsonPropertyName("region")] + public string Region { get; set; } + + + [JsonPropertyName("current_month")] + public string CurrentMonth { get; set; } + + + [JsonPropertyName("data_month")] + public string DataMonth { get; set; } + + + [JsonPropertyName("month_data")] + public InterKnotReportMonthData MonthData { get; set; } + + + [JsonPropertyName("optional_month")] + public List OptionalMonth { get; set; } + + + [JsonPropertyName("role_info")] + public InterKnotReportRoleInfo RoleInfo { get; set; } + + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } + +} + + diff --git a/src/Starward.Language/Lang.Designer.cs b/src/Starward.Language/Lang.Designer.cs index 165ff2bed..c14e2c908 100644 --- a/src/Starward.Language/Lang.Designer.cs +++ b/src/Starward.Language/Lang.Designer.cs @@ -1954,6 +1954,15 @@ public static string HoyolabToolboxPage_InputCookie { } } + /// + /// 查找类似 Inter-Knot Monthly Report 的本地化字符串。 + /// + public static string HoyolabToolboxPage_InterKnotMonthlyReport { + get { + return ResourceManager.GetString("HoyolabToolboxPage_InterKnotMonthlyReport", resourceCulture); + } + } + /// /// 查找类似 Log in 的本地化字符串。 /// @@ -2422,6 +2431,78 @@ public static string InstallGameSystemTrayPage_Installer { } } + /// + /// 查找类似 Daily Activity Rewardeds 的本地化字符串。 + /// + public static string InterKnotMonthlyReportPage_DailyActivityRewardeds { + get { + return ResourceManager.GetString("InterKnotMonthlyReportPage_DailyActivityRewardeds", resourceCulture); + } + } + + /// + /// 查找类似 Development Rewards 的本地化字符串。 + /// + public static string InterKnotMonthlyReportPage_DevelopmentRewards { + get { + return ResourceManager.GetString("InterKnotMonthlyReportPage_DevelopmentRewards", resourceCulture); + } + } + + /// + /// 查找类似 Event Rewards 的本地化字符串。 + /// + public static string InterKnotMonthlyReportPage_EventRewards { + get { + return ResourceManager.GetString("InterKnotMonthlyReportPage_EventRewards", resourceCulture); + } + } + + /// + /// 查找类似 Hollow Zero Rewards 的本地化字符串。 + /// + public static string InterKnotMonthlyReportPage_HollowZeroRewards { + get { + return ResourceManager.GetString("InterKnotMonthlyReportPage_HollowZeroRewards", resourceCulture); + } + } + + /// + /// 查找类似 Mail Rewards 的本地化字符串。 + /// + public static string InterKnotMonthlyReportPage_MailRewards { + get { + return ResourceManager.GetString("InterKnotMonthlyReportPage_MailRewards", resourceCulture); + } + } + + /// + /// 查找类似 Other Rewards 的本地化字符串。 + /// + public static string InterKnotMonthlyReportPage_OtherRewards { + get { + return ResourceManager.GetString("InterKnotMonthlyReportPage_OtherRewards", resourceCulture); + } + } + + /// + /// 查找类似 Polychrome Revenue Streams 的本地化字符串。 + /// + public static string InterKnotMonthlyReportPage_PolychromeRevenueStreams { + get { + return ResourceManager.GetString("InterKnotMonthlyReportPage_PolychromeRevenueStreams", resourceCulture); + } + } + + /// + /// 查找类似 Shiyu Defense Rewards 的本地化字符串。 + /// + public static string InterKnotMonthlyReportPage_ShiyuDefenseRewards { + get { + return ResourceManager.GetString("InterKnotMonthlyReportPage_ShiyuDefenseRewards", resourceCulture); + } + } + /// /// 查找类似 Chinese 的本地化字符串。 /// diff --git a/src/Starward.Language/Lang.resx b/src/Starward.Language/Lang.resx index 616fd3234..9c8f9e9ed 100644 --- a/src/Starward.Language/Lang.resx +++ b/src/Starward.Language/Lang.resx @@ -1582,4 +1582,31 @@ Do you accept the risk and continue to use it? Removable storage device not connected. + + Polychrome Revenue Streams + + + Inter-Knot Monthly Report + + + Daily Activity Rewardeds + + + Development Rewards + + + Event Rewards + + + Hollow Zero Rewards + + + Shiyu Defense Rewards + + + Mail Rewards + + + Other Rewards + \ No newline at end of file diff --git a/src/Starward.Language/Lang.zh-CN.resx b/src/Starward.Language/Lang.zh-CN.resx index 967f4ab4b..2e642782d 100644 --- a/src/Starward.Language/Lang.zh-CN.resx +++ b/src/Starward.Language/Lang.zh-CN.resx @@ -1582,4 +1582,31 @@ 可移动存储设备未连接 + + 菲林收入组成 + + + 绳网月报 + + + 日常活跃奖励 + + + 成长奖励 + + + 活动奖励 + + + 零号空洞奖励 + + + 式舆防卫战奖励 + + + 邮件奖励 + + + 其他奖励 + \ No newline at end of file diff --git a/src/Starward/Controls/BBSWebBridge.xaml.cs b/src/Starward/Controls/BBSWebBridge.xaml.cs index 1dc14800f..1de759951 100644 --- a/src/Starward/Controls/BBSWebBridge.xaml.cs +++ b/src/Starward/Controls/BBSWebBridge.xaml.cs @@ -168,10 +168,10 @@ public async Task LoadPageAsync(bool force = false) { (true, GameBiz.GenshinImpact) => "https://act.hoyolab.com/app/community-game-records-sea/m.html?gid=2", (true, GameBiz.StarRail) => "https://act.hoyolab.com/app/community-game-records-sea/m.html?gid=6", - (true, GameBiz.ZZZ) => "https://act.hoyolab.com/app/community-game-records-sea/m.html?gid=8", + (true, GameBiz.ZZZ) => "https://act.hoyolab.com/app/zzz-game-record/m.html?gid=8", (false, GameBiz.GenshinImpact) => "https://webstatic.mihoyo.com/app/community-game-records/?game_id=2", (false, GameBiz.StarRail) => "https://webstatic.mihoyo.com/app/community-game-records/?game_id=6", - (false, GameBiz.ZZZ) => "https://webstatic.mihoyo.com/app/community-game-records/?game_id=8", + (false, GameBiz.ZZZ) => "https://act.mihoyo.com/app/mihoyo-zzz-game-record/m.html?game_id=8", _ => null, }; if (url is not null) diff --git a/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml b/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml index 6d6c15e8b..ea3698da1 100644 --- a/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml +++ b/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml @@ -417,6 +417,23 @@ + + + + + + + + + + + + + diff --git a/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml.cs b/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml.cs index 0b051218e..dee0c7bb8 100644 --- a/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml.cs +++ b/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml.cs @@ -226,6 +226,13 @@ private void InitializeNavigationViewItemVisibility() // 铁道战绩图片 Image_BattleChronicle.Source = new BitmapImage(new("ms-appx:///Assets/Image/ade9545750299456a3fcbc8c3b63521d_2941971308029698042.png")); } + if (CurrentGameBiz.ToGame() is GameBiz.ZZZ) + { + NavigationViewItem_BattleChronicle.Visibility = Visibility.Visible; + NavigationViewItem_InterKnotMonthlyReport.Visibility = Visibility.Visible; + // 绝区零战绩图片 + Image_BattleChronicle.Source = new BitmapImage(new("ms-appx:///Assets/Image/bc8f0b7384b306c80f2a1fcca9f3d14b_8590605504999484795.png")); + } } @@ -479,6 +486,7 @@ private void NavigationView_Toolbox_ItemInvoked(NavigationView sender, Navigatio nameof(PureFictionPage) => typeof(PureFictionPage), nameof(ApocalypticShadowPage) => typeof(ApocalypticShadowPage), nameof(TrailblazeCalendarPage) => typeof(TrailblazeCalendarPage), + nameof(InterKnotMonthlyReportPage) => typeof(InterKnotMonthlyReportPage), _ => null, }; diff --git a/src/Starward/Pages/HoyolabToolbox/InterKnotMonthlyReportPage.xaml b/src/Starward/Pages/HoyolabToolbox/InterKnotMonthlyReportPage.xaml new file mode 100644 index 000000000..e5dc76eec --- /dev/null +++ b/src/Starward/Pages/HoyolabToolbox/InterKnotMonthlyReportPage.xaml @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Starward/Pages/HoyolabToolbox/InterKnotMonthlyReportPage.xaml.cs b/src/Starward/Pages/HoyolabToolbox/InterKnotMonthlyReportPage.xaml.cs new file mode 100644 index 000000000..1fbad8d35 --- /dev/null +++ b/src/Starward/Pages/HoyolabToolbox/InterKnotMonthlyReportPage.xaml.cs @@ -0,0 +1,369 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Microsoft.Extensions.Logging; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Imaging; +using Microsoft.UI.Xaml.Navigation; +using Starward.Controls; +using Starward.Core; +using Starward.Core.GameRecord; +using Starward.Core.GameRecord.ZZZ.InterKnotReport; +using Starward.Helpers; +using Starward.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using Windows.UI; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Starward.Pages.HoyolabToolbox; + +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +[INotifyPropertyChanged] +public sealed partial class InterKnotMonthlyReportPage : PageBase +{ + + private readonly ILogger _logger = AppConfig.GetLogger(); + + + private readonly GameRecordService _gameRecordService = AppConfig.GetService(); + + + public InterKnotMonthlyReportPage() + { + this.InitializeComponent(); + } + + + + private GameRecordRole gameRole; + + + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + if (e.Parameter is GameRecordRole role) + { + gameRole = role; + } + } + + + protected override async void OnLoaded() + { + await Task.Delay(16); + await InitializeDataAsync(); + } + + + + [ObservableProperty] + private InterKnotReportSummary currentSummary; + + + [ObservableProperty] + private InterKnotReportSummary? selectMonthData; + + + [ObservableProperty] + private List monthDataList; + + + [ObservableProperty] + private List? currentSeries; + + + [ObservableProperty] + private List? selectSeries; + + + [ObservableProperty] + private List dayDataList; + + + + private static readonly Dictionary actionColorMap = new Dictionary() + { + ["daily_activity_rewards"] = Color.FromArgb(0xFF, 0x5C, 0xC8, 0x3D), + ["growth_rewards"] = Color.FromArgb(0xFF, 0xA2, 0xD1, 0x04), + ["event_rewards"] = Color.FromArgb(0xFF, 0xFF, 0xDE, 0x00), + ["hollow_rewards"] = Color.FromArgb(0xFF, 0xFF, 0x44, 0x83), + ["shiyu_rewards"] = Color.FromArgb(0xFF, 0x57, 0xBF, 0xF7), + ["mail_rewards"] = Color.FromArgb(0xFF, 0xC9, 0x2A, 0xDE), + ["other_rewards"] = Color.FromArgb(0xFF, 0xF1, 0xAD, 0x3D), + }; + + + + [RelayCommand] + private async Task InitializeDataAsync() + { + await Task.Delay(16); + await GetCurrentSummaryAsync(); + GetMonthDataList(); + } + + + + + private async Task GetCurrentSummaryAsync() + { + try + { + if (gameRole is null) + { + return; + } + CurrentSummary = await _gameRecordService.GetInterKnotReportSummaryAsync(gameRole); + MenuFlyout_GetDetails.Items.Clear(); + foreach (string monthStr in CurrentSummary.OptionalMonth) + { + if (DateTime.TryParseExact(monthStr, "yyyyMM", null, System.Globalization.DateTimeStyles.None, out DateTime time)) + { + MenuFlyout_GetDetails.Items.Add(new MenuFlyoutItem + { + Text = time.ToString("MMM"), + Command = GetDataDetailsCommand, + CommandParameter = monthStr, + }); + } + else + { + MenuFlyout_GetDetails.Items.Add(new MenuFlyoutItem + { + Text = monthStr, + Command = GetDataDetailsCommand, + CommandParameter = monthStr, + }); + } + } + CurrentSeries = CurrentSummary.MonthData.IncomeComponents.Select(x => new ColorRectChart.ChartLegend(ActionName(x.Action), x.Percent, actionColorMap.GetValueOrDefault(x.Action))).ToList(); + } + catch (miHoYoApiException ex) + { + _logger.LogError(ex, "Get realtime inter knot report data ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + NotificationBehavior.Instance.Warning(Lang.Common_AccountError, ex.Message); + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Get realtime inter knot report data ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + NotificationBehavior.Instance.Warning(Lang.Common_NetworkError, ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Get realtime inter knot report data ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + NotificationBehavior.Instance.Error(ex); + } + } + + + + private void GetMonthDataList() + { + try + { + SelectMonthData = null; + MonthDataList = _gameRecordService.GetInterKnotReportSummaryList(gameRole); + Image_Emoji.Visibility = MonthDataList.Count == 0 ? Visibility.Visible : Visibility.Collapsed; + } + catch (Exception ex) + { + _logger.LogError(ex, "Load inter knot report month data ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + } + } + + + + + + [RelayCommand] + private async Task GetDataDetailsAsync(string month) + { + try + { + if (gameRole is null) + { + return; + } + var summary = await _gameRecordService.GetInterKnotReportSummaryAsync(gameRole, month); + foreach (var item in summary.MonthData.List) + { + await _gameRecordService.GetInterKnotReportDetailAsync(gameRole, month, item.DataType); + } + GetMonthDataList(); + } + catch (miHoYoApiException ex) + { + _logger.LogError(ex, "Get inter knot report data details ({gameBiz}, {uid}, {month}).", gameRole?.GameBiz, gameRole?.Uid, month); + NotificationBehavior.Instance.Warning(Lang.Common_AccountError, ex.Message); + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Get inter knot report data details ({gameBiz}, {uid}, {month}).", gameRole?.GameBiz, gameRole?.Uid, month); + NotificationBehavior.Instance.Warning(Lang.Common_NetworkError, ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Get inter knot report data details ({gameBiz}, {uid}, {month}).", gameRole?.GameBiz, gameRole?.Uid, month); + NotificationBehavior.Instance.Error(ex); + } + } + + + + + + private void ListView_MonthDataList_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + try + { + if (e.AddedItems.FirstOrDefault() is InterKnotReportSummary data) + { + SelectMonthData = _gameRecordService.GetInterKnotReportSummary(data)!; + SelectSeries = SelectMonthData.MonthData.IncomeComponents.Select(x => new ColorRectChart.ChartLegend(ActionName(x.Action), x.Percent, actionColorMap.GetValueOrDefault(x.Action))).ToList(); + RefreshDailyDataPlot(data); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Selection changed ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + } + } + + + + private void RefreshDailyDataPlot(InterKnotReportSummary data) + { + try + { + var items_poly = _gameRecordService.GetInterKnotReportDetailItems(data.Uid, data.DataMonth, InterKnotReportDataType.PolychromesData); + var items_tape = _gameRecordService.GetInterKnotReportDetailItems(data.Uid, data.DataMonth, InterKnotReportDataType.MatserTapeData); + var items_boopon = _gameRecordService.GetInterKnotReportDetailItems(data.Uid, data.DataMonth, InterKnotReportDataType.BooponsData); + int days = DateTime.DaysInMonth(int.Parse(data.DataMonth[..4]), int.Parse(data.DataMonth[4..])); + var x = Enumerable.Range(1, days).ToArray(); + + var stats_poly = new int[days]; + foreach (var item in items_poly) + { + var day = item.Time.Day; + if (day <= days) + { + stats_poly[day - 1] += item.Number; + } + } + + var stats_tape = new int[days]; + foreach (var item in items_tape) + { + var day = item.Time.Day; + if (day <= days) + { + stats_tape[day - 1] += item.Number; + } + } + + var stats_boopon = new int[days]; + foreach (var item in items_boopon) + { + var day = item.Time.Day; + if (day <= days) + { + stats_boopon[day - 1] += item.Number; + } + } + + double max_poly = stats_poly.Max(); + double max_tape = stats_tape.Max(); + double max_boopon = stats_boopon.Max(); + max_poly = max_poly == 0 ? double.MaxValue : max_poly; + max_tape = max_tape == 0 ? double.MaxValue : max_tape; + max_boopon = max_boopon == 0 ? double.MaxValue : max_boopon; + var list = new List(days); + for (int i = 0; i < days; i++) + { + list.Add(new CalendarDayData + { + Day = $"{data.DataMonth[4..]}-{i + 1:D2}", + Poly = stats_poly[i], + Tape = stats_tape[i], + Boopon = stats_boopon[i], + PolyProgress = stats_poly[i] / max_poly, + TapeProgress = stats_tape[i] / max_tape, + BooponProgress = stats_boopon[i] / max_boopon, + }); + } + DayDataList = list; + } + catch (Exception ex) + { + _logger.LogError(ex, "Refresh daily data plot"); + } + } + + + + + public class CalendarDayData + { + + public string Day { get; set; } + + public int Poly { get; set; } + + public int Tape { get; set; } + + public int Boopon { get; set; } + + public double PolyProgress { get; set; } + + public double TapeProgress { get; set; } + + public double BooponProgress { get; set; } + + } + + + + + + public static BitmapImage? DataTypeToImage(string type) + { + return type switch + { + InterKnotReportDataType.PolychromesData => new BitmapImage(new("ms-appx:///Assets/Image/IconCurrency.png")), + InterKnotReportDataType.MatserTapeData => new BitmapImage(new("ms-appx:///Assets/Image/GachaTicket2Big.png")), + InterKnotReportDataType.BooponsData => new BitmapImage(new("ms-appx:///Assets/Image/GachaTicket3Big.png")), + _ => null, + }; + } + + + + + public static string ActionName(string action) + { + return action switch + { + "daily_activity_rewards" => Lang.InterKnotMonthlyReportPage_DailyActivityRewardeds, + "growth_rewards" => Lang.InterKnotMonthlyReportPage_DevelopmentRewards, + "event_rewards" => Lang.InterKnotMonthlyReportPage_EventRewards, + "hollow_rewards" => Lang.InterKnotMonthlyReportPage_HollowZeroRewards, + "shiyu_rewards" => Lang.InterKnotMonthlyReportPage_ShiyuDefenseRewards, + "mail_rewards" => Lang.InterKnotMonthlyReportPage_MailRewards, + "other_rewards" => Lang.InterKnotMonthlyReportPage_OtherRewards, + _ => action, + }; + } + + + + +} diff --git a/src/Starward/Pages/MainPage.xaml.cs b/src/Starward/Pages/MainPage.xaml.cs index 477da65ab..0fd176a92 100644 --- a/src/Starward/Pages/MainPage.xaml.cs +++ b/src/Starward/Pages/MainPage.xaml.cs @@ -534,7 +534,7 @@ private void UpdateNavigationViewItemsText() NavigationViewItem_GameSetting.Visibility = Visibility.Visible; NavigationViewItem_Screenshot.Visibility = Visibility.Visible; NavigationViewItem_GachaLog.Visibility = Visibility.Visible; - NavigationViewItem_HoyolabToolbox.Visibility = Visibility.Collapsed; + NavigationViewItem_HoyolabToolbox.Visibility = Visibility.Visible; NavigationViewItem_SelfQuery.Visibility = Visibility.Visible; } else @@ -625,7 +625,7 @@ public void NavigateTo(Type? page, object? param = null, NavigationTransitionInf string? destPage = page?.Name; if (destPage is null or nameof(BlankPage) || (CurrentGameBiz.ToGame() is GameBiz.Honkai3rd && destPage is not nameof(GameLauncherPage) and not nameof(GameSettingPage) and not nameof(ScreenshotPage)) - || CurrentGameBiz.ToGame() is GameBiz.ZZZ && destPage is not nameof(GameLauncherPage) and not nameof(GameSettingPage) and not nameof(GachaLogPage) and not nameof(ScreenshotPage) and not nameof(SelfQueryPage)) + || CurrentGameBiz.ToGame() is GameBiz.ZZZ && destPage is not nameof(GameLauncherPage) and not nameof(GameSettingPage) and not nameof(GachaLogPage) and not nameof(ScreenshotPage) and not nameof(HoyolabToolboxPage) and not nameof(SelfQueryPage)) { page = typeof(GameLauncherPage); destPage = nameof(GameLauncherPage); diff --git a/src/Starward/Services/DatabaseService.cs b/src/Starward/Services/DatabaseService.cs index 499ea87e0..ac9cfd207 100644 --- a/src/Starward/Services/DatabaseService.cs +++ b/src/Starward/Services/DatabaseService.cs @@ -209,7 +209,7 @@ public void SetValue(string key, T value, DateTime? time = null) #region Database Structure - private static readonly List DatabaseSqls = [Sql_v1, Sql_v2, Sql_v3, Sql_v4, Sql_v5, Sql_v6, Sql_v7, Sql_v8, Sql_v9, Sql_v10]; + private static readonly List DatabaseSqls = [Sql_v1, Sql_v2, Sql_v3, Sql_v4, Sql_v5, Sql_v6, Sql_v7, Sql_v8, Sql_v9, Sql_v10, Sql_v11]; private const string Sql_v1 = """ @@ -677,6 +677,36 @@ PRIMARY KEY (Uid, ScheduleId) COMMIT TRANSACTION; """; + private const string Sql_v11 = """ + BEGIN TRANSACTION; + + CREATE TABLE IF NOT EXISTS InterKnotReportSummary + ( + Uid INTEGER NOT NULL, + DataMonth TEXT NOT NULL, + Value TEXT, + PRIMARY KEY (Uid, DataMonth) + ); + + CREATE TABLE IF NOT EXISTS InterKnotReportDetailItem + ( + Uid INTEGER NOT NULL, + Id INTEGER NOT NULL, + DataMonth TEXT NOT NULL, + DataType TEXT NOT NULL, + Action TEXT NOT NULL, + Time TEXT NOT NULL, + Number INTEGER NOT NULL, + PRIMARY KEY (Uid, Id) + ); + CREATE INDEX IF NOT EXISTS IX_InterKnotReportDetailItem_DataMonth ON InterKnotReportDetailItem (DataMonth); + CREATE INDEX IF NOT EXISTS IX_InterKnotReportDetailItem_DataType ON InterKnotReportDetailItem (DataType); + CREATE INDEX IF NOT EXISTS IX_InterKnotReportDetailItem_Time ON InterKnotReportDetailItem (Time); + + PRAGMA USER_VERSION = 11; + COMMIT TRANSACTION; + """; + #endregion diff --git a/src/Starward/Services/GameRecordService.cs b/src/Starward/Services/GameRecordService.cs index 1eb77b1e7..6372544d6 100644 --- a/src/Starward/Services/GameRecordService.cs +++ b/src/Starward/Services/GameRecordService.cs @@ -10,6 +10,7 @@ using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; +using Starward.Core.GameRecord.ZZZ.InterKnotReport; using System; using System.Collections.Generic; using System.Linq; @@ -814,5 +815,93 @@ public List GetTrailblazeCalendarDetailItems(long + #region Inter Knot Report + + + + public async Task GetInterKnotReportSummaryAsync(GameRecordRole role, string month = "") + { + var summary = await _gameRecordClient.GetInterKnotReportSummaryAsync(role, month); + using var dapper = _databaseService.CreateConnection(); + var obj = new + { + summary.Uid, + summary.DataMonth, + Value = JsonSerializer.Serialize(summary), + }; + dapper.Execute(""" + INSERT OR REPLACE INTO InterKnotReportSummary (Uid, DataMonth, Value) VALUES (@Uid, @DataMonth, @Value); + """, obj); + return summary; + } + + + public List GetInterKnotReportSummaryList(GameRecordRole role) + { + if (role is null) + { + return new List(); + } + using var dapper = _databaseService.CreateConnection(); + var list = dapper.Query("SELECT * FROM InterKnotReportSummary WHERE Uid = @Uid ORDER BY DataMonth DESC;", new { role.Uid }); + return list.ToList(); + } + + + public InterKnotReportSummary? GetInterKnotReportSummary(InterKnotReportSummary summary) + { + if (summary is null) + { + return null; + } + using var dapper = _databaseService.CreateConnection(); + string? value = dapper.QueryFirstOrDefault("SELECT Value FROM InterKnotReportSummary WHERE Uid = @Uid AND DataMonth = @DataMonth LIMIT 1;", summary); + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + return JsonSerializer.Deserialize(value); + } + + + + public async Task GetInterKnotReportDetailAsync(GameRecordRole role, string month, string type) + { + int total = (await _gameRecordClient.GetInterKnotReportDetailByPageAsync(role, month, type, 1, 1)).Total; + if (total == 0) + { + return 0; + } + using var dapper = _databaseService.CreateConnection(); + var existCount = dapper.QuerySingleOrDefault("SELECT COUNT(*) FROM InterKnotReportDetailItem WHERE Uid = @Uid AND DataMonth = @month AND DataType = @type;", new { role.Uid, month, type }); + if (existCount == total) + { + return 0; + } + var detail = await _gameRecordClient.GetInterKnotReportDetailAsync(role, month, type); + var list = detail.List; + using var t = dapper.BeginTransaction(); + dapper.Execute($"DELETE FROM InterKnotReportDetailItem WHERE Uid = @Uid AND DataMonth = @DataMonth AND DataType = @DataType;", list.FirstOrDefault(), t); + dapper.Execute(""" + INSERT INTO InterKnotReportDetailItem (Uid, Id, DataMonth, DataType, Action, Time, Number) + VALUES (@Uid, @Id, @DataMonth, @DataType, @Action, @Time, @Number); + """, list, t); + t.Commit(); + return total - existCount; + } + + + + public List GetInterKnotReportDetailItems(long uid, string month, string type) + { + using var dapper = _databaseService.CreateConnection(); + return dapper.Query("SELECT * FROM InterKnotReportDetailItem WHERE Uid=@uid AND DataMonth=@month AND DataType=@type ORDER BY Time;", new { uid, month, type }).ToList(); + } + + + + + #endregion + }