Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
commit
ee76366a1c
|
|
@ -44,8 +44,10 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"WeComNoticeConfig": {
|
"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
|
"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": {
|
"IRaCISImageStore": {
|
||||||
"SwitchingMode": "RemainingDiskCapacity",
|
"SwitchingMode": "RemainingDiskCapacity",
|
||||||
|
|
|
||||||
|
|
@ -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.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||||
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter;
|
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)
|
.Select(e => e.ErrorMessage)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
|
var request = context.HttpContext.Request;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool isOpenWeComNotice = _config.GetValue<bool>("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<string[]>();
|
||||||
|
|
||||||
|
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)));
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject(validationErrors)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,27 +53,41 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
|
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
|
||||||
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
|
|
||||||
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
|
|
||||||
|
|
||||||
var userList = _config.GetSection("WeComNoticeConfig:NoticeUserList").Get<string[]>();
|
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<string[]>();
|
||||||
|
|
||||||
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
||||||
_logger.LogError($"异常过滤器里发送企业微信出现错误:{ex.Message}");
|
_logger.LogError($"异常过滤器里发送企业微信出现错误:{ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||||
|
|
|
||||||
|
|
@ -12,76 +12,87 @@ using IdentityModel;
|
||||||
namespace IRaCIS.Core.Application.Helper.OtherTool;
|
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 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
|
try
|
||||||
{
|
{
|
||||||
var client = new RestClient();
|
var client = new RestClient();
|
||||||
var request = new RestRequest(webhook, Method.Post);
|
var request = new RestRequest(webhook, Method.Post);
|
||||||
|
|
||||||
string hostName = Dns.GetHostName();
|
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
||||||
|
|
||||||
var stack = ex.StackTrace ?? "无堆栈信息";
|
// 处理 @
|
||||||
stack = stack.Replace("\n", "\n> "); // 每行变成引用,防止markdown断裂
|
var atText = alert.AtUsers != null && alert.AtUsers.Any()
|
||||||
if (stack.Length > 1200)
|
? string.Join("", alert.AtUsers.Select(u => $" <@{u}>"))
|
||||||
stack = stack.Substring(0, 1200) + "...(已截断)";
|
: "";
|
||||||
|
|
||||||
string atText = "";
|
// 处理堆栈
|
||||||
if (atUsers != null && atUsers.Any())
|
var stack = alert.Stack;
|
||||||
|
if (!string.IsNullOrWhiteSpace(stack))
|
||||||
{
|
{
|
||||||
foreach (var u in atUsers)
|
stack = stack.Replace("\n", "\n> ");
|
||||||
{
|
if (stack.Length > 1200)
|
||||||
atText += $" <@{u}>"; // 注意空格分隔
|
stack = stack[..1200] + "...(已截断)";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var markdown = $@"## 🚨 系统告警
|
||||||
|
> {atText}
|
||||||
|
> **部署环境:** [{alert.Env}]({alert.Env})
|
||||||
|
> **发生时间:** {time}
|
||||||
|
> **操作人:** {alert.UserName}
|
||||||
|
> **接口地址:** {alert.Api}
|
||||||
|
### ❗ 告警信息
|
||||||
|
<font color=""warning"">{alert.Message}</font>";
|
||||||
|
|
||||||
//> **运行环境:** < font color = ""comment"" >{ EnvironmentName}</ font > danger danger warning
|
if (!string.IsNullOrWhiteSpace(alert.JsonData))
|
||||||
var markdown = $@"## 🚨 系统异常告警
|
{
|
||||||
> {atText}
|
markdown += $@"
|
||||||
> **部署环境:** [{env}]({env})
|
> **📦 请求数据(JSON 格式):**
|
||||||
> **服务器:** {hostName}
|
|
||||||
> **发生时间:** {time}
|
|
||||||
> **操作人:** {userName}
|
|
||||||
> **接口地址:** {api}
|
|
||||||
### ❗ 异常信息
|
|
||||||
<font color=""warning"">{ex.Message}</font>
|
|
||||||
>**📦 请求数据(JSON 格式):**
|
|
||||||
```json
|
```json
|
||||||
{json}
|
{alert.JsonData}
|
||||||
```
|
```";
|
||||||
### 堆栈信息(部分)
|
}
|
||||||
> <font color=""comment"">{stack}</font>
|
|
||||||
";
|
if (!string.IsNullOrWhiteSpace(stack))
|
||||||
|
{
|
||||||
|
markdown += $@"
|
||||||
|
### 堆栈信息(部分)
|
||||||
|
<font color=""comment"">{stack}</font>";
|
||||||
|
}
|
||||||
|
|
||||||
var payload = new
|
var payload = new
|
||||||
{
|
{
|
||||||
msgtype = "markdown",
|
msgtype = "markdown",
|
||||||
markdown = new
|
markdown = new { content = markdown }
|
||||||
{
|
|
||||||
content = markdown
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddHeader("Content-Type", "application/json");
|
request.AddHeader("Content-Type", "application/json");
|
||||||
request.AddStringBody(JsonConvert.SerializeObject(payload), DataFormat.Json);
|
request.AddStringBody(JsonConvert.SerializeObject(payload), DataFormat.Json);
|
||||||
|
|
||||||
var response = await client.ExecuteAsync(request);
|
await client.ExecuteAsync(request);
|
||||||
|
|
||||||
if (!response.IsSuccessful)
|
|
||||||
{
|
|
||||||
Log.Logger.Error($"企业微信通知失败: {response.StatusCode} {response.ErrorMessage}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception notifyEx)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Logger.Error("发送企业微信告警异常: " + notifyEx.Message);
|
Log.Logger.Error("企业微信告警发送失败: " + ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -985,6 +985,8 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||||
|
|
||||||
public string QuestionGroupName { get; set; }
|
public string QuestionGroupName { get; set; }
|
||||||
|
|
||||||
|
public string QuestionGroupEnName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 影像工具
|
/// 影像工具
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
.ForMember(dest => dest.CreateUserRole, opt => opt.Ignore());
|
.ForMember(dest => dest.CreateUserRole, opt => opt.Ignore());
|
||||||
CreateMap<ReadingQuestionTrial, ReadingQuestionTrialView>()
|
CreateMap<ReadingQuestionTrial, ReadingQuestionTrialView>()
|
||||||
.ForMember(d => d.QuestionGroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName))
|
.ForMember(d => d.QuestionGroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName))
|
||||||
|
.ForMember(d => d.QuestionGroupEnName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupEnName : s.GroupInfo.GroupEnName))
|
||||||
.ForMember(d => d.GroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName))
|
.ForMember(d => d.GroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName))
|
||||||
.ForMember(d => d.GroupEnName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupEnName : s.GroupInfo.GroupEnName))
|
.ForMember(d => d.GroupEnName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupEnName : s.GroupInfo.GroupEnName))
|
||||||
.ForMember(d => d.ParentQuestionName, u => u.MapFrom(s => s.ParentReadingQuestionTrial == null ? string.Empty : s.ParentReadingQuestionTrial.QuestionName))
|
.ForMember(d => d.ParentQuestionName, u => u.MapFrom(s => s.ParentReadingQuestionTrial == null ? string.Empty : s.ParentReadingQuestionTrial.QuestionName))
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
{
|
{
|
||||||
isVirtual = await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => x.TrialType != TrialType.OfficialTrial).FirstNotNullAsync();
|
isVirtual = await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => x.TrialType != TrialType.OfficialTrial).FirstNotNullAsync();
|
||||||
|
|
||||||
await _doctorRepository.UpdatePartialFromQueryAsync(x => x.Id == dockerInfo.Id, y => new Doctor()
|
await _doctorRepository.BatchUpdateNoTrackingAsync(x => x.Id == dockerInfo.Id, y => new Doctor()
|
||||||
{
|
{
|
||||||
IsVirtual = isVirtual,
|
IsVirtual = isVirtual,
|
||||||
AcceptingNewTrial = inDto.TrialId == null ? false : true,
|
AcceptingNewTrial = inDto.TrialId == null ? false : true,
|
||||||
|
|
@ -245,6 +245,17 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
{
|
{
|
||||||
result.DoctorId = dockerInfo.Id;
|
result.DoctorId = dockerInfo.Id;
|
||||||
result.ReviewStatus = dockerInfo.ReviewStatus;
|
result.ReviewStatus = dockerInfo.ReviewStatus;
|
||||||
|
|
||||||
|
if(inDto.TrialId != null)
|
||||||
|
{
|
||||||
|
var isVirtual = await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => x.TrialType != TrialType.OfficialTrial).FirstNotNullAsync();
|
||||||
|
await _doctorRepository.BatchUpdateNoTrackingAsync(x => x.Id == dockerInfo.Id, y => new Doctor()
|
||||||
|
{
|
||||||
|
IsVirtual = isVirtual,
|
||||||
|
AcceptingNewTrial = inDto.TrialId == null ? false : true,
|
||||||
|
CooperateStatus = inDto.TrialId == null ? ContractorStatusEnum.Noncooperation : ContractorStatusEnum.Cooperation,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -254,16 +265,19 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
{
|
{
|
||||||
isVirtual = await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => x.TrialType != TrialType.OfficialTrial).FirstNotNullAsync();
|
isVirtual = await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => x.TrialType != TrialType.OfficialTrial).FirstNotNullAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
Doctor doctor = new Doctor()
|
Doctor doctor = new Doctor()
|
||||||
{
|
{
|
||||||
EMail = inDto.EmailOrPhone,
|
EMail = inDto.EmailOrPhone,
|
||||||
IsVirtual = isVirtual,
|
IsVirtual = isVirtual,
|
||||||
AcceptingNewTrial = false,
|
AcceptingNewTrial = inDto.TrialId == null ? false : true,
|
||||||
ActivelyReading = false,
|
ActivelyReading = false,
|
||||||
CooperateStatus = ContractorStatusEnum.Noncooperation,
|
ResumeStatus = ResumeStatusEnum.Pass,
|
||||||
|
CooperateStatus = inDto.TrialId == null ? ContractorStatusEnum.Noncooperation : ContractorStatusEnum.Cooperation,
|
||||||
|
|
||||||
ReviewStatus = ReviewerInformationConfirmStatus.ConfirmRefuse
|
ReviewStatus = ReviewerInformationConfirmStatus.ConfirmRefuse
|
||||||
};
|
};
|
||||||
|
|
||||||
doctor.Code = await _doctorRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync() + 1;
|
doctor.Code = await _doctorRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync() + 1;
|
||||||
doctor.ReviewerCode = AppSettings.GetCodeStr(doctor.Code, nameof(Doctor));
|
doctor.ReviewerCode = AppSettings.GetCodeStr(doctor.Code, nameof(Doctor));
|
||||||
var info = await _doctorRepository.AddAsync(doctor, true);
|
var info = await _doctorRepository.AddAsync(doctor, true);
|
||||||
|
|
|
||||||
|
|
@ -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";
|
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" });
|
//await WeComNotifier.SendErrorAsync(webhook, "http://irc.test.extimaging.com/login", new Exception("测试异常"), new[] { "ZhouHang" });
|
||||||
|
|
||||||
throw new Exception("手动测试异常抛出");
|
//throw new Exception("手动测试异常抛出");
|
||||||
return ResponseOutput.Ok(modelVerify);
|
return ResponseOutput.Ok(modelVerify);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue