CloudBuilder/CloudBuilder.Core/DatabaseAccessor/MasterDbContext.cs
owenchen 873f65dbbe ow
2026-08-06 13:15:56 +08:00

219 lines
8.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using CloudBuilder.Core.DatabaseAccessor.Entity;
using CloudBuilder.Core.DependencyInjection.EFCore;
using CloudBuilder.Core.Policy;
using CloudBuilder.Core.Service;
using DocumentFormat.OpenXml.Math;
using DocumentFormat.OpenXml.Vml.Office;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.JsonWebTokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor
{
public class MasterDbContext : DbContext
{
public const string MASTER_DB_CONTEXT = "MasterDbContext";
public IEnumerable<IMasterRegistEntityType<ModelBuilder>> _entityManagers;
private readonly IServiceProvider _serviceProvider;
private readonly IConfiguration configuration;
private readonly IHttpContextAccessor httpContextAccessor;
private FileLog fileLog;
public MasterDbContext(DbContextOptions<MasterDbContext> options,
IEnumerable<IMasterRegistEntityType<ModelBuilder>> entityManagers,
IServiceProvider serviceProvider,
IConfiguration configuration,
IHttpContextAccessor httpContextAccessor
) : base(options)
{
_entityManagers = entityManagers;
_serviceProvider = serviceProvider;
this.configuration = configuration;
this.httpContextAccessor = httpContextAccessor;
fileLog = new FileLog(configuration);
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
try
{
SavingChangesEvent();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
catch (Exception ex)
{
//自动检验数据是否过时
//ALTER TABLE table ADD row_version rowversion;
//[Column("row_version")]
//[Timestamp]
//public byte[] RowVersion { get; set; }
if (ex.Message.Contains("data may have been modified or deleted since entities were loaded."))
{
throw new ValidatedException(CoreMessagePolicy.GetInstance().ComposeMessage(CoreMessagePolicy.THE_DATA_HAS_BEEN_MODIFIED));
}
throw new ValidatedException(ex.InnerException?.Message ?? ex.Message);
}
}
public void SavingChangesEvent()
{
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;
string uid = this.httpContextAccessor.HttpContext?.User?.Identity?.Name;
DateTime now = DateTime.Now;
if (string.IsNullOrEmpty(uid))
{
uid = GetUserIdFromToken(httpContextAccessor.HttpContext!);
}
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;
}
public string GetUserIdFromToken(HttpContext httpContext)
{
string token = GetJwtBearerToken(httpContextAccessor.HttpContext!);
if (!string.IsNullOrEmpty(token))
{
try
{
JsonWebToken json = new JsonWebTokenHandler().ReadJsonWebToken(token);
return json.GetClaim(ClaimPolicy.CLAINM_USERID).Value;
}
catch (Exception)
{
return string.Empty;
}
}
return string.Empty;
}
}
}