using System.Linq.Expressions;
using BuildFlow.Shared.Domain.Common;
using BuildFlow.Shared.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Scripting.Domain.Entities;
namespace Scripting.Infrastructure.Persistence;
///
/// Scripting 持久化上下文。承载脚本函数定义与执行日志。
/// 注册 AuditInterceptor 自动维护审计字段;对实现 ISoftDelete 的实体统一生成全局查询过滤器,
/// 自动排除已软删除记录。约定与 WorkflowDbContext 保持一致。
///
public class ScriptingDbContext : DbContext
{
private readonly ICurrentUserContext? _currentUserContext;
public DbSet Scripts => Set();
public DbSet ScriptExecutionLogs => Set();
public ScriptingDbContext(DbContextOptions options) : base(options) { }
public ScriptingDbContext(DbContextOptions options, ICurrentUserContext currentUserContext)
: base(options)
{
_currentUserContext = currentUserContext;
}
// 注意:此处通过 OnConfiguring 注册拦截器(而非 DI AddInterceptors),与 rag-backend 约定一致,
// 保证无参构造(如设计时工厂、测试夹具)也能正常工作。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (_currentUserContext is not null)
{
optionsBuilder.AddInterceptors(new AuditInterceptor(_currentUserContext));
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 自动发现本程序集内所有 IEntityTypeConfiguration 实现
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ScriptingDbContext).Assembly);
// 为所有实现 ISoftDelete 的实体动态生成 HasQueryFilter(e => !e.IsDeleted)
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
{
modelBuilder.Entity(entityType.ClrType).HasQueryFilter(
CreateSoftDeleteFilter(entityType.ClrType));
}
}
}
/// 用表达式树动态构造 e => !e.IsDeleted,避免对每个实体类型手写泛型过滤。
private static LambdaExpression CreateSoftDeleteFilter(Type entityType)
{
var param = Expression.Parameter(entityType, "e");
var isDeletedProp = Expression.Property(param, nameof(ISoftDelete.IsDeleted));
var filter = Expression.Lambda(Expression.Not(isDeletedProp), param);
return filter;
}
}