diff --git a/IRaCIS.Core.API/appsettings.json b/IRaCIS.Core.API/appsettings.json index 46d4d60fe..a39b1b988 100644 --- a/IRaCIS.Core.API/appsettings.json +++ b/IRaCIS.Core.API/appsettings.json @@ -44,8 +44,10 @@ ] }, "WeComNoticeConfig": { + "IsOpenWeComNotice": true, "WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322 - "NoticeUserList": [ "ZhouHang" ] + "APINoticeUserList": [ "ZhouHang" ], + "VueNoticeUserList": [ "wangxiaoshuang" ] }, "IRaCISImageStore": { "SwitchingMode": "RemainingDiskCapacity", diff --git a/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs index ee1d392cf..65552ebcf 100644 --- a/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs +++ b/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs @@ -1,18 +1,22 @@ -using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Helper.OtherTool; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Localization; using Newtonsoft.Json; +using System; using System.Reflection; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace IRaCIS.Core.Application.Filter; -public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttribute, IActionFilter +public class ModelActionFilter(IStringLocalizer _localizer, IConfiguration _config, IUserInfo _userInfo) : ActionFilterAttribute, IActionFilter { @@ -27,6 +31,49 @@ public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttrib .Select(e => e.ErrorMessage) .ToArray(); + var request = context.HttpContext.Request; + + try + { + bool isOpenWeComNotice = _config.GetValue("WeComNoticeConfig:IsOpenWeComNotice"); + + if (isOpenWeComNotice) + { + string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty; + + var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]); + var baseUrl = uri.GetLeftPart(UriPartial.Authority); + + var userList = _config.GetSection("WeComNoticeConfig:VueNoticeUserList").Get(); + + var requestBody = context.HttpContext.Items["RequestBody"] as string; + + + // 🔔 异步告警(不要阻塞请求) + _ = WeComNotifier.SendAlertAsync( + webhook: webhook, + alert: new WeComAlert + { + Env = baseUrl, + UserName = _userInfo.UserName.IsNotNullOrEmpty()? $"{_userInfo.UserName}({_userInfo.UserTypeShortName})": "匿名", + Api = $"{request.Method} {request.Path}", + Message = $"前端传递参数,后端模型验证失败\n 具体信息:{JsonConvert.SerializeObject(validationErrors)}", + JsonData = requestBody, + AtUsers = userList ?? [] + } + ); + + } + + + } + catch (Exception ex) + { + Log.Logger.Error($"模型验证过滤器里发送企业微信出现错误:{ex.Message}"); + } + + + //---提供给接口的参数无效。 context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject(validationErrors))); } diff --git a/IRaCIS.Core.Application/BusinessFilter/LegacyController/ProjectExceptionFilter.cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/ProjectExceptionFilter.cs index 3231e9059..eeb70dcc9 100644 --- a/IRaCIS.Core.Application/BusinessFilter/LegacyController/ProjectExceptionFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/LegacyController/ProjectExceptionFilter.cs @@ -53,27 +53,41 @@ public class ProjectExceptionFilter(ILogger _logger, ISt try { - string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty; - var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]); - var baseUrl = uri.GetLeftPart(UriPartial.Authority); + bool isOpenWeComNotice = _config.GetValue("WeComNoticeConfig:IsOpenWeComNotice"); - var userList = _config.GetSection("WeComNoticeConfig:NoticeUserList").Get(); + if (isOpenWeComNotice) + { + string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty; + var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]); + var baseUrl = uri.GetLeftPart(UriPartial.Authority); + var userList = _config.GetSection("WeComNoticeConfig:APINoticeUserList").Get(); - var requestBody = context.HttpContext.Items["RequestBody"] as string; + var requestBody = context.HttpContext.Items["RequestBody"] as string; + var alert = new WeComAlert + { + Env = baseUrl, + UserName = $"{_userInfo.UserName}({_userInfo.UserTypeShortName})", + Api = context.HttpContext.Request.Path, + Message = exception.Message, + JsonData = requestBody, + Stack = exception.StackTrace, + AtUsers = userList ?? [] + }; + _ = WeComNotifier.SendAlertAsync(webhook, alert); + } - - WeComNotifier.SendErrorAsync(webhook, baseUrl, $"{_userInfo.UserName}({_userInfo.UserTypeShortName})", context.HttpContext.Request.Path, requestBody, exception, userList).GetAwaiter().GetResult(); + } catch (Exception ex) { _logger.LogError($"异常过滤器里发送企业微信出现错误:{ex.Message}"); } - + } context.ExceptionHandled = true;//标记当前异常已经被处理过了 diff --git a/IRaCIS.Core.Application/Helper/OtherTool/WeComNotifier.cs b/IRaCIS.Core.Application/Helper/OtherTool/WeComNotifier.cs index 641b117b1..db100aa68 100644 --- a/IRaCIS.Core.Application/Helper/OtherTool/WeComNotifier.cs +++ b/IRaCIS.Core.Application/Helper/OtherTool/WeComNotifier.cs @@ -12,76 +12,87 @@ using IdentityModel; namespace IRaCIS.Core.Application.Helper.OtherTool; +public class WeComAlert +{ + public string Env { get; set; } = ""; + public string UserName { get; set; } = ""; + public string Api { get; set; } = ""; + public string Message { get; set; } = ""; + public string? JsonData { get; set; } + public string? Stack { get; set; } + public bool HasStack => !string.IsNullOrWhiteSpace(Stack); + public string[] AtUsers { get; set; } = []; +} + + public static class WeComNotifier { - - public static async Task SendErrorAsync(string webhook, string env, string userName,string api,string json, Exception ex, params string[] atUsers) + + public static async Task SendAlertAsync(string webhook, WeComAlert alert) { try { var client = new RestClient(); var request = new RestRequest(webhook, Method.Post); - string hostName = Dns.GetHostName(); - string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); - var stack = ex.StackTrace ?? "无堆栈信息"; - stack = stack.Replace("\n", "\n> "); // 每行变成引用,防止markdown断裂 - if (stack.Length > 1200) - stack = stack.Substring(0, 1200) + "...(已截断)"; + // 处理 @ + var atText = alert.AtUsers != null && alert.AtUsers.Any() + ? string.Join("", alert.AtUsers.Select(u => $" <@{u}>")) + : ""; - string atText = ""; - if (atUsers != null && atUsers.Any()) + // 处理堆栈 + var stack = alert.Stack; + if (!string.IsNullOrWhiteSpace(stack)) { - foreach (var u in atUsers) - { - atText += $" <@{u}>"; // 注意空格分隔 - } + stack = stack.Replace("\n", "\n> "); + if (stack.Length > 1200) + stack = stack[..1200] + "...(已截断)"; } + var markdown = $@"## 🚨 系统告警 +> {atText} +> **部署环境:** [{alert.Env}]({alert.Env}) +> **发生时间:** {time} +> **操作人:** {alert.UserName} +> **接口地址:** {alert.Api} +### ❗ 告警信息 +{alert.Message}"; - //> **运行环境:** < font color = ""comment"" >{ EnvironmentName} danger danger warning - var markdown = $@"## 🚨 系统异常告警 - > {atText} - > **部署环境:** [{env}]({env}) - > **服务器:** {hostName} - > **发生时间:** {time} - > **操作人:** {userName} - > **接口地址:** {api} - ### ❗ 异常信息 - {ex.Message} - >**📦 请求数据(JSON 格式):** + if (!string.IsNullOrWhiteSpace(alert.JsonData)) + { + markdown += $@" +> **📦 请求数据(JSON 格式):** ```json -{json} -``` - ### 堆栈信息(部分) - > {stack} - "; +{alert.JsonData} +```"; + } + + if (!string.IsNullOrWhiteSpace(stack)) + { + markdown += $@" +### 堆栈信息(部分) +{stack}"; + } var payload = new { msgtype = "markdown", - markdown = new - { - content = markdown - } + markdown = new { content = markdown } }; request.AddHeader("Content-Type", "application/json"); request.AddStringBody(JsonConvert.SerializeObject(payload), DataFormat.Json); - var response = await client.ExecuteAsync(request); - - if (!response.IsSuccessful) - { - Log.Logger.Error($"企业微信通知失败: {response.StatusCode} {response.ErrorMessage}"); - } + await client.ExecuteAsync(request); } - catch (Exception notifyEx) + catch (Exception ex) { - Log.Logger.Error("发送企业微信告警异常: " + notifyEx.Message); + Log.Logger.Error("企业微信告警发送失败: " + ex.Message); } } + } diff --git a/IRaCIS.Core.Application/TestService.cs b/IRaCIS.Core.Application/TestService.cs index 591a1ec2a..a4ce65daf 100644 --- a/IRaCIS.Core.Application/TestService.cs +++ b/IRaCIS.Core.Application/TestService.cs @@ -116,7 +116,7 @@ namespace IRaCIS.Core.Application.Service var webhook = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=cdd97aab-d256-4f07-9145-a0a2b1555322"; //await WeComNotifier.SendErrorAsync(webhook, "http://irc.test.extimaging.com/login", new Exception("测试异常"), new[] { "ZhouHang" }); - throw new Exception("手动测试异常抛出"); + //throw new Exception("手动测试异常抛出"); return ResponseOutput.Ok(modelVerify); }