ow
This commit is contained in:
1 parent
31fbe199c1
commit
1c0c83af5f
469 files changed
+62544
-5
No files matched your search
@@ -0,0 +1,248 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user