数据记录审计方法调整
continuous-integration/drone/push Build is passing Details

IRC_NewDev
hang 2024-08-17 16:06:35 +08:00
parent 5c2bebf96c
commit cd2166f11b
8 changed files with 183 additions and 94 deletions

View File

@ -4,6 +4,7 @@ using IRaCIS.Core.Infra.EFCore;
using Medallion.Threading;
using Medallion.Threading.SqlServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -15,11 +16,12 @@ namespace IRaCIS.Core.SCP
{
services.AddHttpContextAccessor();
services.AddScoped<IUserInfo, UserInfo>();
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
//这个注入没有成功--注入是没问题的构造函数也只是支持参数就好错在注入的地方不能写DbContext
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量 这在概念上类似于ADO.NET Provider原生的连接池操作方式具有节省DbContext实例化成本的优点
services.AddDbContextPool<IRaCISDBContext>(options =>
services.AddDbContextPool<IRaCISDBContext>((sp, options) =>
{
// 在控制台
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
@ -35,6 +37,7 @@ namespace IRaCIS.Core.SCP
options.EnableSensitiveDataLogging();
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
options.UseProjectables();

View File

@ -29,6 +29,10 @@ using IRaCIS.Core.Application.Service.ImageAndDoc;
using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;
using IRaCIS.Core.Application.BusinessFilter;
using Microsoft.AspNetCore.Http;
using IRaCIS.Core.Infrastructure.Extention;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Diagnostics;
#region 获取环境变量
@ -190,6 +194,52 @@ var env = app.Environment;
// Configure the HTTP request pipeline.
#region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
app.UseStatusCodePages(async context =>
{
var code = context.HttpContext.Response.StatusCode;
context.HttpContext.Response.ContentType = "application/json";
if (code < 500)
{
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"Client error, actual request error status code({code})")));
}
else
{
//ResultFilter 里面的异常并不会到这里
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject((ResponseOutput.NotOk($"Server error , actual request error status code({code})"))));
}
});
app.UseExceptionHandler(configure =>
{
configure.Run(async context =>
{
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
var ex = exceptionHandlerPathFeature?.Error;
context.Response.ContentType = "application/json";
if (ex != null)
{
var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}")));
Log.Logger.Error(errorInfo);
}
});
});
#endregion
//本地化
app.UseLocalization();
@ -211,22 +261,22 @@ app.UseLogDashboard("/LogDashboard");
//hangfire
app.UseHangfireConfig(env);
#region 暂时废弃
////限流 中间件
//app.UseIpRateLimiting();
//if (env.IsDevelopment())
//{
// app.UseDeveloperExceptionPage();
//}
//else
//{
// //app.UseHsts();
//}
#endregion
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
//app.UseHsts();
}
// 特殊异常处理 比如 404
app.UseStatusCodePagesWithReExecute("/Error/{0}");
SwaggerSetup.Configure(app, env);

View File

@ -7,6 +7,7 @@ using IRaCIS.Core.Infra.EFCore;
using Medallion.Threading;
using Medallion.Threading.SqlServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -21,12 +22,14 @@ namespace IRaCIS.Core.API
services.AddHttpContextAccessor();
services.AddScoped<IUserInfo, UserInfo>();
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
// First, register a pooling context factory as a Singleton service, as usual:
//这个注入没有成功--注入是没问题的构造函数也只是支持参数就好错在注入的地方不能写DbContext
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量 这在概念上类似于ADO.NET Provider原生的连接池操作方式具有节省DbContext实例化成本的优点
services.AddDbContext<IRaCISDBContext>(options =>
services.AddDbContext<IRaCISDBContext>((sp, options) =>
{
// 在控制台
@ -43,6 +46,7 @@ namespace IRaCIS.Core.API
options.EnableSensitiveDataLogging();
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
options.UseProjectables();

View File

@ -45,6 +45,7 @@ namespace IRaCIS.Core.Application.Filter
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info));
//warning 级别记录
//_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}");
}
@ -60,12 +61,17 @@ namespace IRaCIS.Core.Application.Filter
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
}
context.ExceptionHandled = true;//标记当前异常已经被处理过了
//throw new Exception("test-result-exceptioin");
}
else
{
//继续
}
context.ExceptionHandled = true;//标记当前异常已经被处理过了
}
}
}

View File

@ -511,7 +511,6 @@ namespace IRaCIS.Core.Infra.EFCore
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
// 采用触发器的方式 设置 CreateUserId CreateTime UpdateTime UpdateUserId 稽查实体里面没有这四个字段的值 因为先后顺序的原因
SetCommonEntityAuditInfo();
await AddAudit();
try
@ -595,85 +594,7 @@ namespace IRaCIS.Core.Infra.EFCore
}
/// <summary>
/// 重写savechange方式 统一增加审计信息 CreateUserId CreateTime UpdateTime Update UserId
/// </summary>
private void SetCommonEntityAuditInfo()
{
ChangeTracker.DetectChanges(); // Important!
// 获取所有更改,删除,新增的实体,但排除审计实体(避免死循环)
var entities = ChangeTracker.Entries()
.Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)).Where(x => !typeof(DataInspection).IsAssignableFrom(x.Entity.GetType())).ToList();
foreach (var t in entities)
{
switch (t.State)
{
case EntityState.Deleted:
break;
case EntityState.Modified:
if (t.Entity is IAuditUpdate updateEntity1)
{
updateEntity1.UpdateTime = DateTime.Now;
updateEntity1.UpdateUserId = _userInfo.Id;
}
if (t.Entity is ISoftDelete softDelete)
{
if (softDelete.IsDeleted)
{
softDelete.DeleteUserId = _userInfo.Id;
softDelete.DeletedTime = DateTime.Now;
}
else
{
softDelete.DeletedTime = null;
}
}
break;
//添加的时候,更新审计字段也赋值
case EntityState.Added:
if (t.Entity is IAuditAdd addEntity)
{
if (addEntity.CreateTime == default(DateTime))
{
addEntity.CreateTime = DateTime.Now;
}
addEntity.CreateUserId = _userInfo.Id;
}
if (t.Entity is IAuditUpdate updateEntity)
{
updateEntity.UpdateTime = DateTime.Now;
updateEntity.UpdateUserId = _userInfo.Id;
}
if (t.Entity is IAuditAddWithUserName addEntity3)
{
if (addEntity3.CreateTime == default(DateTime))
{
addEntity3.CreateTime = DateTime.Now;
}
addEntity3.CreateUserId = _userInfo.Id;
addEntity3.CreateUser = _userInfo.RealName;
}
break;
}
}
}
public virtual DbSet<TaskAllocationRule> TaskAllocationRule { get; set; }

View File

@ -0,0 +1,103 @@
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace IRaCIS.Core.Infra.EFCore;
public class AuditEntityInterceptor(IUserInfo _userInfo) : SaveChangesInterceptor
{
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
{
AuditEntities(eventData.Context);
return base.SavingChanges(eventData, result);
}
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData,
InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
AuditEntities(eventData.Context);
return base.SavingChangesAsync(eventData, result, cancellationToken);
}
public void AuditEntities(DbContext? context)
{
if (context == null) return;
// 获取所有更改,删除,新增的实体,但排除审计实体(避免死循环)
foreach (var entry in context.ChangeTracker.Entries().Where(u => (u.State == EntityState.Modified || u.State == EntityState.Added))
.Where(x => !typeof(DataInspection).IsAssignableFrom(x.Entity.GetType())).ToList())
{
switch (entry.State)
{
case EntityState.Deleted:
break;
case EntityState.Modified:
if (entry.Entity is IAuditUpdate updateEntity1)
{
updateEntity1.UpdateTime = DateTime.Now;
updateEntity1.UpdateUserId = _userInfo.Id;
}
if (entry.Entity is ISoftDelete softDelete)
{
if (softDelete.IsDeleted)
{
softDelete.DeleteUserId = _userInfo.Id;
softDelete.DeletedTime = DateTime.Now;
}
else
{
softDelete.DeletedTime = null;
}
}
break;
//添加的时候,更新审计字段也赋值
case EntityState.Added:
if (entry.Entity is IAuditAdd addEntity)
{
if (addEntity.CreateTime == default(DateTime))
{
addEntity.CreateTime = DateTime.Now;
}
addEntity.CreateUserId = _userInfo.Id;
}
if (entry.Entity is IAuditUpdate updateEntity)
{
updateEntity.UpdateTime = DateTime.Now;
updateEntity.UpdateUserId = _userInfo.Id;
}
if (entry.Entity is IAuditAddWithUserName addEntity3)
{
if (addEntity3.CreateTime == default(DateTime))
{
addEntity3.CreateTime = DateTime.Now;
}
addEntity3.CreateUserId = _userInfo.Id;
addEntity3.CreateUser = _userInfo.RealName;
}
break;
}
}
}
}

View File

@ -19,6 +19,8 @@
public string LocalizedInfo { get; set; }
}
/// <summary>