数据记录审计方法调整
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
5c2bebf96c
commit
cd2166f11b
|
@ -4,6 +4,7 @@ using IRaCIS.Core.Infra.EFCore;
|
||||||
using Medallion.Threading;
|
using Medallion.Threading;
|
||||||
using Medallion.Threading.SqlServer;
|
using Medallion.Threading.SqlServer;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
@ -15,11 +16,12 @@ namespace IRaCIS.Core.SCP
|
||||||
{
|
{
|
||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
services.AddScoped<IUserInfo, UserInfo>();
|
services.AddScoped<IUserInfo, UserInfo>();
|
||||||
|
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省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(); });
|
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||||
|
@ -35,6 +37,7 @@ namespace IRaCIS.Core.SCP
|
||||||
options.EnableSensitiveDataLogging();
|
options.EnableSensitiveDataLogging();
|
||||||
|
|
||||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||||
|
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
||||||
|
|
||||||
options.UseProjectables();
|
options.UseProjectables();
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,10 @@ using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||||
using IP2Region.Net.Abstractions;
|
using IP2Region.Net.Abstractions;
|
||||||
using IP2Region.Net.XDB;
|
using IP2Region.Net.XDB;
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Microsoft.AspNetCore.Diagnostics;
|
||||||
|
|
||||||
|
|
||||||
#region 获取环境变量
|
#region 获取环境变量
|
||||||
|
@ -190,6 +194,52 @@ var env = app.Environment;
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// 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();
|
app.UseLocalization();
|
||||||
|
|
||||||
|
@ -211,22 +261,22 @@ app.UseLogDashboard("/LogDashboard");
|
||||||
//hangfire
|
//hangfire
|
||||||
app.UseHangfireConfig(env);
|
app.UseHangfireConfig(env);
|
||||||
|
|
||||||
|
#region 暂时废弃
|
||||||
////限流 中间件
|
////限流 中间件
|
||||||
//app.UseIpRateLimiting();
|
//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);
|
SwaggerSetup.Configure(app, env);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ using IRaCIS.Core.Infra.EFCore;
|
||||||
using Medallion.Threading;
|
using Medallion.Threading;
|
||||||
using Medallion.Threading.SqlServer;
|
using Medallion.Threading.SqlServer;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -21,12 +22,14 @@ namespace IRaCIS.Core.API
|
||||||
|
|
||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
services.AddScoped<IUserInfo, UserInfo>();
|
services.AddScoped<IUserInfo, UserInfo>();
|
||||||
|
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
||||||
|
|
||||||
|
|
||||||
// First, register a pooling context factory as a Singleton service, as usual:
|
// First, register a pooling context factory as a Singleton service, as usual:
|
||||||
|
|
||||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省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.EnableSensitiveDataLogging();
|
||||||
|
|
||||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||||
|
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
||||||
|
|
||||||
options.UseProjectables();
|
options.UseProjectables();
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ namespace IRaCIS.Core.Application.Filter
|
||||||
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info));
|
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info));
|
||||||
|
|
||||||
|
|
||||||
//warning 级别记录
|
//warning 级别记录
|
||||||
//_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}");
|
//_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));
|
_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
|
else
|
||||||
{
|
{
|
||||||
//继续
|
//继续
|
||||||
}
|
}
|
||||||
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -511,7 +511,6 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
|
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
|
||||||
{
|
{
|
||||||
// 采用触发器的方式 设置 CreateUserId CreateTime UpdateTime UpdateUserId 稽查实体里面没有这四个字段的值 因为先后顺序的原因
|
// 采用触发器的方式 设置 CreateUserId CreateTime UpdateTime UpdateUserId 稽查实体里面没有这四个字段的值 因为先后顺序的原因
|
||||||
SetCommonEntityAuditInfo();
|
|
||||||
await AddAudit();
|
await AddAudit();
|
||||||
|
|
||||||
try
|
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; }
|
public virtual DbSet<TaskAllocationRule> TaskAllocationRule { get; set; }
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
|
|
||||||
public string LocalizedInfo { get; set; }
|
public string LocalizedInfo { get; set; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
Loading…
Reference in New Issue