using System.Threading.Tasks; using System.Threading; using AIK.Common.Interface; using AIK.Common.SysCommon; using AIK.Models.ApiModels; using AIK.Models.ApiModels.Membership; using AIK.Service.IService; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; namespace AIK.Service.Service { public class MembershipService : IMembershipService { private static readonly JsonSerializerSettings JsonSettings = new() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore }; private readonly IHttpClientService _httpClientService; private readonly IConfigurationService _configurationService; private readonly ISettingsService _settingsService; private readonly IRetryPolicyService _retryPolicyService; private readonly ILogger _logger; public MembershipService( IHttpClientService httpClientService, IConfigurationService configurationService, ISettingsService settingsService, IRetryPolicyService retryPolicyService, ILogger logger) { _httpClientService = httpClientService; _configurationService = configurationService; _settingsService = settingsService; _retryPolicyService = retryPolicyService; _logger = logger; } public Task> GetModuleListAsync(string model = "key", CancellationToken cancellationToken = default) { return _retryPolicyService.ExecuteWithRetryAsync(async () => { var url = BuildUrl(WebApiAddress.MembershipModuleList, new() { ["model"] = string.IsNullOrWhiteSpace(model) ? "key" : model, ["language"] = GetLanguage() }); var response = await _httpClientService.GetStringWithTimeoutAsync(url, TimeSpan.FromSeconds(15), cancellationToken); var apiResponse = DeserializeResponse>(response, nameof(GetModuleListAsync)); return IsSuccess(apiResponse) ? apiResponse.Data ?? new List() : new List(); }, nameof(GetModuleListAsync), cancellationToken); } public Task> FindOpenedFunctionsAsync(string token, MembershipFindRequest request, CancellationToken cancellationToken = default) { return _retryPolicyService.ExecuteWithRetryAsync(async () => { request.Language ??= GetLanguage(); var url = BuildUrl(WebApiAddress.MembershipFind); var json = JsonConvert.SerializeObject(request, JsonSettings); var response = await _httpClientService.PostJsonWithTimeoutAndTokenAsync(url, json, token, TimeSpan.FromSeconds(15), cancellationToken); return DeserializePageResponse(response, nameof(FindOpenedFunctionsAsync)); }, nameof(FindOpenedFunctionsAsync), cancellationToken); } public Task> OpenFunctionAsync(string token, OpenMembershipRequest request, CancellationToken cancellationToken = default) { return _retryPolicyService.ExecuteWithRetryAsync(async () => { request.Language ??= GetLanguage(); return await PostWithTokenAsync(WebApiAddress.MembershipOpen, token, request, nameof(OpenFunctionAsync), cancellationToken); }, nameof(OpenFunctionAsync), cancellationToken); } public Task BindDeviceAsync(string token, BindMembershipDeviceRequest request, CancellationToken cancellationToken = default) { return _retryPolicyService.ExecuteWithRetryAsync(async () => { request.Language ??= GetLanguage(); var response = await PostWithTokenAsync(WebApiAddress.MembershipBind, token, request, nameof(BindDeviceAsync), cancellationToken); return ToUntypedResponse(response); }, nameof(BindDeviceAsync), cancellationToken); } public Task CheckPermissionAsync(string token, CheckMembershipPermissionRequest request, CancellationToken cancellationToken = default) { return _retryPolicyService.ExecuteWithRetryAsync(async () => { request.Language ??= GetLanguage(); var response = await PostWithTokenAsync(WebApiAddress.MembershipCheck, token, request, nameof(CheckPermissionAsync), cancellationToken); return IsSuccess(response) && response.Data; }, nameof(CheckPermissionAsync), cancellationToken); } public Task ActivateDeviceAsync(string token, ActivateDeviceRequest request, CancellationToken cancellationToken = default) { return _retryPolicyService.ExecuteWithRetryAsync(async () => { request.Language ??= GetLanguage(); var response = await PostWithTokenAsync(WebApiAddress.MembershipActivate, token, request, nameof(ActivateDeviceAsync), cancellationToken); return ToUntypedResponse(response); }, nameof(ActivateDeviceAsync), cancellationToken); } public Task GetBurnCountInfoAsync(string token, CancellationToken cancellationToken = default) { return _retryPolicyService.ExecuteWithRetryAsync(async () => { var url = BuildUrl(WebApiAddress.MembershipFindCount); var response = await _httpClientService.GetStringWithTimeoutAndTokenAsync(url, token, TimeSpan.FromSeconds(15), cancellationToken); var apiResponse = DeserializeResponse(response, nameof(GetBurnCountInfoAsync)); return IsSuccess(apiResponse) ? apiResponse.Data : null; }, nameof(GetBurnCountInfoAsync), cancellationToken); } public Task AddBurnCountAsync(string token, string type = "ysjs", CancellationToken cancellationToken = default) { return _retryPolicyService.ExecuteWithRetryAsync(async () => { var url = BuildUrl(WebApiAddress.MembershipAddCount, new() { ["type"] = string.IsNullOrWhiteSpace(type) ? "ysjs" : type }); var response = await _httpClientService.GetStringWithTimeoutAndTokenAsync(url, token, TimeSpan.FromSeconds(15), cancellationToken); return ToUntypedResponse(DeserializeResponse(response, nameof(AddBurnCountAsync))); }, nameof(AddBurnCountAsync), cancellationToken); } private async Task> PostWithTokenAsync(string path, string token, object request, string operationName, CancellationToken cancellationToken) { var url = BuildUrl(path); var json = JsonConvert.SerializeObject(request, JsonSettings); var response = await _httpClientService.PostJsonWithTimeoutAndTokenAsync(url, json, token, TimeSpan.FromSeconds(15), cancellationToken); return DeserializeResponse(response, operationName); } private string BuildUrl(string path, Dictionary? query = null) { var url = $"{_configurationService.ApiSettings.BaseUrl}{path}"; if (query == null || query.Count == 0) { return url; } var queryString = string.Join("&", query .Where(item => !string.IsNullOrWhiteSpace(item.Value)) .Select(item => $"{WebUtility.UrlEncode(item.Key)}={WebUtility.UrlEncode(item.Value)}")); return string.IsNullOrEmpty(queryString) ? url : $"{url}?{queryString}"; } private string GetLanguage() { return string.IsNullOrWhiteSpace(_settingsService.Load().Language) ? "zh" : _settingsService.Load().Language; } private ApiResponse DeserializeResponse(string response, string operationName) { var apiResponse = JsonConvert.DeserializeObject>(response) ?? new ApiResponse(); if (!IsSuccess(apiResponse)) { _logger.LogWarning("{OperationName} failed. Code: {Code}, Message: {Message}, Response: {Response}", operationName, apiResponse.Code, apiResponse.Msg, response); } return apiResponse; } private ApiPageResult DeserializePageResponse(string response, string operationName) { var pageResponse = JsonConvert.DeserializeObject>(response) ?? new ApiPageResponse(); if (pageResponse.Code != 1) { _logger.LogWarning("{OperationName} failed. Code: {Code}, Message: {Message}, Response: {Response}", operationName, pageResponse.Code, pageResponse.Msg, response); return new ApiPageResult(); } return new ApiPageResult { Records = pageResponse.Records ?? new List(), Total = pageResponse.Total, Current = pageResponse.Current, Size = pageResponse.Size, Pages = pageResponse.Pages }; } private static bool IsSuccess(ApiResponse response) { return response.Code == 1; } private static ApiResponse ToUntypedResponse(ApiResponse response) { return new ApiResponse { Code = response.Code, Msg = response.Msg, I18nMsg = response.I18nMsg, Data = response.Data, Dict = response.Dict }; } private sealed class ApiPageResponse : ApiPageResult { public int Code { get; set; } public string? Msg { get; set; } public string? I18nMsg { get; set; } public Dictionary? Dict { get; set; } } } }