diff --git a/YD_Event.Application/applicationsettings.json b/YD_Event.Application/applicationsettings.json index f9a477c..992b354 100644 --- a/YD_Event.Application/applicationsettings.json +++ b/YD_Event.Application/applicationsettings.json @@ -98,6 +98,8 @@ }, // 微信小程序配置 "WeChat": { + // 小程序码版本:develop=开发版,trial=体验版,release=正式版;上线前请改为 release + "MiniProgramEnvVersion": "develop", "AppId": "wxf5a3f98d44c3a563", "Secret": "ef6d17e0419700c137e066aa3dce23ca" }, @@ -172,4 +174,4 @@ "ScoringUnitRoleId": 1003, "HiddenMenuIds": [2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009] } -} \ No newline at end of file +} diff --git a/YD_Event.Core/Infrastructure/WeChat/WeChatMiniProgramCodeService.cs b/YD_Event.Core/Infrastructure/WeChat/WeChatMiniProgramCodeService.cs index 69bf944..2742971 100644 --- a/YD_Event.Core/Infrastructure/WeChat/WeChatMiniProgramCodeService.cs +++ b/YD_Event.Core/Infrastructure/WeChat/WeChatMiniProgramCodeService.cs @@ -1,4 +1,3 @@ -using System.Net.Http.Json; using System.Text; using EasyCaching.Core; using Furion.DependencyInjection; @@ -41,7 +40,21 @@ public class WeChatMiniProgramCodeService : ITransient throw Oops.Bah("微信小程序配置不完整"); using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - return await GenerateAsync(scene, page, width, false, timeout.Token); + try + { + return await GenerateAsync(scene, page, width, false, timeout.Token); + } + catch (OperationCanceledException) when (timeout.IsCancellationRequested) + { + _logger.LogError("WeChat mini program code request timed out after 15 seconds"); + throw Oops.Bah("微信小程序码服务请求超时,请稍后重试"); + } + catch (HttpRequestException exception) + { + _logger.LogError("WeChat mini program code network error: {ErrorType}, {ErrorMessage}", + exception.GetType().Name, exception.Message); + throw Oops.Bah("微信小程序码服务网络异常,请稍后重试"); + } } private async Task GenerateAsync(string scene, string page, int width, @@ -49,22 +62,26 @@ public class WeChatMiniProgramCodeService : ITransient { var accessToken = await GetAccessTokenAsync(forceRefreshToken, cancellationToken); var client = _httpClientFactory.CreateClient(); - using var response = await client.PostAsJsonAsync( + var requestBody = JsonConvert.SerializeObject(new + { + scene, + page, + width, + check_path = true, + env_version = NormalizeEnvVersion(_options.MiniProgramEnvVersion) + }); + using var requestContent = new StringContent(requestBody, Encoding.UTF8, "application/json"); + using var response = await client.PostAsync( $"https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={Uri.EscapeDataString(accessToken)}", - new - { - scene, - page, - width, - check_path = true, - env_version = NormalizeEnvVersion(_options.MiniProgramEnvVersion) - }, cancellationToken); + requestContent, cancellationToken); if (!response.IsSuccessStatusCode) { var errorBody = await response.Content.ReadAsStringAsync(cancellationToken); - _logger.LogError("WeChat mini program code HTTP error: {StatusCode}, Body: {ErrorBody}", - response.StatusCode, errorBody); - throw Oops.Bah("微信小程序码服务暂不可用,请检查微信接口白名单配置"); + _logger.LogError( + "WeChat mini program code HTTP error: {StatusCode}, Page: {Page}, EnvVersion: {EnvVersion}, Body: {ErrorBody}", + response.StatusCode, page, NormalizeEnvVersion(_options.MiniProgramEnvVersion), LimitLogContent(errorBody)); + + throw Oops.Bah("微信小程序码服务暂不可用,请稍后重试"); } var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken); @@ -89,7 +106,7 @@ public class WeChatMiniProgramCodeService : ITransient return await GenerateAsync(scene, page, width, true, cancellationToken); } - _logger.LogError("WeChat mini program code error: {Error}", errorJson); + _logger.LogError("WeChat mini program code error: {Error}", LimitLogContent(errorJson)); throw Oops.Bah("微信小程序码生成失败,请稍后重试"); } @@ -109,15 +126,15 @@ public class WeChatMiniProgramCodeService : ITransient { var errorBody = await response.Content.ReadAsStringAsync(cancellationToken); _logger.LogError("WeChat access token HTTP error: {StatusCode}, Body: {ErrorBody}", - response.StatusCode, errorBody); - throw Oops.Bah("微信服务暂不可用,请检查微信接口白名单配置"); + response.StatusCode, LimitLogContent(errorBody)); + throw Oops.Bah("微信服务暂不可用,请稍后重试"); } var json = await response.Content.ReadAsStringAsync(cancellationToken); var token = JsonConvert.DeserializeObject(json); if (token == null || string.IsNullOrWhiteSpace(token.AccessToken)) { - _logger.LogError("WeChat access token error: {Error}", json); + _logger.LogError("WeChat access token error: {Error}", LimitLogContent(json)); throw Oops.Bah("微信访问凭证获取失败,请检查小程序配置"); } @@ -129,13 +146,18 @@ public class WeChatMiniProgramCodeService : ITransient private static bool IsJpeg(byte[] bytes) => bytes.Length >= 3 && bytes[0] == 0xFF && bytes[1] == 0xD8 && bytes[2] == 0xFF; - private static string NormalizeEnvVersion(string? envVersion) => envVersion?.ToLowerInvariant() switch + private static string NormalizeEnvVersion(string? envVersion) => envVersion?.Trim().ToLowerInvariant() switch { "develop" => "develop", "trial" => "trial", - _ => "release" + null or "" or "release" => "release", + _ => throw Oops.Bah("微信小程序码版本配置无效,仅支持 develop、trial 或 release") }; + private static string LimitLogContent(string content) => content.Length <= 4096 + ? content + : $"{content[..4096]}...(truncated)"; + private sealed class WeChatAccessTokenResponse : WeChatApiError { [JsonProperty("access_token")]