CloudBuilder/CloudBuilder.Core/DatabaseAccessor/MasterDbContext.cs
owenchen 1c0c83af5f ow
2026-05-12 10:09:31 +08:00

249 lines
9.8 KiB
C#
Raw 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;
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;
}
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}";
}
WriteToFile(sqlLog);
}
}
private void WriteToFile(string log)
{
try
{
long MaxFileSizeBytes = (long)(configuration.GetValue<int>("Logging:MaxFileSizeMB") * 1024 * 1024);
string today = DateTime.Now.ToString("yyyyMMdd");
string basePath = configuration["Logging:LogFileDirectory"];
Directory.CreateDirectory(basePath);
string baseFileName = $"log-{today}";
string ext = ".log";
string finalPath = Path.Combine(basePath, $"{baseFileName}{ext}");
int index = 1;
// 循环找没超过大小的文件
while (File.Exists(finalPath) && new FileInfo(finalPath).Length >= MaxFileSizeBytes)
{
finalPath = Path.Combine(basePath, $"{baseFileName}-{index}{ext}");
index++;
}
// 写入(线程安全)
lock (this)
{
File.AppendAllText(finalPath, log + Environment.NewLine, Encoding.UTF8);
}
}
catch { }
}
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;
}
}
}