Files
Docker/CloudBuilder/CloudBuilder.Core/DatabaseAccessor/SecondaryDbContext.cs
T
2026-09-06 16:07:04 +08:00

205 lines
8.2 KiB
C#

using CloudBuilder.Core.DatabaseAccessor.Entity;
using CloudBuilder.Core.DependencyInjection.EFCore;
using CloudBuilder.Core.Policy;
using CloudBuilder.Core.Service;
using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.JsonWebTokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor
{
public class SecondaryDbContext : DbContext
{
public const string SECONDARY_DB_CONTEXT = "SecondaryDbContext";
public IEnumerable<IMasterRegistEntityType<ModelBuilder>> _entityManagers;
private readonly IServiceProvider _serviceProvider;
private readonly IHttpContextAccessor httpContextAccessor;
private FileLog fileLog;
public bool SkipAudit { get; set; } = false;
public SecondaryDbContext(DbContextOptions<SecondaryDbContext> options,
IEnumerable<IMasterRegistEntityType<ModelBuilder>> entityManagers,
IServiceProvider serviceProvider,
IHttpContextAccessor httpContextAccessor) : base(options)
{
_entityManagers = entityManagers;
_serviceProvider = serviceProvider;
this.httpContextAccessor = httpContextAccessor;
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
try
{
SavingChangesEvent();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
catch (Exception ex)
{
if (ex.Message.Contains("data may have been modified or deleted since entities were loaded."))
{
throw new ValidatedException(CoreMessagePolicy.GetInstance(_serviceProvider).ComposeMessage(CoreMessagePolicy.THE_DATA_HAS_BEEN_MODIFIED));
}
throw new ValidatedException(ex.InnerException?.Message ?? ex.Message);
}
}
public void SavingChangesEvent()
{
if (SkipAudit) return;
var entities = this.ChangeTracker.Entries()
.Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)).ToList();
if (entities == null || entities.Count < 1) return;
DateTime now = DateTime.Now;
string uid = this.httpContextAccessor.HttpContext?.User?.Identity?.Name;
if (string.IsNullOrEmpty(uid))
{
uid = string.Empty;
string token = GetJwtBearerToken(httpContextAccessor.HttpContext!);
if (!string.IsNullOrEmpty(token))
{
try
{
JsonWebToken json = new JsonWebTokenHandler().ReadJsonWebToken(token);
uid = json.GetClaim(ClaimPolicy.CLAINM_USERID).Value;
}
catch (Exception)
{
}
}
}
foreach (var entity in entities)
{
if (!(entity.State == EntityState.Added || entity.State == EntityState.Modified)) continue;
// 获取所有实体有效属性,排除 [NotMapper] 属性
var props = entity.OriginalValues.Properties;
// 获取实体当前(现在)的值
var currentValues = entity.CurrentValues;
// 遍历所有属性
foreach (var prop in props)
{
// 获取属性名
var propName = prop.Name;
if (propName == IEntityAudited.CREATED_BY && entity.State == EntityState.Added)
{
currentValues[propName] = uid;
continue;
}
if (propName == IEntityAudited.CREATED_DATETIME && entity.State == EntityState.Added)
{
currentValues[propName] = DateTime.Now;
continue;
}
if (propName == IEntityAudited.UPDATED_BY)
{
currentValues[propName] = uid;
continue;
}
if (propName == IEntityAudited.UPDATED_DATETIME)
{
currentValues[propName] = DateTime.Now;
continue;
}
}
}
foreach (var entity in entities)
{
var entityName = entity.Metadata.GetTableName() ?? entity.Entity.GetType().Name;
var state = entity.State;
// ======================================
// 1. 获取所有主键(支持复合主键)
// ======================================
var pkProps = entity.Properties
.Where(p => p.Metadata.IsPrimaryKey())
.ToList();
// 拼接 WHERE 条件:Id=1 AND TenantId=2
var whereClauses = pkProps
.Select(p => $"{p.Metadata.GetColumnName() ?? p.Metadata.Name} = '{p.CurrentValue?.ToString()?.Replace("'", "''")}'");
string whereSql = string.Join(" AND ", whereClauses);
string sqlLog = "";
// ======================================
// 修改 UPDATE
// ======================================
if (state == EntityState.Modified)
{
var setClauses = entity.Properties
.Where(p => p.IsModified) // 只记录真正修改的字段
.Select(p => $"{p.Metadata.GetColumnName() ?? p.Metadata.Name} = '{p.CurrentValue?.ToString()?.Replace("'", "''")}'");
string setSql = string.Join(", ", setClauses);
sqlLog = $"[{uid}][{now.ToString("yyyy-MM-dd HH:mm:ss")}]UPDATE {entityName} SET {setSql} WHERE {whereSql}";
}
// ======================================
// 新增 INSERT
// ======================================
else if (state == EntityState.Added)
{
var data = entity.Properties.ToDictionary(p => p.Metadata.GetColumnName() ?? p.Metadata.Name, p => p.CurrentValue);
var columns = string.Join(", ", data.Keys);
var values = string.Join(", ", data.Values.Select(v => $"'{v?.ToString()?.Replace("'", "''")}'"));
sqlLog = $"[{uid}][{now.ToString("yyyy-MM-dd HH:mm:ss")}]INSERT INTO {entityName} ({columns}) VALUES ({values})";
}
// ======================================
// 删除 DELETE
// ======================================
else if (state == EntityState.Deleted)
{
sqlLog = $"[{uid}][{now.ToString("yyyy-MM-dd HH:mm:ss")}]DELETE FROM {entityName} WHERE {whereSql}";
}
fileLog.WriteToFile(sqlLog);
}
}
protected override void OnModelCreating(ModelBuilder builder)
{
foreach (IMasterRegistEntityType<ModelBuilder> entityManager in _entityManagers)
{
entityManager.RegistEntity(builder);
}
base.OnModelCreating(builder);
}
public string GetJwtBearerToken(HttpContext httpContext, string headerKey = "Authorization", string tokenPrefix = "Bearer ")
{
if (httpContext == null) return null;
// 判断请求报文头中是否有 "Authorization" 报文头
var bearerToken = httpContext.Request.Headers[headerKey].ToString();
if (string.IsNullOrWhiteSpace(bearerToken)) return default;
var prefixLenght = tokenPrefix.Length;
return bearerToken.StartsWith(tokenPrefix, true, null) && bearerToken.Length > prefixLenght ? bearerToken[prefixLenght..] : default;
}
}
}